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.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.Contexts;
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.*;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
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.structs.InstanceShaders;
import com.jozufozu.flywheel.core.vertex.LayoutShaders;
import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances;
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
import com.mojang.logging.LogUtils;
import net.minecraft.commands.synchronization.ArgumentTypes;
@ -112,10 +103,7 @@ public class Flywheel {
// forgeEventBus.addListener(ExampleEffect::tick);
// forgeEventBus.addListener(ExampleEffect::onReload);
LayoutShaders.init();
InstanceShaders.init();
Contexts.init();
MaterialShaders.init();
Components.init();
GameStateRegistry.register(NormalDebugStateProvider.INSTANCE);

View file

@ -1,9 +1,17 @@
package com.jozufozu.flywheel.api.material;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
import net.minecraft.client.renderer.RenderType;
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");
}
sources.postResolve();
FileResolution.checkAll(errorReporter);
Backend.LOGGER.info("Loaded all shader sources.");
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}.
*/
public interface Factory<P extends GlProgram> {
public interface Factory {
@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.entity.EntityInstanceManager;
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.event.BeginFrameEvent;
import com.jozufozu.flywheel.util.ClientLevelExtension;
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
@ -39,7 +38,7 @@ public class InstanceWorld {
public static InstanceWorld create(LevelAccessor level) {
var engine = switch (Backend.getBackendType()) {
case INSTANCING -> new InstancingEngine<>(Contexts.WORLD);
case INSTANCING -> new InstancingEngine(Components.WORLD);
case BATCHING -> new BatchingEngine();
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.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -131,4 +132,8 @@ public class InstancedRenderDispatcher {
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.List;
import java.util.Map;
import java.util.SortedSet;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import com.jozufozu.flywheel.api.InstancedPart;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
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.Engine;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
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.core.Contexts;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry;
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.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.WorldProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.Textures;
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.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.util.Mth;
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;
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ProgramCompiler<P> context;
protected final ContextShader context;
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 instanceCount;
public InstancingEngine(ProgramCompiler<P> context) {
public InstancingEngine(ContextShader context) {
this.context = context;
this.instanceManagers = new WeakHashSet<>();
@ -98,16 +74,8 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
@Override
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()) {
render(renderType, camX, camY, camZ, vp, context.level());
render(renderType);
}
}
@ -117,18 +85,10 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
return;
}
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);
render(type, camX, camY, camZ, vp, context.level());
render(type);
}
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
protected void render(RenderType type) {
vertexCount = 0;
instanceCount = 0;
@ -138,16 +98,16 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
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();
Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
StateSnapshot state = GameStateRegistry.takeSnapshot();
for (var entry : Multimaps.asMap(multimap).entrySet()) {
for (var entry : multimap.asMap().entrySet()) {
var shader = entry.getKey();
var drawCalls = entry.getValue();
@ -157,7 +117,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
continue;
}
setup(shader, coreShaderInfo, camX, camY, camZ, viewProjection, level, state);
setup(shader, coreShaderInfo, state);
for (var drawCall : drawCalls) {
drawCall.render();
@ -168,21 +128,18 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
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();
FileResolution instanceShader = desc.instance()
.getInstanceShader();
Material material = desc.material();
P program = context.getProgram(new ProgramCompiler.Context(vertexType, instanceShader,
material.vertexShader(), material.fragmentShader(), coreShaderInfo.getAdjustedAlphaDiscard(),
coreShaderInfo.fogType(), ctx));
var program = ProgramCompiler.INSTANCE.getProgram(new ProgramCompiler.Context(vertexType, material,
instanceShader, context, coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(), ctx));
program.bind();
program.uploadUniforms(camX, camY, camZ, viewProjection, level);
return program;
UniformBuffer.getInstance().sync();
}
public void clearAll() {
@ -250,131 +207,4 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
info.add("Vertices: " + vertexCount);
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.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.ModelSupplier;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.NonNullSupplier;
import net.minecraft.client.renderer.RenderType;
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 final Lazy<Mesh> supplier;
public BasicModelSupplier(NonNullSupplier<Mesh> supplier) {
this(supplier, DEFAULT_MATERIAL);
this(supplier, Materials.DEFAULT);
}
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.shader.ShaderConstants;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndex;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile;
/**
* Handles compilation and deletion of fragment shaders.
*/
public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
private final FileResolution contextShader;
private final GLSLVersion glslVersion;
public FragmentCompiler(FileResolution contextShader, GLSLVersion glslVersion) {
this.contextShader = contextShader;
this.glslVersion = glslVersion;
public FragmentCompiler() {
}
@Override
protected GlShader _create(Context key) {
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);
finalSource.append('\n');
FileIndex index = new FileIndex();
var ctx = new CompilationContext();
// MATERIAL
SourceFile materialShader = key.materialShader;
materialShader.generateFinalSource(index, finalSource);
finalSource.append(materialShader.generateFinalSource(ctx));
// CONTEXT
SourceFile contextShaderSource = contextShader.getFile();
contextShaderSource.generateFinalSource(index, finalSource);
SourceFile contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(ctx));
// MAIN
@ -52,7 +47,7 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name), shaderConstants);
} 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 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() {
ShaderConstants shaderConstants = ctx.getShaderConstants();

View file

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

View file

@ -3,8 +3,8 @@ package com.jozufozu.flywheel.core.compile;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.api.material.Material;
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.core.CoreShaderInfoMap;
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.
* </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 FragmentCompiler fragmentCompiler;
public ProgramCompiler(GlProgram.Factory<P> factory, VertexCompiler vertexCompiler, FragmentCompiler fragmentCompiler) {
this.factory = factory;
this.vertexCompiler = vertexCompiler;
this.fragmentCompiler = fragmentCompiler;
public ProgramCompiler() {
this.vertexCompiler = new VertexCompiler();
this.fragmentCompiler = new FragmentCompiler();
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.
*
* @param ctx The context of compilation.
* @return A compiled GlProgram.
*/
public P getProgram(Context ctx) {
public GlProgram getProgram(ProgramCompiler.Context ctx) {
return super.get(ctx);
}
@ -68,17 +55,28 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramCompil
}
@Override
protected P _create(Context ctx) {
protected GlProgram _create(ProgramCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile
return new ProgramAssembler(ctx.instanceShader().getFileLoc())
.attachShader(vertexCompiler.get(new VertexCompiler.Context(ctx.vertexType(), ctx.instanceShader().getFile(), ctx.vertexMaterialShader().getFile(), ctx.ctx())))
.attachShader(fragmentCompiler.get(new FragmentCompiler.Context(ctx.fragmentMaterialShader().getFile(), ctx.alphaDiscard(), ctx.fogType(), ctx.ctx())))
Material material = ctx.material;
StateSnapshot snapshot = 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()
.build(this.factory);
.build(contextShader.factory());
}
@Override
protected void _destroy(P value) {
protected void _destroy(GlProgram value) {
value.delete();
}
@ -89,15 +87,16 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramCompil
/**
* Represents the entire context of a program's usage.
*
* @param vertexType The vertexType the program should be adapted for.
* @param instanceShader The instance shader to use.
* @param vertexMaterialShader The vertex material shader to use.
* @param fragmentMaterialShader The fragment material shader to use.
* @param alphaDiscard Alpha threshold below which pixels are discarded.
* @param fogType Which type of fog should be applied.
* @param ctx A snapshot of the game state.
* @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 contextShader The context shader to use.
* @param alphaDiscard Alpha threshold below which pixels are discarded.
* @param fogType Which type of fog should be applied.
* @param ctx A snapshot of the game state.
*/
public record Context(VertexType vertexType, FileResolution instanceShader, FileResolution vertexMaterialShader,
FileResolution fragmentMaterialShader, float alphaDiscard, CoreShaderInfoMap.CoreShaderInfo.FogType fogType, StateSnapshot ctx) {
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
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 com.jozufozu.flywheel.core.source.FileIndex;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
public class ShaderCompilationException extends ShaderLoadingException {
@ -16,11 +16,11 @@ public class ShaderCompilationException extends ShaderLoadingException {
this.shaderHandle = shaderHandle;
}
public ShaderLoadingException withErrorLog(FileIndex fileIndex) {
public ShaderLoadingException withErrorLog(CompilationContext ctx) {
if (this.shaderHandle == -1)
return this;
this.errors = fileIndex.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle));
this.errors = ctx.parseErrors(GL20.glGetShaderInfoLog(this.shaderHandle));
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.ShaderType;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndex;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.ShaderField;
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.util.Pair;
@ -21,30 +18,26 @@ import com.jozufozu.flywheel.util.Pair;
* Handles compilation and deletion of vertex shaders.
*/
public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
private final FileResolution contextShader;
private final GLSLVersion glslVersion;
public VertexCompiler(FileResolution contextShader, GLSLVersion glslVersion) {
this.contextShader = contextShader;
this.glslVersion = glslVersion;
public VertexCompiler() {
}
@Override
protected GlShader _create(Context key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(glslVersion, ShaderType.VERTEX));
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
var shaderConstants = key.ctx.getShaderConstants();
shaderConstants.writeInto(finalSource);
finalSource.append('\n');
var index = new FileIndex();
var index = new CompilationContext();
// LAYOUT
var layoutShader = key.vertexType.getLayoutShader().getFile();
layoutShader.generateFinalSource(index, finalSource);
finalSource.append(layoutShader.generateFinalSource(index));
// INSTANCE
@ -62,17 +55,17 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
int newLocation = location + attributeBaseIndex;
replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
}
instanceShader.generateFinalSource(index, finalSource, replacements);
finalSource.append(instanceShader.generateFinalSource(index, replacements));
// MATERIAL
var materialShader = key.materialShader;
materialShader.generateFinalSource(index, finalSource);
finalSource.append(materialShader.generateFinalSource(index));
// CONTEXT
var contextShaderSource = contextShader.getFile();
contextShaderSource.generateFinalSource(index, finalSource);
var contextShaderSource = key.contextShader;
finalSource.append(contextShaderSource.generateFinalSource(index));
// MAIN
@ -104,8 +97,9 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
* @param vertexType The vertex type to use.
* @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
* @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.SerialTaskEngine;
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.event.ReloadRenderersEvent;
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;
public CrumblingEngine() {
super(Contexts.CRUMBLING);
super(Components.CRUMBLING);
}
@Override
protected void render(RenderType type, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
protected void render(RenderType type) {
if (!type.affectsCrumbling()) {
return;
}
@ -172,7 +172,7 @@ public class CrumblingRenderer {
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;
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.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;
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 uLightMap;
public WorldProgram(ResourceLocation name, int handle) {
super(name, handle);
fog = new WorldFog(this);
bind();
registerSamplers();
unbind();
@ -39,48 +22,4 @@ public class WorldProgram extends GlProgram {
uBlockAtlas = setSamplerBinding("uBlockAtlas", 0);
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.span.Span;
public class FileIndex {
public class CompilationContext {
public final List<SourceFile> files = new ArrayList<>();
/**
@ -27,7 +27,7 @@ public class FileIndex {
return size;
}
public boolean exists(SourceFile sourceFile) {
public boolean contains(SourceFile sourceFile) {
return files.contains(sourceFile);
}

View file

@ -24,6 +24,7 @@ import net.minecraft.resources.ResourceLocation;
public class FileResolution {
private static final Map<ResourceLocation, FileResolution> ALL = new HashMap<>();
private static final Map<ResourceLocation, FileResolution> WEAK = new HashMap<>();
private static boolean tooLate = false;
/**
@ -56,34 +57,65 @@ 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.
*/
public static void run(ErrorReporter errorReporter, SourceFinder sources) {
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;
}
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);
if (file == null) {
ErrorBuilder builder = errorReporter.error(String.format("could not find source for file %s", fileLoc));
for (Span location : neededAt) {
builder.pointAtFile(location.getSourceFile())
.pointAt(location, 1);
}
} else {
runChecks(errorReporter);
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));
for (Span location : neededAt) {
builder.pointAtFile(location.getSourceFile())
.pointAt(location, 1);
}
}
private void runChecks(ErrorReporter errorReporter) {
for (var check : checks) {
check.accept(errorReporter, file);

View file

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Lists;
@ -29,14 +30,8 @@ public class ShaderSources implements SourceFinder {
public final Index index;
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)) {
String source = StringUtil.readToString(resource.getInputStream());
@ -51,9 +46,25 @@ public class ShaderSources implements SourceFinder {
index = new Index(shaderSources);
}
public void postResolve() {
for (SourceFile file : shaderSources.values()) {
file.postResolve();
}
}
@Override
@Nullable
public SourceFile findSource(ResourceLocation 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;
private final List<Span> elisions;
// POST-RESOLUTION
private List<Import> flattenedImports;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent;
this.name = name;
@ -72,6 +75,37 @@ public class SourceFile {
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) {
int begin = lines.getLineStart(line);
int end = begin + lines.getLine(line).length();
@ -95,16 +129,23 @@ public class SourceFile {
* @param name The name of the struct to find.
* @return null if no definition matches the name.
*/
public Optional<ShaderStruct> findStruct(CharSequence name) {
ShaderStruct struct = structs.get(name.toString());
public Optional<ShaderStruct> findStruct(String name) {
ShaderStruct struct = structs.get(name);
if (struct != null) return Optional.of(struct);
for (Import include : imports) {
Optional<ShaderStruct> externalStruct = include.getOptional()
.flatMap(file -> file.findStruct(name));
for (Import include : flattenedImports) {
var file = include.getFile();
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();
@ -116,16 +157,23 @@ public class SourceFile {
* @param name The name of the function to find.
* @return Optional#empty() if no definition matches the name.
*/
public Optional<ShaderFunction> findFunction(CharSequence name) {
ShaderFunction local = functions.get(name.toString());
public Optional<ShaderFunction> findFunction(String name) {
ShaderFunction local = functions.get(name);
if (local != null) return Optional.of(local);
for (Import include : imports) {
Optional<ShaderFunction> external = include.getOptional()
.flatMap(file -> file.findFunction(name));
var file = include.getFile();
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();
@ -135,40 +183,46 @@ public class SourceFile {
return "#use " + '"' + name + '"';
}
public void generateFinalSource(FileIndex env, StringBuilder source) {
generateFinalSource(env, source, Collections.emptyList());
public String generateFinalSource(CompilationContext env) {
return generateFinalSource(env, Collections.emptyList());
}
public void generateFinalSource(FileIndex env, StringBuilder source, List<Pair<Span, String>> replacements) {
for (Import include : imports) {
public String generateFinalSource(CompilationContext env, List<Pair<Span, String>> replacements) {
var out = new StringBuilder();
for (Import include : flattenedImports) {
SourceFile file = include.getFile();
if (file != null && !env.exists(file)) {
file.generateFinalSource(env, source, replacements);
if (file == null || env.contains(file)) {
continue;
}
out.append(file.generateLineHeader(env))
.append(file.replaceAndElide(replacements));
}
source.append("#line ")
.append(0)
.append(' ')
.append(env.getFileID(this))
.append(" // ")
.append(name)
.append('\n');
out.append(this.generateLineHeader(env))
.append(this.replaceAndElide(replacements));
var replacementsAndElisions = new ArrayList<>(replacements);
for (Span elision : elisions) {
replacementsAndElisions.add(Pair.of(elision, ""));
}
return out.toString();
}
source.append(this.replace(replacementsAndElisions));
source.append('\n');
private String generateLineHeader(CompilationContext env) {
return "#line " + 0 + ' ' + env.getFileID(this) + " // " + name + '\n';
}
public String printSource() {
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) {
StringBuilder out = new StringBuilder();
@ -276,7 +330,7 @@ public class SourceFile {
Matcher uses = Import.PATTERN.matcher(source);
Set<String> importedFiles = new HashSet<>();
List<Import> imports = new ArrayList<>();
var imports = ImmutableList.<Import>builder();
while (uses.find()) {
Span use = Span.fromMatcher(this, uses);
@ -284,17 +338,14 @@ public class SourceFile {
String fileName = file.get();
if (importedFiles.add(fileName)) {
// FIXME: creating imports after the first resource reload crashes the game
var checked = Import.create(errorReporter, use, file);
if (checked != null) {
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 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.SourceLines;
import com.jozufozu.flywheel.core.source.error.lines.ErrorLine;
@ -45,7 +45,7 @@ public class ErrorBuilder {
}
@Nullable
public static ErrorBuilder fromLogLine(FileIndex env, String s) {
public static ErrorBuilder fromLogLine(CompilationContext env, String s) {
Matcher matcher = ERROR_LINE.matcher(s);
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);
private final FileResolution resolution;
public final FileResolution resolution;
protected Import(Span self, FileResolution resolution, Span file) {
super(self);
@ -34,11 +34,7 @@ public class Import extends AbstractShaderElement {
return null;
}
return new Import(self, FileResolution.get(fileLocation), file);
}
public FileResolution getResolution() {
return resolution;
return new Import(self, FileResolution.weak(fileLocation), file);
}
public Optional<SourceFile> getOptional() {

View file

@ -131,14 +131,14 @@ public abstract class Span implements CharSequence, Comparable<Span> {
if (isErr()) {
return Optional.empty();
}
return in.findStruct(this);
return in.findStruct(this.toString());
}
public Optional<ShaderFunction> findFunction() {
if (isErr()) {
return Optional.empty();
}
return in.findFunction(this);
return in.findFunction(this.toString());
}
@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;
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.TransformedType;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedType;
public class StructTypes {
public static final StructType<TransformedPart> TRANSFORMED = new TransformedType();
public static final StructType<OrientedPart> ORIENTED = new OrientedType();
public static final StructType<TransformedPart> TRANSFORMED = ComponentRegistry.register(new TransformedType());
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.StructWriter;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
public class TransformedType implements StructType<TransformedPart> {
@ -34,7 +34,7 @@ public class TransformedType implements StructType<TransformedPart> {
@Override
public FileResolution getInstanceShader() {
return InstanceShaders.TRANSFORMED;
return Components.Files.TRANSFORMED;
}
@Override

View file

@ -4,11 +4,11 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.struct.StructType;
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.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.structs.InstanceShaders;
import com.mojang.math.Quaternion;
public class OrientedType implements StructType<OrientedPart> {
@ -35,7 +35,7 @@ public class OrientedType implements StructType<OrientedPart> {
@Override
public FileResolution getInstanceShader() {
return InstanceShaders.ORIENTED;
return Components.Files.ORIENTED;
}
@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.VertexType;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
@ -40,7 +41,7 @@ public class BlockVertex implements VertexType {
@Override
public FileResolution getLayoutShader() {
return LayoutShaders.BLOCK;
return Components.Files.BLOCK_LAYOUT;
}
public BlockVertexListUnsafe.Shaded createReader(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) {

View file

@ -1,6 +1,12 @@
package com.jozufozu.flywheel.core.vertex;
import com.jozufozu.flywheel.core.ComponentRegistry;
public class Formats {
public static final BlockVertex BLOCK = new BlockVertex();
public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex();
public static final BlockVertex BLOCK = ComponentRegistry.register(new BlockVertex());
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 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.CommonItems;
import com.jozufozu.flywheel.core.source.FileResolution;
@ -30,6 +31,6 @@ public class PosTexNormalVertex implements VertexType {
@Override
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 com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
/**
* @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin
* @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin
@ -14,4 +17,12 @@ public interface MatrixWrite {
void flywheel$writeUnsafe(long ptr);
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));
}
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) {
if (!shaded) {
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.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Components;
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.oriented.OrientedPart;
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 {
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;

View file

@ -12,8 +12,8 @@ import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
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.model.TransformedPart;
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.client.renderer.Sheets;
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.Block;
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 {
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, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), CHEST_MATERIAL));
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), CHEST_MATERIAL));
private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat), Materials.CHEST));
private final OrientedPart body;
private final TransformedPart lid;
private final Float2FloatFunction lidProgress;
private final Material renderMaterial;
private final TextureAtlasSprite sprite;
@NotNull
private final ChestType chestType;
private final Quaternion baseRotation;
@ -58,7 +56,7 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
Block block = blockState.getBlock();
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()
.setPosition(getInstancePosition());
@ -126,14 +124,14 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Block
private OrientedPart baseInstance() {
return instancerManager.factory(StructTypes.ORIENTED)
.model(BASE.apply(chestType, renderMaterial))
.model(BASE.apply(chestType, sprite))
.createInstance();
}
private TransformedPart lidInstance() {
return instancerManager.factory(StructTypes.TRANSFORMED)
.model(LID.apply(chestType, renderMaterial))
.model(LID.apply(chestType, sprite))
.createInstance();
}

View file

@ -5,12 +5,11 @@ import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.InstancerManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
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.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.Models;
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.structs.StructTypes;
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.math.Vector3f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
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 {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, new Material(RenderType.entitySolid(MINECART_LOCATION), MaterialShaders.SHADED_VERTEX, MaterialShaders.DEFAULT_FRAGMENT));
private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, Materials.MINECART);
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.InstancerManager;
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.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
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.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -21,7 +20,6 @@ import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import net.minecraft.Util;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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 {
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), SHULKER_BOX_MATERIAL));
private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), SHULKER_BOX_MATERIAL));
private static final Function<TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize(it -> new BasicModelSupplier(() -> makeBaseModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), Materials.SHULKER));
private final TextureAtlasSprite texture;

View file

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

View file

@ -1,8 +1,6 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
uniform vec2 uFogRange;
uniform vec4 uFogColor;
#use "flywheel:uniform/fog.glsl"
uniform sampler2D uCrumblingTex;
@ -37,9 +35,9 @@ void flw_contextFragment() {
#endif
#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)
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
fragColor = color;

View file

@ -1,5 +1,6 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
// optimize discard usage
#ifdef ALPHA_DISCARD
@ -8,9 +9,6 @@ layout (depth_greater) out float gl_FragDepth;
#endif
#endif
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
@ -28,9 +26,9 @@ void flw_contextFragment() {
#endif
#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)
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
fragColor = color;

View file

@ -1,13 +1,12 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/diffuse.glsl"
uniform int uConstantAmbientLight;
#use "flywheel:uniform/view.glsl"
void flw_materialVertex() {
flw_vertexNormal = normalize(flw_vertexNormal);
float diffuseFactor;
if (uConstantAmbientLight == 1) {
if (flw_constantAmbientLight == 1) {
diffuseFactor = diffuseNether(flw_vertexNormal);
} else {
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",
"EntityTypeMixin",
"FixFabulousDepthMixin",
"FogUpdateMixin",
"FrustumMixin",
"GlStateManagerMixin",
"InstanceAddMixin",