mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-06 04:16:36 +01:00
Spooky crumbling from a distance
- Add crumbling renderer for indirect backend. - As usual, it's ugly. - Submit one indirect draw per crumbling instance. Could do multidraw here, but we'd have to perform the material sort which seems unnecessary.
This commit is contained in:
parent
c79e94cd18
commit
7c0959be9a
8 changed files with 234 additions and 29 deletions
|
@ -0,0 +1,38 @@
|
|||
package com.jozufozu.flywheel.backend.engine;
|
||||
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.material.Transparency;
|
||||
import com.jozufozu.flywheel.api.material.WriteMask;
|
||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.lib.material.CutoutShaders;
|
||||
import com.jozufozu.flywheel.lib.material.FogShaders;
|
||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class CommonCrumbling {
|
||||
public static void applyCrumblingProperties(SimpleMaterial.Builder crumblingMaterial, Material baseMaterial) {
|
||||
crumblingMaterial.copyFrom(baseMaterial)
|
||||
.fog(FogShaders.NONE)
|
||||
.cutout(CutoutShaders.ONE_TENTH)
|
||||
.polygonOffset(true)
|
||||
.transparency(Transparency.CRUMBLING)
|
||||
.writeMask(WriteMask.COLOR)
|
||||
.useOverlay(false)
|
||||
.useLight(false);
|
||||
}
|
||||
|
||||
public static int getDiffuseTexture(Material material) {
|
||||
return Minecraft.getInstance()
|
||||
.getTextureManager()
|
||||
.getTexture(material.texture())
|
||||
.getId();
|
||||
}
|
||||
|
||||
public static void setActiveAndBindForCrumbling(int diffuseTexture) {
|
||||
GlTextureUnit.T1.makeActive();
|
||||
RenderSystem.setShaderTexture(1, diffuseTexture);
|
||||
RenderSystem.bindTexture(diffuseTexture);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,12 @@ public class IndirectBuffers {
|
|||
public static final long DRAW_COMMAND_STRIDE = 40;
|
||||
public static final long DRAW_COMMAND_OFFSET = 0;
|
||||
|
||||
public static final int OBJECT_INDEX = 0;
|
||||
public static final int TARGET_INDEX = 1;
|
||||
public static final int MODEL_INDEX = 2;
|
||||
public static final int DRAW_INDEX = 3;
|
||||
|
||||
|
||||
// Offsets to the 3 segments
|
||||
private static final long HANDLE_OFFSET = 0;
|
||||
private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE;
|
||||
|
@ -106,6 +112,11 @@ public class IndirectBuffers {
|
|||
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
|
||||
}
|
||||
|
||||
public void bindForCrumbling() {
|
||||
final long ptr = multiBindBlock.ptr();
|
||||
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, 3, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
multiBindBlock.free();
|
||||
|
||||
|
|
|
@ -44,11 +44,13 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
private final List<IndirectModel> indirectModels = new ArrayList<>();
|
||||
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
||||
private final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||
private final InstanceType<I> instanceType;
|
||||
private boolean needsDrawBarrier;
|
||||
private boolean hasNewDraws;
|
||||
private int instanceCountThisFrame;
|
||||
|
||||
IndirectCullingGroup(InstanceType<I> instanceType) {
|
||||
this.instanceType = instanceType;
|
||||
var programs = IndirectPrograms.get();
|
||||
cullProgram = programs.getCullingProgram(instanceType);
|
||||
applyProgram = programs.getApplyProgram();
|
||||
|
@ -167,7 +169,9 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
for (Map.Entry<Material, Mesh> entry : model.meshes().entrySet()) {
|
||||
IndirectMeshPool.BufferedMesh bufferedMesh = meshPool.alloc(entry.getValue());
|
||||
indirectDraws.add(new IndirectDraw(indirectModel, entry.getKey(), bufferedMesh, stage));
|
||||
var draw = new IndirectDraw(indirectModel, entry.getKey(), bufferedMesh, stage);
|
||||
indirectDraws.add(draw);
|
||||
instancer.addDraw(draw);
|
||||
}
|
||||
|
||||
hasNewDraws = true;
|
||||
|
@ -193,6 +197,23 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
}
|
||||
|
||||
public void bindForCrumbling() {
|
||||
var program = IndirectPrograms.get()
|
||||
.getIndirectProgram(instanceType, Contexts.CRUMBLING);
|
||||
|
||||
program.bind();
|
||||
|
||||
UniformBuffer.get()
|
||||
.sync();
|
||||
meshPool.bindForDraw();
|
||||
buffers.bindForCrumbling();
|
||||
|
||||
drawBarrier();
|
||||
|
||||
var flwBaseDraw = drawProgram.getUniformLocation("_flw_baseDraw");
|
||||
glUniform1ui(flwBaseDraw, 0);
|
||||
}
|
||||
|
||||
private void drawBarrier() {
|
||||
if (needsDrawBarrier) {
|
||||
glMemoryBarrier(DRAW_BARRIER_BITS);
|
||||
|
|
|
@ -56,4 +56,19 @@ public class IndirectDraw {
|
|||
MemoryUtil.memPutInt(ptr + 32, packedFogAndCutout); // packedFogAndCutout
|
||||
MemoryUtil.memPutInt(ptr + 36, packedMaterialProperties); // packedMaterialProperties
|
||||
}
|
||||
|
||||
public void writeWithOverrides(long ptr, int instanceIndex, Material materialOverride) {
|
||||
MemoryUtil.memPutInt(ptr, mesh.indexCount()); // count
|
||||
MemoryUtil.memPutInt(ptr + 4, 1); // instanceCount - only drawing one instance
|
||||
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
||||
MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex()); // baseVertex
|
||||
MemoryUtil.memPutInt(ptr + 16, model.baseInstance() + instanceIndex); // baseInstance
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 20, model.index); // modelIndex
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 24, ShaderIndices.getVertexShaderIndex(materialOverride.shaders())); // materialVertexIndex
|
||||
MemoryUtil.memPutInt(ptr + 28, ShaderIndices.getFragmentShaderIndex(materialOverride.shaders())); // materialFragmentIndex
|
||||
MemoryUtil.memPutInt(ptr + 32, MaterialEncoder.packFogAndCutout(materialOverride)); // packedFogAndCutout
|
||||
MemoryUtil.memPutInt(ptr + 36, MaterialEncoder.packProperties(materialOverride)); // packedMaterialProperties
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,42 @@
|
|||
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferRange;
|
||||
import static org.lwjgl.opengl.GL40.glDrawElementsIndirect;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.Engine;
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
|
||||
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.util.Pair;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
|
||||
public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>> {
|
||||
private final StagingBuffer stagingBuffer = new StagingBuffer();
|
||||
private final Map<InstanceType<?>, IndirectCullingGroup<?>> cullingGroups = new HashMap<>();
|
||||
private final GlBuffer crumblingDrawBuffer = new GlBuffer();
|
||||
|
||||
@Override
|
||||
protected <I extends Instance> IndirectInstancer<?> create(InstanceType<I> type) {
|
||||
|
@ -71,5 +95,89 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
|
|||
cullingGroups.clear();
|
||||
|
||||
stagingBuffer.delete();
|
||||
|
||||
crumblingDrawBuffer.delete();
|
||||
}
|
||||
|
||||
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||
var byType = doCrumblingSort(crumblingBlocks);
|
||||
|
||||
if (byType.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var crumblingMaterial = SimpleMaterial.builder();
|
||||
|
||||
// Scratch memory for writing draw commands.
|
||||
var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
|
||||
GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle());
|
||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, IndirectBuffers.DRAW_INDEX, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
|
||||
for (var instanceTypeEntry : byType.entrySet()) {
|
||||
var byProgress = instanceTypeEntry.getValue();
|
||||
|
||||
// Set up the crumbling program buffers. Nothing changes here between draws.
|
||||
cullingGroups.get(instanceTypeEntry.getKey())
|
||||
.bindForCrumbling();
|
||||
|
||||
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
||||
for (var instanceHandlePair : progressEntry.getValue()) {
|
||||
IndirectInstancer<?> instancer = instanceHandlePair.first();
|
||||
int instanceIndex = instanceHandlePair.second().index;
|
||||
|
||||
for (IndirectDraw draw : instancer.draws()) {
|
||||
var baseMaterial = draw.material();
|
||||
int diffuseTexture = CommonCrumbling.getDiffuseTexture(baseMaterial);
|
||||
|
||||
// Transform the material to be suited for crumbling.
|
||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, baseMaterial);
|
||||
crumblingMaterial.texture(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
||||
|
||||
// Set up gl state for the draw.
|
||||
MaterialRenderState.setup(crumblingMaterial);
|
||||
CommonCrumbling.setActiveAndBindForCrumbling(diffuseTexture);
|
||||
|
||||
// Upload the draw command.
|
||||
draw.writeWithOverrides(block.ptr(), instanceIndex, crumblingMaterial);
|
||||
crumblingDrawBuffer.upload(block);
|
||||
|
||||
// Submit! Everything is already bound by here.
|
||||
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block.free();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Map<InstanceType<?>, Int2ObjectMap<List<Pair<IndirectInstancer<?>, InstanceHandleImpl>>>> doCrumblingSort(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||
Map<InstanceType<?>, Int2ObjectMap<List<Pair<IndirectInstancer<?>, InstanceHandleImpl>>>> byType = new HashMap<>();
|
||||
for (Engine.CrumblingBlock block : crumblingBlocks) {
|
||||
int progress = block.progress();
|
||||
|
||||
if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Instance instance : block.instances()) {
|
||||
// Filter out instances that weren't created by this engine.
|
||||
// If all is well, we probably shouldn't take the `continue`
|
||||
// branches but better to do checked casts.
|
||||
if (!(instance.handle() instanceof InstanceHandleImpl impl)) {
|
||||
continue;
|
||||
}
|
||||
if (!(impl.instancer instanceof IndirectInstancer<?> instancer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
byType.computeIfAbsent(instancer.type, $ -> new Int2ObjectArrayMap<>())
|
||||
.computeIfAbsent(progress, $ -> new ArrayList<>())
|
||||
.add(Pair.of(instancer, impl));
|
||||
}
|
||||
}
|
||||
return byType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,30 @@ public class IndirectEngine extends AbstractEngine {
|
|||
|
||||
@Override
|
||||
public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
|
||||
// TODO: implement
|
||||
executor.syncUntil(flushFlag::isRaised);
|
||||
|
||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||
int prevActiveTexture = GlStateManager._getActiveTexture();
|
||||
Minecraft.getInstance().gameRenderer.overlayTexture()
|
||||
.setupOverlayColor();
|
||||
Minecraft.getInstance().gameRenderer.lightTexture()
|
||||
.turnOnLightLayer();
|
||||
|
||||
GlTextureUnit.T1.makeActive();
|
||||
RenderSystem.bindTexture(RenderSystem.getShaderTexture(1));
|
||||
GlTextureUnit.T2.makeActive();
|
||||
RenderSystem.bindTexture(RenderSystem.getShaderTexture(2));
|
||||
|
||||
drawManager.renderCrumbling(crumblingBlocks);
|
||||
|
||||
MaterialRenderState.reset();
|
||||
|
||||
Minecraft.getInstance().gameRenderer.overlayTexture()
|
||||
.teardownOverlayColor();
|
||||
Minecraft.getInstance().gameRenderer.lightTexture()
|
||||
.turnOffLightLayer();
|
||||
GlStateManager._activeTexture(prevActiveTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
|
@ -10,6 +13,7 @@ import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
|||
public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||
private final long objectStride;
|
||||
private final InstanceWriter<I> writer;
|
||||
private final List<IndirectDraw> associatedDraws = new ArrayList<>();
|
||||
private int modelIndex;
|
||||
|
||||
private long lastStartPos = -1;
|
||||
|
@ -22,6 +26,14 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
writer = this.type.getWriter();
|
||||
}
|
||||
|
||||
public void addDraw(IndirectDraw draw) {
|
||||
associatedDraws.add(draw);
|
||||
}
|
||||
|
||||
public List<IndirectDraw> draws() {
|
||||
return associatedDraws;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
removeDeletedInstances();
|
||||
}
|
||||
|
|
|
@ -9,24 +9,17 @@ import org.jetbrains.annotations.NotNull;
|
|||
|
||||
import com.jozufozu.flywheel.api.backend.Engine;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.material.Transparency;
|
||||
import com.jozufozu.flywheel.api.material.WriteMask;
|
||||
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||
import com.jozufozu.flywheel.lib.material.CutoutShaders;
|
||||
import com.jozufozu.flywheel.lib.material.FogShaders;
|
||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
|
||||
public class InstancedCrumbling {
|
||||
|
@ -51,16 +44,9 @@ public class InstancedCrumbling {
|
|||
ShaderState shader = shaderStateEntry.getKey();
|
||||
|
||||
var baseMaterial = shader.material();
|
||||
int diffuseTexture = getDiffuseTexture(baseMaterial);
|
||||
int diffuseTexture = CommonCrumbling.getDiffuseTexture(baseMaterial);
|
||||
|
||||
crumblingMaterial.copyFrom(baseMaterial)
|
||||
.fog(FogShaders.NONE)
|
||||
.cutout(CutoutShaders.ONE_TENTH)
|
||||
.polygonOffset(true)
|
||||
.transparency(Transparency.CRUMBLING)
|
||||
.writeMask(WriteMask.COLOR)
|
||||
.useOverlay(false)
|
||||
.useLight(false);
|
||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, baseMaterial);
|
||||
|
||||
var program = InstancingPrograms.get()
|
||||
.get(shader.instanceType(), Contexts.CRUMBLING);
|
||||
|
@ -79,10 +65,7 @@ public class InstancedCrumbling {
|
|||
crumblingMaterial.texture(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
||||
|
||||
MaterialRenderState.setup(crumblingMaterial);
|
||||
|
||||
GlTextureUnit.T1.makeActive();
|
||||
RenderSystem.setShaderTexture(1, diffuseTexture);
|
||||
RenderSystem.bindTexture(diffuseTexture);
|
||||
CommonCrumbling.setActiveAndBindForCrumbling(diffuseTexture);
|
||||
|
||||
drawCalls.forEach(Runnable::run);
|
||||
}
|
||||
|
@ -128,10 +111,4 @@ public class InstancedCrumbling {
|
|||
return out;
|
||||
}
|
||||
|
||||
private static int getDiffuseTexture(Material material) {
|
||||
return Minecraft.getInstance()
|
||||
.getTextureManager()
|
||||
.getTexture(material.texture())
|
||||
.getId();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue