Culling experiments

- Cull updates based on the view frustum.
 - Instances check themselves against a FrustumIntersection object
 - Make GlProgram not abstract
 - Leave in small debug rendering experiment
This commit is contained in:
Jozufozu 2022-07-26 19:12:18 -07:00
parent ed3aca8bfc
commit 4ec1f8eaf3
36 changed files with 648 additions and 387 deletions

View file

@ -12,15 +12,15 @@ import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.DebugRender;
import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite; import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.event.EntityWorldHandler; import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents; import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances; import com.jozufozu.flywheel.vanilla.VanillaInstances;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
@ -80,7 +80,7 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll); forgeEventBus.addListener(InstancedArraysCompiler::invalidateAll);
forgeEventBus.addListener(Models::onReload); forgeEventBus.addListener(Models::onReload);
forgeEventBus.addListener(MeshPool::reset); forgeEventBus.addListener(MeshPool::reset);
forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers); forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers);
@ -108,6 +108,7 @@ public class Flywheel {
// forgeEventBus.addListener(ExampleEffect::onReload); // forgeEventBus.addListener(ExampleEffect::onReload);
Components.init(); Components.init();
DebugRender.init();
VanillaInstances.init(); VanillaInstances.init();

View file

@ -1,9 +1,19 @@
package com.jozufozu.flywheel.api.instance; package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
public interface Instance { public interface Instance {
BlockPos getWorldPosition(); BlockPos getWorldPosition();
/**
* Check this instance against a frustum.<p>
* An implementor may choose to return a constant to skip the frustum check.
* @param frustum A frustum intersection tester for the current frame.
* @return {@code true} if this instance should be considered for updates.
*/
boolean checkFrustum(FrustumIntersection frustum);
boolean isRemoved(); boolean isRemoved();
} }

View file

@ -6,7 +6,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.compile.ContextShader; import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderLoadingException;
@ -70,8 +70,8 @@ public class Loader implements ResourceManagerReloadListener {
for (StructType<?> structType : ComponentRegistry.structTypes) { for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) { for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) { for (ContextShader contextShader : ComponentRegistry.contextShaders) {
var ctx = new ProgramCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader); var ctx = new InstancedArraysCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader);
ProgramCompiler.INSTANCE.getProgram(ctx); InstancedArraysCompiler.INSTANCE.getProgram(ctx);
} }
} }
} }

View file

@ -5,25 +5,19 @@ import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform1i; import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import java.nio.FloatBuffer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.GlObject;
import com.mojang.blaze3d.shaders.ProgramManager; import com.mojang.blaze3d.shaders.ProgramManager;
import com.mojang.math.Matrix4f;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public abstract class GlProgram extends GlObject { public class GlProgram extends GlObject {
private static final FloatBuffer floatBuffer = MemoryStack.stackGet()
.mallocFloat(16);
public final ResourceLocation name; public final ResourceLocation name;
protected GlProgram(ResourceLocation name, int handle) { public GlProgram(ResourceLocation name, int handle) {
this.name = name; this.name = name;
setHandle(handle); setHandle(handle);
} }
@ -70,11 +64,6 @@ public abstract class GlProgram extends GlObject {
return samplerUniform; return samplerUniform;
} }
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
mat.store(floatBuffer);
glUniformMatrix4fv(uniform, false, floatBuffer);
}
@Override @Override
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
glDeleteProgram(handle); glDeleteProgram(handle);

View file

