From 9e066f8d417dc04ce5bf41b7705ad5651495b52a Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 27 Jul 2021 17:31:58 -0700 Subject: [PATCH 01/16] Documentation and organization - Add a package-info.java to many packages. - Annotate the world parameter in Backend#canUseInstancing as nullable. - New utility constructor for BlockModel - Note that IDynamicInstance#beginFrame and ITickableInstance#tick are run in parallel. - Refactor internals of InstancedRenderDispatcher to group things by InstanceWorlds. - InstanceWorlds take over most responsibility for dispatching calls. - Simplify massive private call chains in InstanceMaterial. - Reorganize methods and add some documentation in MaterialManager, MaterialGroup, InstanceMaterial, and Instancer. - Remove unused field from MaterialSpec. - Remove unused fields from Instancer and InstanceMaterial - Document RenderLayer - Add RenderLayer field to RenderLayerEvent --- build.gradle | 14 ++- .../jozufozu/flywheel/backend/Backend.java | 2 +- .../backend/gl/attrib/package-info.java | 6 + .../backend/gl/buffer/package-info.java | 6 + .../flywheel/backend/gl/package-info.java | 6 + .../backend/gl/shader/package-info.java | 6 + .../backend/instancing/IDynamicInstance.java | 4 + .../backend/instancing/ITickableInstance.java | 4 + .../backend/instancing/InstanceWorld.java | 104 ++++++++++++++++++ .../instancing/InstancedRenderDispatcher.java | 53 +++------ .../backend/instancing/Instancer.java | 57 +++++++--- .../instancing/entity/package-info.java | 6 + .../backend/instancing/tile/package-info.java | 6 + .../backend/loading/package-info.java | 6 + .../backend/material/InstanceMaterial.java | 88 +++++++-------- .../backend/material/MaterialGroup.java | 38 ++++--- .../backend/material/MaterialManager.java | 70 +++++++----- .../backend/material/MaterialSpec.java | 7 -- .../backend/material/package-info.java | 6 + .../flywheel/backend/state/RenderLayer.java | 27 +++++ .../flywheel/backend/state/package-info.java | 6 + .../flywheel/core/crumbling/package-info.java | 6 + .../flywheel/core/model/BlockModel.java | 6 + .../flywheel/event/ReloadRenderersEvent.java | 3 + .../flywheel/event/RenderLayerEvent.java | 12 ++ .../jozufozu/flywheel/event/package-info.java | 6 + 26 files changed, 395 insertions(+), 160 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/buffer/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/shader/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/entity/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/instancing/tile/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/material/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/state/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/crumbling/package-info.java create mode 100644 src/main/java/com/jozufozu/flywheel/event/package-info.java diff --git a/build.gradle b/build.gradle index 8fd354ad3..a5fa99fdd 100644 --- a/build.gradle +++ b/build.gradle @@ -121,6 +121,12 @@ jar { jar.finalizedBy('reobfJar') +javadoc { + source = [sourceSets.main.allJava] + // prevent java 8's strict doclint for javadocs from failing builds + options.addStringOption('Xdoclint:none', '-quiet') +} + task sourcesJar(type: Jar) { from sourceSets.main.allSource archiveBaseName.set(project.archivesBaseName) @@ -128,8 +134,13 @@ task sourcesJar(type: Jar) { archiveClassifier.set('sources') } +task javadocJar(type: Jar, dependsOn: javadoc) { + from javadoc.destinationDir + archiveClassifier.set('javadoc') +} + artifacts { - archives jar, sourcesJar + archives jar, sourcesJar, javadocJar } publishing { @@ -138,6 +149,7 @@ publishing { mavenJava(MavenPublication) { artifact jar artifact sourcesJar + artifact javadocJar } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 0cfb51552..770e0734c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -155,7 +155,7 @@ public class Backend { .enabled() && !OptifineHandler.usingShaders(); } - public boolean canUseInstancing(World world) { + public boolean canUseInstancing(@Nullable World world) { return canUseInstancing() && isFlywheelWorld(world); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java new file mode 100644 index 000000000..4d3485865 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.gl.attrib; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/package-info.java new file mode 100644 index 000000000..fb593bf7c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.gl.buffer; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/gl/package-info.java new file mode 100644 index 000000000..8abd8a8a9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.gl; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/package-info.java new file mode 100644 index 000000000..5c4bed0b3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.gl.shader; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/IDynamicInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/IDynamicInstance.java index fd17da753..e801d16f8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/IDynamicInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/IDynamicInstance.java @@ -14,6 +14,10 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance; public interface IDynamicInstance extends IInstance { /** * Called every frame. + *
+ * DISPATCHED IN PARALLEL, don't attempt to mutate anything outside of this instance. + *
+ * {@link Instancer}/{@link InstanceData} creation/acquisition is safe here. */ void beginFrame(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ITickableInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ITickableInstance.java index 4c3265d0c..041f62ff1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/ITickableInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ITickableInstance.java @@ -22,6 +22,10 @@ public interface ITickableInstance extends IInstance { /** * Called every tick. + *
+ * DISPATCHED IN PARALLEL, don't attempt to mutate anything outside of this instance. + *
+ * {@link Instancer}/{@link InstanceData} creation/acquisition is safe here. */ void tick(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java new file mode 100644 index 000000000..26ddbbb67 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -0,0 +1,104 @@ +package com.jozufozu.flywheel.backend.instancing; + +import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; +import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; +import com.jozufozu.flywheel.backend.material.MaterialManager; +import com.jozufozu.flywheel.backend.state.RenderLayer; +import com.jozufozu.flywheel.core.Contexts; +import com.jozufozu.flywheel.core.shader.WorldProgram; +import com.jozufozu.flywheel.event.BeginFrameEvent; +import com.jozufozu.flywheel.event.RenderLayerEvent; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IWorld; +import net.minecraftforge.event.TickEvent; + +/** + * A manager class for a single world where instancing is supported. + *
+ * The material manager is shared between the different instance managers. + */ +public class InstanceWorld { + protected final MaterialManager materialManager; + protected final InstanceManager entityInstanceManager; + protected final InstanceManager tileEntityInstanceManager; + + public InstanceWorld() { + + materialManager = MaterialManager.builder(Contexts.WORLD) + .build(); + entityInstanceManager = new EntityInstanceManager(materialManager); + tileEntityInstanceManager = new TileInstanceManager(materialManager); + } + + public MaterialManager getMaterialManager() { + return materialManager; + } + + public InstanceManager getEntityInstanceManager() { + return entityInstanceManager; + } + + public InstanceManager getTileEntityInstanceManager() { + return tileEntityInstanceManager; + } + + /** + * Free all acquired resources and invalidate this instance world. + */ + public void delete() { + materialManager.delete(); + } + + /** + * Instantiate all the necessary instances to render the given world. + */ + public void loadAll(ClientWorld world) { + world.blockEntityList.forEach(tileEntityInstanceManager::add); + world.entitiesForRendering() + .forEach(entityInstanceManager::add); + } + + /** + * Get ready to render a frame: + *
+ * Check and shift the origin coordinate. + *
+ * Call {@link IDynamicInstance#beginFrame()} on all instances in this world. + */ + public void beginFrame(BeginFrameEvent event) { + materialManager.checkAndShiftOrigin(event.getInfo()); + + tileEntityInstanceManager.beginFrame(event.getInfo()); + entityInstanceManager.beginFrame(event.getInfo()); + } + + /** + * Tick the renderers after the game has ticked: + *
+ * Call {@link ITickableInstance#tick()} on all instances in this world. + */ + public void tick() { + Minecraft mc = Minecraft.getInstance(); + Entity renderViewEntity = mc.cameraEntity != null ? mc.cameraEntity : mc.player; + + if (renderViewEntity == null) return; + + tileEntityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); + entityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); + } + + /** + * Draw the given layer. + */ + public void renderLayer(RenderLayerEvent event) { + event.type.setupRenderState(); + + materialManager.render(event.layer, event.viewProjection, event.camX, event.camY, event.camZ); + + event.type.clearRenderState(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 95958d162..1db796450 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -3,12 +3,7 @@ package com.jozufozu.flywheel.backend.instancing; import javax.annotation.Nonnull; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; -import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; -import com.jozufozu.flywheel.backend.material.MaterialManager; import com.jozufozu.flywheel.backend.state.RenderLayer; -import com.jozufozu.flywheel.core.Contexts; -import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.RenderLayerEvent; @@ -30,19 +25,18 @@ import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(Dist.CLIENT) public class InstancedRenderDispatcher { - private static final WorldAttached> materialManagers = new WorldAttached<>($ -> MaterialManager.builder(Contexts.WORLD).build()); - - private static final WorldAttached> entityInstanceManager = new WorldAttached<>(world -> new EntityInstanceManager(materialManagers.get(world))); - private static final WorldAttached> tileInstanceManager = new WorldAttached<>(world -> new TileInstanceManager(materialManagers.get(world))); + private static final WorldAttached instanceWorlds = new WorldAttached<>($ -> new InstanceWorld()); @Nonnull public static InstanceManager getTiles(IWorld world) { - return tileInstanceManager.get(world); + return instanceWorlds.get(world) + .getTileEntityInstanceManager(); } @Nonnull public static InstanceManager getEntities(IWorld world) { - return entityInstanceManager.get(world); + return instanceWorlds.get(world) + .getEntityInstanceManager(); } @SubscribeEvent @@ -55,12 +49,7 @@ public class InstancedRenderDispatcher { ClientWorld world = mc.level; AnimationTickHolder.tick(); - Entity renderViewEntity = mc.cameraEntity != null ? mc.cameraEntity : mc.player; - - if (renderViewEntity == null) return; - - getTiles(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); - getEntities(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); + instanceWorlds.get(world).tick(); } public static void enqueueUpdate(TileEntity te) { @@ -73,29 +62,18 @@ public class InstancedRenderDispatcher { @SubscribeEvent public static void onBeginFrame(BeginFrameEvent event) { - materialManagers.get(event.getWorld()) - .checkAndShiftOrigin(event.getInfo()); - - getTiles(event.getWorld()).beginFrame(event.getInfo()); - getEntities(event.getWorld()).beginFrame(event.getInfo()); + instanceWorlds.get(event.getWorld()).beginFrame(event); } @SubscribeEvent public static void renderLayer(RenderLayerEvent event) { + if (event.layer == null) return; + ClientWorld world = event.getWorld(); if (!Backend.getInstance() .canUseInstancing(world)) return; - RenderLayer renderLayer = RenderLayer.fromRenderType(event.type); - - if (renderLayer == null) return; - - event.type.setupRenderState(); - - materialManagers.get(world) - .render(renderLayer, event.viewProjection, event.camX, event.camY, event.camZ); - - event.type.clearRenderState(); + instanceWorlds.get(world).renderLayer(event); } @SubscribeEvent @@ -108,13 +86,8 @@ public class InstancedRenderDispatcher { } public static void loadAllInWorld(ClientWorld world) { - materialManagers.replace(world, MaterialManager::delete); - - InstanceManager tiles = tileInstanceManager.replace(world); - world.blockEntityList.forEach(tiles::add); - - InstanceManager entities = entityInstanceManager.replace(world); - world.entitiesForRendering() - .forEach(entities::add); + instanceWorlds.replace(world, InstanceWorld::delete) + .loadAll(world); } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java index babec0d6e..61bd13723 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.backend.instancing; - import java.util.ArrayList; import java.util.BitSet; import java.util.function.Supplier; @@ -17,12 +16,26 @@ import com.jozufozu.flywheel.core.model.IModel; import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.util.AttribUtil; -import net.minecraft.util.math.vector.Vector3i; - +/** + * An instancer is how you interact with an instanced model. + *

+ * Instanced models can have many copies, and on most systems it's very fast to draw all of the copies at once. + * There is no limit to how many copies an instanced model can have. + * Each copy is represented by an InstanceData object. + *

+ *

+ * When you call {@link #createInstance()} you are given an InstanceData object that you can manipulate however + * you want. The changes you make to the InstanceData object are automatically made visible, and persistent. + * Changing the position of your InstanceData object every frame means that that copy of the model will be in a + * different position in the world each frame. Setting the position of your InstanceData once and not touching it + * again means that your model will be in the same position in the world every frame. This persistence is useful + * because it means the properties of your model don't have to be re-evaluated every frame. + *

+ * + * @param the data that represents a copy of the instanced model. + */ public class Instancer { - public final Supplier originCoordinate; - protected final Supplier gen; protected BufferedModel model; @@ -40,11 +53,30 @@ public class Instancer { boolean anyToRemove; boolean anyToUpdate; - public Instancer(Supplier model, Supplier originCoordinate, MaterialSpec spec) { + public Instancer(Supplier model, MaterialSpec spec) { this.gen = model; this.factory = spec.getInstanceFactory(); this.instanceFormat = spec.getInstanceFormat(); - this.originCoordinate = originCoordinate; + } + + /** + * @return a handle to a new copy of this model. + */ + public D createInstance() { + return _add(factory.create(this)); + } + + /** + * Copy a data from another Instancer to this. + * + * This has the effect of swapping out one model for another. + * @param inOther the data associated with a different model. + */ + public void stealInstance(D inOther) { + if (inOther.owner == this) return; + + inOther.owner.anyToRemove = true; + _add(inOther); } public void render() { @@ -59,17 +91,6 @@ public class Instancer { vao.unbind(); } - public D createInstance() { - return _add(factory.create(this)); - } - - public void stealInstance(D inOther) { - if (inOther.owner == this) return; - - inOther.owner.anyToRemove = true; - _add(inOther); - } - private void init() { model = ModelUtil.getIndexedModel(gen.get()); initialized = true; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/package-info.java new file mode 100644 index 000000000..4ce85a9f8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.instancing.entity; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/package-info.java new file mode 100644 index 000000000..6e52353b7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.instancing.tile; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java new file mode 100644 index 000000000..21e83243b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/loading/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.loading; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java index 2f55c4c9a..a8c46059c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java @@ -4,36 +4,31 @@ import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.function.Supplier; -import org.apache.commons.lang3.tuple.Pair; - import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.jozufozu.flywheel.backend.RenderWork; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.instancing.InstanceData; import com.jozufozu.flywheel.backend.instancing.Instancer; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.model.BlockModel; import com.jozufozu.flywheel.core.model.IModel; +import com.jozufozu.flywheel.util.Pair; import com.jozufozu.flywheel.util.RenderUtil; import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraft.block.BlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.util.Direction; -import net.minecraft.util.math.vector.Vector3i; +/** + * A collection of Instancers that all have the same format. + * @param + */ public class InstanceMaterial { - protected final Supplier originCoordinate; protected final Cache> models; protected final MaterialSpec spec; - private final VertexFormat modelFormat; - public InstanceMaterial(Supplier renderer, MaterialSpec spec) { - this.originCoordinate = renderer; + public InstanceMaterial(MaterialSpec spec) { this.spec = spec; this.models = CacheBuilder.newBuilder() @@ -42,7 +37,37 @@ public class InstanceMaterial { RenderWork.enqueue(instancer::delete); }) .build(); - modelFormat = this.spec.getModelFormat(); + } + + /** + * Get an instancer for the given model. Calling this method twice with the same key will return the same instancer. + * + * @param key An object that uniquely identifies the model. + * @param modelSupplier A factory that creates the IModel that you want to render. + * @return An instancer for the given model, capable of rendering many copies for little cost. + */ + public Instancer model(Object key, Supplier modelSupplier) { + try { + return models.get(key, () -> new Instancer<>(modelSupplier, spec)); + } catch (ExecutionException e) { + throw new RuntimeException("error creating instancer", e); + } + } + + public Instancer getModel(PartialModel partial, BlockState referenceState) { + return model(partial, () -> new BlockModel(spec.getModelFormat(), partial.get(), referenceState)); + } + + public Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir) { + return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir)); + } + + public Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier modelTransform) { + return model(Pair.of(dir, partial), () -> new BlockModel(spec.getModelFormat(), partial.get(), referenceState, modelTransform.get())); + } + + public Instancer getModel(BlockState toRender) { + return model(toRender, () -> new BlockModel(spec.getModelFormat(), toRender)); } public boolean nothingToRender() { @@ -72,43 +97,4 @@ public class InstanceMaterial { } } - public Instancer getModel(PartialModel partial, BlockState referenceState) { - return model(partial, () -> buildModel(partial.get(), referenceState)); - } - - public Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir) { - return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir)); - } - - public Instancer getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier modelTransform) { - return model(Pair.of(dir, partial), () -> buildModel(partial.get(), referenceState, modelTransform.get())); - } - - public Instancer getModel(BlockState toRender) { - return model(toRender, () -> buildModel(toRender)); - } - - public Instancer model(Object key, Supplier supplier) { - try { - return models.get(key, () -> new Instancer<>(supplier, originCoordinate, spec)); - } catch (ExecutionException e) { - e.printStackTrace(); - return null; - } - } - - private IModel buildModel(BlockState renderedState) { - BlockRendererDispatcher dispatcher = Minecraft.getInstance() - .getBlockRenderer(); - return buildModel(dispatcher.getBlockModel(renderedState), renderedState); - } - - private IModel buildModel(IBakedModel model, BlockState renderedState) { - return buildModel(model, renderedState, new MatrixStack()); - } - - private IModel buildModel(IBakedModel model, BlockState referenceState, MatrixStack ms) { - - return new BlockModel(modelFormat, model, referenceState, ms); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialGroup.java index d9d84e5af..9dacda4c6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialGroup.java @@ -10,6 +10,12 @@ import com.jozufozu.flywheel.core.shader.WorldProgram; import net.minecraft.util.math.vector.Matrix4f; +/** + * A group of materials all rendered with the same GL state. + * + * The children of a material group will all be rendered at the same time. + * No guarantees are made about the order of draw calls. + */ public class MaterialGroup

{ protected final MaterialManager

owner; @@ -24,6 +30,17 @@ public class MaterialGroup

{ this.state = state; } + /** + * Get the material as defined by the given {@link MaterialSpec spec}. + * @param spec The material you want to create instances with. + * @param The type representing the per instance data. + * @return A + */ + @SuppressWarnings("unchecked") + public InstanceMaterial material(MaterialSpec spec) { + return (InstanceMaterial) materials.computeIfAbsent(spec, this::createInstanceMaterial); + } + public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { for (MaterialRenderer

renderer : renderers) { renderer.render(viewProjection, camX, camY, camZ); @@ -34,19 +51,6 @@ public class MaterialGroup

{ } - @SuppressWarnings("unchecked") - public InstanceMaterial material(MaterialSpec spec) { - return (InstanceMaterial) materials.computeIfAbsent(spec, this::createInstanceMaterial); - } - - private InstanceMaterial createInstanceMaterial(MaterialSpec type) { - InstanceMaterial material = new InstanceMaterial<>(owner::getOriginCoordinate, type); - - this.renderers.add(new MaterialRenderer<>(owner.getProgram(type.getProgramName()), material, this::setup)); - - return material; - } - public void clear() { materials.values().forEach(InstanceMaterial::clear); } @@ -58,4 +62,12 @@ public class MaterialGroup

{ materials.clear(); renderers.clear(); } + + private InstanceMaterial createInstanceMaterial(MaterialSpec type) { + InstanceMaterial material = new InstanceMaterial<>(type); + + this.renderers.add(new MaterialRenderer<>(owner.getProgram(type.getProgramName()), material, this::setup)); + + return material; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java index d9436c117..869be4cff 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java @@ -60,9 +60,44 @@ public class MaterialManager

{ } } + /** + * Get a material group that will render in the given layer with the given state. + * + * @param layer The {@link RenderLayer} you want to draw in. + * @param state The {@link IRenderState} you need to draw with. + * @return A material group whose children will + */ + public MaterialGroup

state(RenderLayer layer, IRenderState state) { + return layers.get(layer).computeIfAbsent(state, this::createGroup); + } + + public MaterialGroup

solid(IRenderState state) { + return layers.get(RenderLayer.SOLID).computeIfAbsent(state, this::createGroup); + } + + public MaterialGroup

cutout(IRenderState state) { + return layers.get(RenderLayer.CUTOUT).computeIfAbsent(state, this::createGroup); + } + + public MaterialGroup

transparent(IRenderState state) { + return layers.get(RenderLayer.TRANSPARENT).computeIfAbsent(state, this::createGroup); + } + + public MaterialGroup

defaultSolid() { + return solid(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); + } + + public MaterialGroup

defaultCutout() { + return cutout(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); + } + + public MaterialGroup

defaultTransparent() { + return transparent(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); + } + /** * Render every model for every material. - * @param layer Which vanilla {@link RenderType} is being drawn? + * @param layer Which of the 3 {@link RenderLayer render layers} is being drawn? * @param viewProjection How do we get from camera space to clip space? */ public void render(RenderLayer layer, Matrix4f viewProjection, double camX, double camY, double camZ) { @@ -95,34 +130,6 @@ public class MaterialManager

{ } } - public MaterialGroup

state(RenderLayer layer, IRenderState state) { - return layers.get(layer).computeIfAbsent(state, this::createGroup); - } - - public MaterialGroup

solid(IRenderState state) { - return layers.get(RenderLayer.SOLID).computeIfAbsent(state, this::createGroup); - } - - public MaterialGroup

cutout(IRenderState state) { - return layers.get(RenderLayer.CUTOUT).computeIfAbsent(state, this::createGroup); - } - - public MaterialGroup

transparent(IRenderState state) { - return layers.get(RenderLayer.TRANSPARENT).computeIfAbsent(state, this::createGroup); - } - - public MaterialGroup

defaultSolid() { - return solid(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); - } - - public MaterialGroup

defaultCutout() { - return cutout(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); - } - - public MaterialGroup

defaultTransparent() { - return transparent(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS)); - } - @Deprecated public InstanceMaterial getMaterial(MaterialSpec materialType) { return defaultCutout().material(materialType); @@ -155,6 +162,11 @@ public class MaterialManager

{ listeners.add(listener); } + /** + * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. + * + * This prevents floating point precision issues at high coordinates. + */ public void checkAndShiftOrigin(ActiveRenderInfo info) { int cX = MathHelper.floor(info.getPosition().x); int cY = MathHelper.floor(info.getPosition().y); diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialSpec.java b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialSpec.java index 8946fcbff..9b3a87b5a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialSpec.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialSpec.java @@ -4,7 +4,6 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.instancing.IInstanceFactory; import com.jozufozu.flywheel.backend.instancing.InstanceData; -import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.util.ResourceLocation; public class MaterialSpec { @@ -15,19 +14,13 @@ public class MaterialSpec { private final VertexFormat modelFormat; private final VertexFormat instanceFormat; private final IInstanceFactory instanceFactory; - private final ResourceLocation texture; public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, IInstanceFactory instanceFactory) { - this(name, programSpec, modelFormat, instanceFormat, PlayerContainer.BLOCK_ATLAS, instanceFactory); - } - - public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, ResourceLocation texture, IInstanceFactory instanceFactory) { this.name = name; this.programSpec = programSpec; this.modelFormat = modelFormat; this.instanceFormat = instanceFormat; this.instanceFactory = instanceFactory; - this.texture = texture; } public ResourceLocation getProgramName() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/material/package-info.java new file mode 100644 index 000000000..da6906de5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/material/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.material; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/state/RenderLayer.java b/src/main/java/com/jozufozu/flywheel/backend/state/RenderLayer.java index 231e5cead..8594f1f00 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/state/RenderLayer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/state/RenderLayer.java @@ -4,9 +4,36 @@ import javax.annotation.Nullable; import net.minecraft.client.renderer.RenderType; +/** + * The 3 discrete stages the world is rendered in. + */ public enum RenderLayer { + /** + * Solid layer:
+ * + * All polygons will entirely occlude everything behind them. + * + *

+ * e.g. stone, dirt, solid blocks + */ SOLID, + /** + * Cutout layer:
+ * + * Fragments will either occlude or not occlude depending on the texture/material. + * + *

+ * e.g. leaves, cobwebs, tall grass, saplings, glass + */ CUTOUT, + /** + * Transparent layer:
+ * + * Nothing is guaranteed to occlude and fragments blend their color with what's behind them. + * + *

+ * e.g. stained glass, water + */ TRANSPARENT, ; diff --git a/src/main/java/com/jozufozu/flywheel/backend/state/package-info.java b/src/main/java/com/jozufozu/flywheel/backend/state/package-info.java new file mode 100644 index 000000000..0fb20311b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/state/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.backend.state; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/package-info.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/package-info.java new file mode 100644 index 000000000..00190daac --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.crumbling; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 3a30847c6..631c87da1 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -30,6 +30,12 @@ public class BlockModel implements IModel { private final VertexFormat modelFormat; + public BlockModel(VertexFormat modelFormat, BlockState state) { + this(modelFormat, Minecraft.getInstance() + .getBlockRenderer() + .getBlockModel(state), state); + } + public BlockModel(VertexFormat modelFormat, IBakedModel model, BlockState referenceState) { this(modelFormat, model, referenceState, IDENTITY); } diff --git a/src/main/java/com/jozufozu/flywheel/event/ReloadRenderersEvent.java b/src/main/java/com/jozufozu/flywheel/event/ReloadRenderersEvent.java index dc7bac5fb..aad67e6fd 100644 --- a/src/main/java/com/jozufozu/flywheel/event/ReloadRenderersEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/ReloadRenderersEvent.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.event; +import javax.annotation.Nullable; + import net.minecraft.client.world.ClientWorld; import net.minecraftforge.eventbus.api.Event; @@ -10,6 +12,7 @@ public class ReloadRenderersEvent extends Event { this.world = world; } + @Nullable public ClientWorld getWorld() { return world; } diff --git a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java index a22816f71..fafcfbf75 100644 --- a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java @@ -1,5 +1,9 @@ package com.jozufozu.flywheel.event; +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.state.RenderLayer; + import net.minecraft.client.renderer.RenderType; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.vector.Matrix4f; @@ -12,6 +16,7 @@ public class RenderLayerEvent extends Event { public final double camX; public final double camY; public final double camZ; + public final RenderLayer layer; public RenderLayerEvent(ClientWorld world, RenderType type, Matrix4f viewProjection, double camX, double camY, double camZ) { this.world = world; @@ -20,6 +25,13 @@ public class RenderLayerEvent extends Event { this.camX = camX; this.camY = camY; this.camZ = camZ; + + this.layer = RenderLayer.fromRenderType(type); + } + + @Nullable + public RenderLayer getLayer() { + return layer; } public ClientWorld getWorld() { diff --git a/src/main/java/com/jozufozu/flywheel/event/package-info.java b/src/main/java/com/jozufozu/flywheel/event/package-info.java new file mode 100644 index 000000000..b2b0463bf --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/event/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.event; + +import javax.annotation.ParametersAreNonnullByDefault; + +import mcp.MethodsReturnNonnullByDefault; From 5fd7e10235264780ac2616f3d71667d50448b2cf Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 28 Jul 2021 14:18:24 -0700 Subject: [PATCH 02/16] More info in RenderLayerEvent --- .../flywheel/event/RenderLayerEvent.java | 17 +++++++++++++++-- .../flywheel/mixin/RenderHooksMixin.java | 17 +++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java index fafcfbf75..e47f2886c 100644 --- a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java @@ -2,9 +2,12 @@ package com.jozufozu.flywheel.event; import javax.annotation.Nullable; +import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.state.RenderLayer; +import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeBuffers; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.vector.Matrix4f; import net.minecraftforge.eventbus.api.Event; @@ -12,16 +15,26 @@ import net.minecraftforge.eventbus.api.Event; public class RenderLayerEvent extends Event { private final ClientWorld world; public final RenderType type; + public final MatrixStack stack; public final Matrix4f viewProjection; + public final RenderTypeBuffers buffers; public final double camX; public final double camY; public final double camZ; public final RenderLayer layer; - public RenderLayerEvent(ClientWorld world, RenderType type, Matrix4f viewProjection, double camX, double camY, double camZ) { + public RenderLayerEvent(ClientWorld world, RenderType type, MatrixStack stack, RenderTypeBuffers buffers, double camX, double camY, double camZ) { this.world = world; this.type = type; - this.viewProjection = viewProjection; + this.stack = stack; + + viewProjection = stack.last() + .pose() + .copy(); + viewProjection.multiplyBackward(Backend.getInstance() + .getProjectionMatrix()); + + this.buffers = buffers; this.camX = camX; this.camY = camY; this.camZ = camZ; diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index 056efaa7b..238e478b6 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.mixin; import org.lwjgl.opengl.GL20; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -21,6 +22,7 @@ import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeBuffers; import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.BlockPos; @@ -37,6 +39,10 @@ public class RenderHooksMixin { @Shadow private ClientWorld level; + @Shadow + @Final + private RenderTypeBuffers renderBuffers; + @Inject(at = @At(value = "INVOKE", target = "net.minecraft.client.renderer.WorldRenderer.compileChunksUntil(J)V"), method = "renderLevel") private void setupFrame(MatrixStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) { MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, stack, info, gameRenderer, lightTexture)); @@ -49,15 +55,14 @@ public class RenderHooksMixin { */ @Inject(at = @At("TAIL"), method = "renderChunkLayer") private void renderLayer(RenderType type, MatrixStack stack, double camX, double camY, double camZ, CallbackInfo ci) { - Matrix4f view = stack.last() - .pose(); - Matrix4f viewProjection = view.copy(); - viewProjection.multiplyBackward(Backend.getInstance() - .getProjectionMatrix()); - MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(level, type, viewProjection, camX, camY, camZ)); + RenderTypeBuffers renderBuffers = this.renderBuffers; + + MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(level, type, stack, renderBuffers, camX, camY, camZ)); if (!OptifineHandler.usingShaders()) GL20.glUseProgram(0); + + renderBuffers.bufferSource().endBatch(type); } @Inject(at = @At("TAIL"), method = "allChanged") From 17d50813454a316458ccbcd123be0dd64a705949 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 28 Jul 2021 18:13:47 -0700 Subject: [PATCH 03/16] Expose ClippingHelper in BeginFrameEvent --- .../com/jozufozu/flywheel/core/Clipping.java | 16 +++++++++++++++ .../flywheel/event/BeginFrameEvent.java | 9 ++++++++- .../mixin/GlobalClippingHelperMixin.java | 20 +++++++++++++++++++ .../flywheel/mixin/RenderHooksMixin.java | 3 ++- src/main/resources/flywheel.mixins.json | 3 ++- 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/core/Clipping.java create mode 100644 src/main/java/com/jozufozu/flywheel/mixin/GlobalClippingHelperMixin.java diff --git a/src/main/java/com/jozufozu/flywheel/core/Clipping.java b/src/main/java/com/jozufozu/flywheel/core/Clipping.java new file mode 100644 index 000000000..9f28ef46e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/Clipping.java @@ -0,0 +1,16 @@ +package com.jozufozu.flywheel.core; + +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.renderer.culling.ClippingHelper; + +/** + * Used to capture the ClippingHelper from WorldRenderer#renderLevel + */ +public class Clipping { + + /** + * Assigned in {@link com.jozufozu.flywheel.mixin.GlobalClippingHelperMixin this} mixin. + */ + public static ClippingHelper HELPER; +} diff --git a/src/main/java/com/jozufozu/flywheel/event/BeginFrameEvent.java b/src/main/java/com/jozufozu/flywheel/event/BeginFrameEvent.java index 042cc1acd..a4c508328 100644 --- a/src/main/java/com/jozufozu/flywheel/event/BeginFrameEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/BeginFrameEvent.java @@ -5,6 +5,7 @@ import com.mojang.blaze3d.matrix.MatrixStack; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.culling.ClippingHelper; import net.minecraft.client.world.ClientWorld; import net.minecraftforge.eventbus.api.Event; @@ -14,13 +15,15 @@ public class BeginFrameEvent extends Event { private final ActiveRenderInfo info; private final GameRenderer gameRenderer; private final LightTexture lightTexture; + private final ClippingHelper clippingHelper; - public BeginFrameEvent(ClientWorld world, MatrixStack stack, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture) { + public BeginFrameEvent(ClientWorld world, MatrixStack stack, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture, ClippingHelper clippingHelper) { this.world = world; this.stack = stack; this.info = info; this.gameRenderer = gameRenderer; this.lightTexture = lightTexture; + this.clippingHelper = clippingHelper; } public ClientWorld getWorld() { @@ -42,4 +45,8 @@ public class BeginFrameEvent extends Event { public LightTexture getLightTexture() { return lightTexture; } + + public ClippingHelper getClippingHelper() { + return clippingHelper; + } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/GlobalClippingHelperMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/GlobalClippingHelperMixin.java new file mode 100644 index 000000000..39958bbc0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/GlobalClippingHelperMixin.java @@ -0,0 +1,20 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.jozufozu.flywheel.core.Clipping; + +import net.minecraft.client.renderer.culling.ClippingHelper; +import net.minecraft.util.math.vector.Matrix4f; + +@Mixin(ClippingHelper.class) +public class GlobalClippingHelperMixin { + + @Inject(at = @At("TAIL"), method = "") + private void init(Matrix4f p_i226026_1_, Matrix4f p_i226026_2_, CallbackInfo ci) { + Clipping.HELPER = (ClippingHelper) (Object) this; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index 238e478b6..47002e44e 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.core.Clipping; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; @@ -45,7 +46,7 @@ public class RenderHooksMixin { @Inject(at = @At(value = "INVOKE", target = "net.minecraft.client.renderer.WorldRenderer.compileChunksUntil(J)V"), method = "renderLevel") private void setupFrame(MatrixStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) { - MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, stack, info, gameRenderer, lightTexture)); + MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, stack, info, gameRenderer, lightTexture, Clipping.HELPER)); } /** diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 2c4db2153..1959046f5 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -18,7 +18,8 @@ "atlas.SheetDataAccessor", "light.LightUpdateMixin", "light.NetworkLightUpdateMixin", - "FastChunkProviderMixin" + "FastChunkProviderMixin", + "GlobalClippingHelperMixin" ], "injectors": { "defaultRequire": 0 From d69ff7054ef8a8201d3eb87f97bbe9f032e4540b Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 29 Jul 2021 01:37:47 -0700 Subject: [PATCH 04/16] Model changes - Buffered models directly consume IModels - Document IModel more - Move contraption world render spoofing to flywheel - Miscellaneous new RenderMaths - Added WorldModel, renders many blocks given a world instance - Fix broken transparency on contraptions when using Flywheel --- .../flywheel/backend/gl/buffer/VecBuffer.java | 8 +++ .../backend/instancing/Instancer.java | 8 +-- .../backend/model/ArrayModelRenderer.java | 43 ++++++++++--- .../flywheel/backend/model/BufferedModel.java | 40 +++++------- .../backend/model/IBufferedModel.java | 32 ++++++++++ .../flywheel/backend/model/IndexedModel.java | 19 ++---- .../flywheel/backend/model/ModelRenderer.java | 31 +++++++-- .../com/jozufozu/flywheel/core/Formats.java | 11 +++- .../flywheel/core/model/BlockModel.java | 9 +-- .../jozufozu/flywheel/core/model/IModel.java | 42 ++++++++++++- .../flywheel/core/model/ModelPart.java | 6 -- .../flywheel/core/model/ModelUtil.java | 63 +++++++++++++++---- .../flywheel/core/model/WorldModel.java | 53 ++++++++++++++++ .../jozufozu/flywheel/util/RenderMath.java | 24 +++++++ 14 files changed, 311 insertions(+), 78 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java index 22092e6ca..d24867f8e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java @@ -97,6 +97,14 @@ public class VecBuffer { return this; } + public VecBuffer putColor(byte r, byte g, byte b, byte a) { + internal.put(r); + internal.put(g); + internal.put(b); + internal.put(a); + return this; + } + public VecBuffer putVec3(float x, float y, float z) { internal.putFloat(x); internal.putFloat(y); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java index 61bd13723..132cbd9f2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java @@ -11,9 +11,9 @@ 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.material.MaterialSpec; -import com.jozufozu.flywheel.backend.model.BufferedModel; +import com.jozufozu.flywheel.backend.model.IBufferedModel; +import com.jozufozu.flywheel.backend.model.IndexedModel; import com.jozufozu.flywheel.core.model.IModel; -import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.util.AttribUtil; /** @@ -37,7 +37,7 @@ import com.jozufozu.flywheel.util.AttribUtil; public class Instancer { protected final Supplier gen; - protected BufferedModel model; + protected IBufferedModel model; protected final VertexFormat instanceFormat; protected final IInstanceFactory factory; @@ -92,7 +92,7 @@ public class Instancer { } private void init() { - model = ModelUtil.getIndexedModel(gen.get()); + model = new IndexedModel(gen.get()); initialized = true; if (model.getVertexCount() <= 0) diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java index 5347e74d1..17bcff621 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -1,23 +1,23 @@ package com.jozufozu.flywheel.backend.model; +import java.util.function.Supplier; + import com.jozufozu.flywheel.backend.gl.GlVertexArray; +import com.jozufozu.flywheel.core.model.IModel; +import com.jozufozu.flywheel.util.AttribUtil; public class ArrayModelRenderer extends ModelRenderer { protected GlVertexArray vao; - public ArrayModelRenderer(BufferedModel model) { + public ArrayModelRenderer(Supplier model) { super(model); - vao = new GlVertexArray(); - - vao.bind(); - model.setupState(); - vao.unbind(); - model.clearState(); } + @Override public void draw() { - if (!model.valid()) return; + if (!isInitialized()) init(); + if (!isValid()) return; vao.bind(); @@ -25,4 +25,31 @@ public class ArrayModelRenderer extends ModelRenderer { vao.unbind(); } + + private boolean isValid() { + return model != null && model.valid(); + } + + @Override + protected void init() { + initialized = true; + IModel model = modelSupplier.get(); + + if (model.vertexCount() <= 0) return; + + this.model = new IndexedModel(model); + + vao = new GlVertexArray(); + + vao.bind(); + + // bind the model's vbo to our vao + this.model.setupState(); + + AttribUtil.enableArrays(this.model.getAttributeCount()); + + vao.unbind(); + + this.model.clearState(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index 3c775cfbb..5808c177e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -9,46 +9,45 @@ import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; 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.core.model.IModel; import com.jozufozu.flywheel.util.AttribUtil; -public class BufferedModel { +public class BufferedModel implements IBufferedModel { + protected final IModel model; protected final GlPrimitive primitiveMode; - protected final ByteBuffer data; - protected final VertexFormat format; - protected final int vertexCount; protected GlBuffer vbo; protected boolean deleted; - public BufferedModel(GlPrimitive primitiveMode, VertexFormat format, ByteBuffer data, int vertices) { + public BufferedModel(GlPrimitive primitiveMode, IModel model) { + this.model = model; this.primitiveMode = primitiveMode; - this.data = data; - this.format = format; - this.vertexCount = vertices; vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); vbo.bind(); // allocate the buffer on the gpu - vbo.alloc(this.data.capacity()); + vbo.alloc(model.size()); // mirror it in system memory so we can write to it, and upload our model. - vbo.getBuffer(0, this.data.capacity()) - .put(this.data) - .flush(); + MappedBuffer buffer = vbo.getBuffer(0, model.size()); + model.buffer(buffer); + buffer.flush(); + vbo.unbind(); } public VertexFormat getFormat() { - return format; + return model.format(); } public int getVertexCount() { - return vertexCount; + return model.vertexCount(); } public boolean valid() { - return vertexCount > 0 && !deleted; + return getVertexCount() > 0 && !deleted; } /** @@ -57,7 +56,7 @@ public class BufferedModel { public void setupState() { vbo.bind(); AttribUtil.enableArrays(getAttributeCount()); - format.vertexAttribPointers(0); + getFormat().vertexAttribPointers(0); } public void clearState() { @@ -66,7 +65,7 @@ public class BufferedModel { } public void drawCall() { - glDrawArrays(primitiveMode.glEnum, 0, vertexCount); + glDrawArrays(primitiveMode.glEnum, 0, getVertexCount()); } /** @@ -75,7 +74,7 @@ public class BufferedModel { public void drawInstances(int instanceCount) { if (!valid()) return; - Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, vertexCount, instanceCount); + Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, getVertexCount(), instanceCount); } public void delete() { @@ -84,10 +83,5 @@ public class BufferedModel { deleted = true; vbo.delete(); } - - public int getAttributeCount() { - return format.getAttributeCount(); - } - } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java new file mode 100644 index 000000000..4ac73c572 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java @@ -0,0 +1,32 @@ +package com.jozufozu.flywheel.backend.model; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; + +public interface IBufferedModel { + + VertexFormat getFormat(); + + int getVertexCount(); + + boolean valid(); + + /** + * The VBO/VAO should be bound externally. + */ + void setupState(); + + void clearState(); + + void drawCall(); + + /** + * Draws many instances of this model, assuming the appropriate state is already bound. + */ + void drawInstances(int instanceCount); + + void delete(); + + default int getAttributeCount() { + return getFormat().getAttributeCount(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java index 628491766..9ae5c8c47 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.model.IModel; /** * An indexed triangle model. Just what the driver ordered. @@ -18,15 +19,10 @@ public class IndexedModel extends BufferedModel { protected ElementBuffer ebo; - public IndexedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices, ElementBuffer ebo) { - super(GlPrimitive.TRIANGLES, modelFormat, buf, vertices); + public IndexedModel(IModel model) { + super(GlPrimitive.TRIANGLES, model); - this.ebo = ebo; - } - - public static IndexedModel fromSequentialQuads(VertexFormat modelFormat, ByteBuffer quads, int vertices) { - return new IndexedModel(modelFormat, quads, vertices, QuadConverter.getInstance() - .quads2Tris(vertices / 4)); + this.ebo = model.createEBO(); } @Override @@ -48,13 +44,8 @@ public class IndexedModel extends BufferedModel { @Override public void drawInstances(int instanceCount) { - if (vertexCount <= 0 || deleted) return; + if (!valid()) return; Backend.getInstance().compat.drawInstanced.drawElementsInstanced(primitiveMode, ebo.elementCount, ebo.eboIndexType, 0, instanceCount); } - - @Override - public void delete() { - super.delete(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java index fd1efd896..e0a2e97b9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java @@ -1,17 +1,26 @@ package com.jozufozu.flywheel.backend.model; +import java.util.function.Supplier; + +import com.jozufozu.flywheel.core.model.IModel; + public class ModelRenderer { - protected BufferedModel model; + protected Supplier modelSupplier; + protected IBufferedModel model; - public ModelRenderer(BufferedModel model) { - this.model = model; + protected boolean initialized; + + public ModelRenderer(Supplier modelSupplier) { + this.modelSupplier = modelSupplier; } /** * Renders this model, checking first if there is anything to render. */ public void draw() { + + if (!isInitialized()) init(); if (!model.valid()) return; model.setupState(); @@ -19,7 +28,21 @@ public class ModelRenderer { model.clearState(); } + protected void init() { + initialized = true; + IModel model = modelSupplier.get(); + + if (model.vertexCount() <= 0) return; + + this.model = new IndexedModel(model); + } + + public boolean isInitialized() { + return initialized; + } + public void delete() { - model.delete(); + if (model != null) + model.delete(); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Formats.java b/src/main/java/com/jozufozu/flywheel/core/Formats.java index e7cca829a..6632c2117 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Formats.java +++ b/src/main/java/com/jozufozu/flywheel/core/Formats.java @@ -9,12 +9,21 @@ public class Formats { .addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV) .build(); + public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder() + .addAttributes(CommonAttributes.VEC3, + CommonAttributes.NORMAL, + CommonAttributes.UV, + CommonAttributes.RGBA, + CommonAttributes.LIGHT) + .build(); + public static final VertexFormat TRANSFORMED = litInstance().addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3) .build(); + public static final VertexFormat ORIENTED = litInstance().addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION) .build(); - public static VertexFormat.Builder litInstance() { + public static VertexFormat.Builder litInstance() { return VertexFormat.builder() .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 631c87da1..8ddb4aed2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -23,6 +23,9 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +/** + * A model of a single block. + */ public class BlockModel implements IModel { private static final MatrixStack IDENTITY = new MatrixStack(); @@ -69,12 +72,6 @@ public class BlockModel implements IModel { } } - @Override - public ElementBuffer createEBO() { - return QuadConverter.getInstance() - .quads2Tris(vertexCount() / 4); - } - public static BufferBuilder getBufferBuilder(IBakedModel model, BlockState referenceState, MatrixStack ms) { Minecraft mc = Minecraft.getInstance(); BlockRendererDispatcher dispatcher = mc.getBlockRenderer(); diff --git a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java index 4c3e9d027..7bc41a9ae 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java @@ -3,9 +3,27 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer; +import com.jozufozu.flywheel.core.QuadConverter; /** * A model that can be rendered by flywheel. + * + *

+ * It is expected that the following assertion will not fail: + *

+ * + *
{@code
+ * IModel model = ...;
+ * VecBuffer into = ...;
+ *
+ * int initial = VecBuffer.unwrap().position();
+ *
+ * model.buffer(into);
+ *
+ * int final = VecBuffer.unwrap().position();
+ *
+ * assert model.size() == final - initial;
+ * }
*/ public interface IModel { @@ -14,12 +32,34 @@ public interface IModel { */ void buffer(VecBuffer buffer); + /** + * @return The number of vertices the model has. + */ int vertexCount(); + /** + * @return The format of this model's vertices + */ VertexFormat format(); - ElementBuffer createEBO(); + /** + * Create an element buffer object that indexes the vertices of this model. + * + *

+ * Very often models in minecraft are made up of sequential quads, which is a very predictable pattern. + * The default implementation accommodates this, however this can be overridden to change the behavior and + * support more complex models. + *

+ * @return an element buffer object indexing this model's vertices. + */ + default ElementBuffer createEBO() { + return QuadConverter.getInstance() + .quads2Tris(vertexCount() / 4); + } + /** + * The size in bytes that this model's data takes up. + */ default int size() { return vertexCount() * format().getStride(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java index fff356878..6e81c7d2e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java @@ -43,10 +43,4 @@ public class ModelPart implements IModel { public VertexFormat format() { return Formats.UNLIT_MODEL; } - - @Override - public ElementBuffer createEBO() { - return QuadConverter.getInstance() - .quads2Tris(vertices / 4); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 3039e2218..52b69403d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -1,21 +1,62 @@ package com.jozufozu.flywheel.core.model; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import static org.lwjgl.opengl.GL11.GL_QUADS; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.backend.model.IndexedModel; + +import java.util.Collection; +import java.util.Random; + +import com.mojang.blaze3d.matrix.MatrixStack; + +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockModelRenderer; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.common.util.Lazy; public class ModelUtil { - public static IndexedModel getIndexedModel(IModel blockModel) { - ByteBuffer vertices = ByteBuffer.allocate(blockModel.size()); - vertices.order(ByteOrder.nativeOrder()); + private static final Lazy MODEL_RENDERER = Lazy.of(() -> new BlockModelRenderer(Minecraft.getInstance().getBlockColors())); + private static final Lazy BLOCK_MODELS = Lazy.of(() -> Minecraft.getInstance().getModelManager().getBlockModelShaper()); - blockModel.buffer(new VecBuffer(vertices)); + public static BufferBuilder getBufferBuilderFromTemplate(IBlockDisplayReader renderWorld, RenderType layer, Collection blocks) { + MatrixStack ms = new MatrixStack(); + Random random = new Random(); + BufferBuilder builder = new BufferBuilder(DefaultVertexFormats.BLOCK.getIntegerSize()); + builder.begin(GL_QUADS, DefaultVertexFormats.BLOCK); - ((Buffer) vertices).rewind(); + ForgeHooksClient.setRenderLayer(layer); + BlockModelRenderer.enableCaching(); + for (Template.BlockInfo info : blocks) { + BlockState state = info.state; - return new IndexedModel(blockModel.format(), vertices, blockModel.vertexCount(), blockModel.createEBO()); + if (state.getRenderShape() != BlockRenderType.MODEL) + continue; + if (!RenderTypeLookup.canRenderInLayer(state, layer)) + continue; + + BlockPos pos = info.pos; + + ms.pushPose(); + ms.translate(pos.getX(), pos.getY(), pos.getZ()); + MODEL_RENDERER.get().renderModel(renderWorld, BLOCK_MODELS.get().getBlockModel(state), state, pos, ms, builder, true, + random, 42, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE); + ms.popPose(); + } + BlockModelRenderer.clearCache(); + ForgeHooksClient.setRenderLayer(null); + + builder.end(); + return builder; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java new file mode 100644 index 000000000..fa2001d0d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -0,0 +1,53 @@ +package com.jozufozu.flywheel.core.model; + +import java.util.Collection; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.util.BufferBuilderReader; + +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraft.world.gen.feature.template.Template; + +public class WorldModel implements IModel { + + private final BufferBuilderReader reader; + + public WorldModel(IBlockDisplayReader renderWorld, RenderType layer, Collection blocks) { + reader = new BufferBuilderReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); + } + + @Override + public void buffer(VecBuffer vertices) { + for (int i = 0; i < vertexCount(); i++) { + vertices.putVec3(reader.getX(i), reader.getY(i), reader.getZ(i)); + + vertices.putVec3(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); + + vertices.putVec2(reader.getU(i), reader.getV(i)); + + vertices.putColor(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); + + int light = reader.getLight(i); + + byte block = (byte) (LightTexture.block(light) << 4); + byte sky = (byte) (LightTexture.sky(light) << 4); + + vertices.putVec2(block, sky); + } + } + + @Override + public int vertexCount() { + return reader.getVertexCount(); + } + + @Override + public VertexFormat format() { + return Formats.COLORED_LIT_MODEL; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java index 79028b953..3b891d683 100644 --- a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java +++ b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java @@ -2,7 +2,31 @@ package com.jozufozu.flywheel.util; public class RenderMath { + /** + * Convert a signed, normalized floating point value into a normalized byte. + */ public static byte nb(float f) { return (byte) (f * 127); } + + /** + * Convert a signed byte into a normalized float. + */ + public static float f(byte b) { + return b / 127f; + } + + /** + * Convert an unsigned byte into a normalized float. + */ + public static float uf(byte b) { + return (float) (Byte.toUnsignedInt(b)) / 255f; + } + + /** + * Convert an unsigned, normalized float into an unsigned normalized byte. + */ + public static byte unb(float f) { + return (byte) Math.floor(f * 255); + } } From 0183451a060daadeaa8e9a5154e257a84df98290 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 29 Jul 2021 18:50:47 -0700 Subject: [PATCH 05/16] Fix issue with missing banner pattern textures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #12 What the heck CrumblingRenderer gets classloaded early because of its event listener. CrumblingRenderer had an innocent static reference to a member of ModelBakery. This caused ModelBakery to classload before additional enums could be injected into BannerPattern. ¯\_(ツ)_/¯ --- .../flywheel/core/crumbling/CrumblingRenderer.java | 7 +++---- src/main/java/com/jozufozu/flywheel/util/Lazy.java | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index caca38784..5415512dc 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -53,8 +53,6 @@ public class CrumblingRenderer { INVALIDATOR = state.getSecond(); } - private static final RenderType crumblingLayer = ModelBakery.DESTROY_TYPES.get(0); - public static void renderBreaking(ClientWorld world, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) { if (!Backend.getInstance() .canUseInstancing(world)) return; @@ -64,6 +62,7 @@ public class CrumblingRenderer { if (activeStages.isEmpty()) return; State state = STATE.get(); + RenderType layer = ModelBakery.DESTROY_TYPES.get(0); InstanceManager renderer = state.instanceManager; @@ -71,7 +70,7 @@ public class CrumblingRenderer { ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getMainCamera(); MaterialManager materials = state.materialManager; - crumblingLayer.setupRenderState(); + layer.setupRenderState(); for (Int2ObjectMap.Entry> stage : activeStages.int2ObjectEntrySet()) { int i = stage.getIntKey(); @@ -92,7 +91,7 @@ public class CrumblingRenderer { } - crumblingLayer.clearRenderState(); + layer.clearRenderState(); GlTextureUnit.T0.makeActive(); Texture breaking = textureManager.getTexture(ModelBakery.BREAKING_LOCATIONS.get(0)); diff --git a/src/main/java/com/jozufozu/flywheel/util/Lazy.java b/src/main/java/com/jozufozu/flywheel/util/Lazy.java index 344a07976..6bcbd9d23 100644 --- a/src/main/java/com/jozufozu/flywheel/util/Lazy.java +++ b/src/main/java/com/jozufozu/flywheel/util/Lazy.java @@ -36,6 +36,10 @@ public class Lazy { return Pair.of(lazy, killSwitch); } + public static Lazy of(NonNullSupplier factory) { + return new Lazy<>(factory); + } + public static class KillSwitch { private final Lazy lazy; From a5850a46c80e09df08115241ca5fe2d6a75e0945 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 30 Jul 2021 14:34:04 -0700 Subject: [PATCH 06/16] Update changelog and remove dead QuaternionTransformStack.java --- changelog.txt | 27 ++++++ .../transform/QuaternionTransformStack.java | 95 ------------------- 2 files changed, 27 insertions(+), 95 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/util/transform/QuaternionTransformStack.java diff --git a/changelog.txt b/changelog.txt index 4b5166b4d..6482468a4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,30 @@ +0.2.0: +New + - Flywheel driven shulker box rendering + - Optimize chunk accesses by caching previous result + - Further optimize flywheel rendered objects through parallel updates +Changes + - Distant objects are update throttled according to the sequence of prime numbers, smoothing out updates + - Rename normalOverlay to debugNormals, make naming consistent across command and config +Fixes + - Fix issue causing modded banner patterns to all have missing textures +Technical/API + - Reorganize, simplify, and document everything in the MaterialManager tree + - MaterialManagers associate RenderStates with MaterialGroups + - Proper support for rendering in different layers (SOLID, CUTOUT, and TRANSPARENT) + - New methods in MaterialManager to accommodate these changes + - Deprecate old functions in MaterialManager in favor of new ones using MaterialGroups + - InstanceDatas can be transferred to other Instancers via "instance stealing" + - Abstraction for models, IModel + - Easier to use, and gives Flywheel more freedom to optimize + - Buffered models directly consume IModels + - Added BlockModel, renders a single block + - Added WorldModel, renders many blocks given a world instance + - Cuboids can be inverted across Y and Z, used by many vanilla models for some reason + - TransformStack scaling + - VecBuffer coloring + - Add more information to RenderLayerEvent and BeginFrameEvent + 0.1.1: New - Flywheel driven chest and bell rendering, ~20x performance improvement in contrived cases diff --git a/src/main/java/com/jozufozu/flywheel/util/transform/QuaternionTransformStack.java b/src/main/java/com/jozufozu/flywheel/util/transform/QuaternionTransformStack.java deleted file mode 100644 index 3e6908201..000000000 --- a/src/main/java/com/jozufozu/flywheel/util/transform/QuaternionTransformStack.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.jozufozu.flywheel.util.transform; - -import java.util.ArrayDeque; -import java.util.Deque; - -import net.minecraft.util.math.vector.Quaternion; - -public class QuaternionTransformStack implements TransformStack { - - private final Deque stack; - - public QuaternionTransformStack() { - stack = new ArrayDeque<>(); - stack.add(new Transform()); - } - - @Override - public TransformStack translate(double x, double y, double z) { - - Transform peek = stack.peek(); - - double qx = peek.qx; - double qy = peek.qy; - double qz = peek.qz; - double qw = peek.qw; - peek.x += qw * x + qy * z - qz * y; - peek.y += qw * y - qx * z + qz * x; - peek.z += qw * z + qx * y - qy * x; - - return this; - } - - @Override - public TransformStack multiply(Quaternion quaternion) { - return this; - } - - @Override - public TransformStack push() { - stack.push(stack.peek().copy()); - return this; - } - - @Override - public TransformStack scale(float factor) { - return this; - } - - @Override - public TransformStack pop() { - - if (stack.size() == 1) { - stack.peek().loadIdentity(); - } else { - stack.pop(); - } - - return this; - } - - private static class Transform { - public double qx; - public double qy; - public double qz; - public double qw; - public double x; - public double y; - public double z; - - public Transform() { - qw = 1.0; - } - - public void loadIdentity() { - x = y = z = 0.0; - - qx = qy = qz = 0.0; - qw = 1.0; - } - - public Transform copy() { - Transform transform = new Transform(); - - transform.qx = this.qx; - transform.qy = this.qy; - transform.qz = this.qz; - transform.qw = this.qw; - transform.x = this.x; - transform.y = this.y; - transform.z = this.z; - - return transform; - } - } -} From b530ea1be566e385a060285cc648fd162316f614 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 2 Aug 2021 00:06:26 -0700 Subject: [PATCH 07/16] Fix chunk caching race condition add toggle - /flywheel chunkCaching on|off - synchronized ftw --- .../jozufozu/flywheel/backend/Backend.java | 3 ++ .../flywheel/config/BooleanConfig.java | 20 ++++++++ .../jozufozu/flywheel/config/FlwCommands.java | 4 +- .../jozufozu/flywheel/config/FlwConfig.java | 8 ++++ .../mixin/FastChunkProviderMixin.java | 46 ++++++++++++++----- 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 770e0734c..51853e0ba 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -43,6 +43,7 @@ public class Backend { private Matrix4f projectionMatrix = new Matrix4f(); private boolean instancedArrays; private boolean enabled; + public boolean chunkCachingEnabled; private final List> contexts = new ArrayList<>(); private final Map> materialRegistry = new HashMap<>(); @@ -153,6 +154,8 @@ public class Backend { enabled = FlwConfig.get() .enabled() && !OptifineHandler.usingShaders(); + chunkCachingEnabled = FlwConfig.get() + .chunkCaching(); } public boolean canUseInstancing(@Nullable World world) { diff --git a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java index 8d143dea9..c34a80c32 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java @@ -18,6 +18,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; public enum BooleanConfig { ENGINE(() -> BooleanConfig::enabled), NORMAL_OVERLAY(() -> BooleanConfig::normalOverlay), + CHUNK_CACHING(() -> BooleanConfig::chunkCaching), ; final Supplier> receiver; @@ -71,6 +72,25 @@ public enum BooleanConfig { player.displayClientMessage(text, false); } + @OnlyIn(Dist.CLIENT) + private static void chunkCaching(BooleanDirective state) { + ClientPlayerEntity player = Minecraft.getInstance().player; + if (player == null || state == null) return; + + if (state == BooleanDirective.DISPLAY) { + ITextComponent text = new StringTextComponent("Chunk caching is currently: ").append(boolToText(FlwConfig.get().client.debugNormals.get())); + player.displayClientMessage(text, false); + return; + } + + FlwConfig.get().client.chunkCaching.set(state.get()); + + ITextComponent text = boolToText(FlwConfig.get().client.chunkCaching.get()).append(new StringTextComponent(" chunk caching").withStyle(TextFormatting.WHITE)); + + player.displayClientMessage(text, false); + Backend.reloadWorldRenderers(); + } + private static IFormattableTextComponent boolToText(boolean b) { return b ? new StringTextComponent("enabled").withStyle(TextFormatting.DARK_GREEN) : new StringTextComponent("disabled").withStyle(TextFormatting.RED); } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index fe58438d0..a601971cb 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -16,6 +16,8 @@ public class FlwCommands { dispatcher.register(Commands.literal("flywheel") .then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register()) - .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register())); + .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register()) + .then(new BooleanConfigCommand("chunkCaching", BooleanConfig.CHUNK_CACHING).register()) + ); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index 0fd5a2673..359d7111e 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -34,12 +34,17 @@ public class FlwConfig { return client.debugNormals.get(); } + public boolean chunkCaching() { + return client.chunkCaching.get(); + } + public static void init() { } public static class ClientConfig { public final BooleanValue enabled; public final BooleanValue debugNormals; + public final BooleanValue chunkCaching; public ClientConfig(ForgeConfigSpec.Builder builder) { @@ -48,6 +53,9 @@ public class FlwConfig { debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal") .define("debugNormals", false); + + chunkCaching = builder.comment("Cache chunk lookups to improve performance.") + .define("chunkCaching", true); } } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/FastChunkProviderMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/FastChunkProviderMixin.java index 9a1f3a0f9..28301ea18 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/FastChunkProviderMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/FastChunkProviderMixin.java @@ -1,6 +1,8 @@ package com.jozufozu.flywheel.mixin; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -8,7 +10,10 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import com.jozufozu.flywheel.backend.Backend; + import net.minecraft.client.multiplayer.ClientChunkProvider; +import net.minecraft.client.world.ClientWorld; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.PacketBuffer; import net.minecraft.world.biome.BiomeContainer; @@ -20,6 +25,9 @@ import net.minecraft.world.chunk.IChunk; @Mixin(ClientChunkProvider.class) public abstract class FastChunkProviderMixin extends AbstractChunkProvider { + @Shadow + @Final + private ClientWorld level; @Unique private int lastX; @Unique @@ -32,36 +40,52 @@ public abstract class FastChunkProviderMixin extends AbstractChunkProvider { at = @At("HEAD"), cancellable = true) public void returnCachedChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable cir) { - if (status.isOrAfter(ChunkStatus.FULL) && lastChunk != null && x == lastX && z == lastZ) { - cir.setReturnValue(lastChunk); + if (Backend.getInstance().chunkCachingEnabled && status.isOrAfter(ChunkStatus.FULL)) { + synchronized (level) { + if (lastChunk != null && x == lastX && z == lastZ) { + cir.setReturnValue(lastChunk); + } + } } } @Inject(method = "getChunk", at = @At("RETURN")) public void cacheChunk(int x, int z, ChunkStatus status, boolean create, CallbackInfoReturnable cir) { - if (status.isOrAfter(ChunkStatus.FULL)) { - lastChunk = cir.getReturnValue(); - lastX = x; - lastZ = z; + if (Backend.getInstance().chunkCachingEnabled && status.isOrAfter(ChunkStatus.FULL)) { + synchronized (level) { + lastChunk = cir.getReturnValue(); + lastX = x; + lastZ = z; + } } } @Inject(method = "drop", at = @At("HEAD")) public void invalidateOnDrop(int x, int z, CallbackInfo ci) { - if (x == lastX && z == lastZ) - lastChunk = null; + if (Backend.getInstance().chunkCachingEnabled) { + synchronized (level) { + if (x == lastX && z == lastZ) lastChunk = null; + } + } } @Inject(method = "replaceWithPacketData", at = @At("HEAD")) public void invalidateOnPacket(int x, int z, BiomeContainer p_228313_3_, PacketBuffer p_228313_4_, CompoundNBT p_228313_5_, int p_228313_6_, boolean p_228313_7_, CallbackInfoReturnable cir) { - if (x == lastX && z == lastZ) - lastChunk = null; + if (Backend.getInstance().chunkCachingEnabled) { + synchronized (level) { + if (x == lastX && z == lastZ) lastChunk = null; + } + } } @Redirect(method = "isTickingChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientChunkProvider;hasChunk(II)Z")) public boolean redirectTicking(ClientChunkProvider clientChunkProvider, int x, int z) { - if (lastChunk != null && x == lastX && z == lastZ) return true; + if (Backend.getInstance().chunkCachingEnabled) { + synchronized (level) { + if (lastChunk != null && x == lastX && z == lastZ) return true; + } + } return clientChunkProvider.hasChunk(x, z); } From 0875930e6407718aceeb828eb9ba3b7247ee0de1 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 2 Aug 2021 14:06:51 -0700 Subject: [PATCH 08/16] Bump version - 0.2.1 --- changelog.txt | 6 ++++++ gradle.properties | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 6482468a4..f448768ae 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +0.2.1: +Fixes + - Potential fix for many issues caused by optimized chunk accesses +New + - Added config+command to disable chunk access optimization + 0.2.0: New - Flywheel driven shulker box rendering diff --git a/gradle.properties b/gradle.properties index 224c737c4..1e9ce980a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false # mod version info -mod_version=0.2.0 +mod_version=0.2.1 mc_update_version=1.16 minecraft_version=1.16.5 forge_version=36.1.66 From d4517fbe948360be9f87da2cf0e7fe24d92e37f4 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 2 Aug 2021 14:22:21 -0700 Subject: [PATCH 09/16] Wrong display value for chunk caching command --- src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java index c34a80c32..98f8701f2 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java @@ -78,7 +78,7 @@ public enum BooleanConfig { if (player == null || state == null) return; if (state == BooleanDirective.DISPLAY) { - ITextComponent text = new StringTextComponent("Chunk caching is currently: ").append(boolToText(FlwConfig.get().client.debugNormals.get())); + ITextComponent text = new StringTextComponent("Chunk caching is currently: ").append(boolToText(FlwConfig.get().client.chunkCaching.get())); player.displayClientMessage(text, false); return; } From 55968d2c989a6407bdfb06a1f4f982e9d34d712e Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 2 Aug 2021 20:07:49 -0700 Subject: [PATCH 10/16] Switch to MIT Licence --- LICENCE.md | 140 ++++---------------------- src/main/resources/META-INF/mods.toml | 2 +- 2 files changed, 18 insertions(+), 124 deletions(-) diff --git a/LICENCE.md b/LICENCE.md index 34859d7dd..ecc0bf77c 100644 --- a/LICENCE.md +++ b/LICENCE.md @@ -1,126 +1,20 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +Copyright (c) 2021 Jozufozu -Copyright (C) 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU -General Public License, supplemented by the additional permissions listed below. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -0. Additional Definitions. - -As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to -version 3 of the GNU General Public License. - -"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined -below. - -An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on -the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by -the Library. - -A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of -the Library with which the Combined Work was made is also called the "Linked Version". - -The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding -any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not -on the Linked Version. - -The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, -including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the -System Libraries of the Combined Work. - -1. Exception to Section 3 of the GNU GPL. - -You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. - -2. Conveying Modified Versions. - -If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied -by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may -convey a copy of the modified version: - -a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not -supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, -or - -b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. - -3. Object Code Incorporating Material from Library Header Files. - -The object code form of an Application may incorporate material from a header file that is part of the Library. You may -convey such object code under terms of your choice, provided that, if the incorporated material is not limited to -numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - -a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its -use are covered by this License. - -b) Accompany the object code with a copy of the GNU GPL and this license document. - -4. Combined Works. - -You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification -of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, -if you also do each of the following: - -a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its -use are covered by this License. - -b) Accompany the Combined Work with a copy of the GNU GPL and this license document. - -c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library -among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. - -d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - -e) Provide Installation Information, but only if you would otherwise be required to provide such information under -section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified -version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked -Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and -Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner -specified by section 6 of the GNU GPL for conveying Corresponding Source.) - -5. Combined Libraries. - -You may place library facilities that are a work based on the Library side by side in a single library together with -other library facilities that are not Applications and are not covered by this License, and convey such a combined -library under terms of your choice, if you do both of the following: - -a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library -facilities, conveyed under the terms of this License. - -b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where -to find the accompanying uncombined form of the same work. - -6. Revised Versions of the GNU Lesser General Public License. - -The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time -to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new -problems or concerns. - -Each version is given a distinguishing version number. If the Library as you received it specifies that a certain -numbered version of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and conditions either of that published version or of any -later version published by the Free Software Foundation. If the Library as you received it does not specify a version -number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License -ever published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General -Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for -you to choose that version for the Library. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 5be2bde90..5ceeaca9b 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,7 +1,7 @@ modLoader = "javafml" loaderVersion = "[36,)" issueTrackerURL = "https://github.com/Jozufozu/Flywheel/issues" -license = "LGPLv3" +license = "MIT" [[mods]] modId = "flywheel" From 9ae823ef92ce713161c16f5ed8ff78389b667263 Mon Sep 17 00:00:00 2001 From: Jordan Ramsay Date: Tue, 3 Aug 2021 16:52:00 +1000 Subject: [PATCH 11/16] Patch to stop the java.util.ConcurrentModificationException. --- .../backend/instancing/InstanceManager.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 304bac4f0..636a59eb4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -3,7 +3,10 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nullable; @@ -19,8 +22,12 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public final MaterialManager materialManager; - protected final ArrayList queuedAdditions; - protected final ConcurrentHashMap.KeySetView queuedUpdates; + //Thread locks to ensure concurrent access exceptions don't occur + private final ReadWriteLock queuedUpdatesLock = new ReentrantReadWriteLock(); + private final ReadWriteLock queuedAdditionsLock = new ReentrantReadWriteLock(); + + private final ArrayList queuedAdditions; + private final ConcurrentHashMap.KeySetView queuedUpdates; protected final Map instances; protected final Object2ObjectOpenHashMap tickableInstances; @@ -73,11 +80,20 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL }); } + + + + //suggested replacement ? + //Unable to confirm if the call to update(te) causes updates to the que. + //queuedUpdatesLock.writeLock().lock(); + //* queuedUpdates.forEach(te -> { queuedUpdates.remove(te); - update(te); - }); + });//*/ + //queuedUpdates.forEach(this::update); + //queuedUpdates.clear(); + //queuedUpdatesLock.writeLock().unlock(); } public void beginFrame(ActiveRenderInfo info) { @@ -118,7 +134,9 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL if (!Backend.getInstance() .canUseInstancing()) return; + queuedAdditionsLock.writeLock().lock(); queuedAdditions.add(obj); + queuedAdditionsLock.writeLock().unlock(); } public void update(T obj) { @@ -144,8 +162,9 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public synchronized void queueUpdate(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; - + queuedUpdatesLock.writeLock().lock(); queuedUpdates.add(obj); + queuedUpdatesLock.writeLock().unlock(); } public void onLightUpdate(T obj) { @@ -194,9 +213,12 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } protected synchronized void processQueuedAdditions() { - if (queuedAdditions.size() > 0) { - queuedAdditions.forEach(this::addInternal); - queuedAdditions.clear(); + queuedAdditionsLock.writeLock().lock(); + ArrayList queued = new ArrayList<>(queuedAdditions); + queuedAdditions.clear(); + queuedAdditionsLock.writeLock().unlock(); + if (queued.size() > 0) { + queued.forEach(this::addInternal); } } From 7eb0284db95be8a2d4e0e07e4ae3b914ca08f8ec Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 3 Aug 2021 02:06:41 -0700 Subject: [PATCH 12/16] Simplify synchronization - Use `synchronized` on the queue sets in favor of explicit locks. - Updates and additions work the same now - Move updates processing to separate function --- .../backend/instancing/InstanceManager.java | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 636a59eb4..7a8800bc3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -1,12 +1,16 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; import javax.annotation.Nullable; @@ -14,6 +18,7 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.material.MaterialManager; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3f; @@ -22,12 +27,8 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public final MaterialManager materialManager; - //Thread locks to ensure concurrent access exceptions don't occur - private final ReadWriteLock queuedUpdatesLock = new ReentrantReadWriteLock(); - private final ReadWriteLock queuedAdditionsLock = new ReentrantReadWriteLock(); - - private final ArrayList queuedAdditions; - private final ConcurrentHashMap.KeySetView queuedUpdates; + private final Set queuedAdditions; + private final Set queuedUpdates; protected final Map instances; protected final Object2ObjectOpenHashMap tickableInstances; @@ -38,8 +39,8 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public InstanceManager(MaterialManager materialManager) { this.materialManager = materialManager; - this.queuedUpdates = ConcurrentHashMap.newKeySet(64); - this.queuedAdditions = new ArrayList<>(64); + this.queuedUpdates = new HashSet<>(64); + this.queuedAdditions = new HashSet<>(64); this.instances = new HashMap<>(); this.dynamicInstances = new Object2ObjectOpenHashMap<>(); @@ -56,6 +57,7 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public void tick(double cameraX, double cameraY, double cameraZ) { tick++; + processQueuedUpdates(); // integer camera pos int cX = (int) cameraX; @@ -79,21 +81,6 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); }); } - - - - - //suggested replacement ? - //Unable to confirm if the call to update(te) causes updates to the que. - //queuedUpdatesLock.writeLock().lock(); - //* - queuedUpdates.forEach(te -> { - queuedUpdates.remove(te); - update(te); - });//*/ - //queuedUpdates.forEach(this::update); - //queuedUpdates.clear(); - //queuedUpdatesLock.writeLock().unlock(); } public void beginFrame(ActiveRenderInfo info) { @@ -134,9 +121,9 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL if (!Backend.getInstance() .canUseInstancing()) return; - queuedAdditionsLock.writeLock().lock(); - queuedAdditions.add(obj); - queuedAdditionsLock.writeLock().unlock(); + synchronized (queuedAdditions) { + queuedAdditions.add(obj); + } } public void update(T obj) { @@ -162,9 +149,9 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL public synchronized void queueUpdate(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; - queuedUpdatesLock.writeLock().lock(); - queuedUpdates.add(obj); - queuedUpdatesLock.writeLock().unlock(); + synchronized (queuedUpdates) { + queuedUpdates.add(obj); + } } public void onLightUpdate(T obj) { @@ -195,7 +182,6 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL tickableInstances.clear(); } - @SuppressWarnings("unchecked") @Nullable protected IInstance getInstance(I obj, boolean create) { if (!Backend.getInstance() @@ -212,16 +198,32 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } - protected synchronized void processQueuedAdditions() { - queuedAdditionsLock.writeLock().lock(); - ArrayList queued = new ArrayList<>(queuedAdditions); - queuedAdditions.clear(); - queuedAdditionsLock.writeLock().unlock(); + protected void processQueuedAdditions() { + ArrayList queued; + + synchronized (queuedAdditions) { + queued = new ArrayList<>(queuedAdditions); + queuedAdditions.clear(); + } + if (queued.size() > 0) { queued.forEach(this::addInternal); } } + protected void processQueuedUpdates() { + ArrayList queued; + + synchronized (queuedUpdates) { + queued = new ArrayList<>(queuedUpdates); + queuedUpdates.clear(); + } + + if (queued.size() > 0) { + queued.forEach(this::update); + } + } + protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { int dX = worldPos.getX() - cX; int dY = worldPos.getY() - cY; From 71ff2363a5e72450c77102fea4ab1c43afc95620 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Mon, 2 Aug 2021 23:52:24 -0700 Subject: [PATCH 13/16] Miscellaneous documentation --- .../backend/instancing/IInstance.java | 9 ++++ .../backend/instancing/InstanceManager.java | 49 ++++++++++++++++--- .../flywheel/mixin/RenderHooksMixin.java | 2 +- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/IInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/IInstance.java index fa424ffa6..e3b969f7a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/IInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/IInstance.java @@ -16,6 +16,15 @@ public interface IInstance { void remove(); + /** + * When an instance is reset, the instance is deleted and re-created. + * + *

+ * This is used to handle things like block state changes. + *

+ * + * @return true if this instance should be reset + */ boolean shouldReset(); void update(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 7a8800bc3..6b9dbc8f6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -49,17 +49,41 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL materialManager.addListener(this); } + /** + * Is the given object capable of being instanced at all? + * + * @return false if on object cannot be instanced. + */ protected abstract boolean canInstance(T obj); + /** + * Is the given object currently capable of being instanced? + * + *

+ * This won't be the case for TEs or entities that are outside of loaded chunks. + *

+ * + * @return true if the object is currently capable of being instanced. + */ + protected abstract boolean canCreateInstance(T obj); + + @Nullable protected abstract IInstance createRaw(T obj); - protected abstract boolean canCreateInstance(T entity); - + /** + * Ticks the InstanceManager. + * + *

+ * {@link ITickableInstance}s get ticked. + *
+ * Queued updates are processed. + *

+ */ public void tick(double cameraX, double cameraY, double cameraZ) { tick++; processQueuedUpdates(); - // integer camera pos + // integer camera pos as a micro-optimization int cX = (int) cameraX; int cY = (int) cameraY; int cZ = (int) cameraZ; @@ -117,7 +141,7 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } - public synchronized void queueAdd(T obj) { + public void queueAdd(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; @@ -126,6 +150,17 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } + /** + * Update the instance associated with an object. + * + *

+ * By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency + * update hook IInstance gets. For more frequent updates, see {@link ITickableInstance} and + * {@link IDynamicInstance}. + *

+ * + * @param obj the object to update. + */ public void update(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; @@ -135,9 +170,11 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL if (instance != null) { + // resetting instances is by default used to handle block state changes. if (instance.shouldReset()) { + // delete and re-create the instance. + // resetting an instance supersedes updating it. removeInternal(obj, instance); - createInternal(obj); } else { instance.update(); @@ -146,7 +183,7 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL } } - public synchronized void queueUpdate(T obj) { + public void queueUpdate(T obj) { if (!Backend.getInstance() .canUseInstancing()) return; synchronized (queuedUpdates) { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java index 47002e44e..80abdb73e 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java @@ -45,7 +45,7 @@ public class RenderHooksMixin { private RenderTypeBuffers renderBuffers; @Inject(at = @At(value = "INVOKE", target = "net.minecraft.client.renderer.WorldRenderer.compileChunksUntil(J)V"), method = "renderLevel") - private void setupFrame(MatrixStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f p_228426_9_, CallbackInfo ci) { + private void setupFrame(MatrixStack stack, float p_228426_2_, long p_228426_3_, boolean p_228426_5_, ActiveRenderInfo info, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projection, CallbackInfo ci) { MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, stack, info, gameRenderer, lightTexture, Clipping.HELPER)); } From 16e8eec6abc4be08e16d878f8604b6a20ed0ce4a Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 4 Aug 2021 14:34:13 -0700 Subject: [PATCH 14/16] Fix nullpointer in ModelRenderer - Somehow managed to only do a check in a subclass - Add #empty() helper method to IModel --- .../backend/model/ArrayModelRenderer.java | 8 ++--- .../flywheel/backend/model/ModelRenderer.java | 31 +++++++++---------- .../jozufozu/flywheel/core/model/IModel.java | 8 +++++ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java index 17bcff621..48359821c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -16,7 +16,7 @@ public class ArrayModelRenderer extends ModelRenderer { @Override public void draw() { - if (!isInitialized()) init(); + if (!initialized) init(); if (!isValid()) return; vao.bind(); @@ -26,16 +26,12 @@ public class ArrayModelRenderer extends ModelRenderer { vao.unbind(); } - private boolean isValid() { - return model != null && model.valid(); - } - @Override protected void init() { initialized = true; IModel model = modelSupplier.get(); - if (model.vertexCount() <= 0) return; + if (model.empty()) return; this.model = new IndexedModel(model); diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java index e0a2e97b9..72a11d601 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java @@ -19,30 +19,29 @@ public class ModelRenderer { * Renders this model, checking first if there is anything to render. */ public void draw() { - - if (!isInitialized()) init(); - if (!model.valid()) return; + if (!initialized) init(); + if (!isValid()) return; model.setupState(); model.drawCall(); model.clearState(); } - protected void init() { - initialized = true; - IModel model = modelSupplier.get(); - - if (model.vertexCount() <= 0) return; - - this.model = new IndexedModel(model); - } - - public boolean isInitialized() { - return initialized; - } - public void delete() { if (model != null) model.delete(); } + + protected void init() { + initialized = true; + IModel model = modelSupplier.get(); + + if (model.empty()) return; + + this.model = new IndexedModel(model); + } + + protected boolean isValid() { + return model != null && model.valid(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java index 7bc41a9ae..b7ffd1a1b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java @@ -63,4 +63,12 @@ public interface IModel { default int size() { return vertexCount() * format().getStride(); } + + /** + * Is there nothing to render? + * @return true if there are no vertices. + */ + default boolean empty() { + return vertexCount() == 0; + } } From cda66c0ad8f922f9817602b4765d0c7a7d18efd1 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 4 Aug 2021 20:20:24 -0700 Subject: [PATCH 15/16] Fix #23 - Clamp update divisor --- .../jozufozu/flywheel/backend/instancing/InstanceManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 6b9dbc8f6..9c88d2ff1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -16,11 +16,13 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.material.MaterialManager; +import com.jozufozu.flywheel.util.RenderMath; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArraySet; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector3f; public abstract class InstanceManager implements MaterialManager.OriginShiftListener { @@ -281,7 +283,7 @@ public abstract class InstanceManager implements MaterialManager.OriginShiftL int i = (dSq / 2048); - return divisorSequence[Math.min(i, divisorSequence.length - 1)]; + return divisorSequence[MathHelper.clamp(i, 0, divisorSequence.length - 1)]; } protected void addInternal(T tile) { From 894350efecdd834aa17705101aa15476a08f4fdb Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 4 Aug 2021 22:44:21 -0700 Subject: [PATCH 16/16] Bump version - 0.2.2 --- changelog.txt | 6 ++++++ gradle.properties | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index f448768ae..40f8f2581 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +0.2.2: +Fixes + - Fix ConcurrentModificationException crash + - Fix NullPointer rendering create contraptions on older graphics cards + - Fix crash triggered when moving large distances in a single frame + 0.2.1: Fixes - Potential fix for many issues caused by optimized chunk accesses diff --git a/gradle.properties b/gradle.properties index 1e9ce980a..14e6e72ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false # mod version info -mod_version=0.2.1 +mod_version=0.2.2 mc_update_version=1.16 minecraft_version=1.16.5 forge_version=36.1.66