mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-03-04 06:34:40 +01:00
Merge branch '1.20.1/dev' into 1.21.1/dev
# Conflicts: # common/src/api/java/dev/engine_room/flywheel/api/Flywheel.java # common/src/main/java/dev/engine_room/flywheel/impl/event/RenderContextImpl.java # common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java # fabric/src/lib/java/dev/engine_room/flywheel/lib/model/baked/MeshEmitter.java # fabric/src/main/java/dev/engine_room/flywheel/impl/FabricFlwConfig.java # forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java # neoforge/src/main/java/dev/engine_room/flywheel/impl/NeoForgeFlwConfig.java
This commit is contained in:
commit
7adc132a0a
85 changed files with 1628 additions and 1014 deletions
|
@ -1,14 +1,11 @@
|
|||
package dev.engine_room.flywheel.api;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public final class Flywheel {
|
||||
/**
|
||||
* The mod ID and resource namespace of Flywheel.
|
||||
*/
|
||||
public static final String ID = "flywheel";
|
||||
|
||||
private Flywheel() {
|
||||
}
|
||||
|
||||
public static ResourceLocation rl(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(ID, path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,8 @@ import java.util.List;
|
|||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.task.Plan;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.client.Camera;
|
||||
|
@ -22,14 +20,13 @@ public interface Engine {
|
|||
/**
|
||||
* Create a visualization context that will be used to create visuals of the given type.
|
||||
*
|
||||
* @param visualType The type of visual.
|
||||
* @return A new visualization context.
|
||||
*/
|
||||
VisualizationContext createVisualizationContext(VisualType visualType);
|
||||
VisualizationContext createVisualizationContext();
|
||||
|
||||
/**
|
||||
* Create a plan that will start execution after the start of the level render and
|
||||
* finish execution before {@link #setupRender} is called.
|
||||
* finish execution before {@link #render} is called.
|
||||
*
|
||||
* @return A new plan.
|
||||
*/
|
||||
|
@ -60,32 +57,20 @@ public interface Engine {
|
|||
void onLightUpdate(SectionPos sectionPos, LightLayer layer);
|
||||
|
||||
/**
|
||||
* Set up rendering for the current level render.
|
||||
* Render all instances necessary for the given visual type.
|
||||
*
|
||||
* <p>This method is guaranteed to be called after
|
||||
* {@linkplain #createFramePlan() the frame plan} has finished execution and before
|
||||
* {@link #render} and {@link #renderCrumbling} are called. This method is guaranteed to
|
||||
* be called on the render thread.
|
||||
* {@link #renderCrumbling} are called. This method is guaranteed to be called on the render thread.
|
||||
*
|
||||
* @param context The context for the current level render.
|
||||
*/
|
||||
void setupRender(RenderContext context);
|
||||
|
||||
/**
|
||||
* Render all instances necessary for the given visual type.
|
||||
*
|
||||
* <p>This method is guaranteed to be called after {@link #setupRender} for the current
|
||||
* level render. This method is guaranteed to be called on the render thread.
|
||||
*
|
||||
* @param context The context for the current level render.
|
||||
* @param visualType The type of visual.
|
||||
*/
|
||||
void render(RenderContext context, VisualType visualType);
|
||||
void render(RenderContext context);
|
||||
|
||||
/**
|
||||
* Render the given instances as a crumbling overlay.
|
||||
*
|
||||
* <p>This method is guaranteed to be called after {@link #setupRender} for the current
|
||||
* <p>This method is guaranteed to be called after {@link #render} for the current
|
||||
* level render. This method is guaranteed to be called on the render thread.
|
||||
*
|
||||
* @param context The context for the current level render.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package dev.engine_room.flywheel.api;
|
||||
package dev.engine_room.flywheel.api.backend;
|
||||
|
||||
import org.joml.Matrix4fc;
|
||||
|
|
@ -1,10 +1,81 @@
|
|||
package dev.engine_room.flywheel.api.material;
|
||||
|
||||
public enum Transparency {
|
||||
/**
|
||||
* No blending. Used for solid and cutout geometry.
|
||||
*/
|
||||
OPAQUE,
|
||||
|
||||
/**
|
||||
* Additive blending.
|
||||
*
|
||||
* <p>Each fragment blends color and alpha with the following equation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* out = src + dst
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
ADDITIVE,
|
||||
|
||||
/**
|
||||
* Lightning transparency.
|
||||
*
|
||||
* <p>Each fragment blends color and alpha with the following equation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* out = src * alpha_src + dst
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
LIGHTNING,
|
||||
|
||||
/**
|
||||
* Glint transparency. Used for the enchantment effect.
|
||||
*
|
||||
* <p>Each fragment blends with the following equations:
|
||||
* <pre>
|
||||
* {@code
|
||||
* color_out = color_src^2 + color_dst
|
||||
* alpha_out = alpha_dst
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
GLINT,
|
||||
|
||||
/**
|
||||
* Crumbling transparency. Used for the block breaking overlay.
|
||||
*
|
||||
* <p>Each fragment blends with the following equations:
|
||||
* <pre>
|
||||
* {@code
|
||||
* color_out = 2 * color_src * color_dst
|
||||
* alpha_out = alpha_src
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
CRUMBLING,
|
||||
|
||||
/**
|
||||
* Translucent transparency.
|
||||
*
|
||||
* <p>Each fragment blends with the following equations:
|
||||
* <pre>
|
||||
* {@code
|
||||
* color_out = color_src * alpha_src + color_dst * (1 - alpha_src)
|
||||
* alpha_out = alpha_src + alpha_dst * (1 - alpha_src)
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
TRANSLUCENT,
|
||||
|
||||
/**
|
||||
* If supported by the backend, this mode will use OIT that approximates {@code TRANSLUCENT} transparency.
|
||||
*
|
||||
* <p>If a backend does not support OIT, it must treat this the same as {@code TRANSLUCENT}.
|
||||
*
|
||||
* <p>It is recommended to use this option when possible, though for cases where blend modes are used as an
|
||||
* overlay against solid geometry the order dependent modes are preferred.
|
||||
*/
|
||||
ORDER_INDEPENDENT,
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package dev.engine_room.flywheel.api.visualization;
|
||||
|
||||
public enum VisualType {
|
||||
BLOCK_ENTITY,
|
||||
ENTITY,
|
||||
EFFECT;
|
||||
}
|
|
@ -5,7 +5,7 @@ import java.util.SortedSet;
|
|||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.api.internal.FlwApiLink;
|
||||
import dev.engine_room.flywheel.api.visual.Effect;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
|
@ -47,14 +47,31 @@ public interface VisualizationManager {
|
|||
|
||||
@ApiStatus.NonExtendable
|
||||
interface RenderDispatcher {
|
||||
/**
|
||||
* Prepare visuals for render.
|
||||
*
|
||||
* <p>Guaranteed to be called before {@link #afterEntities} and {@link #beforeCrumbling}.
|
||||
* <br>Guaranteed to be called after the render thread has processed all light updates.
|
||||
* <br>The caller is otherwise free to choose an invocation site, but it is recommended to call
|
||||
* this as early as possible to give the VisualizationManager time to process things off-thread.
|
||||
*/
|
||||
void onStartLevelRender(RenderContext ctx);
|
||||
|
||||
void afterBlockEntities(RenderContext ctx);
|
||||
|
||||
/**
|
||||
* Render instances.
|
||||
*
|
||||
* <p>Guaranteed to be called after {@link #onStartLevelRender} and before {@link #beforeCrumbling}.
|
||||
* <br>The caller is otherwise free to choose an invocation site, but it is recommended to call
|
||||
* this between rendering entities and block entities.
|
||||
*/
|
||||
void afterEntities(RenderContext ctx);
|
||||
|
||||
/**
|
||||
* Render crumbling block entities.
|
||||
*
|
||||
* <p>Guaranteed to be called after {@link #onStartLevelRender} and {@link #afterEntities}
|
||||
* @param destructionProgress The destruction progress map from {@link net.minecraft.client.renderer.LevelRenderer LevelRenderer}.
|
||||
*/
|
||||
void beforeCrumbling(RenderContext ctx, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress);
|
||||
|
||||
void afterParticles(RenderContext ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package dev.engine_room.flywheel.backend;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.backend.Backend;
|
||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||
import dev.engine_room.flywheel.backend.engine.EngineImpl;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.IndirectDrawManager;
|
||||
import dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager;
|
||||
import dev.engine_room.flywheel.backend.gl.Driver;
|
||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.lib.backend.SimpleBackend;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import dev.engine_room.flywheel.lib.util.ShadersModHelper;
|
||||
|
||||
public final class Backends {
|
||||
|
@ -19,16 +20,25 @@ public final class Backends {
|
|||
.engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256))
|
||||
.priority(500)
|
||||
.supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse())
|
||||
.register(Flywheel.rl("instancing"));
|
||||
.register(ResourceUtil.rl("instancing"));
|
||||
|
||||
/**
|
||||
* Use Compute shaders to cull instances.
|
||||
*/
|
||||
public static final Backend INDIRECT = SimpleBackend.builder()
|
||||
.engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256))
|
||||
.priority(1000)
|
||||
.priority(() -> {
|
||||
// Read from GlCompat in these provider because loading GlCompat
|
||||
// at the same time the backends are registered causes GlCapabilities to be null.
|
||||
if (GlCompat.DRIVER == Driver.INTEL) {
|
||||
// Intel has very poor performance with indirect rendering, and on top of that has graphics bugs
|
||||
return 1;
|
||||
} else {
|
||||
return 1000;
|
||||
}
|
||||
})
|
||||
.supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse())
|
||||
.register(Flywheel.rl("indirect"));
|
||||
.register(ResourceUtil.rl("indirect"));
|
||||
|
||||
private Backends() {
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ package dev.engine_room.flywheel.backend;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.layout.FloatRepr;
|
||||
import dev.engine_room.flywheel.api.layout.Layout;
|
||||
import dev.engine_room.flywheel.api.layout.LayoutBuilder;
|
||||
import dev.engine_room.flywheel.backend.gl.array.VertexAttribute;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import dev.engine_room.flywheel.lib.vertex.FullVertexView;
|
||||
import dev.engine_room.flywheel.lib.vertex.VertexView;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -24,7 +24,7 @@ public final class InternalVertex {
|
|||
public static final List<VertexAttribute> ATTRIBUTES = LayoutAttributes.attributes(LAYOUT);
|
||||
public static final int STRIDE = LAYOUT.byteSize();
|
||||
|
||||
public static final ResourceLocation LAYOUT_SHADER = Flywheel.rl("internal/vertex_input.vert");
|
||||
public static final ResourceLocation LAYOUT_SHADER = ResourceUtil.rl("internal/vertex_input.vert");
|
||||
|
||||
private InternalVertex() {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package dev.engine_room.flywheel.backend;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
||||
public class NoiseTextures {
|
||||
public static final ResourceLocation NOISE_TEXTURE = ResourceUtil.rl("textures/flywheel/noise/blue.png");
|
||||
|
||||
@UnknownNullability
|
||||
public static DynamicTexture BLUE_NOISE;
|
||||
|
||||
public static void reload(ResourceManager manager) {
|
||||
if (BLUE_NOISE != null) {
|
||||
BLUE_NOISE.close();
|
||||
BLUE_NOISE = null;
|
||||
}
|
||||
var optional = manager.getResource(NOISE_TEXTURE);
|
||||
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (var is = optional.get()
|
||||
.open()) {
|
||||
var image = NativeImage.read(NativeImage.Format.LUMINANCE, is);
|
||||
|
||||
BLUE_NOISE = new DynamicTexture(image);
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
BLUE_NOISE.bind();
|
||||
|
||||
NoiseTextures.BLUE_NOISE.setFilter(true, false);
|
||||
RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_REPEAT);
|
||||
RenderSystem.texParameter(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_REPEAT);
|
||||
|
||||
RenderSystem.bindTexture(0);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,4 +10,8 @@ public class Samplers {
|
|||
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
|
||||
public static final GlTextureUnit LIGHT_LUT = GlTextureUnit.T5;
|
||||
public static final GlTextureUnit LIGHT_SECTIONS = GlTextureUnit.T6;
|
||||
|
||||
public static final GlTextureUnit DEPTH_RANGE = GlTextureUnit.T7;
|
||||
public static final GlTextureUnit COEFFICIENTS = GlTextureUnit.T8;
|
||||
public static final GlTextureUnit NOISE = GlTextureUnit.T9;
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@ import org.slf4j.LoggerFactory;
|
|||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
||||
public final class FlwPrograms {
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(Flywheel.ID + "/backend/shaders");
|
||||
|
||||
private static final ResourceLocation COMPONENTS_HEADER_FRAG = Flywheel.rl("internal/components_header.frag");
|
||||
private static final ResourceLocation COMPONENTS_HEADER_FRAG = ResourceUtil.rl("internal/components_header.frag");
|
||||
|
||||
public static ShaderSources SOURCES;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent;
|
||||
|
@ -25,13 +24,12 @@ import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class IndirectPrograms extends AtomicReferenceCounted {
|
||||
private static final ResourceLocation CULL_SHADER_API_IMPL = Flywheel.rl("internal/indirect/cull_api_impl.glsl");
|
||||
private static final ResourceLocation CULL_SHADER_MAIN = Flywheel.rl("internal/indirect/cull.glsl");
|
||||
private static final ResourceLocation APPLY_SHADER_MAIN = Flywheel.rl("internal/indirect/apply.glsl");
|
||||
private static final ResourceLocation SCATTER_SHADER_MAIN = Flywheel.rl("internal/indirect/scatter.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_FIRST = Flywheel.rl("internal/indirect/downsample_first.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_SECOND = Flywheel.rl("internal/indirect/downsample_second.glsl");
|
||||
public static final List<ResourceLocation> UTIL_SHADERS = List.of(APPLY_SHADER_MAIN, SCATTER_SHADER_MAIN, DOWNSAMPLE_FIRST, DOWNSAMPLE_SECOND);
|
||||
private static final ResourceLocation CULL_SHADER_API_IMPL = ResourceUtil.rl("internal/indirect/cull_api_impl.glsl");
|
||||
private static final ResourceLocation CULL_SHADER_MAIN = ResourceUtil.rl("internal/indirect/cull.glsl");
|
||||
private static final ResourceLocation APPLY_SHADER_MAIN = ResourceUtil.rl("internal/indirect/apply.glsl");
|
||||
private static final ResourceLocation SCATTER_SHADER_MAIN = ResourceUtil.rl("internal/indirect/scatter.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_FIRST = ResourceUtil.rl("internal/indirect/downsample_first.glsl");
|
||||
private static final ResourceLocation DOWNSAMPLE_SECOND = ResourceUtil.rl("internal/indirect/downsample_second.glsl");
|
||||
|
||||
private static final Compile<InstanceType<?>> CULL = new Compile<>();
|
||||
private static final Compile<ResourceLocation> UTIL = new Compile<>();
|
||||
|
@ -45,11 +43,13 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
private final PipelineCompiler pipeline;
|
||||
private final CompilationHarness<InstanceType<?>> culling;
|
||||
private final CompilationHarness<ResourceLocation> utils;
|
||||
private final OitPrograms oitPrograms;
|
||||
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils) {
|
||||
private IndirectPrograms(PipelineCompiler pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils, OitPrograms oitPrograms) {
|
||||
this.pipeline = pipeline;
|
||||
this.culling = culling;
|
||||
this.utils = utils;
|
||||
this.oitPrograms = oitPrograms;
|
||||
}
|
||||
|
||||
private static List<String> getExtensions(GlslVersion glslVersion) {
|
||||
|
@ -59,9 +59,11 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V420) < 0) {
|
||||
extensions.add("GL_ARB_shading_language_420pack");
|
||||
extensions.add("GL_ARB_shader_image_load_store");
|
||||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V430) < 0) {
|
||||
extensions.add("GL_ARB_shader_storage_buffer_object");
|
||||
extensions.add("GL_ARB_shader_image_size");
|
||||
}
|
||||
if (glslVersion.compareTo(GlslVersion.V460) < 0) {
|
||||
extensions.add("GL_ARB_shader_draw_parameters");
|
||||
|
@ -88,8 +90,9 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||
var cullingCompiler = createCullingCompiler(sources);
|
||||
var utilCompiler = createUtilCompiler(sources);
|
||||
var fullscreenCompiler = OitPrograms.createFullscreenCompiler(sources);
|
||||
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler);
|
||||
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler, fullscreenCompiler);
|
||||
|
||||
setInstance(newInstance);
|
||||
}
|
||||
|
@ -148,8 +151,8 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
setInstance(null);
|
||||
}
|
||||
|
||||
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
return pipeline.get(instanceType, contextShader, material);
|
||||
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode oit) {
|
||||
return pipeline.get(instanceType, contextShader, material, oit);
|
||||
}
|
||||
|
||||
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||
|
@ -172,10 +175,15 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
return utils.get(DOWNSAMPLE_SECOND);
|
||||
}
|
||||
|
||||
public OitPrograms oitPrograms() {
|
||||
return oitPrograms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
pipeline.delete();
|
||||
culling.delete();
|
||||
utils.delete();
|
||||
oitPrograms.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,11 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
|
||||
private final PipelineCompiler pipeline;
|
||||
|
||||
private InstancingPrograms(PipelineCompiler pipeline) {
|
||||
private final OitPrograms oitPrograms;
|
||||
|
||||
private InstancingPrograms(PipelineCompiler pipeline, OitPrograms oitPrograms) {
|
||||
this.pipeline = pipeline;
|
||||
this.oitPrograms = oitPrograms;
|
||||
}
|
||||
|
||||
private static List<String> getExtensions(GlslVersion glslVersion) {
|
||||
|
@ -41,7 +44,8 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
|
||||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler);
|
||||
var fullscreen = OitPrograms.createFullscreenCompiler(sources);
|
||||
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler, fullscreen);
|
||||
|
||||
setInstance(newInstance);
|
||||
}
|
||||
|
@ -69,12 +73,17 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
setInstance(null);
|
||||
}
|
||||
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
return pipeline.get(instanceType, contextShader, material);
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material, PipelineCompiler.OitMode mode) {
|
||||
return pipeline.get(instanceType, contextShader, material, mode);
|
||||
}
|
||||
|
||||
public OitPrograms oitPrograms() {
|
||||
return oitPrograms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
pipeline.delete();
|
||||
oitPrograms.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
|
||||
import dev.engine_room.flywheel.backend.compile.core.Compile;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class OitPrograms {
|
||||
private static final ResourceLocation FULLSCREEN = ResourceUtil.rl("internal/fullscreen.vert");
|
||||
static final ResourceLocation OIT_COMPOSITE = ResourceUtil.rl("internal/oit_composite.frag");
|
||||
static final ResourceLocation OIT_DEPTH = ResourceUtil.rl("internal/oit_depth.frag");
|
||||
|
||||
private static final Compile<ResourceLocation> COMPILE = new Compile<>();
|
||||
|
||||
private final CompilationHarness<ResourceLocation> harness;
|
||||
|
||||
public OitPrograms(CompilationHarness<ResourceLocation> harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public static OitPrograms createFullscreenCompiler(ShaderSources sources) {
|
||||
var harness = COMPILE.program()
|
||||
.link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.VERTEX)
|
||||
.nameMapper($ -> "fullscreen/fullscreen")
|
||||
.withResource(FULLSCREEN))
|
||||
.link(COMPILE.shader(GlCompat.MAX_GLSL_VERSION, ShaderType.FRAGMENT)
|
||||
.nameMapper(rl -> "fullscreen/" + ResourceUtil.toDebugFileNameNoExtension(rl))
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0) {
|
||||
// Need to define FMA for the wavelet calculations
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.withResource(s -> s))
|
||||
.postLink((key, program) -> {
|
||||
program.bind();
|
||||
Uniforms.setUniformBlockBindings(program);
|
||||
program.setSamplerBinding("_flw_accumulate", GlTextureUnit.T0);
|
||||
program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE);
|
||||
program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS);
|
||||
|
||||
GlProgram.unbind();
|
||||
})
|
||||
.harness("fullscreen", sources);
|
||||
return new OitPrograms(harness);
|
||||
}
|
||||
|
||||
public GlProgram getOitCompositeProgram() {
|
||||
return harness.get(OitPrograms.OIT_COMPOSITE);
|
||||
}
|
||||
|
||||
public GlProgram getOitDepthProgram() {
|
||||
return harness.get(OitPrograms.OIT_DEPTH);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
harness.delete();
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.material.LightShader;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
|
@ -24,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
|||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.FnSignature;
|
||||
|
@ -40,8 +40,8 @@ public final class PipelineCompiler {
|
|||
private static UberShaderComponent FOG;
|
||||
private static UberShaderComponent CUTOUT;
|
||||
|
||||
private static final ResourceLocation API_IMPL_VERT = Flywheel.rl("internal/api_impl.vert");
|
||||
private static final ResourceLocation API_IMPL_FRAG = Flywheel.rl("internal/api_impl.frag");
|
||||
private static final ResourceLocation API_IMPL_VERT = ResourceUtil.rl("internal/api_impl.vert");
|
||||
private static final ResourceLocation API_IMPL_FRAG = ResourceUtil.rl("internal/api_impl.frag");
|
||||
|
||||
private final CompilationHarness<PipelineProgramKey> harness;
|
||||
|
||||
|
@ -50,7 +50,7 @@ public final class PipelineCompiler {
|
|||
ALL.add(this);
|
||||
}
|
||||
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material) {
|
||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, Material material, OitMode oit) {
|
||||
var light = material.light();
|
||||
var cutout = material.cutout();
|
||||
var shaders = material.shaders();
|
||||
|
@ -66,7 +66,7 @@ public final class PipelineCompiler {
|
|||
MaterialShaderIndices.cutoutSources()
|
||||
.index(cutout.source());
|
||||
|
||||
return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn()));
|
||||
return harness.get(new PipelineProgramKey(instanceType, contextShader, light, shaders, cutout != CutoutShaders.OFF, FrameUniforms.debugOn(), oit));
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
|
@ -96,6 +96,12 @@ public final class PipelineCompiler {
|
|||
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "/" + material + "_" + context + debug;
|
||||
})
|
||||
.requireExtensions(extensions)
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) {
|
||||
// Only define fma if it wouldn't be declared by gpu shader 5
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness()
|
||||
|
@ -128,10 +134,17 @@ public final class PipelineCompiler {
|
|||
.source());
|
||||
var debug = key.debugEnabled() ? "_debug" : "";
|
||||
var cutout = key.useCutout() ? "_cutout" : "";
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug;
|
||||
var oit = key.oit().name;
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + context + cutout + debug + oit;
|
||||
})
|
||||
.requireExtensions(extensions)
|
||||
.enableExtension("GL_ARB_conservative_depth")
|
||||
.onCompile((rl, compilation) -> {
|
||||
if (GlCompat.MAX_GLSL_VERSION.compareTo(GlslVersion.V400) < 0 && !extensions.contains("GL_ARB_gpu_shader5")) {
|
||||
// Only define fma if it wouldn't be declared by gpu shader 5
|
||||
compilation.define("fma(a, b, c) ((a) * (b) + (c))");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.onCompile((key, comp) -> BackendConfig.INSTANCE.lightSmoothness()
|
||||
|
@ -146,6 +159,12 @@ public final class PipelineCompiler {
|
|||
comp.define("_FLW_USE_DISCARD");
|
||||
}
|
||||
})
|
||||
.onCompile((key, comp) -> {
|
||||
if (key.oit() != OitMode.OFF) {
|
||||
comp.define("_FLW_OIT");
|
||||
comp.define(key.oit().define);
|
||||
}
|
||||
})
|
||||
.withResource(API_IMPL_FRAG)
|
||||
.withResource(key -> key.materialShaders()
|
||||
.fragmentSource())
|
||||
|
@ -171,6 +190,9 @@ public final class PipelineCompiler {
|
|||
program.setSamplerBinding("flw_diffuseTex", Samplers.DIFFUSE);
|
||||
program.setSamplerBinding("flw_overlayTex", Samplers.OVERLAY);
|
||||
program.setSamplerBinding("flw_lightTex", Samplers.LIGHT);
|
||||
program.setSamplerBinding("_flw_depthRange", Samplers.DEPTH_RANGE);
|
||||
program.setSamplerBinding("_flw_coefficients", Samplers.COEFFICIENTS);
|
||||
program.setSamplerBinding("_flw_blueNoise", Samplers.NOISE);
|
||||
pipeline.onLink()
|
||||
.accept(program);
|
||||
key.contextShader()
|
||||
|
@ -184,7 +206,7 @@ public final class PipelineCompiler {
|
|||
}
|
||||
|
||||
public static void createFogComponent() {
|
||||
FOG = UberShaderComponent.builder(Flywheel.rl("fog"))
|
||||
FOG = UberShaderComponent.builder(ResourceUtil.rl("fog"))
|
||||
.materialSources(MaterialShaderIndices.fogSources()
|
||||
.all())
|
||||
.adapt(FnSignature.create()
|
||||
|
@ -197,7 +219,7 @@ public final class PipelineCompiler {
|
|||
}
|
||||
|
||||
private static void createCutoutComponent() {
|
||||
CUTOUT = UberShaderComponent.builder(Flywheel.rl("cutout"))
|
||||
CUTOUT = UberShaderComponent.builder(ResourceUtil.rl("cutout"))
|
||||
.materialSources(MaterialShaderIndices.cutoutSources()
|
||||
.all())
|
||||
.adapt(FnSignature.create()
|
||||
|
@ -217,6 +239,23 @@ public final class PipelineCompiler {
|
|||
* @param light The light shader to use.
|
||||
*/
|
||||
public record PipelineProgramKey(InstanceType<?> instanceType, ContextShader contextShader, LightShader light,
|
||||
MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled) {
|
||||
MaterialShaders materialShaders, boolean useCutout, boolean debugEnabled,
|
||||
OitMode oit) {
|
||||
}
|
||||
|
||||
public enum OitMode {
|
||||
OFF("", ""),
|
||||
DEPTH_RANGE("_FLW_DEPTH_RANGE", "_depth_range"),
|
||||
GENERATE_COEFFICIENTS("_FLW_COLLECT_COEFFS", "_generate_coefficients"),
|
||||
EVALUATE("_FLW_EVALUATE", "_resolve"),
|
||||
;
|
||||
|
||||
public final String define;
|
||||
public final String name;
|
||||
|
||||
OitMode(String define, String name) {
|
||||
this.define = define;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.component.BufferTextureInstanceComponent;
|
||||
import dev.engine_room.flywheel.backend.compile.component.SsboInstanceComponent;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class Pipelines {
|
||||
public static final Pipeline INSTANCING = Pipeline.builder()
|
||||
.compilerMarker("instancing")
|
||||
.vertexMain(Flywheel.rl("internal/instancing/main.vert"))
|
||||
.fragmentMain(Flywheel.rl("internal/instancing/main.frag"))
|
||||
.vertexMain(ResourceUtil.rl("internal/instancing/main.vert"))
|
||||
.fragmentMain(ResourceUtil.rl("internal/instancing/main.frag"))
|
||||
.assembler(BufferTextureInstanceComponent::new)
|
||||
.onLink(program -> {
|
||||
program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER);
|
||||
|
@ -20,8 +20,8 @@ public final class Pipelines {
|
|||
|
||||
public static final Pipeline INDIRECT = Pipeline.builder()
|
||||
.compilerMarker("indirect")
|
||||
.vertexMain(Flywheel.rl("internal/indirect/main.vert"))
|
||||
.fragmentMain(Flywheel.rl("internal/indirect/main.frag"))
|
||||
.vertexMain(ResourceUtil.rl("internal/indirect/main.vert"))
|
||||
.fragmentMain(ResourceUtil.rl("internal/indirect/main.frag"))
|
||||
.assembler(SsboInstanceComponent::new)
|
||||
.onLink($ -> {
|
||||
})
|
||||
|
|
|
@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.compile.component;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.layout.Layout;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.FnSignature;
|
||||
|
@ -11,6 +10,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder;
|
|||
import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.GlslStmt;
|
||||
import dev.engine_room.flywheel.lib.math.MoreMath;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
|
||||
private static final String[] SWIZZLE_SELECTORS = { "x", "y", "z", "w" };
|
||||
|
@ -21,7 +21,7 @@ public class BufferTextureInstanceComponent extends InstanceAssemblerComponent {
|
|||
|
||||
@Override
|
||||
public String name() {
|
||||
return Flywheel.rl("buffer_texture_instance_assembler").toString();
|
||||
return ResourceUtil.rl("buffer_texture_instance_assembler").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,12 +3,12 @@ package dev.engine_room.flywheel.backend.compile.component;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.layout.Layout;
|
||||
import dev.engine_room.flywheel.backend.compile.LayoutInterpreter;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public class InstanceStructComponent implements SourceComponent {
|
||||
private static final String STRUCT_NAME = "FlwInstance";
|
||||
|
@ -21,7 +21,7 @@ public class InstanceStructComponent implements SourceComponent {
|
|||
|
||||
@Override
|
||||
public String name() {
|
||||
return Flywheel.rl("instance_struct").toString();
|
||||
return ResourceUtil.rl("instance_struct").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.compile.component;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.layout.Layout;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.BufferBindings;
|
||||
|
@ -12,6 +11,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder;
|
|||
import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.GlslStmt;
|
||||
import dev.engine_room.flywheel.lib.math.MoreMath;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
||||
public SsboInstanceComponent(InstanceType<?> type) {
|
||||
|
@ -20,7 +20,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
|||
|
||||
@Override
|
||||
public String name() {
|
||||
return Flywheel.rl("ssbo_instance_assembler").toString();
|
||||
return ResourceUtil.rl("ssbo_instance_assembler").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,8 +3,8 @@ package dev.engine_room.flywheel.backend.compile.component;
|
|||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class StringSubstitutionComponent implements SourceComponent {
|
||||
private final SourceComponent source;
|
||||
|
@ -42,7 +42,7 @@ public final class StringSubstitutionComponent implements SourceComponent {
|
|||
|
||||
@Override
|
||||
public String name() {
|
||||
return Flywheel.rl("string_substitution").toString() + " / " + source.name();
|
||||
return ResourceUtil.rl("string_substitution").toString() + " / " + source.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceFile;
|
||||
|
@ -19,6 +18,7 @@ import dev.engine_room.flywheel.backend.glsl.generate.GlslBlock;
|
|||
import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.GlslExpr;
|
||||
import dev.engine_room.flywheel.backend.glsl.generate.GlslSwitch;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class UberShaderComponent implements SourceComponent {
|
||||
|
@ -40,7 +40,7 @@ public class UberShaderComponent implements SourceComponent {
|
|||
|
||||
@Override
|
||||
public String name() {
|
||||
return Flywheel.rl("uber_shader").toString() + " / " + name;
|
||||
return ResourceUtil.rl("uber_shader").toString() + " / " + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,18 +8,18 @@ import java.util.regex.Pattern;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceFile;
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceLines;
|
||||
import dev.engine_room.flywheel.backend.glsl.error.ConsoleColors;
|
||||
import dev.engine_room.flywheel.backend.glsl.error.ErrorBuilder;
|
||||
import dev.engine_room.flywheel.backend.glsl.error.ErrorLevel;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import dev.engine_room.flywheel.lib.util.StringUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class FailedCompilation {
|
||||
public static final ResourceLocation GENERATED_SOURCE_NAME = Flywheel.rl("generated_source");
|
||||
public static final ResourceLocation GENERATED_SOURCE_NAME = ResourceUtil.rl("generated_source");
|
||||
private static final Pattern PATTERN_ONE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
|
||||
private static final Pattern PATTERN_TWO = Pattern.compile("(\\w+): (\\d+):(\\d+):(?: '(.+?)' :)?(.*)");
|
||||
private final List<SourceFile> files;
|
||||
|
|
|
@ -12,13 +12,12 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.task.Plan;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.FlwBackend;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||
|
@ -39,12 +38,12 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||
/**
|
||||
* A list of instancers that have not yet been initialized.
|
||||
* <br>
|
||||
* All new instancers land here before having resources allocated in {@link #flush}.
|
||||
* All new instancers land here before having resources allocated in {@link #render}.
|
||||
*/
|
||||
protected final Queue<UninitializedInstancer<N, ?>> initializationQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public <I extends Instance> AbstractInstancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
|
||||
return getInstancer(new InstancerKey<>(environment, type, model, visualType, bias));
|
||||
public <I extends Instance> AbstractInstancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, int bias) {
|
||||
return getInstancer(new InstancerKey<>(environment, type, model, bias));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -57,7 +56,7 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||
return ForEachPlan.of(() -> new ArrayList<>(instancers.values()), AbstractInstancer::parallelUpdate);
|
||||
}
|
||||
|
||||
public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
// Thread safety: flush is called from the render thread after all visual updates have been made,
|
||||
// so there are no:tm: threads we could be racing with.
|
||||
for (var init : initializationQueue) {
|
||||
|
@ -76,8 +75,6 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||
.forEach(AbstractInstancer::clear);
|
||||
}
|
||||
|
||||
public abstract void render(VisualType visualType);
|
||||
|
||||
public abstract void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks);
|
||||
|
||||
protected abstract <I extends Instance> N create(InstancerKey<I> type);
|
||||
|
|
|
@ -4,8 +4,8 @@ import java.util.List;
|
|||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.instance.Instancer;
|
||||
|
@ -13,7 +13,6 @@ import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
|||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.task.Plan;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import dev.engine_room.flywheel.backend.FlwBackend;
|
||||
import dev.engine_room.flywheel.backend.compile.core.ShaderException;
|
||||
|
@ -47,8 +46,8 @@ public class EngineImpl implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public VisualizationContext createVisualizationContext(VisualType visualType) {
|
||||
return new VisualizationContextImpl(visualType);
|
||||
public VisualizationContext createVisualizationContext() {
|
||||
return new VisualizationContextImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,23 +89,13 @@ public class EngineImpl implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setupRender(RenderContext context) {
|
||||
public void render(RenderContext context) {
|
||||
try (var state = GlStateTracker.getRestoreState()) {
|
||||
// Process the render queue for font updates
|
||||
RenderSystem.replayQueue();
|
||||
Uniforms.update(context);
|
||||
environmentStorage.flush();
|
||||
drawManager.flush(lightStorage, environmentStorage);
|
||||
} catch (ShaderException e) {
|
||||
FlwBackend.LOGGER.error("Falling back", e);
|
||||
triggerFallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderContext context, VisualType visualType) {
|
||||
try (var state = GlStateTracker.getRestoreState()) {
|
||||
drawManager.render(visualType);
|
||||
drawManager.render(lightStorage, environmentStorage);
|
||||
} catch (ShaderException e) {
|
||||
FlwBackend.LOGGER.error("Falling back", e);
|
||||
triggerFallback();
|
||||
|
@ -134,8 +123,8 @@ public class EngineImpl implements Engine {
|
|||
drawManager.triggerFallback();
|
||||
}
|
||||
|
||||
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, bias);
|
||||
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, int bias) {
|
||||
return drawManager.getInstancer(environment, type, model, bias);
|
||||
}
|
||||
|
||||
public EnvironmentStorage environmentStorage() {
|
||||
|
@ -148,11 +137,9 @@ public class EngineImpl implements Engine {
|
|||
|
||||
private class VisualizationContextImpl implements VisualizationContext {
|
||||
private final InstancerProviderImpl instancerProvider;
|
||||
private final VisualType visualType;
|
||||
|
||||
public VisualizationContextImpl(VisualType visualType) {
|
||||
instancerProvider = new InstancerProviderImpl(EngineImpl.this, visualType);
|
||||
this.visualType = visualType;
|
||||
public VisualizationContextImpl() {
|
||||
instancerProvider = new InstancerProviderImpl(EngineImpl.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,7 +154,7 @@ public class EngineImpl implements Engine {
|
|||
|
||||
@Override
|
||||
public VisualEmbedding createEmbedding(Vec3i renderOrigin) {
|
||||
var out = new EmbeddedEnvironment(EngineImpl.this, visualType, renderOrigin);
|
||||
var out = new EmbeddedEnvironment(EngineImpl.this, renderOrigin);
|
||||
environmentStorage.track(out);
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ package dev.engine_room.flywheel.backend.engine;
|
|||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model,
|
||||
VisualType visualType, int bias) {
|
||||
public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model, int bias) {
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@ import dev.engine_room.flywheel.api.instance.InstanceType;
|
|||
import dev.engine_room.flywheel.api.instance.Instancer;
|
||||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.GlobalEnvironment;
|
||||
|
||||
public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider {
|
||||
public record InstancerProviderImpl(EngineImpl engine) implements InstancerProvider {
|
||||
@Override
|
||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, int bias) {
|
||||
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias);
|
||||
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, bias);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,17 @@ public final class MaterialRenderState {
|
|||
setupWriteMask(material.writeMask());
|
||||
}
|
||||
|
||||
public static void setupOit(Material material) {
|
||||
setupTexture(material);
|
||||
setupBackfaceCulling(material.backfaceCulling());
|
||||
setupPolygonOffset(material.polygonOffset());
|
||||
setupDepthTest(material.depthTest());
|
||||
|
||||
WriteMask mask = material.writeMask();
|
||||
boolean writeColor = mask.color();
|
||||
RenderSystem.colorMask(writeColor, writeColor, writeColor, writeColor);
|
||||
}
|
||||
|
||||
private static void setupTexture(Material material) {
|
||||
Samplers.DIFFUSE.makeActive();
|
||||
AbstractTexture texture = Minecraft.getInstance()
|
||||
|
|
|
@ -12,7 +12,6 @@ import dev.engine_room.flywheel.api.instance.Instancer;
|
|||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||
import dev.engine_room.flywheel.backend.engine.EngineImpl;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
|
@ -21,7 +20,6 @@ import net.minecraft.core.Vec3i;
|
|||
|
||||
public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
||||
private final EngineImpl engine;
|
||||
private final VisualType visualType;
|
||||
private final Vec3i renderOrigin;
|
||||
@Nullable
|
||||
private final EmbeddedEnvironment parent;
|
||||
|
@ -36,9 +34,8 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
|||
|
||||
private boolean deleted = false;
|
||||
|
||||
public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, Vec3i renderOrigin, @Nullable EmbeddedEnvironment parent) {
|
||||
public EmbeddedEnvironment(EngineImpl engine, Vec3i renderOrigin, @Nullable EmbeddedEnvironment parent) {
|
||||
this.engine = engine;
|
||||
this.visualType = visualType;
|
||||
this.renderOrigin = renderOrigin;
|
||||
this.parent = parent;
|
||||
|
||||
|
@ -46,13 +43,13 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
|||
@Override
|
||||
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.
|
||||
return engine.instancer(EmbeddedEnvironment.this, type, model, visualType, bias);
|
||||
return engine.instancer(EmbeddedEnvironment.this, type, model, bias);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, Vec3i renderOrigin) {
|
||||
this(engine, visualType, renderOrigin, null);
|
||||
public EmbeddedEnvironment(EngineImpl engine, Vec3i renderOrigin) {
|
||||
this(engine, renderOrigin, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,7 +70,7 @@ public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
|||
|
||||
@Override
|
||||
public VisualEmbedding createEmbedding(Vec3i renderOrigin) {
|
||||
var out = new EmbeddedEnvironment(engine, visualType, renderOrigin, this);
|
||||
var out = new EmbeddedEnvironment(engine, renderOrigin, this);
|
||||
engine.environmentStorage()
|
||||
.track(out);
|
||||
return out;
|
||||
|
|
|
@ -8,17 +8,16 @@ import static org.lwjgl.opengl.GL43.glDispatchCompute;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
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.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.InstancerKey;
|
||||
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
||||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
|
@ -28,8 +27,7 @@ import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
|||
import dev.engine_room.flywheel.lib.math.MoreMath;
|
||||
|
||||
public class IndirectCullingGroup<I extends Instance> {
|
||||
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType)
|
||||
.thenComparing(IndirectDraw::isEmbedded)
|
||||
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::isEmbedded)
|
||||
.thenComparing(IndirectDraw::bias)
|
||||
.thenComparing(IndirectDraw::indexOfMeshInModel)
|
||||
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);
|
||||
|
@ -39,7 +37,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
private final IndirectBuffers buffers;
|
||||
private final List<IndirectInstancer<I>> instancers = new ArrayList<>();
|
||||
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
||||
private final Map<VisualType, List<MultiDraw>> multiDraws = new EnumMap<>(VisualType.class);
|
||||
private final List<MultiDraw> multiDraws = new ArrayList<>();
|
||||
private final List<MultiDraw> oitDraws = new ArrayList<>();
|
||||
|
||||
private final IndirectPrograms programs;
|
||||
private final GlProgram cullProgram;
|
||||
|
@ -58,7 +57,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
cullProgram = programs.getCullingProgram(instanceType);
|
||||
}
|
||||
|
||||
public void flushInstancers() {
|
||||
public boolean flushInstancers() {
|
||||
instanceCountThisFrame = 0;
|
||||
int modelIndex = 0;
|
||||
for (var iterator = instancers.iterator(); iterator.hasNext(); ) {
|
||||
|
@ -80,13 +79,17 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
if (indirectDraws.removeIf(IndirectDraw::deleted)) {
|
||||
needsDrawSort = true;
|
||||
}
|
||||
|
||||
var out = indirectDraws.isEmpty();
|
||||
|
||||
if (out) {
|
||||
delete();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public void upload(StagingBuffer stagingBuffer) {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.updateCounts(instanceCountThisFrame, instancers.size(), indirectDraws.size());
|
||||
|
||||
// Upload only instances that have changed.
|
||||
|
@ -108,10 +111,6 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
|
||||
public void dispatchCull() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uniforms.bindAll();
|
||||
cullProgram.bind();
|
||||
|
||||
|
@ -120,24 +119,17 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
|
||||
public void dispatchApply() {
|
||||
if (nothingToDo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.bindForApply();
|
||||
glDispatchCompute(GlCompat.getComputeGroupCount(indirectDraws.size()), 1, 1);
|
||||
}
|
||||
|
||||
private boolean nothingToDo() {
|
||||
return indirectDraws.isEmpty() || instanceCountThisFrame == 0;
|
||||
}
|
||||
|
||||
private boolean nothingToDo(VisualType visualType) {
|
||||
return nothingToDo() || !multiDraws.containsKey(visualType);
|
||||
public boolean hasOitDraws() {
|
||||
return !oitDraws.isEmpty();
|
||||
}
|
||||
|
||||
private void sortDraws() {
|
||||
multiDraws.clear();
|
||||
oitDraws.clear();
|
||||
// sort by visual type, then material
|
||||
indirectDraws.sort(DRAW_COMPARATOR);
|
||||
|
||||
|
@ -146,28 +138,21 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
// if the next draw call has a different VisualType or Material, start a new MultiDraw
|
||||
if (i == indirectDraws.size() - 1 || incompatibleDraws(draw1, indirectDraws.get(i + 1))) {
|
||||
multiDraws.computeIfAbsent(draw1.visualType(), s -> new ArrayList<>())
|
||||
.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1));
|
||||
var dst = draw1.material()
|
||||
.transparency() == Transparency.ORDER_INDEPENDENT ? oitDraws : multiDraws;
|
||||
dst.add(new MultiDraw(draw1.material(), draw1.isEmbedded(), start, i + 1));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean incompatibleDraws(IndirectDraw draw1, IndirectDraw draw2) {
|
||||
if (draw1.visualType() != draw2.visualType()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (draw1.isEmbedded() != draw2.isEmbedded()) {
|
||||
return true;
|
||||
}
|
||||
return !MaterialRenderState.materialEquals(draw1.material(), draw2.material());
|
||||
}
|
||||
|
||||
public boolean hasVisualType(VisualType visualType) {
|
||||
return multiDraws.containsKey(visualType);
|
||||
}
|
||||
|
||||
public void add(IndirectInstancer<I> instancer, InstancerKey<I> key, MeshPool meshPool) {
|
||||
instancer.mapping = buffers.objectStorage.createMapping();
|
||||
instancer.update(instancers.size(), -1);
|
||||
|
@ -180,7 +165,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
var entry = meshes.get(i);
|
||||
|
||||
MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh());
|
||||
var draw = new IndirectDraw(instancer, entry.material(), mesh, key.visualType(), key.bias(), i);
|
||||
var draw = new IndirectDraw(instancer, entry.material(), mesh, key.bias(), i);
|
||||
indirectDraws.add(draw);
|
||||
instancer.addDraw(draw);
|
||||
}
|
||||
|
@ -188,8 +173,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
needsDrawSort = true;
|
||||
}
|
||||
|
||||
public void submit(VisualType visualType) {
|
||||
if (nothingToDo(visualType)) {
|
||||
public void submitSolid() {
|
||||
if (multiDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -199,8 +184,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
GlProgram lastProgram = null;
|
||||
|
||||
for (var multiDraw : multiDraws.get(visualType)) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material);
|
||||
for (var multiDraw : multiDraws) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, PipelineCompiler.OitMode.OFF);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
|
@ -214,8 +199,36 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
}
|
||||
|
||||
public void submitTransparent(PipelineCompiler.OitMode oit) {
|
||||
if (oitDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buffers.bindForDraw();
|
||||
|
||||
drawBarrier();
|
||||
|
||||
GlProgram lastProgram = null;
|
||||
|
||||
for (var multiDraw : oitDraws) {
|
||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material, oit);
|
||||
if (drawProgram != lastProgram) {
|
||||
lastProgram = drawProgram;
|
||||
|
||||
// Don't need to do this unless the program changes.
|
||||
drawProgram.bind();
|
||||
|
||||
drawProgram.setFloat("_flw_blueNoiseFactor", 0.07f);
|
||||
}
|
||||
|
||||
MaterialRenderState.setupOit(multiDraw.material);
|
||||
|
||||
multiDraw.submit(drawProgram);
|
||||
}
|
||||
}
|
||||
|
||||
public void bindForCrumbling(Material material) {
|
||||
var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material);
|
||||
var program = programs.getIndirectProgram(instanceType, ContextShader.CRUMBLING, material, PipelineCompiler.OitMode.OFF);
|
||||
|
||||
program.bind();
|
||||
|
||||
|
@ -273,16 +286,6 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
buffers.delete();
|
||||
}
|
||||
|
||||
public boolean checkEmptyAndDelete() {
|
||||
var out = indirectDraws.isEmpty();
|
||||
|
||||
if (out) {
|
||||
delete();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private record MultiDraw(Material material, boolean embedded, int start, int end) {
|
||||
private void submit(GlProgram drawProgram) {
|
||||
GlCompat.safeMultiDrawElementsIndirect(drawProgram, GL_TRIANGLES, GL_UNSIGNED_INT, this.start, this.end, IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
|
|
|
@ -3,7 +3,6 @@ package dev.engine_room.flywheel.backend.engine.indirect;
|
|||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.engine.MaterialEncoder;
|
||||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment;
|
||||
|
@ -12,7 +11,6 @@ public class IndirectDraw {
|
|||
private final IndirectInstancer<?> instancer;
|
||||
private final Material material;
|
||||
private final MeshPool.PooledMesh mesh;
|
||||
private final VisualType visualType;
|
||||
private final int bias;
|
||||
private final int indexOfMeshInModel;
|
||||
|
||||
|
@ -20,11 +18,10 @@ public class IndirectDraw {
|
|||
private final int packedMaterialProperties;
|
||||
private boolean deleted;
|
||||
|
||||
public IndirectDraw(IndirectInstancer<?> instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int bias, int indexOfMeshInModel) {
|
||||
public IndirectDraw(IndirectInstancer<?> instancer, Material material, MeshPool.PooledMesh mesh, int bias, int indexOfMeshInModel) {
|
||||
this.instancer = instancer;
|
||||
this.material = material;
|
||||
this.mesh = mesh;
|
||||
this.visualType = visualType;
|
||||
this.bias = bias;
|
||||
this.indexOfMeshInModel = indexOfMeshInModel;
|
||||
|
||||
|
@ -50,10 +47,6 @@ public class IndirectDraw {
|
|||
return mesh;
|
||||
}
|
||||
|
||||
public VisualType visualType() {
|
||||
return visualType;
|
||||
}
|
||||
|
||||
public int bias() {
|
||||
return bias;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import java.util.Map;
|
|||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||
|
@ -49,7 +49,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
|
||||
private final DepthPyramid depthPyramid;
|
||||
|
||||
private boolean needsBarrier = false;
|
||||
private final OitFramebuffer oitFramebuffer;
|
||||
|
||||
public IndirectDrawManager(IndirectPrograms programs) {
|
||||
this.programs = programs;
|
||||
|
@ -65,6 +65,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
matrixBuffer = new MatrixBuffer();
|
||||
|
||||
depthPyramid = new DepthPyramid(programs);
|
||||
|
||||
oitFramebuffer = new OitFramebuffer(programs.oitPrograms());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,51 +81,15 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
group.add((IndirectInstancer<I>) instancer, key, meshPool);
|
||||
}
|
||||
|
||||
public boolean hasVisualType(VisualType visualType) {
|
||||
for (var group : cullingGroups.values()) {
|
||||
if (group.hasVisualType(visualType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void render(VisualType visualType) {
|
||||
if (!hasVisualType(visualType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextureBinder.bindLightAndOverlay();
|
||||
|
||||
vertexArray.bindForDraw();
|
||||
lightBuffers.bind();
|
||||
matrixBuffer.bind();
|
||||
Uniforms.bindAll();
|
||||
|
||||
if (needsBarrier) {
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
needsBarrier = false;
|
||||
}
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submit(visualType);
|
||||
}
|
||||
|
||||
MaterialRenderState.reset();
|
||||
TextureBinder.resetLightAndOverlay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
super.flush(lightStorage, environmentStorage);
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.flushInstancers();
|
||||
}
|
||||
public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
super.render(lightStorage, environmentStorage);
|
||||
|
||||
// Flush instance counts, page mappings, and prune empty groups.
|
||||
cullingGroups.values()
|
||||
.removeIf(IndirectCullingGroup::checkEmptyAndDelete);
|
||||
.removeIf(IndirectCullingGroup::flushInstancers);
|
||||
|
||||
// Instancers may have been emptied in the above call, now remove them here.
|
||||
instancers.values()
|
||||
.removeIf(instancer -> instancer.instanceCount() == 0);
|
||||
|
||||
|
@ -131,6 +97,12 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
|
||||
stagingBuffer.reclaim();
|
||||
|
||||
// Genuinely nothing to do, we can just early out.
|
||||
// Still process the mesh pool and reclaim fenced staging regions though.
|
||||
if (cullingGroups.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lightBuffers.flush(stagingBuffer, lightStorage);
|
||||
|
||||
matrixBuffer.flush(stagingBuffer, environmentStorage);
|
||||
|
@ -165,7 +137,59 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
group.dispatchApply();
|
||||
}
|
||||
|
||||
needsBarrier = true;
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
TextureBinder.bindLightAndOverlay();
|
||||
|
||||
vertexArray.bindForDraw();
|
||||
lightBuffers.bind();
|
||||
matrixBuffer.bind();
|
||||
Uniforms.bindAll();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitSolid();
|
||||
}
|
||||
|
||||
// Let's avoid invoking the oit chain if we don't have anything to do
|
||||
boolean useOit = false;
|
||||
for (var group : cullingGroups.values()) {
|
||||
if (group.hasOitDraws()) {
|
||||
useOit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (useOit) {
|
||||
oitFramebuffer.prepare();
|
||||
|
||||
oitFramebuffer.depthRange();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.DEPTH_RANGE);
|
||||
}
|
||||
|
||||
oitFramebuffer.renderTransmittance();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS);
|
||||
}
|
||||
|
||||
oitFramebuffer.renderDepthFromTransmittance();
|
||||
|
||||
// Need to bind this again because we just drew a full screen quad for OIT.
|
||||
vertexArray.bindForDraw();
|
||||
|
||||
oitFramebuffer.accumulate();
|
||||
|
||||
for (var group : cullingGroups.values()) {
|
||||
group.submitTransparent(PipelineCompiler.OitMode.EVALUATE);
|
||||
}
|
||||
|
||||
oitFramebuffer.composite();
|
||||
}
|
||||
|
||||
MaterialRenderState.reset();
|
||||
TextureBinder.resetLightAndOverlay();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -189,6 +213,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
lightBuffers.delete();
|
||||
|
||||
matrixBuffer.delete();
|
||||
|
||||
oitFramebuffer.delete();
|
||||
}
|
||||
|
||||
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||
|
|
|
@ -141,12 +141,8 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
// This is safe because only one bit position changes at a time.
|
||||
parent.fullPages.set(pageNo);
|
||||
}
|
||||
if (isEmpty(currentValue)) {
|
||||
// Value we just saw was zero, so since we added something we are now mergeable!
|
||||
if (isMergeable(newValue)) {
|
||||
parent.mergeablePages.set(pageNo);
|
||||
} else if (Integer.bitCount(currentValue) == 16) {
|
||||
// We just filled the 17th instance, so we are no longer mergeable.
|
||||
parent.mergeablePages.clear(pageNo);
|
||||
}
|
||||
|
||||
parent.instanceCount.incrementAndGet();
|
||||
|
@ -199,11 +195,7 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
|
||||
if (valid.compareAndSet(currentValue, newValue)) {
|
||||
parent.validityChanged.set(pageNo);
|
||||
if (isEmpty(newValue)) {
|
||||
// If we decremented to zero then we're no longer mergeable.
|
||||
parent.mergeablePages.clear(pageNo);
|
||||
} else if (Integer.bitCount(newValue) == 16) {
|
||||
// If we decremented to 16 then we're now mergeable.
|
||||
if (isMergeable(newValue)) {
|
||||
parent.mergeablePages.set(pageNo);
|
||||
}
|
||||
// Set full page last so that other threads don't race to set the other bitsets.
|
||||
|
@ -223,6 +215,13 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
// Fill the holes in this page with instances from the other page.
|
||||
|
||||
int valid = this.valid.get();
|
||||
|
||||
if (isFull(valid)) {
|
||||
// We got filled after being marked mergeable, nothing to do
|
||||
parent.mergeablePages.clear(pageNo);
|
||||
return;
|
||||
}
|
||||
|
||||
int otherValid = other.valid.get();
|
||||
|
||||
for (int i = 0; i < ObjectStorage.PAGE_SIZE; i++) {
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
package dev.engine_room.flywheel.backend.engine.indirect;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL46;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.OitPrograms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||
import dev.engine_room.flywheel.backend.gl.GlTextureUnit;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class OitFramebuffer {
|
||||
public static final float[] CLEAR_TO_ZERO = {0, 0, 0, 0};
|
||||
public static final int[] DEPTH_RANGE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT0};
|
||||
public static final int[] RENDER_TRANSMITTANCE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT1, GL46.GL_COLOR_ATTACHMENT2, GL46.GL_COLOR_ATTACHMENT3, GL46.GL_COLOR_ATTACHMENT4};
|
||||
public static final int[] ACCUMULATE_DRAW_BUFFERS = {GL46.GL_COLOR_ATTACHMENT5};
|
||||
public static final int[] DEPTH_ONLY_DRAW_BUFFERS = {};
|
||||
|
||||
private final OitPrograms programs;
|
||||
private final int vao;
|
||||
|
||||
public int fbo = -1;
|
||||
public int depthBounds = -1;
|
||||
public int coefficients = -1;
|
||||
public int accumulate = -1;
|
||||
|
||||
private int lastWidth = -1;
|
||||
private int lastHeight = -1;
|
||||
|
||||
public OitFramebuffer(OitPrograms programs) {
|
||||
this.programs = programs;
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
vao = GL46.glCreateVertexArrays();
|
||||
} else {
|
||||
vao = GL32.glGenVertexArrays();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the framebuffer.
|
||||
*/
|
||||
public void prepare() {
|
||||
RenderTarget renderTarget;
|
||||
|
||||
if (Minecraft.useShaderTransparency()) {
|
||||
renderTarget = Minecraft.getInstance().levelRenderer.getItemEntityTarget();
|
||||
|
||||
renderTarget.copyDepthFrom(Minecraft.getInstance()
|
||||
.getMainRenderTarget());
|
||||
} else {
|
||||
renderTarget = Minecraft.getInstance()
|
||||
.getMainRenderTarget();
|
||||
}
|
||||
|
||||
maybeResizeFBO(renderTarget.width, renderTarget.height);
|
||||
|
||||
Samplers.COEFFICIENTS.makeActive();
|
||||
// Bind zero to render system to make sure we clear their internal state
|
||||
RenderSystem.bindTexture(0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients);
|
||||
|
||||
Samplers.DEPTH_RANGE.makeActive();
|
||||
RenderSystem.bindTexture(depthBounds);
|
||||
|
||||
Samplers.NOISE.makeActive();
|
||||
NoiseTextures.BLUE_NOISE.bind();
|
||||
|
||||
GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo);
|
||||
GL32.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, renderTarget.getDepthTextureId(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render out the min and max depth per fragment.
|
||||
*/
|
||||
public void depthRange() {
|
||||
// No depth writes, but we'll still use the depth test.
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_MAX);
|
||||
|
||||
var far = Minecraft.getInstance().gameRenderer.getDepthFar();
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_RANGE_DRAW_BUFFERS);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, new float[]{-far, -far, 0, 0});
|
||||
} else {
|
||||
GL32.glDrawBuffers(DEPTH_RANGE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(-far, -far, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the coefficients to the transmittance function.
|
||||
*/
|
||||
public void renderTransmittance() {
|
||||
// No depth writes, but we'll still use the depth test
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_FUNC_ADD);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, RENDER_TRANSMITTANCE_DRAW_BUFFERS);
|
||||
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 1, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 2, CLEAR_TO_ZERO);
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 3, CLEAR_TO_ZERO);
|
||||
} else {
|
||||
GL32.glDrawBuffers(RENDER_TRANSMITTANCE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(0, 0, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If any fragment has its transmittance fall off to zero, search the transmittance
|
||||
* function to determine at what depth that occurs and write out to the depth buffer.
|
||||
*/
|
||||
public void renderDepthFromTransmittance() {
|
||||
// Only write to depth, not color.
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.colorMask(false, false, false, false);
|
||||
RenderSystem.disableBlend();
|
||||
RenderSystem.depthFunc(GL32.GL_ALWAYS);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, DEPTH_ONLY_DRAW_BUFFERS);
|
||||
} else {
|
||||
GL32.glDrawBuffers(DEPTH_ONLY_DRAW_BUFFERS);
|
||||
}
|
||||
|
||||
programs.getOitDepthProgram()
|
||||
.bind();
|
||||
|
||||
drawFullscreenQuad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample the transmittance function and accumulate.
|
||||
*/
|
||||
public void accumulate() {
|
||||
// No depth writes, but we'll still use the depth test
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE);
|
||||
RenderSystem.blendEquation(GL32.GL_FUNC_ADD);
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
GL46.glNamedFramebufferDrawBuffers(fbo, ACCUMULATE_DRAW_BUFFERS);
|
||||
|
||||
GL46.glClearNamedFramebufferfv(fbo, GL46.GL_COLOR, 0, CLEAR_TO_ZERO);
|
||||
} else {
|
||||
GL32.glDrawBuffers(ACCUMULATE_DRAW_BUFFERS);
|
||||
RenderSystem.clearColor(0, 0, 0, 0);
|
||||
RenderSystem.clear(GL32.GL_COLOR_BUFFER_BIT, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composite the accumulated luminance onto the main framebuffer.
|
||||
*/
|
||||
public void composite() {
|
||||
if (Minecraft.useShaderTransparency()) {
|
||||
Minecraft.getInstance().levelRenderer.getItemEntityTarget()
|
||||
.bindWrite(false);
|
||||
} else {
|
||||
Minecraft.getInstance()
|
||||
.getMainRenderTarget()
|
||||
.bindWrite(false);
|
||||
}
|
||||
|
||||
// The composite shader writes out the closest depth to gl_FragDepth.
|
||||
// depthMask = true: OIT stuff renders on top of other transparent stuff.
|
||||
// depthMask = false: other transparent stuff renders on top of OIT stuff.
|
||||
// If Neo gets wavelet OIT we can use their hooks to be correct with everything.
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.colorMask(true, true, true, true);
|
||||
RenderSystem.enableBlend();
|
||||
|
||||
// We rely on the blend func to achieve:
|
||||
// final color = (1 - transmittance_total) * sum(color_f * alpha_f * transmittance_f) / sum(alpha_f * transmittance_f)
|
||||
// + color_dst * transmittance_total
|
||||
//
|
||||
// Though note that the alpha value we emit in the fragment shader is actually (1. - transmittance_total).
|
||||
// The extra inversion step is so we can have a sane alpha value written out for the fabulous blit shader to consume.
|
||||
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
RenderSystem.blendEquation(GL32.GL_FUNC_ADD);
|
||||
RenderSystem.depthFunc(GL32.GL_ALWAYS);
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
RenderSystem.bindTexture(accumulate);
|
||||
|
||||
programs.getOitCompositeProgram()
|
||||
.bind();
|
||||
|
||||
drawFullscreenQuad();
|
||||
|
||||
Minecraft.getInstance()
|
||||
.getMainRenderTarget()
|
||||
.bindWrite(false);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleteTextures();
|
||||
GL32.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
private void drawFullscreenQuad() {
|
||||
// Empty VAO, the actual full screen triangle is generated in the vertex shader
|
||||
GlStateManager._glBindVertexArray(vao);
|
||||
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
private void deleteTextures() {
|
||||
if (depthBounds != -1) {
|
||||
GL32.glDeleteTextures(depthBounds);
|
||||
}
|
||||
if (coefficients != -1) {
|
||||
GL32.glDeleteTextures(coefficients);
|
||||
}
|
||||
if (accumulate != -1) {
|
||||
GL32.glDeleteTextures(accumulate);
|
||||
}
|
||||
if (fbo != -1) {
|
||||
GL32.glDeleteFramebuffers(fbo);
|
||||
}
|
||||
|
||||
// We sometimes get the same texture ID back when creating new textures,
|
||||
// so bind zero to clear the GlStateManager
|
||||
Samplers.COEFFICIENTS.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
Samplers.DEPTH_RANGE.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
}
|
||||
|
||||
private void maybeResizeFBO(int width, int height) {
|
||||
if (lastWidth == width && lastHeight == height) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
|
||||
deleteTextures();
|
||||
|
||||
if (GlCompat.SUPPORTS_DSA) {
|
||||
fbo = GL46.glCreateFramebuffers();
|
||||
|
||||
depthBounds = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
coefficients = GL46.glCreateTextures(GL46.GL_TEXTURE_2D_ARRAY);
|
||||
accumulate = GL46.glCreateTextures(GL46.GL_TEXTURE_2D);
|
||||
|
||||
GL46.glTextureStorage2D(depthBounds, 1, GL32.GL_RG32F, width, height);
|
||||
GL46.glTextureStorage3D(coefficients, 1, GL32.GL_RGBA16F, width, height, 4);
|
||||
GL46.glTextureStorage2D(accumulate, 1, GL32.GL_RGBA16F, width, height);
|
||||
|
||||
GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2);
|
||||
GL46.glNamedFramebufferTextureLayer(fbo, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3);
|
||||
GL46.glNamedFramebufferTexture(fbo, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0);
|
||||
} else {
|
||||
fbo = GL46.glGenFramebuffers();
|
||||
|
||||
depthBounds = GL32.glGenTextures();
|
||||
coefficients = GL32.glGenTextures();
|
||||
accumulate = GL32.glGenTextures();
|
||||
|
||||
GlTextureUnit.T0.makeActive();
|
||||
RenderSystem.bindTexture(0);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, depthBounds);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RG32F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D_ARRAY, coefficients);
|
||||
GL32.glTexImage3D(GL32.GL_TEXTURE_2D_ARRAY, 0, GL32.GL_RGBA16F, width, height, 4, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D_ARRAY, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, accumulate);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16F, width, height, 0, GL46.GL_RGBA, GL46.GL_BYTE, 0);
|
||||
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_EDGE);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GlStateManager._glBindFramebuffer(GL32.GL_FRAMEBUFFER, fbo);
|
||||
|
||||
GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, depthBounds, 0);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, coefficients, 0, 0);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT2, coefficients, 0, 1);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT3, coefficients, 0, 2);
|
||||
GL46.glFramebufferTextureLayer(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT4, coefficients, 0, 3);
|
||||
GL46.glFramebufferTexture(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT5, accumulate, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import org.lwjgl.system.MemoryUtil;
|
|||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlFence;
|
||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBufferUsage;
|
||||
import dev.engine_room.flywheel.lib.memory.FlwMemoryTracker;
|
||||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||
import it.unimi.dsi.fastutil.PriorityQueue;
|
||||
|
@ -22,6 +23,8 @@ public class StagingBuffer {
|
|||
private static final int STORAGE_FLAGS = GL45C.GL_MAP_PERSISTENT_BIT | GL45C.GL_MAP_WRITE_BIT | GL45C.GL_CLIENT_STORAGE_BIT;
|
||||
private static final int MAP_FLAGS = GL45C.GL_MAP_PERSISTENT_BIT | GL45C.GL_MAP_WRITE_BIT | GL45C.GL_MAP_FLUSH_EXPLICIT_BIT | GL45C.GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
|
||||
private static final int SSBO_ALIGNMENT = GL45.glGetInteger(GL45.GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
||||
|
||||
private final int vbo;
|
||||
private final long map;
|
||||
private final long capacity;
|
||||
|
@ -30,7 +33,7 @@ public class StagingBuffer {
|
|||
private final OverflowStagingBuffer overflow = new OverflowStagingBuffer();
|
||||
private final TransferList transfers = new TransferList();
|
||||
private final PriorityQueue<FencedRegion> fencedRegions = new ObjectArrayFIFOQueue<>();
|
||||
private final GlBuffer scatterBuffer = new GlBuffer();
|
||||
private final GlBuffer scatterBuffer = new GlBuffer(GlBufferUsage.STREAM_COPY);
|
||||
private final ScatterList scatterList = new ScatterList();
|
||||
|
||||
/**
|
||||
|
@ -252,7 +255,6 @@ public class StagingBuffer {
|
|||
.bind();
|
||||
|
||||
// These bindings don't change between dstVbos.
|
||||
GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 0, scatterBuffer.handle());
|
||||
GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 1, vbo);
|
||||
|
||||
int dstVbo;
|
||||
|
@ -274,7 +276,25 @@ public class StagingBuffer {
|
|||
}
|
||||
|
||||
private void dispatchScatter(int dstVbo) {
|
||||
scatterBuffer.upload(scatterList.ptr(), scatterList.usedBytes());
|
||||
var scatterSize = scatterList.usedBytes();
|
||||
|
||||
// If there's enough space in the staging buffer still, lets write the scatter in it directly.
|
||||
long alignedPos = pos + SSBO_ALIGNMENT - 1 - (pos + SSBO_ALIGNMENT - 1) % SSBO_ALIGNMENT;
|
||||
|
||||
long remaining = capacity - alignedPos;
|
||||
if (scatterSize <= remaining && scatterSize <= totalAvailable) {
|
||||
MemoryUtil.memCopy(scatterList.ptr(), map + alignedPos, scatterSize);
|
||||
GL45.glBindBufferRange(GL45C.GL_SHADER_STORAGE_BUFFER, 0, vbo, alignedPos, scatterSize);
|
||||
|
||||
long alignmentCost = alignedPos - pos;
|
||||
|
||||
usedCapacity += scatterSize + alignmentCost;
|
||||
totalAvailable -= scatterSize + alignmentCost;
|
||||
pos += scatterSize + alignmentCost;
|
||||
} else {
|
||||
scatterBuffer.upload(scatterList.ptr(), scatterSize);
|
||||
GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 0, scatterBuffer.handle());
|
||||
}
|
||||
|
||||
GL45.glBindBufferBase(GL45C.GL_SHADER_STORAGE_BUFFER, 2, dstVbo);
|
||||
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package dev.engine_room.flywheel.backend.engine.instancing;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||
import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
|
||||
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||
|
@ -22,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
|||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.OitFramebuffer;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
||||
|
@ -31,10 +33,16 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
|
||||
public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
||||
/**
|
||||
* The set of draw calls to make for each {@link VisualType}.
|
||||
*/
|
||||
private final Map<VisualType, InstancedRenderStage> stages = new EnumMap<>(VisualType.class);
|
||||
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
|
||||
.thenComparing(InstancedDraw::indexOfMeshInModel)
|
||||
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
||||
|
||||
private final List<InstancedDraw> allDraws = new ArrayList<>();
|
||||
private boolean needSort = false;
|
||||
|
||||
private final List<InstancedDraw> draws = new ArrayList<>();
|
||||
private final List<InstancedDraw> oitDraws = new ArrayList<>();
|
||||
|
||||
private final InstancingPrograms programs;
|
||||
/**
|
||||
* A map of vertex types to their mesh pools.
|
||||
|
@ -44,6 +52,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
private final TextureBuffer instanceTexture;
|
||||
private final InstancedLight light;
|
||||
|
||||
private final OitFramebuffer oitFramebuffer;
|
||||
|
||||
public InstancedDrawManager(InstancingPrograms programs) {
|
||||
programs.acquire();
|
||||
this.programs = programs;
|
||||
|
@ -54,11 +64,14 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
light = new InstancedLight();
|
||||
|
||||
meshPool.bind(vao);
|
||||
|
||||
oitFramebuffer = new OitFramebuffer(programs.oitPrograms());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
super.flush(lightStorage, environmentStorage);
|
||||
public void render(LightStorage lightStorage, EnvironmentStorage environmentStorage) {
|
||||
super.render(lightStorage, environmentStorage);
|
||||
|
||||
this.instancers.values()
|
||||
.removeIf(instancer -> {
|
||||
|
@ -71,21 +84,32 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
}
|
||||
});
|
||||
|
||||
for (InstancedRenderStage stage : stages.values()) {
|
||||
// Remove the draw calls for any instancers we deleted.
|
||||
stage.flush();
|
||||
needSort |= allDraws.removeIf(InstancedDraw::deleted);
|
||||
|
||||
if (needSort) {
|
||||
allDraws.sort(DRAW_COMPARATOR);
|
||||
|
||||
draws.clear();
|
||||
oitDraws.clear();
|
||||
|
||||
for (var draw : allDraws) {
|
||||
if (draw.material()
|
||||
.transparency() == Transparency.ORDER_INDEPENDENT) {
|
||||
oitDraws.add(draw);
|
||||
} else {
|
||||
draws.add(draw);
|
||||
}
|
||||
}
|
||||
|
||||
needSort = false;
|
||||
}
|
||||
|
||||
meshPool.flush();
|
||||
|
||||
light.flush(lightStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(VisualType visualType) {
|
||||
var stage = stages.get(visualType);
|
||||
|
||||
if (stage == null || stage.isEmpty()) {
|
||||
if (allDraws.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -94,20 +118,92 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
TextureBinder.bindLightAndOverlay();
|
||||
light.bind();
|
||||
|
||||
stage.draw(instanceTexture, programs);
|
||||
submitDraws();
|
||||
|
||||
if (!oitDraws.isEmpty()) {
|
||||
oitFramebuffer.prepare();
|
||||
|
||||
oitFramebuffer.depthRange();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.DEPTH_RANGE);
|
||||
|
||||
oitFramebuffer.renderTransmittance();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.GENERATE_COEFFICIENTS);
|
||||
|
||||
oitFramebuffer.renderDepthFromTransmittance();
|
||||
|
||||
// Need to bind this again because we just drew a full screen quad for OIT.
|
||||
vao.bindForDraw();
|
||||
|
||||
oitFramebuffer.accumulate();
|
||||
|
||||
submitOitDraws(PipelineCompiler.OitMode.EVALUATE);
|
||||
|
||||
oitFramebuffer.composite();
|
||||
}
|
||||
|
||||
MaterialRenderState.reset();
|
||||
TextureBinder.resetLightAndOverlay();
|
||||
}
|
||||
|
||||
private void submitDraws() {
|
||||
for (var drawCall : draws) {
|
||||
var material = drawCall.material();
|
||||
var groupKey = drawCall.groupKey;
|
||||
var environment = groupKey.environment();
|
||||
|
||||
var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, PipelineCompiler.OitMode.OFF);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setup(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
private void submitOitDraws(PipelineCompiler.OitMode mode) {
|
||||
for (var drawCall : oitDraws) {
|
||||
var material = drawCall.material();
|
||||
var groupKey = drawCall.groupKey;
|
||||
var environment = groupKey.environment();
|
||||
|
||||
var program = programs.get(groupKey.instanceType(), environment.contextShader(), material, mode);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setupOit(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
instancers.values()
|
||||
.forEach(InstancedInstancer::delete);
|
||||
|
||||
stages.values()
|
||||
.forEach(InstancedRenderStage::delete);
|
||||
stages.clear();
|
||||
allDraws.forEach(InstancedDraw::delete);
|
||||
allDraws.clear();
|
||||
draws.clear();
|
||||
oitDraws.clear();
|
||||
|
||||
meshPool.delete();
|
||||
instanceTexture.delete();
|
||||
|
@ -116,6 +212,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
|
||||
light.delete();
|
||||
|
||||
oitFramebuffer.delete();
|
||||
|
||||
super.delete();
|
||||
}
|
||||
|
||||
|
@ -128,8 +226,6 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
protected <I extends Instance> void initialize(InstancerKey<I> key, InstancedInstancer<?> instancer) {
|
||||
instancer.init();
|
||||
|
||||
InstancedRenderStage stage = stages.computeIfAbsent(key.visualType(), $ -> new InstancedRenderStage());
|
||||
|
||||
var meshes = key.model()
|
||||
.meshes();
|
||||
for (int i = 0; i < meshes.size(); i++) {
|
||||
|
@ -139,7 +235,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
GroupKey<?> groupKey = new GroupKey<>(key.type(), key.environment());
|
||||
InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i);
|
||||
|
||||
stage.put(groupKey, instancedDraw);
|
||||
allDraws.add(instancedDraw);
|
||||
needSort = true;
|
||||
instancer.addDrawCall(instancedDraw);
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +279,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||
|
||||
for (InstancedDraw draw : instancer.draws()) {
|
||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial);
|
||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial, PipelineCompiler.OitMode.OFF);
|
||||
program.bind();
|
||||
program.setInt("_flw_baseInstance", index);
|
||||
uploadMaterialUniform(program, crumblingMaterial);
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.engine.instancing;
|
||||
|
||||
import static dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager.uploadMaterialUniform;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.engine_room.flywheel.backend.Samplers;
|
||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||
import dev.engine_room.flywheel.backend.engine.GroupKey;
|
||||
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
|
||||
public class InstancedRenderStage {
|
||||
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
|
||||
.thenComparing(InstancedDraw::indexOfMeshInModel)
|
||||
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);
|
||||
|
||||
private final Map<GroupKey<?>, DrawGroup> groups = new HashMap<>();
|
||||
|
||||
public InstancedRenderStage() {
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
groups.values()
|
||||
.forEach(DrawGroup::delete);
|
||||
groups.clear();
|
||||
}
|
||||
|
||||
public void put(GroupKey<?> groupKey, InstancedDraw instancedDraw) {
|
||||
groups.computeIfAbsent(groupKey, $ -> new DrawGroup())
|
||||
.put(instancedDraw);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return groups.isEmpty();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
groups.values()
|
||||
.forEach(DrawGroup::flush);
|
||||
|
||||
groups.values()
|
||||
.removeIf(DrawGroup::isEmpty);
|
||||
}
|
||||
|
||||
public void draw(TextureBuffer instanceTexture, InstancingPrograms programs) {
|
||||
for (var entry : groups.entrySet()) {
|
||||
var shader = entry.getKey();
|
||||
var drawCalls = entry.getValue();
|
||||
|
||||
var environment = shader.environment();
|
||||
|
||||
for (var drawCall : drawCalls.draws) {
|
||||
var material = drawCall.material();
|
||||
|
||||
var program = programs.get(shader.instanceType(), environment.contextShader(), material);
|
||||
program.bind();
|
||||
|
||||
environment.setupDraw(program);
|
||||
|
||||
uploadMaterialUniform(program, material);
|
||||
|
||||
program.setUInt("_flw_vertexOffset", drawCall.mesh()
|
||||
.baseVertex());
|
||||
|
||||
MaterialRenderState.setup(material);
|
||||
|
||||
Samplers.INSTANCE_BUFFER.makeActive();
|
||||
|
||||
drawCall.render(instanceTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrawGroup {
|
||||
private final List<InstancedDraw> draws = new ArrayList<>();
|
||||
private boolean needSort = false;
|
||||
|
||||
public void put(InstancedDraw instancedDraw) {
|
||||
draws.add(instancedDraw);
|
||||
needSort = true;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
draws.forEach(InstancedDraw::delete);
|
||||
draws.clear();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
needSort |= draws.removeIf(InstancedDraw::deleted);
|
||||
|
||||
if (needSort) {
|
||||
draws.sort(DRAW_COMPARATOR);
|
||||
needSort = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return draws.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,13 +6,14 @@ import org.joml.Vector2f;
|
|||
import org.joml.Vector3f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.DepthPyramid;
|
||||
import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -116,6 +117,9 @@ public final class FrameUniforms extends UniformWriter {
|
|||
|
||||
ptr = writeInt(ptr, debugMode);
|
||||
|
||||
// OIT noise factor
|
||||
ptr = writeFloat(ptr, 0.07f);
|
||||
|
||||
firstWrite = false;
|
||||
BUFFER.markDirty();
|
||||
}
|
||||
|
@ -195,7 +199,7 @@ public final class FrameUniforms extends UniformWriter {
|
|||
int pyramidHeight = DepthPyramid.mip0Size(mainRenderTarget.height);
|
||||
int pyramidDepth = DepthPyramid.getImageMipLevels(pyramidWidth, pyramidHeight);
|
||||
|
||||
ptr = writeFloat(ptr, 0.05F); // zNear
|
||||
ptr = writeFloat(ptr, GameRenderer.PROJECTION_Z_NEAR); // zNear
|
||||
ptr = writeFloat(ptr, mc.gameRenderer.getDepthFar()); // zFar
|
||||
ptr = writeFloat(ptr, PROJECTION.m00()); // P00
|
||||
ptr = writeFloat(ptr, PROJECTION.m11()); // P11
|
||||
|
|
|
@ -2,7 +2,7 @@ package dev.engine_room.flywheel.backend.engine.uniform;
|
|||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
|
|
@ -2,7 +2,7 @@ package dev.engine_room.flywheel.backend.engine.uniform;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.backend.FlwBackendXplat;
|
||||
import dev.engine_room.flywheel.backend.mixin.AbstractClientPlayerAccessor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package dev.engine_room.flywheel.backend.engine.uniform;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
public final class Uniforms {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package dev.engine_room.flywheel.backend.gl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
@ -13,6 +11,7 @@ import org.lwjgl.opengl.GL43;
|
|||
import org.lwjgl.opengl.GLCapabilities;
|
||||
import org.lwjgl.opengl.KHRShaderSubgroup;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.backend.FlwBackend;
|
||||
import dev.engine_room.flywheel.backend.compile.core.Compilation;
|
||||
|
@ -40,6 +39,8 @@ public final class GlCompat {
|
|||
public static final boolean ALLOW_DSA = true;
|
||||
public static final GlslVersion MAX_GLSL_VERSION = maxGlslVersion();
|
||||
|
||||
public static final boolean SUPPORTS_DSA = ALLOW_DSA && isDsaSupported();
|
||||
|
||||
public static final boolean SUPPORTS_INSTANCING = isInstancingSupported();
|
||||
public static final boolean SUPPORTS_INDIRECT = isIndirectSupported();
|
||||
|
||||
|
@ -67,10 +68,11 @@ public final class GlCompat {
|
|||
*/
|
||||
public static void safeShaderSource(int glId, CharSequence source) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final ByteBuffer sourceBuffer = stack.UTF8(source, true);
|
||||
var sourceBuffer = MemoryUtil.memUTF8(source, true);
|
||||
final PointerBuffer pointers = stack.mallocPointer(1);
|
||||
pointers.put(sourceBuffer);
|
||||
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
|
||||
MemoryUtil.memFree(sourceBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,8 +163,15 @@ public final class GlCompat {
|
|||
&& CAPABILITIES.GL_ARB_multi_draw_indirect
|
||||
&& CAPABILITIES.GL_ARB_shader_draw_parameters
|
||||
&& CAPABILITIES.GL_ARB_shader_storage_buffer_object
|
||||
&& CAPABILITIES.GL_ARB_shading_language_420pack
|
||||
&& CAPABILITIES.GL_ARB_vertex_attrib_binding;
|
||||
&& CAPABILITIES.GL_ARB_shading_language_420pack && CAPABILITIES.GL_ARB_vertex_attrib_binding && CAPABILITIES.GL_ARB_shader_image_load_store && CAPABILITIES.GL_ARB_shader_image_size;
|
||||
}
|
||||
|
||||
private static boolean isDsaSupported() {
|
||||
if (CAPABILITIES == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CAPABILITIES.GL_ARB_direct_state_access;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
package dev.engine_room.flywheel.backend.glsl;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceLines;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
|
||||
public record Import(Span self, Span file) {
|
|
@ -10,13 +10,8 @@ import java.util.function.Function;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.parse.Import;
|
||||
import dev.engine_room.flywheel.backend.glsl.parse.ShaderField;
|
||||
import dev.engine_room.flywheel.backend.glsl.parse.ShaderFunction;
|
||||
import dev.engine_room.flywheel.backend.glsl.parse.ShaderStruct;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.StringSpan;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
@ -35,39 +30,25 @@ public class SourceFile implements SourceComponent {
|
|||
|
||||
public final SourceLines source;
|
||||
|
||||
/**
|
||||
* Function lookup by name.
|
||||
*/
|
||||
public final ImmutableMap<String, ShaderFunction> functions;
|
||||
|
||||
/**
|
||||
* Struct lookup by name.
|
||||
*/
|
||||
public final ImmutableMap<String, ShaderStruct> structs;
|
||||
|
||||
/**
|
||||
* Includes ordered as defined in the source.
|
||||
*/
|
||||
public final ImmutableList<Import> imports;
|
||||
public final ImmutableMap<String, ShaderField> fields;
|
||||
|
||||
public final List<SourceFile> included;
|
||||
|
||||
public final String finalSource;
|
||||
|
||||
private SourceFile(ResourceLocation name, SourceLines source, ImmutableMap<String, ShaderFunction> functions, ImmutableMap<String, ShaderStruct> structs, ImmutableList<Import> imports, ImmutableMap<String, ShaderField> fields, List<SourceFile> included, String finalSource) {
|
||||
private SourceFile(ResourceLocation name, SourceLines source, ImmutableList<Import> imports, List<SourceFile> included, String finalSource) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
this.functions = functions;
|
||||
this.structs = structs;
|
||||
this.imports = imports;
|
||||
this.fields = fields;
|
||||
this.included = included;
|
||||
this.finalSource = finalSource;
|
||||
}
|
||||
|
||||
public static LoadResult empty(ResourceLocation name) {
|
||||
return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of(), ImmutableMap.of(), ImmutableList.of(), ""));
|
||||
return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableList.of(), ImmutableList.of(), ""));
|
||||
}
|
||||
|
||||
public static LoadResult parse(Function<ResourceLocation, LoadResult> sourceFinder, ResourceLocation name, String stringSource) {
|
||||
|
@ -106,12 +87,8 @@ public class SourceFile implements SourceComponent {
|
|||
return new LoadResult.Failure(new LoadError.IncludeError(name, failures));
|
||||
}
|
||||
|
||||
var functions = ShaderFunction.parseFunctions(source);
|
||||
var structs = ShaderStruct.parseStructs(source);
|
||||
var fields = ShaderField.parseFields(source);
|
||||
|
||||
var finalSource = generateFinalSource(imports, source);
|
||||
return new LoadResult.Success(new SourceFile(name, source, functions, structs, imports, fields, included, finalSource));
|
||||
return new LoadResult.Success(new SourceFile(name, source, imports, included, finalSource));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,13 +106,6 @@ public class SourceFile implements SourceComponent {
|
|||
return name.toString();
|
||||
}
|
||||
|
||||
public Span getLineSpan(int lineNo) {
|
||||
int begin = source.lineStartIndex(lineNo);
|
||||
int end = begin + source.lineString(lineNo)
|
||||
.length();
|
||||
return new StringSpan(source, begin, end);
|
||||
}
|
||||
|
||||
public Span getLineSpanNoWhitespace(int line) {
|
||||
int begin = source.lineStartIndex(line);
|
||||
int end = begin + source.lineString(line)
|
||||
|
@ -166,56 +136,6 @@ public class SourceFile implements SourceComponent {
|
|||
return new StringSpan(source, begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search this file and recursively search all imports to find a struct definition matching the given name.
|
||||
*
|
||||
* @param name The name of the struct to find.
|
||||
* @return null if no definition matches the name.
|
||||
*/
|
||||
@Nullable
|
||||
public ShaderStruct findStruct(String name) {
|
||||
ShaderStruct struct = structs.get(name);
|
||||
|
||||
if (struct != null) {
|
||||
return struct;
|
||||
}
|
||||
|
||||
for (var include : included) {
|
||||
var external = include.findStruct(name);
|
||||
|
||||
if (external != null) {
|
||||
return external;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search this file and recursively search all imports to find a function definition matching the given name.
|
||||
*
|
||||
* @param name The name of the function to find.
|
||||
* @return null if no definition matches the name.
|
||||
*/
|
||||
@Nullable
|
||||
public ShaderFunction findFunction(String name) {
|
||||
ShaderFunction function = functions.get(name);
|
||||
|
||||
if (function != null) {
|
||||
return function;
|
||||
}
|
||||
|
||||
for (var include : included) {
|
||||
var external = include.findFunction(name);
|
||||
|
||||
if (external != null) {
|
||||
return external;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name.toString();
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceLines;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
|
||||
public class ShaderField {
|
||||
public static final Pattern PATTERN = Pattern.compile("layout\\s*\\(location\\s*=\\s*(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)");
|
||||
|
||||
public final Span self;
|
||||
public final Span location;
|
||||
public final Span qualifierSpan;
|
||||
@Nullable
|
||||
public final Qualifier qualifier;
|
||||
public final Span type;
|
||||
public final Span name;
|
||||
|
||||
public ShaderField(Span self, Span location, Span qualifier, Span type, Span name) {
|
||||
this.self = self;
|
||||
this.location = location;
|
||||
this.qualifierSpan = qualifier;
|
||||
this.qualifier = Qualifier.fromSpan(qualifier);
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
public static ImmutableMap<String, ShaderField> parseFields(SourceLines source) {
|
||||
Matcher matcher = PATTERN.matcher(source);
|
||||
|
||||
ImmutableMap.Builder<String, ShaderField> fields = ImmutableMap.builder();
|
||||
while (matcher.find()) {
|
||||
Span self = Span.fromMatcher(source, matcher);
|
||||
Span location = Span.fromMatcher(source, matcher, 1);
|
||||
Span decoration = Span.fromMatcher(source, matcher, 2);
|
||||
Span type = Span.fromMatcher(source, matcher, 3);
|
||||
Span name = Span.fromMatcher(source, matcher, 4);
|
||||
|
||||
fields.put(location.get(), new ShaderField(self, location, decoration, type, name));
|
||||
}
|
||||
|
||||
return fields.build();
|
||||
}
|
||||
|
||||
public enum Qualifier {
|
||||
IN,
|
||||
OUT,
|
||||
FLAT,
|
||||
;
|
||||
|
||||
@Nullable
|
||||
public static Qualifier fromSpan(Span span) {
|
||||
return switch (span.toString()) {
|
||||
case "in" -> IN;
|
||||
case "out" -> OUT;
|
||||
case "flat" -> FLAT;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceLines;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.ErrorSpan;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.StringSpan;
|
||||
|
||||
public class ShaderFunction {
|
||||
// https://regexr.com/60n3d
|
||||
public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
|
||||
public static final Pattern ARGUMENT_PATTERN = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
|
||||
public static final Pattern ASSIGNMENT_PATTERN = Pattern.compile("(\\w+)\\s*=");
|
||||
|
||||
public final Span self;
|
||||
public final Span type;
|
||||
public final Span name;
|
||||
public final Span args;
|
||||
public final Span body;
|
||||
|
||||
public final ImmutableList<ShaderVariable> parameters;
|
||||
|
||||
public ShaderFunction(Span self, Span type, Span name, Span args, Span body) {
|
||||
this.self = self;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.body = body;
|
||||
|
||||
this.parameters = parseArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
public static ImmutableMap<String, ShaderFunction> parseFunctions(SourceLines source) {
|
||||
Matcher matcher = PATTERN.matcher(source);
|
||||
|
||||
Map<String, ShaderFunction> functions = new HashMap<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
Span type = Span.fromMatcher(source, matcher, 1);
|
||||
Span name = Span.fromMatcher(source, matcher, 2);
|
||||
Span args = Span.fromMatcher(source, matcher, 3);
|
||||
|
||||
int blockStart = matcher.end();
|
||||
int blockEnd = findEndOfBlock(source, blockStart);
|
||||
|
||||
Span self;
|
||||
Span body;
|
||||
if (blockEnd > blockStart) {
|
||||
self = new StringSpan(source, matcher.start(), blockEnd + 1);
|
||||
body = new StringSpan(source, blockStart, blockEnd);
|
||||
} else {
|
||||
self = new ErrorSpan(source, matcher.start(), matcher.end());
|
||||
body = new ErrorSpan(source, blockStart);
|
||||
}
|
||||
|
||||
ShaderFunction function = new ShaderFunction(self, type, name, args, body);
|
||||
|
||||
functions.put(name.get(), function);
|
||||
}
|
||||
|
||||
return ImmutableMap.copyOf(functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the position of an opening brace, scans through the source for a paired closing brace.
|
||||
*/
|
||||
private static int findEndOfBlock(CharSequence source, int start) {
|
||||
int blockDepth = 0;
|
||||
for (int i = start + 1; i < source.length(); i++) {
|
||||
char ch = source.charAt(i);
|
||||
|
||||
if (ch == '{') {
|
||||
blockDepth++;
|
||||
} else if (ch == '}') {
|
||||
blockDepth--;
|
||||
}
|
||||
|
||||
if (blockDepth < 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String call(String... args) {
|
||||
return name + "(" + String.join(", ", args) + ")";
|
||||
}
|
||||
|
||||
public Span getParameterType(int index) {
|
||||
return parameters.get(index).type;
|
||||
}
|
||||
|
||||
protected ImmutableList<ShaderVariable> parseArguments() {
|
||||
if (args.isErr() || args.isEmpty()) return ImmutableList.of();
|
||||
|
||||
Matcher arguments = ARGUMENT_PATTERN.matcher(args.get());
|
||||
|
||||
ImmutableList.Builder<ShaderVariable> builder = ImmutableList.builder();
|
||||
|
||||
while (arguments.find()) {
|
||||
Span self = Span.fromMatcher(args, arguments);
|
||||
Span qualifier = Span.fromMatcher(args, arguments, 1);
|
||||
Span type = Span.fromMatcher(args, arguments, 2);
|
||||
Span name = Span.fromMatcher(args, arguments, 3);
|
||||
|
||||
builder.add(new ShaderVariable(self, qualifier, type, name));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String p = parameters.stream()
|
||||
.map(variable -> variable.type)
|
||||
.map(Span::get)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
return type + " " + name + "(" + p + ")";
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.SourceLines;
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
|
||||
public class ShaderStruct {
|
||||
// https://regexr.com/61rpe
|
||||
public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w_]*)\\s*\\{(.*?)}\\s*([\\w_]*)?\\s*;\\s", Pattern.DOTALL);
|
||||
|
||||
public final Span self;
|
||||
public final Span name;
|
||||
public final Span body;
|
||||
public final Span variableName;
|
||||
|
||||
public final ImmutableList<StructField> fields;
|
||||
public final ImmutableMap<String, Span> fields2Types;
|
||||
|
||||
public ShaderStruct(Span self, Span name, Span body, Span variableName) {
|
||||
this.self = self;
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.variableName = variableName;
|
||||
|
||||
this.fields = parseFields();
|
||||
this.fields2Types = createTypeLookup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
|
||||
*/
|
||||
public static ImmutableMap<String, ShaderStruct> parseStructs(SourceLines source) {
|
||||
Matcher matcher = PATTERN.matcher(source);
|
||||
|
||||
ImmutableMap.Builder<String, ShaderStruct> structs = ImmutableMap.builder();
|
||||
while (matcher.find()) {
|
||||
Span self = Span.fromMatcher(source, matcher);
|
||||
Span name = Span.fromMatcher(source, matcher, 1);
|
||||
Span body = Span.fromMatcher(source, matcher, 2);
|
||||
Span variableName = Span.fromMatcher(source, matcher, 3);
|
||||
|
||||
ShaderStruct shaderStruct = new ShaderStruct(self, name, body, variableName);
|
||||
|
||||
structs.put(name.get(), shaderStruct);
|
||||
}
|
||||
|
||||
return structs.build();
|
||||
}
|
||||
|
||||
private ImmutableList<StructField> parseFields() {
|
||||
Matcher matcher = StructField.PATTERN.matcher(body);
|
||||
|
||||
ImmutableList.Builder<StructField> fields = ImmutableList.builder();
|
||||
|
||||
while (matcher.find()) {
|
||||
Span field = Span.fromMatcher(body, matcher);
|
||||
Span type = Span.fromMatcher(body, matcher, 1);
|
||||
Span name = Span.fromMatcher(body, matcher, 2);
|
||||
|
||||
fields.add(new StructField(field, type, name));
|
||||
}
|
||||
|
||||
return fields.build();
|
||||
}
|
||||
|
||||
private ImmutableMap<String, Span> createTypeLookup() {
|
||||
ImmutableMap.Builder<String, Span> lookup = ImmutableMap.builder();
|
||||
for (StructField field : fields) {
|
||||
lookup.put(field.name.get(), field.type);
|
||||
}
|
||||
|
||||
return lookup.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "struct " + name;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
|
||||
public class ShaderVariable {
|
||||
public final Span self;
|
||||
public final Span qualifierSpan;
|
||||
@Nullable
|
||||
public final Qualifier qualifier;
|
||||
public final Span type;
|
||||
public final Span name;
|
||||
|
||||
public ShaderVariable(Span self, Span qualifier, Span type, Span name) {
|
||||
this.self = self;
|
||||
this.qualifierSpan = qualifier;
|
||||
this.qualifier = Qualifier.fromSpan(qualifierSpan);
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + " " + name;
|
||||
}
|
||||
|
||||
public enum Qualifier {
|
||||
NONE,
|
||||
IN,
|
||||
OUT,
|
||||
INOUT;
|
||||
|
||||
@Nullable
|
||||
public static Qualifier fromSpan(Span s) {
|
||||
String span = s.toString();
|
||||
|
||||
return switch (span) {
|
||||
case "" -> NONE;
|
||||
case "in" -> IN;
|
||||
case "inout" -> INOUT;
|
||||
case "out" -> OUT;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.glsl.parse;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.span.Span;
|
||||
|
||||
public class StructField {
|
||||
public static final Pattern PATTERN = Pattern.compile("(\\S+)\\s*(\\S+);");
|
||||
|
||||
public final Span self;
|
||||
public final Span type;
|
||||
public final Span name;
|
||||
|
||||
public StructField(Span self, Span type, Span name) {
|
||||
this.self = self;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + " " + name;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
#include "flywheel:internal/packed_material.glsl"
|
||||
#include "flywheel:internal/diffuse.glsl"
|
||||
#include "flywheel:internal/colorizer.glsl"
|
||||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
|
||||
// optimize discard usage
|
||||
#if defined(GL_ARB_conservative_depth) && defined(_FLW_USE_DISCARD)
|
||||
|
@ -17,8 +19,67 @@ in vec2 _flw_crumblingTexCoord;
|
|||
flat in uvec2 _flw_ids;
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
uniform sampler2D _flw_depthRange;
|
||||
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
uniform sampler2D _flw_blueNoise;
|
||||
|
||||
float tented_blue_noise(float normalizedDepth) {
|
||||
|
||||
float tentIn = abs(normalizedDepth * 2. - 1);
|
||||
float tentIn2 = tentIn * tentIn;
|
||||
float tentIn4 = tentIn2 * tentIn2;
|
||||
float tent = 1 - (tentIn2 * tentIn4);
|
||||
|
||||
float b = texture(_flw_blueNoise, gl_FragCoord.xy / vec2(64)).r;
|
||||
|
||||
return b * tent;
|
||||
}
|
||||
|
||||
float linear_depth() {
|
||||
return linearize_depth(gl_FragCoord.z, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
}
|
||||
|
||||
float depth() {
|
||||
float linearDepth = linear_depth();
|
||||
|
||||
vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg;
|
||||
float delta = depthRange.x + depthRange.y;
|
||||
float depth = (linearDepth + depthRange.x) / delta;
|
||||
|
||||
return depth - tented_blue_noise(depth) * _flw_oitNoise;
|
||||
}
|
||||
|
||||
#ifdef _FLW_DEPTH_RANGE
|
||||
|
||||
out vec2 _flw_depthRange_out;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_COLLECT_COEFFS
|
||||
|
||||
out vec4 _flw_coeffs0;
|
||||
out vec4 _flw_coeffs1;
|
||||
out vec4 _flw_coeffs2;
|
||||
out vec4 _flw_coeffs3;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EVALUATE
|
||||
|
||||
out vec4 _flw_accumulate;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
out vec4 _flw_outputColor;
|
||||
|
||||
#endif
|
||||
|
||||
float _flw_diffuseFactor() {
|
||||
if (flw_material.cardinalLightingMode == 2u) {
|
||||
return diffuseFromLightDirections(flw_vertexNormal);
|
||||
|
@ -99,5 +160,47 @@ void _flw_main() {
|
|||
}
|
||||
#endif
|
||||
|
||||
_flw_outputColor = flw_fogFilter(color);
|
||||
color = flw_fogFilter(color);
|
||||
|
||||
#ifdef _FLW_OIT
|
||||
|
||||
#ifdef _FLW_DEPTH_RANGE
|
||||
float linearDepth = linear_depth();
|
||||
|
||||
// Pad the depth by some unbalanced epsilons because minecraft has a lot of single-quad tranparency.
|
||||
// The unbalance means our fragment will be considered closer to the screen in the normalization,
|
||||
// which helps prevent unnecessary noise as it'll be closer to the edge of our tent function.
|
||||
_flw_depthRange_out = vec2(-linearDepth + 1e-5, linearDepth + 1e-2);
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_COLLECT_COEFFS
|
||||
|
||||
vec4[4] result;
|
||||
result[0] = vec4(0.);
|
||||
result[1] = vec4(0.);
|
||||
result[2] = vec4(0.);
|
||||
result[3] = vec4(0.);
|
||||
|
||||
add_transmittance(result, 1. - color.a, depth());
|
||||
|
||||
_flw_coeffs0 = result[0];
|
||||
_flw_coeffs1 = result[1];
|
||||
_flw_coeffs2 = result[2];
|
||||
_flw_coeffs3 = result[3];
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EVALUATE
|
||||
|
||||
float transmittance = signal_corrected_transmittance(_flw_coefficients, depth(), 1. - color.a);
|
||||
|
||||
_flw_accumulate = vec4(color.rgb * color.a, color.a) * transmittance;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
_flw_outputColor = color;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
float linearize_depth(float d, float zNear, float zFar) {
|
||||
float z_n = 2.0 * d - 1.0;
|
||||
return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
|
||||
}
|
||||
|
||||
float delinearize_depth(float linearDepth, float zNear, float zFar) {
|
||||
float z_n = (2.0 * zNear * zFar / linearDepth) - (zFar + zNear);
|
||||
return 0.5 * (z_n / (zNear - zFar) + 1.0);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
void main() {
|
||||
vec2 vertices[3] = vec2[3](vec2(-1, -1), vec2(3, -1), vec2(-1, 3));
|
||||
gl_Position = vec4(vertices[gl_VertexID], 0, 1);
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
const uint _FLW_BLOCKS_PER_SECTION = 18 * 18 * 18;
|
||||
const uint _FLW_BLOCKS_PER_SECTION = 18u * 18u * 18u;
|
||||
const uint _FLW_LIGHT_SIZE_BYTES = _FLW_BLOCKS_PER_SECTION;
|
||||
const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31) / 32) * 4;
|
||||
const uint _FLW_SOLID_SIZE_BYTES = ((_FLW_BLOCKS_PER_SECTION + 31u) / 32u) * 4u;
|
||||
const uint _FLW_LIGHT_START_BYTES = _FLW_SOLID_SIZE_BYTES;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_BYTES = _FLW_SOLID_SIZE_BYTES + _FLW_LIGHT_SIZE_BYTES;
|
||||
|
||||
const uint _FLW_SOLID_START_INTS = 0;
|
||||
const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4;
|
||||
const uint _FLW_SOLID_START_INTS = 0u;
|
||||
const uint _FLW_LIGHT_START_INTS = _FLW_SOLID_SIZE_BYTES / 4u;
|
||||
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4u;
|
||||
|
||||
const uint _FLW_COMPLETELY_SOLID = 0x7FFFFFFu;
|
||||
const float _FLW_EPSILON = 1e-5;
|
||||
|
@ -29,39 +29,39 @@ bool _flw_nextLut(uint base, int coord, out uint next) {
|
|||
// The base coordinate.
|
||||
int start = int(_flw_indexLut(base));
|
||||
// The width of the coordinate span.
|
||||
uint size = _flw_indexLut(base + 1);
|
||||
uint size = _flw_indexLut(base + 1u);
|
||||
|
||||
// Index of the coordinate in the span.
|
||||
int i = coord - start;
|
||||
|
||||
if (i < 0 || i >= size) {
|
||||
if (i < 0 || i >= int(size)) {
|
||||
// We missed.
|
||||
return true;
|
||||
}
|
||||
|
||||
next = _flw_indexLut(base + 2 + i);
|
||||
next = _flw_indexLut(base + 2u + uint(i));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
|
||||
uint first;
|
||||
if (_flw_nextLut(0, sectionPos.y, first) || first == 0) {
|
||||
if (_flw_nextLut(0u, sectionPos.y, first) || first == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint second;
|
||||
if (_flw_nextLut(first, sectionPos.x, second) || second == 0) {
|
||||
if (_flw_nextLut(first, sectionPos.x, second) || second == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint sectionIndex;
|
||||
if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0) {
|
||||
if (_flw_nextLut(second, sectionPos.z, sectionIndex) || sectionIndex == 0u) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The index is written as 1-based so we can properly detect missing sections.
|
||||
index = sectionIndex - 1;
|
||||
index = sectionIndex - 1u;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ bool _flw_isSolid(uint sectionOffset, uvec3 blockInSectionPos) {
|
|||
|
||||
uint word = _flw_indexLight(sectionOffset + _FLW_SOLID_START_INTS + uintOffset);
|
||||
|
||||
return (word & (1u << bitInWordOffset)) != 0;
|
||||
return (word & (1u << bitInWordOffset)) != 0u;
|
||||
}
|
||||
|
||||
bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
||||
|
@ -98,7 +98,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
|||
// The offset of the section in the light buffer.
|
||||
uint sectionOffset = lightSectionIndex * _FLW_LIGHT_SECTION_SIZE_INTS;
|
||||
|
||||
uvec3 blockInSectionPos = (blockPos & 0xF) + 1;
|
||||
uvec3 blockInSectionPos = uvec3((blockPos & 0xF) + 1);
|
||||
|
||||
lightCoord = vec2(_flw_lightAt(sectionOffset, blockInSectionPos)) * _FLW_LIGHT_NORMALIZER;
|
||||
return true;
|
||||
|
@ -106,7 +106,7 @@ bool flw_lightFetch(ivec3 blockPos, out vec2 lightCoord) {
|
|||
|
||||
|
||||
uint _flw_fetchSolid3x3x3(uint sectionOffset, ivec3 blockInSectionPos) {
|
||||
uint ret = 0;
|
||||
uint ret = 0u;
|
||||
|
||||
// The formatter does NOT like these macros
|
||||
// @formatter:off
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
#include "flywheel:internal/uniforms/frame.glsl"
|
||||
|
||||
out vec4 frag;
|
||||
|
||||
uniform sampler2D _flw_accumulate;
|
||||
uniform sampler2D _flw_depthRange;
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texelFetch(_flw_accumulate, ivec2(gl_FragCoord.xy), 0);
|
||||
|
||||
if (texel.a < 1e-5) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float total_transmittance = total_transmittance(_flw_coefficients);
|
||||
|
||||
frag = vec4(texel.rgb / texel.a, 1. - total_transmittance);
|
||||
|
||||
float minDepth = -texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).r;
|
||||
|
||||
gl_FragDepth = delinearize_depth(minDepth, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#include "flywheel:internal/uniforms/frame.glsl"
|
||||
#include "flywheel:internal/wavelet.glsl"
|
||||
#include "flywheel:internal/depth.glsl"
|
||||
|
||||
uniform sampler2D _flw_depthRange;
|
||||
|
||||
uniform sampler2DArray _flw_coefficients;
|
||||
|
||||
float eye_depth_from_normalized_transparency_depth(float tDepth) {
|
||||
vec2 depthRange = texelFetch(_flw_depthRange, ivec2(gl_FragCoord.xy), 0).rg;
|
||||
|
||||
float delta = depthRange.x + depthRange.y;
|
||||
|
||||
return tDepth * delta - depthRange.x;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float threshold = 0.0001;
|
||||
|
||||
//
|
||||
// If transmittance an infinite depth is above the threshold, it doesn't ever become
|
||||
// zero, so we can bail out.
|
||||
//
|
||||
float transmittance_at_far_depth = total_transmittance(_flw_coefficients);
|
||||
if (transmittance_at_far_depth > threshold) {
|
||||
discard;
|
||||
}
|
||||
|
||||
float normalized_depth_at_zero_transmittance = 1.0;
|
||||
float sample_depth = 0.5;
|
||||
float delta = 0.25;
|
||||
|
||||
//
|
||||
// Quick & Dirty way to binary search through the transmittance function
|
||||
// looking for a value that's below the threshold.
|
||||
//
|
||||
int steps = 6;
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
float transmittance = transmittance(_flw_coefficients, sample_depth);
|
||||
if (transmittance <= threshold) {
|
||||
normalized_depth_at_zero_transmittance = sample_depth;
|
||||
sample_depth -= delta;
|
||||
} else {
|
||||
sample_depth += delta;
|
||||
}
|
||||
delta *= 0.5;
|
||||
}
|
||||
|
||||
//
|
||||
// Searching inside the transparency depth bounds, so have to transform that to
|
||||
// a world-space linear-depth and that into a device depth we can output into
|
||||
// the currently bound depth buffer.
|
||||
//
|
||||
float eyeDepth = eye_depth_from_normalized_transparency_depth(normalized_depth_at_zero_transmittance);
|
||||
gl_FragDepth = delinearize_depth(eyeDepth, _flw_cullData.znear, _flw_cullData.zfar);
|
||||
}
|
|
@ -62,6 +62,8 @@ layout(std140) uniform _FlwFrameUniforms {
|
|||
uint flw_cameraInBlock;
|
||||
|
||||
uint _flw_debugMode;
|
||||
|
||||
float _flw_oitNoise;
|
||||
};
|
||||
|
||||
#define flw_renderOrigin (_flw_renderOrigin.xyz)
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
#define TRANSPARENCY_WAVELET_RANK 3
|
||||
#define TRANSPARENCY_WAVELET_COEFFICIENT_COUNT 16
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// WRITING
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void add_to_index(inout vec4[4] coefficients, int index, float addend) {
|
||||
coefficients[index >> 2][index & 3] = addend;
|
||||
}
|
||||
|
||||
void add_absorbance(inout vec4[4] coefficients, float signal, float depth) {
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
int index = clamp(int(floor(depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
index += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
int new_index = (index - 1) >> 1;
|
||||
float k = float((new_index + 1) & ((1 << power) - 1));
|
||||
|
||||
int wavelet_sign = ((index & 1) << 1) - 1;
|
||||
float wavelet_phase = ((index + 1) & 1) * exp2(-power);
|
||||
float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign, wavelet_phase) * exp2(power * 0.5) * signal;
|
||||
add_to_index(coefficients, new_index, addend);
|
||||
|
||||
index = new_index;
|
||||
}
|
||||
|
||||
float addend = fma(signal, -depth, signal);
|
||||
add_to_index(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1, addend);
|
||||
}
|
||||
|
||||
void add_transmittance(inout vec4[4] coefficients, float transmittance, float depth) {
|
||||
float absorbance = -log(max(transmittance, 0.00001));// transforming the signal from multiplicative transmittance to additive absorbance
|
||||
add_absorbance(coefficients, absorbance, depth);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// READING
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// TODO: maybe we could reduce the number of texel fetches below?
|
||||
float get_coefficients(in sampler2DArray coefficients, int index) {
|
||||
return texelFetch(coefficients, ivec3(gl_FragCoord.xy, index >> 2), 0)[index & 3];
|
||||
}
|
||||
|
||||
/// Compute the total absorbance, as if at infinite depth.
|
||||
float total_absorbance(in sampler2DArray coefficients) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index_b = TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// Compute the absorbance at a given normalized depth.
|
||||
float absorbance(in sampler2DArray coefficients, float depth) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
bool sample_a = index_b >= 1;
|
||||
int index_a = sample_a ? (index_b - 1) : index_b;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
float a = sample_a ? scale_coefficient : 0;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
|
||||
if (sample_a) {
|
||||
int new_index_a = (index_a - 1) >> 1;
|
||||
int wavelet_sign_a = ((index_a & 1) << 1) - 1;
|
||||
float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);
|
||||
a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a;
|
||||
index_a = new_index_a;
|
||||
}
|
||||
}
|
||||
|
||||
float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth);
|
||||
|
||||
return mix(a, b, t);
|
||||
}
|
||||
|
||||
/// Compute the absorbance at a given normalized depth,
|
||||
/// correcting for self-occlusion by undoing the previously recorded absorbance event.
|
||||
float signal_corrected_absorbance(in sampler2DArray coefficients, float depth, float signal) {
|
||||
float scale_coefficient = get_coefficients(coefficients, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
if (scale_coefficient == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
depth *= float(TRANSPARENCY_WAVELET_COEFFICIENT_COUNT-1) / TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
|
||||
float scale_coefficient_addend = fma(signal, -depth, signal);
|
||||
scale_coefficient -= scale_coefficient_addend;
|
||||
|
||||
float coefficient_depth = depth * TRANSPARENCY_WAVELET_COEFFICIENT_COUNT;
|
||||
int index_b = clamp(int(floor(coefficient_depth)), 0, TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1);
|
||||
bool sample_a = index_b >= 1;
|
||||
int index_a = sample_a ? (index_b - 1) : index_b;
|
||||
|
||||
index_b += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
index_a += TRANSPARENCY_WAVELET_COEFFICIENT_COUNT - 1;
|
||||
|
||||
float b = scale_coefficient;
|
||||
float a = sample_a ? scale_coefficient : 0;
|
||||
|
||||
for (int i = 0; i < (TRANSPARENCY_WAVELET_RANK+1); ++i) {
|
||||
int power = TRANSPARENCY_WAVELET_RANK - i;
|
||||
|
||||
int new_index_b = (index_b - 1) >> 1;
|
||||
int wavelet_sign_b = ((index_b & 1) << 1) - 1;
|
||||
float coeff_b = get_coefficients(coefficients, new_index_b);
|
||||
|
||||
float wavelet_phase_b = ((index_b + 1) & 1) * exp2(-power);
|
||||
float k = float((new_index_b + 1) & ((1 << power) - 1));
|
||||
float addend = fma(fma(-exp2(-power), k, depth), wavelet_sign_b, wavelet_phase_b) * exp2(power * 0.5) * signal;
|
||||
coeff_b -= addend;
|
||||
|
||||
b -= exp2(float(power) * 0.5) * coeff_b * wavelet_sign_b;
|
||||
index_b = new_index_b;
|
||||
|
||||
if (sample_a) {
|
||||
int new_index_a = (index_a - 1) >> 1;
|
||||
int wavelet_sign_a = ((index_a & 1) << 1) - 1;
|
||||
float coeff_a = (new_index_a == new_index_b) ? coeff_b : get_coefficients(coefficients, new_index_a);// No addend here on purpose, the original signal didn't contribute to this coefficient
|
||||
a -= exp2(float(power) * 0.5) * coeff_a * wavelet_sign_a;
|
||||
index_a = new_index_a;
|
||||
}
|
||||
}
|
||||
|
||||
float t = coefficient_depth >= TRANSPARENCY_WAVELET_COEFFICIENT_COUNT ? 1.0 : fract(coefficient_depth);
|
||||
|
||||
return mix(a, b, t);
|
||||
}
|
||||
|
||||
// Helpers below to deal directly in transmittance.
|
||||
|
||||
#define ABSORBANCE_TO_TRANSMITTANCE(a) clamp(exp(-(a)), 0., 1.)
|
||||
|
||||
float total_transmittance(in sampler2DArray coefficients) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(total_absorbance(coefficients));
|
||||
}
|
||||
|
||||
float transmittance(in sampler2DArray coefficients, float depth) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(absorbance(coefficients, depth));
|
||||
}
|
||||
|
||||
float signal_corrected_transmittance(in sampler2DArray coefficients, float depth, float signal) {
|
||||
return ABSORBANCE_TO_TRANSMITTANCE(signal_corrected_absorbance(coefficients, depth, signal));
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
|
@ -3,6 +3,9 @@ package dev.engine_room.flywheel.lib.backend;
|
|||
import java.util.Objects;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import dev.engine_room.flywheel.api.backend.Backend;
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
|
@ -11,12 +14,12 @@ import net.minecraft.world.level.LevelAccessor;
|
|||
|
||||
public final class SimpleBackend implements Backend {
|
||||
private final Function<LevelAccessor, Engine> engineFactory;
|
||||
private final int priority;
|
||||
private final IntSupplier priority;
|
||||
private final BooleanSupplier isSupported;
|
||||
|
||||
public SimpleBackend(int priority, Function<LevelAccessor, Engine> engineFactory, BooleanSupplier isSupported) {
|
||||
this.priority = priority;
|
||||
public SimpleBackend(Function<LevelAccessor, Engine> engineFactory, IntSupplier priority, BooleanSupplier isSupported) {
|
||||
this.engineFactory = engineFactory;
|
||||
this.priority = priority;
|
||||
this.isSupported = isSupported;
|
||||
}
|
||||
|
||||
|
@ -31,7 +34,7 @@ public final class SimpleBackend implements Backend {
|
|||
|
||||
@Override
|
||||
public int priority() {
|
||||
return priority;
|
||||
return priority.getAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,8 +43,10 @@ public final class SimpleBackend implements Backend {
|
|||
}
|
||||
|
||||
public static final class Builder {
|
||||
@Nullable
|
||||
private Function<LevelAccessor, Engine> engineFactory;
|
||||
private int priority = 0;
|
||||
private IntSupplier priority = () -> 0;
|
||||
@Nullable
|
||||
private BooleanSupplier isSupported;
|
||||
|
||||
public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) {
|
||||
|
@ -50,6 +55,10 @@ public final class SimpleBackend implements Backend {
|
|||
}
|
||||
|
||||
public Builder priority(int priority) {
|
||||
return priority(() -> priority);
|
||||
}
|
||||
|
||||
public Builder priority(IntSupplier priority) {
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
@ -63,7 +72,7 @@ public final class SimpleBackend implements Backend {
|
|||
Objects.requireNonNull(engineFactory);
|
||||
Objects.requireNonNull(isSupported);
|
||||
|
||||
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(priority, engineFactory, isSupported));
|
||||
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineFactory, priority, isSupported));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ package dev.engine_room.flywheel.lib.instance;
|
|||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||
import dev.engine_room.flywheel.api.layout.FloatRepr;
|
||||
import dev.engine_room.flywheel.api.layout.IntegerRepr;
|
||||
import dev.engine_room.flywheel.api.layout.LayoutBuilder;
|
||||
import dev.engine_room.flywheel.lib.util.ExtraMemoryOps;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class InstanceTypes {
|
||||
public static final InstanceType<TransformedInstance> TRANSFORMED = SimpleInstanceType.builder(TransformedInstance::new)
|
||||
|
@ -26,8 +26,8 @@ public final class InstanceTypes {
|
|||
ExtraMemoryOps.put2x16(ptr + 8, instance.light);
|
||||
ExtraMemoryOps.putMatrix4f(ptr + 12, instance.pose);
|
||||
})
|
||||
.vertexShader(Flywheel.rl("instance/transformed.vert"))
|
||||
.cullShader(Flywheel.rl("instance/cull/transformed.glsl"))
|
||||
.vertexShader(ResourceUtil.rl("instance/transformed.vert"))
|
||||
.cullShader(ResourceUtil.rl("instance/cull/transformed.glsl"))
|
||||
.build();
|
||||
|
||||
public static final InstanceType<PosedInstance> POSED = SimpleInstanceType.builder(PosedInstance::new)
|
||||
|
@ -48,8 +48,8 @@ public final class InstanceTypes {
|
|||
ExtraMemoryOps.putMatrix4f(ptr + 12, instance.pose);
|
||||
ExtraMemoryOps.putMatrix3f(ptr + 76, instance.normal);
|
||||
})
|
||||
.vertexShader(Flywheel.rl("instance/posed.vert"))
|
||||
.cullShader(Flywheel.rl("instance/cull/posed.glsl"))
|
||||
.vertexShader(ResourceUtil.rl("instance/posed.vert"))
|
||||
.cullShader(ResourceUtil.rl("instance/cull/posed.glsl"))
|
||||
.build();
|
||||
|
||||
public static final InstanceType<OrientedInstance> ORIENTED = SimpleInstanceType.builder(OrientedInstance::new)
|
||||
|
@ -76,8 +76,8 @@ public final class InstanceTypes {
|
|||
MemoryUtil.memPutFloat(ptr + 32, instance.pivotZ);
|
||||
ExtraMemoryOps.putQuaternionf(ptr + 36, instance.rotation);
|
||||
})
|
||||
.vertexShader(Flywheel.rl("instance/oriented.vert"))
|
||||
.cullShader(Flywheel.rl("instance/cull/oriented.glsl"))
|
||||
.vertexShader(ResourceUtil.rl("instance/oriented.vert"))
|
||||
.cullShader(ResourceUtil.rl("instance/cull/oriented.glsl"))
|
||||
.build();
|
||||
|
||||
public static final InstanceType<ShadowInstance> SHADOW = SimpleInstanceType.builder(ShadowInstance::new)
|
||||
|
@ -99,8 +99,8 @@ public final class InstanceTypes {
|
|||
MemoryUtil.memPutFloat(ptr + 28, instance.alpha);
|
||||
MemoryUtil.memPutFloat(ptr + 32, instance.radius);
|
||||
})
|
||||
.vertexShader(Flywheel.rl("instance/shadow.vert"))
|
||||
.cullShader(Flywheel.rl("instance/cull/shadow.glsl"))
|
||||
.vertexShader(ResourceUtil.rl("instance/shadow.vert"))
|
||||
.cullShader(ResourceUtil.rl("instance/cull/shadow.glsl"))
|
||||
.build();
|
||||
|
||||
private InstanceTypes() {
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
package dev.engine_room.flywheel.lib.material;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.material.CutoutShader;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class CutoutShaders {
|
||||
/**
|
||||
* Do not discard any fragments based on alpha.
|
||||
*/
|
||||
public static final CutoutShader OFF = new SimpleCutoutShader(Flywheel.rl("cutout/off.glsl"));
|
||||
public static final CutoutShader OFF = new SimpleCutoutShader(ResourceUtil.rl("cutout/off.glsl"));
|
||||
/**
|
||||
* Discard fragments with alpha close to or equal to zero.
|
||||
*/
|
||||
public static final CutoutShader EPSILON = new SimpleCutoutShader(Flywheel.rl("cutout/epsilon.glsl"));
|
||||
public static final CutoutShader EPSILON = new SimpleCutoutShader(ResourceUtil.rl("cutout/epsilon.glsl"));
|
||||
/**
|
||||
* Discard fragments with alpha less than to 0.1.
|
||||
*/
|
||||
public static final CutoutShader ONE_TENTH = new SimpleCutoutShader(Flywheel.rl("cutout/one_tenth.glsl"));
|
||||
public static final CutoutShader ONE_TENTH = new SimpleCutoutShader(ResourceUtil.rl("cutout/one_tenth.glsl"));
|
||||
/**
|
||||
* Discard fragments with alpha less than to 0.5.
|
||||
*/
|
||||
public static final CutoutShader HALF = new SimpleCutoutShader(Flywheel.rl("cutout/half.glsl"));
|
||||
public static final CutoutShader HALF = new SimpleCutoutShader(ResourceUtil.rl("cutout/half.glsl"));
|
||||
|
||||
private CutoutShaders() {
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package dev.engine_room.flywheel.lib.material;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.material.FogShader;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class FogShaders {
|
||||
public static final FogShader NONE = new SimpleFogShader(Flywheel.rl("fog/none.glsl"));
|
||||
public static final FogShader LINEAR = new SimpleFogShader(Flywheel.rl("fog/linear.glsl"));
|
||||
public static final FogShader LINEAR_FADE = new SimpleFogShader(Flywheel.rl("fog/linear_fade.glsl"));
|
||||
public static final FogShader NONE = new SimpleFogShader(ResourceUtil.rl("fog/none.glsl"));
|
||||
public static final FogShader LINEAR = new SimpleFogShader(ResourceUtil.rl("fog/linear.glsl"));
|
||||
public static final FogShader LINEAR_FADE = new SimpleFogShader(ResourceUtil.rl("fog/linear_fade.glsl"));
|
||||
|
||||
private FogShaders() {
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package dev.engine_room.flywheel.lib.material;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.material.LightShader;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class LightShaders {
|
||||
public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(Flywheel.rl("light/smooth_when_embedded.glsl"));
|
||||
public static final LightShader SMOOTH = new SimpleLightShader(Flywheel.rl("light/smooth.glsl"));
|
||||
public static final LightShader FLAT = new SimpleLightShader(Flywheel.rl("light/flat.glsl"));
|
||||
public static final LightShader SMOOTH_WHEN_EMBEDDED = new SimpleLightShader(ResourceUtil.rl("light/smooth_when_embedded.glsl"));
|
||||
public static final LightShader SMOOTH = new SimpleLightShader(ResourceUtil.rl("light/smooth.glsl"));
|
||||
public static final LightShader FLAT = new SimpleLightShader(ResourceUtil.rl("light/flat.glsl"));
|
||||
|
||||
private LightShaders() {
|
||||
}
|
||||
|
|
|
@ -32,20 +32,20 @@ public final class Materials {
|
|||
.build();
|
||||
|
||||
public static final Material TRANSLUCENT_BLOCK = SimpleMaterial.builder()
|
||||
.transparency(Transparency.TRANSLUCENT)
|
||||
.transparency(Transparency.ORDER_INDEPENDENT)
|
||||
.build();
|
||||
public static final Material TRANSLUCENT_UNSHADED_BLOCK = SimpleMaterial.builder()
|
||||
.transparency(Transparency.TRANSLUCENT)
|
||||
.transparency(Transparency.ORDER_INDEPENDENT)
|
||||
.diffuse(false)
|
||||
.build();
|
||||
|
||||
public static final Material TRIPWIRE_BLOCK = SimpleMaterial.builder()
|
||||
.cutout(CutoutShaders.ONE_TENTH)
|
||||
.transparency(Transparency.TRANSLUCENT)
|
||||
.transparency(Transparency.ORDER_INDEPENDENT)
|
||||
.build();
|
||||
public static final Material TRIPWIRE_UNSHADED_BLOCK = SimpleMaterial.builder()
|
||||
.cutout(CutoutShaders.ONE_TENTH)
|
||||
.transparency(Transparency.TRANSLUCENT)
|
||||
.transparency(Transparency.ORDER_INDEPENDENT)
|
||||
.diffuse(false)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package dev.engine_room.flywheel.lib.material;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.material.MaterialShaders;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
|
||||
public final class StandardMaterialShaders {
|
||||
public static final MaterialShaders DEFAULT = new SimpleMaterialShaders(
|
||||
Flywheel.rl("material/default.vert"), Flywheel.rl("material/default.frag"));
|
||||
ResourceUtil.rl("material/default.vert"), ResourceUtil.rl("material/default.frag"));
|
||||
|
||||
public static final MaterialShaders WIREFRAME = new SimpleMaterialShaders(Flywheel.rl("material/wireframe.vert"), Flywheel.rl("material/wireframe.frag"));
|
||||
public static final MaterialShaders WIREFRAME = new SimpleMaterialShaders(ResourceUtil.rl("material/wireframe.vert"), ResourceUtil.rl("material/wireframe.frag"));
|
||||
|
||||
public static final MaterialShaders LINE = new SimpleMaterialShaders(Flywheel.rl("material/lines.vert"), Flywheel.rl("material/lines.frag"));
|
||||
public static final MaterialShaders LINE = new SimpleMaterialShaders(ResourceUtil.rl("material/lines.vert"), ResourceUtil.rl("material/lines.frag"));
|
||||
|
||||
public static final MaterialShaders GLINT = new SimpleMaterialShaders(Flywheel.rl("material/glint.vert"), Flywheel.rl("material/default.frag"));
|
||||
public static final MaterialShaders GLINT = new SimpleMaterialShaders(ResourceUtil.rl("material/glint.vert"), ResourceUtil.rl("material/default.frag"));
|
||||
|
||||
private StandardMaterialShaders() {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
|
||||
public final class EmptyModel implements Model {
|
||||
private static final Vector4fc BOUNDING_SPHERE = new Vector4f(0, 0, 0, 0);
|
||||
public static final EmptyModel INSTANCE = new EmptyModel();
|
||||
|
||||
@Override
|
||||
public List<ConfiguredMesh> meshes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4fc boundingSphere() {
|
||||
return BOUNDING_SPHERE;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
|
@ -32,14 +31,37 @@ public final class LineModelBuilder {
|
|||
private MemoryBlock data;
|
||||
private int vertexCount = 0;
|
||||
|
||||
public LineModelBuilder(int segmentCount) {
|
||||
public LineModelBuilder() {
|
||||
}
|
||||
|
||||
public LineModelBuilder(int initialSegmentCount) {
|
||||
ensureCapacity(initialSegmentCount);
|
||||
}
|
||||
|
||||
public void ensureCapacity(int segmentCount) {
|
||||
if (segmentCount < 0) {
|
||||
throw new IllegalArgumentException("Segment count must be greater than or equal to 0");
|
||||
} else if (segmentCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
vertexView = new FullVertexView();
|
||||
data = MemoryBlock.mallocTracked(segmentCount * 4 * vertexView.stride());
|
||||
vertexView.ptr(data.ptr());
|
||||
vertexCount = 0;
|
||||
} else {
|
||||
long requiredCapacity = (vertexCount + segmentCount * 4) * vertexView.stride();
|
||||
|
||||
if (requiredCapacity > data.size()) {
|
||||
data = data.realloc(requiredCapacity);
|
||||
vertexView.ptr(data.ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LineModelBuilder line(float x1, float y1, float z1, float x2, float y2, float z2) {
|
||||
ensureCapacity(vertexCount + 4);
|
||||
ensureCapacity(1);
|
||||
|
||||
// We'll use the normal to figure out the orientation of the line in the vertex shader.
|
||||
float dx = x2 - x1;
|
||||
|
@ -79,8 +101,19 @@ public final class LineModelBuilder {
|
|||
}
|
||||
|
||||
public Model build() {
|
||||
vertexView.vertexCount(vertexCount);
|
||||
if (vertexCount == 0) {
|
||||
return EmptyModel.INSTANCE;
|
||||
}
|
||||
|
||||
long requiredCapacity = vertexCount * vertexView.stride();
|
||||
|
||||
if (data.size() > requiredCapacity) {
|
||||
data = data.realloc(requiredCapacity);
|
||||
vertexView.ptr(data.ptr());
|
||||
}
|
||||
|
||||
vertexView.nativeMemoryOwner(data);
|
||||
vertexView.vertexCount(vertexCount);
|
||||
|
||||
var boundingSphere = ModelUtil.computeBoundingSphere(vertexView);
|
||||
boundingSphere.w += 0.1f; // make the bounding sphere a little bigger to account for line width
|
||||
|
@ -94,17 +127,6 @@ public final class LineModelBuilder {
|
|||
return model;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int vertexCount) {
|
||||
if (data == null) {
|
||||
vertexView = new FullVertexView();
|
||||
data = MemoryBlock.mallocTracked(vertexCount * vertexView.stride());
|
||||
vertexView.ptr(data.ptr());
|
||||
} else if (vertexCount * vertexView.stride() > data.size()) {
|
||||
data = data.realloc(vertexCount * vertexView.stride());
|
||||
vertexView.ptr(data.ptr());
|
||||
}
|
||||
}
|
||||
|
||||
private static class LineMesh implements Mesh {
|
||||
private static final IndexSequence INDEX_SEQUENCE = (ptr, count) -> {
|
||||
int numVertices = 2 * count / 3;
|
||||
|
@ -124,9 +146,9 @@ public final class LineModelBuilder {
|
|||
}
|
||||
};
|
||||
private final VertexList vertexList;
|
||||
private final Vector4f boundingSphere;
|
||||
private final Vector4fc boundingSphere;
|
||||
|
||||
public LineMesh(VertexList vertexList, Vector4f boundingSphere) {
|
||||
public LineMesh(VertexList vertexList, Vector4fc boundingSphere) {
|
||||
this.vertexList = vertexList;
|
||||
this.boundingSphere = boundingSphere;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ public final class ResourceUtil {
|
|||
private ResourceUtil() {
|
||||
}
|
||||
|
||||
public static ResourceLocation rl(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Flywheel.ID, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link ResourceLocation#parse(String)}, but defaults to Flywheel namespace.
|
||||
*/
|
||||
|
|
|
@ -2,10 +2,10 @@ package dev.engine_room.flywheel.impl;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.api.backend.Backend;
|
||||
import dev.engine_room.flywheel.impl.visualization.VisualizationManagerImpl;
|
||||
import dev.engine_room.flywheel.lib.backend.SimpleBackend;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
|
@ -15,7 +15,7 @@ public final class BackendManagerImpl {
|
|||
throw new UnsupportedOperationException("Cannot create engine when backend is off.");
|
||||
})
|
||||
.supported(() -> true)
|
||||
.register(Flywheel.rl("off"));
|
||||
.register(ResourceUtil.rl("off"));
|
||||
|
||||
private static Backend backend = OFF_BACKEND;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import dev.engine_room.flywheel.api.backend.Backend;
|
|||
import dev.engine_room.flywheel.backend.BackendConfig;
|
||||
|
||||
public interface FlwConfig {
|
||||
String DEFAULT_BACKEND_STR = "DEFAULT";
|
||||
|
||||
FlwConfig INSTANCE = FlwImplXplat.INSTANCE.getConfig();
|
||||
|
||||
Backend backend();
|
||||
|
|
|
@ -3,7 +3,7 @@ package dev.engine_room.flywheel.impl.event;
|
|||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
|
|
|
@ -9,8 +9,6 @@ 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.At.Shift;
|
||||
import org.spongepowered.asm.mixin.injection.Group;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
|
@ -83,16 +81,6 @@ abstract class LevelRendererMixin {
|
|||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/OutlineBufferSource;endOutlineBatch()V", ordinal = 0))
|
||||
private void flywheel$afterBlockEntities(CallbackInfo ci) {
|
||||
if (flywheel$renderContext != null) {
|
||||
VisualizationManager manager = VisualizationManager.get(level);
|
||||
if (manager != null) {
|
||||
manager.renderDispatcher().afterBlockEntities(flywheel$renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress"))
|
||||
private void flywheel$beforeRenderCrumbling(CallbackInfo ci) {
|
||||
if (flywheel$renderContext != null) {
|
||||
|
@ -103,28 +91,6 @@ abstract class LevelRendererMixin {
|
|||
}
|
||||
}
|
||||
|
||||
@Group(name = "afterParticles", min = 2, max = 3)
|
||||
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleEngine;render(Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;F)V", shift = Shift.AFTER))
|
||||
private void flywheel$afterParticles$fabric(CallbackInfo ci) {
|
||||
if (flywheel$renderContext != null) {
|
||||
VisualizationManager manager = VisualizationManager.get(level);
|
||||
if (manager != null) {
|
||||
manager.renderDispatcher().afterParticles(flywheel$renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Group(name = "afterParticles")
|
||||
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/ParticleEngine;render(Lnet/minecraft/client/renderer/LightTexture;Lnet/minecraft/client/Camera;FLnet/minecraft/client/renderer/culling/Frustum;Ljava/util/function/Predicate;)V", shift = Shift.AFTER))
|
||||
private void flywheel$afterParticles$neoforge(CallbackInfo ci) {
|
||||
if (flywheel$renderContext != null) {
|
||||
VisualizationManager manager = VisualizationManager.get(level);
|
||||
if (manager != null) {
|
||||
manager.renderDispatcher().afterParticles(flywheel$renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderEntity", at = @At("HEAD"), cancellable = true)
|
||||
private void flywheel$decideNotToRenderEntity(Entity entity, double camX, double camY, double camZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, CallbackInfo ci) {
|
||||
if (VisualizationManager.supportsVisualization(entity.level()) && VisualizationHelper.skipVanillaRender(entity)) {
|
||||
|
|
|
@ -9,16 +9,15 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.joml.FrustumIntersection;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import dev.engine_room.flywheel.api.RenderContext;
|
||||
import dev.engine_room.flywheel.api.backend.BackendManager;
|
||||
import dev.engine_room.flywheel.api.backend.Engine;
|
||||
import dev.engine_room.flywheel.api.backend.RenderContext;
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.task.Plan;
|
||||
import dev.engine_room.flywheel.api.visual.DynamicVisual;
|
||||
import dev.engine_room.flywheel.api.visual.Effect;
|
||||
import dev.engine_room.flywheel.api.visual.TickableVisual;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualManager;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationLevel;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
|
||||
import dev.engine_room.flywheel.impl.FlwConfig;
|
||||
|
@ -74,17 +73,16 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
private final Plan<RenderContext> framePlan;
|
||||
private final Plan<TickableVisual.Context> tickPlan;
|
||||
|
||||
private boolean canEngineRender;
|
||||
|
||||
private VisualizationManagerImpl(LevelAccessor level) {
|
||||
taskExecutor = FlwTaskExecutor.get();
|
||||
engine = BackendManager.currentBackend()
|
||||
.createEngine(level);
|
||||
frameLimiter = createUpdateLimiter();
|
||||
|
||||
var blockEntitiesStorage = new BlockEntityStorage(engine.createVisualizationContext(VisualType.BLOCK_ENTITY));
|
||||
var entitiesStorage = new EntityStorage(engine.createVisualizationContext(VisualType.ENTITY));
|
||||
var effectsStorage = new EffectStorage(engine.createVisualizationContext(VisualType.EFFECT));
|
||||
var visualizationContext = engine.createVisualizationContext();
|
||||
var blockEntitiesStorage = new BlockEntityStorage(visualizationContext);
|
||||
var entitiesStorage = new EntityStorage(visualizationContext);
|
||||
var effectsStorage = new EffectStorage(visualizationContext);
|
||||
|
||||
blockEntities = new VisualManagerImpl<>(blockEntitiesStorage);
|
||||
entities = new VisualManagerImpl<>(entitiesStorage);
|
||||
|
@ -241,25 +239,16 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
frameFlag.lower();
|
||||
|
||||
frameLimiter.tick();
|
||||
canEngineRender = false;
|
||||
|
||||
framePlan.execute(taskExecutor, context);
|
||||
}
|
||||
|
||||
private void ensureCanRender(RenderContext context) {
|
||||
taskExecutor.syncUntil(frameFlag::isRaised);
|
||||
if (!canEngineRender) {
|
||||
engine.setupRender(context);
|
||||
canEngineRender = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw all visuals of the given type.
|
||||
*/
|
||||
private void render(RenderContext context, VisualType visualType) {
|
||||
ensureCanRender(context);
|
||||
engine.render(context, visualType);
|
||||
private void render(RenderContext context) {
|
||||
taskExecutor.syncUntil(frameFlag::isRaised);
|
||||
engine.render(context);
|
||||
}
|
||||
|
||||
private void renderCrumbling(RenderContext context, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress) {
|
||||
|
@ -267,8 +256,6 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
return;
|
||||
}
|
||||
|
||||
ensureCanRender(context);
|
||||
|
||||
List<Engine.CrumblingBlock> crumblingBlocks = new ArrayList<>();
|
||||
|
||||
for (var entry : destructionProgress.long2ObjectEntrySet()) {
|
||||
|
@ -337,25 +324,15 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
beginFrame(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBlockEntities(RenderContext ctx) {
|
||||
render(ctx, VisualType.BLOCK_ENTITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEntities(RenderContext ctx) {
|
||||
render(ctx, VisualType.ENTITY);
|
||||
render(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeCrumbling(RenderContext ctx, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress) {
|
||||
renderCrumbling(ctx, destructionProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterParticles(RenderContext ctx) {
|
||||
render(ctx, VisualType.EFFECT);
|
||||
}
|
||||
}
|
||||
|
||||
private record CrumblingBlockImpl(BlockPos pos, int progress, List<Instance> instances) implements Engine.CrumblingBlock {
|
||||
|
|
|
@ -6,13 +6,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class TestBase {
|
||||
public static final ResourceLocation FLW_A = Flywheel.rl("a.glsl");
|
||||
public static final ResourceLocation FLW_B = Flywheel.rl("b.glsl");
|
||||
public static final ResourceLocation FLW_C = Flywheel.rl("c.glsl");
|
||||
public static final ResourceLocation FLW_A = ResourceUtil.rl("a.glsl");
|
||||
public static final ResourceLocation FLW_B = ResourceUtil.rl("b.glsl");
|
||||
public static final ResourceLocation FLW_C = ResourceUtil.rl("c.glsl");
|
||||
|
||||
public static <T> T assertSingletonList(List<T> list) {
|
||||
assertEquals(1, list.size());
|
||||
|
|
|
@ -7,8 +7,6 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import dev.engine_room.flywheel.backend.glsl.parse.Import;
|
||||
|
||||
public class TestShaderSourceLoading extends TestBase {
|
||||
@Test
|
||||
void testSimpleFind() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
@ -8,7 +9,7 @@ import net.minecraft.server.packs.resources.ResourceManager;
|
|||
public final class FlwProgramsReloader implements SimpleSynchronousResourceReloadListener {
|
||||
public static final FlwProgramsReloader INSTANCE = new FlwProgramsReloader();
|
||||
|
||||
public static final ResourceLocation ID = Flywheel.rl("programs");
|
||||
public static final ResourceLocation ID = ResourceUtil.rl("programs");
|
||||
|
||||
private FlwProgramsReloader() {
|
||||
}
|
||||
|
@ -16,6 +17,7 @@ public final class FlwProgramsReloader implements SimpleSynchronousResourceReloa
|
|||
@Override
|
||||
public void onResourceManagerReload(ResourceManager manager) {
|
||||
FlwPrograms.reload(manager);
|
||||
NoiseTextures.reload(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.util.List;
|
|||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import dev.engine_room.flywheel.api.Flywheel;
|
||||
import dev.engine_room.flywheel.lib.util.ResourceUtil;
|
||||
import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys;
|
||||
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -32,7 +32,7 @@ public final class PartialModelEventHandler {
|
|||
public static final class ReloadListener implements SimpleSynchronousResourceReloadListener {
|
||||
public static final ReloadListener INSTANCE = new ReloadListener();
|
||||
|
||||
public static final ResourceLocation ID = Flywheel.rl("partial_models");
|
||||
public static final ResourceLocation ID = ResourceUtil.rl("partial_models");
|
||||
public static final List<ResourceLocation> DEPENDENCIES = List.of(ResourceReloadListenerKeys.MODELS);
|
||||
|
||||
private ReloadListener() {
|
||||
|
|
|
@ -38,8 +38,8 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
|
||||
private final File file;
|
||||
|
||||
// Don't actually default to off, we'll find the true default in #load
|
||||
public Backend backend = BackendManager.offBackend();
|
||||
public boolean useDefaultBackend = true;
|
||||
public boolean limitUpdates = LIMIT_UPDATES_DEFAULT;
|
||||
public int workerThreads = WORKER_THREADS_DEFAULT;
|
||||
|
||||
|
@ -51,6 +51,10 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
|
||||
@Override
|
||||
public Backend backend() {
|
||||
if (useDefaultBackend) {
|
||||
return BackendManager.defaultBackend();
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
|
@ -70,10 +74,6 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
}
|
||||
|
||||
public void load() {
|
||||
// Grab the default backend here because this object is constructed
|
||||
// very early in flywheel loading and not all backends may be registered
|
||||
backend = BackendManager.defaultBackend();
|
||||
|
||||
if (file.exists()) {
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
fromJson(JsonParser.parseReader(reader));
|
||||
|
@ -96,7 +96,8 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
public void fromJson(JsonElement json) {
|
||||
if (!(json instanceof JsonObject object)) {
|
||||
FlwImpl.CONFIG_LOGGER.warn("Config JSON must be an object");
|
||||
backend = BackendManager.defaultBackend();
|
||||
backend = BackendManager.offBackend();
|
||||
useDefaultBackend = true;
|
||||
limitUpdates = LIMIT_UPDATES_DEFAULT;
|
||||
workerThreads = WORKER_THREADS_DEFAULT;
|
||||
return;
|
||||
|
@ -114,8 +115,15 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
|
||||
if (backendJson instanceof JsonPrimitive primitive && primitive.isString()) {
|
||||
var value = primitive.getAsString();
|
||||
if (value.equals(DEFAULT_BACKEND_STR)) {
|
||||
backend = BackendManager.offBackend();
|
||||
useDefaultBackend = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.backend = Backend.REGISTRY.getOrThrow(ResourceLocation.parse(value));
|
||||
useDefaultBackend = false;
|
||||
return;
|
||||
} catch (ResourceLocationException e) {
|
||||
msg = "'backend' value '" + value + "' is not a valid resource location";
|
||||
|
@ -133,7 +141,8 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
if (msg != null) {
|
||||
FlwImpl.CONFIG_LOGGER.warn(msg);
|
||||
}
|
||||
backend = BackendManager.defaultBackend();
|
||||
backend = BackendManager.offBackend();
|
||||
useDefaultBackend = true;
|
||||
}
|
||||
|
||||
private void readLimitUpdates(JsonObject object) {
|
||||
|
@ -181,7 +190,7 @@ public class FabricFlwConfig implements FlwConfig {
|
|||
|
||||
public JsonObject toJson() {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("backend", Backend.REGISTRY.getIdOrThrow(backend).toString());
|
||||
object.addProperty("backend", useDefaultBackend ? DEFAULT_BACKEND_STR : Backend.REGISTRY.getIdOrThrow(backend).toString());
|
||||
object.addProperty("limitUpdates", limitUpdates);
|
||||
object.addProperty("workerThreads", workerThreads);
|
||||
object.add("flw_backends", backendConfig.toJson());
|
||||
|
|
|
@ -38,10 +38,26 @@ public final class FlwCommands {
|
|||
context.getSource().sendFeedback(Component.translatable("command.flywheel.backend.get", idStr));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
.then(ClientCommandManager.literal("DEFAULT")
|
||||
.executes(context -> {
|
||||
FabricFlwConfig.INSTANCE.backend = BackendManager.offBackend();
|
||||
FabricFlwConfig.INSTANCE.useDefaultBackend = true;
|
||||
FabricFlwConfig.INSTANCE.save();
|
||||
|
||||
// Reload renderers so we can report the actual backend.
|
||||
Minecraft.getInstance().levelRenderer.allChanged();
|
||||
|
||||
Backend actualBackend = BackendManager.currentBackend();
|
||||
String actualIdStr = Backend.REGISTRY.getIdOrThrow(actualBackend)
|
||||
.toString();
|
||||
context.getSource().sendFeedback(Component.translatable("command.flywheel.backend.set", actualIdStr));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}))
|
||||
.then(ClientCommandManager.argument("id", BackendArgument.INSTANCE)
|
||||
.executes(context -> {
|
||||
Backend requestedBackend = context.getArgument("id", Backend.class);
|
||||
FabricFlwConfig.INSTANCE.backend = requestedBackend;
|
||||
FabricFlwConfig.INSTANCE.useDefaultBackend = false;
|
||||
FabricFlwConfig.INSTANCE.save();
|
||||
|
||||
// Reload renderers so we can report the actual backend.
|
||||
|
|
|
@ -7,6 +7,7 @@ import dev.engine_room.flywheel.api.event.EndClientResourceReloadCallback;
|
|||
import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback;
|
||||
import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.impl.mixin.fabric.ArgumentTypeInfosAccessor;
|
||||
import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler;
|
||||
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
|
||||
import dev.engine_room.flywheel.lib.util.RendererReloadCache;
|
||||
|
@ -16,7 +17,6 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallba
|
|||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
|
||||
import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
|
@ -60,9 +60,12 @@ public final class FlywheelFabric implements ClientModInitializer {
|
|||
EndClientResourceReloadCallback.EVENT.register((minecraft, resourceManager, initialReload, error) ->
|
||||
BackendManagerImpl.onEndClientResourceReload(error.isPresent()));
|
||||
|
||||
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("backend"), BackendArgument.class, BackendArgument.INFO);
|
||||
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("debug_mode"), DebugModeArgument.class, DebugModeArgument.INFO);
|
||||
ArgumentTypeRegistry.registerArgumentType(Flywheel.rl("light_smoothness"), LightSmoothnessArgument.class, LightSmoothnessArgument.INFO);
|
||||
// We can't use ArgumentTypeRegistry from Fabric API here as it also registers to BuiltInRegistries.COMMAND_ARGUMENT_TYPE.
|
||||
// We can't register anything to BuiltInRegistries.COMMAND_ARGUMENT_TYPE because it is a synced registry but
|
||||
// Flywheel is a client-side only mod.
|
||||
ArgumentTypeInfosAccessor.getBY_CLASS().put(BackendArgument.class, BackendArgument.INFO);
|
||||
ArgumentTypeInfosAccessor.getBY_CLASS().put(DebugModeArgument.class, DebugModeArgument.INFO);
|
||||
ArgumentTypeInfosAccessor.getBY_CLASS().put(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO);
|
||||
}
|
||||
|
||||
private static void setupLib() {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package dev.engine_room.flywheel.impl.mixin.fabric;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
|
||||
@Mixin(ArgumentTypeInfos.class)
|
||||
public interface ArgumentTypeInfosAccessor {
|
||||
@Accessor("BY_CLASS")
|
||||
static Map<Class<?>, ArgumentTypeInfo<?, ?>> getBY_CLASS() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
"compatibilityLevel": "JAVA_21",
|
||||
"refmap": "flywheel.refmap.json",
|
||||
"client": [
|
||||
"ArgumentTypeInfosAccessor",
|
||||
"DebugScreenOverlayMixin",
|
||||
"MinecraftMixin",
|
||||
"SystemReportMixin"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.engine_room.flywheel.backend.compile;
|
||||
|
||||
import dev.engine_room.flywheel.backend.NoiseTextures;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
|
||||
|
||||
|
@ -12,5 +13,6 @@ public final class FlwProgramsReloader implements ResourceManagerReloadListener
|
|||
@Override
|
||||
public void onResourceManagerReload(ResourceManager manager) {
|
||||
FlwPrograms.reload(manager);
|
||||
NoiseTextures.reload(manager);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,19 @@ public final class FlwCommands {
|
|||
sendMessage(context.getSource(), Component.translatable("command.flywheel.backend.get", idStr));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
})
|
||||
.then(Commands.literal("DEFAULT")
|
||||
.executes(context -> {
|
||||
backendValue.set(FlwConfig.DEFAULT_BACKEND_STR);
|
||||
|
||||
// Reload renderers so we can report the actual backend.
|
||||
Minecraft.getInstance().levelRenderer.allChanged();
|
||||
|
||||
Backend actualBackend = BackendManager.currentBackend();
|
||||
String actualIdStr = Backend.REGISTRY.getIdOrThrow(actualBackend)
|
||||
.toString();
|
||||
sendMessage(context.getSource(), Component.translatable("command.flywheel.backend.set", actualIdStr));
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}))
|
||||
.then(Commands.argument("id", BackendArgument.INSTANCE)
|
||||
.executes(context -> {
|
||||
Backend requestedBackend = context.getArgument("id", Backend.class);
|
||||
|
|
|
@ -16,7 +16,6 @@ import dev.engine_room.flywheel.lib.util.RendererReloadCache;
|
|||
import dev.engine_room.flywheel.lib.util.ResourceReloadHolder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.fml.CrashReportCallables;
|
||||
|
@ -30,7 +29,6 @@ import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
|
|||
import net.neoforged.neoforge.event.entity.EntityLeaveLevelEvent;
|
||||
import net.neoforged.neoforge.event.level.LevelEvent;
|
||||
import net.neoforged.neoforge.event.tick.LevelTickEvent;
|
||||
import net.neoforged.neoforge.registries.RegisterEvent;
|
||||
|
||||
@Mod(value = Flywheel.ID, dist = Dist.CLIENT)
|
||||
public final class FlywheelNeoForge {
|
||||
|
@ -83,17 +81,12 @@ public final class FlywheelNeoForge {
|
|||
modEventBus.addListener((EndClientResourceReloadEvent e) -> BackendManagerImpl.onEndClientResourceReload(e.error().isPresent()));
|
||||
|
||||
modEventBus.addListener((FMLCommonSetupEvent e) -> {
|
||||
// We can't register anything to Registries.COMMAND_ARGUMENT_TYPE because it is a synced registry but
|
||||
// Flywheel is a client-side only mod.
|
||||
ArgumentTypeInfos.registerByClass(BackendArgument.class, BackendArgument.INFO);
|
||||
ArgumentTypeInfos.registerByClass(DebugModeArgument.class, DebugModeArgument.INFO);
|
||||
ArgumentTypeInfos.registerByClass(LightSmoothnessArgument.class, LightSmoothnessArgument.INFO);
|
||||
});
|
||||
modEventBus.addListener((RegisterEvent e) -> {
|
||||
if (e.getRegistryKey().equals(Registries.COMMAND_ARGUMENT_TYPE)) {
|
||||
e.register(Registries.COMMAND_ARGUMENT_TYPE, Flywheel.rl("backend"), () -> BackendArgument.INFO);
|
||||
e.register(Registries.COMMAND_ARGUMENT_TYPE, Flywheel.rl("debug_mode"), () -> DebugModeArgument.INFO);
|
||||
e.register(Registries.COMMAND_ARGUMENT_TYPE, Flywheel.rl("light_smoothness"), () -> LightSmoothnessArgument.INFO);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void registerLibEventListeners(IEventBus gameEventBus, IEventBus modEventBus) {
|
||||
|
|
|
@ -29,20 +29,24 @@ public class NeoForgeFlwConfig implements FlwConfig {
|
|||
public Backend backend() {
|
||||
Backend backend = parseBackend(client.backend.get());
|
||||
if (backend == null) {
|
||||
client.backend.set(DEFAULT_BACKEND_STR);
|
||||
backend = BackendManager.defaultBackend();
|
||||
client.backend.set(Backend.REGISTRY.getIdOrThrow(backend).toString());
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Backend parseBackend(String idStr) {
|
||||
private static Backend parseBackend(String value) {
|
||||
if (value.equals(DEFAULT_BACKEND_STR)) {
|
||||
return BackendManager.defaultBackend();
|
||||
}
|
||||
|
||||
ResourceLocation backendId;
|
||||
try {
|
||||
backendId = ResourceLocation.parse(idStr);
|
||||
backendId = ResourceLocation.parse(value);
|
||||
} catch (ResourceLocationException e) {
|
||||
FlwImpl.CONFIG_LOGGER.warn("'backend' value '{}' is not a valid resource location", idStr);
|
||||
FlwImpl.CONFIG_LOGGER.warn("'backend' value '{}' is not a valid resource location", value);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -82,8 +86,8 @@ public class NeoForgeFlwConfig implements FlwConfig {
|
|||
public final NeoForgeBackendConfig backendConfig;
|
||||
|
||||
private ClientConfig(ModConfigSpec.Builder builder) {
|
||||
backend = builder.comment("Select the backend to use.")
|
||||
.define("backend", () -> Backend.REGISTRY.getIdOrThrow(BackendManager.defaultBackend()).toString(), o -> o != null && String.class.isAssignableFrom(o.getClass()));
|
||||
backend = builder.comment("Select the backend to use. Set to \"DEFAULT\" to let Flywheel decide.")
|
||||
.define("backend", DEFAULT_BACKEND_STR);
|
||||
|
||||
limitUpdates = builder.comment("Enable or disable instance update limiting with distance.")
|
||||
.define("limitUpdates", true);
|
||||
|
@ -103,7 +107,7 @@ public class NeoForgeFlwConfig implements FlwConfig {
|
|||
public final ModConfigSpec.EnumValue<LightSmoothness> lightSmoothness;
|
||||
|
||||
public NeoForgeBackendConfig(ModConfigSpec.Builder builder) {
|
||||
lightSmoothness = builder.comment("How smooth flywheel's shader-based lighting should be. May have a large performance impact.")
|
||||
lightSmoothness = builder.comment("How smooth Flywheel's shader-based lighting should be. May have a large performance impact.")
|
||||
.defineEnum("lightSmoothness", LightSmoothness.SMOOTH);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue