Uniformly incomplete

- Initial work on uniform buffer registry
 - Setup skeleton for ComponentRegistry
 - InstancingEngine is no longer generic about the program
 - ProgramCompiler considers ContextShaders as input
 - Support weak file resolutions
 - Temporary GLSL version bump to ease UniformProvider growing pains
 - Centralize component shaders to Components
This commit is contained in:
Jozufozu 2022-07-22 17:24:41 -07:00
parent 5e517a1bfe
commit 55de2beabb
57 changed files with 1004 additions and 658 deletions

View file

@ -11,24 +11,15 @@ import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.*;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider; import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
import com.jozufozu.flywheel.core.vertex.LayoutShaders;
import com.jozufozu.flywheel.event.EntityWorldHandler; import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents; import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances; import com.jozufozu.flywheel.vanilla.VanillaInstances;
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
import com.mojang.logging.LogUtils; import com.mojang.logging.LogUtils;
import net.minecraft.commands.synchronization.ArgumentTypes; import net.minecraft.commands.synchronization.ArgumentTypes;
@ -112,10 +103,7 @@ public class Flywheel {
// forgeEventBus.addListener(ExampleEffect::tick); // forgeEventBus.addListener(ExampleEffect::tick);
// forgeEventBus.addListener(ExampleEffect::onReload); // forgeEventBus.addListener(ExampleEffect::onReload);
LayoutShaders.init(); Components.init();
InstanceShaders.init();
Contexts.init();
MaterialShaders.init();
GameStateRegistry.register(NormalDebugStateProvider.INSTANCE); GameStateRegistry.register(NormalDebugStateProvider.INSTANCE);

View file

@ -1,9 +1,17 @@
package com.jozufozu.flywheel.api.material; package com.jozufozu.flywheel.api.material;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
public record Material(RenderType renderType, FileResolution vertexShader, FileResolution fragmentShader) { public record Material(RenderType renderType, FileResolution vertexShader, FileResolution fragmentShader) {
public SourceFile getVertexShader() {
return vertexShader.getFile();
}
public SourceFile getFragmentShader() {
return fragmentShader.getFile();
}
} }

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.api.uniform;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.core.source.FileResolution;
public abstract class UniformProvider {
protected ByteBuffer buffer;
protected Notifier notifier;
public abstract int getSize();
public void updatePtr(ByteBuffer backing, Notifier notifier) {
this.buffer = backing;
this.notifier = notifier;
}
public abstract FileResolution getUniformShader();
public interface Notifier {
void signalChanged();
}
}

View file

@ -47,6 +47,10 @@ public class Loader implements ResourceManagerReloadListener {
throw new ShaderLoadingException("Failed to resolve all source files, see log for details"); throw new ShaderLoadingException("Failed to resolve all source files, see log for details");
} }
sources.postResolve();
FileResolution.checkAll(errorReporter);
Backend.LOGGER.info("Loaded all shader sources."); Backend.LOGGER.info("Loaded all shader sources.");
ClientLevel world = Minecraft.getInstance().level; ClientLevel world = Minecraft.getInstance().level;

View file

@ -88,9 +88,9 @@ public abstract class GlProgram extends GlObject {
/** /**
* A factory interface to create a {@link GlProgram}. * A factory interface to create a {@link GlProgram}.
*/ */
public interface Factory<P extends GlProgram> { public interface Factory {
@NotNull @NotNull
P create(ResourceLocation name, int handle); GlProgram create(ResourceLocation name, int handle);
} }
} }

View file

@ -9,11 +9,10 @@ import com.jozufozu.flywheel.backend.instancing.effect.Effect;
import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager; import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.ClientLevelExtension; import com.jozufozu.flywheel.util.ClientLevelExtension;
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -39,7 +38,7 @@ public class InstanceWorld {
public static InstanceWorld create(LevelAccessor level) { public static InstanceWorld create(LevelAccessor level) {
var engine = switch (Backend.getBackendType()) { var engine = switch (Backend.getBackendType()) {
case INSTANCING -> new InstancingEngine<>(Contexts.WORLD); case INSTANCING -> new InstancingEngine(Components.WORLD);
case BATCHING -> new BatchingEngine(); case BATCHING -> new BatchingEngine();
case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off."); case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off.");
}; };

View file

@ -16,6 +16,7 @@ import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -131,4 +132,8 @@ public class InstancedRenderDispatcher {
debug.add("Disabled"); debug.add("Disabled");
} }
} }
public static Vec3i getOriginCoordinate(ClientLevel level) {
return instanceWorlds.get(level).engine.getOriginCoordinate();
}
} }

View file

@ -0,0 +1,154 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.GPUInstancer;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.server.level.BlockDestructionProgress;
public class SadCrumbling {
// public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) {
// var dataByStage = getDataByStage(levelRenderer, level);
// if (dataByStage.isEmpty()) {
// return;
// }
//
// var map = modelsToParts(dataByStage);
// var stateSnapshot = GameStateRegistry.takeSnapshot();
//
//// Vec3 cameraPosition = camera.getPosition();
//// var camX = cameraPosition.x - originCoordinate.getX();
//// var camY = cameraPosition.y - originCoordinate.getY();
//// var camZ = cameraPosition.z - originCoordinate.getZ();
////
//// // don't want to mutate viewProjection
//// var vp = projectionMatrix.copy();
//// vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
//
// GlBuffer instanceBuffer = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
//
// GlVertexArray crumblingVAO = new GlVertexArray();
//
// crumblingVAO.bind();
//
// // crumblingVAO.bindAttributes();
//
// for (var entry : map.entrySet()) {
// var model = entry.getKey();
// var parts = entry.getValue();
//
// if (parts.isEmpty()) {
// continue;
// }
//
// StructType<?> structType = parts.get(0).type;
//
// for (var meshEntry : model.get()
// .entrySet()) {
// Material material = meshEntry.getKey();
// Mesh mesh = meshEntry.getValue();
//
// MeshPool.BufferedMesh bufferedMesh = MeshPool.getInstance()
// .get(mesh);
//
// if (bufferedMesh == null || !bufferedMesh.isGpuResident()) {
// continue;
// }
//
// material.renderType().setupRenderState();
//
// CoreShaderInfoMap.CoreShaderInfo coreShaderInfo = CoreShaderInfoMap.CoreShaderInfo.get();
//
//
// var program = Compile.PROGRAM.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
// material, structType.getInstanceShader(), Components.CRUMBLING,
// coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
// GameStateRegistry.takeSnapshot()));
//
// program.bind();
//
// // bufferedMesh.drawInstances();
// }
// }
// }
@NotNull
private Map<ModelSupplier, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
var map = new HashMap<ModelSupplier, List<InstancedPart>>();
for (var entry : dataByStage.int2ObjectEntrySet()) {
RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey());
// something about when we call this means that the textures are not ready for use on the first frame they should appear
if (currentLayer == null) {
continue;
}
for (var blockEntityInstance : entry.getValue()) {
for (var part : blockEntityInstance.getCrumblingParts()) {
if (part.getOwner() instanceof GPUInstancer instancer) {
// queue the instances for copying to the crumbling instance buffer
map.computeIfAbsent(instancer.parent.getModel(), k -> new ArrayList<>()).add(part);
}
}
}
}
return map;
}
@Nonnull
private Int2ObjectMap<List<BlockEntityInstance<?>>> getDataByStage(LevelRenderer levelRenderer, ClientLevel level) {
var destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress();
if (destructionProgress.isEmpty()) {
return Int2ObjectMaps.emptyMap();
}
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
.getBlockEntities() instanceof BlockEntityInstanceManager beim)) {
return Int2ObjectMaps.emptyMap();
}
var dataByStage = new Int2ObjectArrayMap<List<BlockEntityInstance<?>>>();
for (var entry : destructionProgress.long2ObjectEntrySet()) {
SortedSet<BlockDestructionProgress> progresses = entry.getValue();
if (progresses == null || progresses.isEmpty()) {
continue;
}
int progress = progresses.last()
.getProgress();
var data = dataByStage.computeIfAbsent(progress, $ -> new ArrayList<>());
long pos = entry.getLongKey();
beim.getCrumblingInstances(pos, data);
}
return dataByStage;
}
}

View file

@ -4,67 +4,43 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedSet;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo; import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry; import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.shader.StateSnapshot; import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.vertex.Formats; import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Textures; import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet; import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class InstancingEngine<P extends WorldProgram> implements Engine { public class InstancingEngine implements Engine {
public static int MAX_ORIGIN_DISTANCE = 100; public static int MAX_ORIGIN_DISTANCE = 100;
protected BlockPos originCoordinate = BlockPos.ZERO; protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ProgramCompiler<P> context; protected final ContextShader context;
protected final Map<StructType<?>, GPUInstancerFactory<?>> factories = new HashMap<>(); protected final Map<StructType<?>, GPUInstancerFactory<?>> factories = new HashMap<>();
@ -78,7 +54,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
private int vertexCount; private int vertexCount;
private int instanceCount; private int instanceCount;
public InstancingEngine(ProgramCompiler<P> context) { public InstancingEngine(ContextShader context) {
this.context = context; this.context = context;
this.instanceManagers = new WeakHashSet<>(); this.instanceManagers = new WeakHashSet<>();
@ -98,16 +74,8 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
@Override @Override
public void renderAllRemaining(TaskEngine taskEngine, RenderContext context) { public void renderAllRemaining(TaskEngine taskEngine, RenderContext context) {
var camX = context.camX() - originCoordinate.getX();
var camY = context.camY() - originCoordinate.getY();
var camZ = context.camZ() - originCoordinate.getZ();
// don't want to mutate viewProjection
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
for (RenderType renderType : renderLists.drainLayers()) { for (RenderType renderType : renderLists.drainLayers()) {
render(renderType, camX, camY, camZ, vp, context.level()); render(renderType);
} }
} }
@ -117,18 +85,10 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
return; return;
} }
var camX = context.camX() - originCoordinate.getX(); render(type);
var camY = context.camY() - originCoordinate.getY();
var camZ = context.camZ() - originCoordinate.getZ();
// don't want to mutate viewProjection
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
render(type, camX, camY, camZ, vp, context.level());
} }
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) { protected void render(RenderType type) {
vertexCount = 0; vertexCount = 0;
instanceCount = 0; instanceCount = 0;
@ -138,16 +98,16 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
return; return;
} }
render(type, multimap, camX, camY, camZ, viewProjection, level); render(type, multimap);
} }
protected void render(RenderType type, ListMultimap<ShaderState, DrawCall> multimap, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) { protected void render(RenderType type, ListMultimap<ShaderState, DrawCall> multimap) {
type.setupRenderState(); type.setupRenderState();
Textures.bindActiveTextures(); Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get(); CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
StateSnapshot state = GameStateRegistry.takeSnapshot(); StateSnapshot state = GameStateRegistry.takeSnapshot();
for (var entry : Multimaps.asMap(multimap).entrySet()) { for (var entry : multimap.asMap().entrySet()) {
var shader = entry.getKey(); var shader = entry.getKey();
var drawCalls = entry.getValue(); var drawCalls = entry.getValue();
@ -157,7 +117,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
continue; continue;
} }
setup(shader, coreShaderInfo, camX, camY, camZ, viewProjection, level, state); setup(shader, coreShaderInfo, state);
for (var drawCall : drawCalls) { for (var drawCall : drawCalls) {
drawCall.render(); drawCall.render();
@ -168,21 +128,18 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
type.clearRenderState(); type.clearRenderState();
} }
protected P setup(ShaderState desc, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level, StateSnapshot ctx) { protected void setup(ShaderState desc, CoreShaderInfo coreShaderInfo, StateSnapshot ctx) {
VertexType vertexType = desc.vertex(); VertexType vertexType = desc.vertex();
FileResolution instanceShader = desc.instance() FileResolution instanceShader = desc.instance()
.getInstanceShader(); .getInstanceShader();
Material material = desc.material(); Material material = desc.material();
P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceShader, var program = ProgramCompiler.INSTANCE.getProgram(new ProgramCompiler.Context(vertexType, material,
material.vertexShader(), material.fragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(), instanceShader, context, coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(), ctx));
coreShaderInfo.fogType(), ctx));
program.bind(); program.bind();
program.uploadUniforms(camX, camY, camZ, viewProjection, level); UniformBuffer.getInstance().sync();
return program;
} }
public void clearAll() { public void clearAll() {
@ -250,131 +207,4 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
info.add("Vertices: " + vertexCount); info.add("Vertices: " + vertexCount);
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
} }
public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) {
var dataByStage = getDataByStage(levelRenderer, level);
if (dataByStage.isEmpty()) {
return;
}
var map = modelsToParts(dataByStage);
var stateSnapshot = GameStateRegistry.takeSnapshot();
Vec3 cameraPosition = camera.getPosition();
var camX = cameraPosition.x - originCoordinate.getX();
var camY = cameraPosition.y - originCoordinate.getY();
var camZ = cameraPosition.z - originCoordinate.getZ();
// don't want to mutate viewProjection
var vp = projectionMatrix.copy();
vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ);
GlBuffer instanceBuffer = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
GlVertexArray crumblingVAO = new GlVertexArray();
crumblingVAO.bind();
// crumblingVAO.bindAttributes();
for (var entry : map.entrySet()) {
var model = entry.getKey();
var parts = entry.getValue();
if (parts.isEmpty()) {
continue;
}
StructType<?> structType = parts.get(0).type;
for (var meshEntry : model.get()
.entrySet()) {
Material material = meshEntry.getKey();
Mesh mesh = meshEntry.getValue();
MeshPool.BufferedMesh bufferedMesh = MeshPool.getInstance()
.get(mesh);
if (bufferedMesh == null || !bufferedMesh.isGpuResident()) {
continue;
}
material.renderType().setupRenderState();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL,
structType.getInstanceShader(), material.vertexShader(), material.fragmentShader(),
coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(),
GameStateRegistry.takeSnapshot()));
program.bind();
program.uploadUniforms(camX, camY, camZ, vp, level);
// bufferedMesh.drawInstances();
}
}
}
@NotNull
private Map<ModelSupplier, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
var map = new HashMap<ModelSupplier, List<InstancedPart>>();
for (var entry : dataByStage.int2ObjectEntrySet()) {
RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey());
// something about when we call this means that the textures are not ready for use on the first frame they should appear
if (currentLayer == null) {
continue;
}
for (var blockEntityInstance : entry.getValue()) {
for (var part : blockEntityInstance.getCrumblingParts()) {
if (part.getOwner() instanceof GPUInstancer instancer) {
// queue the instances for copying to the crumbling instance buffer
map.computeIfAbsent(instancer.parent.getModel(), k -> new ArrayList<>()).add(part);
}
}
}
}
return map;
}
@Nonnull
private Int2ObjectMap<List<BlockEntityInstance<?>>> getDataByStage(LevelRenderer levelRenderer, ClientLevel level) {
var destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress();
if (destructionProgress.isEmpty()) {
return Int2ObjectMaps.emptyMap();
}
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
.getBlockEntities() instanceof BlockEntityInstanceManager beim)) {
return Int2ObjectMaps.emptyMap();
}
var dataByStage = new Int2ObjectArrayMap<List<BlockEntityInstance<?>>>();
for (var entry : destructionProgress.long2ObjectEntrySet()) {
SortedSet<BlockDestructionProgress> progresses = entry.getValue();
if (progresses == null || progresses.isEmpty()) {
continue;
}
int progress = progresses.last()
.getProgress();
var data = dataByStage.computeIfAbsent(progress, $ -> new ArrayList<>());
long pos = entry.getLongKey();
beim.getCrumblingInstances(pos, data);
}
return dataByStage;
}
} }

View file