@ -15,9 +15,8 @@ import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.light.LightUpdater;
import com.mojang.math.Vector3f; import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
public abstract class InstanceManager<T> { public abstract class InstanceManager<T> {
@ -100,27 +99,26 @@ public abstract class InstanceManager<T> {
int dY = pos.getY() - cY; int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ; int dZ = pos.getZ() - cZ;
if (tick.shouldUpdate(dX, dY, dZ)) instance.tick(); if (!tick.shouldUpdate(dX, dY, dZ)) {
return;
}
instance.tick();
} }
public void beginFrame(TaskEngine taskEngine, RenderContext context) { public void beginFrame(TaskEngine taskEngine, RenderContext context) {
frame.tick(); frame.tick();
processQueuedAdditions(); processQueuedAdditions();
Camera camera = context.camera();
Vector3f look = camera.getLookVector();
float lookX = look.x();
float lookY = look.y();
float lookZ = look.z();
// integer camera pos // integer camera pos
BlockPos cameraIntPos = camera.getBlockPosition(); BlockPos cameraIntPos = context.camera().getBlockPosition();
int cX = cameraIntPos.getX(); int cX = cameraIntPos.getX();
int cY = cameraIntPos.getY(); int cY = cameraIntPos.getY();
int cZ = cameraIntPos.getZ(); int cZ = cameraIntPos.getZ();
FrustumIntersection culler = context.culler();
var instances = getStorage().getInstancesForUpdate(); var instances = getStorage().getInstancesForUpdate();
distributeWork(taskEngine, instances, instance -> updateInstance(instance, lookX, lookY, lookZ, cX, cY, cZ)); distributeWork(taskEngine, instances, instance -> updateInstance(instance, culler, cX, cY, cZ));
} }
private static <I> void distributeWork(TaskEngine taskEngine, List<I> instances, Consumer<I> action) { private static <I> void distributeWork(TaskEngine taskEngine, List<I> instances, Consumer<I> action) {
@ -140,7 +138,7 @@ public abstract class InstanceManager<T> {
} }
} }
protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { protected void updateInstance(DynamicInstance dyn, FrustumIntersection test, int cX, int cY, int cZ) {
if (!dyn.decreaseFramerateWithDistance()) { if (!dyn.decreaseFramerateWithDistance()) {
dyn.beginFrame(); dyn.beginFrame();
return; return;
@ -151,17 +149,16 @@ public abstract class InstanceManager<T> {
int dY = worldPos.getY() - cY; int dY = worldPos.getY() - cY;
int dZ = worldPos.getZ() - cZ; int dZ = worldPos.getZ() - cZ;
// is it more than 2 blocks behind the camera? if (!frame.shouldUpdate(dX, dY, dZ)) {
int dist = 2;
float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ;
if (dot < 0) {
return; return;
} }
if (frame.shouldUpdate(dX, dY, dZ)) if (dyn.checkFrustum(test)) {
dyn.beginFrame(); dyn.beginFrame();
} }
}
public void add(T obj) { public void add(T obj) {
if (!Backend.isOn()) return; if (!Backend.isOn()) return;

View file

@ -13,7 +13,7 @@ import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.ClientLevelExtension; import com.jozufozu.flywheel.util.extension.ClientLevelExtension;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@ -133,10 +133,7 @@ public class InstanceWorld {
*/ */
public void renderStage(RenderContext context, RenderStage stage) { public void renderStage(RenderContext context, RenderStage stage) {
taskEngine.syncPoint(); taskEngine.syncPoint();
context.pushPose();
context.translateBack(context.camera().getPosition());
engine.renderStage(taskEngine, context, stage); engine.renderStage(taskEngine, context, stage);
context.popPose();
} }
/** /**

View file

@ -14,6 +14,7 @@ import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -90,16 +91,13 @@ public abstract class BlockEntityInstance<T extends BlockEntity> extends Abstrac
return pos; return pos;
} }
protected InstancerFactory<TransformedPart> getTransformFactory() {
return instancerManager.factory(StructTypes.TRANSFORMED);
}
protected InstancerFactory<OrientedPart> getOrientedFactory() {
return instancerManager.factory(StructTypes.ORIENTED);
}
@Override @Override
public ImmutableBox getVolume() { public ImmutableBox getVolume() {
return GridAlignedBB.from(pos); return GridAlignedBB.from(pos);
} }
@Override
public boolean checkFrustum(FrustumIntersection frustum) {
return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
}
} }

View file

@ -8,6 +8,7 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM
import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.TickingLightListener; import com.jozufozu.flywheel.light.TickingLightListener;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -96,4 +97,11 @@ public abstract class EntityInstance<E extends Entity> extends AbstractInstance
public BlockPos getWorldPosition() { public BlockPos getWorldPosition() {
return entity.blockPosition(); return entity.blockPosition();
} }
@Override
public boolean checkFrustum(FrustumIntersection frustum) {
AABB aabb = entity.getBoundingBox();
return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ,
(float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
}
} }

View file

@ -20,7 +20,7 @@ import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ContextShader; import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.InstancedArraysCompiler;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.uniform.UniformBuffer; import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.WeakHashSet; import com.jozufozu.flywheel.util.WeakHashSet;
@ -137,9 +137,9 @@ public class InstancingEngine implements Engine {
.getInstanceShader(); .getInstanceShader();
Material material = desc.material(); Material material = desc.material();
var ctx = new ProgramCompiler.Context(vertexType, material, instanceShader, context); var ctx = new InstancedArraysCompiler.Context(vertexType, material, instanceShader, context);
ProgramCompiler.INSTANCE.getProgram(ctx) InstancedArraysCompiler.INSTANCE.getProgram(ctx)
.bind(); .bind();
UniformBuffer.getInstance().sync(); UniformBuffer.getInstance().sync();
} }

View file

@ -0,0 +1,104 @@
package com.jozufozu.flywheel.core;
import org.lwjgl.opengl.GL46;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.compile.DebugCompiler;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.mojang.blaze3d.systems.RenderSystem;
public class DebugRender {
private static final Lazy<GlProgram> SHADER = Lazy.of(() -> DebugCompiler.INSTANCE.get(new DebugCompiler.Context(Files.VERTEX, Files.FRAGMENT)));
private static final Lazy<Frustum> FRUSTUM_VBO = Lazy.of(Frustum::new);
public static void init() {
Files.init();
}
public static void updateFrustum(FrustumIntersection culler) {
FRUSTUM_VBO.get()
.upload(culler);
}
public static void drawFrustum() {
if (!FRUSTUM_VBO.isInitialized()) {
return;
}
RenderSystem.disableCull();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
try (var ignored = GlStateTracker.getRestoreState()) {
SHADER.get()
.bind();
FRUSTUM_VBO.get()
.draw();
}
}
public static class Files {
public static final FileResolution VERTEX = FileResolution.get(Flywheel.rl("debug/debug.vert"));
public static final FileResolution FRAGMENT = FileResolution.get(Flywheel.rl("debug/debug.frag"));
public static void init() {
}
}
// FIXME: This never worked (and the thing it was meant to debug is already fixed),
// but it should be a quick turnaround
private static class Frustum {
private static final int[] indices = new int[]{
0, 2, 3, 0, 3, 1,
2, 6, 7, 2, 7, 3,
6, 4, 5, 6, 5, 7,
4, 0, 1, 4, 1, 5,
0, 4, 6, 0, 6, 2,
1, 5, 7, 1, 7, 3,
};
private static final int elementCount = indices.length;
private static final int indicesSize = elementCount * 4;
private static final int verticesSize = 3 * 8 * 4;
private final int buffer;
private final int vao;
public Frustum() {
// holy moly DSA is nice
buffer = GL46.glCreateBuffers();
GL46.glNamedBufferStorage(buffer, verticesSize + indicesSize, GL46.GL_DYNAMIC_STORAGE_BIT);
GL46.glNamedBufferSubData(buffer, 0, indices);
vao = GL46.glCreateVertexArrays();
GL46.glEnableVertexArrayAttrib(vao, 0);
GL46.glVertexArrayElementBuffer(vao, buffer);
GL46.glVertexArrayVertexBuffer(vao, 0, buffer, indicesSize, 3 * 4);
GL46.glVertexArrayAttribFormat(vao, 0, 3, GL46.GL_FLOAT, false, 0);
}
public void upload(FrustumIntersection culler) {
try (var stack = MemoryStack.stackPush()) {
var buf = stack.malloc(3 * 8 * 4);
culler.bufferPlanes(buf);
GL46.glNamedBufferSubData(buffer, indicesSize, buf);
}
}
public void draw() {
GL46.glEnableVertexArrayAttrib(vao, 0);
GL46.glVertexArrayElementBuffer(vao, buffer);
GL46.glBindVertexArray(vao);
GL46.glDrawElements(GL46.GL_TRIANGLES, elementCount, GL46.GL_UNSIGNED_INT, 0);
}
}
}

View file

@ -2,56 +2,19 @@ package com.jozufozu.flywheel.core;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.extension.Matrix4fExtension;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.world.phys.Vec3;
public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection, public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection,
Matrix4f projection, RenderBuffers buffers, Camera camera) implements TransformStack { Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) {
@Override
public TransformStack multiply(Quaternion quaternion) {
return TransformStack.cast(stack).multiply(quaternion);
}
@Override
public TransformStack scale(float factorX, float factorY, float factorZ) {
return TransformStack.cast(stack).scale(factorX, factorY, factorZ);
}
@Override
public TransformStack pushPose() {
stack.pushPose();
return TransformStack.cast(stack);
}
@Override
public TransformStack popPose() {
stack.popPose();
return TransformStack.cast(stack);
}
@Override
public TransformStack mulPose(Matrix4f pose) {
return TransformStack.cast(stack).mulPose(pose);
}
@Override
public TransformStack mulNormal(Matrix3f normal) {
return TransformStack.cast(stack).mulNormal(normal);
}
@Override
public TransformStack translate(double x, double y, double z) {
return TransformStack.cast(stack).translate(x, y, z);
}
@NotNull @NotNull
public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) { public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) {
@ -59,4 +22,15 @@ public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack
viewProjection.multiply(view.last().pose()); viewProjection.multiply(view.last().pose());
return viewProjection; return viewProjection;
} }
public static FrustumIntersection createCuller(Camera camera, Matrix4f viewProjection) {
com.jozufozu.flywheel.util.joml.Matrix4f proj = Matrix4fExtension.clone(viewProjection);
Vec3 cam = camera
.getPosition();
proj.translate((float) -cam.x, (float) -cam.y, (float) -cam.z);
return new FrustumIntersection(proj);
}
} }

View file

@ -0,0 +1,89 @@
package com.jozufozu.flywheel.core.compile;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
/**
* Simple shader compiler that pulls no excessive tricks.<p>
* Useful for writing experimental shaders or
*/
public class DebugCompiler extends Memoizer<DebugCompiler.Context, GlProgram> {
public static final DebugCompiler INSTANCE = new DebugCompiler();
private final ShaderCompiler shaderCompiler;
private DebugCompiler() {
this.shaderCompiler = new ShaderCompiler();
}
@Override
public void invalidate() {
super.invalidate();
shaderCompiler.invalidate();
}
@Override
protected GlProgram _create(DebugCompiler.Context ctx) {
return new ProgramAssembler(ctx.vertex.getFileLoc())
.attachShader(shaderCompiler.vertex(ctx.vertex))
.attachShader(shaderCompiler.fragment(ctx.fragment))
.link()
.build(GlProgram::new);
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
public record Context(FileResolution vertex, FileResolution fragment) {
}
/**
* Handles compilation and deletion of vertex shaders.
*/
private static class ShaderCompiler extends Memoizer<ShaderCompiler.Context, GlShader> {
public GlShader vertex(FileResolution source) {
return get(new Context(source, ShaderType.VERTEX));
}
public GlShader fragment(FileResolution source) {
return get(new Context(source, ShaderType.FRAGMENT));
}
@Override
protected GlShader _create(Context ctx) {
var index = new CompilationContext();
String source = CompileUtil.generateHeader(GLSLVersion.V420, ctx.type) + ctx.source.getFile()
.generateFinalSource(index);
try {
return new GlShader(source, ctx.type, ImmutableList.of(ctx.source.getFileLoc()));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
public record Context(FileResolution source, ShaderType type) {
}
}
}

View file

@ -1,72 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile;
/**
* Handles compilation and deletion of fragment shaders.
*/
public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
public FragmentCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
var ctx = new CompilationContext();
// MATERIAL
SourceFile materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(ctx));
// CONTEXT
SourceFile contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(ctx));
// MAIN
finalSource.append(generateFooter());
try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
}
protected String generateFooter() {
return """
void main() {
flw_initFragment();
flw_materialFragment();
flw_contextFragment();
}
""";
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* Represents the conditions under which a shader is compiled.
*
* @param materialShader The fragment material shader source.
*/
public record Context(SourceFile materialShader, SourceFile contextShader) {
}
}

View file

@ -0,0 +1,248 @@
package com.jozufozu.flywheel.core.compile;
import java.util.ArrayList;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderField;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.Pair;
/**
* A caching compiler.
*
* <p>
* This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
* <p>
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p>
*/
public class InstancedArraysCompiler extends Memoizer<InstancedArraysCompiler.Context, GlProgram> {
public static final InstancedArraysCompiler INSTANCE = new InstancedArraysCompiler();
private final VertexCompiler vertexCompiler;
private final FragmentCompiler fragmentCompiler;
private InstancedArraysCompiler() {
this.vertexCompiler = new VertexCompiler();
this.fragmentCompiler = new FragmentCompiler();
}
/**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public GlProgram getProgram(InstancedArraysCompiler.Context ctx) {
return super.get(ctx);
}
@Override
public void invalidate() {
super.invalidate();
vertexCompiler.invalidate();
fragmentCompiler.invalidate();
}
@Override
protected GlProgram _create(InstancedArraysCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile
Material material = ctx.material;
FileResolution instanceShader = ctx.instanceShader();
ContextShader contextShader = ctx.contextShader;
var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader());
var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader());
return new ProgramAssembler(instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(vertex))
.attachShader(fragmentCompiler.get(fragment))
.link()
.build(contextShader.factory());
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use.
* @param instanceShader The instance shader to use.
* @param contextShader The context shader to use.
*/
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
ContextShader contextShader) {
}
/**
* Handles compilation and deletion of vertex shaders.
*/
public static class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
public VertexCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
var index = new CompilationContext();
// LAYOUT
var layoutShader = key.vertexType.getLayoutShader().getFile();
finalSource.append(layoutShader.generateFinalSource(index));
// INSTANCE
int attributeBaseIndex = key.vertexType.getLayout()
.getAttributeCount();
var instanceShader = key.instanceShader;
var replacements = new ArrayList<Pair<Span, String>>();
for (ShaderField field : instanceShader.fields.values()) {
if (field.decoration != ShaderField.Decoration.IN) {
continue;
}
int location = Integer.parseInt(field.location.get());
int newLocation = location + attributeBaseIndex;
replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
}
finalSource.append(instanceShader.generateFinalSource(index, replacements));
// MATERIAL
var materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(index));
// CONTEXT
var contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(index));
// MAIN
finalSource.append("""
void main() {
flw_layoutVertex();
flw_instanceVertex();
flw_materialVertex();
flw_contextVertex();
}
""");
try {
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* @param vertexType The vertex type to use.
* @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
*/
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) {
}
}
/**
* Handles compilation and deletion of fragment shaders.
*/
public static class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
public FragmentCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
var ctx = new CompilationContext();
// MATERIAL
SourceFile materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(ctx));
// CONTEXT
SourceFile contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(ctx));
// MAIN
finalSource.append(generateFooter());
try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
}
protected String generateFooter() {
return """
void main() {
flw_initFragment();
flw_materialFragment();
flw_contextFragment();
}
""";
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* Represents the conditions under which a shader is compiled.
*
* @param materialShader The fragment material shader source.
*/
public record Context(SourceFile materialShader, SourceFile contextShader) {
}
}
}

