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.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.FlwCommands;
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.Models;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
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.shader.NormalDebugStateProvider;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
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.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances;
@ -28,6 +35,7 @@ import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.CrashReportCallables;
import net.minecraftforge.fml.DistExecutor;
@ -75,8 +83,25 @@ public class Flywheel {
Backend.init();
forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload);
forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll);
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::onModelBake);

View file

@ -1,12 +1,19 @@
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 boolean dirty;
private boolean removed;
protected InstancedPart(StructType<?> type) {
this.type = type;
}
public final void markDirty() {
dirty = true;
owner.notifyDirty();
@ -34,8 +41,9 @@ public abstract class InstanceData {
return owner;
}
public InstanceData setOwner(Instancer<?> owner) {
public void setOwner(Instancer<?> 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.
*/
public interface Instancer<D extends InstanceData> {
public interface Instancer<D extends InstancedPart> {
/**
* @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;
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.

View file

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

View file

@ -11,5 +11,5 @@ public interface MaterialGroup {
* @param <D> The type representing the per instance data.
* @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;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
@ -19,7 +19,7 @@ public interface DynamicInstance extends Instance {
* <br>
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this instance.
* <br>
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here.
* {@link Instancer}/{@link InstancedPart} creation/acquisition is safe here.
*/
void beginFrame();

View file

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

View file

@ -3,9 +3,9 @@ package com.jozufozu.flywheel.api.vertex;
public interface VertexWriter {
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) {
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.concurrent.ConcurrentLinkedQueue;
import net.minecraftforge.api.distmarker.Dist;
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 {
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void onRenderWorldLast(RenderLevelLastEvent event) {
while (!runs.isEmpty()) {
runs.remove()

View file

@ -1,14 +1,19 @@
package com.jozufozu.flywheel.backend.gl;
import static org.lwjgl.opengl.GL32.GL_ALREADY_SIGNALED;
import static org.lwjgl.opengl.GL32.GL_CONDITION_SATISFIED;
import static org.lwjgl.opengl.GL32.GL_SIGNALED;
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_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.glDeleteSync;
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 {
private long fence;
@ -26,16 +31,38 @@ public class GlFence {
}
}
public void waitSync() {
public boolean poll() {
if (fence != 0) {
int waitReturn = GL_UNSIGNALED;
while (waitReturn != GL_ALREADY_SIGNALED && waitReturn != GL_CONDITION_SATISFIED) {
waitReturn = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 1);
}
glDeleteSync(fence);
poll0();
}
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;
}
}

View file

@ -42,20 +42,20 @@ public class GlStateTracker {
public record State(int[] buffers, int vao, int program) implements AutoCloseable {
public void restore() {
GlBufferType[] values = GlBufferType.values();
for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.buffers[i]) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
}
if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
}
if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao);
}
if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
GlBufferType[] values = GlBufferType.values();
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.
*/
private final int[] offsets = new int[MAX_ATTRIBS];
private final long[] offsets = new long[MAX_ATTRIBS];
/**
* Each attribute's stride.
*/
private final int[] strides = new int[MAX_ATTRIBS];
private int elementBufferBinding = 0;
public GlVertexArray() {
setHandle(GlStateManager._glGenVertexArrays());
@ -62,15 +64,20 @@ public class GlVertexArray extends GlObject {
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();
int targetBuffer = buffer.handle();
GlBufferType.ARRAY_BUFFER.bind(targetBuffer);
int i = startIndex;
int offset = 0;
int i = startAttrib;
final int stride = type.getStride();
for (VertexAttribute attribute : type.getAttributes()) {
@ -126,4 +133,13 @@ public class GlVertexArray extends GlObject {
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);
/**
* Call this after all draw calls using this buffer are complete.
*/
public void doneForThisFrame() {
}
protected void deleteInternal(int handle) {
GL20.glDeleteBuffers(handle);
}

View file

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

View file

@ -2,20 +2,20 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
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.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 boolean anyToRemove;
protected AbstractInstancer(Supplier<D> factory) {
this.factory = factory;
protected AbstractInstancer(StructType<D> type) {
this.type = type;
}
/**
@ -23,7 +23,7 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
*/
@Override
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) {
// 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.
ClientLevelExtension.cast(world)
.flywheel$getAllLoadedEntities()
ClientLevelExtension.getAllLoadedEntities(world)
.forEach(entityInstanceManager::add);
}

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.List;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.RenderContext;
@ -22,7 +23,6 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class InstancedRenderDispatcher {
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) {
if (!Backend.isGameActive() || event.phase == TickEvent.Phase.START) {
return;
@ -86,7 +85,6 @@ public class InstancedRenderDispatcher {
}
}
@SubscribeEvent
public static void onBeginFrame(BeginFrameEvent event) {
if (Backend.isGameActive() && Backend.isOn()) {
instanceWorlds.get(event.getWorld())
@ -108,7 +106,6 @@ public class InstancedRenderDispatcher {
instanceWorlds.get(world).renderAllRemaining(context);
}
@SubscribeEvent
public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld();
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.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
@ -16,7 +16,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
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 instanceCount;
@ -26,7 +26,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
@SuppressWarnings("unchecked")
@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);
}

View file

@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.List;
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.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.Engine;
@ -20,12 +20,12 @@ import net.minecraft.core.Vec3i;
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();
@SuppressWarnings("unchecked")
@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);
}

View file

@ -1,6 +1,6 @@
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.backend.instancing.AbstractInstancer;
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.VertexConsumer;
public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
public class CPUInstancer<D extends InstancedPart> extends AbstractInstancer<D> {
// private final Batched<D> batchingType;
//
// final ModelTransformer sbb;
public CPUInstancer(StructType<D> type) {
super(type::create);
super(type);
// batchingType = type;
//
// 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.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.InstancerFactory;
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.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;
private final StructType<D> type;

View file

@ -1,13 +1,17 @@
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.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
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());
}
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.
* 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;
}
protected InstancerFactory<ModelData> getTransformFactory() {
return instancerManager.factory(StructTypes.MODEL);
protected InstancerFactory<TransformedPart> getTransformFactory() {
return instancerManager.factory(StructTypes.TRANSFORMED);
}
protected InstancerFactory<OrientedData> getOrientedFactory() {
protected InstancerFactory<OrientedPart> getOrientedFactory() {
return instancerManager.factory(StructTypes.ORIENTED);
}

View file

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

View file

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

View file

@ -4,16 +4,16 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
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.InstancerFactory;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.model.ModelSupplier;
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.
* @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 StructType<D> type;
protected final List<InstancedModel<D>> uninitialized = new ArrayList<>();
// FIXME: these should not be public
public final Multimap<RenderType, Material> materials = HashMultimap.create();
public final Multimap<Material, Renderable> renderables = ArrayListMultimap.create();
private final ListMultimap<RenderType, Renderable> renderLists = ArrayListMultimap.create();
public GPUInstancerFactory(StructType<D> type) {
this.type = type;
@ -39,7 +37,7 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
@Override
public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, this::createInstancer).instancer;
return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer();
}
public int getInstanceCount() {
@ -60,8 +58,7 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
public void delete() {
models.values().forEach(InstancedModel::delete);
models.clear();
materials.clear();
renderables.clear();
renderLists.clear();
}
/**
@ -74,21 +71,35 @@ public class GPUInstancerFactory<D extends InstanceData> implements InstancerFac
.forEach(GPUInstancer::clear);
}
public void init(MeshPool allocator) {
public void init() {
for (var instanced : uninitialized) {
var map = instanced.init(allocator);
instanced.init();
map.forEach((material, renderable) -> {
materials.put(material.getRenderType(), material);
renderables.get(material).add(renderable);
});
for (Renderable renderable : instanced.getLayers()) {
renderLists.put(renderable.getMaterial()
.getRenderType(), renderable);
}
}
uninitialized.clear();
}
private InstancedModel<D> createInstancer(ModelSupplier model) {
var instancer = new InstancedModel<>(new GPUInstancer<>(type), model);
var instancer = new InstancedModel<>(type, model);
uninitialized.add(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;
import java.util.List;
import java.util.Map;
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.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
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.util.Pair;
public class InstancedModel<D extends InstanceData> {
public class InstancedModel<D extends InstancedPart> {
final GPUInstancer<D> instancer;
final ModelSupplier model;
private Map<Material, Layer> layers;
public final GPUInstancer<D> instancer;
public final ModelSupplier model;
private List<Layer> layers;
public InstancedModel(GPUInstancer<D> instancer, ModelSupplier model) {
this.instancer = instancer;
public InstancedModel(StructType<D> type, ModelSupplier model) {
this.model = model;
this.instancer = new GPUInstancer<>(this, type);
}
public Map<Material, ? extends Renderable> init(MeshPool allocator) {
public void init() {
instancer.init();
buildLayers();
}
public List<? extends Renderable> getLayers() {
return layers;
}
private void buildLayers() {
layers = model.get()
.entrySet()
.stream()
.map(entry -> Pair.of(entry.getKey(), new Layer(allocator, entry.getKey(), entry.getValue())))
.collect(ImmutableMap.toImmutableMap(Pair::first, Pair::second));
return layers;
.map(entry -> new Layer(entry.getKey(), entry.getValue()))
.toList();
}
private class Layer implements Renderable {
@ -42,14 +50,25 @@ public class InstancedModel<D extends InstanceData> {
MeshPool.BufferedMesh bufferedMesh;
GlVertexArray vao;
private Layer(MeshPool allocator, Material material, Mesh mesh) {
private Layer(Material material, Mesh mesh) {
this.material = material;
vao = new GlVertexArray();
bufferedMesh = allocator.alloc(mesh);
bufferedMesh = MeshPool.getInstance()
.alloc(mesh);
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
}
@Override
public Material getMaterial() {
return material;
}
@Override
public VertexType getVertexType() {
return bufferedMesh.getVertexType();
}
@Override
public void render() {
if (invalid()) return;
@ -61,9 +80,6 @@ public class InstancedModel<D extends InstanceData> {
if (instancer.glInstanceCount > 0) {
bufferedMesh.drawInstances(vao, instancer.glInstanceCount);
}
// persistent mapping sync point
instancer.vbo.doneForThisFrame();
}
}
@ -72,6 +88,9 @@ public class InstancedModel<D extends InstanceData> {
return invalid();
}
/**
* Only {@code true} if the InstancedModel has been destroyed.
*/
private boolean invalid() {
return instancer.vbo == null || bufferedMesh == null || vao == null;
}
@ -105,7 +124,7 @@ public class InstancedModel<D extends InstanceData> {
instancer.vbo.delete();
instancer.vbo = null;
for (var layer : layers.values()) {
for (var layer : layers) {
layer.delete();
}
}

View file

@ -1,38 +1,59 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import javax.annotation.Nonnull;
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.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.InstancedRenderDispatcher;
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.core.CoreShaderInfoMap;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.RenderContext;
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.vertex.Formats;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
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.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
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 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 int vertexCount;
@ -60,7 +80,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
@SuppressWarnings("unchecked")
@NotNull
@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);
}
@ -74,14 +94,18 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
for (RenderType renderType : toRender) {
for (RenderType renderType : layersToProcess) {
render(renderType, camX, camY, camZ, vp, context.level());
}
toRender.clear();
layersToProcess.clear();
}
@Override
public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) {
if (!layersToProcess.remove(type)) {
return;
}
var camX = context.camX() - originCoordinate.getX();
var camY = context.camY() - originCoordinate.getY();
var camZ = context.camZ() - originCoordinate.getZ();
@ -90,9 +114,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
var vp = context.viewProjection().copy();
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) {
@ -101,55 +123,36 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
type.setupRenderState();
Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = getCoreShaderInfo();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
for (var entry : factories.entrySet()) {
var instanceType = entry.getKey();
var factory = entry.getValue();
var materials = factory.materials.get(type);
for (Material material : materials) {
var toRender = factory.renderables.get(material);
toRender.removeIf(Renderable::shouldRemove);
var toRender = factory.getRenderList(type);
if (!toRender.isEmpty()) {
setup(instanceType, material, coreShaderInfo, camX, camY, camZ, viewProjection, level);
instanceCount += factory.getInstanceCount();
vertexCount += factory.getVertexCount();
toRender.forEach(Renderable::render);
}
if (toRender.isEmpty()) {
continue;
}
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();
}
protected CoreShaderInfo getCoreShaderInfo() {
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, VertexType vertexType) {
protected P setup(StructType<?> instanceType, Material material, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
float alphaDiscard = coreShaderInfo.alphaDiscard();
if (alphaDiscard == 0) {
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()));
P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceType.getInstanceShader(),
material.getVertexShader(), material.getFragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(),
coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
program.bind();
program.uploadUniforms(camX, camY, camZ, viewProjection, level);
@ -187,15 +190,15 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
public void beginFrame(Camera info) {
checkOriginDistance(info);
MeshPool allocator = getModelAllocator();
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) {
@ -228,17 +231,130 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
}
private MeshPool getModelAllocator() {
if (allocator == null) {
allocator = createAllocator();
public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) {
var dataByStage = getDataByStage(levelRenderer, level);
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
return new MeshPool(Formats.POS_TEX_NORMAL);
for (var entry : dataByStage.int2ObjectEntrySet()) {
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

View file

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

View file

@ -5,7 +5,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
public class ElementBuffer {
private final GlBuffer buffer;
public final GlBuffer buffer;
public final int elementCount;
public final GlNumericType eboIndexType;
@ -14,12 +14,4 @@ public class ElementBuffer {
this.eboIndexType = indexType;
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;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.Flywheel;
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.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
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.event.ReloadRenderersEvent;
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 GlBuffer vbo;
private int vertices;
private long byteSize;
private boolean dirty;
private boolean anyToRemove;
/**
* Create a new model pool.
*
* @param vertexType The vertex type of the models that will be stored in the pool.
* Create a new mesh pool.
*/
public MeshPool(VertexType vertexType) {
this.vertexType = vertexType;
int stride = vertexType.getStride();
public MeshPool() {
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.
*/
public BufferedMesh alloc(Mesh mesh) {
BufferedMesh bufferedModel = new BufferedMesh(mesh, vertices);
vertices += mesh.getVertexCount();
models.add(bufferedModel);
pendingUpload.add(bufferedModel);
return meshes.computeIfAbsent(mesh, m -> {
BufferedMesh bufferedModel = new BufferedMesh(m, byteSize);
byteSize += m.size();
allBuffered.add(bufferedModel);
pendingUpload.add(bufferedModel);
dirty = true;
return bufferedModel;
dirty = true;
return bufferedModel;
});
}
@Nullable
public BufferedMesh get(Mesh mesh) {
return meshes.get(mesh);
}
public void flush() {
if (dirty) {
if (anyToRemove) processDeletions();
if (anyToRemove) {
processDeletions();
}
if (realloc()) {
uploadAll();
@ -80,21 +104,28 @@ public class MeshPool {
private void processDeletions() {
// remove deleted models
models.removeIf(BufferedMesh::isDeleted);
// remove deleted meshes
allBuffered.removeIf(bufferedMesh -> {
boolean deleted = bufferedMesh.isDeleted();
if (deleted) {
meshes.remove(bufferedMesh.mesh);
}
return deleted;
});
// re-evaluate first vertex for each model
int vertices = 0;
for (BufferedMesh model : models) {
if (model.first != vertices)
int byteIndex = 0;
for (BufferedMesh model : allBuffered) {
if (model.byteIndex != byteIndex) {
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;
}
@ -104,20 +135,20 @@ public class MeshPool {
* @return true if the buffer was reallocated
*/
private boolean realloc() {
return vbo.ensureCapacity((long) vertices * vertexType.getStride());
return vbo.ensureCapacity(byteSize);
}
private void uploadAll() {
try (MappedBuffer buffer = vbo.map()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
try (MappedBuffer mapped = vbo.map()) {
ByteBuffer buffer = mapped.unwrap();
int vertices = 0;
for (BufferedMesh model : models) {
model.first = vertices;
int byteIndex = 0;
for (BufferedMesh model : allBuffered) {
model.byteIndex = byteIndex;
model.buffer(writer);
model.buffer(buffer);
vertices += model.mesh.getVertexCount();
byteIndex += model.mesh.size();
}
} catch (Exception e) {
@ -126,10 +157,10 @@ public class MeshPool {
}
private void uploadPending() {
try (MappedBuffer buffer = vbo.map()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
try (MappedBuffer mapped = vbo.map()) {
ByteBuffer buffer = mapped.unwrap();
for (BufferedMesh model : pendingUpload) {
model.buffer(writer);
model.buffer(buffer);
}
pendingUpload.clear();
} catch (Exception e) {
@ -139,49 +170,63 @@ public class MeshPool {
public void delete() {
vbo.delete();
meshes.clear();
allBuffered.clear();
pendingUpload.clear();
}
public class BufferedMesh {
private final ElementBuffer ebo;
private final Mesh mesh;
private int first;
private final BufferLayout layout;
private long byteIndex;
private boolean deleted;
private boolean gpuResident = false;
private final Set<GlVertexArray> boundTo = new HashSet<>();
public BufferedMesh(Mesh mesh, int first) {
public BufferedMesh(Mesh mesh, long byteIndex) {
this.mesh = mesh;
this.first = first;
this.byteIndex = byteIndex;
this.ebo = mesh.createEBO();
this.layout = mesh.getType()
.getLayout();
}
public void drawCall(GlVertexArray vao) {
attachTo(vao);
vao.bind();
this.ebo.bind();
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, this.first);
drawInstances(vao, 1);
}
public void drawInstances(GlVertexArray vao, int instanceCount) {
if (mesh.getVertexCount() <= 0 || isDeleted()) return;
if (hasAnythingToRender()) return;
attachTo(vao);
setup(vao);
vao.bind();
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);
draw(instanceCount);
}
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)) {
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() {
@ -194,14 +239,23 @@ public class MeshPool {
this.deleted = true;
}
private void buffer(VertexWriter writer) {
writer.seekToVertex(this.first);
writer.writeVertexList(this.mesh.getReader());
private void buffer(ByteBuffer buffer) {
this.mesh.writeInto(buffer, this.byteIndex);
this.boundTo.clear();
this.gpuResident = true;
}
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 com.jozufozu.flywheel.backend.OptifineHandler;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.ShaderInstance;
public class CoreShaderInfoMap {
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 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 {
NO_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.glDrawArrays;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
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.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
@ -52,8 +50,8 @@ public class FullscreenQuad {
vao.enableArrays(1);
vao.bindAttributes(vbo, 0, LAYOUT);
}
vao.bindAttributes(vbo, 0, LAYOUT, 0L);
}
}
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.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.
*/
@Mod.EventBusSubscriber(Dist.CLIENT)
public class QuadConverter {
private static QuadConverter INSTANCE;
@ -42,7 +36,7 @@ public class QuadConverter {
return INSTANCE;
}
private MappedGlBuffer ebo;
private final MappedGlBuffer ebo;
private int quadCapacity;
public QuadConverter() {
@ -111,7 +105,6 @@ public class QuadConverter {
}
// make sure this gets reset first so it has a chance to repopulate
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onRendererReload(ReloadRenderersEvent event) {
if (INSTANCE != null) {
INSTANCE.delete();

View file

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

View file

@ -34,7 +34,7 @@ public class ModelPart implements Mesh {
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 com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer;
public class ConditionalInstance<D extends InstanceData> {
public class ConditionalInstance<D extends InstancedPart> {
final Instancer<D> model;
ICondition condition;

View file

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

View file

@ -6,10 +6,10 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.Instancer;
public class SelectInstance<D extends InstanceData> {
public class SelectInstance<D extends InstancedPart> {
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.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.QuadConverter;
@ -77,7 +78,9 @@ public interface Mesh {
return getVertexCount() == 0;
}
default void writeInto(ByteBuffer buffer) {
getType().createWriter(buffer).writeVertexList(getReader());
default void writeInto(ByteBuffer buffer, long byteIndex) {
VertexWriter writer = getType().createWriter(buffer);
writer.seek(byteIndex);
writer.writeVertexList(getReader());
}
}

View file

@ -1,11 +1,12 @@
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 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 skyLight;
@ -15,15 +16,19 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
public byte b = (byte) 0xFF;
public byte a = (byte) 0xFF;
public ColoredLitPart(StructType<?> type) {
super(type);
}
@Override
public BasicData setBlockLight(int blockLight) {
public ColoredLitPart setBlockLight(int blockLight) {
this.blockLight = (byte) blockLight;
markDirty();
return this;
}
@Override
public BasicData setSkyLight(int skyLight) {
public ColoredLitPart setSkyLight(int skyLight) {
this.skyLight = (byte) skyLight;
markDirty();
return this;
@ -34,7 +39,7 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
return LightTexture.pack(this.blockLight, this.skyLight);
}
public BasicData setColor(Color color) {
public ColoredLitPart setColor(Color color) {
this.r = (byte) color.getRed();
this.g = (byte) color.getGreen();
this.b = (byte) color.getBlue();
@ -43,11 +48,11 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
return this;
}
public BasicData setColor(int color) {
public ColoredLitPart setColor(int color) {
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 g = (byte) ((color >> 8) & 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);
}
public BasicData setColor(byte r, byte g, byte b) {
public ColoredLitPart setColor(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
@ -72,7 +77,7 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
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.g = g;
this.b = b;
@ -80,5 +85,4 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
markDirty();
return this;
}
}

View file

@ -7,9 +7,9 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
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);
}

View file

@ -1,20 +1,20 @@
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.world.level.BlockAndTintGetter;
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.
* <p>
* This only covers flat lighting, smooth lighting is still TODO.
*
* @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
* amount of block light this instance should receive.

View file

@ -14,7 +14,7 @@ import net.minecraft.resources.ResourceLocation;
public class InstanceShaders {
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 FileResolution create(ResourceLocation location) {
@ -25,7 +25,7 @@ public class InstanceShaders {
}
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");
}
}

View file

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

View file

@ -1,6 +1,7 @@
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.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
@ -9,14 +10,18 @@ import com.mojang.math.Quaternion;
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 Matrix3f EMPTY_MATRIX_3f = new Matrix3f();
public final Matrix4f model = new Matrix4f();
public final Matrix3f normal = new Matrix3f();
public ModelData setTransform(PoseStack stack) {
public TransformedPart() {
super(StructTypes.TRANSFORMED);
}
public TransformedPart setTransform(PoseStack stack) {
markDirty();
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".
* </p>
*/
public ModelData setEmptyTransform() {
public TransformedPart setEmptyTransform() {
markDirty();
this.model.load(EMPTY_MATRIX_4f);
@ -39,7 +44,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
return this;
}
public ModelData loadIdentity() {
public TransformedPart loadIdentity() {
markDirty();
this.model.setIdentity();
@ -48,7 +53,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
}
@Override
public ModelData multiply(Quaternion quaternion) {
public TransformedPart multiply(Quaternion quaternion) {
markDirty();
model.multiply(quaternion);
@ -57,7 +62,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
}
@Override
public ModelData scale(float pX, float pY, float pZ) {
public TransformedPart scale(float pX, float pY, float pZ) {
markDirty();
model.multiply(Matrix4f.createScaleMatrix(pX, pY, pZ));
@ -79,7 +84,7 @@ public class ModelData extends BasicData implements Transform<ModelData> {
}
@Override
public ModelData translate(double x, double y, double z) {
public TransformedPart translate(double x, double y, double z) {
markDirty();
model.multiplyWithTranslation((float) x, (float) y, (float) z);
@ -87,14 +92,28 @@ public class ModelData extends BasicData implements Transform<ModelData> {
}
@Override
public ModelData mulPose(Matrix4f pose) {
public TransformedPart mulPose(Matrix4f pose) {
this.model.multiply(pose);
return this;
}
@Override
public ModelData mulNormal(Matrix3f normal) {
public TransformedPart mulNormal(Matrix3f normal) {
this.normal.mul(normal);
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.structs.InstanceShaders;
public class ModelType implements StructType<ModelData> {
public class TransformedType implements StructType<TransformedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
@ -18,8 +18,8 @@ public class ModelType implements StructType<ModelData> {
.build();
@Override
public ModelData create() {
return new ModelData();
public TransformedPart create() {
return new TransformedPart();
}
@Override
@ -28,17 +28,17 @@ public class ModelType implements StructType<ModelData> {
}
@Override
public StructWriter<ModelData> getWriter(ByteBuffer backing) {
return new ModelWriterUnsafe(this, backing);
public StructWriter<TransformedPart> getWriter(ByteBuffer backing) {
return new TransformedWriterUnsafe(this, backing);
}
@Override
public FileResolution getInstanceShader() {
return InstanceShaders.MODEL;
return InstanceShaders.TRANSFORMED;
}
@Override
public void transform(ModelData d, ModelTransformer.Params b) {
public void transform(TransformedPart d, ModelTransformer.Params b) {
b.transform(d.model, d.normal)
.color(d.r, d.g, d.b, d.a)
.light(d.getPackedLight());

View file

@ -3,17 +3,17 @@ package com.jozufozu.flywheel.core.structs.model;
import java.nio.ByteBuffer;
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;
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);
}
@Override
protected void writeInternal(ModelData d) {
protected void writeInternal(TransformedPart d) {
super.writeInternal(d);
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.mojang.math.Quaternion;
public class OrientedType implements StructType<OrientedData> {
public class OrientedType implements StructType<OrientedPart> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
@ -19,8 +19,8 @@ public class OrientedType implements StructType<OrientedData> {
.build();
@Override
public OrientedData create() {
return new OrientedData();
public OrientedPart create() {
return new OrientedPart();
}
@Override
@ -29,7 +29,7 @@ public class OrientedType implements StructType<OrientedData> {
}
@Override
public StructWriter<OrientedData> getWriter(ByteBuffer backing) {
public StructWriter<OrientedPart> getWriter(ByteBuffer backing) {
return new OrientedWriterUnsafe(this, backing);
}
@ -39,7 +39,7 @@ public class OrientedType implements StructType<OrientedData> {
}
@Override
public void transform(OrientedData d, ModelTransformer.Params b) {
public void transform(OrientedPart d, ModelTransformer.Params b) {
b.light(d.getPackedLight())
.color(d.r, d.g, d.b, d.a)
.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 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 OrientedWriterUnsafe(StructType<OrientedData> structType, ByteBuffer byteBuffer) {
public class OrientedWriterUnsafe extends ColoredLitWriterUnsafe<OrientedPart> {
public OrientedWriterUnsafe(StructType<OrientedPart> structType, ByteBuffer byteBuffer) {
super(structType, byteBuffer);
}
@Override
protected void writeInternal(OrientedData d) {
protected void writeInternal(OrientedPart d) {
long ptr = writePointer;
super.writeInternal(d);

View file

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

View file

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

View file

@ -12,8 +12,6 @@ public abstract class VertexWriterUnsafe<V extends VertexType> implements Vertex
public final V type;
protected final ByteBuffer buffer;
private int totalVertices;
private int writeVertex;
protected long ptr;
protected VertexWriterUnsafe(V type, ByteBuffer buffer) {
@ -22,21 +20,14 @@ public abstract class VertexWriterUnsafe<V extends VertexType> implements Vertex
this.ptr = MemoryUtil.memAddress(buffer);
}
protected void advance() {
writeVertex++;
// account for seeking
if (writeVertex > totalVertices) totalVertices = writeVertex;
}
@Override
public void seekToVertex(int vertex) {
buffer.position(type.byteOffset(vertex));
writeVertex = vertex;
public void seek(long offset) {
buffer.position((int) offset);
ptr = MemoryUtil.memAddress(buffer);
}
@Override
public VertexList intoReader() {
return type.createReader(buffer, totalVertices);
public VertexList intoReader(int vertices) {
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.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class EntityWorldHandler {
@SubscribeEvent
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
.queueAdd(event.getEntity());
}
@SubscribeEvent
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
if (event.getWorld().isClientSide && Backend.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
.remove(event.getEntity());

View file

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

View file

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

View file

@ -1,7 +1,11 @@
package com.jozufozu.flywheel.vanilla;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
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.material.MaterialShaders;
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.mojang.math.Quaternion;
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 final OrientedData bell;
private final OrientedPart bell;
private float lastRingTime = Float.NaN;
@ -59,12 +63,17 @@ public class BellInstance extends BlockEntityInstance<BellBlockEntity> implement
relight(getWorldPosition(), bell);
}
@Override
public void addCrumblingParts(List<InstancedPart> data) {
Collections.addAll(data, bell);
}
@Override
public void remove() {
bell.delete();
}
private OrientedData createBellInstance() {
private OrientedPart createBellInstance() {
return instancerManager.factory(StructTypes.ORIENTED)
.model(MODEL)
.createInstance();

View file

@ -1,10 +1,13 @@
package com.jozufozu.flywheel.vanilla;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
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.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.ModelData;
import com.jozufozu.flywheel.core.structs.oriented.OrientedData;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.math.Quaternion;
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> 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 ModelData lid;
private final OrientedPart body;
private final TransformedPart lid;
private final Float2FloatFunction lidProgress;
private final Material renderMaterial;
@ -108,22 +111,27 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
relight(getWorldPosition(), body, lid);
}
@Override
public void addCrumblingParts(List<InstancedPart> data) {
Collections.addAll(data, body, lid);
}
@Override
public void remove() {
body.delete();
lid.delete();
}
private OrientedData baseInstance() {
private OrientedPart baseInstance() {
return instancerManager.factory(StructTypes.ORIENTED)
.model(BASE.apply(chestType, renderMaterial))
.createInstance();
}
private ModelData lidInstance() {
private TransformedPart lidInstance() {
return instancerManager.factory(StructTypes.MODEL)
return instancerManager.factory(StructTypes.TRANSFORMED)
.model(LID.apply(chestType, renderMaterial))
.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.model.Mesh;
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.transform.TransformStack;
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 ModelData body;
private ModelData contents;
private final TransformedPart body;
private TransformedPart contents;
private BlockState blockState;
private boolean active;
@ -48,6 +48,11 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
contents = getContents();
}
@Override
public boolean decreaseFramerateWithDistance() {
return false;
}
@Override
public void tick() {
BlockState displayBlockState = entity.getDisplayBlockState();
@ -153,7 +158,7 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
if (contents != null) contents.delete();
}
private ModelData getContents() {
private TransformedPart getContents() {
RenderShape shape = blockState.getRenderShape();
if (shape == RenderShape.ENTITYBLOCK_ANIMATED) {
@ -166,13 +171,13 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
if (shape == RenderShape.INVISIBLE)
return null;
return instancerManager.factory(StructTypes.MODEL)
return instancerManager.factory(StructTypes.TRANSFORMED)
.model(Models.block(blockState))
.createInstance();
}
private ModelData getBody() {
return instancerManager.factory(StructTypes.MODEL)
private TransformedPart getBody() {
return instancerManager.factory(StructTypes.TRANSFORMED)
.model(MODEL)
.createInstance();
}

View file

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