diff --git a/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java b/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java
index 8feb72ca3..a921ca61c 100644
--- a/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java
+++ b/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java
@@ -11,10 +11,38 @@ public interface InstancerProvider {
*
Calling this method twice with the same arguments in the
* same frame will return the same instancer.
*
- * It is not safe to store instancers between frames. Each
+ *
It is NOT safe to store instancers between frames. Each
* time you need an instancer, you should call this method.
*
- * @return An instancer for the given instance type rendering the given model.
+ * Render Order
+ * In general, you can assume all instances in the same instancer will be rendered in a single draw call.
+ * Backends are free to optimize the ordering of draw calls to a certain extent, but utilities are provided to let
+ * you control the order of draw calls
+ *
Mesh Order
+ *
For one, Meshes within a Model are guaranteed to render in the order they appear in their containing list.
+ * This lets you e.g. preserve (or break!) vanilla's chunk RenderType order guarantees or control which Meshes of
+ * your Model render over others.
+ * Bias Order
+ *
The other method is via the {@code bias} parameter to this method. An instancer with a lower bias will have
+ * its instances draw BEFORE an instancer with a higher bias. This allows you to control the render order between
+ * your instances to e.g. create an "overlay" instance to selectively color or apply decals to another instance.
+ *
+ * @param type The instance type to parameterize your instances by.
+ * @param model The Model to instance.
+ * @param bias A weight to control render order between instancers.
+ * Instancers are rendered in ascending order by bias.
+ * @return An instancer.
*/
- Instancer instancer(InstanceType type, Model model);
+ Instancer instancer(InstanceType type, Model model, int bias);
+
+ /**
+ * Get an instancer with no bias for the given instance type rendering the given model with.
+ *
+ * @param type The instance type to parameterize your instances by.
+ * @param model The model to instance.
+ * @return An instancer with {@code bias == 0}.
+ */
+ default Instancer instancer(InstanceType type, Model model) {
+ return instancer(type, model, 0);
+ }
}
diff --git a/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java b/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java
index 64b240d4b..9ad458193 100644
--- a/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java
+++ b/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java
@@ -2,6 +2,10 @@ package dev.engine_room.flywheel.api.model;
import java.util.List;
+import dev.engine_room.flywheel.api.instance.InstanceType;
+
+import dev.engine_room.flywheel.api.instance.InstancerProvider;
+
import org.joml.Vector4fc;
import dev.engine_room.flywheel.api.material.Material;
@@ -12,8 +16,8 @@ public interface Model {
*
* The contents of the returned list will be queried, but never modified.
*
- * Meshes will be rendered in the order they appear in this list, though
- * no render order guarantees are made for meshes between different models.
+ * Meshes will be rendered in the order they appear in this list. See
+ * {@link InstancerProvider#instancer(InstanceType, Model, int)} for a complete explanation
*
* @return A list of meshes.
*/
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java
index 4f74b1fc1..cf07a4597 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java
@@ -36,8 +36,8 @@ public abstract class DrawManager> {
protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>();
@SuppressWarnings("unchecked")
- public Instancer getInstancer(Environment environment, InstanceType type, Model model, VisualType visualType) {
- return (Instancer) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType), this::createAndDeferInit);
+ public Instancer getInstancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) {
+ return (Instancer) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType, bias), this::createAndDeferInit);
}
public void flush(LightStorage lightStorage) {
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java
index f3a644883..cdec9eece 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java
@@ -109,8 +109,8 @@ public class EngineImpl implements Engine {
lightStorage.delete();
}
- public Instancer instancer(Environment environment, InstanceType type, Model model, VisualType visualType) {
- return drawManager.getInstancer(environment, type, model, visualType);
+ public Instancer instancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) {
+ return drawManager.getInstancer(environment, type, model, visualType, bias);
}
public EnvironmentStorage environmentStorage() {
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java
index ecdbc29e2..1910c9fbf 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java
@@ -7,5 +7,5 @@ import dev.engine_room.flywheel.api.visualization.VisualType;
import dev.engine_room.flywheel.backend.engine.embed.Environment;
public record InstancerKey(Environment environment, InstanceType type, Model model,
- VisualType visualType) {
+ VisualType visualType, int bias) {
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java
index 3a297b76a..064a5388a 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java
@@ -10,7 +10,7 @@ import dev.engine_room.flywheel.backend.engine.embed.GlobalEnvironment;
public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider {
@Override
- public Instancer instancer(InstanceType type, Model model) {
- return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType);
+ public Instancer instancer(InstanceType type, Model model, int bias) {
+ return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias);
}
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java
index 42deacf6a..bea81b45c 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java
@@ -39,9 +39,9 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment {
instancerProvider = new InstancerProvider() {
@Override
- public Instancer instancer(InstanceType type, Model model) {
+ public Instancer instancer(InstanceType type, Model model, int bias) {
// Kinda cursed usage of anonymous classes here, but it does the job.
- return engine.instancer(EmbeddedEnvironment.this, type, model, visualType);
+ return engine.instancer(EmbeddedEnvironment.this, type, model, visualType, bias);
}
};
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java
index 5cc8702c9..5dbcbe3c8 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java
@@ -21,6 +21,7 @@ import dev.engine_room.flywheel.api.model.Model;
import dev.engine_room.flywheel.api.visualization.VisualType;
import dev.engine_room.flywheel.backend.compile.ContextShader;
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
+import dev.engine_room.flywheel.backend.engine.InstancerKey;
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
import dev.engine_room.flywheel.backend.engine.MeshPool;
import dev.engine_room.flywheel.backend.engine.embed.Environment;
@@ -31,6 +32,7 @@ import dev.engine_room.flywheel.lib.math.MoreMath;
public class IndirectCullingGroup {
private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType)
+ .thenComparing(IndirectDraw::bias)
.thenComparing(IndirectDraw::indexOfMeshInModel)
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
@@ -174,16 +176,17 @@ public class IndirectCullingGroup {
return multiDraws.containsKey(visualType);
}
- public void add(IndirectInstancer instancer, Model model, VisualType visualType, MeshPool meshPool) {
+ public void add(IndirectInstancer instancer, InstancerKey key, MeshPool meshPool) {
instancer.modelIndex = instancers.size();
instancers.add(instancer);
- List meshes = model.meshes();
+ List meshes = key.model()
+ .meshes();
for (int i = 0; i < meshes.size(); i++) {
var entry = meshes.get(i);
MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh());
- var draw = new IndirectDraw(instancer, entry.material(), mesh, visualType, i);
+ var draw = new IndirectDraw(instancer, entry.material(), mesh, key.visualType(), key.bias(), i);
indirectDraws.add(draw);
instancer.addDraw(draw);
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java
index 2ad217f1a..093616337 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java
@@ -13,6 +13,7 @@ public class IndirectDraw {
private final Material material;
private final MeshPool.PooledMesh mesh;
private final VisualType visualType;
+ private final int bias;
private final int indexOfMeshInModel;
private final int materialVertexIndex;
@@ -21,11 +22,12 @@ public class IndirectDraw {
private final int packedMaterialProperties;
private boolean deleted;
- public IndirectDraw(IndirectInstancer> instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int indexOfMeshInModel) {
+ public IndirectDraw(IndirectInstancer> instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int bias, int indexOfMeshInModel) {
this.instancer = instancer;
this.material = material;
this.mesh = mesh;
this.visualType = visualType;
+ this.bias = bias;
this.indexOfMeshInModel = indexOfMeshInModel;
mesh.acquire();
@@ -52,6 +54,10 @@ public class IndirectDraw {
return visualType;
}
+ public int bias() {
+ return bias;
+ }
+
public int indexOfMeshInModel() {
return indexOfMeshInModel;
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java
index 204da6bb2..2066554f0 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java
@@ -63,7 +63,7 @@ public class IndirectDrawManager extends DrawManager> {
protected void initialize(InstancerKey key, IndirectInstancer> instancer) {
var groupKey = new GroupKey<>(key.type(), key.environment());
var group = (IndirectCullingGroup) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs));
- group.add((IndirectInstancer) instancer, key.model(), key.visualType(), meshPool);
+ group.add((IndirectInstancer) instancer, key, meshPool);
}
public boolean hasVisualType(VisualType visualType) {
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java
index 91b8d782c..d01e916e5 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java
@@ -10,20 +10,26 @@ public class InstancedDraw {
private final InstancedInstancer> instancer;
private final MeshPool.PooledMesh mesh;
private final Material material;
+ private final int bias;
private final int indexOfMeshInModel;
private boolean deleted;
- public InstancedDraw(InstancedInstancer> instancer, MeshPool.PooledMesh mesh, GroupKey> groupKey, Material material, int indexOfMeshInModel) {
+ public InstancedDraw(InstancedInstancer> instancer, MeshPool.PooledMesh mesh, GroupKey> groupKey, Material material, int bias, int indexOfMeshInModel) {
this.instancer = instancer;
this.mesh = mesh;
this.groupKey = groupKey;
this.material = material;
+ this.bias = bias;
this.indexOfMeshInModel = indexOfMeshInModel;
mesh.acquire();
}
+ public int bias() {
+ return bias;
+ }
+
public int indexOfMeshInModel() {
return indexOfMeshInModel;
}
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java
index 549463c37..bc7610871 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java
@@ -142,7 +142,7 @@ public class InstancedDrawManager extends DrawManager> {
var mesh = meshPool.alloc(entry.mesh());
GroupKey> groupKey = new GroupKey<>(key.type(), key.environment());
- InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), i);
+ InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i);
stage.put(groupKey, instancedDraw);
instancer.addDrawCall(instancedDraw);
diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java
index 94dee9230..5e62e9718 100644
--- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java
+++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java
@@ -15,7 +15,8 @@ import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
public class InstancedRenderStage {
- private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::indexOfMeshInModel)
+ private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
+ .thenComparing(InstancedDraw::indexOfMeshInModel)
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
private final Map, DrawGroup> groups = new HashMap<>();