View file

@ -1,88 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
/**
* A caching compiler.
*
* <p>
* This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
* <p>
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p>
*/
public class ProgramCompiler extends Memoizer<ProgramCompiler.Context, GlProgram> {
public static final ProgramCompiler INSTANCE = new ProgramCompiler();
private final VertexCompiler vertexCompiler;
private final FragmentCompiler fragmentCompiler;
private ProgramCompiler() {
this.vertexCompiler = new VertexCompiler();
this.fragmentCompiler = new FragmentCompiler();
}
/**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public GlProgram getProgram(ProgramCompiler.Context ctx) {
return super.get(ctx);
}
@Override
public void invalidate() {
super.invalidate();
vertexCompiler.invalidate();
fragmentCompiler.invalidate();
}
@Override
protected GlProgram _create(ProgramCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile
Material material = ctx.material;
FileResolution instanceShader = ctx.instanceShader();
ContextShader contextShader = ctx.contextShader;
var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader());
var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader());
return new ProgramAssembler(instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(vertex))
.attachShader(fragmentCompiler.get(fragment))
.link()
.build(contextShader.factory());
}
@Override
protected void _destroy(GlProgram value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent ignored) {
INSTANCE.invalidate();
}
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use.
* @param instanceShader The instance shader to use.
* @param contextShader The context shader to use.
*/
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
ContextShader contextShader) {
}
}