@ -6,22 +6,18 @@ import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelSupplier; import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.util.Lazy; import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.NonNullSupplier; import com.jozufozu.flywheel.util.NonNullSupplier;
import net.minecraft.client.renderer.RenderType;
public class BasicModelSupplier implements ModelSupplier { public class BasicModelSupplier implements ModelSupplier {
private static final Material DEFAULT_MATERIAL = new Material(RenderType.solid(), MaterialShaders.DEFAULT_VERTEX, MaterialShaders.DEFAULT_FRAGMENT);
private Material material; private Material material;
private final Lazy<Mesh> supplier; private final Lazy<Mesh> supplier;
public BasicModelSupplier(NonNullSupplier<Mesh> supplier) { public BasicModelSupplier(NonNullSupplier<Mesh> supplier) {
this(supplier, DEFAULT_MATERIAL); this(supplier, Materials.DEFAULT);
} }
public BasicModelSupplier(NonNullSupplier<Mesh> supplier, Material material) { public BasicModelSupplier(NonNullSupplier<Mesh> supplier, Material material) {

View file

@ -0,0 +1,60 @@
package com.jozufozu.flywheel.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.vertex.BlockVertex;
import net.minecraft.resources.ResourceLocation;
public class ComponentRegistry {
private static final Set<ResourceLocation> uniformProviderFiles = new HashSet<>();
private static final List<UniformProvider> uniformProviders = new ArrayList<>();
// TODO: fill out the rest of the registry
public static Material register(Material material) {
return material;
}
public static <T extends StructType<?>> T register(T type) {
return type;
}
public static <T extends VertexType> T register(T vertexType) {
return vertexType;
}
public static ContextShader register(ContextShader contextShader) {
return contextShader;
}
public static <T extends UniformProvider> T register(T provider) {
var file = provider.getUniformShader();
ResourceLocation location = file.getFileLoc();
if (uniformProviderFiles.contains(location)) {
throw new IllegalArgumentException("UniformProvider for '" + location + "' already registered");
}
uniformProviderFiles.add(location);
uniformProviders.add(provider);
return provider;
}
public static Collection<UniformProvider> getAllUniformProviders() {
return Collections.unmodifiableCollection(uniformProviders);
}
}

View file

@ -0,0 +1,9 @@
package com.jozufozu.flywheel.core;
public enum ComponentType {
MATERIAL,
INSTANCE,
LAYOUT,
CONTEXT,
UNIFORM_PROVIDER
}

View file

@ -0,0 +1,113 @@
package com.jozufozu.flywheel.core;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.uniform.FogProvider;
import com.jozufozu.flywheel.core.uniform.ViewProvider;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class Components {
public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider());
public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider());
public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.CRUMBLING_VERTEX, Files.CRUMBLING_FRAGMENT));
public static void init() {
Files.init();
Formats.init();
StructTypes.init();
Materials.init();
}
public static class Files {
public static final FileResolution VIEW_UNIFORMS = uniform(Flywheel.rl("uniform/view.glsl"));
public static final FileResolution FOG_UNIFORMS = uniform(Flywheel.rl("uniform/fog.glsl"));
public static final FileResolution BLOCK_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.BLOCK, ".vert"));
public static final FileResolution POS_TEX_NORMAL_LAYOUT = layoutVertex(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"));
public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));
public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert"));
public static final FileResolution WORLD_VERTEX = contextVertex(ResourceUtil.subPath(Names.WORLD, ".vert"));
public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag"));
public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
public static FileResolution uniform(ResourceLocation location) {
return FileResolution.get(location);
}
public static FileResolution layoutVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.LAYOUT_VERTEX);
}
public static FileResolution instanceVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.INSTANCE_VERTEX);
}
public static FileResolution materialVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.MATERIAL_VERTEX);
}
public static FileResolution materialFragment(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.MATERIAL_FRAGMENT);
}
public static FileResolution contextVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.CONTEXT_VERTEX);
}
public static FileResolution contextFragment(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.CONTEXT_FRAGMENT);
}
public static void init() {
// noop, just in case
}
}
public static class Checks {
public static final BiConsumer<ErrorReporter, SourceFile> LAYOUT_VERTEX = SourceChecks.checkFunctionArity("flw_layoutVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> INSTANCE_VERTEX = SourceChecks.checkFunctionArity("flw_instanceVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_VERTEX = SourceChecks.checkFunctionArity("flw_materialVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> MATERIAL_FRAGMENT = SourceChecks.checkFunctionArity("flw_materialFragment", 0);
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_VERTEX = SourceChecks.checkFunctionArity("flw_contextVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> CONTEXT_FRAGMENT = SourceChecks.checkFunctionArity("flw_contextFragment", 0);
}
public static class Names {
public static final ResourceLocation BLOCK = Flywheel.rl("layout/block");
public static final ResourceLocation POS_TEX_NORMAL = Flywheel.rl("layout/pos_tex_normal");
public static final ResourceLocation TRANSFORMED = Flywheel.rl("instance/transformed");
public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented");
public static final ResourceLocation DEFAULT = Flywheel.rl("material/default");
public static final ResourceLocation SHADED = Flywheel.rl("material/shaded");
public static final ResourceLocation WORLD = Flywheel.rl("context/world");
public static final ResourceLocation CRUMBLING = Flywheel.rl("context/crumbling");
}
}

View file

@ -1,50 +0,0 @@
package com.jozufozu.flywheel.core;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class Contexts {
public static final BiConsumer<ErrorReporter, SourceFile> VERTEX_CHECK = SourceChecks.checkFunctionArity("flw_contextVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> FRAGMENT_CHECK = SourceChecks.checkFunctionArity("flw_contextFragment", 0);
public static final ProgramCompiler<WorldProgram> WORLD;
public static final ProgramCompiler<CrumblingProgram> CRUMBLING;
static {
var worldVertex = createVertex(ResourceUtil.subPath(Names.WORLD, ".vert"));
var worldFragment = createFragment(ResourceUtil.subPath(Names.WORLD, ".frag"));
var crumblingVertex = createVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
var crumblingFragment = createFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
WORLD = ProgramCompiler.create(WorldProgram::new, worldVertex, worldFragment, GLSLVersion.V330);
CRUMBLING = ProgramCompiler.create(CrumblingProgram::new, crumblingVertex, crumblingFragment, GLSLVersion.V330);
}
public static FileResolution createVertex(ResourceLocation location) {
return FileResolution.get(location).validateWith(VERTEX_CHECK);
}
public static FileResolution createFragment(ResourceLocation location) {
return FileResolution.get(location).validateWith(FRAGMENT_CHECK);
}
public static void init() {
}
public static class Names {
public static final ResourceLocation WORLD = Flywheel.rl("context/world");
public static final ResourceLocation CRUMBLING = Flywheel.rl("context/crumbling");
}
}

View file

@ -0,0 +1,19 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.api.material.Material;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.resources.ResourceLocation;
public class Materials {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
public static final Material DEFAULT = ComponentRegistry.register(new Material(RenderType.solid(), Components.Files.DEFAULT_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material CHEST = ComponentRegistry.register(new Material(Sheets.chestSheet(), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material SHULKER = ComponentRegistry.register(new Material(RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material MINECART = ComponentRegistry.register(new Material(RenderType.entitySolid(MINECART_LOCATION), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static void init() {
// noop
}
}

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader, FileResolution fragmentShader) {
public SourceFile getVertexShader() {
return vertexShader.getFile();
}
public SourceFile getFragmentShader() {
return fragmentShader.getFile();
}
}

View file

@ -7,43 +7,38 @@ import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType; import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType;
import com.jozufozu.flywheel.core.shader.ShaderConstants; import com.jozufozu.flywheel.core.shader.ShaderConstants;
import com.jozufozu.flywheel.core.shader.StateSnapshot; import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndex; import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceFile;
/** /**
* Handles compilation and deletion of fragment shaders. * Handles compilation and deletion of fragment shaders.
*/ */
public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> { public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
private final FileResolution contextShader;
private final GLSLVersion glslVersion;
public FragmentCompiler(FileResolution contextShader, GLSLVersion glslVersion) { public FragmentCompiler() {
this.contextShader = contextShader;
this.glslVersion = glslVersion;
} }
@Override @Override
protected GlShader _create(Context key) { protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder(); StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(glslVersion, ShaderType.FRAGMENT)); finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
ShaderConstants shaderConstants = key.getShaderConstants(); var shaderConstants = key.getShaderConstants();
shaderConstants.writeInto(finalSource); shaderConstants.writeInto(finalSource);
finalSource.append('\n'); finalSource.append('\n');
FileIndex index = new FileIndex(); var ctx = new CompilationContext();
// MATERIAL // MATERIAL
SourceFile materialShader = key.materialShader; SourceFile materialShader = key.materialShader;
materialShader.generateFinalSource(index, finalSource); finalSource.append(materialShader.generateFinalSource(ctx));
// CONTEXT // CONTEXT
SourceFile contextShaderSource = contextShader.getFile(); SourceFile contextShaderSource = key.contextShader;
contextShaderSource.generateFinalSource(index, finalSource); finalSource.append(contextShaderSource.generateFinalSource(ctx));
// MAIN // MAIN
@ -52,7 +47,7 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
try { try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name), shaderConstants); return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name), shaderConstants);
} catch (ShaderCompilationException e) { } catch (ShaderCompilationException e) {
throw e.withErrorLog(index); throw e.withErrorLog(ctx);
} }
} }
@ -78,7 +73,7 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
* @param fogType Which type of fog should be applied. * @param fogType Which type of fog should be applied.
* @param ctx The shader constants to apply. * @param ctx The shader constants to apply.
*/ */
public record Context(SourceFile materialShader, float alphaDiscard, FogType fogType, StateSnapshot ctx) { public record Context(SourceFile materialShader, SourceFile contextShader, float alphaDiscard, FogType fogType, StateSnapshot ctx) {
public ShaderConstants getShaderConstants() { public ShaderConstants getShaderConstants() {
ShaderConstants shaderConstants = ctx.getShaderConstants(); ShaderConstants shaderConstants = ctx.getShaderConstants();

View file

@ -49,7 +49,7 @@ public class ProgramAssembler {
return this; return this;
} }
public <P extends GlProgram> P build(GlProgram.Factory<P> factory) { public GlProgram build(GlProgram.Factory factory) {
return factory.create(name, program); return factory.create(name, program);
} }
} }

View file

@ -3,8 +3,8 @@ package com.jozufozu.flywheel.core.compile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.CoreShaderInfoMap; import com.jozufozu.flywheel.core.CoreShaderInfoMap;
import com.jozufozu.flywheel.core.shader.StateSnapshot; import com.jozufozu.flywheel.core.shader.StateSnapshot;
@ -22,41 +22,28 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent;
* A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload. * A ProgramCompiler is also responsible for deleting programs and shaders on renderer reload.
* </p> * </p>
*/ */
public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramCompiler.Context, P> { public class ProgramCompiler extends Memoizer<ProgramCompiler.Context, GlProgram> {
private static final List<ProgramCompiler<?>> ALL_COMPILERS = new ArrayList<>(); public static final ProgramCompiler INSTANCE = new ProgramCompiler();
private static final List<ProgramCompiler> ALL_COMPILERS = new ArrayList<>();
private final GlProgram.Factory<P> factory;
private final VertexCompiler vertexCompiler; private final VertexCompiler vertexCompiler;
private final FragmentCompiler fragmentCompiler; private final FragmentCompiler fragmentCompiler;
public ProgramCompiler(GlProgram.Factory<P> factory, VertexCompiler vertexCompiler, FragmentCompiler fragmentCompiler) { public ProgramCompiler() {
this.factory = factory; this.vertexCompiler = new VertexCompiler();
this.vertexCompiler = vertexCompiler; this.fragmentCompiler = new FragmentCompiler();
this.fragmentCompiler = fragmentCompiler;
ALL_COMPILERS.add(this); ALL_COMPILERS.add(this);
} }
/**
* Creates a program compiler using provided templates and headers.
* @param factory A factory to add meaning to compiled programs.
* @param vertexContextShader The context shader to use when compiling vertex shaders.
* @param fragmentContextShader The context shader to use when compiling fragment shaders.
* @param <P> The type of program to compile.
* @return A program compiler.
*/
public static <P extends GlProgram> ProgramCompiler<P> create(GlProgram.Factory<P> factory, FileResolution vertexContextShader, FileResolution fragmentContextShader, GLSLVersion glslVersion) {
return new ProgramCompiler<>(factory, new VertexCompiler(vertexContextShader, glslVersion), new FragmentCompiler(fragmentContextShader, glslVersion));
}
/** /**
* Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec.
* *
* @param ctx The context of compilation. * @param ctx The context of compilation.
* @return A compiled GlProgram. * @return A compiled GlProgram.
*/ */
public P getProgram(Context ctx) { public GlProgram getProgram(ProgramCompiler.Context ctx) {
return super.get(ctx); return super.get(ctx);
} }
@ -68,17 +55,28 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramCompil
} }
@Override @Override
protected P _create(Context ctx) { protected GlProgram _create(ProgramCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile // TODO: try-catch here to prevent crashing if shaders failed to compile
return new ProgramAssembler(ctx.instanceShader().getFileLoc()) Material material = ctx.material;
.attachShader(vertexCompiler.get(new VertexCompiler.Context(ctx.vertexType(), ctx.instanceShader().getFile(), ctx.vertexMaterialShader().getFile(), ctx.ctx()))) StateSnapshot snapshot = ctx.ctx();
.attachShader(fragmentCompiler.get(new FragmentCompiler.Context(ctx.fragmentMaterialShader().getFile(), ctx.alphaDiscard(), ctx.fogType(), ctx.ctx()))) FileResolution instanceShader = ctx.instanceShader();
ContextShader contextShader = ctx.contextShader;
var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader(),
contextShader.getVertexShader(), snapshot);
var fragment = new FragmentCompiler.Context(material.getFragmentShader(), contextShader.getFragmentShader(),
ctx.alphaDiscard(), ctx.fogType(), snapshot);
return new ProgramAssembler(instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(vertex))
.attachShader(fragmentCompiler.get(fragment))
.link() .link()
.build(this.factory); .build(contextShader.factory());
} }
@Override @Override
protected void _destroy(P value) { protected void _destroy(GlProgram value) {
value.delete(); value.delete();
} }
@ -90,14 +88,15 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramCompil
* Represents the entire context of a program's usage. * Represents the entire context of a program's usage.
* *
* @param vertexType The vertexType the program should be adapted for. * @param vertexType The vertexType the program should be adapted for.
* @param material The material shader to use.
* @param instanceShader The instance shader to use. * @param instanceShader The instance shader to use.
* @param vertexMaterialShader The vertex material shader to use. * @param contextShader The context shader to use.
* @param fragmentMaterialShader The fragment material shader to use.
* @param alphaDiscard Alpha threshold below which pixels are discarded. * @param alphaDiscard Alpha threshold below which pixels are discarded.
* @param fogType Which type of fog should be applied. * @param fogType Which type of fog should be applied.
* @param ctx A snapshot of the game state. * @param ctx A snapshot of the game state.
*/ */
public record Context(VertexType vertexType, FileResolution instanceShader, FileResolution vertexMaterialShader, public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
FileResolution fragmentMaterialShader, float alphaDiscard, CoreShaderInfoMap.CoreShaderInfo.FogType fogType, StateSnapshot ctx) { ContextShader contextShader, float alphaDiscard,
CoreShaderInfoMap.CoreShaderInfo.FogType fogType, StateSnapshot ctx) {
} }
} }

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core.compile;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.core.source.FileIndex; import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderLoadingException;
public class ShaderCompilationException extends ShaderLoadingException { public class ShaderCompilationException extends ShaderLoadingException {
@ -16,11 +16,11 @@ public class ShaderCompilationException extends ShaderLoadingException {
this.shaderHandle = shaderHandle; this.shaderHandle = shaderHandle;
} }
public ShaderLoadingException withErrorLog(FileIndex fileIndex) { public ShaderLoadingException withErrorLog(CompilationContext ctx) {
if (this.shaderHandle == -1) if (this.shaderHandle == -1)
return this; return this;
this.errors = fileIndex.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle)); this.errors = ctx.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle));
return this; return this;
} }

