Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ private void Update() {
AddPropertyIfNotDefault("Plane", appearance.Plane, MutableAppearance.Default.Plane);
AddPropertyIfNotDefault("Blend Mode", appearance.BlendMode, MutableAppearance.Default.BlendMode);
AddPropertyIfNotDefault("Appearance Flags", appearance.AppearanceFlags, MutableAppearance.Default.AppearanceFlags);
AddPropertyIfNotDefault("Vis Flags", appearance.VisFlags, MutableAppearance.Default.VisFlags);
AddPropertyIfNotDefault("Invisibility", appearance.Invisibility, MutableAppearance.Default.Invisibility);
AddPropertyIfNotDefault("Opacity", appearance.Opacity, MutableAppearance.Default.Opacity);
AddPropertyIfNotDefault("Override", appearance.Override, MutableAppearance.Default.Override);
Expand Down
28 changes: 13 additions & 15 deletions OpenDreamClient/Rendering/DreamIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ private set {

private DMIResource? _dmi;

public int AnimationFrame {
get {
UpdateAnimation();
return _animationFrame;
}
}

[ViewVariables]
public ImmutableAppearance? Appearance {
get => CalculateAnimatedAppearance();
Expand Down Expand Up @@ -95,9 +88,9 @@ public void Dispose() {

var dmi = flick?.Icon ?? DMI;
var iconState = flick?.IconState ?? _iconState;
var animationFrame = flick?.GetAnimationFrame(gameTiming) ?? AnimationFrame;
var animationFrame = flick?.GetAnimationFrame(gameTiming) ?? GetAnimationFrame();
if (animationFrame == -1) // A flick returns -1 for a finished animation
animationFrame = AnimationFrame;
animationFrame = GetAnimationFrame();
if (CachedTexture != null && !_textureDirty && flick == null)
return CachedTexture.Texture;

Expand Down Expand Up @@ -221,16 +214,19 @@ public void GetWorldAABB(Vector2 worldPos, ref Box2? aabb) {
}
}

private void UpdateAnimation() {
public int GetAnimationFrame() => GetAnimationFrame(_iconState, _direction);

public int GetAnimationFrame(string? iconState, AtomDirection dir) {
if(DMI == null || Appearance == null || _animationComplete)
return;
return _animationFrame;

DMIParser.ParsedDMIState? dmiState = DMI.Description.GetStateOrDefault(_iconState);
DMIParser.ParsedDMIState? dmiState = DMI.Description.GetStateOrDefault(iconState);
if(dmiState == null)
return;
DMIParser.ParsedDMIFrame[] frames = dmiState.GetFrames(_direction);
return _animationFrame;
DMIParser.ParsedDMIFrame[] frames = dmiState.GetFrames(dir);

if (frames.Length <= 1) return;
if (frames.Length <= 1)
return 0;

var oldFrame = _animationFrame;
var currentGameTicks = gameTiming.CurTime.Ticks;
Expand All @@ -253,6 +249,8 @@ private void UpdateAnimation() {

if (oldFrame != _animationFrame)
DirtyTexture();

return _animationFrame;
}

private ImmutableAppearance? CalculateAnimatedAppearance() {
Expand Down
4 changes: 3 additions & 1 deletion OpenDreamClient/Rendering/DreamPlane.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ public void DrawMouseMap(DrawingHandleWorld handle, DreamViewOverlay overlay, Ve
texture = Texture.White;
}

var pos = (sprite.Position - worldAABB.BottomLeft) * overlay.IconSize;
var posOffset = -worldAABB.BottomLeft + sprite.RenderPosOffset;

var pos = (sprite.Position + posOffset) * overlay.IconSize;
if (sprite.MainIcon != null)
pos += sprite.MainIcon.TextureRenderOffset;

Expand Down
116 changes: 88 additions & 28 deletions OpenDreamClient/Rendering/DreamViewOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,24 @@ private void DrawAll(OverlayDrawArgs args, EntityUid eye, Vector2i viewportSize)
}

//handles underlays, overlays, appearance flags, images. Adds them to the result list, so they can be sorted and drawn with DrawIcon()
private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid uid, bool isScreen, ref int tieBreaker, List<RendererMetaData> result, sbyte seeVis, RendererMetaData? parentIcon = null, bool keepTogether = false, Vector3? turfCoords = null, ClientAppearanceSystem.Flick? flick = null) {
private void ProcessIconComponents(
DreamIcon icon,
Vector2 position,
EntityUid uid,
bool isScreen,
bool isVisChild, // DIRECT vis_contents child
ref int tieBreaker,
List<RendererMetaData> result,
sbyte seeVis,
RendererMetaData? parentIcon = null,
bool keepTogether = false,
Vector3? turfCoords = null,
ClientAppearanceSystem.Flick? flick = null
) {
if (icon.Appearance is null) //in the event that appearance hasn't loaded yet
return;
if ((isVisChild || (parentIcon?.IsVisContent ?? false)) && (icon.Appearance.VisFlags & VisFlags.Hide) != 0)
return;

result.EnsureCapacity(result.Count + icon.Underlays.Count + icon.Overlays.Count + 1);
RendererMetaData current = RentRendererMetaData();
Expand All @@ -211,6 +226,8 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
current.TieBreaker = tieBreaker;
current.RenderSource = icon.Appearance.RenderSource;
current.RenderTarget = icon.Appearance.RenderTarget;
current.VisFlags = icon.Appearance.VisFlags;
current.IsVisContent = parentIcon?.IsVisContent ?? false;
current.AppearanceFlags = icon.Appearance.AppearanceFlags;
current.BlendMode = icon.Appearance.BlendMode;
current.Flick = flick;
Expand All @@ -223,8 +240,24 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
);

if (parentIcon != null) {
current.ClickUid = parentIcon.ClickUid;
current.MouseOpacity = parentIcon.MouseOpacity;
if(isVisChild) {
current.IsVisContent = true;
var visFlags = current.VisFlags;
current.ClickUid = (visFlags & VisFlags.InheritId) != 0 ? parentIcon.ClickUid : current.ClickUid;
current.MouseOpacity = (visFlags & VisFlags.InheritId) != 0 ? parentIcon.MouseOpacity : icon.Appearance.MouseOpacity;

if((visFlags & (VisFlags.InheritIcon | VisFlags.InheritIconState | VisFlags.InheritDir)) != 0) {
var usedIcon = (visFlags & VisFlags.InheritIcon) != 0 ? parentIcon.MainIcon! : current.MainIcon;
var usedIconState = (visFlags & VisFlags.InheritIconState) != 0 ? parentIcon.MainIcon!.Appearance!.IconState : current.MainIcon.Appearance.IconState;
var usedDir = (visFlags & VisFlags.InheritDir) != 0 ? parentIcon.MainIcon!.Appearance!.Direction : current.MainIcon.Appearance.Direction;

current.TextureOverride = usedIcon.DMI?.GetState(usedIconState)?.GetFrames(usedDir).ElementAtOrDefault(usedIcon.GetAnimationFrame(usedIconState, usedDir));
}
} else {
current.ClickUid = parentIcon.ClickUid;
current.MouseOpacity = parentIcon.MouseOpacity;
}

if ((icon.Appearance.AppearanceFlags & AppearanceFlags.ResetColor) != 0 || keepTogether) { //RESET_COLOR
current.ColorToApply = icon.Appearance.Color;
current.ColorMatrixToApply = icon.Appearance.ColorMatrix;
Expand All @@ -243,13 +276,16 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
else
current.TransformToApply = iconAppearanceTransformMatrix * parentIcon.TransformToApply;

if ((icon.Appearance.Plane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit.
current.Plane = parentIcon.Plane + (icon.Appearance.Plane + 32767);
var effectivePlane = (isVisChild && (current.VisFlags & VisFlags.InheritPlane) != 0) ? parentIcon.Plane : icon.Appearance.Plane;
var effectiveLayer = (isVisChild && (current.VisFlags & VisFlags.InheritLayer) != 0) ? parentIcon.Layer : icon.Appearance.Layer;

if ((effectivePlane < -10000)) //FLOAT_PLANE - Note: yes, this really is how it works. Yes it's dumb as shit.
current.Plane = parentIcon.Plane + (effectivePlane + 32767);
else
current.Plane = icon.Appearance.Plane;
current.Plane = effectivePlane;

//FLOAT_LAYER - if this icon's layer is negative, it's a float layer so set it's layer equal to the parent object and sort through the float_layer shit later
current.Layer = (icon.Appearance.Layer < 0) ? parentIcon.Layer : icon.Appearance.Layer;
current.Layer = (effectiveLayer < 0) ? parentIcon.Layer : effectiveLayer;

if (current.BlendMode == BlendMode.Default)
current.BlendMode = parentIcon.BlendMode;
Expand Down Expand Up @@ -294,6 +330,35 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
result.Add(renderTargetPlaceholder);
}

// vis_contents are split with VIS_UNDERLAY, so we need to do this early
var visContents = icon.Appearance.VisContents;
List<EntityUid>? underContents = null;
List<EntityUid>? overContents = null;
foreach(var visContent in visContents) {
EntityUid visContentEntity = _entityManager.GetEntity(visContent);
if (!_spriteQuery.TryGetComponent(visContentEntity, out var sprite))
continue;

var appearance = sprite.Icon.Appearance;
if(appearance is null || (appearance.VisFlags & VisFlags.Hide) != 0) // don't waste our time
continue;
if (!_spriteSystem.IsVisible(sprite, null, seeVis, null))
continue;

if((appearance.VisFlags & VisFlags.Underlay) == 0)
(overContents ??= new(visContents.Length)).Add(visContentEntity);
else
(underContents ??= new(visContents.Length)).Add(visContentEntity);
}

//underlay vis_contents are rendered first, before the underlays even
if(underContents is not null) {
foreach(var visContentEntity in underContents) {
var sprite = _spriteQuery.GetComponent(visContentEntity);
ProcessIconComponents(sprite.Icon, position, visContentEntity, false, true, ref tieBreaker, result, seeVis, current, keepTogether);
}
}

//underlays - colour, alpha, and transform are inherited, but filters aren't
//underlays are sorted in reverse order to overlays
for(int underlayIndex = icon.Underlays.Count-1; underlayIndex >= 0; underlayIndex--) {
Expand All @@ -308,10 +373,10 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
(underlay.Appearance.AppearanceFlags & AppearanceFlags.KeepApart) != 0;

if (!keepTogether || keepApart) { //KEEP_TOGETHER wasn't set on our parent, or KEEP_APART
ProcessIconComponents(underlay, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current);
ProcessIconComponents(underlay, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current);
} else {
current.KeepTogetherGroup ??= new();
ProcessIconComponents(underlay, current.Position, uid, isScreen, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether);
ProcessIconComponents(underlay, current.Position, uid, isScreen, false, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether);
}
}

Expand All @@ -330,10 +395,10 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
(overlay.Appearance.AppearanceFlags & AppearanceFlags.KeepApart) != 0;

if (!keepTogether || keepApart) { //KEEP_TOGETHER wasn't set on our parent, or KEEP_APART
ProcessIconComponents(overlay, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current);
ProcessIconComponents(overlay, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current);
} else {
current.KeepTogetherGroup ??= new();
ProcessIconComponents(overlay, current.Position, uid, isScreen, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether);
ProcessIconComponents(overlay, current.Position, uid, isScreen, false, ref tieBreaker, current.KeepTogetherGroup, seeVis, current, keepTogether);
}
}

Expand All @@ -351,21 +416,16 @@ private void ProcessIconComponents(DreamIcon icon, Vector2 position, EntityUid u
current.MainIcon = sprite.Icon;
current.Position += (sprite.Icon.Appearance.TotalPixelOffset / (float)IconSize);
} else
ProcessIconComponents(sprite.Icon, current.Position, uid, isScreen, ref tieBreaker, result, seeVis, current);
ProcessIconComponents(sprite.Icon, current.Position, uid, isScreen, false, ref tieBreaker, result, seeVis, current);
}
}

foreach (var visContent in icon.Appearance.VisContents) {
EntityUid visContentEntity = _entityManager.GetEntity(visContent);
if (!_spriteQuery.TryGetComponent(visContentEntity, out var sprite))
continue;
if (!_spriteSystem.IsVisible(sprite, null, seeVis, null))
continue;

ProcessIconComponents(sprite.Icon, position, visContentEntity, false, ref tieBreaker, result, seeVis, current, keepTogether);

// TODO: click uid should be set to current.uid again
// TODO: vis_flags
// overlay vis_contents are rendered last
if(overContents is not null) {
foreach(var visContentEntity in overContents) {
var sprite = _spriteQuery.GetComponent(visContentEntity);
ProcessIconComponents(sprite.Icon, position, visContentEntity, false, true, ref tieBreaker, result, seeVis, current, keepTogether);
}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}

//maptext is basically just an image of rendered text added as an overlay
Expand Down Expand Up @@ -453,7 +513,7 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende
// For now, just generate a buffer of 128 pixels around the main icon...
Vector2i ktSize = (256, 256) + iconMetaData.MainIcon?.DMI?.IconSize ?? (0,0);
iconMetaData.TextureOverride = ProcessKeepTogether(handle, iconMetaData, ktSize);
positionOffset -= ((ktSize/IconSize) - Vector2.One) * new Vector2(0.5f); //correct for KT group texture offset
iconMetaData.RenderPosOffset -= ((ktSize/IconSize) - Vector2.One) * new Vector2(0.5f); //correct for KT group texture offset
}

//Maptext
Expand All @@ -472,7 +532,7 @@ public void DrawIcon(DrawingHandleWorld handle, Vector2i renderTargetSize, Rende
}

var frame = iconMetaData.GetTexture(this, handle);
var pixelPosition = (iconMetaData.Position + positionOffset) * IconSize;
var pixelPosition = (iconMetaData.Position + (positionOffset + iconMetaData.RenderPosOffset)) * IconSize;

//if frame is null, this doesn't require a draw, so return NOP
if (frame == null)
Expand Down Expand Up @@ -648,7 +708,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU
tValue = 0;
//pass the turf coords for client.images lookup
Vector3 turfCoords = new Vector3(tileRef.X, tileRef.Y, (int) worldPos.MapId);
ProcessIconComponents(_appearanceSystem.GetTurfIcon((uint)tileRef.Tile.TypeId), worldPos.Position - Vector2.One, EntityUid.Invalid, false, ref tValue, _spriteContainer, seeVis, turfCoords: turfCoords, flick: flick);
ProcessIconComponents(_appearanceSystem.GetTurfIcon((uint)tileRef.Tile.TypeId), worldPos.Position - Vector2.One, EntityUid.Invalid, false, false, ref tValue, _spriteContainer, seeVis, turfCoords: turfCoords, flick: flick);
}

// Visible entities
Expand Down Expand Up @@ -679,7 +739,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU
var flick = _appearanceSystem.GetMovableFlick(entity);

tValue = 0;
ProcessIconComponents(sprite.Icon, worldPos - new Vector2(0.5f), entity, false, ref tValue, _spriteContainer, seeVis, flick: flick);
ProcessIconComponents(sprite.Icon, worldPos - new Vector2(0.5f), entity, false, false, ref tValue, _spriteContainer, seeVis, flick: flick);
}
}

Expand All @@ -701,7 +761,7 @@ private void CollectVisibleSprites(ViewAlgorithm.Tile?[,] tiles, EntityUid gridU
for (int x = 0; x < sprite.ScreenLocation.RepeatX; x++) {
for (int y = 0; y < sprite.ScreenLocation.RepeatY; y++) {
tValue = 0;
ProcessIconComponents(sprite.Icon, position + iconSize * new Vector2(x, y), uid, true, ref tValue, _spriteContainer, seeVis);
ProcessIconComponents(sprite.Icon, position + iconSize * new Vector2(x, y), uid, true, false, ref tValue, _spriteContainer, seeVis);
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions OpenDreamClient/Rendering/RendererMetaData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ internal sealed class RendererMetaData : IComparable<RendererMetaData> {
public ColorMatrix ColorMatrixToApply;
public float AlphaToApply;
public Matrix3x2 TransformToApply;
public Vector2 RenderPosOffset;
public string? RenderSource;
public string? RenderTarget;
public List<RendererMetaData>? KeepTogetherGroup;
public VisFlags VisFlags;
public bool IsVisContent;
public AppearanceFlags AppearanceFlags;
public BlendMode BlendMode;
public MouseOpacity MouseOpacity;
Expand Down Expand Up @@ -50,9 +53,12 @@ public void Reset() {
ColorMatrixToApply = ColorMatrix.Identity;
AlphaToApply = 1.0f;
TransformToApply = Matrix3x2.Identity;
RenderPosOffset = Vector2.Zero;
RenderSource = "";
RenderTarget = "";
KeepTogetherGroup = null; //don't actually need to allocate this 90% of the time
VisFlags = VisFlags.None;
IsVisContent = false;
AppearanceFlags = AppearanceFlags.None;
BlendMode = BlendMode.Default;
MouseOpacity = MouseOpacity.Transparent;
Expand Down
Loading
Loading