View file

@ -1,99 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import java.util.ArrayList;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderField;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.util.Pair;
/**
* Handles compilation and deletion of vertex shaders.
*/
public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
public VertexCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
var index = new CompilationContext();
// LAYOUT
var layoutShader = key.vertexType.getLayoutShader().getFile();
finalSource.append(layoutShader.generateFinalSource(index));
// INSTANCE
int attributeBaseIndex = key.vertexType.getLayout()
.getAttributeCount();
var instanceShader = key.instanceShader;
var replacements = new ArrayList<Pair<Span, String>>();
for (ShaderField field : instanceShader.fields.values()) {
if (field.decoration != ShaderField.Decoration.IN) {
continue;
}
int location = Integer.parseInt(field.location.get());
int newLocation = location + attributeBaseIndex;
replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
}
finalSource.append(instanceShader.generateFinalSource(index, replacements));
// MATERIAL
var materialShader = key.materialShader;
finalSource.append(materialShader.generateFinalSource(index));
// CONTEXT
var contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(index));
// MAIN
finalSource.append("""
void main() {
flw_layoutVertex();
flw_instanceVertex();
flw_materialVertex();
flw_contextVertex();
}
""");
try {
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
/**
* @param vertexType The vertex type to use.
* @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
*/
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) {
}
}

