mirror of
https://github.com/ppy/osu.git
synced 2025-02-14 19:53:08 +08:00
Merge pull request #27967 from peppy/storyboard-video-transforms-and-sizing
Fix storyboard videos not accepting transforms
This commit is contained in:
commit
c454dd2496
@ -10,6 +10,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -40,7 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("disallow all lookups", () =>
|
AddStep("disallow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = false;
|
storyboard.UseSkinSprites = false;
|
||||||
storyboard.AlwaysProvideTexture = false;
|
storyboard.ProvideResources = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow storyboard lookup", () =>
|
AddStep("allow storyboard lookup", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = false;
|
storyboard.UseSkinSprites = false;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -67,13 +68,48 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
assertStoryboardSourced();
|
assertStoryboardSourced();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(false)]
|
||||||
|
[TestCase(true)]
|
||||||
|
public void TestVideo(bool scaleTransformProvided)
|
||||||
|
{
|
||||||
|
AddStep("allow storyboard lookup", () =>
|
||||||
|
{
|
||||||
|
storyboard.ProvideResources = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("create video", () => SetContents(_ =>
|
||||||
|
{
|
||||||
|
var layer = storyboard.GetLayer("Video");
|
||||||
|
|
||||||
|
var sprite = new StoryboardVideo("Videos/test-video.mp4", Time.Current);
|
||||||
|
|
||||||
|
if (scaleTransformProvided)
|
||||||
|
{
|
||||||
|
sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current, Time.Current + 1000, 1, 2);
|
||||||
|
sprite.TimelineGroup.Scale.Add(Easing.None, Time.Current + 1000, Time.Current + 2000, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
layer.Elements.Clear();
|
||||||
|
layer.Add(sprite);
|
||||||
|
|
||||||
|
return new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
storyboard.CreateDrawable()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSkinLookupPreferredOverStoryboard()
|
public void TestSkinLookupPreferredOverStoryboard()
|
||||||
{
|
{
|
||||||
AddStep("allow all lookups", () =>
|
AddStep("allow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -91,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow skin lookup", () =>
|
AddStep("allow skin lookup", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = false;
|
storyboard.ProvideResources = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -109,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow all lookups", () =>
|
AddStep("allow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -127,7 +163,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow all lookups", () =>
|
AddStep("allow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -142,7 +178,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow all lookups", () =>
|
AddStep("allow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -156,7 +192,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("allow all lookups", () =>
|
AddStep("allow all lookups", () =>
|
||||||
{
|
{
|
||||||
storyboard.UseSkinSprites = true;
|
storyboard.UseSkinSprites = true;
|
||||||
storyboard.AlwaysProvideTexture = true;
|
storyboard.ProvideResources = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
@ -170,7 +206,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("origin back", () => sprites.All(s => s.Origin == Anchor.TopLeft));
|
AddAssert("origin back", () => sprites.All(s => s.Origin == Anchor.TopLeft));
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableStoryboard createSprite(string lookupName, Anchor origin, Vector2 initialPosition)
|
private Drawable createSprite(string lookupName, Anchor origin, Vector2 initialPosition)
|
||||||
{
|
{
|
||||||
var layer = storyboard.GetLayer("Background");
|
var layer = storyboard.GetLayer("Background");
|
||||||
|
|
||||||
@ -180,7 +216,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
layer.Elements.Clear();
|
layer.Elements.Clear();
|
||||||
layer.Add(sprite);
|
layer.Add(sprite);
|
||||||
|
|
||||||
return storyboard.CreateDrawable().With(s => s.RelativeSizeAxes = Axes.Both);
|
return new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
storyboard.CreateDrawable()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertStoryboardSourced()
|
private void assertStoryboardSourced()
|
||||||
@ -202,42 +245,52 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
return new TestDrawableStoryboard(this, mods);
|
return new TestDrawableStoryboard(this, mods);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AlwaysProvideTexture { get; set; }
|
public bool ProvideResources { get; set; }
|
||||||
|
|
||||||
public override string GetStoragePathFromStoryboardPath(string path) => AlwaysProvideTexture ? path : string.Empty;
|
public override string GetStoragePathFromStoryboardPath(string path) => ProvideResources ? path : string.Empty;
|
||||||
|
|
||||||
private partial class TestDrawableStoryboard : DrawableStoryboard
|
private partial class TestDrawableStoryboard : DrawableStoryboard
|
||||||
{
|
{
|
||||||
private readonly bool alwaysProvideTexture;
|
private readonly bool provideResources;
|
||||||
|
|
||||||
public TestDrawableStoryboard(TestStoryboard storyboard, IReadOnlyList<Mod>? mods)
|
public TestDrawableStoryboard(TestStoryboard storyboard, IReadOnlyList<Mod>? mods)
|
||||||
: base(storyboard, mods)
|
: base(storyboard, mods)
|
||||||
{
|
{
|
||||||
alwaysProvideTexture = storyboard.AlwaysProvideTexture;
|
provideResources = storyboard.ProvideResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IResourceStore<byte[]> CreateResourceLookupStore() => alwaysProvideTexture
|
protected override IResourceStore<byte[]> CreateResourceLookupStore() => provideResources
|
||||||
? new AlwaysReturnsTextureStore()
|
? new ResourcesTextureStore()
|
||||||
: new ResourceStore<byte[]>();
|
: new ResourceStore<byte[]>();
|
||||||
|
|
||||||
internal class AlwaysReturnsTextureStore : IResourceStore<byte[]>
|
internal class ResourcesTextureStore : IResourceStore<byte[]>
|
||||||
{
|
{
|
||||||
private const string test_image = "Resources/Textures/test-image.png";
|
|
||||||
|
|
||||||
private readonly DllResourceStore store;
|
private readonly DllResourceStore store;
|
||||||
|
|
||||||
public AlwaysReturnsTextureStore()
|
public ResourcesTextureStore()
|
||||||
{
|
{
|
||||||
store = TestResources.GetStore();
|
store = TestResources.GetStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => store.Dispose();
|
public void Dispose() => store.Dispose();
|
||||||
|
|
||||||
public byte[] Get(string name) => store.Get(test_image);
|
public byte[] Get(string name) => store.Get(map(name));
|
||||||
|
|
||||||
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(test_image, cancellationToken);
|
public Task<byte[]> GetAsync(string name, CancellationToken cancellationToken = new CancellationToken()) => store.GetAsync(map(name), cancellationToken);
|
||||||
|
|
||||||
public Stream GetStream(string name) => store.GetStream(test_image);
|
public Stream GetStream(string name) => store.GetStream(map(name));
|
||||||
|
|
||||||
|
private string map(string name)
|
||||||
|
{
|
||||||
|
switch (name)
|
||||||
|
{
|
||||||
|
case lookup_name:
|
||||||
|
return "Resources/Textures/test-image.png";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return $"Resources/{name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetAvailableResources() => store.GetAvailableResources();
|
public IEnumerable<string> GetAvailableResources() => store.GetAvailableResources();
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant()))
|
if (!OsuGameBase.VIDEO_EXTENSIONS.Contains(Path.GetExtension(path).ToLowerInvariant()))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset));
|
storyboard.GetLayer("Video").Add(storyboardSprite = new StoryboardVideo(path, offset));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,10 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Graphics.Video;
|
using osu.Framework.Graphics.Video;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
@ -23,11 +21,21 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
{
|
{
|
||||||
Video = video;
|
Video = video;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
// In osu-stable, a mapper can add a scale command for a storyboard video.
|
||||||
|
// This allows scaling based on the video's absolute size.
|
||||||
|
//
|
||||||
|
// If not specified we take up the full available space.
|
||||||
|
bool useRelative = !video.TimelineGroup.Scale.HasCommands;
|
||||||
|
|
||||||
|
RelativeSizeAxes = useRelative ? Axes.Both : Axes.None;
|
||||||
|
AutoSizeAxes = useRelative ? Axes.None : Axes.Both;
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, TextureStore textureStore)
|
private void load(TextureStore textureStore)
|
||||||
{
|
{
|
||||||
var stream = textureStore.GetStream(Video.Path);
|
var stream = textureStore.GetStream(Video.Path);
|
||||||
|
|
||||||
@ -36,12 +44,14 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
|
|
||||||
InternalChild = drawableVideo = new Video(stream, false)
|
InternalChild = drawableVideo = new Video(stream, false)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = RelativeSizeAxes,
|
||||||
FillMode = FillMode.Fill,
|
FillMode = FillMode.Fill,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Video.ApplyTransforms(drawableVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -3,23 +3,20 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Storyboards.Drawables;
|
using osu.Game.Storyboards.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Storyboards
|
namespace osu.Game.Storyboards
|
||||||
{
|
{
|
||||||
public class StoryboardVideo : IStoryboardElement
|
public class StoryboardVideo : StoryboardSprite
|
||||||
{
|
{
|
||||||
public string Path { get; }
|
|
||||||
|
|
||||||
public bool IsDrawable => true;
|
|
||||||
|
|
||||||
public double StartTime { get; }
|
|
||||||
|
|
||||||
public StoryboardVideo(string path, double offset)
|
public StoryboardVideo(string path, double offset)
|
||||||
|
: base(path, Anchor.Centre, Vector2.Zero)
|
||||||
{
|
{
|
||||||
Path = path;
|
// This is just required to get a valid StartTime based on the incoming offset.
|
||||||
StartTime = offset;
|
// Actual fades are handled inside DrawableStoryboardVideo for now.
|
||||||
|
TimelineGroup.Alpha.Add(Easing.None, offset, offset, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
|
public override Drawable CreateDrawable() => new DrawableStoryboardVideo(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user