A little of everything

- Rename InstanceData -> InstancedPart, and subclasses
 - Burger Fences
 - Fix GlStateTracker nuking vao element buffer bindings
 - GlVertexArray tracks element buffer bindings
 - Use vertexAttribPointer offset instead of ..BaseVertex
 - Setup code for better crumbling rendering
 - Move some logic into CoreShaderInfoMap
 - Simplify VertexWriter/VertexList
 - Prefer IEventBus#addListener to @SubscribeEvent
 - Stop using persistent buffers... for now
This commit is contained in:
Jozufozu 2022-06-19 23:25:44 -07:00
parent d22f715f79
commit 08fff1c125
63 changed files with 861 additions and 495 deletions

View file

@ -5,6 +5,9 @@ import org.slf4j.Logger;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
@ -12,12 +15,16 @@ import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.GameStateRegistry; import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.Models; import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite; import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.material.MaterialShaders; import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider; import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider;
import com.jozufozu.flywheel.core.structs.InstanceShaders; import com.jozufozu.flywheel.core.structs.InstanceShaders;
import com.jozufozu.flywheel.core.vertex.LayoutShaders; import com.jozufozu.flywheel.core.vertex.LayoutShaders;
import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances; import com.jozufozu.flywheel.vanilla.VanillaInstances;
@ -28,6 +35,7 @@ import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.CrashReportCallables; import net.minecraftforge.fml.CrashReportCallables;
import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.DistExecutor;
@ -75,8 +83,25 @@ public class Flywheel {
Backend.init(); Backend.init();
forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll); forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll);
forgeEventBus.addListener(Models::onReload); forgeEventBus.addListener(Models::onReload);
forgeEventBus.addListener(MeshPool::reset);
forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers);
forgeEventBus.addListener(InstancedRenderDispatcher::onReloadRenderers);
forgeEventBus.addListener(InstancedRenderDispatcher::onBeginFrame);
forgeEventBus.addListener(InstancedRenderDispatcher::tick);
forgeEventBus.addListener(EntityWorldHandler::onEntityJoinWorld);
forgeEventBus.addListener(EntityWorldHandler::onEntityLeaveWorld);
forgeEventBus.addListener(ForgeEvents::addToDebugScreen);
forgeEventBus.addListener(ForgeEvents::unloadWorld);
forgeEventBus.addListener(ForgeEvents::tickLight);
forgeEventBus.addListener(EventPriority.LOWEST, RenderWork::onRenderWorldLast);
modEventBus.addListener(PartialModel::onModelRegistry); modEventBus.addListener(PartialModel::onModelRegistry);
modEventBus.addListener(PartialModel::onModelBake); modEventBus.addListener(PartialModel::onModelBake);

View file