View file

@ -1,17 +0,0 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
public class CrumblingInstanceManager extends BlockEntityInstanceManager {
public CrumblingInstanceManager(InstancerManager instancerManager) {
super(instancerManager);
}
@Override
protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
dyn.beginFrame();
}
}

View file

@ -8,6 +8,8 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine; import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.DrawCall;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Components; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
@ -128,7 +130,7 @@ public class CrumblingRenderer {
private State() { private State() {
instancerManager = new CrumblingEngine(); instancerManager = new CrumblingEngine();
instanceManager = new CrumblingInstanceManager(instancerManager); instanceManager = new BlockEntityInstanceManager(instancerManager);
instancerManager.attachManagers(instanceManager); instancerManager.attachManagers(instanceManager);
} }

View file

@ -4,7 +4,7 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe; import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.extension.MatrixExtension;
public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe<TransformedPart> { public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe<TransformedPart> {
@ -17,7 +17,7 @@ public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe<TransformedP
super.writeInternal(d); super.writeInternal(d);
long ptr = writePointer + 6; long ptr = writePointer + 6;
((MatrixWrite) (Object) d.model).flywheel$writeUnsafe(ptr); ((MatrixExtension) (Object) d.model).flywheel$writeUnsafe(ptr);
((MatrixWrite) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16); ((MatrixExtension) (Object) d.normal).flywheel$writeUnsafe(ptr + 4 * 16);
} }
} }

