From 54f57834898144557492b148e9d34ea7f7b61d3e Mon Sep 17 00:00:00 2001 From: JozsefA Date: Wed, 28 Apr 2021 00:01:26 -0700 Subject: [PATCH] More versatile ui/settings - The channel mask still needs work - Add contrast filter --- .../resources/assets/create/lang/en_us.json | 13 +- .../projector/ChromaticProjectorInstance.java | 2 +- .../projector/ChromaticProjectorScreen.java | 112 +++++++++++++++--- .../ChromaticProjectorTileEntity.java | 74 +++++++++--- .../curiosities/projector/ColorEffect.java | 3 +- .../projector/ConfigureProjectorPacket.java | 72 +++++++++-- .../curiosities/projector/FilterStep.java | 2 +- .../create/foundation/gui/AllGuiTextures.java | 2 +- .../create/foundation/gui/AllIcons.java | 17 +-- .../foundation/gui/widgets/IconButton.java | 3 + .../foundation/render/backend/RenderUtil.java | 12 +- .../render/backend/effects/ColorMatrices.java | 33 ++++-- .../backend/effects/EffectsHandler.java | 11 +- .../render/backend/effects/FilterSphere.java | 92 +++++--------- .../create/flywheel/shaders/area_effect.frag | 32 +++-- .../assets/create/textures/gui/icons.png | Bin 2923 -> 4934 bytes .../assets/create/textures/gui/projector.png | Bin 3428 -> 4461 bytes 17 files changed, 336 insertions(+), 144 deletions(-) diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index b266bf403..b43e08e8e 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -1154,8 +1154,17 @@ "create.gui.chromatic_projector.filter.saturate": "Saturate", "create.gui.chromatic_projector.filter.hue_shift": "Hue shift", "create.gui.chromatic_projector.filter.darken": "Darken", + "create.gui.chromatic_projector.filter.contrast": "Contrast", "create.gui.chromatic_projector.filter.end": "End", "create.gui.chromatic_projector.filter": "Filter", + "create.gui.chromatic_projector.surface": "Surface", + "create.gui.chromatic_projector.field": "Field", + "create.gui.chromatic_projector.strength": "Strength", + "create.gui.chromatic_projector.radius": "Radius", + "create.gui.chromatic_projector.feather": "Feather", + "create.gui.chromatic_projector.density": "Density", + "create.gui.chromatic_projector.fade": "Fade", + "create.gui.chromatic_projector.blend": "Blend", "_": "->------------------------] Subtitles [------------------------<-", "create.subtitle.cogs": "Cogwheels rumble", "create.subtitle.slime_added": "Slime squishes", @@ -1163,8 +1172,8 @@ "create.subtitle.mechanical_press_activation_belt": "Mechanical Press bonks", "create.subtitle.blockzapper_confirm": "Affirmative ding", "create.subtitle.depot_slide": "Item slides", - "create.subtitle.blockzapper_place": "Blockzapper zaps", - "create.subtitle.blaze_munch": "Blaze Burner munches", + "create.subtitle.blockzapper_place": "Blockzapper zaps", + "create.subtitle.blaze_munch": "Blaze Burner munches", "create.subtitle.schematicannon_launch_block": "Schematicannon fires", "create.subtitle.funnel_flap": "Funnel Flaps", "create.subtitle.schematicannon_finish": "Schematicannon dings", diff --git a/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorInstance.java b/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorInstance.java index 3394a08c3..ea7aa760a 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorInstance.java +++ b/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorInstance.java @@ -13,7 +13,7 @@ public class ChromaticProjectorInstance extends TileEntityInstance stages; + private final Vector stages; private Vector> inputs; @@ -39,6 +41,15 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { private ScrollInput feather; private ScrollInput fade; + private IconButton blend; + + private ScrollInput strength; + private IconButton fieldEffect; + + private IconButton rChannel; + private IconButton gChannel; + private IconButton bChannel; + public ChromaticProjectorScreen(ChromaticProjectorTileEntity te) { this.tile = te; this.stages = te.stages; @@ -62,35 +73,73 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { for (int row = 0; row < stages.size(); row++) initInputsOfRow(row); + int guiBottom = guiTop + background.height; + int guiRight = guiLeft + background.width; confirmButton = - new IconButton(guiLeft + background.width - 33, guiTop + background.height - 24, AllIcons.I_CONFIRM); + new IconButton(guiRight - 33, guiBottom - 26, AllIcons.I_CONFIRM); widgets.add(confirmButton); - int xRight = guiLeft + 53; - int xLeft = guiLeft + 93; - int yTop = guiTop + 117; - int yBottom = guiTop + 139; + initEffectSettings(); + initMetaSettings(); + } - radius = new ScrollInput(xRight, yTop, 28, 18) - .titled(new StringTextComponent("Radius")) + private void initMetaSettings() { + int guiBottom = guiTop + background.height; + int y = guiBottom - 23; + + blend = new IconButton(guiLeft + 16, y, AllIcons.I_FX_BLEND); + blend.setToolTip(Lang.translate("gui.chromatic_projector.blend")); + + int channelX = guiLeft + 39; + rChannel = new IconButton(channelX, y, AllIcons.I_FX_BLEND); + rChannel.setToolTip(new StringTextComponent("R")); + channelX += 18; + gChannel = new IconButton(channelX, y, AllIcons.I_FX_BLEND); + gChannel.setToolTip(new StringTextComponent("G")); + channelX += 18; + bChannel = new IconButton(channelX, y, AllIcons.I_FX_BLEND); + bChannel.setToolTip(new StringTextComponent("B")); + + fieldEffect = new IconButton(guiLeft + 135, y, tile.field ? AllIcons.I_FX_FIELD_ON : AllIcons.I_FX_FIELD_OFF); + fieldEffect.setToolTip(Lang.translate("gui.chromatic_projector.field")); + + strength = new ScrollInput(guiLeft + 159, y, 25, 18) + .titled(Lang.translate("gui.chromatic_projector.strength")) + .withStepFunction(ctx -> step(ctx, 5)) + .calling(tile::setStrength) + .withRange(0, 101) + .setState((int) (tile.strength * 100)); + + Collections.addAll(widgets, blend, rChannel, gChannel, bChannel, fieldEffect, strength); + } + + private void initEffectSettings() { + int x = guiLeft + 188; + int y = guiTop + 40; + + radius = new ScrollInput(x, y, 28, 18) + .titled(Lang.translate("gui.chromatic_projector.radius")) .withStepFunction(ctx -> step(ctx, 2)) .calling(tile::setRadius) .withRange(0, 201) .setState((int) (tile.radius * 2)); - feather = new ScrollInput(xRight, yBottom, 28, 18) - .titled(new StringTextComponent("Feather")) + y += 22; + feather = new ScrollInput(x, y, 28, 18) + .titled(Lang.translate("gui.chromatic_projector.feather")) .withStepFunction(ctx -> step(ctx, 5)) .calling(tile::setFeather) .withRange(0, 201) .setState((int) (tile.feather * 10)); - density = new ScrollInput(xLeft, yTop, 28, 18) - .titled(new StringTextComponent("Density")) + y += 22; + density = new ScrollInput(x, y, 28, 18) + .titled(Lang.translate("gui.chromatic_projector.density")) .withStepFunction(ctx -> step(ctx, 10)) .calling(tile::setDensity) .withRange(0, 401) .setState((int) (tile.density * 100)); - fade = new ScrollInput(xLeft, yBottom, 28, 18) - .titled(new StringTextComponent("Fade")) + y += 22; + fade = new ScrollInput(x, y, 28, 18) + .titled(Lang.translate("gui.chromatic_projector.fade")) .withStepFunction(ctx -> step(ctx, 1)) .calling(tile::setFade) .withRange(0, 51) @@ -112,7 +161,7 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { ScrollInput type = new SelectionScrollInput(x, y + rowHeight * row, 86, 18) .forOptions(ColorEffect.getOptions()) - .calling(state -> instructionUpdated(row, state)) + .calling(state -> stageUpdated(row, state)) .setState(filter.filter.id) .titled(Lang.translate("gui.chromatic_projector.filter")); ScrollInput value = @@ -174,6 +223,8 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { renderScroll(matrixStack, feather, 10f); renderScroll(matrixStack, fade, 10f); + renderScroll(matrixStack, strength, 100f); + textRenderer.drawWithShadow(matrixStack, title, guiLeft - 3 + (background.width - textRenderer.getWidth(title)) / 2, guiTop + 3, 0xffffff); @@ -188,7 +239,7 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { String text = String.valueOf(input.getState() / divisor); int stringWidth = textRenderer.getStringWidth(text); - textRenderer.drawWithShadow(matrixStack, text, input.x + (12 - stringWidth / 2), input.y + 5, 0xFFFFEE); + textRenderer.drawWithShadow(matrixStack, text, input.x + 2, input.y + 5, 0xFFFFEE); } private void label(MatrixStack matrixStack, int x, int y, ITextComponent text) { @@ -204,7 +255,7 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { sendPacket(); } - private void instructionUpdated(int index, int state) { + private void stageUpdated(int index, int state) { ColorEffect newValue = ColorEffect.all.get(state); stages.get(index).filter = newValue; stages.get(index).value = newValue.defaultValue; @@ -231,6 +282,33 @@ public class ChromaticProjectorScreen extends AbstractSimiScreen { return true; } + if (blend.isHovered()) { + tile.blend = !tile.blend; + return true; + } + + if (fieldEffect.isHovered()) { + tile.field = !tile.field; + + fieldEffect.setIcon(tile.field ? AllIcons.I_FX_FIELD_ON : AllIcons.I_FX_FIELD_OFF); + return fieldEffect.mouseClicked(x, y, button); + } + + if (rChannel.isHovered()) { + tile.rMask = !tile.rMask; + return true; + } + + if (gChannel.isHovered()) { + tile.gMask = !tile.gMask; + return true; + } + + if (bChannel.isHovered()) { + tile.bMask = !tile.bMask; + return true; + } + return super.mouseClicked(x, y, button); } diff --git a/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorTileEntity.java b/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorTileEntity.java index 338c2fd20..0bb20e983 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorTileEntity.java +++ b/src/main/java/com/simibubi/create/content/curiosities/projector/ChromaticProjectorTileEntity.java @@ -10,7 +10,6 @@ import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.vector.Matrix4f; import net.minecraftforge.common.util.Constants; public class ChromaticProjectorTileEntity extends SyncedTileEntity implements IInstanceRendered { @@ -18,27 +17,49 @@ public class ChromaticProjectorTileEntity extends SyncedTileEntity implements II Vector stages = FilterStep.createDefault(); float radius = 3f; - float density = 1; + float feather = 1; + float density = 1; float fade = 1; boolean blend = true; + public boolean surface = true; + public boolean field = true; + public float strength = 1; + + public boolean rMask = true; + public boolean gMask = true; + public boolean bMask = true; + public ChromaticProjectorTileEntity(TileEntityType te) { super(te); } - public FilterSphere makeFilter() { - Matrix4f filter = FilterStep.fold(stages); + public FilterSphere getFilter() { BlockPos pos = getPos(); - return new FilterSphere() - .setFilter(filter) - .setCenter(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) - .setRadius(radius) - .setDensity(density) - .setFeather(feather) - .setBlendOver(true) - .setFade(fade); + FilterSphere sphere = new FilterSphere(); + + sphere.x = (float) (pos.getX() + 0.5); + sphere.y = (float) (pos.getY() + 0.5); + sphere.z = (float) (pos.getZ() + 0.5); + sphere.radius = radius; + + sphere.feather = feather; + sphere.density = density; + sphere.fade = fade; + sphere.blend = blend; + + sphere.surface = surface; + sphere.field = field; + sphere.strength = strength; + + sphere.rMask = rMask; + sphere.gMask = gMask; + sphere.bMask = bMask; + + sphere.filter = FilterStep.fold(stages); + return sphere; } public ChromaticProjectorTileEntity setRadius(int radius) { @@ -66,6 +87,11 @@ public class ChromaticProjectorTileEntity extends SyncedTileEntity implements II return this; } + public ChromaticProjectorTileEntity setStrength(int strength) { + this.strength = strength / 100f; + return this; + } + @Override public CompoundNBT write(CompoundNBT tag) { super.write(tag); @@ -73,9 +99,19 @@ public class ChromaticProjectorTileEntity extends SyncedTileEntity implements II tag.put("filters", FilterStep.writeAll(stages)); tag.putFloat("radius", radius); - tag.putFloat("density", density); + tag.putFloat("feather", feather); + tag.putFloat("density", density); tag.putFloat("fade", fade); + tag.putBoolean("blend", blend); + + tag.putBoolean("surface", surface); + tag.putBoolean("field", field); + tag.putFloat("strength", strength); + + tag.putBoolean("rMask", rMask); + tag.putBoolean("gMask", gMask); + tag.putBoolean("bMask", bMask); return tag; } @@ -87,8 +123,18 @@ public class ChromaticProjectorTileEntity extends SyncedTileEntity implements II stages = FilterStep.readAll(tag.getList("filters", Constants.NBT.TAG_COMPOUND)); radius = tag.getFloat("radius"); - density = tag.getFloat("density"); + feather = tag.getFloat("feather"); + density = tag.getFloat("density"); fade = tag.getFloat("fade"); + blend = tag.getBoolean("blend"); + + surface = tag.getBoolean("surface"); + field = tag.getBoolean("field"); + strength = tag.getFloat("strength"); + + rMask = tag.getBoolean("rMask"); + gMask = tag.getBoolean("gMask"); + bMask = tag.getBoolean("bMask"); } } diff --git a/src/main/java/com/simibubi/create/content/curiosities/projector/ColorEffect.java b/src/main/java/com/simibubi/create/content/curiosities/projector/ColorEffect.java index 12c0a0822..0d072b419 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/projector/ColorEffect.java +++ b/src/main/java/com/simibubi/create/content/curiosities/projector/ColorEffect.java @@ -19,12 +19,13 @@ public class ColorEffect { static final HashMap lookup = new HashMap<>(); private static int nextId = 0; - public static final ColorEffect INVERT = create("invert", ColorMatrices::invert); public static final ColorEffect SEPIA = create("sepia", ColorMatrices::sepia); public static final ColorEffect GRAYSCALE = create("grayscale", ColorMatrices::grayscale); public static final ColorEffect DARKEN = create("darken", ColorMatrices::darken).setDefaultValue(20); + public static final ColorEffect CONTRAST = create("contrast", ColorMatrices::contrast).setRange(0, 200).setDefaultValue(100); public static final ColorEffect SATURATE = create("saturate", ColorMatrices::saturate).setRange(0, 200); public static final ColorEffect HUE_SHIFT = create("hue_shift", ColorMatrices::hueShift).setRange(0, 360).setDivisor(1f).setDefaultValue(120); + public static final ColorEffect INVERT = create("invert", ColorMatrices::invert); public static final ColorEffect END = create("end", ColorMatrices::identity).setBackground(AllGuiTextures.PROJECTOR_END); boolean hasParameter; diff --git a/src/main/java/com/simibubi/create/content/curiosities/projector/ConfigureProjectorPacket.java b/src/main/java/com/simibubi/create/content/curiosities/projector/ConfigureProjectorPacket.java index be616532b..89eb463b3 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/projector/ConfigureProjectorPacket.java +++ b/src/main/java/com/simibubi/create/content/curiosities/projector/ConfigureProjectorPacket.java @@ -12,9 +12,19 @@ public class ConfigureProjectorPacket extends TileEntityConfigurationPacket stages; float radius; - float density; + float feather; + float density; float fade; + boolean blend; + + public boolean surface; + public boolean field; + public float strength; + + public boolean rMask; + public boolean gMask; + public boolean bMask; public ConfigureProjectorPacket(PacketBuffer buffer) { super(buffer); @@ -26,18 +36,38 @@ public class ConfigureProjectorPacket extends TileEntityConfigurationPacket(FilterStep.MAX_STEPS); @@ -66,10 +106,20 @@ public class ConfigureProjectorPacket extends TileEntityConfigurationPacket (int) Math.signum(o2.center.length() - o1.center.length())); + spheres.sort((o1, o2) -> { + double l1 = RenderUtil.length(o1.x, o1.y, o1.z); + double l2 = RenderUtil.length(o2.x, o2.y, o2.z); + return (int) Math.signum(l2 - l1); + }); program.uploadFilters(spheres); diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/effects/FilterSphere.java b/src/main/java/com/simibubi/create/foundation/render/backend/effects/FilterSphere.java index 6cf354755..0b6d4f881 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/effects/FilterSphere.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/effects/FilterSphere.java @@ -5,87 +5,49 @@ import java.nio.FloatBuffer; import com.simibubi.create.foundation.render.backend.RenderUtil; import net.minecraft.util.math.vector.Matrix4f; -import net.minecraft.util.math.vector.Vector3d; public class FilterSphere { - public Vector3d center; + public float x; + public float y; + public float z; public float radius; + public float feather; public float fade; public float density = 2; - public float strength = 1; - public boolean blendOver = false; + public boolean blend = false; - public float r; - public float g; - public float b; - public float colorFeather; + public boolean surface = true; + public boolean field = true; + public float strength = 1; + + public boolean rMask; + public boolean gMask; + public boolean bMask; public Matrix4f filter; - public FilterSphere setCenter(Vector3d center) { - this.center = center; - return this; - } - - public FilterSphere setCenter(double x, double y, double z) { - this.center = new Vector3d(x, y, z); - return this; - } - - public FilterSphere setRadius(float radius) { - this.radius = radius; - return this; - } - - public FilterSphere setFeather(float feather) { - this.feather = feather; - return this; - } - - public FilterSphere setFade(float fade) { - this.fade = fade; - return this; - } - - public FilterSphere setDensity(float density) { - this.density = density; - return this; - } - - public FilterSphere setStrength(float strength) { - this.strength = strength; - return this; - } - - public FilterSphere setFilter(Matrix4f filter) { - this.filter = filter; - return this; - } - - public FilterSphere setBlendOver(boolean blendOver) { - this.blendOver = blendOver; - return this; - } - public void write(FloatBuffer buf) { buf.put(new float[]{ - (float) center.x, - (float) center.y, - (float) center.z, + x, + y, + z, radius, + feather, fade, density, - blendOver ? 1f : 0f, - 1f, - 1f, - 0f, - 0f, - 0.5f, //r, - 0.1f, //g, - 0.1f, //b, - 0f, //colorFeather, + blend ? 1 : 0, + + surface ? 1 : 0, + field ? 1 : 0, + strength, + 0, // padding + + rMask ? 1 : 0, + gMask ? 1 : 0, + bMask ? 1 : 0, + 0, // padding }); buf.put(RenderUtil.writeMatrix(filter)); diff --git a/src/main/resources/assets/create/flywheel/shaders/area_effect.frag b/src/main/resources/assets/create/flywheel/shaders/area_effect.frag index 3a43ac7a4..86e987ecc 100644 --- a/src/main/resources/assets/create/flywheel/shaders/area_effect.frag +++ b/src/main/resources/assets/create/flywheel/shaders/area_effect.frag @@ -17,8 +17,8 @@ uniform vec3 uCameraPos; struct SphereFilter { vec4 sphere;// vec4 d1;// - vec4 d2;// - vec4 colorMask;// + vec4 strength;// + vec4 channelMask;// mat4 colorOp; }; @@ -69,6 +69,12 @@ vec2 raySphere(vec3 worldDir, vec3 position, float radius) { return vec2(discriminant, hitDepth); } +// if i == 0 return s +// if i == 1 return 1 - s +float invert(float s, float i) { + return i - 2*i*s + s; +} + float bubbleFilterStrength(vec3 worldDir, float depth, vec4 sphere, float feather, float density) { vec3 position = sphere.xyz; @@ -96,18 +102,18 @@ float filterStrength(vec3 worldDir, float depth, inout SphereFilter f) { vec4 data = f.d1; float feather = data.x; - float strength = 0.; + float strength; // transition effect float transitionRadius = sphere.w + feather; - strength += 1. - smoothstep(transitionRadius, transitionRadius + data.y, length(sphere.xyz)); + strength = 1. - smoothstep(transitionRadius, transitionRadius + max(0.5, data.y), length(sphere.xyz)); // bubble effect - strength += bubbleFilterStrength(worldDir, depth, sphere, feather, data.z); + strength = max(strength, bubbleFilterStrength(worldDir, depth, sphere, feather, data.z)); - strength *= f.d2.y; + strength *= f.strength.y; // surface effect - strength += surfaceFilterStrength(worldDir * depth, sphere, feather) * f.d2.x; + strength = max(strength, surfaceFilterStrength(worldDir * depth, sphere, feather) * f.strength.x); - return strength; + return strength * f.strength.z; } vec3 applyFilters(vec3 worldDir, float depth, vec3 diffuse) { @@ -127,13 +133,13 @@ vec3 applyFilters(vec3 worldDir, float depth, vec3 diffuse) { vec3 filtered = filterColor(s.colorOp, baseColor); - vec3 baseHsv = rgb2hsv(baseColor); - vec3 maskHsv = rgb2hsv(s.colorMask.rgb); - vec3 diff = abs(baseHsv - maskHsv) * vec3(1., 1.1, 0.1); - float colorMask = step(s.colorMask.w, length(diff)); + // vec3 baseHsv = rgb2hsv(baseColor); + // vec3 maskHsv = rgb2hsv(s.colorMask.rgb); + // float diff = dot(abs(baseHsv - maskHsv), vec3(1., 1.1, 0.1)); + // float colorMask = step(s.colorMask.w, diff); float mixing = clamp(strength, 0., 1.); - accum = mix(accum, filtered, mixing * colorMask); + accum = mix(accum, filtered, mixing * s.channelMask.xyz); //accum = vec3(colorMask); } } diff --git a/src/main/resources/assets/create/textures/gui/icons.png b/src/main/resources/assets/create/textures/gui/icons.png index 1dff79b367fb92985f19643b0fe3032bca2a9dae..2fbdf43bb6a8df13e5eaa780edf8ca662ee9c0e7 100644 GIT binary patch literal 4934 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 Hdc=PKERk3x diff --git a/src/main/resources/assets/create/textures/gui/projector.png b/src/main/resources/assets/create/textures/gui/projector.png index 968618e948b3dde383ab64f20f199f5166f4d068..6638b2b2fc32dd365fc6bc49a593dd6031655705 100644 GIT binary patch literal 4461 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