@ -1,12 +1,19 @@
package com.jozufozu.flywheel.api; package com.jozufozu.flywheel.api;
public abstract class InstanceData { import com.jozufozu.flywheel.api.struct.StructType;
public abstract class InstancedPart {
public final StructType<?> type;
private Instancer<?> owner; private Instancer<?> owner;
private boolean dirty; private boolean dirty;
private boolean removed; private boolean removed;
protected InstancedPart(StructType<?> type) {
this.type = type;
}
public final void markDirty() { public final void markDirty() {
dirty = true; dirty = true;
owner.notifyDirty(); owner.notifyDirty();
@ -34,8 +41,9 @@ public abstract class InstanceData {
return owner; return owner;
} }
public InstanceData setOwner(Instancer<?> owner) { public void setOwner(Instancer<?> owner) {
this.owner = owner; this.owner = owner;
return this;
} }
public abstract InstancedPart copy();
} }

View file

@ -18,7 +18,7 @@ package com.jozufozu.flywheel.api;
* *
* @param <D> the data that represents a copy of the instanced model. * @param <D> the data that represents a copy of the instanced model.
*/ */
public interface Instancer<D extends InstanceData> { public interface Instancer<D extends InstancedPart> {
/** /**
* @return a handle to a new copy of this model. * @return a handle to a new copy of this model.
*/ */

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.api;
import com.jozufozu.flywheel.core.model.ModelSupplier; import com.jozufozu.flywheel.core.model.ModelSupplier;
public interface InstancerFactory<D extends InstanceData> { public interface InstancerFactory<D extends InstancedPart> {
/** /**
* Get an instancer for the given model. Calling this method twice with the same key will return the same instancer. * Get an instancer for the given model. Calling this method twice with the same key will return the same instancer.

View file

@ -6,7 +6,7 @@ import net.minecraft.core.Vec3i;
public interface InstancerManager { public interface InstancerManager {
<D extends InstanceData> InstancerFactory<D> factory(StructType<D> type); <D extends InstancedPart> InstancerFactory<D> factory(StructType<D> type);
Vec3i getOriginCoordinate(); Vec3i getOriginCoordinate();

View file

@ -11,5 +11,5 @@ public interface MaterialGroup {
* @param <D> The type representing the per instance data. * @param <D> The type representing the per instance data.
* @return A material you can use to render models. * @return A material you can use to render models.
*/ */
<D extends InstanceData> InstancerFactory<D> material(StructType<D> spec); <D extends InstancedPart> InstancerFactory<D> material(StructType<D> spec);
} }

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.api.instance; package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
@ -19,7 +19,7 @@ public interface DynamicInstance extends Instance {
* <br> * <br>
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this instance. * <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this instance.
* <br> * <br>
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here. * {@link Instancer}/{@link InstancedPart} creation/acquisition is safe here.
*/ */
void beginFrame(); void beginFrame();

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.api.instance; package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
@ -27,7 +27,7 @@ public interface TickableInstance extends Instance {
* <br> * <br>
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance. * <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance.
* <br> * <br>
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here. * {@link Instancer}/{@link InstancedPart} creation/acquisition is safe here.
*/ */
void tick(); void tick();

View file

@ -3,9 +3,9 @@ package com.jozufozu.flywheel.api.vertex;
public interface VertexWriter { public interface VertexWriter {
void writeVertex(VertexList list, int index); void writeVertex(VertexList list, int index);
void seekToVertex(int vertex); void seek(long offset);
VertexList intoReader(); VertexList intoReader(int vertices);
default void writeVertexList(VertexList list) { default void writeVertexList(VertexList list) {
for (int i = 0; i < list.getVertexCount(); i++) { for (int i = 0; i < list.getVertexCount(); i++) {

View file

@ -3,18 +3,12 @@ package com.jozufozu.flywheel.backend;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderLevelLastEvent; import net.minecraftforge.client.event.RenderLevelLastEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class RenderWork { public class RenderWork {
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>(); private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void onRenderWorldLast(RenderLevelLastEvent event) { public static void onRenderWorldLast(RenderLevelLastEvent event) {
while (!runs.isEmpty()) { while (!runs.isEmpty()) {
runs.remove() runs.remove()

View file

@ -1,14 +1,19 @@
package com.jozufozu.flywheel.backend.gl; package com.jozufozu.flywheel.backend.gl;
import static org.lwjgl.opengl.GL32.GL_ALREADY_SIGNALED; import static org.lwjgl.opengl.GL32.GL_SIGNALED;
import static org.lwjgl.opengl.GL32.GL_CONDITION_SATISFIED;
import static org.lwjgl.opengl.GL32.GL_SYNC_FLUSH_COMMANDS_BIT; import static org.lwjgl.opengl.GL32.GL_SYNC_FLUSH_COMMANDS_BIT;
import static org.lwjgl.opengl.GL32.GL_SYNC_GPU_COMMANDS_COMPLETE; import static org.lwjgl.opengl.GL32.GL_SYNC_GPU_COMMANDS_COMPLETE;
import static org.lwjgl.opengl.GL32.GL_UNSIGNALED; import static org.lwjgl.opengl.GL32.GL_SYNC_STATUS;
import static org.lwjgl.opengl.GL32.GL_TIMEOUT_IGNORED;
import static org.lwjgl.opengl.GL32.glClientWaitSync; import static org.lwjgl.opengl.GL32.glClientWaitSync;
import static org.lwjgl.opengl.GL32.glDeleteSync; import static org.lwjgl.opengl.GL32.glDeleteSync;
import static org.lwjgl.opengl.GL32.glFenceSync; import static org.lwjgl.opengl.GL32.glFenceSync;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
// https://github.com/CaffeineMC/sodium-fabric/blob/da17fc8d0cb1a4e82fe6956ac4f07a63d32eca5a/components/gfx-opengl/src/main/java/net/caffeinemc/gfx/opengl/sync/GlFence.java
public class GlFence { public class GlFence {
private long fence; private long fence;
@ -26,16 +31,38 @@ public class GlFence {
} }
} }
public void waitSync() { public boolean poll() {
if (fence != 0) { if (fence != 0) {
int waitReturn = GL_UNSIGNALED; poll0();
while (waitReturn != GL_ALREADY_SIGNALED && waitReturn != GL_CONDITION_SATISFIED) {
waitReturn = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 1);
}
glDeleteSync(fence);
} }
return fence == 0;
}
private void poll0() {
int result;
try (var memoryStack = MemoryStack.stackPush()) {
long checkPtr = memoryStack.ncalloc(Integer.BYTES, 0, Integer.BYTES);
GL32.nglGetSynciv(fence, GL_SYNC_STATUS, 1, MemoryUtil.NULL, checkPtr);
result = MemoryUtil.memGetInt(checkPtr);
}
if (result == GL_SIGNALED) {
glDeleteSync(fence);
fence = 0;
}
}
public void waitSync() {
if (poll()) {
return;
}
glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(fence);
fence = 0; fence = 0;
} }
} }

View file

@ -42,20 +42,20 @@ public class GlStateTracker {
public record State(int[] buffers, int vao, int program) implements AutoCloseable { public record State(int[] buffers, int vao, int program) implements AutoCloseable {
public void restore() { public void restore() {
GlBufferType[] values = GlBufferType.values(); if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.buffers[i]) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
}
} }
if (vao != GlStateTracker.vao) { if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao); GlStateManager._glBindVertexArray(vao);
} }
if (program != GlStateTracker.program) { GlBufferType[] values = GlBufferType.values();
GlStateManager._glUseProgram(program);
for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.buffers[i]) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
}
} }
} }

View file

@ -36,13 +36,15 @@ public class GlVertexArray extends GlObject {
/** /**
* Each attribute's offset. * Each attribute's offset.
*/ */
private final int[] offsets = new int[MAX_ATTRIBS]; private final long[] offsets = new long[MAX_ATTRIBS];
/** /**
* Each attribute's stride. * Each attribute's stride.
*/ */
private final int[] strides = new int[MAX_ATTRIBS]; private final int[] strides = new int[MAX_ATTRIBS];
private int elementBufferBinding = 0;
public GlVertexArray() { public GlVertexArray() {
setHandle(GlStateManager._glGenVertexArrays()); setHandle(GlStateManager._glGenVertexArrays());
@ -62,15 +64,20 @@ public class GlVertexArray extends GlObject {
GlStateManager._glBindVertexArray(0); GlStateManager._glBindVertexArray(0);
} }
public void bindAttributes(GlBuffer buffer, int startIndex, BufferLayout type) { /**
* @param buffer The buffer where the data is stored.
* @param startAttrib The first attribute to be used by the data.
* @param type The format of the attributes.
* @param offset The offset in bytes to the start of the data.
*/
public void bindAttributes(GlBuffer buffer, int startAttrib, BufferLayout type, long offset) {
bind(); bind();
int targetBuffer = buffer.handle(); int targetBuffer = buffer.handle();
GlBufferType.ARRAY_BUFFER.bind(targetBuffer); GlBufferType.ARRAY_BUFFER.bind(targetBuffer);
int i = startIndex; int i = startAttrib;
int offset = 0;
final int stride = type.getStride(); final int stride = type.getStride();
for (VertexAttribute attribute : type.getAttributes()) { for (VertexAttribute attribute : type.getAttributes()) {
@ -126,4 +133,13 @@ public class GlVertexArray extends GlObject {
divisors[index] = divisor; divisors[index] = divisor;
} }
} }
public void bindElementArray(GlBuffer ebo) {
int handle = ebo.handle();
if (elementBufferBinding != handle) {
bind();
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(handle);
elementBufferBinding = handle;
}
}
} }

View file

@ -77,13 +77,6 @@ public abstract class GlBuffer extends GlObject {
*/ */
public abstract boolean ensureCapacity(long size); public abstract boolean ensureCapacity(long size);
/**
* Call this after all draw calls using this buffer are complete.
*/
public void doneForThisFrame() {
}
protected void deleteInternal(int handle) { protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle); GL20.glDeleteBuffers(handle);
} }

View file

@ -10,7 +10,6 @@ import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlFence;
import com.jozufozu.flywheel.backend.gl.error.GlError; import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException; import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
@ -19,19 +18,12 @@ public class PersistentGlBuffer extends GlBuffer {
@Nullable @Nullable
private MappedBuffer access; private MappedBuffer access;
int flags; private final int storageFlags;
private final GlFence fence;
public PersistentGlBuffer(GlBufferType type) { public PersistentGlBuffer(GlBufferType type) {
super(type); super(type);
flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; storageFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
fence = new GlFence();
}
@Override
public void doneForThisFrame() {
fence.post();
} }
@Override @Override
@ -47,7 +39,7 @@ public class PersistentGlBuffer extends GlBuffer {
if (this.size == 0) { if (this.size == 0) {
this.size = size; this.size = size;
bind(); bind();
GlCompat.getInstance().bufferStorage.bufferStorage(type, this.size, flags); GlCompat.getInstance().bufferStorage.bufferStorage(type, this.size, storageFlags);
return true; return true;
} }
@ -55,8 +47,6 @@ public class PersistentGlBuffer extends GlBuffer {
var oldSize = this.size; var oldSize = this.size;
this.size = size + growthMargin; this.size = size + growthMargin;
fence.clear();
realloc(this.size, oldSize); realloc(this.size, oldSize);
access = null; access = null;
@ -89,7 +79,7 @@ public class PersistentGlBuffer extends GlBuffer {
private void mapToClientMemory() { private void mapToClientMemory() {
bind(); bind();
ByteBuffer byteBuffer = GL32.glMapBufferRange(type.glEnum, 0, size, flags); ByteBuffer byteBuffer = GL32.glMapBufferRange(type.glEnum, 0, size, storageFlags);
if (byteBuffer == null) { if (byteBuffer == null) {
throw new GlException(GlError.poll(), "Could not map buffer"); throw new GlException(GlError.poll(), "Could not map buffer");
@ -105,7 +95,7 @@ public class PersistentGlBuffer extends GlBuffer {
GlBufferType.COPY_READ_BUFFER.bind(oldHandle); GlBufferType.COPY_READ_BUFFER.bind(oldHandle);
type.bind(newHandle); type.bind(newHandle);
GlCompat.getInstance().bufferStorage.bufferStorage(type, newSize, flags); GlCompat.getInstance().bufferStorage.bufferStorage(type, newSize, storageFlags);
GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize); GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize);
@ -122,8 +112,6 @@ public class PersistentGlBuffer extends GlBuffer {
private MappedBuffer getWriteAccess() { private MappedBuffer getWriteAccess() {
if (access == null) { if (access == null) {
mapToClientMemory(); mapToClientMemory();
} else {
fence.waitSync(); // FIXME: Hangs too much, needs double/triple buffering
} }
return access; return access;

View file

@ -2,20 +2,20 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet; import java.util.BitSet;
import java.util.function.Supplier;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.struct.StructType;
public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> { public abstract class AbstractInstancer<D extends InstancedPart> implements Instancer<D> {
protected final Supplier<D> factory; protected final StructType<D> type;
protected final ArrayList<D> data = new ArrayList<>(); protected final ArrayList<D> data = new ArrayList<>();
protected boolean anyToRemove; protected boolean anyToRemove;
protected AbstractInstancer(Supplier<D> factory) { protected AbstractInstancer(StructType<D> type) {
this.factory = factory; this.type = type;
} }
/** /**
@ -23,7 +23,7 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
*/ */
@Override @Override
public D createInstance() { public D createInstance() {
return _add(factory.get()); return _add(type.create());
} }
/** /**

View file

@ -141,8 +141,7 @@ public class InstanceWorld {
public void loadEntities(ClientLevel world) { public void loadEntities(ClientLevel world) {
// Block entities are loaded while chunks are baked. // Block entities are loaded while chunks are baked.
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added. // Entities are loaded with the world, so when chunks are reloaded they need to be re-added.
ClientLevelExtension.cast(world) ClientLevelExtension.getAllLoadedEntities(world)
.flywheel$getAllLoadedEntities()
.forEach(entityInstanceManager::add); .forEach(entityInstanceManager::add);
} }

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
@ -22,7 +23,6 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class InstancedRenderDispatcher { public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>(InstanceWorld::create); private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>(InstanceWorld::create);
@ -71,7 +71,6 @@ public class InstancedRenderDispatcher {
} }
} }
@SubscribeEvent
public static void tick(TickEvent.ClientTickEvent event) { public static void tick(TickEvent.ClientTickEvent event) {
if (!Backend.isGameActive() || event.phase == TickEvent.Phase.START) { if (!Backend.isGameActive() || event.phase == TickEvent.Phase.START) {
return; return;
@ -86,7 +85,6 @@ public class InstancedRenderDispatcher {
} }
} }
@SubscribeEvent
public static void onBeginFrame(BeginFrameEvent event) { public static void onBeginFrame(BeginFrameEvent event) {
if (Backend.isGameActive() && Backend.isOn()) { if (Backend.isGameActive() && Backend.isOn()) {
instanceWorlds.get(event.getWorld()) instanceWorlds.get(event.getWorld())
@ -108,7 +106,6 @@ public class InstancedRenderDispatcher {
instanceWorlds.get(world).renderAllRemaining(context); instanceWorlds.get(world).renderAllRemaining(context);
} }
@SubscribeEvent
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld(); ClientLevel world = event.getWorld();
if (Backend.isOn() && world != null) { if (Backend.isOn() && world != null) {

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker; import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
@ -16,7 +16,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
protected final RenderType state; protected final RenderType state;
private final Map<StructType<? extends InstanceData>, CPUInstancerFactory<?>> materials = new HashMap<>(); private final Map<StructType<? extends InstancedPart>, CPUInstancerFactory<?>> materials = new HashMap<>();
private int vertexCount; private int vertexCount;
private int instanceCount; private int instanceCount;
@ -26,7 +26,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <D extends InstanceData> CPUInstancerFactory<D> material(StructType<D> type) { public <D extends InstancedPart> CPUInstancerFactory<D> material(StructType<D> type) {
return (CPUInstancerFactory<D>) materials.computeIfAbsent(type, CPUInstancerFactory::new); return (CPUInstancerFactory<D>) materials.computeIfAbsent(type, CPUInstancerFactory::new);
} }

View file

@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker; import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
@ -20,12 +20,12 @@ import net.minecraft.core.Vec3i;
public class BatchingEngine implements Engine { public class BatchingEngine implements Engine {
private final Map<StructType<? extends InstanceData>, CPUInstancerFactory<?>> factories = new HashMap<>(); private final Map<StructType<? extends InstancedPart>, CPUInstancerFactory<?>> factories = new HashMap<>();
private final BatchDrawingTracker batchTracker = new BatchDrawingTracker(); private final BatchDrawingTracker batchTracker = new BatchDrawingTracker();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <D extends InstanceData> CPUInstancerFactory<D> factory(StructType<D> type) { public <D extends InstancedPart> CPUInstancerFactory<D> factory(StructType<D> type) {
return (CPUInstancerFactory<D>) factories.computeIfAbsent(type, CPUInstancerFactory::new); return (CPUInstancerFactory<D>) factories.computeIfAbsent(type, CPUInstancerFactory::new);
} }

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.batching; package com.jozufozu.flywheel.backend.instancing.batching;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
@ -8,14 +8,14 @@ import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> { public class CPUInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
// private final Batched<D> batchingType; // private final Batched<D> batchingType;
// //
// final ModelTransformer sbb; // final ModelTransformer sbb;
public CPUInstancer(StructType<D> type) { public CPUInstancer(StructType<D> type) {
super(type::create); super(type);
// batchingType = type; // batchingType = type;
// //
// sbb = new ModelTransformer(modelData.get()); // sbb = new ModelTransformer(modelData.get());

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.InstancerFactory; import com.jozufozu.flywheel.api.InstancerFactory;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
@ -11,7 +11,7 @@ import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
public class CPUInstancerFactory<D extends InstanceData> implements InstancerFactory<D> { public class CPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final Map<ModelSupplier, CPUInstancer<D>> models; protected final Map<ModelSupplier, CPUInstancer<D>> models;
private final StructType<D> type; private final StructType<D> type;

View file

@ -1,13 +1,17 @@
package com.jozufozu.flywheel.backend.instancing.blockentity; package com.jozufozu.flywheel.backend.instancing.blockentity;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerFactory; import com.jozufozu.flywheel.api.InstancerFactory;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance; 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.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox; import com.jozufozu.flywheel.util.box.ImmutableBox;
@ -47,6 +51,16 @@ public abstract class BlockEntityInstance<T extends BlockEntity> extends Abstrac
this.instancePos = pos.subtract(instancerManager.getOriginCoordinate()); this.instancePos = pos.subtract(instancerManager.getOriginCoordinate());
} }
public List<InstancedPart> getCrumblingParts() {
var out = new ArrayList<InstancedPart>();
addCrumblingParts(out);
return out;
}
public void addCrumblingParts(List<InstancedPart> data) {
}
/** /**
* Just before {@link #update()} would be called, {@code shouldReset()} is checked. * Just before {@link #update()} would be called, {@code shouldReset()} is checked.
* If this function returns {@code true}, then this instance will be {@link #remove removed}, * If this function returns {@code true}, then this instance will be {@link #remove removed},
@ -76,11 +90,11 @@ public abstract class BlockEntityInstance<T extends BlockEntity> extends Abstrac
return pos; return pos;
} }
protected InstancerFactory<ModelData> getTransformFactory() { protected InstancerFactory<TransformedPart> getTransformFactory() {
return instancerManager.factory(StructTypes.MODEL); return instancerManager.factory(StructTypes.TRANSFORMED);
} }
protected InstancerFactory<OrientedData> getOrientedFactory() { protected InstancerFactory<OrientedPart> getOrientedFactory() {
return instancerManager.factory(StructTypes.ORIENTED); return instancerManager.factory(StructTypes.ORIENTED);
} }

View file

@ -1,11 +1,15 @@
package com.jozufozu.flywheel.backend.instancing.blockentity; package com.jozufozu.flywheel.backend.instancing.blockentity;
import java.util.List;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
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 it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
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;
@ -13,10 +17,19 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> { public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
private final Long2ObjectMap<BlockEntityInstance<?>> posLookup = new Long2ObjectOpenHashMap<>();
public BlockEntityInstanceManager(InstancerManager instancerManager) { public BlockEntityInstanceManager(InstancerManager instancerManager) {
super(instancerManager); super(instancerManager);
} }
public void getCrumblingInstances(long pos, List<BlockEntityInstance<?>> data) {
BlockEntityInstance<?> instance = posLookup.get(pos);
if (instance != null) {
data.add(instance);
}
}
@Override @Override
protected boolean canInstance(BlockEntity obj) { protected boolean canInstance(BlockEntity obj) {
return obj != null && InstancedRenderRegistry.canInstance(obj.getType()); return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
@ -24,7 +37,20 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
@Override @Override
protected AbstractInstance createRaw(BlockEntity obj) { protected AbstractInstance createRaw(BlockEntity obj) {
return InstancedRenderRegistry.createInstance(instancerManager, obj); var instance = InstancedRenderRegistry.createInstance(instancerManager, obj);
if (instance != null) {
BlockPos blockPos = obj.getBlockPos();
posLookup.put(blockPos.asLong(), instance);
}
return instance;
}
@Override
protected void removeInternal(BlockEntity obj, AbstractInstance instance) {
super.removeInternal(obj, instance);
posLookup.remove(obj.getBlockPos().asLong());
} }
@Override @Override

View file

@ -4,20 +4,22 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> { public class GPUInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
final BufferLayout instanceFormat; public final BufferLayout instanceFormat;
final StructType<D> instancedType; public final StructType<D> structType;
public final InstancedModel<D> parent;
GlBuffer vbo; GlBuffer vbo;
int attributeBaseIndex; int attributeBaseIndex;
@ -25,10 +27,11 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
boolean anyToUpdate; boolean anyToUpdate;
public GPUInstancer(StructType<D> type) { public GPUInstancer(InstancedModel<D> parent, StructType<D> type) {
super(type::create); super(type);
this.parent = parent;
this.instanceFormat = type.getLayout(); this.instanceFormat = type.getLayout();
instancedType = type; this.structType = type;
} }
@Override @Override
@ -39,7 +42,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
public void init() { public void init() {
if (vbo != null) return; if (vbo != null) return;
vbo = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.setGrowthMargin(instanceFormat.getStride() * 16); vbo.setGrowthMargin(instanceFormat.getStride() * 16);
} }
@ -82,7 +85,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
if (size > 0) { if (size > 0) {
final StructWriter<D> writer = instancedType.getWriter(buf.unwrap()); final StructWriter<D> writer = structType.getWriter(buf.unwrap());
boolean sequential = true; boolean sequential = true;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@ -115,7 +118,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
} }
private void bindInstanceAttributes(GlVertexArray vao) { private void bindInstanceAttributes(GlVertexArray vao) {
vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat); vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat, 0L);
for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) { for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) {
vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); vao.setAttributeDivisor(this.attributeBaseIndex + i, 1);

View file

@ -4,16 +4,16 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.InstancerFactory; import com.jozufozu.flywheel.api.InstancerFactory;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.model.ModelSupplier; import com.jozufozu.flywheel.core.model.ModelSupplier;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@ -22,16 +22,14 @@ import net.minecraft.client.renderer.RenderType;
* A collection of Instancers that all have the same format. * A collection of Instancers that all have the same format.
* @param <D> * @param <D>
*/ */
public class GPUInstancerFactory<D extends InstanceData> implements InstancerFactory<D> { public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>(); protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>();
protected final StructType<D> type; protected final StructType<D> type;
protected final List<InstancedModel<D>> uninitialized = new ArrayList<>(); protected final List<InstancedModel<D>> uninitialized = new ArrayList<>();
// FIXME: these should not be public private final ListMultimap<RenderType, Renderable> renderLists = ArrayListMultimap.create();
public final Multimap<RenderType, Material> materials = HashMultimap.create();
public final Multimap<Material, Renderable> renderables = ArrayListMultimap.create();
public GPUInstancerFactory(StructType<D> type) { public GPUInstancerFactory(StructType<D> type) {
this.type = type; this.type = type;
@ -39,7 +37,7 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
@Override @Override
public Instancer<D> model(ModelSupplier modelKey) { public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, this::createInstancer).instancer; return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer();
} }
public int getInstanceCount() { public int getInstanceCount() {
@ -60,8 +58,7 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
public void delete() { public void delete() {
models.values().forEach(InstancedModel::delete); models.values().forEach(InstancedModel::delete);
models.clear(); models.clear();
materials.clear(); renderLists.clear();
renderables.clear();
} }
/** /**
@ -74,21 +71,35 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
.forEach(GPUInstancer::clear); .forEach(GPUInstancer::clear);
} }
public void init(MeshPool allocator) { public void init() {
for (var instanced : uninitialized) { for (var instanced : uninitialized) {
var map = instanced.init(allocator); instanced.init();
map.forEach((material, renderable) -> { for (Renderable renderable : instanced.getLayers()) {
materials.put(material.getRenderType(), material); renderLists.put(renderable.getMaterial()
renderables.get(material).add(renderable); .getRenderType(), renderable);
}); }
} }
uninitialized.clear(); uninitialized.clear();
} }
private InstancedModel<D> createInstancer(ModelSupplier model) { private InstancedModel<D> createInstancer(ModelSupplier model) {
var instancer = new InstancedModel<>(new GPUInstancer<>(type), model); var instancer = new InstancedModel<>(type, model);
uninitialized.add(instancer); uninitialized.add(instancer);
return instancer; return instancer;
} }
/**
* Adds all the RenderTypes that this InstancerFactory will render to the given set.
* @param layersToProcess The set of RenderTypes that the InstancingEngine will process.
*/
public void gatherLayers(Set<RenderType> layersToProcess) {
layersToProcess.addAll(renderLists.keySet());
}
public List<Renderable> getRenderList(RenderType type) {
var out = renderLists.get(type);
out.removeIf(Renderable::shouldRemove);
return out;
}
} }

View file

@ -1,10 +1,13 @@
package com.jozufozu.flywheel.backend.instancing.instancing; package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.backend.model.MeshPool;
@ -12,28 +15,33 @@ import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelSupplier; import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.util.Pair; import com.jozufozu.flywheel.util.Pair;
public class InstancedModel<D extends InstanceData> { public class InstancedModel<D extends InstancedPart> {
final GPUInstancer<D> instancer; public final GPUInstancer<D> instancer;
final ModelSupplier model; public final ModelSupplier model;
private Map<Material, Layer> layers; private List<Layer> layers;
public InstancedModel(GPUInstancer<D> instancer, ModelSupplier model) { public InstancedModel(StructType<D> type, ModelSupplier model) {
this.instancer = instancer;
this.model = model; this.model = model;
this.instancer = new GPUInstancer<>(this, type);
} }
public Map<Material, ? extends Renderable> init(MeshPool allocator) { public void init() {
instancer.init(); instancer.init();
buildLayers();
}
public List<? extends Renderable> getLayers() {
return layers;
}
private void buildLayers() {
layers = model.get() layers = model.get()
.entrySet() .entrySet()
.stream() .stream()
.map(entry -> Pair.of(entry.getKey(), new Layer(allocator, entry.getKey(), entry.getValue()))) .map(entry -> new Layer(entry.getKey(), entry.getValue()))
.collect(ImmutableMap.toImmutableMap(Pair::first, Pair::second)); .toList();
return layers;
} }
private class Layer implements Renderable { private class Layer implements Renderable {
@ -42,14 +50,25 @@ public class InstancedModel<D extends InstanceData> {
MeshPool.BufferedMesh bufferedMesh; MeshPool.BufferedMesh bufferedMesh;
GlVertexArray vao; GlVertexArray vao;
private Layer(MeshPool allocator, Material material, Mesh mesh) { private Layer(Material material, Mesh mesh) {
this.material = material; this.material = material;
vao = new GlVertexArray(); vao = new GlVertexArray();
bufferedMesh = allocator.alloc(mesh); bufferedMesh = MeshPool.getInstance()
.alloc(mesh);
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount(); instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount()); vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
} }
@Override
public Material getMaterial() {
return material;
}
@Override
public VertexType getVertexType() {
return bufferedMesh.getVertexType();
}
@Override @Override
public void render() { public void render() {
if (invalid()) return; if (invalid()) return;
@ -61,9 +80,6 @@ public class InstancedModel<D extends InstanceData> {
if (instancer.glInstanceCount > 0) { if (instancer.glInstanceCount > 0) {
bufferedMesh.drawInstances(vao, instancer.glInstanceCount); bufferedMesh.drawInstances(vao, instancer.glInstanceCount);
} }
// persistent mapping sync point
instancer.vbo.doneForThisFrame();
} }
} }
@ -72,6 +88,9 @@ public class InstancedModel<D extends InstanceData> {
return invalid(); return invalid();
} }
/**
* Only {@code true} if the InstancedModel has been destroyed.
*/
private boolean invalid() { private boolean invalid() {
return instancer.vbo == null || bufferedMesh == null || vao == null; return instancer.vbo == null || bufferedMesh == null || vao == null;
} }
@ -105,7 +124,7 @@ public class InstancedModel<D extends InstanceData> {
instancer.vbo.delete(); instancer.vbo.delete();
instancer.vbo = null; instancer.vbo = null;
for (var layer : layers.values()) { for (var layer : layers) {
layer.delete(); layer.delete();
} }
} }

View file

@ -1,38 +1,59 @@
package com.jozufozu.flywheel.backend.instancing.instancing; package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedSet;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.CoreShaderInfoMap; import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo; import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry; import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Textures; import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet; import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
public class InstancingEngine<P extends WorldProgram> implements Engine { public class InstancingEngine<P extends WorldProgram> implements Engine {
@ -41,11 +62,10 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO; protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ProgramCompiler<P> context; protected final ProgramCompiler<P> context;
private MeshPool allocator;
protected final Map<StructType<? extends InstanceData>, GPUInstancerFactory<?>> factories = new HashMap<>(); protected final Map<StructType<? extends InstancedPart>, GPUInstancerFactory<?>> factories = new HashMap<>();
protected final Set<RenderType> toRender = new HashSet<>(); protected final Set<RenderType> layersToProcess = new HashSet<>();
private final WeakHashSet<OriginShiftListener> listeners; private final WeakHashSet<OriginShiftListener> listeners;
private int vertexCount; private int vertexCount;
@ -60,7 +80,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@NotNull @NotNull
@Override @Override
public <D extends InstanceData> GPUInstancerFactory<D> factory(StructType<D> type) { public <D extends InstancedPart> GPUInstancerFactory<D> factory(StructType<D> type) {
return (GPUInstancerFactory<D>) factories.computeIfAbsent(type, GPUInstancerFactory::new); return (GPUInstancerFactory<D>) factories.computeIfAbsent(type, GPUInstancerFactory::new);
} }
@ -74,14 +94,18 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
var vp = context.viewProjection().copy(); var vp = context.viewProjection().copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ); vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
for (RenderType renderType : toRender) { for (RenderType renderType : layersToProcess) {
render(renderType, camX, camY, camZ, vp, context.level()); render(renderType, camX, camY, camZ, vp, context.level());
} }
toRender.clear(); layersToProcess.clear();
} }
@Override @Override
public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) { public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) {
if (!layersToProcess.remove(type)) {
return;
}
var camX = context.camX() - originCoordinate.getX(); var camX = context.camX() - originCoordinate.getX();
var camY = context.camY() - originCoordinate.getY(); var camY = context.camY() - originCoordinate.getY();
var camZ = context.camZ() - originCoordinate.getZ(); var camZ = context.camZ() - originCoordinate.getZ();
@ -90,9 +114,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
var vp = context.viewProjection().copy(); var vp = context.viewProjection().copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ); vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
if (toRender.remove(type)) { render(type, camX, camY, camZ, vp, context.level());
render(type, camX, camY, camZ, vp, context.level());
}
} }
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) { protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
@ -101,55 +123,36 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
type.setupRenderState(); type.setupRenderState();
Textures.bindActiveTextures(); Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = getCoreShaderInfo(); CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
for (var entry : factories.entrySet()) { for (var entry : factories.entrySet()) {
var instanceType = entry.getKey(); var instanceType = entry.getKey();
var factory = entry.getValue(); var factory = entry.getValue();
var materials = factory.materials.get(type); var toRender = factory.getRenderList(type);
for (Material material : materials) {
var toRender = factory.renderables.get(material);
toRender.removeIf(Renderable::shouldRemove);
if (!toRender.isEmpty()) { if (toRender.isEmpty()) {
setup(instanceType, material, coreShaderInfo, camX, camY, camZ, viewProjection, level); continue;
instanceCount += factory.getInstanceCount();
vertexCount += factory.getVertexCount();
toRender.forEach(Renderable::render);
}
} }
for (Renderable renderable : toRender) {
setup(instanceType, renderable.getMaterial(), coreShaderInfo, camX, camY, camZ, viewProjection, level, renderable.getVertexType());
renderable.render();
}
instanceCount += factory.getInstanceCount();
vertexCount += factory.getVertexCount();
} }
type.clearRenderState(); type.clearRenderState();
} }
protected CoreShaderInfo getCoreShaderInfo() { protected P setup(StructType<?> instanceType, Material material, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level, VertexType vertexType) {
CoreShaderInfo coreShaderInfo;
ShaderInstance coreShader = RenderSystem.getShader();
if (coreShader != null) {
String coreShaderName = coreShader.getName();
coreShaderInfo = CoreShaderInfoMap.getInfo(coreShaderName);
} else {
coreShaderInfo = null;
}
if (coreShaderInfo == null) {
coreShaderInfo = CoreShaderInfo.DEFAULT;
}
return coreShaderInfo;
}
protected P setup(StructType<?> instanceType, Material material, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) { P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceType.getInstanceShader(),
float alphaDiscard = coreShaderInfo.alphaDiscard(); material.getVertexShader(), material.getFragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(),
if (alphaDiscard == 0) { coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
alphaDiscard = 0.0001f;
} else if (alphaDiscard < 0) {
alphaDiscard = 0;
}
P program = context.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL, instanceType.getInstanceShader(), material.getVertexShader(), material.getFragmentShader(), alphaDiscard, coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
program.bind(); program.bind();
program.uploadUniforms(camX, camY, camZ, viewProjection, level); program.uploadUniforms(camX, camY, camZ, viewProjection, level);
@ -187,15 +190,15 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
public void beginFrame(Camera info) { public void beginFrame(Camera info) {
checkOriginDistance(info); checkOriginDistance(info);
MeshPool allocator = getModelAllocator();
for (GPUInstancerFactory<?> factory : factories.values()) { for (GPUInstancerFactory<?> factory : factories.values()) {
factory.init(allocator); factory.init();
toRender.addAll(factory.materials.keySet()); factory.gatherLayers(layersToProcess);
} }
allocator.flush(); MeshPool.getInstance()
.flush();
} }
private void checkOriginDistance(Camera info) { private void checkOriginDistance(Camera info) {
@ -228,17 +231,130 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
} }
private MeshPool getModelAllocator() { public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) {
if (allocator == null) { var dataByStage = getDataByStage(levelRenderer, level);
allocator = createAllocator(); if (dataByStage.isEmpty()) {
return;
}
var map = modelsToParts(dataByStage);
var stateSnapshot = GameStateRegistry.takeSnapshot();
Vec3 cameraPosition = camera.getPosition();
var camX = cameraPosition.x - originCoordinate.getX();
var camY = cameraPosition.y - originCoordinate.getY();
var camZ = cameraPosition.z - originCoordinate.getZ();
// don't want to mutate viewProjection
var vp = projectionMatrix.copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
GlBuffer instanceBuffer = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
GlVertexArray crumblingVAO = new GlVertexArray();
crumblingVAO.bind();
// crumblingVAO.bindAttributes();
for (var entry : map.entrySet()) {
var model = entry.getKey();
var parts = entry.getValue();
if (parts.isEmpty()) {
continue;
}
StructType<?> structType = parts.get(0).type;
for (var meshEntry : model.get()
.entrySet()) {
Material material = meshEntry.getKey();
Mesh mesh = meshEntry.getValue();
MeshPool.BufferedMesh bufferedMesh = MeshPool.getInstance()
.get(mesh);
if (bufferedMesh == null || !bufferedMesh.isGpuResident()) {
continue;
}
material.getRenderType().setupRenderState();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
structType.getInstanceShader(), material.getVertexShader(), material.getFragmentShader(),
coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
GameStateRegistry.takeSnapshot()));
program.bind();
program.uploadUniforms(camX, camY, camZ, vp, level);
// bufferedMesh.drawInstances();
}
} }
return this.allocator;
} }
private static MeshPool createAllocator() { @NotNull
private Map<ModelSupplier, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
var map = new HashMap<ModelSupplier, List<InstancedPart>>();
// FIXME: Windows AMD Drivers don't like ..BaseVertex for (var entry : dataByStage.int2ObjectEntrySet()) {
return new MeshPool(Formats.POS_TEX_NORMAL); RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey());
// something about when we call this means that the textures are not ready for use on the first frame they should appear
if (currentLayer == null) {
continue;
}
for (var blockEntityInstance : entry.getValue()) {
for (var part : blockEntityInstance.getCrumblingParts()) {
if (part.getOwner() instanceof GPUInstancer instancer) {
// queue the instances for copying to the crumbling instance buffer
map.computeIfAbsent(instancer.parent.model, k -> new ArrayList<>()).add(part);
}
}
}
}
return map;
}
@Nonnull
private Int2ObjectMap<List<BlockEntityInstance<?>>> getDataByStage(LevelRenderer levelRenderer, ClientLevel level) {
var destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress();
if (destructionProgress.isEmpty()) {
return Int2ObjectMaps.emptyMap();
}
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
.getBlockEntityInstanceManager() instanceof BlockEntityInstanceManager beim)) {
return Int2ObjectMaps.emptyMap();
}
var dataByStage = new Int2ObjectArrayMap<List<BlockEntityInstance<?>>>();
for (var entry : destructionProgress.long2ObjectEntrySet()) {
SortedSet<BlockDestructionProgress> progresses = entry.getValue();
if (progresses == null || progresses.isEmpty()) {
continue;
}
int progress = progresses.last()
.getProgress();
var data = dataByStage.computeIfAbsent(progress, $ -> new ArrayList<>());
long pos = entry.getLongKey();
beim.getCrumblingInstances(pos, data);
}
return dataByStage;
} }
@FunctionalInterface @FunctionalInterface

View file

@ -1,8 +1,15 @@
package com.jozufozu.flywheel.backend.instancing.instancing; package com.jozufozu.flywheel.backend.instancing.instancing;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
public interface Renderable { public interface Renderable {
void render(); void render();
boolean shouldRemove(); boolean shouldRemove();
Material getMaterial();
VertexType getVertexType();
} }

View file

@ -5,7 +5,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
public class ElementBuffer { public class ElementBuffer {
private final GlBuffer buffer; public final GlBuffer buffer;
public final int elementCount; public final int elementCount;
public final GlNumericType eboIndexType; public final GlNumericType eboIndexType;
@ -14,12 +14,4 @@ public class ElementBuffer {
this.eboIndexType = indexType; this.eboIndexType = indexType;
this.elementCount = elementCount; this.elementCount = elementCount;
} }
public void bind() {
buffer.bind();
}
public void unbind() {
buffer.unbind();
}
} }

View file

@ -1,50 +1,65 @@
package com.jozufozu.flywheel.backend.model; package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
public class MeshPool { public class MeshPool {
protected final VertexType vertexType; private static MeshPool allocator;
private final List<BufferedMesh> models = new ArrayList<>(); public static MeshPool getInstance() {
if (allocator == null) {
allocator = new MeshPool();
}
return allocator;
}
public static void reset(ReloadRenderersEvent ignored) {
if (allocator != null) {
allocator.delete();
allocator = null;
}
}
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> allBuffered = new ArrayList<>();
private final List<BufferedMesh> pendingUpload = new ArrayList<>(); private final List<BufferedMesh> pendingUpload = new ArrayList<>();
private final GlBuffer vbo; private final GlBuffer vbo;
private int vertices; private long byteSize;
private boolean dirty; private boolean dirty;
private boolean anyToRemove; private boolean anyToRemove;
/** /**
* Create a new model pool. * Create a new mesh pool.
*
* @param vertexType The vertex type of the models that will be stored in the pool.
*/ */
public MeshPool(VertexType vertexType) { public MeshPool() {
this.vertexType = vertexType;
int stride = vertexType.getStride();
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.setGrowthMargin(stride * 64); vbo.setGrowthMargin(2048);
} }
/** /**
@ -54,18 +69,27 @@ public class MeshPool {
* @return A handle to the allocated model. * @return A handle to the allocated model.
*/ */
public BufferedMesh alloc(Mesh mesh) { public BufferedMesh alloc(Mesh mesh) {
BufferedMesh bufferedModel = new BufferedMesh(mesh, vertices); return meshes.computeIfAbsent(mesh, m -> {
vertices += mesh.getVertexCount(); BufferedMesh bufferedModel = new BufferedMesh(m, byteSize);
models.add(bufferedModel); byteSize += m.size();
pendingUpload.add(bufferedModel); allBuffered.add(bufferedModel);
pendingUpload.add(bufferedModel);
dirty = true; dirty = true;
return bufferedModel; return bufferedModel;
});
}
@Nullable
public BufferedMesh get(Mesh mesh) {
return meshes.get(mesh);
} }
public void flush() { public void flush() {
if (dirty) { if (dirty) {
if (anyToRemove) processDeletions(); if (anyToRemove) {
processDeletions();
}
if (realloc()) { if (realloc()) {
uploadAll(); uploadAll();
@ -80,21 +104,28 @@ public class MeshPool {
private void processDeletions() { private void processDeletions() {
// remove deleted models // remove deleted meshes
models.removeIf(BufferedMesh::isDeleted); allBuffered.removeIf(bufferedMesh -> {
boolean deleted = bufferedMesh.isDeleted();
if (deleted) {
meshes.remove(bufferedMesh.mesh);
}
return deleted;
});
// re-evaluate first vertex for each model // re-evaluate first vertex for each model
int vertices = 0; int byteIndex = 0;
for (BufferedMesh model : models) { for (BufferedMesh model : allBuffered) {
if (model.first != vertices) if (model.byteIndex != byteIndex) {
pendingUpload.add(model); pendingUpload.add(model);
}
model.first = vertices; model.byteIndex = byteIndex;
vertices += model.mesh.getVertexCount(); byteIndex += model.mesh.size();
} }
this.vertices = vertices; this.byteSize = byteIndex;
this.anyToRemove = false; this.anyToRemove = false;
} }
@ -104,20 +135,20 @@ public class MeshPool {
* @return true if the buffer was reallocated * @return true if the buffer was reallocated
*/ */
private boolean realloc() { private boolean realloc() {
return vbo.ensureCapacity((long) vertices * vertexType.getStride()); return vbo.ensureCapacity(byteSize);
} }
private void uploadAll() { private void uploadAll() {
try (MappedBuffer buffer = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap()); ByteBuffer buffer = mapped.unwrap();
int vertices = 0; int byteIndex = 0;
for (BufferedMesh model : models) { for (BufferedMesh model : allBuffered) {
model.first = vertices; model.byteIndex = byteIndex;
model.buffer(writer); model.buffer(buffer);
vertices += model.mesh.getVertexCount(); byteIndex += model.mesh.size();
} }
} catch (Exception e) { } catch (Exception e) {
@ -126,10 +157,10 @@ public class MeshPool {
} }
private void uploadPending() { private void uploadPending() {
try (MappedBuffer buffer = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap()); ByteBuffer buffer = mapped.unwrap();
for (BufferedMesh model : pendingUpload) { for (BufferedMesh model : pendingUpload) {
model.buffer(writer); model.buffer(buffer);
} }
pendingUpload.clear(); pendingUpload.clear();
} catch (Exception e) { } catch (Exception e) {
@ -139,49 +170,63 @@ public class MeshPool {
public void delete() { public void delete() {
vbo.delete(); vbo.delete();
meshes.clear();
allBuffered.clear();
pendingUpload.clear();
} }
public class BufferedMesh { public class BufferedMesh {
private final ElementBuffer ebo; private final ElementBuffer ebo;
private final Mesh mesh; private final Mesh mesh;
private int first; private final BufferLayout layout;
private long byteIndex;
private boolean deleted; private boolean deleted;
private boolean gpuResident = false;
private final Set<GlVertexArray> boundTo = new HashSet<>(); private final Set<GlVertexArray> boundTo = new HashSet<>();
public BufferedMesh(Mesh mesh, int first) { public BufferedMesh(Mesh mesh, long byteIndex) {
this.mesh = mesh; this.mesh = mesh;
this.first = first; this.byteIndex = byteIndex;
this.ebo = mesh.createEBO(); this.ebo = mesh.createEBO();
this.layout = mesh.getType()
.getLayout();
} }
public void drawCall(GlVertexArray vao) { public void drawCall(GlVertexArray vao) {
attachTo(vao); drawInstances(vao, 1);
vao.bind();
this.ebo.bind();
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, this.first);
} }
public void drawInstances(GlVertexArray vao, int instanceCount) { public void drawInstances(GlVertexArray vao, int instanceCount) {
if (mesh.getVertexCount() <= 0 || isDeleted()) return; if (hasAnythingToRender()) return;
attachTo(vao); setup(vao);
vao.bind(); draw(instanceCount);
this.ebo.bind();
//Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first));
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, instanceCount, this.first);
} }
private void attachTo(GlVertexArray vao) { private boolean hasAnythingToRender() {
return mesh.getVertexCount() <= 0 || isDeleted();
}
private void draw(int instanceCount) {
if (instanceCount > 1) {
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, instanceCount);
} else {
GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0);
}
}
private void setup(GlVertexArray vao) {
if (this.boundTo.add(vao)) { if (this.boundTo.add(vao)) {
vao.enableArrays(getAttributeCount()); vao.enableArrays(getAttributeCount());
vao.bindAttributes(MeshPool.this.vbo, 0, MeshPool.this.vertexType.getLayout()); vao.bindAttributes(MeshPool.this.vbo, 0, this.layout, this.byteIndex);
} }
vao.bindElementArray(this.ebo.buffer);
vao.bind();
} }
public boolean isDeleted() { public boolean isDeleted() {
@ -194,14 +239,23 @@ public class MeshPool {
this.deleted = true; this.deleted = true;
} }
private void buffer(VertexWriter writer) { private void buffer(ByteBuffer buffer) {
writer.seekToVertex(this.first); this.mesh.writeInto(buffer, this.byteIndex);
writer.writeVertexList(this.mesh.getReader());
this.boundTo.clear(); this.boundTo.clear();
this.gpuResident = true;
} }
public int getAttributeCount() { public int getAttributeCount() {
return MeshPool.this.vertexType.getLayout().getAttributeCount(); return this.layout.getAttributeCount();
}
public boolean isGpuResident() {
return gpuResident;
}
public VertexType getVertexType() {
return this.mesh.getType();
} }
} }

View file

@ -10,6 +10,9 @@ import java.util.Map;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.OptifineHandler;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.ShaderInstance;
public class CoreShaderInfoMap { public class CoreShaderInfoMap {
private static final Map<String, CoreShaderInfo> MAP = new HashMap<>(); private static final Map<String, CoreShaderInfo> MAP = new HashMap<>();
@ -86,6 +89,29 @@ public class CoreShaderInfoMap {
public record CoreShaderInfo(float alphaDiscard, boolean appliesDiffuse, FogType fogType) { public record CoreShaderInfo(float alphaDiscard, boolean appliesDiffuse, FogType fogType) {
public static final CoreShaderInfo DEFAULT = new CoreShaderInfo(-1, false, NO_FOG); public static final CoreShaderInfo DEFAULT = new CoreShaderInfo(-1, false, NO_FOG);
public static CoreShaderInfo get() {
CoreShaderInfo out = null;
ShaderInstance coreShader = RenderSystem.getShader();
if (coreShader != null) {
String coreShaderName = coreShader.getName();
out = getInfo(coreShaderName);
}
if (out == null) {
out = DEFAULT;
}
return out;
}
public float getAdjustedAlphaDiscard() {
float alphaDiscard = alphaDiscard();
if (alphaDiscard == 0) {
alphaDiscard = 0.0001f;
} else if (alphaDiscard < 0) {
alphaDiscard = 0;
}
return alphaDiscard;
}
public enum FogType { public enum FogType {
NO_FOG, NO_FOG,
COLOR_FOG, COLOR_FOG,

View file

@ -2,10 +2,8 @@ package com.jozufozu.flywheel.core;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES; import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.glDrawArrays; import static org.lwjgl.opengl.GL11.glDrawArrays;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
@ -52,8 +50,8 @@ public class FullscreenQuad {
vao.enableArrays(1); vao.enableArrays(1);
vao.bindAttributes(vbo, 0, LAYOUT); vao.bindAttributes(vbo, 0, LAYOUT, 0L);
} }
} }
public void draw() { public void draw() {

View file

@ -15,15 +15,9 @@ import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
/** /**
* A class to manage EBOs that index quads as triangles. * A class to manage EBOs that index quads as triangles.
*/ */
@Mod.EventBusSubscriber(Dist.CLIENT)
public class QuadConverter { public class QuadConverter {
private static QuadConverter INSTANCE; private static QuadConverter INSTANCE;
@ -42,7 +36,7 @@ public class QuadConverter {
return INSTANCE; return INSTANCE;
} }
private MappedGlBuffer ebo; private final MappedGlBuffer ebo;
private int quadCapacity; private int quadCapacity;
public QuadConverter() { public QuadConverter() {
@ -111,7 +105,6 @@ public class QuadConverter {
} }
// make sure this gets reset first so it has a chance to repopulate // make sure this gets reset first so it has a chance to repopulate
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onRendererReload(ReloadRenderersEvent event) { public static void onRendererReload(ReloadRenderersEvent event) {
if (INSTANCE != null) { if (INSTANCE != null) {
INSTANCE.delete(); INSTANCE.delete();

View file

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
@ -34,15 +33,11 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.level.BlockDestructionProgress; import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
// TODO: merge directly into InstancingEngine for efficiency // TODO: merge directly into InstancingEngine for efficiency
/** /**
* Responsible for rendering the crumbling overlay for instanced block entities. * Responsible for rendering the crumbling overlay for instanced block entities.
*/ */
@Mod.EventBusSubscriber(Dist.CLIENT)
public class CrumblingRenderer { public class CrumblingRenderer {
private static Lazy<State> STATE; private static Lazy<State> STATE;
@ -128,7 +123,6 @@ public class CrumblingRenderer {
return breakingEntities; return breakingEntities;
} }
@SubscribeEvent
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld(); ClientLevel world = event.getWorld();
if (Backend.isOn() && world != null) { if (Backend.isOn() && world != null) {
@ -176,23 +170,25 @@ public class CrumblingRenderer {
currentLayer.setupRenderState(); currentLayer.setupRenderState();
Textures.bindActiveTextures(); Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = getCoreShaderInfo(); CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
for (var entry : factories.entrySet()) { for (var entry : factories.entrySet()) {
var instanceType = entry.getKey(); var instanceType = entry.getKey();
var factory = entry.getValue(); var factory = entry.getValue();
var materials = factory.materials.get(type); var toRender = factory.getRenderList(type);
for (Material material : materials) {
var toRender = factory.renderables.get(material);
toRender.removeIf(Renderable::shouldRemove);
if (!toRender.isEmpty()) { if (toRender.isEmpty()) {
setup(instanceType, material, coreShaderInfo, camX, camY, camZ, viewProjection, level); continue;
toRender.forEach(Renderable::render);
}
} }
for (var renderable : toRender) {
setup(instanceType, renderable.getMaterial(), coreShaderInfo, camX, camY, camZ, viewProjection, level, renderable.getVertexType());
renderable.render();
}
} }
currentLayer.clearRenderState(); currentLayer.clearRenderState();

View file

@ -34,7 +34,7 @@ public class ModelPart implements Mesh {
cuboid.buffer(writer); cuboid.buffer(writer);
} }
reader = writer.intoReader(); reader = writer.intoReader(this.vertices);
} }
} }

View file

@ -5,10 +5,10 @@ import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
public class ConditionalInstance<D extends InstanceData> { public class ConditionalInstance<D extends InstancedPart> {
final Instancer<D> model; final Instancer<D> model;
ICondition condition; ICondition condition;

View file

@ -5,10 +5,10 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
public class GroupInstance<D extends InstanceData> extends AbstractCollection<D> { public class GroupInstance<D extends InstancedPart> extends AbstractCollection<D> {
final Instancer<D> model; final Instancer<D> model;
final List<D> backing; final List<D> backing;
@ -48,14 +48,14 @@ public class GroupInstance<D extends InstanceData> extends AbstractCollection<D>
} }
} else { } else {
List<D> unnecessary = backing.subList(count, size); List<D> unnecessary = backing.subList(count, size);
unnecessary.forEach(InstanceData::delete); unnecessary.forEach(InstancedPart::delete);
unnecessary.clear(); unnecessary.clear();
} }
return true; return true;
} }
public InstanceData addInstance() { public InstancedPart addInstance() {
D instance = model.createInstance(); D instance = model.createInstance();
backing.add(instance); backing.add(instance);
@ -78,7 +78,7 @@ public class GroupInstance<D extends InstanceData> extends AbstractCollection<D>
@Override @Override
public void clear() { public void clear() {
backing.forEach(InstanceData::delete); backing.forEach(InstancedPart::delete);
backing.clear(); backing.clear();
} }
} }

View file

@ -6,10 +6,10 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Instancer;
public class SelectInstance<D extends InstanceData> { public class SelectInstance<D extends InstancedPart> {
final List<Instancer<D>> models; final List<Instancer<D>> models;

View file

@ -4,6 +4,7 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.model.ElementBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.QuadConverter;
@ -77,7 +78,9 @@ public interface Mesh {
return getVertexCount() == 0; return getVertexCount() == 0;
} }
default void writeInto(ByteBuffer buffer) { default void writeInto(ByteBuffer buffer, long byteIndex) {
getType().createWriter(buffer).writeVertexList(getReader()); VertexWriter writer = getType().createWriter(buffer);
writer.seek(byteIndex);
writer.writeVertexList(getReader());
} }
} }

View file

@ -1,11 +1,12 @@
package com.jozufozu.flywheel.core.structs; package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.util.Color; import com.jozufozu.flywheel.util.Color;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
public abstract class BasicData extends InstanceData implements FlatLit<BasicData> { public abstract class ColoredLitPart extends InstancedPart implements FlatLit<ColoredLitPart> {
public byte blockLight; public byte blockLight;
public byte skyLight; public byte skyLight;
@ -15,15 +16,19 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
public byte b = (byte) 0xFF; public byte b = (byte) 0xFF;
public byte a = (byte) 0xFF; public byte a = (byte) 0xFF;
public ColoredLitPart(StructType<?> type) {
super(type);
}
@Override @Override
public BasicData setBlockLight(int blockLight) { public ColoredLitPart setBlockLight(int blockLight) {
this.blockLight = (byte) blockLight; this.blockLight = (byte) blockLight;
markDirty(); markDirty();
return this; return this;
} }
@Override @Override
public BasicData setSkyLight(int skyLight) { public ColoredLitPart setSkyLight(int skyLight) {
this.skyLight = (byte) skyLight; this.skyLight = (byte) skyLight;
markDirty(); markDirty();
return this; return this;
@ -34,7 +39,7 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
return LightTexture.pack(this.blockLight, this.skyLight); return LightTexture.pack(this.blockLight, this.skyLight);
} }
public BasicData setColor(Color color) { public ColoredLitPart setColor(Color color) {
this.r = (byte) color.getRed(); this.r = (byte) color.getRed();
this.g = (byte) color.getGreen(); this.g = (byte) color.getGreen();
this.b = (byte) color.getBlue(); this.b = (byte) color.getBlue();
@ -43,11 +48,11 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
return this; return this;
} }
public BasicData setColor(int color) { public ColoredLitPart setColor(int color) {
return setColor(color, false); return setColor(color, false);
} }
public BasicData setColor(int color, boolean alpha) { public ColoredLitPart setColor(int color, boolean alpha) {
byte r = (byte) ((color >> 16) & 0xFF); byte r = (byte) ((color >> 16) & 0xFF);
byte g = (byte) ((color >> 8) & 0xFF); byte g = (byte) ((color >> 8) & 0xFF);
byte b = (byte) (color & 0xFF); byte b = (byte) (color & 0xFF);
@ -60,11 +65,11 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
} }
} }
public BasicData setColor(int r, int g, int b) { public ColoredLitPart setColor(int r, int g, int b) {
return setColor((byte) r, (byte) g, (byte) b); return setColor((byte) r, (byte) g, (byte) b);
} }
public BasicData setColor(byte r, byte g, byte b) { public ColoredLitPart setColor(byte r, byte g, byte b) {
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
@ -72,7 +77,7 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
return this; return this;
} }
public BasicData setColor(byte r, byte g, byte b, byte a) { public ColoredLitPart setColor(byte r, byte g, byte b, byte a) {
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;
@ -80,5 +85,4 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
markDirty(); markDirty();
return this; return this;
} }
} }

View file

@ -7,9 +7,9 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter;
public abstract class BasicWriterUnsafe<D extends BasicData> extends UnsafeBufferWriter<D> { public abstract class ColoredLitWriterUnsafe<D extends ColoredLitPart> extends UnsafeBufferWriter<D> {
public BasicWriterUnsafe(StructType<D> structType, ByteBuffer byteBuffer) { public ColoredLitWriterUnsafe(StructType<D> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer); super(structType, byteBuffer);
} }

View file

@ -1,20 +1,20 @@
package com.jozufozu.flywheel.core.structs; package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.InstancedPart;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
/** /**
* An interface that implementors of {@link InstanceData} should also implement * An interface that implementors of {@link InstancedPart} should also implement
* if they wish to make use of Flywheel's provided light update methods. * if they wish to make use of Flywheel's provided light update methods.
* <p> * <p>
* This only covers flat lighting, smooth lighting is still TODO. * This only covers flat lighting, smooth lighting is still TODO.
* *
* @param <D> The name of the class that implements this interface. * @param <D> The name of the class that implements this interface.
*/ */
public interface FlatLit<D extends InstanceData & FlatLit<D>> { public interface FlatLit<D extends InstancedPart & 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.

View file

@ -14,7 +14,7 @@ import net.minecraft.resources.ResourceLocation;
public class InstanceShaders { public class InstanceShaders {
public static final BiConsumer<ErrorReporter, SourceFile> CHECK = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0); public static final BiConsumer<ErrorReporter, SourceFile> CHECK = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0);
public static final FileResolution MODEL = create(ResourceUtil.subPath(Names.MODEL, ".vert")); public static final FileResolution TRANSFORMED = create(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution ORIENTED = create(ResourceUtil.subPath(Names.ORIENTED, ".vert")); public static final FileResolution ORIENTED = create(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
public static FileResolution create(ResourceLocation location) { public static FileResolution create(ResourceLocation location) {
@ -25,7 +25,7 @@ public class InstanceShaders {
} }
public static class Names { public static class Names {
public static final ResourceLocation MODEL = Flywheel.rl("instance/model"); public static final ResourceLocation TRANSFORMED = Flywheel.rl("instance/transformed");
public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented"); public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented");
} }
} }

View file

@ -1,12 +1,12 @@
package com.jozufozu.flywheel.core.structs; package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.structs.model.ModelData; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.model.ModelType; import com.jozufozu.flywheel.core.structs.model.TransformedType;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedType; import com.jozufozu.flywheel.core.structs.oriented.OrientedType;
public class StructTypes { public class StructTypes {
public static final StructType<ModelData> MODEL = new ModelType(); public static final StructType<TransformedPart> TRANSFORMED = new TransformedType();
public static final StructType<OrientedData> ORIENTED = new OrientedType(); public static final StructType<OrientedPart> ORIENTED = new OrientedType();
} }

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.core.structs.model; package com.jozufozu.flywheel.core.structs.model;
import com.jozufozu.flywheel.core.structs.BasicData; import com.jozufozu.flywheel.core.structs.ColoredLitPart;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.util.transform.Transform; import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f; import com.mojang.math.Matrix3f;
@ -9,14 +10,18 @@ import com.mojang.math.Quaternion;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
public class ModelData extends BasicData implements Transform<ModelData> { public class TransformedPart extends ColoredLitPart implements Transform<TransformedPart> {
private static final Matrix4f EMPTY_MATRIX_4f = new Matrix4f(); private static final Matrix4f EMPTY_MATRIX_4f = new Matrix4f();
private static final Matrix3f EMPTY_MATRIX_3f = new Matrix3f(); private static final Matrix3f EMPTY_MATRIX_3f = new Matrix3f();
public final Matrix4f model = new Matrix4f(); public final Matrix4f model = new Matrix4f();
public final Matrix3f normal = new Matrix3f(); public final Matrix3f normal = new Matrix3f();
public ModelData setTransform(PoseStack stack) { public TransformedPart() {
super(StructTypes.TRANSFORMED);
}
public TransformedPart setTransform(PoseStack stack) {
markDirty(); markDirty();
this.model.load(stack.last().pose()); this.model.load(stack.last().pose());
@ -31,7 +36,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
* This will allow the gpu to quickly discard all geometry for this instance, effectively "turning it off". * This will allow the gpu to quickly discard all geometry for this instance, effectively "turning it off".
* </p> * </p>
*/ */
public ModelData setEmptyTransform() { public TransformedPart setEmptyTransform() {
markDirty(); markDirty();
this.model.load(EMPTY_MATRIX_4f); this.model.load(EMPTY_MATRIX_4f);
@ -39,7 +44,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
return this; return this;
} }
public ModelData loadIdentity() { public TransformedPart loadIdentity() {
markDirty(); markDirty();
this.model.setIdentity(); this.model.setIdentity();
@ -48,7 +53,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
} }
@Override @Override
public ModelData multiply(Quaternion quaternion) { public TransformedPart multiply(Quaternion quaternion) {
markDirty(); markDirty();
model.multiply(quaternion); model.multiply(quaternion);
@ -57,7 +62,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
} }
@Override @Override
public ModelData scale(float pX, float pY, float pZ) { public TransformedPart scale(float pX, float pY, float pZ) {
markDirty(); markDirty();
model.multiply(Matrix4f.createScaleMatrix(pX, pY, pZ)); model.multiply(Matrix4f.createScaleMatrix(pX, pY, pZ));
@ -79,7 +84,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
} }
@Override @Override
public ModelData translate(double x, double y, double z) { public TransformedPart translate(double x, double y, double z) {
markDirty(); markDirty();
model.multiplyWithTranslation((float) x, (float) y, (float) z); model.multiplyWithTranslation((float) x, (float) y, (float) z);
@ -87,14 +92,28 @@ public class ModelData extends BasicData implements Transform<ModelData> {
} }
@Override @Override
public ModelData mulPose(Matrix4f pose) { public TransformedPart mulPose(Matrix4f pose) {
this.model.multiply(pose); this.model.multiply(pose);
return this; return this;
} }
@Override @Override
public ModelData mulNormal(Matrix3f normal) { public TransformedPart mulNormal(Matrix3f normal) {
this.normal.mul(normal); this.normal.mul(normal);
return this; return this;
} }
@Override
public TransformedPart copy() {
var out = new TransformedPart();
out.model.load(this.model);
out.normal.load(this.normal);
out.r = this.r;
out.g = this.g;
out.b = this.b;
out.a = this.a;
out.blockLight = this.blockLight;
out.skyLight = this.skyLight;
return out;
}
} }

View file

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders; import com.jozufozu.flywheel.core.structs.InstanceShaders;
public class ModelType implements StructType<ModelData> { public class TransformedType implements StructType<TransformedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder() public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA) .addItems(CommonItems.LIGHT, CommonItems.RGBA)
@ -18,8 +18,8 @@ public class ModelType implements StructType<ModelData> {
.build(); .build();
@Override @Override
public ModelData create() { public TransformedPart create() {
return new ModelData(); return new TransformedPart();
} }
@Override @Override
@ -28,17 +28,17 @@ public class ModelType implements StructType<ModelData> {
} }
@Override @Override
public StructWriter<ModelData> getWriter(ByteBuffer backing) { public StructWriter<TransformedPart> getWriter(ByteBuffer backing) {
return new ModelWriterUnsafe(this, backing); return new TransformedWriterUnsafe(this, backing);
} }
@Override @Override
public FileResolution getInstanceShader() { public FileResolution getInstanceShader() {
return InstanceShaders.MODEL; return InstanceShaders.TRANSFORMED;
} }
@Override @Override
public void transform(ModelData d, ModelTransformer.Params b) { public void transform(TransformedPart d, ModelTransformer.Params b) {
b.transform(d.model, d.normal) b.transform(d.model, d.normal)
.color(d.r, d.g, d.b, d.a) .color(d.r, d.g, d.b, d.a)
.light(d.getPackedLight()); .light(d.getPackedLight());

View file

@ -3,17 +3,17 @@ package com.jozufozu.flywheel.core.structs.model;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.structs.BasicWriterUnsafe; import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe;
import com.jozufozu.flywheel.util.MatrixWrite; import com.jozufozu.flywheel.util.MatrixWrite;
public class ModelWriterUnsafe extends BasicWriterUnsafe<ModelData> { public class TransformedWriterUnsafe extends ColoredLitWriterUnsafe<TransformedPart> {
public ModelWriterUnsafe(StructType<ModelData> structType, ByteBuffer byteBuffer) { public TransformedWriterUnsafe(StructType<TransformedPart> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer); super(structType, byteBuffer);
} }
@Override @Override
protected void writeInternal(ModelData d) { protected void writeInternal(TransformedPart d) {
super.writeInternal(d); super.writeInternal(d);
long ptr = writePointer + 6; long ptr = writePointer + 6;

View file

@ -1,85 +0,0 @@
package com.jozufozu.flywheel.core.structs.oriented;
import com.jozufozu.flywheel.core.structs.BasicData;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import net.minecraft.core.BlockPos;
public class OrientedData extends BasicData {
public float posX;
public float posY;
public float posZ;
public float pivotX = 0.5f;
public float pivotY = 0.5f;
public float pivotZ = 0.5f;
public float qX;
public float qY;
public float qZ;
public float qW = 1;
public OrientedData setPosition(BlockPos pos) {
return setPosition(pos.getX(), pos.getY(), pos.getZ());
}
public OrientedData setPosition(Vector3f pos) {
return setPosition(pos.x(), pos.y(), pos.z());
}
public OrientedData setPosition(float x, float y, float z) {
this.posX = x;
this.posY = y;
this.posZ = z;
markDirty();
return this;
}
public OrientedData nudge(float x, float y, float z) {
this.posX += x;
this.posY += y;
this.posZ += z;
markDirty();
return this;
}
public OrientedData setPivot(Vector3f pos) {
return setPosition(pos.x(), pos.y(), pos.z());
}
public OrientedData setPivot(net.minecraft.world.phys.Vec3 pos) {
return setPosition((float) pos.x(), (float) pos.y(), (float) pos.z());
}
public OrientedData setPivot(float x, float y, float z) {
this.pivotX = x;
this.pivotY = y;
this.pivotZ = z;
markDirty();
return this;
}
public OrientedData setRotation(Quaternion q) {
return setRotation(q.i(), q.j(), q.k(), q.r());
}
public OrientedData setRotation(float x, float y, float z, float w) {
this.qX = x;
this.qY = y;
this.qZ = z;
this.qW = w;
markDirty();
return this;
}
public OrientedData resetRotation() {
this.qX = 0;
this.qY = 0;
this.qZ = 0;
this.qW = 1;
markDirty();
return this;
}
}

View file

@ -0,0 +1,111 @@
package com.jozufozu.flywheel.core.structs.oriented;
import com.jozufozu.flywheel.core.structs.ColoredLitPart;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import net.minecraft.core.BlockPos;
public class OrientedPart extends ColoredLitPart {
public float posX;
public float posY;
public float posZ;
public float pivotX = 0.5f;
public float pivotY = 0.5f;
public float pivotZ = 0.5f;
public float qX;
public float qY;
public float qZ;
public float qW = 1;
public OrientedPart() {
super(StructTypes.ORIENTED);
}
public OrientedPart setPosition(BlockPos pos) {
return setPosition(pos.getX(), pos.getY(), pos.getZ());
}
public OrientedPart setPosition(Vector3f pos) {
return setPosition(pos.x(), pos.y(), pos.z());
}
public OrientedPart setPosition(float x, float y, float z) {
this.posX = x;
this.posY = y;
this.posZ = z;
markDirty();
return this;
}
public OrientedPart nudge(float x, float y, float z) {
this.posX += x;
this.posY += y;
this.posZ += z;
markDirty();
return this;
}
public OrientedPart setPivot(Vector3f pos) {
return setPosition(pos.x(), pos.y(), pos.z());
}
public OrientedPart setPivot(net.minecraft.world.phys.Vec3 pos) {
return setPosition((float) pos.x(), (float) pos.y(), (float) pos.z());
}
public OrientedPart setPivot(float x, float y, float z) {
this.pivotX = x;
this.pivotY = y;
this.pivotZ = z;
markDirty();
return this;
}
public OrientedPart setRotation(Quaternion q) {
return setRotation(q.i(), q.j(), q.k(), q.r());
}
public OrientedPart setRotation(float x, float y, float z, float w) {
this.qX = x;
this.qY = y;
this.qZ = z;
this.qW = w;
markDirty();
return this;
}
public OrientedPart resetRotation() {
this.qX = 0;
this.qY = 0;
this.qZ = 0;
this.qW = 1;
markDirty();
return this;
}
@Override
public OrientedPart copy() {
var out = new OrientedPart();
out.posX = this.posX;
out.posY = this.posY;
out.posZ = this.posZ;
out.pivotX = this.pivotX;
out.pivotY = this.pivotY;
out.pivotZ = this.pivotZ;
out.qX = this.qX;
out.qY = this.qY;
out.qZ = this.qZ;
out.qW = this.qW;
out.r = this.r;
out.g = this.g;
out.b = this.b;
out.a = this.a;
out.blockLight = this.blockLight;
out.skyLight = this.skyLight;
return out;
}
}

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders; import com.jozufozu.flywheel.core.structs.InstanceShaders;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
public class OrientedType implements StructType<OrientedData> { public class OrientedType implements StructType<OrientedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder() public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA) .addItems(CommonItems.LIGHT, CommonItems.RGBA)
@ -19,8 +19,8 @@ public class OrientedType implements StructType<OrientedData> {
.build(); .build();
@Override @Override
public OrientedData create() { public OrientedPart create() {
return new OrientedData(); return new OrientedPart();
} }
@Override @Override
@ -29,7 +29,7 @@ public class OrientedType implements StructType<OrientedData> {
} }
@Override @Override
public StructWriter<OrientedData> getWriter(ByteBuffer backing) { public StructWriter<OrientedPart> getWriter(ByteBuffer backing) {
return new OrientedWriterUnsafe(this, backing); return new OrientedWriterUnsafe(this, backing);
} }
@ -39,7 +39,7 @@ public class OrientedType implements StructType<OrientedData> {
} }
@Override @Override
public void transform(OrientedData d, ModelTransformer.Params b) { public void transform(OrientedPart d, ModelTransformer.Params b) {
b.light(d.getPackedLight()) b.light(d.getPackedLight())
.color(d.r, d.g, d.b, d.a) .color(d.r, d.g, d.b, d.a)
.translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ) .translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ)

View file

@ -5,15 +5,15 @@ import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.structs.BasicWriterUnsafe; import com.jozufozu.flywheel.core.structs.ColoredLitWriterUnsafe;
public class OrientedWriterUnsafe extends BasicWriterUnsafe<OrientedData> { public class OrientedWriterUnsafe extends ColoredLitWriterUnsafe<OrientedPart> {
public OrientedWriterUnsafe(StructType<OrientedData> structType, ByteBuffer byteBuffer) { public OrientedWriterUnsafe(StructType<OrientedPart> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer); super(structType, byteBuffer);
} }
@Override @Override
protected void writeInternal(OrientedData d) { protected void writeInternal(OrientedPart d) {
long ptr = writePointer; long ptr = writePointer;
super.writeInternal(d); super.writeInternal(d);

View file

@ -52,6 +52,5 @@ public class BlockWriterUnsafe extends VertexWriterUnsafe<BlockVertex> {
MemoryUtil.memPutByte(ptr + 30, RenderMath.nb(nZ)); MemoryUtil.memPutByte(ptr + 30, RenderMath.nb(nZ));
ptr += 32; ptr += 32;
advance();
} }
} }

View file

@ -40,6 +40,5 @@ public class PosTexNormalWriterUnsafe extends VertexWriterUnsafe<PosTexNormalVer
MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ)); MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ));
ptr += 23; ptr += 23;
advance();
} }
} }

View file

@ -12,8 +12,6 @@ public abstract class VertexWriterUnsafe<V extends VertexType> implements Vertex
public final V type; public final V type;
protected final ByteBuffer buffer; protected final ByteBuffer buffer;
private int totalVertices;
private int writeVertex;
protected long ptr; protected long ptr;
protected VertexWriterUnsafe(V type, ByteBuffer buffer) { protected VertexWriterUnsafe(V type, ByteBuffer buffer) {
@ -22,21 +20,14 @@ public abstract class VertexWriterUnsafe<V extends VertexType> implements Vertex
this.ptr = MemoryUtil.memAddress(buffer); this.ptr = MemoryUtil.memAddress(buffer);
} }
protected void advance() {
writeVertex++;
// account for seeking
if (writeVertex > totalVertices) totalVertices = writeVertex;
}
@Override @Override
public void seekToVertex(int vertex) { public void seek(long offset) {
buffer.position(type.byteOffset(vertex)); buffer.position((int) offset);
writeVertex = vertex;
ptr = MemoryUtil.memAddress(buffer); ptr = MemoryUtil.memAddress(buffer);
} }
@Override @Override
public VertexList intoReader() { public VertexList intoReader(int vertices) {
return type.createReader(buffer, totalVertices); return type.createReader(buffer, vertices);
} }
} }

View file

@ -9,16 +9,13 @@ import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class EntityWorldHandler { public class EntityWorldHandler {
@SubscribeEvent
public static void onEntityJoinWorld(EntityJoinWorldEvent event) { public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld()) if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
.queueAdd(event.getEntity()); .queueAdd(event.getEntity());
} }
@SubscribeEvent
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) { public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld()) if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
.remove(event.getEntity()); .remove(event.getEntity());

View file

@ -17,10 +17,8 @@ import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class ForgeEvents { public class ForgeEvents {
@SubscribeEvent
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) { public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
if (Minecraft.getInstance().options.renderDebug) { if (Minecraft.getInstance().options.renderDebug) {
@ -37,12 +35,10 @@ public class ForgeEvents {
} }
} }
@SubscribeEvent
public static void unloadWorld(WorldEvent.Unload event) { public static void unloadWorld(WorldEvent.Unload event) {
WorldAttached.invalidateWorld(event.getWorld()); WorldAttached.invalidateWorld(event.getWorld());
} }
@SubscribeEvent
public static void tickLight(TickEvent.ClientTickEvent e) { public static void tickLight(TickEvent.ClientTickEvent e) {
if (e.phase == TickEvent.Phase.END && Backend.isGameActive()) { if (e.phase == TickEvent.Phase.END && Backend.isGameActive()) {
LightUpdater.get(Minecraft.getInstance().level) LightUpdater.get(Minecraft.getInstance().level)

View file

@ -16,7 +16,7 @@ public interface ClientLevelExtension {
*/ */
Iterable<Entity> flywheel$getAllLoadedEntities(); Iterable<Entity> flywheel$getAllLoadedEntities();
static ClientLevelExtension cast(ClientLevel level) { static Iterable<Entity> getAllLoadedEntities(ClientLevel level) {
return (ClientLevelExtension) level; return ((ClientLevelExtension) level).flywheel$getAllLoadedEntities();
} }
} }

View file

@ -1,7 +1,11 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
@ -10,7 +14,7 @@ import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders; import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
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;
@ -24,7 +28,7 @@ public class BellInstance extends BlockEntityInstance<BellBlockEntity> implement
private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, new Material(Sheets.solidBlockSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)); private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, new Material(Sheets.solidBlockSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT));
private final OrientedData bell; private final OrientedPart bell;
private float lastRingTime = Float.NaN; private float lastRingTime = Float.NaN;
@ -59,12 +63,17 @@ public class BellInstance extends BlockEntityInstance<BellBlockEntity> implement
relight(getWorldPosition(), bell); relight(getWorldPosition(), bell);
} }
@Override
public void addCrumblingParts(List<InstancedPart> data) {
Collections.addAll(data, bell);
}
@Override @Override
public void remove() { public void remove() {
bell.delete(); bell.delete();
} }
private OrientedData createBellInstance() { private OrientedPart createBellInstance() {
return instancerManager.factory(StructTypes.ORIENTED) return instancerManager.factory(StructTypes.ORIENTED)
.model(MODEL) .model(MODEL)
.createInstance(); .createInstance();

View file

@ -1,10 +1,13 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
@ -12,8 +15,8 @@ import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders; import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
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;
@ -37,8 +40,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT))); private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)));
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT))); private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)));
private final OrientedData body; private final OrientedPart body;
private final ModelData lid; private final TransformedPart lid;
private final Float2FloatFunction lidProgress; private final Float2FloatFunction lidProgress;
private final Material renderMaterial; private final Material renderMaterial;
@ -108,22 +111,27 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
relight(getWorldPosition(), body, lid); relight(getWorldPosition(), body, lid);
} }
@Override
public void addCrumblingParts(List<InstancedPart> data) {
Collections.addAll(data, body, lid);
}
@Override @Override
public void remove() { public void remove() {
body.delete(); body.delete();
lid.delete(); lid.delete();
} }
private OrientedData baseInstance() { private OrientedPart baseInstance() {
return instancerManager.factory(StructTypes.ORIENTED) return instancerManager.factory(StructTypes.ORIENTED)
.model(BASE.apply(chestType, renderMaterial)) .model(BASE.apply(chestType, renderMaterial))
.createInstance(); .createInstance();
} }
private ModelData lidInstance() { private TransformedPart lidInstance() {
return instancerManager.factory(StructTypes.MODEL) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(LID.apply(chestType, renderMaterial)) .model(LID.apply(chestType, renderMaterial))
.createInstance(); .createInstance();
} }

View file

@ -13,7 +13,7 @@ import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders; import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -35,8 +35,8 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
private final PoseStack stack = new PoseStack(); private final PoseStack stack = new PoseStack();
private final ModelData body; private final TransformedPart body;
private ModelData contents; private TransformedPart contents;
private BlockState blockState; private BlockState blockState;
private boolean active; private boolean active;
@ -48,6 +48,11 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
contents = getContents(); contents = getContents();
} }
@Override
public boolean decreaseFramerateWithDistance() {
return false;
}
@Override @Override
public void tick() { public void tick() {
BlockState displayBlockState = entity.getDisplayBlockState(); BlockState displayBlockState = entity.getDisplayBlockState();
@ -153,7 +158,7 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
if (contents != null) contents.delete(); if (contents != null) contents.delete();
} }
private ModelData getContents() { private TransformedPart getContents() {
RenderShape shape = blockState.getRenderShape(); RenderShape shape = blockState.getRenderShape();
if (shape == RenderShape.ENTITYBLOCK_ANIMATED) { if (shape == RenderShape.ENTITYBLOCK_ANIMATED) {
@ -166,13 +171,13 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
if (shape == RenderShape.INVISIBLE) if (shape == RenderShape.INVISIBLE)
return null; return null;
return instancerManager.factory(StructTypes.MODEL) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(Models.block(blockState)) .model(Models.block(blockState))
.createInstance(); .createInstance();
} }
private ModelData getBody() { private TransformedPart getBody() {
return instancerManager.factory(StructTypes.MODEL) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(MODEL) .model(MODEL)
.createInstance(); .createInstance();
} }

View file

@ -1,7 +1,10 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Collections;
import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
@ -10,7 +13,7 @@ import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders; import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack; import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -33,8 +36,8 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
private final TextureAtlasSprite texture; private final TextureAtlasSprite texture;
private final ModelData base; private final TransformedPart base;
private final ModelData lid; private final TransformedPart lid;
private final PoseStack stack = new PoseStack(); private final PoseStack stack = new PoseStack();
private float lastProgress = Float.NaN; private float lastProgress = Float.NaN;
@ -87,6 +90,11 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
stack.popPose(); stack.popPose();
} }
@Override
public void addCrumblingParts(List<InstancedPart> data) {
Collections.addAll(data, base, lid);
}
@Override @Override
public void remove() { public void remove() {
base.delete(); base.delete();
@ -98,14 +106,14 @@ public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntit
relight(pos, base, lid); relight(pos, base, lid);
} }
private ModelData makeBaseInstance() { private TransformedPart makeBaseInstance() {
return instancerManager.factory(StructTypes.MODEL) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(BASE.apply(texture)) .model(BASE.apply(texture))
.createInstance(); .createInstance();
} }
private ModelData makeLidInstance() { private TransformedPart makeLidInstance() {
return instancerManager.factory(StructTypes.MODEL) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(LID.apply(texture)) .model(LID.apply(texture))
.createInstance(); .createInstance();
} }