View file

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.extension.MatrixExtension;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
@ -54,7 +54,7 @@ public class ViewProvider extends UniformProvider {
long ptr = MemoryUtil.memAddress(buffer); long ptr = MemoryUtil.memAddress(buffer);
MatrixWrite.writeUnsafe(vp, ptr); MatrixExtension.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX); MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY); MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ); MemoryUtil.memPutFloat(ptr + 72, camZ);

View file

@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.util.ClientLevelExtension; import com.jozufozu.flywheel.util.extension.ClientLevelExtension;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;

View file

@ -43,7 +43,9 @@ public class LevelRendererMixin {
@Inject(at = @At("HEAD"), method = "renderLevel") @Inject(at = @At("HEAD"), method = "renderLevel")
private void beginRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) { private void beginRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) {
renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, RenderContext.createViewProjection(pPoseStack, pProjectionMatrix), pProjectionMatrix, renderBuffers, pCamera); var viewProjection = RenderContext.createViewProjection(pPoseStack, pProjectionMatrix);
var culler = RenderContext.createCuller(pCamera, viewProjection);
renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler);
try (var restoreState = GlStateTracker.getRestoreState()) { try (var restoreState = GlStateTracker.getRestoreState()) {
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(renderContext)); MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(renderContext));

View file

@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;
import com.jozufozu.flywheel.util.RenderTypeExtension; import com.jozufozu.flywheel.util.extension.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;

View file