View file

@ -8,12 +8,9 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.shader.StateSnapshot; import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndex; import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderField; import com.jozufozu.flywheel.core.source.ShaderField;
import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.parse.StructField;
import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.util.Pair; import com.jozufozu.flywheel.util.Pair;
@ -21,30 +18,26 @@ import com.jozufozu.flywheel.util.Pair;
* Handles compilation and deletion of vertex shaders. * Handles compilation and deletion of vertex shaders.
*/ */
public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> { public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
private final FileResolution contextShader;
private final GLSLVersion glslVersion;
public VertexCompiler(FileResolution contextShader, GLSLVersion glslVersion) { public VertexCompiler() {
this.contextShader = contextShader;
this.glslVersion = glslVersion;
} }
@Override @Override
protected GlShader _create(Context key) { protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder(); StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(glslVersion, ShaderType.VERTEX)); finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
var shaderConstants = key.ctx.getShaderConstants(); var shaderConstants = key.ctx.getShaderConstants();
shaderConstants.writeInto(finalSource); shaderConstants.writeInto(finalSource);
finalSource.append('\n'); finalSource.append('\n');
var index = new FileIndex(); var index = new CompilationContext();
// LAYOUT // LAYOUT
var layoutShader = key.vertexType.getLayoutShader().getFile(); var layoutShader = key.vertexType.getLayoutShader().getFile();
layoutShader.generateFinalSource(index, finalSource); finalSource.append(layoutShader.generateFinalSource(index));
// INSTANCE // INSTANCE
@ -62,17 +55,17 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
int newLocation = location + attributeBaseIndex; int newLocation = location + attributeBaseIndex;
replacements.add(Pair.of(field.location, Integer.toString(newLocation))); replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
} }
instanceShader.generateFinalSource(index, finalSource, replacements); finalSource.append(instanceShader.generateFinalSource(index, replacements));
// MATERIAL // MATERIAL
var materialShader = key.materialShader; var materialShader = key.materialShader;
materialShader.generateFinalSource(index, finalSource); finalSource.append(materialShader.generateFinalSource(index));
// CONTEXT // CONTEXT
var contextShaderSource = contextShader.getFile(); var contextShaderSource = key.contextShader;
contextShaderSource.generateFinalSource(index, finalSource); finalSource.append(contextShaderSource.generateFinalSource(index));
// MAIN // MAIN
@ -104,8 +97,9 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
* @param vertexType The vertex type to use. * @param vertexType The vertex type to use.
* @param instanceShader The instance shader source. * @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source. * @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
* @param ctx The shader constants to apply. * @param ctx The shader constants to apply.
*/ */
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, StateSnapshot ctx) { public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader, StateSnapshot ctx) {
} }
} }

View file

@ -9,7 +9,7 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine; import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor; import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
@ -153,15 +153,15 @@ public class CrumblingRenderer {
} }
} }
private static class CrumblingEngine extends InstancingEngine<CrumblingProgram> { private static class CrumblingEngine extends InstancingEngine {
private RenderType currentLayer; private RenderType currentLayer;
public CrumblingEngine() { public CrumblingEngine() {
super(Contexts.CRUMBLING); super(Components.CRUMBLING);
} }
@Override @Override
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) { protected void render(RenderType type) {
if (!type.affectsCrumbling()) { if (!type.affectsCrumbling()) {
return; return;
} }
@ -172,7 +172,7 @@ public class CrumblingRenderer {
return; return;
} }
render(currentLayer, multimap, camX, camY, camZ, viewProjection, level); render(currentLayer, multimap);
} }
} }
} }

View file

