Material refactor pt 3

- Slight simplification to model allocation
 - RenderLayers are no more
 - Add basically empty RenderTypeRegistry, to be filled out as I go
 - Move content of RenderLayerEvent into RenderContext
 - BlockModels have PartialModel names now
 - Futz around with InstancingEngine's render logic
 - Fix invisibility bug caused by not unbinding vaos
This commit is contained in:
Jozufozu 2022-04-14 21:20:28 -07:00
parent ccbce86a69
commit 671ac33dda
29 changed files with 415 additions and 464 deletions

View file

@ -8,7 +8,7 @@ import net.minecraft.core.Vec3i;
public interface MaterialManager {
<D extends InstanceData> InstancedMaterial<D> material(StructType<D> type);
<D extends InstanceData> Material<D> material(StructType<D> type);
Vec3i getOriginCoordinate();

View file

@ -1,65 +0,0 @@
package com.jozufozu.flywheel.api;
import javax.annotation.Nullable;
import net.minecraft.client.renderer.RenderType;
/**
* The 3 discrete stages the world is rendered in.
*/
public enum RenderLayer {
/**
* Solid layer:<br>
*
* All polygons will entirely occlude everything behind them.
*
* <br><br>
* e.g. stone, dirt, solid blocks
*/
SOLID,
/**
* Cutout layer:<br>
*
* <em>Fragments</em> will either occlude or not occlude depending on the texture/material.
*
* <br><br>
* e.g. leaves, cobwebs, tall grass, saplings, glass
*/
CUTOUT,
/**
* Transparent layer:<br>
*
* Nothing is guaranteed to occlude and fragments blend their color with what's behind them.
*
* <br><br>
* e.g. stained glass, water
*/
TRANSPARENT,
;
@Nullable
public static RenderLayer getPrimaryLayer(RenderType type) {
if (type == RenderType.solid()) {
return SOLID;
} else if (type == RenderType.cutoutMipped()) {
return CUTOUT;
} else if (type == RenderType.translucent()) {
return TRANSPARENT;
}
return null;
}
@Nullable
public static RenderLayer getLayer(RenderType type) {
if (type == RenderType.solid()) {
return SOLID;
} else if (type == RenderType.cutoutMipped() || type == RenderType.cutout()) {
return CUTOUT;
} else if (type == RenderType.translucent()) {
return TRANSPARENT;
}
return null;
}
}

View file

@ -1,12 +1,16 @@
package com.jozufozu.flywheel.backend.gl;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject {
private final boolean[] arrays = new boolean[GL32.glGetInteger(GL32.GL_MAX_VERTEX_ATTRIBS)];
public GlVertexArray() {
setHandle(GlStateManager._glGenVertexArrays());
}
@ -25,13 +29,27 @@ public class GlVertexArray extends GlObject {
public void enableArrays(int count) {
for (int i = 0; i < count; i++) {
GL20.glEnableVertexAttribArray(i);
enable(i);
}
}
public void disableArrays(int count) {
for (int i = 0; i < count; i++) {
disable(i);
}
}
private void enable(int i) {
if (!arrays[i]) {
GL20.glEnableVertexAttribArray(i);
arrays[i] = true;
}
}
private void disable(int i) {
if (arrays[i]) {
GL20.glDisableVertexAttribArray(i);
arrays[i] = false;
}
}

View file

@ -59,7 +59,7 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
}
public int getModelVertexCount() {
return 0;
return modelData.getVertexCount();
}
public int getInstanceCount() {

View file

@ -8,13 +8,16 @@ import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceM
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.util.ClientLevelExtension;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -35,8 +38,7 @@ public class InstanceWorld {
public static InstanceWorld create(LevelAccessor level) {
return switch (Backend.getBackendType()) {
case INSTANCING -> {
InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD)
.build();
InstancingEngine<WorldProgram> manager = new InstancingEngine<>(Contexts.WORLD);
var entityInstanceManager = new EntityInstanceManager(manager);
var blockEntityInstanceManager = new BlockEntityInstanceManager(manager);
@ -116,12 +118,12 @@ public class InstanceWorld {
/**
* Draw the given layer.
*/
public void renderLayer(RenderLayerEvent event) {
public void renderLayer(RenderContext context) {
taskEngine.syncPoint();
event.stack.pushPose();
event.stack.translate(-event.camX, -event.camY, -event.camZ);
engine.render(taskEngine, event);
event.stack.popPose();
context.pushPose();
context.translateBack(context.camX(), context.camY(), context.camZ());
engine.render(taskEngine, context);
context.popPose();
}
/**

View file

@ -98,12 +98,10 @@ public class InstancedRenderDispatcher {
@SubscribeEvent
public static void renderLayer(RenderLayerEvent event) {
if (event.layer == null) return;
ClientLevel world = event.getWorld();
if (!Backend.canUseInstancing(world)) return;
instanceWorlds.get(world).renderLayer(event);
instanceWorlds.get(world).renderLayer(event.context);
}
@SubscribeEvent

View file

@ -1,17 +1,18 @@
package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.core.RenderContext;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
public interface RenderDispatcher {
/**
* Render every model for every material.
*
* @param taskEngine
* @param event Context for rendering.
*/
void render(TaskEngine taskEngine, RenderLayerEvent event);
void render(TaskEngine taskEngine, RenderContext context);
/**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.

View file

@ -1,42 +1,36 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterial;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.util.FlwUtil;
import com.jozufozu.flywheel.core.RenderContext;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
public class BatchingEngine implements Engine {
private final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
private final Map<Batched<? extends InstanceData>, BatchedMaterial<?>> materials = new HashMap<>();
private final BatchDrawingTracker batchTracker = new BatchDrawingTracker();
public BatchingEngine() {
this.layers = new EnumMap<>(RenderLayer.class);
for (RenderLayer value : RenderLayer.values()) {
layers.put(value, new HashMap<>());
}
}
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> InstancedMaterial<D> material(StructType<D> type) {
return null;
public <D extends InstanceData> BatchedMaterial<D> material(StructType<D> type) {
if (type instanceof Batched<D> batched) {
return (BatchedMaterial<D>) materials.computeIfAbsent(batched, BatchedMaterial::new);
} else {
throw new ClassCastException("Cannot use type '" + type + "' with batching.");
}
}
@Override
@ -45,16 +39,34 @@ public class BatchingEngine implements Engine {
}
@Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) {
Map<RenderType, BatchedMaterialGroup> groups = layers.get(event.getLayer());
for (BatchedMaterialGroup group : groups.values()) {
group.render(event.stack, batchTracker, taskEngine);
}
public void render(TaskEngine taskEngine, RenderContext context) {
// vertexCount = 0;
// instanceCount = 0;
// for (BatchedMaterial<?> material : materials.values()) {
// for (CPUInstancer<?> instancer : material.models.values()) {
// instancer.setup();
// vertexCount += instancer.getVertexCount();
// instanceCount += instancer.getInstanceCount();
// }
// }
//
// DirectVertexConsumer consumer = batchTracker.getDirectConsumer(state, vertexCount);
//
// // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
// consumer.memSetZero();
//
// for (BatchedMaterial<?> material : materials.values()) {
// for (CPUInstancer<?> instancer : material.models.values()) {
// instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders();
// instancer.submitTasks(stack, pool, consumer);
// }
// }
// FIXME: this probably breaks some vanilla stuff but it works much better for flywheel
Matrix4f mat = new Matrix4f();
mat.setIdentity();
if (event.getWorld().effects().constantAmbientLight()) {
if (context.level().effects().constantAmbientLight()) {
Lighting.setupNetherLevel(mat);
} else {
Lighting.setupLevel(mat);
@ -76,15 +88,7 @@ public class BatchingEngine implements Engine {
@Override
public void addDebugInfo(List<String> info) {
info.add("Batching");
info.add("Instances: " + layers.values()
.stream()
.flatMap(FlwUtil::mapValues)
.mapToInt(BatchedMaterialGroup::getInstanceCount)
.sum());
info.add("Vertices: " + layers.values()
.stream()
.flatMap(FlwUtil::mapValues)
.mapToInt(BatchedMaterialGroup::getVertexCount)
.sum());
info.add("Instances: " + 0);
info.add("Vertices: " + 0);
}
}

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing.blockentity;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
@ -12,7 +11,6 @@ import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;

View file

@ -9,6 +9,7 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
@ -67,8 +68,16 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
return deleted || model == null;
}
public Map<RenderType, Renderable> init(ModelAllocator modelAllocator) {
if (isInitialized()) return ImmutableMap.of();
public boolean shouldRenderIn(RenderType renderType) {
return modelData.getRenderType() == renderType;
}
public void renderIn(RenderType renderType) {
render();
}
public void init(ModelAllocator modelAllocator) {
if (isInitialized()) return;
initialized = true;
@ -77,16 +86,10 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
vao = new GlVertexArray();
model = modelAllocator.alloc(modelData.get(), arenaModel -> {
vao.bind();
arenaModel.setupState(vao);
});
model = modelAllocator.alloc(modelData.get(), vao);
vao.bind();
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
return ImmutableMap.of(modelData.getRenderType(), this::render);
}
public boolean isInitialized() {

View file

@ -6,13 +6,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.backend.instancing.Renderable;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.core.ModelSupplier;
@ -27,19 +24,12 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
protected final Map<ModelSupplier, GPUInstancer<D>> models = new HashMap<>();
protected final Instanced<D> type;
public final Map<RenderType, List<Renderable>> renderables = new HashMap<>();
protected final List<GPUInstancer<D>> uninitialized = new ArrayList<>();
public InstancedMaterial(Instanced<D> type) {
this.type = type;
}
@Nullable
public List<Renderable> getRenderables(RenderType type) {
return renderables.get(type);
}
@Override
public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, k -> {
@ -80,17 +70,18 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
return models.values();
}
void init(ModelAllocator allocator) {
public void init(ModelAllocator allocator) {
for (GPUInstancer<?> instancer : uninitialized) {
instancer.init(allocator)
.forEach(this::addRenderable);
instancer.init(allocator);
}
uninitialized.clear();
}
private void addRenderable(RenderType type, Renderable renderable) {
this.renderables.computeIfAbsent(type, k -> new ArrayList<>())
.add(renderable);
public void renderIn(RenderType layer) {
for (GPUInstancer<?> instancer : models.values()) {
if (instancer.shouldRenderIn(layer)) {
instancer.renderIn(layer);
}
}
}
}

View file

@ -1,49 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.model.FallbackAllocator;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.backend.model.ModelPool;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.compile.ProgramContext;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.util.Textures;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
/**
* A group of materials all rendered with the same GL state.
*
* The children of a material group will all be rendered at the same time.
* No guarantees are made about the order of draw calls.
*/
public class InstancedMaterialGroup<P extends WorldProgram> {
protected final InstancingEngine<P> owner;
protected final RenderType type;
public InstancedMaterialGroup(InstancingEngine<P> owner, RenderType type) {
this.owner = owner;
this.type = type;
}
public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) {
type.setupRenderState();
Textures.bindActiveTextures();
//renderAll(viewProjection, camX, camY, camZ, layer);
type.clearRenderState();
}
protected void setup(P program) {
}
}

View file

@ -11,16 +11,16 @@ import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.Renderable;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.FallbackAllocator;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.backend.model.ModelPool;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.RenderTypeRegistry;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.compile.ProgramContext;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.math.Matrix4f;
@ -29,6 +29,7 @@ import net.minecraft.client.Camera;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
public class InstancingEngine<P extends WorldProgram> implements Engine {
@ -38,26 +39,18 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ProgramCompiler<P> context;
protected final GroupFactory<P> groupFactory;
protected final boolean ignoreOriginCoordinate;
private ModelAllocator allocator;
private final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
protected final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
private final WeakHashSet<OriginShiftListener> listeners;
private int vertexCount;
private int instanceCount;
public static <P extends WorldProgram> Builder<P> builder(ProgramCompiler<P> context) {
return new Builder<>(context);
}
public InstancingEngine(ProgramCompiler<P> context, GroupFactory<P> groupFactory, boolean ignoreOriginCoordinate) {
public InstancingEngine(ProgramCompiler<P> context) {
this.context = context;
this.ignoreOriginCoordinate = ignoreOriginCoordinate;
this.listeners = new WeakHashSet<>();
this.groupFactory = groupFactory;
}
@SuppressWarnings("unchecked")
@ -72,28 +65,20 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
}
@Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) {
public void render(TaskEngine taskEngine, RenderContext context) {
RenderType type = event.getType();
var camX = context.camX() - originCoordinate.getX();
var camY = context.camY() - originCoordinate.getY();
var camZ = context.camZ() - originCoordinate.getZ();
double camX;
double camY;
double camZ;
Matrix4f viewProjection;
if (!ignoreOriginCoordinate) {
camX = event.camX - originCoordinate.getX();
camY = event.camY - originCoordinate.getY();
camZ = event.camZ - originCoordinate.getZ();
// don't want to mutate viewProjection
var vp = context.viewProjection().copy();
vp.multiply(Matrix4f.createTranslateMatrix((float) -camX, (float) -camY, (float) -camZ));
viewProjection = Matrix4f.createTranslateMatrix((float) -camX, (float) -camY, (float) -camZ);
viewProjection.multiplyBackward(event.viewProjection);
} else {
camX = event.camX;
camY = event.camY;
camZ = event.camZ;
viewProjection = event.viewProjection;
}
render(context.type(), camX, camY, camZ, vp);
}
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection) {
vertexCount = 0;
instanceCount = 0;
@ -101,29 +86,30 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
Textures.bindActiveTextures();
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
List<Renderable> renderables = entry.getValue()
.getRenderables(type);
InstancedMaterial<?> material = entry.getValue();
Instanced<? extends InstanceData> instanceType = entry.getKey();
if (renderables == null || renderables.isEmpty()) {
continue;
}
setup(type, camX, camY, camZ, viewProjection, instanceType.getProgramSpec());
P program = context.getProgram(ProgramContext.create(entry.getKey()
.getProgramSpec(), Formats.POS_TEX_NORMAL, event.layer));
instanceCount += material.getInstanceCount();
vertexCount += material.getVertexCount();
program.bind();
program.uploadViewProjection(viewProjection);
program.uploadCameraPos(camX, camY, camZ);
//setup(program);
for (Renderable renderable : renderables) {
renderable.draw();
}
material.renderIn(type);
}
type.clearRenderState();
}
protected P setup(RenderType layer, double camX, double camY, double camZ, Matrix4f viewProjection, ResourceLocation programSpec) {
P program = context.getProgram(ProgramContext.create(programSpec, Formats.POS_TEX_NORMAL, RenderTypeRegistry.getAlphaDiscard(layer)));
program.bind();
program.uploadViewProjection(viewProjection);
program.uploadCameraPos(camX, camY, camZ);
return program;
}
public void clearAll() {
materials.values().forEach(InstancedMaterial::clear);
}
@ -152,22 +138,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
*/
@Override
public void beginFrame(Camera info) {
int cX = Mth.floor(info.getPosition().x);
int cY = Mth.floor(info.getPosition().y);
int cZ = Mth.floor(info.getPosition().z);
int dX = cX - originCoordinate.getX();
int dY = cY - originCoordinate.getY();
int dZ = cZ - originCoordinate.getZ();
if (Math.abs(dX) > MAX_ORIGIN_DISTANCE || Math.abs(dY) > MAX_ORIGIN_DISTANCE || Math.abs(dZ) > MAX_ORIGIN_DISTANCE) {
originCoordinate = new BlockPos(cX, cY, cZ);
materials.values().forEach(InstancedMaterial::clear);
listeners.forEach(OriginShiftListener::onOriginShift);
}
checkOriginDistance(info);
ModelAllocator allocator = getModelAllocator();
@ -182,6 +153,29 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
}
private void checkOriginDistance(Camera info) {
int cX = Mth.floor(info.getPosition().x);
int cY = Mth.floor(info.getPosition().y);
int cZ = Mth.floor(info.getPosition().z);
int dX = cX - originCoordinate.getX();
int dY = cY - originCoordinate.getY();
int dZ = cZ - originCoordinate.getZ();
if (Math.abs(dX) > MAX_ORIGIN_DISTANCE || Math.abs(dY) > MAX_ORIGIN_DISTANCE || Math.abs(dZ) > MAX_ORIGIN_DISTANCE) {
shiftListeners(cX, cY, cZ);
}
}
private void shiftListeners(int cX, int cY, int cZ) {
originCoordinate = new BlockPos(cX, cY, cZ);
materials.values().forEach(InstancedMaterial::clear);
listeners.forEach(OriginShiftListener::onOriginShift);
}
@Override
public void addDebugInfo(List<String> info) {
info.add("GL33 Instanced Arrays");
@ -210,33 +204,4 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
public interface OriginShiftListener {
void onOriginShift();
}
@FunctionalInterface
public interface GroupFactory<P extends WorldProgram> {
InstancedMaterialGroup<P> create(InstancingEngine<P> engine, RenderType type);
}
public static class Builder<P extends WorldProgram> {
protected final ProgramCompiler<P> context;
protected GroupFactory<P> groupFactory = InstancedMaterialGroup::new;
protected boolean ignoreOriginCoordinate;
public Builder(ProgramCompiler<P> context) {
this.context = context;
}
public Builder<P> setGroupFactory(GroupFactory<P> groupFactory) {
this.groupFactory = groupFactory;
return this;
}
public Builder<P> setIgnoreOriginCoordinate(boolean ignoreOriginCoordinate) {
this.ignoreOriginCoordinate = ignoreOriginCoordinate;
return this;
}
public InstancingEngine<P> build() {
return new InstancingEngine<>(context, groupFactory, ignoreOriginCoordinate);
}
}
}

View file

@ -1,14 +1,16 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model;
public enum FallbackAllocator implements ModelAllocator {
INSTANCE;
@Override
public BufferedModel alloc(Model model, Callback allocationCallback) {
public BufferedModel alloc(Model model, GlVertexArray vao) {
IndexedModel out = new IndexedModel(model);
allocationCallback.onAlloc(out);
vao.bind();
out.setupState(vao);
return out;
}
}

View file

@ -64,8 +64,8 @@ public class IndexedModel implements BufferedModel {
}
/**
* Draws many instances of this model, assuming the appropriate state is already bound.
*/
* Draws many instances of this model, assuming the appropriate state is already bound.
*/
@Override
public void drawInstances(int instanceCount) {
if (!valid()) return;

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model;
public interface ModelAllocator {
@ -7,9 +8,10 @@ public interface ModelAllocator {
* Allocate a model.
*
* @param model The model to allocate.
* @param vao The vertex array object to attach the model to.
* @return A handle to the allocated model.
*/
BufferedModel alloc(Model model, Callback allocationCallback);
BufferedModel alloc(Model model, GlVertexArray vao);
@FunctionalInterface
interface Callback {

View file

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType;
@ -51,12 +50,12 @@ public class ModelPool implements ModelAllocator {
* Allocate a model in the arena.
*
* @param model The model to allocate.
* @param vao The vertex array object to attach the model to.
* @return A handle to the allocated model.
*/
@Override
public PooledModel alloc(Model model, Callback callback) {
PooledModel bufferedModel = new PooledModel(model, vertices);
bufferedModel.callback = callback;
public PooledModel alloc(Model model, GlVertexArray vao) {
PooledModel bufferedModel = new PooledModel(vao, model, vertices);
vertices += model.vertexCount();
models.add(bufferedModel);
pendingUpload.add(bufferedModel);
@ -119,7 +118,7 @@ public class ModelPool implements ModelAllocator {
for (PooledModel model : models) {
model.first = vertices;
buffer(writer, model);
model.buffer(writer);
vertices += model.getVertexCount();
}
@ -133,7 +132,7 @@ public class ModelPool implements ModelAllocator {
try (MappedBuffer buffer = vbo.getBuffer()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
for (PooledModel model : pendingUpload) {
buffer(writer, model);
model.buffer(writer);
}
pendingUpload.clear();
} catch (Exception e) {
@ -141,12 +140,6 @@ public class ModelPool implements ModelAllocator {
}
}
private void buffer(VertexWriter writer, PooledModel model) {
writer.seekToVertex(model.first);
writer.writeVertexList(model.model.getReader());
if (model.callback != null) model.callback.onAlloc(model);
}
private void setDirty() {
dirty = true;
}
@ -158,14 +151,15 @@ public class ModelPool implements ModelAllocator {
public class PooledModel implements BufferedModel {
private final ElementBuffer ebo;
private Callback callback;
private final GlVertexArray vao;
private final Model model;
private int first;
private boolean remove;
private boolean deleted;
public PooledModel(Model model, int first) {
public PooledModel(GlVertexArray vao, Model model, int first) {
this.vao = vao;
this.model = model;
this.first = first;
ebo = model.createEBO();
@ -183,7 +177,7 @@ public class ModelPool implements ModelAllocator {
@Override
public void setupState(GlVertexArray vao) {
vbo.bind();
vao.bind();
vao.enableArrays(getAttributeCount());
vao.bindAttributes(0, vertexType.getLayout());
}
@ -206,14 +200,22 @@ public class ModelPool implements ModelAllocator {
@Override
public boolean isDeleted() {
return false;
return deleted;
}
@Override
public void delete() {
setDirty();
anyToRemove = true;
remove = true;
deleted = true;
}
private void buffer(VertexWriter writer) {
writer.seekToVertex(first);
writer.writeVertexList(model.getReader());
vao.bind();
setupState(vao);
}
}

View file

@ -8,9 +8,7 @@ import com.jozufozu.flywheel.util.NonNullSupplier;
import net.minecraft.client.renderer.RenderType;
public class ModelSupplier {
private final Lazy<Model> supplier;
public class ModelSupplier extends Lazy<Model> {
private RenderType renderType;
@ -19,7 +17,7 @@ public class ModelSupplier {
}
public ModelSupplier(NonNullSupplier<Model> supplier, RenderType renderType) {
this.supplier = Lazy.of(supplier);
super(supplier);
this.renderType = renderType;
}
@ -32,13 +30,17 @@ public class ModelSupplier {
return this;
}
@Nonnull
public Model get() {
return supplier.get();
}
@Nonnull
public RenderType getRenderType() {
return renderType;
}
public int getVertexCount() {
return map(Model::vertexCount).orElse(0);
}
@Override
public String toString() {
return "ModelSupplier{" + map(Model::name).orElse("Uninitialized") + '}';
}
}

View file

@ -23,7 +23,7 @@ public class Models {
}
public static ModelSupplier partial(PartialModel partial) {
return PARTIAL.computeIfAbsent(partial, it -> new ModelSupplier(() -> new BlockModel(it.get(), Blocks.AIR.defaultBlockState())));
return PARTIAL.computeIfAbsent(partial, it -> new ModelSupplier(() -> new BlockModel(it)));
}
public static ModelSupplier partial(PartialModel partial, Direction dir) {
@ -31,7 +31,7 @@ public class Models {
}
public static ModelSupplier partial(PartialModel partial, Direction dir, Supplier<PoseStack> modelTransform) {
return PARTIAL_DIR.computeIfAbsent(Pair.of(dir, partial), $ -> new ModelSupplier(() -> new BlockModel(partial.get(), Blocks.AIR.defaultBlockState(), modelTransform.get())));
return PARTIAL_DIR.computeIfAbsent(Pair.of(dir, partial), $ -> new ModelSupplier(() -> new BlockModel(partial, modelTransform.get())));
}
public static void onReload(ReloadRenderersEvent ignored) {

View file

@ -0,0 +1,52 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraft.client.renderer.RenderType;
public record RenderContext(ClientLevel level, RenderType type, PoseStack stack, Matrix4f viewProjection, RenderBuffers buffers,
double camX, double camY, double camZ) implements TransformStack {
@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);
}
}

View file

@ -0,0 +1,19 @@
package com.jozufozu.flywheel.core;
import javax.annotation.Nullable;
import net.minecraft.client.renderer.RenderType;
public class RenderTypeRegistry {
/**
* Gets the alpha discard threshold for the given render layer.
*
* @param layer The render layer to get the alpha discard threshold for.
* @return The alpha discard threshold.
*/
public static float getAlphaDiscard(@Nullable RenderType layer) {
return layer == RenderType.cutoutMipped() ? 0.1f : 0f;
}
}

View file

@ -2,11 +2,8 @@ package com.jozufozu.flywheel.core.compile;
import java.util.Objects;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
@ -20,29 +17,19 @@ public final class ProgramContext {
/**
* Creates a compilation context for the given program, vertex type and render layer.
*
* @param programName The name of the program to use.
* @param vertexType The vertex type to use.
* @param layer If cutout, the alpha discard threshold is 0.1, otherwise 0.
* @param programName The name of the program to use.
* @param vertexType The vertex type to use.
* @param alphaDiscard The alpha threshold below which pixels are discarded.
* @return A compilation context.
*/
public static ProgramContext create(ResourceLocation programName, VertexType vertexType, @Nullable RenderLayer layer) {
public static ProgramContext create(ResourceLocation programName, VertexType vertexType, float alphaDiscard) {
ProgramSpec spec = Backend.getSpec(programName);
if (spec == null) {
throw new NullPointerException("Cannot compile shader because '" + programName + "' is not recognized.");
}
return new ProgramContext(spec, getAlphaDiscard(layer), vertexType, GameStateRegistry.takeSnapshot());
}
/**
* Gets the alpha discard threshold for the given render layer.
*
* @param layer The render layer to get the alpha discard threshold for.
* @return The alpha discard threshold.
*/
public static float getAlphaDiscard(@Nullable RenderLayer layer) {
return layer == RenderLayer.CUTOUT ? 0.1f : 0f;
return new ProgramContext(spec, alphaDiscard, vertexType, GameStateRegistry.takeSnapshot());
}
public final ProgramSpec spec;

View file

@ -1,60 +0,0 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.util.Textures;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMaterialGroup<P> {
private int width;
private int height;
public CrumblingGroup(InstancingEngine<P> owner, RenderType type) {
super(owner, type);
}
@Override
public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) {
type.setupRenderState();
int renderTex = RenderSystem.getShaderTexture(0);
updateAtlasSize();
type.clearRenderState();
CrumblingRenderer._currentLayer.setupRenderState();
int breakingTex = RenderSystem.getShaderTexture(0);
RenderSystem.setShaderTexture(0, renderTex);
RenderSystem.setShaderTexture(4, breakingTex);
Textures.bindActiveTextures();
//renderAll(viewProjection, camX, camY, camZ, layer);
CrumblingRenderer._currentLayer.clearRenderState();
}
private void updateAtlasSize() {
AtlasInfo.SheetSize sheetSize = AtlasInfo.getSheetSize(Textures.getShaderTexture(0));
if (sheetSize != null) {
width = sheetSize.width();
height = sheetSize.height();
} else {
width = height = 256;
}
}
@Override
protected void setup(P p) {
p.setAtlasSize(width, height);
}
}

View file

@ -2,20 +2,26 @@ package com.jozufozu.flywheel.core.crumbling;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterial;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.Pair;
import com.jozufozu.flywheel.util.Textures;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -50,16 +56,17 @@ public class CrumblingRenderer {
_init();
}
public static void renderBreaking(RenderLayerEvent event) {
if (!Backend.canUseInstancing(event.getWorld())) return;
public static void renderBreaking(ClientLevel level, PoseStack stack, double x, double y, double z, Matrix4f viewProjection) {
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageBlockEntities(event.getWorld());
if (!Backend.canUseInstancing(level)) return;
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageBlockEntities(level);
if (activeStages.isEmpty()) return;
State state = STATE.get();
InstanceManager<BlockEntity> instanceManager = state.instanceManager;
InstancingEngine<CrumblingProgram> materials = state.materialManager;
var instanceManager = state.instanceManager;
var materials = state.materialManager;
TextureManager textureManager = Minecraft.getInstance().getTextureManager();
Camera info = Minecraft.getInstance().gameRenderer.getMainCamera();
@ -72,8 +79,11 @@ public class CrumblingRenderer {
stage.getValue().forEach(instanceManager::add);
instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info);
materials.beginFrame(info);
materials.render(SerialTaskEngine.INSTANCE, event);
var ctx = new RenderContext(level, _currentLayer, stack, viewProjection, null, x, y, z);
materials.render(SerialTaskEngine.INSTANCE, ctx);
instanceManager.invalidate();
}
@ -131,13 +141,11 @@ public class CrumblingRenderer {
}
private static class State {
private final InstancingEngine<CrumblingProgram> materialManager;
private final CrumblingEngine materialManager;
private final InstanceManager<BlockEntity> instanceManager;
private State() {
materialManager = InstancingEngine.builder(Contexts.CRUMBLING)
.setGroupFactory(CrumblingGroup::new)
.build();
materialManager = new CrumblingEngine();
instanceManager = new CrumblingInstanceManager(materialManager);
materialManager.addListener(instanceManager);
}
@ -147,4 +155,51 @@ public class CrumblingRenderer {
instanceManager.invalidate();
}
}
private static class CrumblingEngine extends InstancingEngine<CrumblingProgram> {
public CrumblingEngine() {
super(Contexts.CRUMBLING);
}
@Override
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection) {
type.setupRenderState();
int renderTex = RenderSystem.getShaderTexture(0);
AtlasInfo.SheetSize sheetSize = AtlasInfo.getSheetSize(Textures.getShaderTexture(0));
int width;
int height;
if (sheetSize != null) {
width = sheetSize.width();
height = sheetSize.height();
} else {
width = height = 256;
}
type.clearRenderState();
CrumblingRenderer._currentLayer.setupRenderState();
int breakingTex = RenderSystem.getShaderTexture(0);
RenderSystem.setShaderTexture(0, renderTex);
RenderSystem.setShaderTexture(4, breakingTex);
Textures.bindActiveTextures();
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
CrumblingProgram program = setup(type, camX, camY, camZ, viewProjection, entry.getKey()
.getProgramSpec());
program.setAtlasSize(width, height);
//entry.getValue().getAllRenderables().forEach(Renderable::draw);
}
CrumblingRenderer._currentLayer.clearRenderState();
}
}
}

View file

@ -2,10 +2,12 @@ package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.PartialModel;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
/**
@ -28,9 +30,21 @@ public class BlockModel implements Model {
this(model, referenceState, IDENTITY);
}
public BlockModel(PartialModel model) {
this(model, IDENTITY);
}
public BlockModel(PartialModel model, PoseStack ms) {
this(Formats.BLOCK.createReader(ModelUtil.getBufferBuilder(model.get(), Blocks.AIR.defaultBlockState(), ms)), model.getLocation().toString());
}
public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) {
reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilder(model, referenceState, ms));
name = referenceState.toString();
this(Formats.BLOCK.createReader(ModelUtil.getBufferBuilder(model, referenceState, ms)), referenceState.toString());
}
public BlockModel(VertexList reader, String name) {
this.reader = reader;
this.name = name;
}
@Override

View file

@ -1,8 +1,6 @@
package com.jozufozu.flywheel.event;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.RenderLayer;
import com.jozufozu.flywheel.core.RenderContext;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
@ -13,65 +11,51 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraftforge.eventbus.api.Event;
public class RenderLayerEvent extends Event {
private final ClientLevel world;
public final RenderType type;
public final PoseStack stack;
public final Matrix4f viewProjection;
public final RenderBuffers buffers;
public final double camX;
public final double camY;
public final double camZ;
public final RenderLayer layer;
public final RenderContext context;
public RenderLayerEvent(ClientLevel world, RenderType type, PoseStack stack, RenderBuffers buffers, double camX, double camY, double camZ) {
this.world = world;
this.type = type;
this.stack = stack;
viewProjection = stack.last()
var viewProjection = stack.last()
.pose()
.copy();
viewProjection.multiplyBackward(RenderSystem.getProjectionMatrix());
this.buffers = buffers;
this.camX = camX;
this.camY = camY;
this.camZ = camZ;
this.layer = RenderLayer.getPrimaryLayer(type);
}
@Nullable
public RenderLayer getLayer() {
return layer;
}
public ClientLevel getWorld() {
return world;
}
public RenderType getType() {
return type;
}
public Matrix4f getViewProjection() {
return viewProjection;
}
public double getCamX() {
return camX;
}
public double getCamY() {
return camY;
}
public double getCamZ() {
return camZ;
context = new RenderContext(world, type, stack, viewProjection, buffers, camX, camY, camZ);
}
@Override
public String toString() {
return "RenderLayerEvent[" + layer + "][" + "world=" + world + ", type=" + type + ", stack=" + stack + ", viewProjection=" + viewProjection + ", buffers=" + buffers + ", camX=" + camX + ", camY=" + camY + ", camZ=" + camZ + ']';
return "RenderLayerEvent{" + context + "}";
}
public ClientLevel getWorld() {
return context.level();
}
public RenderType getType() {
return context.type();
}
public PoseStack getStack() {
return context.stack();
}
public Matrix4f getViewProjection() {
return context.viewProjection();
}
public RenderBuffers getBuffers() {
return context.buffers();
}
public double getCamX() {
return context.camX();
}
public double getCamY() {
return context.camY();
}
public double getCamZ() {
return context.camZ();
}
}

View file

@ -6,6 +6,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.core.LastActiveCamera;
import com.jozufozu.flywheel.event.BeginFrameEvent;
@ -19,7 +20,9 @@ public class FrustumMixin {
@Inject(method = "prepare", at = @At("TAIL"))
private void onPrepare(double x, double y, double z, CallbackInfo ci) {
if (OptifineHandler.isShadowPass()) {
GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(Minecraft.getInstance().level, LastActiveCamera.getActiveCamera(), (Frustum) (Object) this));
restoreState.restore();
}
}
}

View file

@ -16,6 +16,7 @@ import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
@ -48,7 +49,9 @@ public class LevelRendererMixin {
@Inject(at = @At("HEAD"), method = "setupRender")
private void setupRender(Camera camera, Frustum frustum, boolean queue, boolean isSpectator, CallbackInfo ci) {
GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, camera, frustum));
restoreState.restore();
}
@Unique
@ -103,8 +106,13 @@ public class LevelRendererMixin {
Vec3 cameraPos = info.getPosition();
Matrix4f viewProjection = stack.last()
.pose()
.copy();
viewProjection.multiplyBackward(RenderSystem.getProjectionMatrix());
GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
CrumblingRenderer.renderBreaking(new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z));
CrumblingRenderer.renderBreaking(level, stack, cameraPos.x, cameraPos.y, cameraPos.z, viewProjection);
restoreState.restore();
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.util;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -25,7 +26,7 @@ public class Lazy<T> implements Supplier<T> {
return value;
}
public <Q> Lazy<Q> map(Function<T, Q> func) {
public <Q> Lazy<Q> lazyMap(Function<T, Q> func) {
return new Lazy<>(() -> func.apply(get()));
}
@ -38,4 +39,18 @@ public class Lazy<T> implements Supplier<T> {
func.accept(value);
}
}
/**
* If initialized, maps the stored value based on the function, otherwise returns the None.
* @param func The function to map the value with.
* @param <Q> The type of the mapped value.
* @return The mapped value, or None if not initialized.
*/
public <Q> Optional<Q> map(Function<T, Q> func) {
if (value != null) {
return Optional.of(func.apply(value));
} else {
return Optional.empty();
}
}
}