mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Post shader will use real sprite bounding box (#1536)
Co-authored-by: Alex Evgrashin <evgrashin.adl@gmail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
@@ -222,6 +222,12 @@ namespace Robust.Client.GameObjects
|
||||
ISpriteLayer this[object layerKey] { get; }
|
||||
|
||||
IEnumerable<ISpriteLayer> AllLayers { get; }
|
||||
|
||||
int GetLayerDirectionCount(ISpriteLayer layer);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate sprite bounding box in world-space coordinates.
|
||||
/// </summary>
|
||||
Box2 CalculateBoundingBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,17 @@ namespace Robust.Client.GameObjects
|
||||
RSI.State.Direction EffectiveDirection(Angle worldRotation);
|
||||
|
||||
Vector2 LocalToLayer(Vector2 localPos);
|
||||
|
||||
/// <summary>
|
||||
/// Layer size in pixels.
|
||||
/// Don't account layer scale or sprite world transform.
|
||||
/// </summary>
|
||||
Vector2i PixelSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculate layer bounding box in sprite local-space coordinates.
|
||||
/// </summary>
|
||||
/// <returns>Bounding box in sprite local-space coordinates.</returns>
|
||||
Box2 CalculateBoundingBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1604,6 +1604,39 @@ namespace Robust.Client.GameObjects
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box2 CalculateBoundingBox()
|
||||
{
|
||||
// fast check for empty sprites
|
||||
if (Layers.Count == 0)
|
||||
return new Box2();
|
||||
|
||||
// we need to calculate bounding box taking into account all nested layers
|
||||
// because layers can have offsets, scale or rotation we need to calculate a new BB
|
||||
// based on lowest bottomLeft and hightest topRight points from all layers
|
||||
var box = Layers[0].CalculateBoundingBox();
|
||||
|
||||
for (int i = 1; i < Layers.Count; i++)
|
||||
{
|
||||
var layer = Layers[i];
|
||||
var layerBB = layer.CalculateBoundingBox();
|
||||
|
||||
box = box.Union(layerBB);
|
||||
}
|
||||
|
||||
// apply sprite transformations and calculate sprite bounding box
|
||||
// we can optimize it a bit, if sprite doesn't have rotation
|
||||
var spriteBox = box.Scale(Scale);
|
||||
var spriteHasRotation = !Rotation.EqualsApprox(Angle.Zero);
|
||||
var spriteBB = spriteHasRotation ?
|
||||
new Box2Rotated(spriteBox, Rotation).CalcBoundingBox() : spriteBox;
|
||||
|
||||
// move it all to world transform system (with sprite offset)
|
||||
var worldPosition = Owner.Transform.WorldPosition;
|
||||
var worldBB = spriteBB.Translated(Offset + worldPosition);
|
||||
return worldBB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum to "offset" a cardinal direction.
|
||||
/// </summary>
|
||||
@@ -1889,6 +1922,33 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Vector2i PixelSize
|
||||
{
|
||||
get
|
||||
{
|
||||
var pixelSize = Vector2i.Zero;
|
||||
if (Texture != null)
|
||||
{
|
||||
pixelSize = Texture.Size;
|
||||
}
|
||||
else if (ActualRsi != null)
|
||||
{
|
||||
pixelSize = ActualRsi.Size;
|
||||
}
|
||||
|
||||
return pixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Box2 CalculateBoundingBox()
|
||||
{
|
||||
// TODO: scale & rotation for layers is currently unimplemented.
|
||||
return Box2.CenteredAround(Offset, PixelSize / EyeManager.PixelsPerMeter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void IAnimationProperties.SetAnimatableProperty(string name, object value)
|
||||
|
||||
@@ -183,27 +183,61 @@ namespace Robust.Client.Graphics.Clyde
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
RenderTexture? entityPostRenderTarget = null;
|
||||
Vector2i roundedPos = default;
|
||||
if (entry.sprite.PostShader != null)
|
||||
{
|
||||
_renderHandle.UseRenderTarget(EntityPostRenderTarget);
|
||||
_renderHandle.Clear(new Color());
|
||||
// Calculate viewport so that the entity thinks it's drawing to the same position,
|
||||
// which is necessary for light application,
|
||||
// but it's ACTUALLY drawing into the center of the render target.
|
||||
var spritePos = entry.sprite.Owner.Transform.WorldPosition;
|
||||
var screenPos = _eyeManager.WorldToScreen(spritePos);
|
||||
var (roundedX, roundedY) = roundedPos = (Vector2i) screenPos;
|
||||
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
|
||||
flippedPos -= EntityPostRenderTarget.Size / 2;
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
// calculate world bounding box
|
||||
var spriteBB = entry.sprite.CalculateBoundingBox();
|
||||
var spriteLB = spriteBB.BottomLeft;
|
||||
var spriteRT = spriteBB.TopRight;
|
||||
|
||||
// finally we can calculate screen bounding in pixels
|
||||
var screenLB = _eyeManager.WorldToScreen(spriteLB);
|
||||
var screenRT = _eyeManager.WorldToScreen(spriteRT);
|
||||
|
||||
// we need to scale RT a for effects like emission or highlight
|
||||
// scale can be passed with PostShader as variable in future
|
||||
var postShadeScale = 1.25f;
|
||||
var screenSpriteSize = (Vector2i)((screenRT - screenLB) * postShadeScale).Rounded();
|
||||
screenSpriteSize.Y = -screenSpriteSize.Y;
|
||||
|
||||
// I'm not 100% sure why it works, but without it post-shader
|
||||
// can be lower or upper by 1px than original sprite depending on sprite rotation or scale
|
||||
// probably some rotation rounding error
|
||||
if (screenSpriteSize.X % 2 != 0)
|
||||
screenSpriteSize.X++;
|
||||
if (screenSpriteSize.Y % 2 != 0)
|
||||
screenSpriteSize.Y++;
|
||||
|
||||
// check that sprite size is valid
|
||||
if (screenSpriteSize.X > 0 && screenSpriteSize.Y > 0)
|
||||
{
|
||||
// create new render texture with correct sprite size
|
||||
entityPostRenderTarget = CreateRenderTarget(screenSpriteSize,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true),
|
||||
name: nameof(entityPostRenderTarget));
|
||||
_renderHandle.UseRenderTarget(entityPostRenderTarget);
|
||||
_renderHandle.Clear(new Color());
|
||||
|
||||
// Calculate viewport so that the entity thinks it's drawing to the same position,
|
||||
// which is necessary for light application,
|
||||
// but it's ACTUALLY drawing into the center of the render target.
|
||||
var spritePos = spriteBB.Center;
|
||||
var screenPos = _eyeManager.WorldToScreen(spritePos);
|
||||
var (roundedX, roundedY) = roundedPos = (Vector2i)screenPos;
|
||||
var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY);
|
||||
flippedPos -= entityPostRenderTarget.Size / 2;
|
||||
_renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize));
|
||||
}
|
||||
}
|
||||
|
||||
var matrix = entry.worldMatrix;
|
||||
var worldPosition = new Vector2(matrix.R0C2, matrix.R1C2);
|
||||
entry.sprite.Render(_renderHandle.DrawingHandleWorld, in entry.worldRotation, in worldPosition);
|
||||
|
||||
if (entry.sprite.PostShader != null)
|
||||
if (entry.sprite.PostShader != null && entityPostRenderTarget != null)
|
||||
{
|
||||
var oldProj = _currentMatrixProj;
|
||||
var oldView = _currentMatrixView;
|
||||
@@ -216,11 +250,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.SetProjView(proj, view);
|
||||
_renderHandle.SetModelTransform(Matrix3.Identity);
|
||||
|
||||
var rounded = roundedPos - EntityPostRenderTarget.Size / 2;
|
||||
var rounded = roundedPos - entityPostRenderTarget.Size / 2;
|
||||
|
||||
var box = Box2i.FromDimensions(rounded, EntityPostRenderTarget.Size);
|
||||
var box = Box2i.FromDimensions(rounded, entityPostRenderTarget.Size);
|
||||
|
||||
_renderHandle.DrawTextureScreen(EntityPostRenderTarget.Texture,
|
||||
_renderHandle.DrawTextureScreen(entityPostRenderTarget.Texture,
|
||||
box.BottomLeft, box.BottomRight, box.TopLeft, box.TopRight,
|
||||
Color.White, null);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
@@ -39,8 +39,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private GLUniformBuffer<ProjViewMatrices> ProjViewUBO = default!;
|
||||
private GLUniformBuffer<UniformConstants> UniformConstantsUBO = default!;
|
||||
|
||||
private RenderTexture EntityPostRenderTarget = default!;
|
||||
|
||||
private GLBuffer BatchVBO = default!;
|
||||
private GLBuffer BatchEBO = default!;
|
||||
private GLHandle BatchVAO;
|
||||
@@ -316,10 +314,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
ProjViewUBO = new GLUniformBuffer<ProjViewMatrices>(this, BindingIndexProjView, nameof(ProjViewUBO));
|
||||
UniformConstantsUBO = new GLUniformBuffer<UniformConstants>(this, BindingIndexUniformConstants, nameof(UniformConstantsUBO));
|
||||
|
||||
EntityPostRenderTarget = CreateRenderTarget(Vector2i.One * 8 * EyeManager.PixelsPerMeter,
|
||||
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true),
|
||||
name: nameof(EntityPostRenderTarget));
|
||||
|
||||
CreateMainViewport();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -118,6 +118,12 @@ namespace Robust.Shared.Maths
|
||||
return FromDimensions(center - size / 2, size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Box2 CentredAroundZero(Vector2 size)
|
||||
{
|
||||
return FromDimensions(-size / 2, size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool Intersects(in Box2 other)
|
||||
{
|
||||
@@ -236,6 +242,16 @@ namespace Robust.Shared.Maths
|
||||
center + halfSize);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Box2 Scale(Vector2 scale)
|
||||
{
|
||||
var center = Center;
|
||||
var halfSize = (Size / 2) * scale;
|
||||
return new Box2(
|
||||
center - halfSize,
|
||||
center + halfSize);
|
||||
}
|
||||
|
||||
/// <summary>Returns a Box2 translated by the given amount.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Box2 Translated(Vector2 point)
|
||||
|
||||
Reference in New Issue
Block a user