diff --git a/fluXis.Resources/Shaders/sh_Perspective.fs b/fluXis.Resources/Shaders/sh_Perspective.fs new file mode 100644 index 000000000..3f1065868 --- /dev/null +++ b/fluXis.Resources/Shaders/sh_Perspective.fs @@ -0,0 +1,74 @@ +layout(std140, set = 0, binding = 0) uniform m_PerspectiveParameters +{ + vec2 g_TexSize; + float g_Strength; + float g_Strength2; + float g_Strength3; +}; + +layout(set = 1, binding = 0) uniform texture2D m_Texture; +layout(set = 1, binding = 1) uniform sampler m_Sampler; + +layout(location = 0) out vec4 o_Colour; + +// this is a port of a godot shader // saludos a mi compaƱero matias +// probably this one: https://godotshaders.com/shader/2d-perspective + +bool cull_back = true; +float inset = 0.0; + +const float PI = 3.14159; + +mat3 getRotationMatrix(float yaw, float pitch) { + float sin_b = sin(yaw / 180.0 * PI); + float cos_b = cos(yaw / 180.0 * PI); + float sin_c = sin(pitch / 180.0 * PI); + float cos_c = cos(pitch / 180.0 * PI); + + mat3 inv_rot_mat; + inv_rot_mat[0][0] = cos_b; + inv_rot_mat[0][1] = 0.0; + inv_rot_mat[0][2] = -sin_b; + + inv_rot_mat[1][0] = sin_b * sin_c; + inv_rot_mat[1][1] = cos_c; + inv_rot_mat[1][2] = cos_b * sin_c; + + inv_rot_mat[2][0] = sin_b * cos_c; + inv_rot_mat[2][1] = -sin_c; + inv_rot_mat[2][2] = cos_b * cos_c; + + return inv_rot_mat; +} + +void main(void) { + vec2 uv = (gl_FragCoord.xy / g_TexSize) - 0.5; + + float x_rot = g_Strength2; + float y_rot = g_Strength; + float fov = g_Strength3 + 0.1; + + mat3 inv_rot_mat = getRotationMatrix(x_rot, y_rot); + + float t = tan(fov / 360.0 * PI); + vec3 ray_direction = inv_rot_mat * vec3(uv, 0.5 / t); + float v = (0.5 / t) + 0.5; + + ray_direction.xy *= v * inv_rot_mat[2].z; + vec2 offset = v * inv_rot_mat[2].xy; + + if (cull_back && ray_direction.z <= 0.0) { + o_Colour = vec4(0.0); + return; + } + + vec2 tex_uv = (ray_direction.xy / ray_direction.z) - offset; + tex_uv += 0.5; + + vec4 color = texture(sampler2D(m_Texture, m_Sampler), tex_uv); + + float distance_from_center = max(abs(tex_uv.x - 0.5), abs(tex_uv.y - 0.5)); + color.a *= step(distance_from_center, 0.5); + + o_Colour = color; +} \ No newline at end of file diff --git a/fluXis/Graphics/Shaders/Perspective/PerspectiveContainer.cs b/fluXis/Graphics/Shaders/Perspective/PerspectiveContainer.cs new file mode 100644 index 000000000..b5957bf72 --- /dev/null +++ b/fluXis/Graphics/Shaders/Perspective/PerspectiveContainer.cs @@ -0,0 +1,11 @@ +using fluXis.Map.Structures.Events; +using osu.Framework.Graphics; + +namespace fluXis.Graphics.Shaders.Perspective; + +public partial class PerspectiveContainer : ShaderContainer +{ + protected override string FragmentShader => "Perspective"; + public override ShaderType Type => ShaderType.Perspective; + protected override DrawNode CreateShaderDrawNode() => new PerspectiveContainerDrawNode(this, SharedData); +} \ No newline at end of file diff --git a/fluXis/Graphics/Shaders/Perspective/PerspectiveDrawNode.cs b/fluXis/Graphics/Shaders/Perspective/PerspectiveDrawNode.cs new file mode 100644 index 000000000..0fb67f315 --- /dev/null +++ b/fluXis/Graphics/Shaders/Perspective/PerspectiveDrawNode.cs @@ -0,0 +1,84 @@ +using System.Runtime.InteropServices; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders.Types; +using osuTK.Graphics; + +namespace fluXis.Graphics.Shaders.Perspective; + +public partial class PerspectiveContainer +{ + private class PerspectiveContainerDrawNode : ShaderDrawNode + { + private float strength; + private float strength2; + private float strength3; + + private IUniformBuffer parametersBuffer; + + public PerspectiveContainerDrawNode(PerspectiveContainer source, BufferedDrawNodeSharedData sharedData) + : base(source, sharedData) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + strength = Source.Strength; + strength2 = Source.Strength2; + strength3 = Source.Strength3; + } + + protected override void PopulateContents(IRenderer renderer) + { + base.PopulateContents(renderer); + if (strength != 0 || strength2 != 0 || strength3 != 0) + drawFrameBuffer(renderer); + } + + private void drawFrameBuffer(IRenderer renderer) + { + parametersBuffer ??= renderer.CreateUniformBuffer(); + + IFrameBuffer current = SharedData.CurrentEffectBuffer; + IFrameBuffer target = SharedData.GetNextEffectBuffer(); + + renderer.SetBlend(BlendingParameters.None); + + using (BindFrameBuffer(target)) + { + parametersBuffer.Data = parametersBuffer.Data with + { + TexSize = current.Size, + Strength = strength, + Strength2 = strength2, + Strength3 = strength3, + }; + + Shader.BindUniformBlock("m_PerspectiveParameters", parametersBuffer); + Shader.Bind(); + renderer.DrawFrameBuffer(current, new RectangleF(0, 0, current.Texture.Width, current.Texture.Height), ColourInfo.SingleColour(Color4.White)); + Shader.Unbind(); + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + parametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct PerspectiveParameters + { + public UniformVector2 TexSize; + public UniformFloat Strength; + public UniformFloat Strength2; + public UniformFloat Strength3; + private readonly UniformPadding12 pad1; + } + } +} \ No newline at end of file diff --git a/fluXis/Map/Structures/Events/ShaderEvent.cs b/fluXis/Map/Structures/Events/ShaderEvent.cs index 1c31f83a6..f4bdd994f 100644 --- a/fluXis/Map/Structures/Events/ShaderEvent.cs +++ b/fluXis/Map/Structures/Events/ShaderEvent.cs @@ -92,5 +92,6 @@ public enum ShaderType Glitch, SplitScreen, FishEye, - Reflections + Reflections, + Perspective } diff --git a/fluXis/Screens/Edit/Tabs/Design/DesignContainer.cs b/fluXis/Screens/Edit/Tabs/Design/DesignContainer.cs index 58696c80b..45050d8ce 100644 --- a/fluXis/Screens/Edit/Tabs/Design/DesignContainer.cs +++ b/fluXis/Screens/Edit/Tabs/Design/DesignContainer.cs @@ -18,6 +18,7 @@ using fluXis.Graphics.Shaders.SplitScreen; using fluXis.Graphics.Shaders.FishEye; using fluXis.Graphics.Shaders.Reflections; +using fluXis.Graphics.Shaders.Perspective; using fluXis.Graphics.Sprites; using fluXis.Map.Structures.Bases; using fluXis.Map.Structures.Events; @@ -79,14 +80,14 @@ public partial class DesignContainer : EditorTabContainer private Bindable userScrollSpeed; private Bindable rulesetScrollSpeed { get; } = new(); - private BackgroundVideo backgroundVideo; - [BackgroundDependencyLoader] private void load(FluXisConfig config) { userScrollSpeed = config.GetBindable(FluXisSetting.ScrollSpeed); } + private BackgroundVideo backgroundVideo; + protected override IEnumerable CreateContent() { drawSizePreserve = new DrawSizePreservingFillContainer @@ -199,6 +200,7 @@ private RulesetContainer createRuleset() var container = new ReplayRulesetContainer(auto.Generate(), Map.MapInfo, effects, new List { new NoFailMod() }); container.ScrollSpeed = rulesetScrollSpeed; container.ParentClock = EditorClock; + return container; } @@ -225,6 +227,7 @@ private ShaderStackContainer createShaderStack() ShaderType.SplitScreen => new SplitScreenContainer(), ShaderType.FishEye => new FishEyeContainer(), ShaderType.Reflections => new ReflectionsContainer(), + ShaderType.Perspective => new PerspectiveContainer(), _ => null }; diff --git a/fluXis/Screens/Edit/Tabs/Design/Points/Entries/ShaderEntry.cs b/fluXis/Screens/Edit/Tabs/Design/Points/Entries/ShaderEntry.cs index fd0985d54..2e20e2669 100644 --- a/fluXis/Screens/Edit/Tabs/Design/Points/Entries/ShaderEntry.cs +++ b/fluXis/Screens/Edit/Tabs/Design/Points/Entries/ShaderEntry.cs @@ -373,6 +373,99 @@ protected override IEnumerable CreateSettings() }); break; + case ShaderType.Perspective: + settings.AddRange(new Drawable[] + { + new PointSettingsSlider + { + Enabled = startValToggle.Bindable, + Text = "Start X Rotation", + TooltipText = "X axis rotation.", + CurrentValue = shader.StartParameters.Strength, + Min = -90.0f, + Max = 90.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.StartParameters.Strength = value; + Map.Update(shader); + } + }, + new PointSettingsSlider + { + Text = "End X Rotation", + TooltipText = "X axis rotation.", + CurrentValue = shader.EndParameters.Strength, + Min = -90.0f, + Max = 90.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.EndParameters.Strength = value; + Map.Update(shader); + } + }, + new PointSettingsSlider + { + Enabled = startValToggle.Bindable, + Text = "Start Y Rotation", + TooltipText = "Y axis rotation.", + CurrentValue = shader.StartParameters.Strength2, + Min = -90.0f, + Max = 90.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.StartParameters.Strength2 = value; + Map.Update(shader); + } + }, + new PointSettingsSlider + { + Text = "End Y Rotation", + TooltipText = "Y axis rotation.", + CurrentValue = shader.EndParameters.Strength2, + Min = -90.0f, + Max = 90.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.EndParameters.Strength2 = value; + Map.Update(shader); + } + }, + new PointSettingsSlider + { + Enabled = startValToggle.Bindable, + Text = "Start FOV", + TooltipText = "FOV.", + CurrentValue = shader.StartParameters.Strength3, + Min = 0.0f, + Max = 100.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.StartParameters.Strength3 = value; + Map.Update(shader); + } + }, + new PointSettingsSlider + { + Text = "End FOV", + TooltipText = "FOV.", + CurrentValue = shader.EndParameters.Strength3, + Min = 0.0f, + Max = 100.0f, + Step = 1.0f, + OnValueChanged = value => + { + shader.EndParameters.Strength3 = value; + Map.Update(shader); + } + } + }); + break; + case ShaderType.Bloom: case ShaderType.Greyscale: case ShaderType.Invert: diff --git a/fluXis/Screens/Gameplay/GameplayScreen.cs b/fluXis/Screens/Gameplay/GameplayScreen.cs index b8b933239..0e41d8edc 100644 --- a/fluXis/Screens/Gameplay/GameplayScreen.cs +++ b/fluXis/Screens/Gameplay/GameplayScreen.cs @@ -25,6 +25,7 @@ using fluXis.Graphics.Shaders.SplitScreen; using fluXis.Graphics.Shaders.FishEye; using fluXis.Graphics.Shaders.Reflections; +using fluXis.Graphics.Shaders.Perspective; using fluXis.Input; using fluXis.Map; using fluXis.Map.Structures.Bases; @@ -379,6 +380,7 @@ private ShaderStackContainer buildShaders() ShaderType.SplitScreen => new SplitScreenContainer(), ShaderType.FishEye => new FishEyeContainer(), ShaderType.Reflections => new ReflectionsContainer(), + ShaderType.Perspective => new PerspectiveContainer(), _ => null };