Update Clickmaps for layer transforms (#6663)

This commit is contained in:
Leon Friedrich
2022-02-17 03:09:08 +13:00
committed by GitHub
parent ccc4bcbd7a
commit d9879ebf2a
2 changed files with 30 additions and 59 deletions

View File

@@ -2,11 +2,6 @@ using System;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using static Robust.Client.GameObjects.SpriteComponent;
namespace Content.Client.Clickable
@@ -18,7 +13,7 @@ namespace Content.Client.Clickable
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[ViewVariables] [DataField("bounds")] private DirBoundData? Bounds;
[ViewVariables] [DataField("bounds")] public DirBoundData? Bounds;
/// <summary>
/// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding
@@ -54,14 +49,17 @@ namespace Content.Client.Clickable
if (worldRot.Theta < 0)
worldRot = new Angle(worldRot.Theta + Math.Tau);
var invSpriteMatrix = Matrix3.CreateTransform(Vector2.Zero, -sprite.Rotation, (1,1)/sprite.Scale);
var invSpriteMatrix = Matrix3.Invert(sprite.GetLocalMatrix());
// This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites.
var relativeRotation = worldRot + _eyeManager.CurrentEye.Rotation;
// localPos is the clicked location in the entity's coordinate frame, but with the sprite offset removed.
var localPos = transform.InvWorldMatrix.Transform(worldPos) - sprite.Offset;
// First we get `localPos`, the clicked location in the sprite-coordinate frame.
var entityXform = Matrix3.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -_eyeManager.CurrentEye.Rotation : worldRot);
var localPos = invSpriteMatrix.Transform(entityXform.Transform(worldPos));
// Check explicitly defined click-able bounds
if (CheckDirBound(sprite, relativeRotation, localPos, invSpriteMatrix))
if (CheckDirBound(sprite, relativeRotation, localPos))
return true;
// Next check each individual sprite layer using automatically computed click maps.
@@ -70,58 +68,39 @@ namespace Content.Client.Clickable
if (!spriteLayer.Visible || spriteLayer is not Layer layer)
continue;
// How many orientations does this rsi have?
var dirCount = sprite.GetLayerDirectionCount(layer);
// If the sprite does not actually rotate we need to fix the rotation that was added to localPos via invSpriteMatrix
var modAngle = Angle.Zero;
if (sprite.NoRotation)
modAngle += CalcRectWorldAngle(relativeRotation, dirCount);
// Check the layer's texture, if it has one
if (layer.Texture != null)
{
// Convert to sprite-coordinates. This includes sprite matrix (scale, rotation), and noRot corrections (modAngle)
// Recall that the sprite offset was already removed
var spritePos = invSpriteMatrix.Transform(modAngle.RotateVec(localPos));
// Convert to image coordinates
var imagePos = (Vector2i) (spritePos * EyeManager.PixelsPerMeter * (1, -1) + layer.Texture.Size / 2f);
var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * (1, -1) + layer.Texture.Size / 2f);
if (_clickMapManager.IsOccluding(layer.Texture, imagePos))
return true;
}
// As the texture failed, we check the RSI next
if (layer.State == null || layer.ActualRsi is not RSI rsi || !rsi.TryGetState(layer.State, out var state))
// Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next
if (layer.State == null || layer.ActualRsi is not RSI rsi || !rsi.TryGetState(layer.State, out var rsiState))
continue;
// Get the sprite direction, in the absence of any direction offset or override. this is just for
// determining the angle-snapping correction for directional sprites.
var dir = relativeRotation.ToRsiDirection(state.Directions);
var dir = (rsiState.Directions == RSI.State.DirectionType.Dir1)
? RSI.State.Direction.South
: relativeRotation.ToRsiDirection(rsiState.Directions);
// Add the correction to the sprite angle due to the fact that it is a directional sprite. This is some
// multiple of 90 or 45 degrees for 4/8 directional sprites, or 0 for one-directional sprites.
modAngle += dir.Convert().ToAngle();
// TODO SPRITE LAYER ROTATION
// Currently this doesn't support layers with scale & rotation. Whenever/if-ever that should be added,
// this needs fixing. See also Layer.CalculateBoundingBox and other engine code with similar warnings.
// Convert to sprite-coordinates. This is the same as for the texture check, but now also includes
// directional angle-snapping corrections in modAngle (+ the previous NoRot stuff)
var layerPos = invSpriteMatrix.Transform(modAngle.RotateVec(localPos));
// convert to layer-local coordinates
layer.GetLayerDrawMatrix(dir, out var matrix);
var inverseMatrix = Matrix3.Invert(matrix);
var layerLocal = inverseMatrix.Transform(localPos);
// Convert to image coordinates
var layerImagePos = (Vector2i) (layerPos * EyeManager.PixelsPerMeter * (1, -1) + layer.ActualRsi.Size / 2f);
var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * (1, -1) + rsiState.Size / 2f);
// Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen.
// This **can** differ from the dir defined before, but can also just be the same.
if (sprite.EnableDirectionOverride)
dir = sprite.DirectionOverride.Convert(state.Directions);;
dir = sprite.DirectionOverride.Convert(rsiState.Directions);;
dir = dir.OffsetRsiDir(layer.DirOffset);
if (_clickMapManager.IsOccluding(layer.ActualRsi, layer.State, dir, layer.AnimationFrame, layerImagePos))
if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos))
return true;
}
@@ -130,28 +109,22 @@ namespace Content.Client.Clickable
return false;
}
public bool CheckDirBound(ISpriteComponent sprite, Angle relativeRotation, Vector2 localPos, Matrix3 spriteMatrix)
public bool CheckDirBound(ISpriteComponent sprite, Angle relativeRotation, Vector2 localPos)
{
if (Bounds == null)
return false;
// Here we get sprite orientation, either from an explicit override or just from the relative rotation
// These explicit bounds only work for either 1 or 4 directional sprites.
// This would be the orientation of a 4-directional sprite.
var direction = relativeRotation.GetCardinalDir();
// Assuming the sprite snaps to 4 orientations we need to adjust our localPos relative to the entity by 90
// degree steps. Effectively, this accounts for the fact that the entity's world rotation + sprite rotation
// does not match the **actual** drawn rotation.
var modAngle = direction.ToAngle();
// If our sprite does not rotate at all, we shouldn't have been bothering with all that rotation logic,
// but it sorta came for free with the matrix transform, but we gotta undo that now.
if (sprite.NoRotation)
modAngle += CalcRectWorldAngle(relativeRotation, 4);
var spritePos = spriteMatrix.Transform(modAngle.RotateVec(localPos));
var modLocalPos = sprite.NoRotation
? localPos
: direction.ToAngle().RotateVec(localPos);
// First, check the bounding box that is valid for all orientations
if (Bounds.All.Contains(spritePos))
if (Bounds.All.Contains(modLocalPos))
return true;
// Next, get and check the appropriate bounding box for the current sprite orientation
@@ -164,7 +137,7 @@ namespace Content.Client.Clickable
_ => throw new InvalidOperationException()
};
return boundsForDir.Contains(spritePos);
return boundsForDir.Contains(modLocalPos);
}
[DataDefinition]

View File

@@ -18,5 +18,3 @@
base: state_
- type: Climbable
- type: Clickable
bounds:
all: "-0.5,-0.5,0.5,0.5"