diff --git a/OpenDreamClient/Rendering/DreamViewOverlay.cs b/OpenDreamClient/Rendering/DreamViewOverlay.cs index ae51b1b402..9116c3e6da 100644 --- a/OpenDreamClient/Rendering/DreamViewOverlay.cs +++ b/OpenDreamClient/Rendering/DreamViewOverlay.cs @@ -415,12 +415,15 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende //KEEP_TOGETHER groups if (iconMetaData.KeepTogetherGroup?.Count > 0) { // TODO: Use something better than a hardcoded 64x64 fallback - iconMetaData.TextureOverride = ProcessKeepTogether(handle, iconMetaData, iconMetaData.Texture?.Size ?? (64,64)); + Vector2i ktSize = iconMetaData.Texture?.Size ?? (64,64); + iconMetaData.TextureOverride = ProcessKeepTogether(handle, iconMetaData, ktSize); + positionOffset -= ((ktSize/EyeManager.PixelsPerMeter) - Vector2.One) * new Vector2(0.5f); //correct for KT group texture offset } var pixelPosition = (iconMetaData.Position + positionOffset) * EyeManager.PixelsPerMeter; var frame = iconMetaData.Texture; + //if frame is null, this doesn't require a draw, so return NOP if (frame == null) return; @@ -437,10 +440,10 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende if (goFastOverride || icon.Appearance == null || icon.Appearance.Filters.Count == 0) { //faster path for rendering unfiltered sprites - DrawIconFast(handle, renderTargetSize, frame, pixelPosition, GetBlendAndColorShader(iconMetaData)); + DrawIconFast(handle, renderTargetSize, frame, pixelPosition, iconMetaData.TransformToApply, GetBlendAndColorShader(iconMetaData)); } else { //Slower path for filtered icons - DrawIconSlow(handle, frame, iconMetaData, renderTargetSize, pixelPosition); + DrawIconSlow(handle, frame, iconMetaData, renderTargetSize, pixelPosition, iconMetaData.TransformToApply); } } @@ -748,8 +751,9 @@ private Texture ProcessKeepTogether(DrawingHandleWorld handle, RendererMetaData iconMetaData.AlphaToApply = 1f; iconMetaData.BlendMode = BlendMode.Default; - List ktItems = new List(iconMetaData.KeepTogetherGroup!.Count+1); - ktItems.Add(iconMetaData); + List ktItems = new List(iconMetaData.KeepTogetherGroup!.Count + 1) { + iconMetaData + }; ktItems.AddRange(iconMetaData.KeepTogetherGroup); iconMetaData.KeepTogetherGroup.Clear(); @@ -760,7 +764,7 @@ private Texture ProcessKeepTogether(DrawingHandleWorld handle, RendererMetaData handle.RenderInRenderTarget(tempTexture, () => { foreach (RendererMetaData ktItem in ktItems) { - DrawIcon(handle, tempTexture.Size, ktItem, -ktItem.Position); + DrawIcon(handle, tempTexture.Size, ktItem, -ktItem.Position+((tempTexture.Size/EyeManager.PixelsPerMeter) - Vector2.One) * new Vector2(0.5f)); //draw the icon in the centre of the KT render target } }, null); @@ -787,9 +791,25 @@ private Texture ProcessKeepTogether(DrawingHandleWorld handle, RendererMetaData /// Render a texture without applying any filters, making this faster and cheaper. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void DrawIconFast(DrawingHandleWorld handle, Vector2i renderTargetSize, Texture texture, Vector2 pos, ShaderInstance? shader) { + private static void DrawIconFast(DrawingHandleWorld handle, Vector2i renderTargetSize, Texture texture, Vector2 pos, Matrix3 transform, ShaderInstance? shader) { handle.UseShader(shader); - handle.SetTransform(CreateRenderTargetFlipMatrix(renderTargetSize, pos)); + + //extract scale component of transform + Vector2 scaleFactors = new Vector2( + MathF.Sqrt(MathF.Pow(transform.R0C0,2) + MathF.Pow(transform.R0C1,2)), + MathF.Sqrt(MathF.Pow(transform.R1C0,2) + MathF.Pow(transform.R1C1,2)) + ); + transform.R0C0 /= scaleFactors.X; + transform.R0C1 /= scaleFactors.X; + transform.R1C0 /= scaleFactors.Y; + transform.R1C1 /= scaleFactors.Y; + + handle.SetTransform( + Matrix3.CreateTranslation(-texture.Size/2) //translate to origin + * transform //rotate and translate + * Matrix3.CreateTranslation(texture.Size/2) //translate back to original position + * Matrix3.CreateScale(scaleFactors) //scale + * CreateRenderTargetFlipMatrix(renderTargetSize, pos-((scaleFactors-Vector2.One)*texture.Size/2))); //flip and apply scale-corrected translation handle.DrawTextureRect(texture, Box2.FromDimensions(Vector2.Zero, texture.Size)); } @@ -797,7 +817,7 @@ private static void DrawIconFast(DrawingHandleWorld handle, Vector2i renderTarge /// A slower method of drawing an icon. This one renders an atom's filters. /// Use instead if the icon has no special rendering needs. /// - private void DrawIconSlow(DrawingHandleWorld handle, Texture frame, RendererMetaData iconMetaData, Vector2i renderTargetSize, Vector2 pos) { + private void DrawIconSlow(DrawingHandleWorld handle, Texture frame, RendererMetaData iconMetaData, Vector2i renderTargetSize, Vector2 pos, Matrix3 transform) { //first we do ping pong rendering for the multiple filters // TODO: This should determine the size from the filters and their settings, not just double the original IRenderTexture ping = RentRenderTarget(frame.Size * 2); @@ -827,9 +847,7 @@ private void DrawIconSlow(DrawingHandleWorld handle, Texture frame, RendererMeta handle.UseShader(s); // Technically this should be ping.Size, but they are the same size so avoid the extra closure alloc - var transform = CreateRenderTargetFlipMatrix(pong.Size, Vector2.Zero); - - handle.SetTransform(transform); + handle.SetTransform(CreateRenderTargetFlipMatrix(pong.Size, Vector2.Zero)); handle.DrawTextureRect(pong.Texture, new Box2(Vector2.Zero, pong.Size)); }, Color.Black.WithAlpha(0)); @@ -837,7 +855,7 @@ private void DrawIconSlow(DrawingHandleWorld handle, Texture frame, RendererMeta } //then we draw the actual icon with filters applied - DrawIconFast(handle, renderTargetSize, pong.Texture, pos - frame.Size / 2, + DrawIconFast(handle, renderTargetSize, pong.Texture, pos - frame.Size / 2, transform, //note we apply the color *before* the filters, so we ignore color here GetBlendAndColorShader(iconMetaData, ignoreColor: true) );