mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-14 06:24:12 +01:00
Biased towards artistic control
- Extend InstancerProvider to allow visuals to bias the render order of their instancers - Keep the old InstancerProvider#instancer method with a bias of 0 - Add an explanation of render order in InstancerProvider
This commit is contained in:
parent
eb2ba12a98
commit
b8f6bf841d
@ -11,10 +11,38 @@ public interface InstancerProvider {
|
|||||||
* <p>Calling this method twice with the same arguments in the
|
* <p>Calling this method twice with the same arguments in the
|
||||||
* same frame will return the same instancer.</p>
|
* same frame will return the same instancer.</p>
|
||||||
*
|
*
|
||||||
* <p>It is not safe to store instancers between frames. Each
|
* <p>It is NOT safe to store instancers between frames. Each
|
||||||
* time you need an instancer, you should call this method.</p>
|
* time you need an instancer, you should call this method.</p>
|
||||||
*
|
*
|
||||||
* @return An instancer for the given instance type rendering the given model.
|
* <h2>Render Order</h2>
|
||||||
|
* <p>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
|
||||||
|
* <h4>Mesh Order</h4>
|
||||||
|
* <br>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.
|
||||||
|
* <h4>Bias Order</h4>
|
||||||
|
* <br>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.</p>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
*/
|
*/
|
||||||
<I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model);
|
<I extends Instance> Instancer<I> instancer(InstanceType<I> 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 <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
|
||||||
|
return instancer(type, model, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@ package dev.engine_room.flywheel.api.model;
|
|||||||
|
|
||||||
import java.util.List;
|
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 org.joml.Vector4fc;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.material.Material;
|
import dev.engine_room.flywheel.api.material.Material;
|
||||||
@ -12,8 +16,8 @@ public interface Model {
|
|||||||
*
|
*
|
||||||
* <p>The contents of the returned list will be queried, but never modified.</p>
|
* <p>The contents of the returned list will be queried, but never modified.</p>
|
||||||
*
|
*
|
||||||
* <p>Meshes will be rendered in the order they appear in this list, though
|
* <p>Meshes will be rendered in the order they appear in this list. See
|
||||||
* no render order guarantees are made for meshes between different models.</p>
|
* {@link InstancerProvider#instancer(InstanceType, Model, int)} for a complete explanation</p>
|
||||||
*
|
*
|
||||||
* @return A list of meshes.
|
* @return A list of meshes.
|
||||||
*/
|
*/
|
||||||
|
@ -36,8 +36,8 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||||||
protected final Queue<UninitializedInstancer<N, ?>> initializationQueue = new ConcurrentLinkedQueue<>();
|
protected final Queue<UninitializedInstancer<N, ?>> initializationQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <I extends Instance> Instancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType) {
|
public <I extends Instance> Instancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
|
||||||
return (Instancer<I>) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType), this::createAndDeferInit);
|
return (Instancer<I>) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType, bias), this::createAndDeferInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush(LightStorage lightStorage) {
|
public void flush(LightStorage lightStorage) {
|
||||||
|
@ -109,8 +109,8 @@ public class EngineImpl implements Engine {
|
|||||||
lightStorage.delete();
|
lightStorage.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType) {
|
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
|
||||||
return drawManager.getInstancer(environment, type, model, visualType);
|
return drawManager.getInstancer(environment, type, model, visualType, bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnvironmentStorage environmentStorage() {
|
public EnvironmentStorage environmentStorage() {
|
||||||
|
@ -7,5 +7,5 @@ import dev.engine_room.flywheel.api.visualization.VisualType;
|
|||||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||||
|
|
||||||
public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model,
|
public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model,
|
||||||
VisualType visualType) {
|
VisualType visualType, int bias) {
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import dev.engine_room.flywheel.backend.engine.embed.GlobalEnvironment;
|
|||||||
|
|
||||||
public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider {
|
public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider {
|
||||||
@Override
|
@Override
|
||||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, int bias) {
|
||||||
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType);
|
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
|||||||
|
|
||||||
instancerProvider = new InstancerProvider() {
|
instancerProvider = new InstancerProvider() {
|
||||||
@Override
|
@Override
|
||||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, int bias) {
|
||||||
// Kinda cursed usage of anonymous classes here, but it does the job.
|
// 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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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.api.visualization.VisualType;
|
||||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
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.MaterialRenderState;
|
||||||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||||
@ -31,6 +32,7 @@ import dev.engine_room.flywheel.lib.math.MoreMath;
|
|||||||
|
|
||||||
public class IndirectCullingGroup<I extends Instance> {
|
public class IndirectCullingGroup<I extends Instance> {
|
||||||
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType)
|
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType)
|
||||||
|
.thenComparing(IndirectDraw::bias)
|
||||||
.thenComparing(IndirectDraw::indexOfMeshInModel)
|
.thenComparing(IndirectDraw::indexOfMeshInModel)
|
||||||
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
|
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
|
||||||
|
|
||||||
@ -174,16 +176,17 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
return multiDraws.containsKey(visualType);
|
return multiDraws.containsKey(visualType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(IndirectInstancer<I> instancer, Model model, VisualType visualType, MeshPool meshPool) {
|
public void add(IndirectInstancer<I> instancer, InstancerKey<I> key, MeshPool meshPool) {
|
||||||
instancer.modelIndex = instancers.size();
|
instancer.modelIndex = instancers.size();
|
||||||
instancers.add(instancer);
|
instancers.add(instancer);
|
||||||
|
|
||||||
List<Model.ConfiguredMesh> meshes = model.meshes();
|
List<Model.ConfiguredMesh> meshes = key.model()
|
||||||
|
.meshes();
|
||||||
for (int i = 0; i < meshes.size(); i++) {
|
for (int i = 0; i < meshes.size(); i++) {
|
||||||
var entry = meshes.get(i);
|
var entry = meshes.get(i);
|
||||||
|
|
||||||
MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh());
|
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);
|
indirectDraws.add(draw);
|
||||||
instancer.addDraw(draw);
|
instancer.addDraw(draw);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ public class IndirectDraw {
|
|||||||
private final Material material;
|
private final Material material;
|
||||||
private final MeshPool.PooledMesh mesh;
|
private final MeshPool.PooledMesh mesh;
|
||||||
private final VisualType visualType;
|
private final VisualType visualType;
|
||||||
|
private final int bias;
|
||||||
private final int indexOfMeshInModel;
|
private final int indexOfMeshInModel;
|
||||||
|
|
||||||
private final int materialVertexIndex;
|
private final int materialVertexIndex;
|
||||||
@ -21,11 +22,12 @@ public class IndirectDraw {
|
|||||||
private final int packedMaterialProperties;
|
private final int packedMaterialProperties;
|
||||||
private boolean deleted;
|
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.instancer = instancer;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.visualType = visualType;
|
this.visualType = visualType;
|
||||||
|
this.bias = bias;
|
||||||
this.indexOfMeshInModel = indexOfMeshInModel;
|
this.indexOfMeshInModel = indexOfMeshInModel;
|
||||||
|
|
||||||
mesh.acquire();
|
mesh.acquire();
|
||||||
@ -52,6 +54,10 @@ public class IndirectDraw {
|
|||||||
return visualType;
|
return visualType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int bias() {
|
||||||
|
return bias;
|
||||||
|
}
|
||||||
|
|
||||||
public int indexOfMeshInModel() {
|
public int indexOfMeshInModel() {
|
||||||
return indexOfMeshInModel;
|
return indexOfMeshInModel;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||||||
protected <I extends Instance> void initialize(InstancerKey<I> key, IndirectInstancer<?> instancer) {
|
protected <I extends Instance> void initialize(InstancerKey<I> key, IndirectInstancer<?> instancer) {
|
||||||
var groupKey = new GroupKey<>(key.type(), key.environment());
|
var groupKey = new GroupKey<>(key.type(), key.environment());
|
||||||
var group = (IndirectCullingGroup<I>) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs));
|
var group = (IndirectCullingGroup<I>) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs));
|
||||||
group.add((IndirectInstancer<I>) instancer, key.model(), key.visualType(), meshPool);
|
group.add((IndirectInstancer<I>) instancer, key, meshPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasVisualType(VisualType visualType) {
|
public boolean hasVisualType(VisualType visualType) {
|
||||||
|
@ -10,20 +10,26 @@ public class InstancedDraw {
|
|||||||
private final InstancedInstancer<?> instancer;
|
private final InstancedInstancer<?> instancer;
|
||||||
private final MeshPool.PooledMesh mesh;
|
private final MeshPool.PooledMesh mesh;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
|
private final int bias;
|
||||||
private final int indexOfMeshInModel;
|
private final int indexOfMeshInModel;
|
||||||
|
|
||||||
private boolean deleted;
|
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.instancer = instancer;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.groupKey = groupKey;
|
this.groupKey = groupKey;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
|
this.bias = bias;
|
||||||
this.indexOfMeshInModel = indexOfMeshInModel;
|
this.indexOfMeshInModel = indexOfMeshInModel;
|
||||||
|
|
||||||
mesh.acquire();
|
mesh.acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int bias() {
|
||||||
|
return bias;
|
||||||
|
}
|
||||||
|
|
||||||
public int indexOfMeshInModel() {
|
public int indexOfMeshInModel() {
|
||||||
return indexOfMeshInModel;
|
return indexOfMeshInModel;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||||||
var mesh = meshPool.alloc(entry.mesh());
|
var mesh = meshPool.alloc(entry.mesh());
|
||||||
|
|
||||||
GroupKey<?> groupKey = new GroupKey<>(key.type(), key.environment());
|
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);
|
stage.put(groupKey, instancedDraw);
|
||||||
instancer.addDrawCall(instancedDraw);
|
instancer.addDrawCall(instancedDraw);
|
||||||
|
@ -15,7 +15,8 @@ import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
|||||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||||
|
|
||||||
public class InstancedRenderStage {
|
public class InstancedRenderStage {
|
||||||
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::indexOfMeshInModel)
|
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
|
||||||
|
.thenComparing(InstancedDraw::indexOfMeshInModel)
|
||||||
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
||||||
|
|
||||||
private final Map<GroupKey<?>, DrawGroup> groups = new HashMap<>();
|
private final Map<GroupKey<?>, DrawGroup> groups = new HashMap<>();
|
||||||
|
Loading…
Reference in New Issue
Block a user