@ -6,11 +6,12 @@ import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.extension.Matrix3fExtension;
import com.jozufozu.flywheel.util.extension.MatrixExtension;
import com.mojang.math.Matrix3f; import com.mojang.math.Matrix3f;
@Mixin(Matrix3f.class) @Mixin(Matrix3f.class)
public abstract class Matrix3fMixin implements MatrixWrite { public abstract class Matrix3fMixin implements MatrixExtension, Matrix3fExtension {
@Shadow protected float m00; @Shadow protected float m00;
@Shadow protected float m01; @Shadow protected float m01;
@ -47,4 +48,9 @@ public abstract class Matrix3fMixin implements MatrixWrite {
buffer.putFloat(m12); buffer.putFloat(m12);
buffer.putFloat(m22); buffer.putFloat(m22);
} }
@Override
public com.jozufozu.flywheel.util.joml.Matrix3f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix3f matrix) {
return matrix.set(m00, m10, m20, m01, m11, m21, m02, m12, m22);
}
} }

View file

@ -6,11 +6,12 @@ import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.extension.Matrix4fExtension;
import com.jozufozu.flywheel.util.extension.MatrixExtension;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
@Mixin(Matrix4f.class) @Mixin(Matrix4f.class)
public abstract class Matrix4fMixin implements MatrixWrite { public abstract class Matrix4fMixin implements MatrixExtension, Matrix4fExtension {
@Shadow protected float m00; @Shadow protected float m00;
@Shadow protected float m01; @Shadow protected float m01;
@ -68,4 +69,13 @@ public abstract class Matrix4fMixin implements MatrixWrite {
buf.putFloat(m23); buf.putFloat(m23);
buf.putFloat(m33); buf.putFloat(m33);
} }
@Override
public com.jozufozu.flywheel.util.joml.Matrix4f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix4f matrix) {
return matrix.set(
m00, m10, m20, m30,
m01, m11, m21, m31,
m02, m12, m22, m32,
m03, m13, m23, m33);
}
} }

View file

