public static void ScaleToFit(this OsbSprite sprite) { var bitmap = Bitmap.FromFile(sprite.GetTexturePathAt(sprite.CommandsStartTime)); sprite.Scale( bitmap.Width / bitmap.Height > 480.0 / 854.0 ? 480.0 / bitmap.Height : 854.0 / bitmap.Width ); }
public bool GenerateCommands(OsbSprite sprite, Box2 bounds, Action <Action, OsbSprite> action = null, double?startTime = null, double?endTime = null, double timeOffset = 0, bool loopable = false) { var previousState = (State)null; var wasVisible = false; var everVisible = false; var stateAdded = false; var imageSize = Vector2.One; foreach (var state in states) { var time = state.Time + timeOffset; var bitmap = StoryboardObjectGenerator.Current.GetMapsetBitmap(sprite.GetTexturePathAt(time)); imageSize = new Vector2(bitmap.Width, bitmap.Height); PostProcess?.Invoke(state); var isVisible = state.IsVisible(bitmap.Width, bitmap.Height, sprite.Origin, bounds); if (isVisible) { everVisible = true; } if (!wasVisible && isVisible) { if (!stateAdded && previousState != null) { addKeyframes(previousState, time); } addKeyframes(state, time); stateAdded = true; } else if (wasVisible && !isVisible) { addKeyframes(state, time); commitKeyframes(imageSize); stateAdded = true; } else if (isVisible) { addKeyframes(state, time); stateAdded = true; } else { stateAdded = false; } previousState = state; wasVisible = isVisible; } if (wasVisible) { commitKeyframes(imageSize); } if (everVisible) { if (action != null) { action(() => convertToCommands(sprite, startTime, endTime, timeOffset, loopable), sprite); } else { convertToCommands(sprite, startTime, endTime, timeOffset, loopable); } } clearFinalKeyframes(); return(everVisible); }
public override void GenerateStates(double time, CameraState cameraState, Object3dState object3dState) { var wvp = object3dState.WorldTransform * cameraState.ViewProjection; var startVector = cameraState.ToScreen(wvp, StartPosition.ValueAt(time)); var endVector = cameraState.ToScreen(wvp, EndPosition.ValueAt(time)); var delta = endVector.Xy - startVector.Xy; var length = delta.Length; if (length == 0) { return; } var angle = Math.Atan2(delta.Y, delta.X); var rotation = InterpolatingFunctions.DoubleAngle(GeneratorBody.EndState?.Rotation ?? 0, angle, 1); var thickness = Thickness.ValueAt(time); var scaleFactor = object3dState.WorldTransform.ExtractScale().Y *(float)cameraState.ResolutionScale; var startScale = scaleFactor * (float)(cameraState.FocusDistance / startVector.W) * thickness * StartThickness.ValueAt(time); var endScale = scaleFactor * (float)(cameraState.FocusDistance / endVector.W) * thickness * EndThickness.ValueAt(time); var totalHeight = Math.Max(startScale, endScale); var bodyHeight = Math.Min(startScale, endScale); var edgeHeight = (totalHeight - bodyHeight) * 0.5f; var flip = startScale < endScale; var ignoreEdges = edgeHeight < EdgeOverlap; if (ignoreEdges) { bodyHeight += edgeHeight * 2; } var opacity = startVector.W < 0 && endVector.W < 0 ? 0 : object3dState.Opacity; if (UseDistanceFade) { opacity *= Math.Max(cameraState.OpacityAt(startVector.W), cameraState.OpacityAt(endVector.W)); } // Body var bitmapBody = StoryboardObjectGenerator.Current.GetMapsetBitmap(spriteBody.GetTexturePathAt(time)); var bodyScale = new Vector2(length / bitmapBody.Width, bodyHeight / bitmapBody.Height); var positionBody = startVector.Xy + delta * 0.5f; var bodyOpacity = opacity; GeneratorBody.Add(new CommandGenerator.State() { Time = time, Position = positionBody, Scale = bodyScale, Rotation = rotation, Color = object3dState.Color, Opacity = bodyOpacity, Additive = Additive, }); // Edges if (SpritePathEdge != null) { var bitmapEdge = StoryboardObjectGenerator.Current.GetMapsetBitmap(spriteTopEdge.GetTexturePathAt(time)); var edgeScale = new Vector2(length / bitmapEdge.Width, edgeHeight / bitmapEdge.Height); var edgeOffset = new Vector2((float)Math.Cos(angle - Math.PI * 0.5), (float)Math.Sin(angle - Math.PI * 0.5)) * (bodyHeight * 0.5f - EdgeOverlap); var positionTop = positionBody + edgeOffset; var positionBottom = positionBody - edgeOffset; var edgeOpacity = ignoreEdges ? 0 : opacity; GeneratorTopEdge.Add(new CommandGenerator.State() { Time = time, Position = positionTop, Scale = edgeScale, Rotation = rotation, Color = object3dState.Color, Opacity = edgeOpacity, FlipH = flip, Additive = Additive, }); GeneratorBottomEdge.Add(new CommandGenerator.State() { Time = time, Position = positionBottom, Scale = edgeScale, Rotation = rotation, Color = object3dState.Color, Opacity = edgeOpacity, Additive = Additive, FlipH = flip, FlipV = true, }); } // Caps if (SpritePathCap != null) { var bitmapCap = StoryboardObjectGenerator.Current.GetMapsetBitmap(spriteStartCap.GetTexturePathAt(time)); var startCapScale = new Vector2(startScale / bitmapCap.Width, startScale / bitmapCap.Height); var endCapScale = new Vector2(endScale / bitmapCap.Width, endScale / bitmapCap.Height); var capOffset = OrientedCaps ? new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * CapOverlap : Vector2.Zero; if (OrientedCaps) { startCapScale.X *= 0.5f; endCapScale.X *= 0.5f; } var startCapOpacity = startScale > 0.5f ? opacity : 0; var endCapOpacity = endScale > 0.5f ? opacity : 0; GeneratorStartCap.Add(new CommandGenerator.State() { Time = time, Position = startVector.Xy + capOffset, Scale = startCapScale, Rotation = OrientedCaps ? rotation + Math.PI : 0, Color = object3dState.Color, Opacity = startCapOpacity, Additive = Additive, }); GeneratorEndCap.Add(new CommandGenerator.State() { Time = time, Position = endVector.Xy - capOffset, Scale = endCapScale, Rotation = OrientedCaps ? rotation + Math.PI : 0, Color = object3dState.Color, Opacity = endCapOpacity, Additive = Additive, FlipH = OrientedCaps, }); } }
public static void Draw(DrawContext drawContext, Camera camera, Box2 bounds, float opacity, Project project, FrameStats frameStats, OsbSprite sprite) { var time = project.DisplayTime * 1000; if (sprite.TexturePath == null || !sprite.IsActive(time)) { return; } if (frameStats != null) { frameStats.SpriteCount++; frameStats.CommandCount += sprite.CommandCost; frameStats.IncompatibleCommands |= sprite.HasIncompatibleCommands; frameStats.OverlappedCommands |= sprite.HasOverlappedCommands; } var fade = sprite.OpacityAt(time); if (fade < 0.00001f) { return; } var scale = (Vector2)sprite.ScaleAt(time); if (scale.X == 0 || scale.Y == 0) { return; } if (sprite.FlipHAt(time)) { scale.X = -scale.X; } if (sprite.FlipVAt(time)) { scale.Y = -scale.Y; } Texture2dRegion texture = null; var fullPath = Path.Combine(project.MapsetPath, sprite.GetTexturePathAt(time)); try { texture = project.TextureContainer.Get(fullPath); if (texture == null) { fullPath = Path.Combine(project.ProjectAssetFolderPath, sprite.GetTexturePathAt(time)); texture = project.TextureContainer.Get(fullPath); } } catch (IOException) { // Happens when another process is writing to the file, will try again later. return; } if (texture == null) { return; } var position = sprite.PositionAt(time); var rotation = sprite.RotationAt(time); var color = sprite.ColorAt(time); var finalColor = ((Color4)color) .LerpColor(Color4.Black, project.DimFactor) .WithOpacity(opacity * fade); var additive = sprite.AdditiveAt(time); var origin = GetOriginVector(sprite.Origin, texture.Width, texture.Height); if (frameStats != null) { var size = texture.Size * scale; var spriteObb = new OrientedBoundingBox(position, origin * scale, size.X, size.Y, rotation); if (spriteObb.Intersects(OsuHitObject.WidescreenStoryboardBounds)) { frameStats.EffectiveCommandCount += sprite.CommandCost; // Approximate how much of the sprite is on screen var spriteAabb = spriteObb.GetAABB(); var intersection = spriteAabb.IntersectWith(OsuHitObject.WidescreenStoryboardBounds); var aabbIntersectionFactor = (intersection.Width * intersection.Height) / (spriteAabb.Width * spriteAabb.Height); var intersectionArea = size.X * size.Y * aabbIntersectionFactor; frameStats.ScreenFill += Math.Min(OsuHitObject.WidescreenStoryboardArea, intersectionArea) / OsuHitObject.WidescreenStoryboardArea; } } var boundsScaling = bounds.Height / 480; DrawState.Prepare(drawContext.Get <QuadRenderer>(), camera, additive ? AdditiveStates : AlphaBlendStates) .Draw(texture, bounds.Left + bounds.Width * 0.5f + (position.X - 320) * boundsScaling, bounds.Top + position.Y * boundsScaling, origin.X, origin.Y, scale.X * boundsScaling, scale.Y * boundsScaling, rotation, finalColor); }
public override void GenerateStates(double time, CameraState cameraState, Object3dState object3dState) { var wvp = object3dState.WorldTransform * cameraState.ViewProjection; var startVector = cameraState.ToScreen(wvp, StartPosition.ValueAt(time)); var endVector = cameraState.ToScreen(wvp, EndPosition.ValueAt(time)); var delta = endVector.Xy - startVector.Xy; var length = delta.Length; if (length == 0) { return; } var angle = Math.Atan2(delta.Y, delta.X); var rotation = InterpolatingFunctions.DoubleAngle(Generator.EndState?.Rotation ?? 0, angle, 1); var bitmap = StoryboardObjectGenerator.Current.GetMapsetBitmap(sprite.GetTexturePathAt(time)); var scale = new Vector2(length / bitmap.Width, Thickness.ValueAt(time)); var opacity = startVector.W < 0 && endVector.W < 0 ? 0 : object3dState.Opacity; if (UseDistanceFade) { opacity *= Math.Max(cameraState.OpacityAt(startVector.W), cameraState.OpacityAt(endVector.W)); } Vector2 position; switch (sprite.Origin) { default: case OsbOrigin.TopLeft: case OsbOrigin.CentreLeft: case OsbOrigin.BottomLeft: position = startVector.Xy; break; case OsbOrigin.TopCentre: case OsbOrigin.Centre: case OsbOrigin.BottomCentre: position = startVector.Xy + delta * 0.5f; break; case OsbOrigin.TopRight: case OsbOrigin.CentreRight: case OsbOrigin.BottomRight: position = endVector.Xy; break; } Generator.Add(new CommandGenerator.State() { Time = time, Position = position, Scale = scale, Rotation = rotation, Color = object3dState.Color, Opacity = opacity, Additive = Additive, }); }
public static void Draw(DrawContext drawContext, Camera camera, Box2 bounds, float opacity, Project project, OsbSprite sprite) { var time = project.DisplayTime * 1000; if (sprite.TexturePath == null || !sprite.IsActive(time)) { return; } var fade = sprite.OpacityAt(time); if (fade < 0.00001f) { return; } var scale = (Vector2)sprite.ScaleAt(time); if (scale.X == 0 || scale.Y == 0) { return; } if (sprite.FlipHAt(time)) { scale.X = -scale.X; } if (sprite.FlipVAt(time)) { scale.Y = -scale.Y; } var fullPath = Path.Combine(project.MapsetPath, sprite.GetTexturePathAt(time)); Texture2d texture = null; try { texture = project.TextureContainer.Get(fullPath); } catch (IOException) { // Happens when another process is writing to the file, will try again later. return; } if (texture == null) { return; } var position = sprite.PositionAt(time); var rotation = sprite.RotationAt(time); var color = sprite.ColorAt(time); var finalColor = ((Color4)color).WithOpacity(opacity * fade); var additive = sprite.AdditiveAt(time); Vector2 origin; switch (sprite.Origin) { default: case OsbOrigin.TopLeft: origin = new Vector2(0, 0); break; case OsbOrigin.TopCentre: origin = new Vector2(texture.Width * 0.5f, 0); break; case OsbOrigin.TopRight: origin = new Vector2(texture.Width, 0); break; case OsbOrigin.CentreLeft: origin = new Vector2(0, texture.Height * 0.5f); break; case OsbOrigin.Centre: origin = new Vector2(texture.Width * 0.5f, texture.Height * 0.5f); break; case OsbOrigin.CentreRight: origin = new Vector2(texture.Width, texture.Height * 0.5f); break; case OsbOrigin.BottomLeft: origin = new Vector2(0, texture.Height); break; case OsbOrigin.BottomCentre: origin = new Vector2(texture.Width * 0.5f, texture.Height); break; case OsbOrigin.BottomRight: origin = new Vector2(texture.Width, texture.Height); break; } var boundsScaling = bounds.Height / 480; DrawState.Prepare(drawContext.Get <SpriteRenderer>(), camera, additive ? AdditiveStates : AlphaBlendStates) .Draw(texture, bounds.Left + bounds.Width * 0.5f + (position.X - 320) * boundsScaling, bounds.Top + position.Y * boundsScaling, origin.X, origin.Y, scale.X * boundsScaling, scale.Y * boundsScaling, rotation, finalColor); }
public override void GenerateStates(double time, CameraState cameraState, Object3dState object3dState) { var wvp = object3dState.WorldTransform * cameraState.ViewProjection; if (FixedEdge >= 0) { edgeIndex = FixedEdge; } Vector4 vector0, vector1, vector2; switch (edgeIndex) { case 0: vector0 = cameraState.ToScreen(wvp, Position0.ValueAt(time)); vector1 = cameraState.ToScreen(wvp, Position1.ValueAt(time)); vector2 = cameraState.ToScreen(wvp, Position2.ValueAt(time)); break; case 1: vector2 = cameraState.ToScreen(wvp, Position0.ValueAt(time)); vector0 = cameraState.ToScreen(wvp, Position1.ValueAt(time)); vector1 = cameraState.ToScreen(wvp, Position2.ValueAt(time)); break; case 2: vector1 = cameraState.ToScreen(wvp, Position0.ValueAt(time)); vector2 = cameraState.ToScreen(wvp, Position1.ValueAt(time)); vector0 = cameraState.ToScreen(wvp, Position2.ValueAt(time)); break; default: throw new InvalidOperationException(); } var cross = (vector2.X - vector0.X) * (vector1.Y - vector0.Y) - (vector2.Y - vector0.Y) * (vector1.X - vector0.X); if (cross > 0) { if (Generator0.EndState != null) { Generator0.EndState.Opacity = 0; } if (Generator1.EndState != null) { Generator1.EndState.Opacity = 0; } return; } var bitmap = StoryboardObjectGenerator.Current.GetMapsetBitmap(sprite0.GetTexturePathAt(time)); var switchedEdge = false; for (var i = 0; i < 3; i++) { var delta = vector2.Xy - vector0.Xy; var deltaLength = delta.Length; var normalizedDelta = delta / deltaLength; var delta2 = vector1.Xy - vector0.Xy; var dot = Vector2.Dot(normalizedDelta, delta2); if (dot <= 0 || dot > deltaLength) { if (FixedEdge >= 0) { if (Generator0.EndState != null) { Generator0.EndState.Opacity = 0; } if (Generator1.EndState != null) { Generator1.EndState.Opacity = 0; } break; } var temp = vector0; vector0 = vector1; vector1 = vector2; vector2 = temp; edgeIndex = (edgeIndex + 1) % 3; switchedEdge = true; continue; } var position = project(vector0.Xy, vector2.Xy, vector1.Xy); var scale0 = new Vector2((vector2.Xy - position).Length / bitmap.Width, (vector1.Xy - position).Length / bitmap.Height); var scale1 = new Vector2((vector0.Xy - position).Length / bitmap.Width, scale0.Y); var angle = Math.Atan2(delta.Y, delta.X); var rotation = InterpolatingFunctions.DoubleAngle(Generator0.EndState?.Rotation ?? 0, angle, 1); var opacity = vector0.W < 0 && vector1.W < 0 && vector2.W < 0 ? 0 : object3dState.Opacity; if (UseDistanceFade) { opacity *= (cameraState.OpacityAt(vector0.W) + cameraState.OpacityAt(vector1.W) + cameraState.OpacityAt(vector2.W)) / 3; } if (switchedEdge) { if (Generator0.EndState != null) { Generator0.EndState.Opacity = 0; } if (Generator1.EndState != null) { Generator1.EndState.Opacity = 0; } } Generator0.Add(new CommandGenerator.State() { Time = time, Position = position, Scale = scale0, Rotation = rotation, Color = object3dState.Color, Opacity = switchedEdge ? 0 : opacity, Additive = Additive, }); Generator1.Add(new CommandGenerator.State() { Time = time, Position = position, Scale = scale1, Rotation = rotation, Color = object3dState.Color, Opacity = switchedEdge ? 0 : opacity, Additive = Additive, FlipH = true, }); break; } }