Merge branch '1.18/dev' into 1.18/shader-sanity

# Conflicts:
#	src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java
#	src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java
#	src/main/java/com/jozufozu/flywheel/core/shader/gamestate/NormalDebugStateProvider.java
#	src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanGameStateCondition.java
#	src/main/java/com/jozufozu/flywheel/core/shader/spec/GameStateCondition.java
#	src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramState.java
#	src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java
This commit is contained in:
Jozufozu 2022-01-07 11:52:42 -08:00
commit 2254a693c9
84 changed files with 1082 additions and 472 deletions

View file

@ -59,6 +59,8 @@ body:
label: Mod Version label: Mod Version
description: The version of the mod you were using when the bug occured description: The version of the mod you were using when the bug occured
options: options:
- "0.5.1"
- "0.5.0a"
- "0.5.0" - "0.5.0"
- "0.4.2-rc" - "0.4.2-rc"
- "0.4.1" - "0.4.1"

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ run/
.gradle/ .gradle/
build/ build/
gradle-app.setting gradle-app.setting
out/
## IntelliJ IDEA ## IntelliJ IDEA

View file

@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G
org.gradle.daemon = false org.gradle.daemon = false
# mod version info # mod version info
mod_version = 0.5.0a mod_version = 0.5.1
mc_update_version = 1.18 mc_update_version = 1.18
minecraft_version = 1.18.1 minecraft_version = 1.18.1
forge_version = 39.0.8 forge_version = 39.0.8

View file