@ -1,37 +0,0 @@
package com.jozufozu.flywheel.core.material;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class MaterialShaders {
public static final BiConsumer<ErrorReporter, SourceFile> VERTEX_CHECK = SourceChecks.checkFunctionArity("flw_materialVertex", 0);
public static final BiConsumer<ErrorReporter, SourceFile> FRAGMENT_CHECK = SourceChecks.checkFunctionArity("flw_materialFragment", 0);
public static final FileResolution DEFAULT_VERTEX = createVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
public static final FileResolution DEFAULT_FRAGMENT = createFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));
public static final FileResolution SHADED_VERTEX = createVertex(ResourceUtil.subPath(Names.SHADED, ".vert"));
public static FileResolution createVertex(ResourceLocation location) {
return FileResolution.get(location).validateWith(VERTEX_CHECK);
}
public static FileResolution createFragment(ResourceLocation location) {
return FileResolution.get(location).validateWith(FRAGMENT_CHECK);
}
public static void init() {
}
public static class Names {
public static final ResourceLocation DEFAULT = Flywheel.rl("material/default");
public static final ResourceLocation SHADED = Flywheel.rl("material/shaded");
}
}

View file

@ -1,25 +0,0 @@
package com.jozufozu.flywheel.core.shader;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.mojang.blaze3d.systems.RenderSystem;
public class WorldFog {
private final int uFogRange;
private final int uFogColor;
private final int uFogShape;
public WorldFog(GlProgram program) {
this.uFogRange = program.getUniformLocation("uFogRange");
this.uFogColor = program.getUniformLocation("uFogColor");
this.uFogShape = program.getUniformLocation("uFogShape");
}
public void uploadUniforms() {
GL20.glUniform2f(uFogRange, RenderSystem.getShaderFogStart(), RenderSystem.getShaderFogEnd());
GL20.glUniform4fv(uFogColor, RenderSystem.getShaderFogColor());
GL20.glUniform1i(uFogShape, RenderSystem.getShaderFogShape().getIndex());
}
}

View file

@ -1,35 +1,18 @@
package com.jozufozu.flywheel.core.shader; package com.jozufozu.flywheel.core.shader;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniform2f;
import static org.lwjgl.opengl.GL20.glUniform3f;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.blaze3d.platform.Window;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public class WorldProgram extends GlProgram { public class WorldProgram extends GlProgram {
protected final int uTime = getUniformLocation("uTime");
protected final int uViewProjection = getUniformLocation("uViewProjection");
protected final int uCameraPos = getUniformLocation("uCameraPos");
protected final int uWindowSize = getUniformLocation("uWindowSize");
protected final int uConstantAmbientLight = getUniformLocation("uConstantAmbientLight");
private final WorldFog fog;
// TODO: sampler registry?
protected int uBlockAtlas; protected int uBlockAtlas;
protected int uLightMap; protected int uLightMap;
public WorldProgram(ResourceLocation name, int handle) { public WorldProgram(ResourceLocation name, int handle) {
super(name, handle); super(name, handle);
fog = new WorldFog(this);
bind(); bind();
registerSamplers(); registerSamplers();
unbind(); unbind();
@ -39,48 +22,4 @@ public class WorldProgram extends GlProgram {
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0); uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
uLightMap = setSamplerBinding("uLightMap", 2); uLightMap = setSamplerBinding("uLightMap", 2);
} }
// TODO: create uniform registry
public void uploadUniforms(double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
fog.uploadUniforms();
uploadTime(AnimationTickHolder.getRenderTime());
uploadViewProjection(viewProjection);
uploadCameraPos(camX, camY, camZ);
uploadWindowSize();
uploadConstantAmbientLight(level);
}
protected void uploadTime(float renderTime) {
if (uTime < 0) return;
glUniform1f(uTime, renderTime);
}
protected void uploadViewProjection(Matrix4f viewProjection) {
if (uViewProjection < 0) return;
uploadMatrixUniform(uViewProjection, viewProjection);
}
protected void uploadCameraPos(double camX, double camY, double camZ) {
if (uCameraPos < 0) return;
glUniform3f(uCameraPos, (float) camX, (float) camY, (float) camZ);
}
protected void uploadWindowSize() {
if (uWindowSize < 0) return;
Window window = Minecraft.getInstance().getWindow();
int height = window.getScreenHeight();
int width = window.getScreenWidth();
glUniform2f(uWindowSize, width, height);
}
protected void uploadConstantAmbientLight(ClientLevel level) {
if (uConstantAmbientLight < 0) return;
glUniform1i(uConstantAmbientLight, level.effects().constantAmbientLight() ? 1 : 0);
}
} }

View file