@ -26,6 +26,10 @@ public class Lazy<T> implements Supplier<T> {
return value; return value;
} }
public boolean isInitialized() {
return value != null;
}
public <Q> Lazy<Q> lazyMap(Function<T, Q> func) { public <Q> Lazy<Q> lazyMap(Function<T, Q> func) {
return new Lazy<>(() -> func.apply(get())); return new Lazy<>(() -> func.apply(get()));
} }

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.util; package com.jozufozu.flywheel.util.extension;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.util.extension;
import com.jozufozu.flywheel.util.joml.Matrix3f;
public interface Matrix3fExtension {
Matrix3f flywheel$store(Matrix3f matrix);
static Matrix3f clone(com.mojang.math.Matrix3f moj) {
return ((Matrix3fExtension)(Object) moj).flywheel$store(new Matrix3f());
}
static void store(com.mojang.math.Matrix3f moj, Matrix3f joml) {
((Matrix3fExtension)(Object) moj).flywheel$store(joml);
}
}

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.util.extension;
import com.jozufozu.flywheel.util.joml.Matrix4f;
public interface Matrix4fExtension {
Matrix4f flywheel$store(Matrix4f matrix);
static Matrix4f clone(com.mojang.math.Matrix4f moj) {
return ((Matrix4fExtension)(Object) moj).flywheel$store(new Matrix4f());
}
static void store(com.mojang.math.Matrix4f moj, Matrix4f joml) {
((Matrix4fExtension)(Object) moj).flywheel$store(joml);
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.util; package com.jozufozu.flywheel.util.extension;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -8,7 +8,7 @@ import com.mojang.math.Matrix4f;
* @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin
* @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin
*/ */
public interface MatrixWrite { public interface MatrixExtension {
/** /**
* Write the contents of this object into sequential memory starting at the given address. * Write the contents of this object into sequential memory starting at the given address.
@ -18,10 +18,10 @@ public interface MatrixWrite {
void flywheel$write(ByteBuffer buf); void flywheel$write(ByteBuffer buf);
static void write(Matrix4f matrix, ByteBuffer buf) { static void write(Matrix4f matrix, ByteBuffer buf) {
((MatrixWrite) (Object) matrix).flywheel$write(buf); ((MatrixExtension) (Object) matrix).flywheel$write(buf);
} }
static void writeUnsafe(Matrix4f matrix, long ptr) { static void writeUnsafe(Matrix4f matrix, long ptr) {
((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr); ((MatrixExtension) (Object) matrix).flywheel$writeUnsafe(ptr);
} }
} }

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.util; package com.jozufozu.flywheel.util.extension;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;

View file

@ -23,6 +23,9 @@
*/ */
package com.jozufozu.flywheel.util.joml; package com.jozufozu.flywheel.util.joml;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
/** /**
* Efficiently performs frustum intersection tests by caching the frustum planes of an arbitrary transformation {@link Matrix4fc matrix}. * Efficiently performs frustum intersection tests by caching the frustum planes of an arbitrary transformation {@link Matrix4fc matrix}.
* <p> * <p>
@ -950,4 +953,40 @@ public class FrustumIntersection {
return da >= 0.0f || db >= 0.0f; return da >= 0.0f || db >= 0.0f;
} }
public void bufferPlanes(ByteBuffer buffer) {
Vector3f scratch = new Vector3f();
Vector3f result = new Vector3f();
long addr = MemoryUtil.memAddress(buffer);
planeIntersect(planes[0], planes[2], planes[4], result, scratch); result.getToAddress(addr);
planeIntersect(planes[0], planes[2], planes[5], result, scratch); result.getToAddress(addr + 12);
planeIntersect(planes[0], planes[3], planes[4], result, scratch); result.getToAddress(addr + 24);
planeIntersect(planes[0], planes[3], planes[5], result, scratch); result.getToAddress(addr + 36);
planeIntersect(planes[1], planes[2], planes[4], result, scratch); result.getToAddress(addr + 48);
planeIntersect(planes[1], planes[2], planes[5], result, scratch); result.getToAddress(addr + 60);
planeIntersect(planes[1], planes[3], planes[4], result, scratch); result.getToAddress(addr + 72);
planeIntersect(planes[1], planes[3], planes[5], result, scratch); result.getToAddress(addr + 84);
}
private Vector3f planeIntersect(Vector4f a, Vector4f b, Vector4f c, Vector3f result, Vector3f scratch) {
// Formula used
// d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 )
//P = ---------------------------------------------------------------------
// N1 . ( N2 * N3 )
//
// Note: N refers to the normal, d refers to the displacement. '.' means dot product. '*' means cross product
float f = result.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).dot(a.x, a.y, a.z);
result.set(0);
scratch.set(b.x, b.y, b.z).cross(c.x, c.y, c.z).mul(a.z);
result.add(scratch);
scratch.set(c.x, c.y, c.z).cross(a.x, a.y, a.z).mul(b.z);
result.add(scratch);
scratch.set(a.x, a.y, a.z).cross(b.x, b.y, b.z).mul(c.z);
result.add(scratch);
return result.div(f);
}
} }

View file

@ -32,6 +32,8 @@ import java.nio.FloatBuffer;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import com.mojang.math.Quaternion;
/** /**
* Quaternion of 4 single-precision floats which can represent rotation and uniform scaling. * Quaternion of 4 single-precision floats which can represent rotation and uniform scaling.
* *
@ -130,6 +132,13 @@ public class Quaternionf implements Externalizable, Cloneable, Quaternionfc {
w = cos; w = cos;
} }
public Quaternionf(Quaternion moj) {
x = moj.i();
y = moj.j();
z = moj.k();
w = moj.r();
}
/** /**
* @return the first component of the vector part * @return the first component of the vector part
*/ */

View file

@ -18,6 +18,7 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.jozufozu.flywheel.util.joml.Vector3f; import com.jozufozu.flywheel.util.joml.Vector3f;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -297,5 +298,10 @@ public class ExampleEffect implements Effect {
public boolean decreaseFramerateWithDistance() { public boolean decreaseFramerateWithDistance() {
return false; return false;
} }
@Override
public boolean checkFrustum(FrustumIntersection frustum) {
return true;
}
} }
} }

View file

@ -0,0 +1,5 @@
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 1.0, 1.0, 0.2);
}

View file

@ -0,0 +1,7 @@
#use "flywheel:uniform/view.glsl"
layout(location = 0) in vec3 worldPos;
void main() {
gl_Position = flw_viewProjection * vec4(worldPos, 1.0);
}