@ -15,7 +15,6 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
public class FlywheelClient { public class FlywheelClient {
public static void clientInit() { public static void clientInit() {
CrashReportCallables.registerCrashCallable("Flywheel Backend", () -> CrashReportCallables.registerCrashCallable("Flywheel Backend", () ->
Backend.getInstance().getBackendDescriptor()); Backend.getInstance().getBackendDescriptor());

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.api;
/**
* Something (a BlockEntity or Entity) that can be rendered using the instancing API.
*/
public interface FlywheelRendered {
/**
* @return true if there are parts of the renderer that cannot be implemented with Flywheel.
*/
default boolean shouldRenderNormally() {
return false;
}
}

View file

@ -2,7 +2,8 @@ package com.jozufozu.flywheel.api;
/** /**
* A marker interface custom worlds can override to indicate * A marker interface custom worlds can override to indicate
* that tiles inside the world should render with Flywheel. * that block entities and entities inside the world should
* render with Flywheel.
* *
* {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default. * {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default.
*/ */

View file

@ -50,4 +50,14 @@ public interface Instancer<D extends InstanceData> {
* </p> * </p>
*/ */
void notifyRemoval(); void notifyRemoval();
/**
* Populate arr with new instances of this model.
* @param arr An array to fill.
*/
default void createInstances(D[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = createInstance();
}
}
} }

View file

@ -2,18 +2,18 @@ package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
/** /**
* An interface giving {@link TileEntityInstance}s a hook to have a function called at * An interface giving {@link BlockEntityInstance}s a hook to have a function called at
* the start of a frame. By implementing {@link IDynamicInstance}, a {@link TileEntityInstance} * the start of a frame. By implementing {@link DynamicInstance}, a {@link BlockEntityInstance}
* can animate its models in ways that could not be easily achieved by shader attribute * can animate its models in ways that could not be easily achieved by shader attribute
* parameterization. * parameterization.
* *
* <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need * <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need
* to parameterize the instances, you're encouraged to implement this for prototyping. * to parameterize the instances, you're encouraged to implement this for prototyping.
*/ */
public interface IDynamicInstance extends IInstance { public interface DynamicInstance extends Instance {
/** /**
* Called every frame, and after initialization. * Called every frame, and after initialization.
* <br> * <br>
@ -30,7 +30,7 @@ public interface IDynamicInstance extends IInstance {
* <br> You might want to opt out of this if you want your animations to remain smooth * <br> You might want to opt out of this if you want your animations to remain smooth
* even when far away from the camera. It is recommended to keep this as is, however. * even when far away from the camera. It is recommended to keep this as is, however.
* *
* @return <code>true</code> if your instance should be slow ticked. * @return {@code true} if your instance should be slow ticked.
*/ */
default boolean decreaseFramerateWithDistance() { default boolean decreaseFramerateWithDistance() {
return true; return true;

View file

@ -2,6 +2,6 @@ package com.jozufozu.flywheel.api.instance;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
public interface IInstance { public interface Instance {
BlockPos getWorldPosition(); BlockPos getWorldPosition();
} }

View file

@ -2,13 +2,13 @@ package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
/** /**
* An interface giving {@link TileEntityInstance}s a hook to have a function called at * An interface giving {@link BlockEntityInstance}s a hook to have a function called at
* the end of every tick. By implementing {@link ITickableInstance}, a {@link TileEntityInstance} * the end of every tick. By implementing {@link TickableInstance}, a {@link BlockEntityInstance}
* can update frequently, but not every frame. * can update frequently, but not every frame.
* <br> There are a few cases in which this should be considered over {@link IDynamicInstance}: * <br> There are a few cases in which this should be considered over {@link DynamicInstance}:
* <ul> * <ul>
* <li> * <li>
* You'd like to change something about the instance every now and then. * You'd like to change something about the instance every now and then.
@ -20,7 +20,7 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
* </li> * </li>
* </ul> * </ul>
*/ */
public interface ITickableInstance extends IInstance { public interface TickableInstance extends Instance {
/** /**
* Called every tick, and after initialization. * Called every tick, and after initialization.
@ -38,7 +38,7 @@ public interface ITickableInstance extends IInstance {
* <br> You might want to opt out of this if you want your animations to remain smooth * <br> You might want to opt out of this if you want your animations to remain smooth
* even when far away from the camera. It is recommended to keep this as is, however. * even when far away from the camera. It is recommended to keep this as is, however.
* *
* @return <code>true</code> if your instance should be slow ticked. * @return {@code true} if your instance should be slow ticked.
*/ */
default boolean decreaseTickRateWithDistance() { default boolean decreaseTickRateWithDistance() {
return true; return true;

View file

@ -55,7 +55,7 @@ public class Backend {
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use. * (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
*/ */
public String getBackendDescriptor() { public String getBackendDescriptor() {
return engine.getProperName(); return engine == null ? "" : engine.getProperName();
} }
public FlwEngine getEngine() { public FlwEngine getEngine() {

View file

@ -3,20 +3,20 @@ package com.jozufozu.flywheel.backend;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider; import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class GameStateRegistry { public class GameStateRegistry {
private static final Map<ResourceLocation, IGameStateProvider> registeredStateProviders = new HashMap<>(); private static final Map<ResourceLocation, GameStateProvider> registeredStateProviders = new HashMap<>();
static void clear() { static void clear() {
registeredStateProviders.clear(); registeredStateProviders.clear();
} }
public static IGameStateProvider getStateProvider(ResourceLocation location) { public static GameStateProvider getStateProvider(ResourceLocation location) {
IGameStateProvider out = registeredStateProviders.get(location); GameStateProvider out = registeredStateProviders.get(location);
if (out == null) { if (out == null) {
throw new IllegalArgumentException("State provider '" + location + "' does not exist."); throw new IllegalArgumentException("State provider '" + location + "' does not exist.");
@ -25,7 +25,7 @@ public class GameStateRegistry {
return out; return out;
} }
public static void register(IGameStateProvider context) { public static void register(GameStateProvider context) {
if (registeredStateProviders.containsKey(context.getID())) { if (registeredStateProviders.containsKey(context.getID())) {
throw new IllegalStateException("Duplicate game state provider: " + context.getID()); throw new IllegalStateException("Duplicate game state provider: " + context.getID());
} }

View file

@ -4,7 +4,11 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Optional; import java.util.Optional;
import java.util.function.BooleanSupplier;
import com.jozufozu.flywheel.util.Lazy;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -15,6 +19,23 @@ public class OptifineHandler {
private static Package optifine; private static Package optifine;
private static OptifineHandler handler; private static OptifineHandler handler;
private static final Lazy<BooleanSupplier> isShadowPass = Lazy.of(() -> {
try {
Class<?> ofShaders = Class.forName("net.optifine.shaders.Shaders");
Field field = ofShaders.getDeclaredField("isShadowPass");
field.setAccessible(true);
return () -> {
try {
return field.getBoolean(null);
} catch (IllegalAccessException ignored) {
return false;
}
};
} catch (Exception ignored) {
return () -> false;
}
});
public final boolean usingShaders; public final boolean usingShaders;
public OptifineHandler(boolean usingShaders) { public OptifineHandler(boolean usingShaders) {
@ -40,6 +61,10 @@ public class OptifineHandler {
.orElse(false); .orElse(false);
} }
public static boolean isShadowPass() {
return isShadowPass.get().getAsBoolean();
}
public static void init() { public static void init() {
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE); optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);

View file

@ -12,10 +12,17 @@ public class GlVertexArray extends GlObject {
setHandle(GlStateManager._glGenVertexArrays()); setHandle(GlStateManager._glGenVertexArrays());
} }
public static void bind(int vao) {
GlStateManager._glBindVertexArray(vao);
BufferUploaderAccessor.flywheel$setLastVAO(vao);
}
public void bind() { public void bind() {
int handle = handle(); bind(handle());
GlStateManager._glBindVertexArray(handle); }
BufferUploaderAccessor.flywheel$setLastVAO(handle);
public static int getBoundVertexArray() {
return BufferUploaderAccessor.flywheel$getLastVAO();
} }
public static void unbind() { public static void unbind() {

View file

@ -50,4 +50,12 @@ public enum GlBufferType {
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(0); case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(0);
} }
} }
public int getBoundBuffer() {
return switch (this.glEnum) {
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$getLastEBO();
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$getLastVBO();
default -> -1;
};
}
} }

View file

@ -4,7 +4,7 @@ import org.lwjgl.opengl.GLCapabilities;
/** /**
* This interface should be implemented by enums such that the * This interface should be implemented by enums such that the
* last defined variant <em>always</em> returns <code>true</code>. * last defined variant <em>always</em> returns {@code true}
*/ */
public interface GlVersioned { public interface GlVersioned {
/** /**

View file

@ -4,15 +4,15 @@ import java.util.Arrays;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.IInstance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.core.materials.FlatLit; import com.jozufozu.flywheel.core.materials.FlatLit;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.ListenerStatus; import com.jozufozu.flywheel.light.ListenerStatus;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -20,9 +20,9 @@ import net.minecraft.world.level.LightLayer;
/** /**
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering. * A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
* Right now, that's only {@link TileInstanceManager}, but there could be an entity equivalent in the future. * Right now, that's only {@link BlockEntityInstanceManager}, but there could be an entity equivalent in the future.
*/ */
public abstract class AbstractInstance implements IInstance, LightListener { public abstract class AbstractInstance implements Instance, LightListener {
protected final MaterialManager materialManager; protected final MaterialManager materialManager;
public final Level world; public final Level world;
@ -48,7 +48,7 @@ public abstract class AbstractInstance implements IInstance, LightListener {
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based. * Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
* Don't query lighting data here, that's handled separately in {@link #updateLight()}. * Don't query lighting data here, that's handled separately in {@link #updateLight()}.
* *
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}. * <br><br> If your animations are complex or more CPU driven, see {@link DynamicInstance} or {@link TickableInstance}.
*/ */
public void update() { public void update() {
} }
@ -65,13 +65,13 @@ public abstract class AbstractInstance implements IInstance, LightListener {
* When an instance is reset, the instance is deleted and re-created. * When an instance is reset, the instance is deleted and re-created.
* *
* <p> * <p>
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked. * Just before {@link #update()} would be called, {@code shouldReset()} is checked.
* If this function returns <code>true</code>, then this instance will be {@link #remove removed}, * If this function returns {@code true}, then this instance will be {@link #remove removed},
* and another instance will be constructed to replace it. This allows for more sane resource * and another instance will be constructed to replace it. This allows for more sane resource
* acquisition compared to trying to update everything within the lifetime of an instance. * acquisition compared to trying to update everything within the lifetime of an instance.
* </p> * </p>
* *
* @return <code>true</code> if this instance should be discarded and refreshed. * @return {@code true} if this instance should be discarded and refreshed.
*/ */
public boolean shouldReset() { public boolean shouldReset() {
return false; return false;

View file

@ -6,7 +6,6 @@ import java.util.function.Supplier;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> { public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> {

View file

@ -0,0 +1,83 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.HashSet;
import java.util.Set;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.BufferBuilder;
import net.minecraft.client.renderer.RenderType;
public class BatchDrawingTracker {
protected final Set<RenderType> activeTypes = new HashSet<>();
private final BufferBuilder scratch;
public BatchDrawingTracker() {
scratch = new BufferBuilder(8);
((BufferBuilderExtension) scratch).flywheel$freeBuffer();
}
/**
* Get a direct vertex consumer for drawing the given number of vertices to the given RenderType.
* @param renderType The RenderType to draw to.
* @param vertexCount The number of vertices that will be drawn.
* @return A direct vertex consumer.
*/
public DirectVertexConsumer getDirectConsumer(RenderType renderType, int vertexCount) {
activeTypes.add(renderType);
return RenderTypeExtension.getDrawBuffer(renderType)
.begin(vertexCount);
}
/**
* Draws all active DrawBuffers and reset them.
*/
public void endBatch() {
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders
// into the RenderBuffers from context.
for (RenderType renderType : activeTypes) {
_draw(renderType);
}
activeTypes.clear();
}
/**
* Draw and reset the DrawBuffer for the given RenderType.
* @param renderType The RenderType to draw.
*/
public void endBatch(RenderType renderType) {
_draw(renderType);
activeTypes.remove(renderType);
}
/**
* Resets all DrawBuffers to 0 vertices.
*/
public void clear() {
for (RenderType type : activeTypes) {
RenderTypeExtension.getDrawBuffer(type)
.reset();
}
activeTypes.clear();
}
private void _draw(RenderType renderType) {
DrawBuffer drawBuffer = RenderTypeExtension.getDrawBuffer(renderType);
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
if (drawBuffer.hasVertices()) {
drawBuffer.inject(scratch);
renderType.end(this.scratch, 0, 0, 0);
drawBuffer.reset();
}
}
}

View file

@ -0,0 +1,77 @@
package com.jozufozu.flywheel.backend.instancing;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
/**
* A byte buffer that can be used to draw vertices through a {@link DirectVertexConsumer}.
*
* The number of vertices needs to be known ahead of time.
*/
public class DrawBuffer {
private final RenderType parent;
private ByteBuffer backingBuffer;
private int expectedVertices;
public DrawBuffer(RenderType parent) {
this.parent = parent;
}
/**
* Creates a direct vertex consumer that can be used to write vertices into this buffer.
* @param vertexCount The number of vertices to reserve memory for.
* @return A direct vertex consumer.
* @throws IllegalStateException If the buffer is already in use.
*/
public DirectVertexConsumer begin(int vertexCount) {
if (expectedVertices != 0) {
throw new IllegalStateException("Already drawing");
}
this.expectedVertices = vertexCount;
VertexFormat format = parent.format();
int byteSize = format.getVertexSize() * vertexCount;
if (backingBuffer == null) {
backingBuffer = MemoryTracker.create(byteSize);
}
if (byteSize > backingBuffer.capacity()) {
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize);
}
return new DirectVertexConsumer(backingBuffer, format, vertexCount);
}
/**
* Injects the backing buffer into the given builder and prepares it for rendering.
* @param bufferBuilder The buffer builder to inject into.
*/
public void inject(BufferBuilderExtension bufferBuilder) {
bufferBuilder.flywheel$injectForRender(backingBuffer, parent.format(), expectedVertices);
}
/**
* @return {@code true} if the buffer has any vertices.
*/
public boolean hasVertices() {
return expectedVertices > 0;
}
/**
* Reset the draw buffer to have no vertices.
*
* Does not clear the backing buffer.
*/
public void reset() {
this.expectedVertices = 0;
}
}

View file

@ -10,8 +10,8 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.light.LightUpdater;
@ -30,8 +30,8 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
private final Set<T> queuedUpdates; private final Set<T> queuedUpdates;
protected final Map<T, AbstractInstance> instances; protected final Map<T, AbstractInstance> instances;
protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances; protected final Object2ObjectOpenHashMap<T, TickableInstance> tickableInstances;
protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances; protected final Object2ObjectOpenHashMap<T, DynamicInstance> dynamicInstances;
protected int frame; protected int frame;
protected int tick; protected int tick;
@ -71,7 +71,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
* Ticks the InstanceManager. * Ticks the InstanceManager.
* *
* <p> * <p>
* {@link ITickableInstance}s get ticked. * {@link TickableInstance}s get ticked.
* <br> * <br>
* Queued updates are processed. * Queued updates are processed.
* </p> * </p>
@ -85,16 +85,16 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
int cY = (int) cameraY; int cY = (int) cameraY;
int cZ = (int) cameraZ; int cZ = (int) cameraZ;
ArrayList<ITickableInstance> instances = new ArrayList<>(tickableInstances.values()); ArrayList<TickableInstance> instances = new ArrayList<>(tickableInstances.values());
int incr = 500; int incr = 500;
int size = instances.size(); int size = instances.size();
int start = 0; int start = 0;
while (start < size) { while (start < size) {
int end = Math.min(start + incr, size); int end = Math.min(start + incr, size);
List<ITickableInstance> sub = instances.subList(start, end); List<TickableInstance> sub = instances.subList(start, end);
taskEngine.submit(() -> { taskEngine.submit(() -> {
for (ITickableInstance instance : sub) { for (TickableInstance instance : sub) {
tickInstance(cX, cY, cZ, instance); tickInstance(cX, cY, cZ, instance);
} }
}); });
@ -103,7 +103,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
} }
} }
private void tickInstance(int cX, int cY, int cZ, ITickableInstance instance) { private void tickInstance(int cX, int cY, int cZ, TickableInstance instance) {
if (!instance.decreaseTickRateWithDistance()) { if (!instance.decreaseTickRateWithDistance()) {
instance.tick(); instance.tick();
return; return;
@ -132,16 +132,16 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
int cY = (int) info.getPosition().y; int cY = (int) info.getPosition().y;
int cZ = (int) info.getPosition().z; int cZ = (int) info.getPosition().z;
ArrayList<IDynamicInstance> instances = new ArrayList<>(dynamicInstances.values()); ArrayList<DynamicInstance> instances = new ArrayList<>(dynamicInstances.values());
int incr = 500; int incr = 500;
int size = instances.size(); int size = instances.size();
int start = 0; int start = 0;
while (start < size) { while (start < size) {
int end = Math.min(start + incr, size); int end = Math.min(start + incr, size);
List<IDynamicInstance> sub = instances.subList(start, end); List<DynamicInstance> sub = instances.subList(start, end);
taskEngine.submit(() -> { taskEngine.submit(() -> {
for (IDynamicInstance dyn : sub) { for (DynamicInstance dyn : sub) {
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ)) if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame(); dyn.beginFrame();
} }
@ -179,8 +179,8 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
* *
* <p> * <p>
* By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency * By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency
* update hook IInstance gets. For more frequent updates, see {@link ITickableInstance} and * update hook IInstance gets. For more frequent updates, see {@link TickableInstance} and
* {@link IDynamicInstance}. * {@link DynamicInstance}.
* </p> * </p>
* *
* @param obj the object to update. * @param obj the object to update.
@ -312,12 +312,12 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
.addListener(renderer); .addListener(renderer);
instances.put(obj, renderer); instances.put(obj, renderer);
if (renderer instanceof ITickableInstance r) { if (renderer instanceof TickableInstance r) {
tickableInstances.put(obj, r); tickableInstances.put(obj, r);
r.tick(); r.tick();
} }
if (renderer instanceof IDynamicInstance r) { if (renderer instanceof DynamicInstance r) {
dynamicInstances.put(obj, r); dynamicInstances.put(obj, r);
r.beginFrame(); r.beginFrame();
} }
@ -328,9 +328,9 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
@Override @Override
public void onOriginShift() { public void onOriginShift() {
ArrayList<T> instancedTiles = new ArrayList<>(instances.keySet()); ArrayList<T> instanced = new ArrayList<>(instances.keySet());
invalidate(); invalidate();
instancedTiles.forEach(this::add); instanced.forEach(this::add);
} }
public void detachLightListeners() { public void detachLightListeners() {

View file

@ -1,12 +1,12 @@
package com.jozufozu.flywheel.backend.instancing; package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine; import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.config.FlwEngine; import com.jozufozu.flywheel.config.FlwEngine;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.WorldProgram;
@ -29,7 +29,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class InstanceWorld { public class InstanceWorld {
protected final Engine engine; protected final Engine engine;
protected final InstanceManager<Entity> entityInstanceManager; protected final InstanceManager<Entity> entityInstanceManager;
protected final InstanceManager<BlockEntity> tileEntityInstanceManager; protected final InstanceManager<BlockEntity> blockEntityInstanceManager;
protected final ParallelTaskEngine taskEngine; protected final ParallelTaskEngine taskEngine;
@ -48,16 +48,16 @@ public class InstanceWorld {
.build(); .build();
entityInstanceManager = new EntityInstanceManager(manager); entityInstanceManager = new EntityInstanceManager(manager);
tileEntityInstanceManager = new TileInstanceManager(manager); blockEntityInstanceManager = new BlockEntityInstanceManager(manager);
manager.addListener(entityInstanceManager); manager.addListener(entityInstanceManager);
manager.addListener(tileEntityInstanceManager); manager.addListener(blockEntityInstanceManager);
this.engine = manager; this.engine = manager;
} }
case BATCHING -> { case BATCHING -> {
this.engine = new BatchingEngine(); this.engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(this.engine); entityInstanceManager = new EntityInstanceManager(this.engine);
tileEntityInstanceManager = new TileInstanceManager(this.engine); blockEntityInstanceManager = new BlockEntityInstanceManager(this.engine);
} }
default -> throw new IllegalArgumentException("Unknown engine type"); default -> throw new IllegalArgumentException("Unknown engine type");
} }
@ -67,8 +67,8 @@ public class InstanceWorld {
return entityInstanceManager; return entityInstanceManager;
} }
public InstanceManager<BlockEntity> getTileEntityInstanceManager() { public InstanceManager<BlockEntity> getBlockEntityInstanceManager() {
return tileEntityInstanceManager; return blockEntityInstanceManager;
} }
/** /**
@ -78,7 +78,7 @@ public class InstanceWorld {
this.taskEngine.stopWorkers(); this.taskEngine.stopWorkers();
engine.delete(); engine.delete();
entityInstanceManager.detachLightListeners(); entityInstanceManager.detachLightListeners();
tileEntityInstanceManager.detachLightListeners(); blockEntityInstanceManager.detachLightListeners();
} }
/** /**
@ -86,22 +86,22 @@ public class InstanceWorld {
* <p> * <p>
* Check and shift the origin coordinate. * Check and shift the origin coordinate.
* <br> * <br>
* Call {@link IDynamicInstance#beginFrame()} on all instances in this world. * Call {@link DynamicInstance#beginFrame()} on all instances in this world.
* </p> * </p>
*/ */
public void beginFrame(BeginFrameEvent event) { public void beginFrame(BeginFrameEvent event) {
engine.beginFrame(event.getInfo()); engine.beginFrame(event.getCamera());
taskEngine.syncPoint(); taskEngine.syncPoint();
tileEntityInstanceManager.beginFrame(taskEngine, event.getInfo()); blockEntityInstanceManager.beginFrame(taskEngine, event.getCamera());
entityInstanceManager.beginFrame(taskEngine, event.getInfo()); entityInstanceManager.beginFrame(taskEngine, event.getCamera());
} }
/** /**
* Tick the renderers after the game has ticked: * Tick the renderers after the game has ticked:
* <p> * <p>
* Call {@link ITickableInstance#tick()} on all instances in this world. * Call {@link TickableInstance#tick()} on all instances in this world.
* </p> * </p>
*/ */
public void tick() { public void tick() {
@ -110,7 +110,7 @@ public class InstanceWorld {
if (renderViewEntity == null) return; if (renderViewEntity == null) return;
tileEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); blockEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
} }

View file

@ -26,13 +26,13 @@ public class InstancedRenderDispatcher {
/** /**
* Call this when you want to manually run {@link AbstractInstance#update()}. * Call this when you want to manually run {@link AbstractInstance#update()}.
* @param te The tile whose instance you want to update. * @param blockEntity The block entity whose instance you want to update.
*/ */
public static void enqueueUpdate(BlockEntity te) { public static void enqueueUpdate(BlockEntity blockEntity) {
if (Backend.isOn() && te.hasLevel() && te.getLevel() instanceof ClientLevel) { if (Backend.isOn() && blockEntity.hasLevel() && blockEntity.getLevel() instanceof ClientLevel) {
instanceWorlds.get(te.getLevel()) instanceWorlds.get(blockEntity.getLevel())
.getTileEntityInstanceManager() .getBlockEntityInstanceManager()
.queueUpdate(te); .queueUpdate(blockEntity);
} }
} }
@ -48,10 +48,10 @@ public class InstancedRenderDispatcher {
} }
} }
public static InstanceManager<BlockEntity> getTiles(LevelAccessor world) { public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor world) {
if (Backend.isOn()) { if (Backend.isOn()) {
return instanceWorlds.get(world) return instanceWorlds.get(world)
.getTileEntityInstanceManager(); .getBlockEntityInstanceManager();
} else { } else {
throw new NullPointerException("Backend is off, cannot retrieve instance world."); throw new NullPointerException("Backend is off, cannot retrieve instance world.");
} }

View file

@ -1,134 +1,304 @@
package com.jozufozu.flywheel.backend.instancing; package com.jozufozu.flywheel.backend.instancing;
import java.util.Map; import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.collect.Maps;
import com.jozufozu.flywheel.api.FlywheelRendered;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstancingController;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityTypeExtension;
import com.jozufozu.flywheel.backend.instancing.blockentity.SimpleBlockEntityInstancingController;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.backend.instancing.entity.IEntityInstanceFactory; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstancingController;
import com.jozufozu.flywheel.backend.instancing.tile.ITileInstanceFactory; import com.jozufozu.flywheel.backend.instancing.entity.EntityTypeExtension;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.entity.SimpleEntityInstancingController;
import it.unimi.dsi.fastutil.objects.Object2BooleanLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
/**
* A utility class for registering and retrieving {@code InstancingController}s.
*/
@SuppressWarnings("unchecked")
public class InstancedRenderRegistry { public class InstancedRenderRegistry {
private static final InstancedRenderRegistry INSTANCE = new InstancedRenderRegistry(); /**
* Checks if the given block entity type can be instanced.
public static InstancedRenderRegistry getInstance() { * @param type The block entity type to check.
return INSTANCE; * @param <T> The type of the block entity.
* @return {@code true} if the block entity type can be instanced.
*/
public static <T extends BlockEntity> boolean canInstance(BlockEntityType<? extends T> type) {
return getController(type) != null;
} }
private final Object2BooleanMap<Object> skipRender = new Object2BooleanLinkedOpenHashMap<>(); /**
private final Map<BlockEntityType<?>, ITileInstanceFactory<?>> tiles = Maps.newHashMap(); * Checks if the given entity type can be instanced.
private final Map<EntityType<?>, IEntityInstanceFactory<?>> entities = Maps.newHashMap(); * @param type The entity type to check.
* @param <T> The type of the entity.
protected InstancedRenderRegistry() { * @return {@code true} if the entity type can be instanced.
skipRender.defaultReturnValue(false); */
public static <T extends Entity> boolean canInstance(EntityType<? extends T> type) {
return getController(type) != null;
} }
public <T extends BlockEntity> boolean shouldSkipRender(T type) { /**
return _skipRender(type.getType()) || ((type instanceof FlywheelRendered) && !((FlywheelRendered) type).shouldRenderNormally()); * Creates an instance for the given block entity, if possible.
* @param materialManager The material manager to use.
* @param blockEntity The block entity to create an instance of.
* @param <T> The type of the block entity.
* @return An instance of the block entity, or {@code null} if the block entity cannot be instanced.
*/
@Nullable
public static <T extends BlockEntity> BlockEntityInstance<? super T> createInstance(MaterialManager materialManager, T blockEntity) {
BlockEntityInstancingController<? super T> controller = getController(getType(blockEntity));
if (controller == null) {
return null;
}
return controller.createInstance(materialManager, blockEntity);
} }
public <T extends Entity> boolean shouldSkipRender(T type) { /**
return _skipRender(type.getType()) || ((type instanceof FlywheelRendered) && !((FlywheelRendered) type).shouldRenderNormally()); * Creates an instance for the given entity, if possible.
* @param materialManager The material manager to use.
* @param entity The entity to create an instance of.
* @param <T> The type of the entity.
* @return An instance of the entity, or {@code null} if the entity cannot be instanced.
*/
@Nullable
public static <T extends Entity> EntityInstance<? super T> createInstance(MaterialManager materialManager, T entity) {
EntityInstancingController<? super T> controller = getController(getType(entity));
if (controller == null) {
return null;
}
return controller.createInstance(materialManager, entity);
} }
public <T extends BlockEntity> boolean canInstance(BlockEntityType<? extends T> type) { /**
return tiles.containsKey(type); * Checks if the given block entity is instanced and should not be rendered normally.
* @param blockEntity The block entity to check.
* @param <T> The type of the block entity.
* @return {@code true} if the block entity is instanced and should not be rendered normally.
*/
public static <T extends BlockEntity> boolean shouldSkipRender(T blockEntity) {
BlockEntityInstancingController<? super T> controller = getController(getType(blockEntity));
if (controller == null) {
return false;
}
return controller.shouldSkipRender(blockEntity);
} }
public <T extends Entity> boolean canInstance(EntityType<? extends T> type) { /**
return entities.containsKey(type); * Checks if the given entity is instanced and should not be rendered normally.
* @param entity The entity to check.
* @param <T> The type of the entity.
* @return {@code true} if the entity is instanced and should not be rendered normally.
*/
public static <T extends Entity> boolean shouldSkipRender(T entity) {
EntityInstancingController<? super T> controller = getController(getType(entity));
if (controller == null) {
return false;
}
return controller.shouldSkipRender(entity);
} }
public <T extends BlockEntity> TileConfig<? extends T> tile(BlockEntityType<? extends T> type) { /**
return new TileConfig<>(type); * Get an object to configure the instancing controller for the given block entity type.
* @param type The block entity type to configure.
* @param <T> The type of the block entity.
* @return The configuration object.
*/
public static <T extends BlockEntity> BlockEntityConfig<T> configure(BlockEntityType<T> type) {
return new BlockEntityConfig<>(type);
} }
public <T extends Entity> EntityConfig<? extends T> entity(EntityType<? extends T> type) { /**
* Get an object to configure the instancing controller for the given entity type.
* @param type The entity type to configure.
* @param <T> The type of the entity.
* @return The configuration object.
*/
public static <T extends Entity> EntityConfig<T> configure(EntityType<T> type) {
return new EntityConfig<>(type); return new EntityConfig<>(type);
} }
@SuppressWarnings("unchecked") /**
* Gets the instancing controller for the given block entity type, if one exists.
* @param type The block entity type to get the instancing controller for.
* @param <T> The type of the block entity.
* @return The instancing controller for the given block entity type, or {@code null} if none exists.
*/
@Nullable @Nullable
public <T extends BlockEntity> TileEntityInstance<? super T> create(MaterialManager manager, T tile) { public static <T extends BlockEntity> BlockEntityInstancingController<? super T> getController(BlockEntityType<T> type) {
BlockEntityType<?> type = tile.getType(); return ((BlockEntityTypeExtension<T>) type).flywheel$getInstancingController();
ITileInstanceFactory<? super T> factory = (ITileInstanceFactory<? super T>) this.tiles.get(type);
if (factory == null) return null;
else return factory.create(manager, tile);
} }
/**
@SuppressWarnings("unchecked") * Gets the instancing controller for the given entity type, if one exists.
* @param type The entity type to get the instancing controller for.
* @param <T> The type of the entity.
* @return The instancing controller for the given entity type, or {@code null} if none exists.
*/
@Nullable @Nullable
public <T extends Entity> EntityInstance<? super T> create(MaterialManager manager, T tile) { public static <T extends Entity> EntityInstancingController<? super T> getController(EntityType<T> type) {
EntityType<?> type = tile.getType(); return ((EntityTypeExtension<T>) type).flywheel$getInstancingController();
IEntityInstanceFactory<? super T> factory = (IEntityInstanceFactory<? super T>) this.entities.get(type);
if (factory == null) return null;
else return factory.create(manager, tile);
} }
private boolean _skipRender(Object o) { /**
return skipRender.getBoolean(o); * Sets the instancing controller for the given block entity type.
* @param type The block entity type to set the instancing controller for.
* @param instancingController The instancing controller to set.
* @param <T> The type of the block entity.
*/
public static <T extends BlockEntity> void setController(BlockEntityType<T> type, BlockEntityInstancingController<? super T> instancingController) {
((BlockEntityTypeExtension<T>) type).flywheel$setInstancingController(instancingController);
} }
public interface Config<CONFIG extends Config<CONFIG, FACTORY>, FACTORY> { /**
* Sets the instancing controller for the given entity type.
CONFIG factory(FACTORY rendererFactory); * @param type The entity type to set the instancing controller for.
* @param instancingController The instancing controller to set.
CONFIG setSkipRender(boolean skipRender); * @param <T> The type of the entity.
*/
public static <T extends Entity> void setController(EntityType<T> type, EntityInstancingController<? super T> instancingController) {
((EntityTypeExtension<T>) type).flywheel$setInstancingController(instancingController);
} }
public class TileConfig<T extends BlockEntity> implements Config<TileConfig<T>, ITileInstanceFactory<? super T>> { /**
* Gets the type of the given block entity.
* @param blockEntity The block entity to get the type of.
* @param <T> The type of the block entity.
* @return The {@link BlockEntityType} associated with the given block entity.
*/
public static <T extends BlockEntity> BlockEntityType<? super T> getType(T blockEntity) {
return (BlockEntityType<? super T>) blockEntity.getType();
}
private final BlockEntityType<T> type; /**
* Gets the type of the given entity.
* @param entity The entity to get the type of.
* @param <T> The type of the entity.
* @return The {@link EntityType} associated with the given entity.
*/
public static <T extends Entity> EntityType<? super T> getType(T entity) {
return (EntityType<? super T>) entity.getType();
}
public TileConfig(BlockEntityType<T> type) { /**
* An object to configure the instancing controller for a block entity.
* @param <T> The type of the block entity.
*/
public static class BlockEntityConfig<T extends BlockEntity> {
protected BlockEntityType<T> type;
protected BiFunction<MaterialManager, T, BlockEntityInstance<? super T>> instanceFactory;
protected Predicate<T> skipRender;
public BlockEntityConfig(BlockEntityType<T> type) {
this.type = type; this.type = type;
} }
public TileConfig<T> factory(ITileInstanceFactory<? super T> rendererFactory) { /**
tiles.put(type, rendererFactory); * Sets the instance factory for the block entity.
* @param instanceFactory The instance factory.
* @return {@code this}
*/
public BlockEntityConfig<T> factory(BiFunction<MaterialManager, T, BlockEntityInstance<? super T>> instanceFactory) {
this.instanceFactory = instanceFactory;
return this; return this;
} }
public TileConfig<T> setSkipRender(boolean skipRender) { /**
InstancedRenderRegistry.this.skipRender.put(type, skipRender); * Sets a predicate to determine whether to skip rendering a block entity.
* @param skipRender The predicate.
* @return {@code this}
*/
public BlockEntityConfig<T> skipRender(Predicate<T> skipRender) {
this.skipRender = skipRender;
return this; return this;
} }
/**
* Sets a predicate to always skip rendering for block entities of this type.
* @return {@code this}
*/
public BlockEntityConfig<T> alwaysSkipRender() {
this.skipRender = be -> true;
return this;
} }
public class EntityConfig<T extends Entity> implements Config<EntityConfig<T>, IEntityInstanceFactory<? super T>> { /**
* Constructs the block entity instancing controller, and sets it for the block entity type.
* @return The block entity instancing controller.
*/
public SimpleBlockEntityInstancingController<T> apply() {
Objects.requireNonNull(instanceFactory, "Instance factory cannot be null!");
if (skipRender == null) {
skipRender = be -> false;
}
SimpleBlockEntityInstancingController<T> controller = new SimpleBlockEntityInstancingController<>(instanceFactory, skipRender);
setController(type, controller);
return controller;
}
}
private final EntityType<T> type; /**
* An object to configure the instancing controller for an entity.
* @param <T> The type of the entity.
*/
public static class EntityConfig<T extends Entity> {
protected EntityType<T> type;
protected BiFunction<MaterialManager, T, EntityInstance<? super T>> instanceFactory;
protected Predicate<T> skipRender;
public EntityConfig(EntityType<T> type) { public EntityConfig(EntityType<T> type) {
this.type = type; this.type = type;
} }
public EntityConfig<T> factory(IEntityInstanceFactory<? super T> rendererFactory) { /**
entities.put(type, rendererFactory); * Sets the instance factory for the entity.
* @param instanceFactory The instance factory.
* @return {@code this}
*/
public EntityConfig<T> factory(BiFunction<MaterialManager, T, EntityInstance<? super T>> instanceFactory) {
this.instanceFactory = instanceFactory;
return this; return this;
} }
public EntityConfig<T> setSkipRender(boolean skipRender) { /**
InstancedRenderRegistry.this.skipRender.put(type, skipRender); * Sets a predicate to determine whether to skip rendering an entity.
* @param skipRender The predicate.
* @return {@code this}
*/
public EntityConfig<T> skipRender(Predicate<T> skipRender) {
this.skipRender = skipRender;
return this; return this;
} }
/**
* Sets a predicate to always skip rendering for entities of this type.
* @return {@code this}
*/
public EntityConfig<T> alwaysSkipRender() {
this.skipRender = entity -> true;
return this;
} }
/**
* Constructs the entity instancing controller, and sets it for the entity type.
* @return The entity instancing controller.
*/
public SimpleEntityInstancingController<T> apply() {
Objects.requireNonNull(instanceFactory, "Instance factory cannot be null!");
if (skipRender == null) {
skipRender = entity -> false;
}
SimpleEntityInstancingController<T> controller = new SimpleEntityInstancingController<>(instanceFactory, skipRender);
setController(type, controller);
return controller;
}
}
} }

View file

@ -0,0 +1,25 @@
package com.jozufozu.flywheel.backend.instancing;
import net.minecraft.client.renderer.RenderType;
/**
* Duck interface to make RenderType store a DrawBuffer.
*
* @see RenderType
*/
public interface RenderTypeExtension {
/**
* @return The DrawBuffer associated with this RenderType.
*/
DrawBuffer flywheel$getDrawBuffer();
/**
* Helper function to cast a RenderType to a RenderTypeExtension and get its DrawBuffer.
* @param type The RenderType to get the DrawBuffer from.
* @return The DrawBuffer associated with the given RenderType.
*/
static DrawBuffer getDrawBuffer(RenderType type) {
return ((RenderTypeExtension) type).flywheel$getDrawBuffer();
}
}

View file

@ -1,79 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.model.BufferBuilderHack;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
public class SuperBufferSource {
protected final Map<RenderType, DrawBuffer> buffers = new HashMap<>();
private final BufferBuilder scratch;
public SuperBufferSource() {
scratch = new BufferBuilder(8);
((BufferBuilderHack) scratch).flywheel$freeBuffer();
}
public DirectVertexConsumer getBuffer(RenderType renderType, int vertexCount) {
return buffers.computeIfAbsent(renderType, DrawBuffer::new)
.begin(vertexCount);
}
public void endBatch() {
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders
// into the RenderBuffers from context.
BufferBuilderHack hack = (BufferBuilderHack) scratch;
for (Map.Entry<RenderType, DrawBuffer> entry : buffers.entrySet()) {
DrawBuffer builder = entry.getValue();
if (builder.expectedVertices > 0) {
RenderType type = entry.getKey();
hack.flywheel$hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices);
type.end(scratch, 0, 0, 0);
builder.expectedVertices = 0;
}
}
}
private static class DrawBuffer {
private final RenderType type;
private ByteBuffer backingBuffer;
private int expectedVertices;
public DrawBuffer(RenderType type) {
this.type = type;
}
public DirectVertexConsumer begin(int vertexCount) {
this.expectedVertices = vertexCount;
VertexFormat format = type.format();
int byteSize = format.getVertexSize() * vertexCount;
if (backingBuffer == null) {
backingBuffer = MemoryTracker.create(byteSize);
} if (byteSize > backingBuffer.capacity()) {
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize);
}
return new DirectVertexConsumer(backingBuffer, format, vertexCount);
}
}
}

View file

@ -7,7 +7,8 @@ import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -34,7 +35,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
} }
} }
public void render(PoseStack stack, SuperBufferSource source, TaskEngine pool) { public void render(PoseStack stack, BatchDrawingTracker source, TaskEngine pool) {
int vertexCount = 0; int vertexCount = 0;
for (BatchedMaterial<?> material : materials.values()) { for (BatchedMaterial<?> material : materials.values()) {
@ -44,14 +45,14 @@ public class BatchedMaterialGroup implements MaterialGroup {
} }
} }
DirectVertexConsumer consumer = source.getBuffer(state, vertexCount); DirectVertexConsumer consumer = source.getDirectConsumer(state, vertexCount);
// avoids rendering garbage, but doesn't fix the issue of some instances not being buffered // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
consumer.memSetZero(); consumer.memSetZero();
for (BatchedMaterial<?> material : materials.values()) { for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) { for (CPUInstancer<?> instancer : material.models.values()) {
instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay(); instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.usingShaders();
instancer.submitTasks(stack, pool, consumer); instancer.submitTasks(stack, pool, consumer);
} }
} }

View file

@ -6,18 +6,14 @@ import java.util.Map;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.event.RenderLayerEvent; import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
@ -25,7 +21,7 @@ import net.minecraft.core.Vec3i;
public class BatchingEngine implements Engine { public class BatchingEngine implements Engine {
private final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers; private final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
private final SuperBufferSource superBufferSource = new SuperBufferSource(); private final BatchDrawingTracker batchTracker = new BatchDrawingTracker();
public BatchingEngine() { public BatchingEngine() {
this.layers = new EnumMap<>(RenderLayer.class); this.layers = new EnumMap<>(RenderLayer.class);
@ -47,14 +43,11 @@ public class BatchingEngine implements Engine {
@Override @Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
Map<RenderType, BatchedMaterialGroup> groups = layers.get(event.getLayer()); Map<RenderType, BatchedMaterialGroup> groups = layers.get(event.getLayer());
for (BatchedMaterialGroup group : groups.values()) { for (BatchedMaterialGroup group : groups.values()) {
group.render(event.stack, superBufferSource, taskEngine); group.render(event.stack, batchTracker, taskEngine);
} }
taskEngine.syncPoint();
// FIXME: this probably breaks some vanilla stuff but it works much better for flywheel // FIXME: this probably breaks some vanilla stuff but it works much better for flywheel
Matrix4f mat = new Matrix4f(); Matrix4f mat = new Matrix4f();
mat.setIdentity(); mat.setIdentity();
@ -64,7 +57,8 @@ public class BatchingEngine implements Engine {
Lighting.setupLevel(mat); Lighting.setupLevel(mat);
} }
superBufferSource.endBatch(); taskEngine.syncPoint();
batchTracker.endBatch();
} }
@Override @Override

View file

@ -1,9 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.tile; package com.jozufozu.flywheel.backend.instancing.blockentity;
import com.jozufozu.flywheel.api.Material; import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.model.ModelData;
@ -22,46 +22,46 @@ import net.minecraft.world.level.block.state.BlockState;
* *
* <br><br> There are a few additional features that overriding classes can opt in to: * <br><br> There are a few additional features that overriding classes can opt in to:
* <ul> * <ul>
* <li>{@link IDynamicInstance}</li> * <li>{@link DynamicInstance}</li>
* <li>{@link ITickableInstance}</li> * <li>{@link TickableInstance}</li>
* </ul> * </ul>
* See the interfaces' documentation for more information about each one. * See the interfaces' documentation for more information about each one.
* *
* <br> Implementing one or more of these will give a {@link TileEntityInstance} access * <br> Implementing one or more of these will give a {@link BlockEntityInstance} access
* to more interesting and regular points within a tick or a frame. * to more interesting and regular points within a tick or a frame.
* *
* @param <T> The type of {@link BlockEntity} your class is an instance of. * @param <T> The type of {@link BlockEntity} your class is an instance of.
*/ */
public abstract class TileEntityInstance<T extends BlockEntity> extends AbstractInstance { public abstract class BlockEntityInstance<T extends BlockEntity> extends AbstractInstance {
protected final T tile; protected final T blockEntity;
protected final BlockPos pos; protected final BlockPos pos;
protected final BlockPos instancePos; protected final BlockPos instancePos;
protected final BlockState blockState; protected final BlockState blockState;
public TileEntityInstance(MaterialManager materialManager, T tile) { public BlockEntityInstance(MaterialManager materialManager, T blockEntity) {
super(materialManager, tile.getLevel()); super(materialManager, blockEntity.getLevel());
this.tile = tile; this.blockEntity = blockEntity;
this.pos = tile.getBlockPos(); this.pos = blockEntity.getBlockPos();
this.blockState = tile.getBlockState(); this.blockState = blockEntity.getBlockState();
this.instancePos = pos.subtract(materialManager.getOriginCoordinate()); this.instancePos = pos.subtract(materialManager.getOriginCoordinate());
} }
/** /**
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked. * Just before {@link #update()} would be called, {@code shouldReset()} is checked.
* If this function returns <code>true</code>, then this instance will be {@link #remove removed}, * If this function returns {@code true}, then this instance will be {@link #remove removed},
* and another instance will be constructed to replace it. This allows for more sane resource * and another instance will be constructed to replace it. This allows for more sane resource
* acquisition compared to trying to update everything within the lifetime of an instance. * acquisition compared to trying to update everything within the lifetime of an instance.
* *
* @return <code>true</code> if this instance should be discarded and refreshed. * @return {@code true} if this instance should be discarded and refreshed.
*/ */
public boolean shouldReset() { public boolean shouldReset() {
return tile.getBlockState() != blockState; return blockEntity.getBlockState() != blockState;
} }
/** /**
* In order to accommodate for floating point precision errors at high coordinates, * In order to accommodate for floating point precision errors at high coordinates,
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and * {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly. * shift the world matrix provided as a shader uniform accordingly.
* *
* @return The {@link BlockPos position} of the {@link BlockEntity} this instance * @return The {@link BlockPos position} of the {@link BlockEntity} this instance

View file

@ -1,46 +1,44 @@
package com.jozufozu.flywheel.backend.instancing.tile; package com.jozufozu.flywheel.backend.instancing.blockentity;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
public class TileInstanceManager extends InstanceManager<BlockEntity> { public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
public TileInstanceManager(MaterialManager materialManager) { public BlockEntityInstanceManager(MaterialManager materialManager) {
super(materialManager); super(materialManager);
} }
@Override @Override
protected boolean canInstance(BlockEntity obj) { protected boolean canInstance(BlockEntity obj) {
return obj != null && InstancedRenderRegistry.getInstance().canInstance(obj.getType()); return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
} }
@Override @Override
protected AbstractInstance createRaw(BlockEntity obj) { protected AbstractInstance createRaw(BlockEntity obj) {
return InstancedRenderRegistry.getInstance() return InstancedRenderRegistry.createInstance(materialManager, obj);
.create(materialManager, obj);
} }
@Override @Override
protected boolean canCreateInstance(BlockEntity tile) { protected boolean canCreateInstance(BlockEntity blockEntity) {
if (tile.isRemoved()) return false; if (blockEntity.isRemoved()) return false;
Level world = tile.getLevel(); Level world = blockEntity.getLevel();
if (world == null) return false; if (world == null) return false;
if (world.isEmptyBlock(tile.getBlockPos())) return false; if (world.isEmptyBlock(blockEntity.getBlockPos())) return false;
if (Backend.isFlywheelWorld(world)) { if (Backend.isFlywheelWorld(world)) {
BlockPos pos = tile.getBlockPos(); BlockPos pos = blockEntity.getBlockPos();
BlockGetter existingChunk = world.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4); BlockGetter existingChunk = world.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4);

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.backend.instancing.blockentity;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* An instancing controller that will be keyed to a block entity type.
* @param <T> The type of block entity this controller is for.
*/
public interface BlockEntityInstancingController<T extends BlockEntity> {
/**
* Given a block entity and a material manager, constructs an instance for the block entity.
* @param materialManager The material manager to use.
* @param blockEntity The block entity to construct an instance for.
* @return The instance.
*/
BlockEntityInstance<? super T> createInstance(MaterialManager materialManager, T blockEntity);
/**
* Checks if the given block entity should not be rendered normally.
* @param blockEntity The block entity to check.
* @return {@code true} if the block entity should not be rendered normally, {@code false} if it should.
*/
boolean shouldSkipRender(T blockEntity);
}

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.backend.instancing.blockentity;
import javax.annotation.Nullable;
import net.minecraft.world.level.block.entity.BlockEntity;
public interface BlockEntityTypeExtension<T extends BlockEntity> {
@Nullable
BlockEntityInstancingController<? super T> flywheel$getInstancingController();
void flywheel$setInstancingController(@Nullable BlockEntityInstancingController<? super T> instancingController);
}

View file

@ -0,0 +1,28 @@
package com.jozufozu.flywheel.backend.instancing.blockentity;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.level.block.entity.BlockEntity;
public class SimpleBlockEntityInstancingController<T extends BlockEntity> implements BlockEntityInstancingController<T> {
protected BiFunction<MaterialManager, T, BlockEntityInstance<? super T>> instanceFactory;
protected Predicate<T> skipRender;
public SimpleBlockEntityInstancingController(BiFunction<MaterialManager, T, BlockEntityInstance<? super T>> instanceFactory, Predicate<T> skipRender) {
this.instanceFactory = instanceFactory;
this.skipRender = skipRender;
}
@Override
public BlockEntityInstance<? super T> createInstance(MaterialManager materialManager, T blockEntity) {
return instanceFactory.apply(materialManager, blockEntity);
}
@Override
public boolean shouldSkipRender(T blockEntity) {
return skipRender.test(blockEntity);
}
}

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.instancing.tile; package com.jozufozu.flywheel.backend.instancing.blockentity;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,14 +1,14 @@
package com.jozufozu.flywheel.backend.instancing.entity; package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.light.LightListener; import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.LightProvider; import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.MovingListener; import com.jozufozu.flywheel.light.MovingListener;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -24,8 +24,8 @@ import net.minecraft.world.phys.Vec3;
* * * *
* <br><br> There are a few additional features that overriding classes can opt in to: * <br><br> There are a few additional features that overriding classes can opt in to:
* <ul> * <ul>
* <li>{@link IDynamicInstance}</li> * <li>{@link DynamicInstance}</li>
* <li>{@link ITickableInstance}</li> * <li>{@link TickableInstance}</li>
* </ul> * </ul>
* See the interfaces' documentation for more information about each one. * See the interfaces' documentation for more information about each one.
* *
@ -65,7 +65,7 @@ public abstract class EntityInstance<E extends Entity> extends AbstractInstance
/** /**
* In order to accommodate for floating point precision errors at high coordinates, * In order to accommodate for floating point precision errors at high coordinates,
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and * {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly. * shift the world matrix provided as a shader uniform accordingly.
* *
* @return The position this instance should be rendered at to appear in the correct location. * @return The position this instance should be rendered at to appear in the correct location.
@ -78,7 +78,7 @@ public abstract class EntityInstance<E extends Entity> extends AbstractInstance
/** /**
* In order to accommodate for floating point precision errors at high coordinates, * In order to accommodate for floating point precision errors at high coordinates,
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and * {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
* shift the world matrix provided as a shader uniform accordingly. * shift the world matrix provided as a shader uniform accordingly.
* *
* @return The position this instance should be rendered at to appear in the correct location. * @return The position this instance should be rendered at to appear in the correct location.

View file

@ -5,7 +5,6 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@ -20,13 +19,12 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
@Override @Override
protected boolean canInstance(Entity obj) { protected boolean canInstance(Entity obj) {
return obj != null && InstancedRenderRegistry.getInstance().canInstance(obj.getType()); return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
} }
@Override @Override
protected AbstractInstance createRaw(Entity obj) { protected AbstractInstance createRaw(Entity obj) {
return InstancedRenderRegistry.getInstance() return InstancedRenderRegistry.createInstance(materialManager, obj);
.create(materialManager, obj);
} }
@Override @Override

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.entity.Entity;
/**
* An instancing controller that will be keyed to an entity type.
* @param <T> The entity type.
*/
public interface EntityInstancingController<T extends Entity> {
/**
* Given an entity and a material manager, constructs an instance for the entity.
* @param materialManager The material manager to use.
* @param entity The entity to construct an instance for.
* @return The instance.
*/
EntityInstance<? super T> createInstance(MaterialManager materialManager, T entity);
/**
* Checks if the given entity should not render normally.
* @param entity The entity to check.
* @return {@code true} if the entity should not render normally, {@code false} if it should.
*/
boolean shouldSkipRender(T entity);
}

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
public interface EntityTypeExtension<T extends Entity> {
@Nullable
EntityInstancingController<? super T> flywheel$getInstancingController();
void flywheel$setInstancingController(@Nullable EntityInstancingController<? super T> instancingController);
}

View file

@ -1,10 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.entity.Entity;
@FunctionalInterface
public interface IEntityInstanceFactory<E extends Entity> {
EntityInstance<? super E> create(MaterialManager manager, E te);
}

View file

@ -0,0 +1,28 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.entity.Entity;
public class SimpleEntityInstancingController<T extends Entity> implements EntityInstancingController<T> {
protected BiFunction<MaterialManager, T, EntityInstance<? super T>> instanceFactory;
protected Predicate<T> skipRender;
public SimpleEntityInstancingController(BiFunction<MaterialManager, T, EntityInstance<? super T>> instanceFactory, Predicate<T> skipRender) {
this.instanceFactory = instanceFactory;
this.skipRender = skipRender;
}
@Override
public EntityInstance<? super T> createInstance(MaterialManager materialManager, T entity) {
return instanceFactory.apply(materialManager, entity);
}
@Override
public boolean shouldSkipRender(T entity) {
return skipRender.test(entity);
}
}

View file

@ -77,6 +77,10 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
*/ */
@Override @Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
int ebo = GlBufferType.ELEMENT_ARRAY_BUFFER.getBoundBuffer();
int vbo = GlBufferType.ARRAY_BUFFER.getBoundBuffer();
int vao = GlVertexArray.getBoundVertexArray();
double camX; double camX;
double camY; double camY;
double camZ; double camZ;
@ -97,9 +101,9 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ, event.getLayer())); getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ, event.getLayer()));
GlBufferType.ELEMENT_ARRAY_BUFFER.unbind(); GlBufferType.ELEMENT_ARRAY_BUFFER.bind(ebo);
GlBufferType.ARRAY_BUFFER.unbind(); GlBufferType.ARRAY_BUFFER.bind(vbo);
GlVertexArray.unbind(); GlVertexArray.bind(vao);
} }
private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) { private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) {

View file

@ -1,10 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.tile;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.level.block.entity.BlockEntity;
@FunctionalInterface
public interface ITileInstanceFactory<T extends BlockEntity> {
TileEntityInstance<? super T> create(MaterialManager manager, T te);
}

View file

@ -0,0 +1,27 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
/**
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
*
* @see com.jozufozu.flywheel.mixin.BufferBuilderMixin
*/
public interface BufferBuilderExtension {
/**
* Frees the internal ByteBuffer, if it exists.
*/
void flywheel$freeBuffer();
/**
* Prepares the BufferBuilder for drawing the contents of the given buffer.
* @param buffer The buffer to draw.
* @param format The format of the buffer.
* @param vertexCount The number of vertices in the buffer.
*/
void flywheel$injectForRender(ByteBuffer buffer, VertexFormat format, int vertexCount);
}

View file

@ -1,16 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
/**
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
*/
public interface BufferBuilderHack {
void flywheel$freeBuffer();
void flywheel$hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount);
}

View file

@ -13,7 +13,7 @@ import com.mojang.blaze3d.vertex.VertexFormatElement;
/** /**
* An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer. * An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer.
* *
* @see BufferBuilderHack * @see BufferBuilderExtension
*/ */
public class DirectVertexConsumer implements VertexConsumer { public class DirectVertexConsumer implements VertexConsumer {
public final VertexFormat format; public final VertexFormat format;

View file

@ -228,7 +228,7 @@ public class SourceFile {
} }
/** /**
* Scan the source for <code>#use "..."</code> directives. * Scan the source for {@code #use "..."} directives.
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision. * Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
* @param elisions * @param elisions
*/ */

View file

@ -3,9 +3,6 @@ package com.jozufozu.flywheel.config;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.OptifineHandler;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.core;
import net.minecraft.client.Camera;
/**
* A class tracking which object last had {@link Camera#setup} called on it.
*
* @see com.jozufozu.flywheel.mixin.CameraMixin
*/
public class LastActiveCamera {
private static Camera camera;
public static void _setActiveCamera(Camera camera) {
LastActiveCamera.camera = camera;
}
public static Camera getActiveCamera() {
return camera;
}
}

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.core.crumbling; package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
public class CrumblingInstanceManager extends TileInstanceManager { public class CrumblingInstanceManager extends BlockEntityInstanceManager {
public CrumblingInstanceManager(MaterialManager materialManager) { public CrumblingInstanceManager(MaterialManager materialManager) {
super(materialManager); super(materialManager);

View file

@ -6,8 +6,8 @@ import java.util.SortedSet;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
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.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -36,7 +36,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
/** /**
* Responsible for rendering the block breaking overlay for instanced tiles. * Responsible for rendering the block breaking overlay for instanced block entities.
*/ */
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
@Mod.EventBusSubscriber(Dist.CLIENT) @Mod.EventBusSubscriber(Dist.CLIENT)
@ -57,7 +57,7 @@ public class CrumblingRenderer {
public static void renderBreaking(RenderLayerEvent event) { public static void renderBreaking(RenderLayerEvent event) {
if (!Backend.canUseInstancing(event.getWorld())) return; if (!Backend.canUseInstancing(event.getWorld())) return;
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageTiles(event.getWorld()); Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageBlockEntities(event.getWorld());
if (activeStages.isEmpty()) return; if (activeStages.isEmpty()) return;
@ -90,9 +90,9 @@ public class CrumblingRenderer {
} }
/** /**
* Associate each breaking stage with a list of all tile entities at that stage. * Associate each breaking stage with a list of all block entities at that stage.
*/ */
private static Int2ObjectMap<List<BlockEntity>> getActiveStageTiles(ClientLevel world) { private static Int2ObjectMap<List<BlockEntity>> getActiveStageBlockEntities(ClientLevel world) {
Int2ObjectMap<List<BlockEntity>> breakingEntities = new Int2ObjectArrayMap<>(); Int2ObjectMap<List<BlockEntity>> breakingEntities = new Int2ObjectArrayMap<>();
@ -105,11 +105,11 @@ public class CrumblingRenderer {
int blockDamage = progresses.last() int blockDamage = progresses.last()
.getProgress(); .getProgress();
BlockEntity tileEntity = world.getBlockEntity(breakingPos); BlockEntity blockEntity = world.getBlockEntity(breakingPos);
if (tileEntity != null) { if (blockEntity != null) {
List<BlockEntity> tileEntities = breakingEntities.computeIfAbsent(blockDamage, $ -> new ArrayList<>()); List<BlockEntity> blockEntities = breakingEntities.computeIfAbsent(blockDamage, $ -> new ArrayList<>());
tileEntities.add(tileEntity); blockEntities.add(blockEntity);
} }
} }
} }

View file

@ -2,9 +2,9 @@ package com.jozufozu.flywheel.core.hardcoded;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe; import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker; import com.mojang.blaze3d.platform.MemoryTracker;

View file

@ -14,14 +14,14 @@ public interface FlatLit<D extends InstanceData & FlatLit<D>> {
/** /**
* @param blockLight An integer in the range [0, 15] representing the * @param blockLight An integer in the range [0, 15] representing the
* amount of block light this instance should receive. * amount of block light this instance should receive.
* @return <code>this</code> * @return {@code this}
*/ */
D setBlockLight(int blockLight); D setBlockLight(int blockLight);
/** /**
* @param skyLight An integer in the range [0, 15] representing the * @param skyLight An integer in the range [0, 15] representing the
* amount of sky light this instance should receive. * amount of sky light this instance should receive.
* @return <code>this</code> * @return {@code this}
*/ */
D setSkyLight(int skyLight); D setSkyLight(int skyLight);

View file

@ -3,11 +3,11 @@ package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.MatrixItems;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.MatrixItems;
import com.jozufozu.flywheel.core.model.ModelTransformer; import com.jozufozu.flywheel.core.model.ModelTransformer;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;

View file

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.core.materials.model; package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe; import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.MatrixWrite;

View file

@ -3,10 +3,10 @@ package com.jozufozu.flywheel.core.materials.oriented;
import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Programs; import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer; import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;

View file

@ -2,8 +2,8 @@ package com.jozufozu.flywheel.core.materials.oriented;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe; import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe;
public class OrientedWriterUnsafe extends BasicWriterUnsafe<OrientedData> { public class OrientedWriterUnsafe extends BasicWriterUnsafe<OrientedData> {

View file

@ -16,7 +16,7 @@ public class WorldModel implements Model {
private final String name; private final String name;
/** /**
* It is expected that <code>renderWorld.getShade(...)</code> returns a constant. * It is expected that {@code renderWorld.getShade(...)} returns a constant.
*/ */
public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) { public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) {
reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));

View file

@ -7,13 +7,13 @@ import javax.annotation.Nonnull;
import com.jozufozu.flywheel.backend.ShaderContext; import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.shader.extension.IExtensionInstance; import com.jozufozu.flywheel.core.shader.extension.ExtensionInstance;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
/** /**
* A shader program that be arbitrarily "extended". This class can take in any number of program extensions, and * A shader program that be arbitrarily "extended". This class can take in any number of program extensions, and
* will initialize them and then call their {@link IExtensionInstance#bind() bind} function every subsequent time this * will initialize them and then call their {@link ExtensionInstance#bind() bind} function every subsequent time this
* program is bound. An "extension" is something that interacts with the shader program in a way that is invisible to * program is bound. An "extension" is something that interacts with the shader program in a way that is invisible to
* the caller using the program. This is used by some programs to implement the different fog modes. Other uses might * the caller using the program. This is used by some programs to implement the different fog modes. Other uses might
* include binding extra textures to allow for blocks to have normal maps, for example. As the extensions are * include binding extra textures to allow for blocks to have normal maps, for example. As the extensions are
@ -22,7 +22,7 @@ import net.minecraft.resources.ResourceLocation;
*/ */
public class ExtensibleGlProgram extends GlProgram { public class ExtensibleGlProgram extends GlProgram {
protected final List<IExtensionInstance> extensions = new ArrayList<>(); protected final List<ExtensionInstance> extensions = new ArrayList<>();
public ExtensibleGlProgram(ResourceLocation name, int handle) { public ExtensibleGlProgram(ResourceLocation name, int handle) {
super(name, handle); super(name, handle);
@ -32,7 +32,7 @@ public class ExtensibleGlProgram extends GlProgram {
public void bind() { public void bind() {
super.bind(); super.bind();
extensions.forEach(IExtensionInstance::bind); extensions.forEach(ExtensionInstance::bind);
} }
@Override @Override
@ -42,7 +42,7 @@ public class ExtensibleGlProgram extends GlProgram {
.append(name) .append(name)
.append('['); .append('[');
for (IExtensionInstance extension : extensions) { for (ExtensionInstance extension : extensions) {
builder.append(extension) builder.append(extension)
.append('+'); .append('+');
} }

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core.shader.extension;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public interface IExtensionInstance { public interface ExtensionInstance {
/** /**
* Bind the extra program state. It is recommended to grab the state information from global variables. * Bind the extra program state. It is recommended to grab the state information from global variables.

View file

@ -5,7 +5,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class UnitExtensionInstance implements IExtensionInstance { public class UnitExtensionInstance implements ExtensionInstance {
public static final ResourceLocation NAME = Flywheel.rl("unit"); public static final ResourceLocation NAME = Flywheel.rl("unit");

View file

@ -8,7 +8,7 @@ import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class WorldFog implements IExtensionInstance { public class WorldFog implements ExtensionInstance {
public static final ResourceLocation NAME = Flywheel.rl("fog"); public static final ResourceLocation NAME = Flywheel.rl("fog");

View file

@ -5,9 +5,9 @@ import com.mojang.serialization.Codec;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public interface IGameStateProvider { public interface GameStateProvider {
Codec<IGameStateProvider> CODEC = ResourceLocation.CODEC.xmap(GameStateRegistry::getStateProvider, IGameStateProvider::getID); Codec<GameStateProvider> CODEC = ResourceLocation.CODEC.xmap(GameStateRegistry::getStateProvider, GameStateProvider::getID);
ResourceLocation getID(); ResourceLocation getID();

View file

@ -0,0 +1,13 @@
package com.jozufozu.flywheel.core.shader.spec;
import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider;
public interface BooleanStateProvider extends GameStateProvider {
boolean isTrue();
@Override
default Boolean getValue() {
return isTrue();
}
}

View file

@ -17,11 +17,11 @@ import net.minecraft.resources.ResourceLocation;
* An object describing a shader program that can be loaded by flywheel. * An object describing a shader program that can be loaded by flywheel.
* *
* <p> * <p>
* These are defined through json. All ProgramSpecs in <code>assets/modid/flywheel/programs</code> are parsed and * These are defined through json. All ProgramSpecs in {@code assets/modid/flywheel/programs} are parsed and
* processed. One ProgramSpec typically specifies one "material" that can be used in game to render things. * processed. One ProgramSpec typically specifies one "material" that can be used in game to render things.
* </p> * </p>
* <p> * <p>
* All shader source files in <code>assets/modid/flywheel/shaders</code> are completely loaded and parsed into * All shader source files in {@code assets/modid/flywheel/shaders} are completely loaded and parsed into
* {@link SourceFile SourceFiles}, but not compiled until one of them is * {@link SourceFile SourceFiles}, but not compiled until one of them is
* referenced by a ProgramSpec. * referenced by a ProgramSpec.
* </p> * </p>

View file

@ -43,7 +43,7 @@ import net.minecraft.world.ticks.LevelTickAccess;
public class VirtualRenderWorld extends Level implements FlywheelWorld { public class VirtualRenderWorld extends Level implements FlywheelWorld {
public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>(); public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>();
public final Map<BlockPos, BlockEntity> tesAdded = new HashMap<>(); public final Map<BlockPos, BlockEntity> besAdded = new HashMap<>();
public final Set<SectionPos> spannedSections = new HashSet<>(); public final Set<SectionPos> spannedSections = new HashSet<>();
private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos(); private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos();
@ -85,9 +85,9 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
lighter.runUpdates(Integer.MAX_VALUE, false, false); lighter.runUpdates(Integer.MAX_VALUE, false, false);
} }
public void setTileEntities(Collection<BlockEntity> tileEntities) { public void setBlockEntities(Collection<BlockEntity> blockEntities) {
tesAdded.clear(); besAdded.clear();
tileEntities.forEach(te -> tesAdded.put(te.getBlockPos(), te)); blockEntities.forEach(be -> besAdded.put(be.getBlockPos(), be));
} }
public void clear() { public void clear() {
@ -153,7 +153,7 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
@Override @Override
@Nullable @Nullable
public BlockEntity getBlockEntity(BlockPos pos) { public BlockEntity getBlockEntity(BlockPos pos) {
return tesAdded.get(pos); return besAdded.get(pos);
} }
@Override @Override

View file

@ -8,28 +8,28 @@ import net.minecraftforge.eventbus.api.Event;
public class BeginFrameEvent extends Event { public class BeginFrameEvent extends Event {
private final ClientLevel world; private final ClientLevel world;
private final Camera info; private final Camera camera;
private final Frustum clippingHelper; private final Frustum frustum;
public BeginFrameEvent(ClientLevel world, Camera info, Frustum clippingHelper) { public BeginFrameEvent(ClientLevel world, Camera camera, Frustum frustum) {
this.world = world; this.world = world;
this.info = info; this.camera = camera;
this.clippingHelper = clippingHelper; this.frustum = frustum;
} }
public ClientLevel getWorld() { public ClientLevel getWorld() {
return world; return world;
} }
public Camera getInfo() { public Camera getCamera() {
return info; return camera;
} }
public Frustum getClippingHelper() { public Frustum getFrustum() {
return clippingHelper; return frustum;
} }
public Vec3 getCameraPos() { public Vec3 getCameraPos() {
return info.getPosition(); return camera.getPosition();
} }
} }

View file

@ -0,0 +1,29 @@
package com.jozufozu.flywheel.mixin;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstancingController;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityTypeExtension;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
@Mixin(BlockEntityType.class)
public class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> {
@Unique
private BlockEntityInstancingController<? super T> flywheel$instancingController;
@Override
@Nullable
public BlockEntityInstancingController<? super T> flywheel$getInstancingController() {
return flywheel$instancingController;
}
@Override
public void flywheel$setInstancingController(@Nullable BlockEntityInstancingController<? super T> instancingController) {
this.flywheel$instancingController = instancingController;
}
}

View file

@ -9,15 +9,13 @@ 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.backend.instancing.SuperBufferSource; import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.backend.model.BufferBuilderHack;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement; import com.mojang.blaze3d.vertex.VertexFormatElement;
@Mixin(BufferBuilder.class) @Mixin(BufferBuilder.class)
public abstract class BufferBuilderMixin implements BufferBuilderHack { public abstract class BufferBuilderMixin implements BufferBuilderExtension {
@Shadow @Shadow
private ByteBuffer buffer; private ByteBuffer buffer;
@ -52,7 +50,7 @@ public abstract class BufferBuilderMixin implements BufferBuilderHack {
} }
@Override @Override
public void flywheel$hackBegin(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) { public void flywheel$injectForRender(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) {
this.building = true; this.building = true;
this.mode = VertexFormat.Mode.QUADS; this.mode = VertexFormat.Mode.QUADS;

View file

@ -21,4 +21,19 @@ public interface BufferUploaderAccessor {
static void flywheel$setLastEBO(int id) { static void flywheel$setLastEBO(int id) {
throw new AssertionError(); throw new AssertionError();
} }
@Accessor("lastIndexBufferObject")
static int flywheel$getLastEBO() {
throw new AssertionError();
}
@Accessor("lastVertexBufferObject")
static int flywheel$getLastVBO() {
throw new AssertionError();
}
@Accessor("lastVertexArrayObject")
static int flywheel$getLastVAO() {
throw new AssertionError();
}
} }

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.core.LastActiveCamera;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
@Mixin(Camera.class)
public class CameraMixin {
@Inject(method = "setup", at = @At("TAIL"))
private void setup(BlockGetter level, Entity entity, boolean is3rdPerson, boolean isMirrored, float pt, CallbackInfo ci) {
LastActiveCamera._setActiveCamera((Camera)(Object) this);
}
}

View file

@ -24,11 +24,9 @@ public class CancelEntityRenderMixin {
private Iterable<Entity> filterEntities(ClientLevel world) { private Iterable<Entity> filterEntities(ClientLevel world) {
Iterable<Entity> entities = world.entitiesForRendering(); Iterable<Entity> entities = world.entitiesForRendering();
if (Backend.isOn()) { if (Backend.isOn()) {
ArrayList<Entity> filtered = Lists.newArrayList(entities); ArrayList<Entity> filtered = Lists.newArrayList(entities);
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance(); filtered.removeIf(InstancedRenderRegistry::shouldSkipRender);
filtered.removeIf(r::shouldSkipRender);
return filtered; return filtered;
} }

View file

@ -22,14 +22,11 @@ public class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true) @Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true)
private <E extends BlockEntity> void addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) { private <E extends BlockEntity> void addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) {
if (Backend.canUseInstancing(be.getLevel())) { if (Backend.canUseInstancing(be.getLevel())) {
if (InstancedRenderRegistry.canInstance(be.getType()))
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
InstancedRenderRegistry registry = InstancedRenderRegistry.getInstance(); if (InstancedRenderRegistry.shouldSkipRender(be))
if (registry.canInstance(be.getType()))
InstancedRenderDispatcher.getTiles(be.getLevel()).queueAdd(be);
if (registry.shouldSkipRender(be))
ci.cancel(); ci.cancel();
} }
} }

View file

@ -0,0 +1,29 @@
package com.jozufozu.flywheel.mixin;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstancingController;
import com.jozufozu.flywheel.backend.instancing.entity.EntityTypeExtension;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
@Mixin(EntityType.class)
public class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> {
@Unique
private EntityInstancingController<? super T> flywheel$instancingController;
@Override
@Nullable
public EntityInstancingController<? super T> flywheel$getInstancingController() {
return flywheel$instancingController;
}
@Override
public void flywheel$setInstancingController(@Nullable EntityInstancingController<? super T> instancingController) {
this.flywheel$instancingController = instancingController;
}
}

View file

@ -0,0 +1,25 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.core.LastActiveCamera;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraftforge.common.MinecraftForge;
@Mixin(Frustum.class)
public class FrustumMixin {
@Inject(method = "prepare", at = @At("TAIL"))
private void onPrepare(double x, double y, double z, CallbackInfo ci) {
if (OptifineHandler.isShadowPass()) {
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(Minecraft.getInstance().level, LastActiveCamera.getActiveCamera(), (Frustum) (Object) this));
}
}
}

View file

@ -26,9 +26,9 @@ public class InstanceAddMixin {
@Inject(method = "setBlockEntity", @Inject(method = "setBlockEntity",
at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")) at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"))
private void tileAdded(BlockEntity be, CallbackInfo ci) { private void blockEntityAdded(BlockEntity be, CallbackInfo ci) {
if (level.isClientSide && Backend.isOn()) { if (level.isClientSide && Backend.isOn()) {
InstancedRenderDispatcher.getTiles(this.level) InstancedRenderDispatcher.getBlockEntities(this.level)
.add(be); .add(be);
} }
} }

View file

@ -25,18 +25,18 @@ public class InstanceRemoveMixin {
@Inject(at = @At("TAIL"), method = "setRemoved") @Inject(at = @At("TAIL"), method = "setRemoved")
private void removeInstance(CallbackInfo ci) { private void removeInstance(CallbackInfo ci) {
if (level instanceof ClientLevel && Backend.isOn()) { if (level instanceof ClientLevel && Backend.isOn()) {
InstancedRenderDispatcher.getTiles(this.level) InstancedRenderDispatcher.getBlockEntities(this.level)
.remove((BlockEntity) (Object) this); .remove((BlockEntity) (Object) this);
} }
} }
// /** // /**
// * Don't do this. // * Don't do this.
// * It can cause infinite loops if an instance class tries to access another tile entity in its constructor. // * It can cause infinite loops if an instance class tries to access another block entity in its constructor.
// */ // */
// @Inject(at = @At("TAIL"), method = "clearRemoved") // @Inject(at = @At("TAIL"), method = "clearRemoved")
// private void addInstance(CallbackInfo ci) { // private void addInstance(CallbackInfo ci) {
// if (level.isClientSide) InstancedRenderDispatcher.getTiles(this.level) // if (level.isClientSide) InstancedRenderDispatcher.getBlockEntities(this.level)
// .add((BlockEntity) (Object) this); // .add((BlockEntity) (Object) this);
// } // }
} }

View file

@ -33,7 +33,7 @@ import net.minecraftforge.common.MinecraftForge;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public class RenderHooksMixin { public class LevelRendererMixin {
@Shadow @Shadow
private ClientLevel level; private ClientLevel level;
@ -52,7 +52,7 @@ public class RenderHooksMixin {
* layer-correct custom rendering. RenderWorldLast is not refined enough for rendering world objects. * layer-correct custom rendering. RenderWorldLast is not refined enough for rendering world objects.
* This should probably be a forge event. * This should probably be a forge event.
*/ */
@Inject(at = @At("TAIL"), method = "renderChunkLayer") @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/ShaderInstance;clear()V"), method = "renderChunkLayer")
private void renderLayer(RenderType type, PoseStack stack, double camX, double camY, double camZ, Matrix4f p_172999_, CallbackInfo ci) { private void renderLayer(RenderType type, PoseStack stack, double camX, double camY, double camZ, Matrix4f p_172999_, CallbackInfo ci) {
RenderBuffers renderBuffers = this.renderBuffers; RenderBuffers renderBuffers = this.renderBuffers;
@ -87,7 +87,7 @@ public class RenderHooksMixin {
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V") @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
if (Backend.isOn()) { if (Backend.isOn()) {
InstancedRenderDispatcher.getTiles(level) InstancedRenderDispatcher.getBlockEntities(level)
.update(level.getBlockEntity(pos)); .update(level.getBlockEntity(pos));
} }
} }

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.mixin;
import javax.annotation.Nonnull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import com.jozufozu.flywheel.backend.instancing.DrawBuffer;
import com.jozufozu.flywheel.backend.instancing.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType;
@Mixin(RenderType.class)
public class RenderTypeMixin implements RenderTypeExtension {
@Unique
private final DrawBuffer flywheel$drawBuffer = new DrawBuffer((RenderType) (Object) this);
@Override
@Nonnull
public DrawBuffer flywheel$getDrawBuffer() {
return flywheel$drawBuffer;
}
}

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
@ -14,14 +14,14 @@ import net.minecraft.client.renderer.blockentity.BellRenderer;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BellBlockEntity; import net.minecraft.world.level.block.entity.BellBlockEntity;
public class BellInstance extends TileEntityInstance<BellBlockEntity> implements IDynamicInstance { public class BellInstance extends BlockEntityInstance<BellBlockEntity> implements DynamicInstance {
private final OrientedData bell; private final OrientedData bell;
private float lastRingTime = Float.NaN; private float lastRingTime = Float.NaN;
public BellInstance(MaterialManager materialManager, BellBlockEntity tile) { public BellInstance(MaterialManager materialManager, BellBlockEntity blockEntity) {
super(materialManager, tile); super(materialManager, blockEntity);
bell = createBellInstance() bell = createBellInstance()
.setPivot(0.5f, 0.75f, 0.5f) .setPivot(0.5f, 0.75f, 0.5f)
@ -30,15 +30,15 @@ public class BellInstance extends TileEntityInstance<BellBlockEntity> implements
@Override @Override
public void beginFrame() { public void beginFrame() {
float ringTime = (float)tile.ticks + AnimationTickHolder.getPartialTicks(); float ringTime = (float)blockEntity.ticks + AnimationTickHolder.getPartialTicks();
if (ringTime == lastRingTime) return; if (ringTime == lastRingTime) return;
lastRingTime = ringTime; lastRingTime = ringTime;
if (tile.shaking) { if (blockEntity.shaking) {
float angle = Mth.sin(ringTime / (float) Math.PI) / (4.0F + ringTime / 3.0F); float angle = Mth.sin(ringTime / (float) Math.PI) / (4.0F + ringTime / 3.0F);
Vector3f ringAxis = tile.clickDirection.getCounterClockWise().step(); Vector3f ringAxis = blockEntity.clickDirection.getCounterClockWise().step();
bell.setRotation(ringAxis.rotation(angle)); bell.setRotation(ringAxis.rotation(angle));
} else { } else {
@ -59,7 +59,7 @@ public class BellInstance extends TileEntityInstance<BellBlockEntity> implements
private OrientedData createBellInstance() { private OrientedData createBellInstance() {
return materialManager.defaultCutout() return materialManager.defaultCutout()
.material(Materials.ORIENTED) .material(Materials.ORIENTED)
.model(tile.getType(), BellInstance::createBellModel) .model(blockEntity.getType(), BellInstance::createBellModel)
.createInstance(); .createInstance();
} }

View file

@ -5,12 +5,12 @@ import java.util.Calendar;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData; import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
@ -28,7 +28,7 @@ import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.LidBlockEntity; import net.minecraft.world.level.block.entity.LidBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ChestType;
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends TileEntityInstance<T> implements IDynamicInstance { public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance {
private final OrientedData body; private final OrientedData body;
private final ModelData lid; private final ModelData lid;
@ -41,13 +41,13 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends TileE
private float lastProgress = Float.NaN; private float lastProgress = Float.NaN;
public ChestInstance(MaterialManager materialManager, T tile) { public ChestInstance(MaterialManager materialManager, T blockEntity) {
super(materialManager, tile); super(materialManager, blockEntity);
Block block = blockState.getBlock(); Block block = blockState.getBlock();
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE; chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
renderMaterial = Sheets.chooseMaterial(tile, chestType, isChristmas()); renderMaterial = Sheets.chooseMaterial(blockEntity, chestType, isChristmas());
body = baseInstance() body = baseInstance()
.setPosition(getInstancePosition()); .setPosition(getInstancePosition());
@ -63,7 +63,7 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends TileE
DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> wrapper = chestBlock.combine(blockState, world, getWorldPosition(), true); DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> wrapper = chestBlock.combine(blockState, world, getWorldPosition(), true);
this.lidProgress = wrapper.apply(ChestBlock.opennessCombiner(tile)); this.lidProgress = wrapper.apply(ChestBlock.opennessCombiner(blockEntity));
} else { } else {

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack; import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
@ -21,7 +21,7 @@ import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements IDynamicInstance, ITickableInstance { public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements DynamicInstance, TickableInstance {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png"); private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials; import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.MatrixTransformStack; import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
@ -19,7 +19,7 @@ import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.ShulkerBoxBlock;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity; import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
public class ShulkerBoxInstance extends TileEntityInstance<ShulkerBoxBlockEntity> implements IDynamicInstance { public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntity> implements DynamicInstance {
private final TextureAtlasSprite texture; private final TextureAtlasSprite texture;
@ -29,10 +29,10 @@ public class ShulkerBoxInstance extends TileEntityInstance<ShulkerBoxBlockEntity
private float lastProgress = Float.NaN; private float lastProgress = Float.NaN;
public ShulkerBoxInstance(MaterialManager materialManager, ShulkerBoxBlockEntity tile) { public ShulkerBoxInstance(MaterialManager materialManager, ShulkerBoxBlockEntity blockEntity) {
super(materialManager, tile); super(materialManager, blockEntity);
DyeColor color = tile.getColor(); DyeColor color = blockEntity.getColor();
if (color == null) { if (color == null) {
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite(); texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
} else { } else {
@ -56,7 +56,7 @@ public class ShulkerBoxInstance extends TileEntityInstance<ShulkerBoxBlockEntity
@Override @Override
public void beginFrame() { public void beginFrame() {
float progress = tile.getProgress(AnimationTickHolder.getPartialTicks()); float progress = blockEntity.getProgress(AnimationTickHolder.getPartialTicks());
if (progress == lastProgress) return; if (progress == lastProgress) return;
lastProgress = progress; lastProgress = progress;

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import static com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry.configure;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
@ -28,34 +28,40 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
public class VanillaInstances { public class VanillaInstances {
public static void init() { public static void init() {
InstancedRenderRegistry r = InstancedRenderRegistry.getInstance(); configure(BlockEntityType.CHEST)
.alwaysSkipRender()
.factory(ChestInstance::new)
.apply();
configure(BlockEntityType.ENDER_CHEST)
.alwaysSkipRender()
.factory(ChestInstance::new)
.apply();
configure(BlockEntityType.TRAPPED_CHEST)
.alwaysSkipRender()
.factory(ChestInstance::new)
.apply();
r.tile(BlockEntityType.CHEST) configure(BlockEntityType.BELL)
.setSkipRender(true) .alwaysSkipRender()
.factory(ChestInstance::new); .factory(BellInstance::new)
r.tile(BlockEntityType.ENDER_CHEST) .apply();
.setSkipRender(true)
.factory(ChestInstance::new);
r.tile(BlockEntityType.TRAPPED_CHEST)
.setSkipRender(true)
.factory(ChestInstance::new);
r.tile(BlockEntityType.BELL) configure(BlockEntityType.SHULKER_BOX)
.setSkipRender(true) .alwaysSkipRender()
.factory(BellInstance::new); .factory(ShulkerBoxInstance::new)
.apply();
r.tile(BlockEntityType.SHULKER_BOX) configure(EntityType.MINECART)
.setSkipRender(true) .alwaysSkipRender()
.factory(ShulkerBoxInstance::new); .factory(MinecartInstance::new)
.apply();
r.entity(EntityType.MINECART) configure(EntityType.HOPPER_MINECART)
.setSkipRender(true) .alwaysSkipRender()
.factory(MinecartInstance::new); .factory(MinecartInstance::new)
r.entity(EntityType.HOPPER_MINECART) .apply();
.setSkipRender(true) configure(EntityType.FURNACE_MINECART)
.factory(MinecartInstance::new); .alwaysSkipRender()
r.entity(EntityType.FURNACE_MINECART) .factory(MinecartInstance::new)
.setSkipRender(true) .apply();
.factory(MinecartInstance::new);
} }
} }

View file

@ -5,17 +5,22 @@
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"refmap": "flywheel.refmap.json", "refmap": "flywheel.refmap.json",
"client": [ "client": [
"BlockEntityTypeMixin",
"BufferBuilderMixin", "BufferBuilderMixin",
"BufferUploaderAccessor", "BufferUploaderAccessor",
"CameraMixin",
"CancelEntityRenderMixin", "CancelEntityRenderMixin",
"ChunkRebuildHooksMixin", "ChunkRebuildHooksMixin",
"EntityTypeMixin",
"FixFabulousDepthMixin", "FixFabulousDepthMixin",
"FrustumMixin",
"InstanceAddMixin", "InstanceAddMixin",
"InstanceRemoveMixin", "InstanceRemoveMixin",
"LevelRendererAccessor", "LevelRendererAccessor",
"LevelRendererMixin",
"PausedPartialTickAccessor", "PausedPartialTickAccessor",
"RenderHooksMixin",
"RenderTexturesMixin", "RenderTexturesMixin",
"RenderTypeMixin",
"ShaderCloseMixin", "ShaderCloseMixin",
"ShaderInstanceAccessor", "ShaderInstanceAccessor",
"atlas.AtlasDataMixin", "atlas.AtlasDataMixin",