@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.source.error.ErrorBuilder; import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.core.source.span.Span;
public class FileIndex { public class CompilationContext {
public final List<SourceFile> files = new ArrayList<>(); public final List<SourceFile> files = new ArrayList<>();
/** /**
@ -27,7 +27,7 @@ public class FileIndex {
return size; return size;
} }
public boolean exists(SourceFile sourceFile) { public boolean contains(SourceFile sourceFile) {
return files.contains(sourceFile); return files.contains(sourceFile);
} }

View file

@ -24,6 +24,7 @@ import net.minecraft.resources.ResourceLocation;
public class FileResolution { public class FileResolution {
private static final Map<ResourceLocation, FileResolution> ALL = new HashMap<>(); private static final Map<ResourceLocation, FileResolution> ALL = new HashMap<>();
private static final Map<ResourceLocation, FileResolution> WEAK = new HashMap<>();
private static boolean tooLate = false; private static boolean tooLate = false;
/** /**
@ -56,32 +57,63 @@ public class FileResolution {
} }
} }
/**
* Weak resolutions don't persist through resource reloads.<p>
* This should be used inside parsing code.
*
* @param file The location of the file to resolve.
* @return A weak resolution for the given file.
*/
public static FileResolution weak(ResourceLocation file) {
FileResolution fileResolution = ALL.get(file);
if (fileResolution != null) {
return fileResolution;
}
// never too late for weak resolutions.
return WEAK.computeIfAbsent(file, FileResolution::new);
}
/** /**
* Try and resolve all referenced source files, printing errors if any aren't found. * Try and resolve all referenced source files, printing errors if any aren't found.
*/ */
public static void run(ErrorReporter errorReporter, SourceFinder sources) { public static void run(ErrorReporter errorReporter, SourceFinder sources) {
for (FileResolution resolution : ALL.values()) { for (FileResolution resolution : ALL.values()) {
resolution.resolveAndCheck(errorReporter, sources); resolution.resolve(errorReporter, sources);
} }
for (FileResolution resolution : WEAK.values()) {
resolution.resolve(errorReporter, sources);
}
WEAK.clear();
tooLate = true; tooLate = true;
} }
private void resolveAndCheck(ErrorReporter errorReporter, SourceFinder sources) { public static void checkAll(ErrorReporter errorReporter) {
for (FileResolution resolution : ALL.values()) {
resolution.runChecks(errorReporter);
}
}
private void resolve(ErrorReporter errorReporter, SourceFinder sources) {
file = sources.findSource(fileLoc); file = sources.findSource(fileLoc);
if (file == null) { if (file == null) {
reportMissing(errorReporter);
}
// Let the GC do its thing
neededAt.clear();
}
private void reportMissing(ErrorReporter errorReporter) {
ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc)); ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc));
for (Span location : neededAt) { for (Span location : neededAt) {
builder.pointAtFile(location.getSourceFile()) builder.pointAtFile(location.getSourceFile())
.pointAt(location, 1); .pointAt(location, 1);
} }
} else {
runChecks(errorReporter);
}
// Let the GC do its thing
neededAt.clear();
} }
private void runChecks(ErrorReporter errorReporter) { private void runChecks(ErrorReporter errorReporter) {

View file

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ -29,14 +30,8 @@ public class ShaderSources implements SourceFinder {
public final Index index; public final Index index;
public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) { public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) {
Collection<ResourceLocation> allShaders = manager.listResources(SHADER_DIR, s -> {
for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true;
}
return false;
});
for (ResourceLocation location : allShaders) { for (ResourceLocation location : getValidShaderFiles(manager)) {
try (Resource resource = manager.getResource(location)) { try (Resource resource = manager.getResource(location)) {
String source = StringUtil.readToString(resource.getInputStream()); String source = StringUtil.readToString(resource.getInputStream());
@ -51,9 +46,25 @@ public class ShaderSources implements SourceFinder {
index = new Index(shaderSources); index = new Index(shaderSources);
} }
public void postResolve() {
for (SourceFile file : shaderSources.values()) {
file.postResolve();
}
}
@Override @Override
@Nullable @Nullable
public SourceFile findSource(ResourceLocation name) { public SourceFile findSource(ResourceLocation name) {
return shaderSources.get(name); return shaderSources.get(name);
} }
@NotNull
private static Collection<ResourceLocation> getValidShaderFiles(ResourceManager manager) {
return manager.listResources(SHADER_DIR, s -> {
for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true;
}
return false;
});
}
} }

View file

@ -57,6 +57,9 @@ public class SourceFile {
public final ImmutableMap<String, ShaderField> fields; public final ImmutableMap<String, ShaderField> fields;
private final List<Span> elisions; private final List<Span> elisions;
// POST-RESOLUTION
private List<Import> flattenedImports;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) { public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent; this.parent = parent;
this.name = name; this.name = name;
@ -72,6 +75,37 @@ public class SourceFile {
this.fields = parseFields(); this.fields = parseFields();
} }
public void postResolve() {
this.flattenImports();
}
private void flattenImports() {
// somebody #used us and got resolved before we did
if (this.flattenedImports != null) {
return;
}
if (this.imports.isEmpty()) {
this.flattenedImports = Collections.emptyList();
return;
}
List<Import> flat = new ArrayList<>(this.imports.size());
for (Import include : this.imports) {
SourceFile file = include.resolution.getFile();
file.flattenImports();
flat.addAll(file.flattenedImports);
flat.add(include);
}
this.flattenedImports = flat.stream()
.distinct()
.toList();
}
public Span getLineSpan(int line) { public Span getLineSpan(int line) {
int begin = lines.getLineStart(line); int begin = lines.getLineStart(line);
int end = begin + lines.getLine(line).length(); int end = begin + lines.getLine(line).length();
@ -95,16 +129,23 @@ public class SourceFile {
* @param name The name of the struct to find. * @param name The name of the struct to find.
* @return null if no definition matches the name. * @return null if no definition matches the name.
*/ */
public Optional<ShaderStruct> findStruct(CharSequence name) { public Optional<ShaderStruct> findStruct(String name) {
ShaderStruct struct = structs.get(name.toString()); ShaderStruct struct = structs.get(name);
if (struct != null) return Optional.of(struct); if (struct != null) return Optional.of(struct);
for (Import include : imports) { for (Import include : flattenedImports) {
Optional<ShaderStruct> externalStruct = include.getOptional() var file = include.getFile();
.flatMap(file -> file.findStruct(name));
if (externalStruct.isPresent()) return externalStruct; if (file == null) {
continue;
}
var external = file.structs.get(name);
if (external != null) {
return Optional.of(external);
}
} }
return Optional.empty(); return Optional.empty();
@ -116,16 +157,23 @@ public class SourceFile {
* @param name The name of the function to find. * @param name The name of the function to find.
* @return Optional#empty() if no definition matches the name. * @return Optional#empty() if no definition matches the name.
*/ */
public Optional<ShaderFunction> findFunction(CharSequence name) { public Optional<ShaderFunction> findFunction(String name) {
ShaderFunction local = functions.get(name.toString()); ShaderFunction local = functions.get(name);
if (local != null) return Optional.of(local); if (local != null) return Optional.of(local);
for (Import include : imports) { for (Import include : imports) {
Optional<ShaderFunction> external = include.getOptional() var file = include.getFile();
.flatMap(file -> file.findFunction(name));
if (external.isPresent()) return external; if (file == null) {
continue;
}
var external = file.functions.get(name);
if (external != null) {
return Optional.of(external);
}
} }
return Optional.empty(); return Optional.empty();
@ -135,40 +183,46 @@ public class SourceFile {
return "#use " + '"' + name + '"'; return "#use " + '"' + name + '"';
} }
public void generateFinalSource(FileIndex env, StringBuilder source) { public String generateFinalSource(CompilationContext env) {
generateFinalSource(env, source, Collections.emptyList()); return generateFinalSource(env, Collections.emptyList());
} }
public void generateFinalSource(FileIndex env, StringBuilder source, List<Pair<Span, String>> replacements) { public String generateFinalSource(CompilationContext env, List<Pair<Span, String>> replacements) {
for (Import include : imports) { var out = new StringBuilder();
for (Import include : flattenedImports) {
SourceFile file = include.getFile(); SourceFile file = include.getFile();
if (file != null && !env.exists(file)) { if (file == null || env.contains(file)) {
file.generateFinalSource(env, source, replacements); continue;
}
} }
source.append("#line ") out.append(file.generateLineHeader(env))
.append(0) .append(file.replaceAndElide(replacements));
.append(' ')
.append(env.getFileID(this))
.append(" // ")
.append(name)
.append('\n');
var replacementsAndElisions = new ArrayList<>(replacements);
for (Span elision : elisions) {
replacementsAndElisions.add(Pair.of(elision, ""));
} }
source.append(this.replace(replacementsAndElisions)); out.append(this.generateLineHeader(env))
source.append('\n'); .append(this.replaceAndElide(replacements));
return out.toString();
}
private String generateLineHeader(CompilationContext env) {
return "#line " + 0 + ' ' + env.getFileID(this) + " // " + name + '\n';
} }
public String printSource() { public String printSource() {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers(); return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
} }
private CharSequence replaceAndElide(List<Pair<Span, String>> replacements) {
var replacementsAndElisions = new ArrayList<>(replacements);
for (var include : imports) {
replacementsAndElisions.add(Pair.of(include.self, ""));
}
return this.replace(replacementsAndElisions);
}
private CharSequence replace(List<Pair<Span, String>> replacements) { private CharSequence replace(List<Pair<Span, String>> replacements) {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
@ -276,7 +330,7 @@ public class SourceFile {
Matcher uses = Import.PATTERN.matcher(source); Matcher uses = Import.PATTERN.matcher(source);
Set<String> importedFiles = new HashSet<>(); Set<String> importedFiles = new HashSet<>();
List<Import> imports = new ArrayList<>(); var imports = ImmutableList.<Import>builder();
while (uses.find()) { while (uses.find()) {
Span use = Span.fromMatcher(this, uses); Span use = Span.fromMatcher(this, uses);
@ -284,17 +338,14 @@ public class SourceFile {
String fileName = file.get(); String fileName = file.get();
if (importedFiles.add(fileName)) { if (importedFiles.add(fileName)) {
// FIXME: creating imports after the first resource reload crashes the game
var checked = Import.create(errorReporter, use, file); var checked = Import.create(errorReporter, use, file);
if (checked != null) { if (checked != null) {
imports.add(checked); imports.add(checked);
} }
} }
this.elisions.add(use); // we have to trim that later
} }
return ImmutableList.copyOf(imports); return imports.build();
} }
/** /**

View file

@ -7,7 +7,7 @@ import java.util.regex.Pattern;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.core.source.FileIndex; import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.SourceLines; import com.jozufozu.flywheel.core.source.SourceLines;
import com.jozufozu.flywheel.core.source.error.lines.ErrorLine; import com.jozufozu.flywheel.core.source.error.lines.ErrorLine;
@ -45,7 +45,7 @@ public class ErrorBuilder {
} }
@Nullable @Nullable
public static ErrorBuilder fromLogLine(FileIndex env, String s) { public static ErrorBuilder fromLogLine(CompilationContext env, String s) {
Matcher matcher = ERROR_LINE.matcher(s); Matcher matcher = ERROR_LINE.matcher(s);
if (matcher.find()) { if (matcher.find()) {

View file

@ -17,7 +17,7 @@ public class Import extends AbstractShaderElement {
public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE); public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE);
private final FileResolution resolution; public final FileResolution resolution;
protected Import(Span self, FileResolution resolution, Span file) { protected Import(Span self, FileResolution resolution, Span file) {
super(self); super(self);
@ -34,11 +34,7 @@ public class Import extends AbstractShaderElement {
return null; return null;
} }
return new Import(self, FileResolution.get(fileLocation), file); return new Import(self, FileResolution.weak(fileLocation), file);
}
public FileResolution getResolution() {
return resolution;
} }
public Optional<SourceFile> getOptional() { public Optional<SourceFile> getOptional() {

View file

@ -131,14 +131,14 @@ public abstract class Span implements CharSequence, Comparable<Span> {
if (isErr()) { if (isErr()) {
return Optional.empty(); return Optional.empty();
} }
return in.findStruct(this); return in.findStruct(this.toString());
} }
public Optional<ShaderFunction> findFunction() { public Optional<ShaderFunction> findFunction() {
if (isErr()) { if (isErr()) {
return Optional.empty(); return Optional.empty();
} }
return in.findFunction(this); return in.findFunction(this.toString());
} }
@Override @Override

View file

@ -1,31 +0,0 @@
package com.jozufozu.flywheel.core.structs;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class InstanceShaders {
public static final BiConsumer<ErrorReporter, SourceFile> CHECK = SourceChecks.checkFunctionArity("flw_instanceVertex", 0);
public static final FileResolution TRANSFORMED = create(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution ORIENTED = create(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
public static FileResolution create(ResourceLocation location) {
return FileResolution.get(location).validateWith(CHECK);
}
public static void init() {
}
public static class Names {
public static final ResourceLocation TRANSFORMED = Flywheel.rl("instance/transformed");
public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented");
}
}

View file

@ -1,12 +1,17 @@
package com.jozufozu.flywheel.core.structs; package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.model.TransformedType; import com.jozufozu.flywheel.core.structs.model.TransformedType;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedType; import com.jozufozu.flywheel.core.structs.oriented.OrientedType;
public class StructTypes { public class StructTypes {
public static final StructType<TransformedPart> TRANSFORMED = new TransformedType(); public static final StructType<TransformedPart> TRANSFORMED = ComponentRegistry.register(new TransformedType());
public static final StructType<OrientedPart> ORIENTED = new OrientedType(); public static final StructType<OrientedPart> ORIENTED = ComponentRegistry.register(new OrientedType());
public static void init() {
// noop
}
} }

View file

@ -4,11 +4,11 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer; import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
public class TransformedType implements StructType<TransformedPart> { public class TransformedType implements StructType<TransformedPart> {
@ -34,7 +34,7 @@ public class TransformedType implements StructType<TransformedPart> {
@Override @Override
public FileResolution getInstanceShader() { public FileResolution getInstanceShader() {
return InstanceShaders.TRANSFORMED; return Components.Files.TRANSFORMED;
} }
@Override @Override

View file

@ -4,11 +4,11 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter; import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer; import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
import com.mojang.math.Quaternion; import com.mojang.math.Quaternion;
public class OrientedType implements StructType<OrientedPart> { public class OrientedType implements StructType<OrientedPart> {
@ -35,7 +35,7 @@ public class OrientedType implements StructType<OrientedPart> {
@Override @Override
public FileResolution getInstanceShader() { public FileResolution getInstanceShader() {
return InstanceShaders.ORIENTED; return Components.Files.ORIENTED;
} }
@Override @Override

View file

@ -0,0 +1,45 @@
package com.jozufozu.flywheel.core.uniform;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.systems.RenderSystem;
public class FogProvider extends UniformProvider {
@Override
public int getSize() {
return 16 + 8 + 4;
}
public void update() {
if (buffer == null) {
return;
}
var color = RenderSystem.getShaderFogColor();
long ptr = MemoryUtil.memAddress(buffer);
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape().getIndex());
notifier.signalChanged();
}
@Override
public FileResolution getUniformShader() {
return Components.Files.FOG_UNIFORMS;
}
}

View file

@ -0,0 +1,131 @@
package com.jozufozu.flywheel.core.uniform;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicLongArray;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.platform.MemoryTracker;
public class UniformBuffer {
private static final int OFFSET_ALIGNMENT = GL32.glGetInteger(GL32.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
private static final int MAX_SIZE = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BLOCK_SIZE);
private static final int MAX_BINDINGS = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BUFFER_BINDINGS);
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
private static UniformBuffer instance;
private final List<Allocated> allocatedProviders;
public static UniformBuffer getInstance() {
if (instance == null) {
instance = new UniformBuffer();
}
return instance;
}
private final MappedGlBuffer buffer;
private final ByteBuffer data;
private final BitSet changedBytes;
private UniformBuffer() {
buffer = new MappedGlBuffer(GlBufferType.UNIFORM_BUFFER);
Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders();
var builder = ImmutableList.<Allocated>builder();
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = provider.getSize();
builder.add(new Allocated(provider, totalBytes, size, index));
totalBytes = align(totalBytes + size);
index++;
}
allocatedProviders = builder.build();
data = MemoryTracker.create(totalBytes);
changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) {
p.updatePtr(data);
}
}
public void sync() {
if (changedBytes.isEmpty()) {
return;
}
changedBytes.clear();
buffer.upload(data);
int handle = buffer.handle();
for (Allocated p : allocatedProviders) {
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size);
}
}
// https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number
private static int align(int numToRound) {
if (PO2_ALIGNMENT) {
return (numToRound + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT;
} else {
return ((numToRound + OFFSET_ALIGNMENT - 1) / OFFSET_ALIGNMENT) * OFFSET_ALIGNMENT;
}
}
private class Allocated implements UniformProvider.Notifier {
private final UniformProvider provider;
private final int offset;
private final int size;
private final int index;
private Allocated(UniformProvider provider, int offset, int size, int index) {
this.provider = provider;
this.offset = offset;
this.size = size;
this.index = index;
}
@Override
public void signalChanged() {
changedBytes.set(offset, offset + size);
}
private void updatePtr(ByteBuffer bufferBase) {
provider.updatePtr(MemoryUtil.memSlice(bufferBase, offset, size), this);
}
public UniformProvider provider() {
return provider;
}
public int offset() {
return offset;
}
public int size() {
return size;
}
public int index() {
return index;
}
}
}

View file

@ -0,0 +1,69 @@
package com.jozufozu.flywheel.core.uniform;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.uniform.UniformProvider;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.MatrixWrite;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
import net.minecraftforge.common.MinecraftForge;
public class ViewProvider extends UniformProvider {
public ViewProvider() {
MinecraftForge.EVENT_BUS.addListener(this::beginFrame);
}
public void beginFrame(BeginFrameEvent event) {
update(RenderContext.CURRENT);
}
@Override
public int getSize() {
return 4 * 16 + 16 + 4;
}
public void update(RenderContext context) {
if (buffer == null) {
return;
}
ClientLevel level = context.level();
int constantAmbientLight = level.effects()
.constantAmbientLight() ? 1 : 0;
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(level);
var camX = (float) (context.camX() - originCoordinate.getX());
var camY = (float) (context.camY() - originCoordinate.getY());
var camZ = (float) (context.camZ() - originCoordinate.getZ());
// don't want to mutate viewProjection
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation(-camX, -camY, -camZ);
long ptr = MemoryUtil.memAddress(buffer);
MatrixWrite.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutInt(ptr + 76, constantAmbientLight);
notifier.signalChanged();
}
@Override
public FileResolution getUniformShader() {
return Components.Files.VIEW_UNIFORMS;
}
}

View file

@ -4,6 +4,7 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder; import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
@ -40,7 +41,7 @@ public class BlockVertex implements VertexType {
@Override @Override
public FileResolution getLayoutShader() { public FileResolution getLayoutShader() {
return LayoutShaders.BLOCK; return Components.Files.BLOCK_LAYOUT;
} }
public BlockVertexListUnsafe.Shaded createReader(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) { public BlockVertexListUnsafe.Shaded createReader(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) {

View file

@ -1,6 +1,12 @@
package com.jozufozu.flywheel.core.vertex; package com.jozufozu.flywheel.core.vertex;
import com.jozufozu.flywheel.core.ComponentRegistry;
public class Formats { public class Formats {
public static final BlockVertex BLOCK = new BlockVertex(); public static final BlockVertex BLOCK = ComponentRegistry.register(new BlockVertex());
public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex(); public static final PosTexNormalVertex POS_TEX_NORMAL = ComponentRegistry.register(new PosTexNormalVertex());
public static void init() {
// noop
}
} }

View file

@ -1,31 +0,0 @@
package com.jozufozu.flywheel.core.vertex;
import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class LayoutShaders {
public static final BiConsumer<ErrorReporter, SourceFile> CHECK = SourceChecks.checkFunctionArity("flw_layoutVertex", 0);
public static final FileResolution BLOCK = create(ResourceUtil.subPath(Names.BLOCK, ".vert"));
public static final FileResolution POS_TEX_NORMAL = create(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"));
public static FileResolution create(ResourceLocation location) {
return FileResolution.get(location).validateWith(CHECK);
}
public static void init() {
}
public static class Names {
public static final ResourceLocation BLOCK = Flywheel.rl("layout/block");
public static final ResourceLocation POS_TEX_NORMAL = Flywheel.rl("layout/pos_tex_normal");
}
}

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
@ -30,6 +31,6 @@ public class PosTexNormalVertex implements VertexType {
@Override @Override
public FileResolution getLayoutShader() { public FileResolution getLayoutShader() {
return LayoutShaders.POS_TEX_NORMAL; return Components.Files.POS_TEX_NORMAL_LAYOUT;
} }
} }

View file

@ -0,0 +1,33 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.core.Components;
import net.minecraft.client.renderer.FogRenderer;
@Mixin(FogRenderer.class)
public class FogUpdateMixin {
@Inject(method = "setupNoFog", at = @At("TAIL"))
private static void onNoFog(CallbackInfo ci) {
flywheel$updateFog();
}
@Inject(method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V", remap = false, at = @At("TAIL"))
private static void onFog(CallbackInfo ci) {
flywheel$updateFog();
}
@Inject(method = "levelFogColor", at = @At("TAIL"))
private static void onFogColor(CallbackInfo ci) {
flywheel$updateFog();
}
private static void flywheel$updateFog() {
Components.FOG_PROVIDER.update();
}
}

View file

@ -2,6 +2,9 @@ package com.jozufozu.flywheel.util;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
/** /**
* @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin
* @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin * @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin
@ -14,4 +17,12 @@ public interface MatrixWrite {
void flywheel$writeUnsafe(long ptr); void flywheel$writeUnsafe(long ptr);
void flywheel$write(ByteBuffer buf); void flywheel$write(ByteBuffer buf);
static void write(Matrix4f matrix, ByteBuffer buf) {
((MatrixWrite) (Object) matrix).flywheel$write(buf);
}
static void writeUnsafe(Matrix4f matrix, long ptr) {
((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr);
}
} }

View file

@ -50,26 +50,6 @@ public class RenderMath {
return Math.sqrt(lengthSqr(x, y, z)); return Math.sqrt(lengthSqr(x, y, z));
} }
public static float rad(double angle) {
if (angle == 0) return 0;
return (float) (angle / 180 * Math.PI);
}
public static float deg(double angle) {
if (angle == 0) return 0;
return (float) (angle * 180 / Math.PI);
}
public static float angleLerp(double pct, double current, double target) {
return (float) (current + getShortestAngleDiff(current, target) * pct);
}
public static float getShortestAngleDiff(double current, double target) {
current = current % 360;
target = target % 360;
return (float) (((((target - current) % 360) + 540) % 360) - 180);
}
public static float diffuseLight(float x, float y, float z, boolean shaded) { public static float diffuseLight(float x, float y, float z, boolean shaded) {
if (!shaded) { if (!shaded) {
return 1f; return 1f;

View file

@ -11,8 +11,8 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier; import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -26,7 +26,7 @@ import net.minecraft.world.level.block.entity.BellBlockEntity;
public class BellInstance extends BlockEntityInstance<BellBlockEntity> implements DynamicInstance { public class BellInstance extends BlockEntityInstance<BellBlockEntity> implements DynamicInstance {
private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, new Material(Sheets.solidBlockSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT)); private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, new Material(Sheets.solidBlockSheet(), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
private final OrientedPart bell; private final OrientedPart bell;

View file

@ -12,8 +12,8 @@ import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier; import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart; import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
@ -25,7 +25,6 @@ import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.Material;
import net.minecraft.world.level.block.AbstractChestBlock; import net.minecraft.world.level.block.AbstractChestBlock;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.ChestBlock; import net.minecraft.world.level.block.ChestBlock;
@ -37,15 +36,14 @@ import net.minecraft.world.level.block.state.properties.ChestType;
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance { public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance {
private static final com.jozufozu.flywheel.api.material.Material CHEST_MATERIAL = new com.jozufozu.flywheel.api.material.Material(Sheets.chestSheet(), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT); private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), CHEST_MATERIAL)); private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), CHEST_MATERIAL));
private final OrientedPart body; private final OrientedPart body;
private final TransformedPart lid; private final TransformedPart lid;
private final Float2FloatFunction lidProgress; private final Float2FloatFunction lidProgress;
private final Material renderMaterial; private final TextureAtlasSprite sprite;
@NotNull @NotNull
private final ChestType chestType; private final ChestType chestType;
private final Quaternion baseRotation; private final Quaternion baseRotation;
@ -58,7 +56,7 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
Block block = blockState.getBlock(); Block block = blockState.getBlock();
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE; chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
renderMaterial = Sheets.chooseMaterial(blockEntity, chestType, isChristmas()); sprite = Sheets.chooseMaterial(blockEntity, chestType, isChristmas()).sprite();
body = baseInstance() body = baseInstance()
.setPosition(getInstancePosition()); .setPosition(getInstancePosition());
@ -126,14 +124,14 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
private OrientedPart baseInstance() { private OrientedPart baseInstance() {
return instancerManager.factory(StructTypes.ORIENTED) return instancerManager.factory(StructTypes.ORIENTED)
.model(BASE.apply(chestType, renderMaterial)) .model(BASE.apply(chestType, sprite))
.createInstance(); .createInstance();
} }
private TransformedPart lidInstance() { private TransformedPart lidInstance() {
return instancerManager.factory(StructTypes.TRANSFORMED) return instancerManager.factory(StructTypes.TRANSFORMED)
.model(LID.apply(chestType, renderMaterial)) .model(LID.apply(chestType, sprite))
.createInstance(); .createInstance();
} }

View file

@ -5,12 +5,11 @@ import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance; import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier; import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.Models; import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
@ -19,9 +18,7 @@ import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.entity.vehicle.AbstractMinecart; import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.RenderShape;
@ -30,8 +27,7 @@ import net.minecraft.world.phys.Vec3;
public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements DynamicInstance, TickableInstance { public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements DynamicInstance, TickableInstance {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png"); private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, Materials.MINECART);
private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, new Material(RenderType.entitySolid(MINECART_LOCATION), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT));
private final PoseStack stack = new PoseStack(); private final PoseStack stack = new PoseStack();

View file

@ -7,11 +7,10 @@ import java.util.function.Function;
import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier; import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart; import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.material.MaterialShaders;
import com.jozufozu.flywheel.core.structs.StructTypes; import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart; import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -21,7 +20,6 @@ import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -31,9 +29,8 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntity> implements DynamicInstance { public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntity> implements DynamicInstance {
private static final Material SHULKER_BOX_MATERIAL = new Material(RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT); private static final Function<TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize(it -> new BasicModelSupplier(() -> makeBaseModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize(it -> new BasicModelSupplier(() -> makeBaseModel(it), SHULKER_BOX_MATERIAL)); private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), SHULKER_BOX_MATERIAL));
private final TextureAtlasSprite texture; private final TextureAtlasSprite texture;

View file

@ -1,9 +1,7 @@
#use "flywheel:api/vertex.glsl" #use "flywheel:api/vertex.glsl"
#use "flywheel:util/fog.glsl" #use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
uniform mat4 uViewProjection; #use "flywheel:uniform/view.glsl"
uniform vec3 uCameraPos;
uniform int uFogShape;
void flw_contextVertex() { void flw_contextVertex() {
// TODO: remove this // TODO: remove this
@ -11,7 +9,7 @@ void flw_contextVertex() {
flw_vertexColor = vec4(flw_vertexNormal, 1.0); flw_vertexColor = vec4(flw_vertexNormal, 1.0);
#endif #endif
flw_distance = fog_distance(flw_vertexPos.xyz, uCameraPos, uFogShape); flw_distance = fog_distance(flw_vertexPos.xyz, flw_cameraPos.xyz, flw_fogShape);
gl_Position = uViewProjection * flw_vertexPos; gl_Position = flw_viewProjection * flw_vertexPos;
flw_vertexNormal = normalize(flw_vertexNormal); flw_vertexNormal = normalize(flw_vertexNormal);
} }

View file

@ -1,8 +1,6 @@
#use "flywheel:api/fragment.glsl" #use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl" #use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uCrumblingTex; uniform sampler2D uCrumblingTex;
@ -37,9 +35,9 @@ void flw_contextFragment() {
#endif #endif
#ifdef COLOR_FOG #ifdef COLOR_FOG
color = linear_fog(color, flw_distance, uFogRange.x, uFogRange.y, uFogColor); color = linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
#elif defined(FADE_FOG) #elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, uFogRange.x, uFogRange.y); color = linear_fog_fade(color, flw_distance, flw_fogRange.x, flw_fogRange.y);
#endif #endif
fragColor = color; fragColor = color;

View file

@ -1,5 +1,6 @@
#use "flywheel:api/fragment.glsl" #use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl" #use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
// optimize discard usage // optimize discard usage
#ifdef ALPHA_DISCARD #ifdef ALPHA_DISCARD
@ -8,9 +9,6 @@ layout (depth_greater) out float gl_FragDepth;
#endif #endif
#endif #endif
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uBlockAtlas; uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap; uniform sampler2D uLightMap;
@ -28,9 +26,9 @@ void flw_contextFragment() {
#endif #endif
#ifdef COLOR_FOG #ifdef COLOR_FOG
color = linear_fog(color, flw_distance, uFogRange.x, uFogRange.y, uFogColor); color = linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
#elif defined(FADE_FOG) #elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, uFogRange.x, uFogRange.y); color = linear_fog_fade(color, flw_distance, flw_fogRange.x, flw_fogRange.y);
#endif #endif
fragColor = color; fragColor = color;

View file

@ -1,13 +1,12 @@
#use "flywheel:api/vertex.glsl" #use "flywheel:api/vertex.glsl"
#use "flywheel:util/diffuse.glsl" #use "flywheel:util/diffuse.glsl"
#use "flywheel:uniform/view.glsl"
uniform int uConstantAmbientLight;
void flw_materialVertex() { void flw_materialVertex() {
flw_vertexNormal = normalize(flw_vertexNormal); flw_vertexNormal = normalize(flw_vertexNormal);
float diffuseFactor; float diffuseFactor;
if (uConstantAmbientLight == 1) { if (flw_constantAmbientLight == 1) {
diffuseFactor = diffuseNether(flw_vertexNormal); diffuseFactor = diffuseNether(flw_vertexNormal);
} else { } else {
diffuseFactor = diffuse(flw_vertexNormal); diffuseFactor = diffuse(flw_vertexNormal);

View file

@ -0,0 +1,5 @@
layout(std140, binding = 1) uniform flw_fog {
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;
};

View file

@ -0,0 +1,6 @@
layout(std140, binding = 0) uniform flw_view {
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;
};

View file

@ -14,6 +14,7 @@
"ClientLevelMixin", "ClientLevelMixin",
"EntityTypeMixin", "EntityTypeMixin",
"FixFabulousDepthMixin", "FixFabulousDepthMixin",
"FogUpdateMixin",
"FrustumMixin", "FrustumMixin",
"GlStateManagerMixin", "GlStateManagerMixin",
"InstanceAddMixin", "InstanceAddMixin",