Merge pull request #11572 from pchote/building-depth

Add building depth imposters for TS base structures.
This commit is contained in:
Oliver Brakmann
2016-07-14 11:10:13 +02:00
committed by GitHub
10 changed files with 459 additions and 65 deletions

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
@@ -43,16 +44,47 @@ namespace OpenRA.Graphics
public class SpriteCache
{
public readonly SheetBuilder SheetBuilder;
readonly Cache<string, Sprite[]> sprites;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
{
SheetBuilder = sheetBuilder;
sprites = new Cache<string, Sprite[]>(filename => SpriteLoader.GetSprites(fileSystem, filename, loaders, sheetBuilder));
this.fileSystem = fileSystem;
this.loaders = loaders;
}
public Sprite[] this[string filename] { get { return sprites[filename]; } }
Sprite[] LoadSprite(string filename, List<Sprite[]> cache)
{
var sprite = SpriteLoader.GetSprites(fileSystem, filename, loaders, SheetBuilder);
cache.Add(sprite);
return sprite;
}
/// <summary>Returns the first set of sprites with the given filename.</summary>
public Sprite[] this[string filename]
{
get
{
var allSprites = sprites.GetOrAdd(filename);
var sprite = allSprites.FirstOrDefault();
return sprite ?? LoadSprite(filename, allSprites);
}
}
/// <summary>Returns all instances of sets of sprites with the given filename</summary>
public IEnumerable<Sprite[]> AllCached(string filename)
{
return sprites.GetOrAdd(filename);
}
/// <summary>Loads and caches a new instance of sprites with the given filename</summary>
public Sprite[] Reload(string filename)
{
return LoadSprite(filename, sprites.GetOrAdd(filename));
}
}
public class FrameCache

View File

@@ -52,7 +52,9 @@ namespace OpenRA.Graphics
float3 ScreenPosition(WorldRenderer wr)
{
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size.XY).ToInt2();
return new float3(xy, wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
// HACK: The z offset needs to be applied somewhere, but this probably is the wrong place.
return new float3(xy, sprite.Offset.Z + wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Graphics
st = ss.SecondaryTop;
sr = ss.SecondaryRight;
sb = ss.SecondaryBottom;
attribC = -(attribC + ChannelSelect[(int)ss.Channel] / 10);
attribC = -(attribC + ChannelSelect[(int)ss.SecondaryChannel] / 10);
}
vertices[nv] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, attribC);

View File

@@ -275,7 +275,9 @@ namespace OpenRA
resolved.Add(kv.Key, new MiniYaml(kv.Value.Value, children));
}
return resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList();
// Resolve any top-level removals (e.g. removing whole actor blocks)
var nodes = new MiniYaml("", resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList());
return ResolveInherits("", nodes, tree, new Dictionary<string, MiniYamlNode.SourceLocation>());
}
static void MergeIntoResolved(MiniYamlNode overrideNode, List<MiniYamlNode> existingNodes,

View File

@@ -34,15 +34,22 @@ namespace OpenRA.Mods.Common.Graphics
var nodes = node.Value.ToDictionary();
MiniYaml defaults;
if (nodes.TryGetValue("Defaults", out defaults))
try
{
nodes.Remove("Defaults");
foreach (var n in nodes)
if (nodes.TryGetValue("Defaults", out defaults))
{
n.Value.Nodes = MiniYaml.Merge(new[] { defaults.Nodes, n.Value.Nodes });
n.Value.Value = n.Value.Value ?? defaults.Value;
nodes.Remove("Defaults");
foreach (var n in nodes)
{
n.Value.Nodes = MiniYaml.Merge(new[] { defaults.Nodes, n.Value.Nodes });
n.Value.Value = n.Value.Value ?? defaults.Value;
}
}
}
catch (Exception e)
{
throw new Exception("Error occurred while parsing {0}".F(node.Key), e);
}
foreach (var kvp in nodes)
{
@@ -154,7 +161,7 @@ namespace OpenRA.Mods.Common.Graphics
// Allow per-sprite offset, flipping, start, and length
var subStart = LoadField(sd, "Start", 0);
var subOffset = LoadField(sd, "Offset", float2.Zero);
var subOffset = LoadField(sd, "Offset", float3.Zero);
var subFlipX = LoadField(sd, "FlipX", false);
var subFlipY = LoadField(sd, "FlipY", false);
@@ -162,7 +169,7 @@ namespace OpenRA.Mods.Common.Graphics
var subSprites = cache[subSrc].Select(
s => new Sprite(s.Sheet,
FlipRectangle(s.Bounds, subFlipX, subFlipY), ZRamp,
new float2(subFlipX ? -s.Offset.X : s.Offset.X, subFlipY ? -s.Offset.Y : s.Offset.Y) + subOffset + offset,
new float3(subFlipX ? -s.Offset.X : s.Offset.X, subFlipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + subOffset + offset,
s.Channel, blendMode));
var subLength = 0;
@@ -185,10 +192,47 @@ namespace OpenRA.Mods.Common.Graphics
sprites = cache[src].Select(
s => new Sprite(s.Sheet,
FlipRectangle(s.Bounds, flipX, flipY), ZRamp,
new float2(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y) + offset,
new float3(flipX ? -s.Offset.X : s.Offset.X, flipY ? -s.Offset.Y : s.Offset.Y, s.Offset.Z) + offset,
s.Channel, blendMode)).ToArray();
}
var depthSprite = LoadField<string>(d, "DepthSprite", null);
if (!string.IsNullOrEmpty(depthSprite))
{
var depthSpriteFrame = LoadField(d, "DepthSpriteFrame", 0);
var depthOffset = LoadField(d, "DepthSpriteOffset", float2.Zero);
var depthSprites = cache.AllCached(depthSprite)
.Select(s => s[depthSpriteFrame]);
sprites = sprites.Select(s =>
{
// The depth sprite must live on the same sheet as the main sprite
var ds = depthSprites.FirstOrDefault(dss => dss.Sheet == s.Sheet);
if (ds == null)
{
// The sequence has probably overflowed onto a new sheet.
// Allocating a new depth sprite on this sheet will almost certainly work
ds = cache.Reload(depthSprite)[depthSpriteFrame];
depthSprites = cache.AllCached(depthSprite)
.Select(ss => ss[depthSpriteFrame]);
// If that doesn't work then we may be referencing a cached sprite from an earlier sheet
// TODO: We could try and reallocate the main sprite, but that requires more complicated code and a perf hit
// We'll only cross that bridge if this becomes a problem in reality
if (ds.Sheet != s.Sheet)
throw new SheetOverflowException("Cross-sheet depth sprite reference: {0}.{1}: {2}");
}
var cw = (ds.Bounds.Left + ds.Bounds.Right) / 2 + (int)(s.Offset.X + depthOffset.X);
var ch = (ds.Bounds.Top + ds.Bounds.Bottom) / 2 + (int)(s.Offset.Y + depthOffset.Y);
var w = s.Bounds.Width / 2;
var h = s.Bounds.Height / 2;
var r = Rectangle.FromLTRB(cw - w, ch - h, cw + w, ch + h);
return new SpriteWithSecondaryData(s, r, ds.Channel);
}).ToArray();
}
MiniYaml length;
if (d.TryGetValue("Length", out length) && length.Value == "*")
Length = sprites.Length - Start;

View File

@@ -26,6 +26,9 @@ namespace OpenRA.Mods.Common.Traits.Render
[Desc("Wall type for connections")]
public readonly string Type = "wall";
[Desc("Override sequence to use when fully open.")]
public readonly string OpenSequence = null;
public override object Create(ActorInitializer init) { return new WithGateSpriteBody(init, this); }
public override IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
@@ -42,10 +45,11 @@ namespace OpenRA.Mods.Common.Traits.Render
}
}
class WithGateSpriteBody : WithSpriteBody, INotifyRemovedFromWorld, INotifyBuildComplete, IWallConnector
class WithGateSpriteBody : WithSpriteBody, INotifyRemovedFromWorld, INotifyBuildComplete, IWallConnector, ITick
{
readonly WithGateSpriteBodyInfo gateInfo;
readonly Gate gate;
bool renderOpen;
public WithGateSpriteBody(ActorInitializer init, WithGateSpriteBodyInfo info)
: base(init, info, () => 0)
@@ -54,6 +58,26 @@ namespace OpenRA.Mods.Common.Traits.Render
gate = init.Self.Trait<Gate>();
}
void UpdateState(Actor self)
{
if (renderOpen)
DefaultAnimation.PlayRepeating(NormalizeSequence(self, gateInfo.OpenSequence));
else
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
}
void ITick.Tick(Actor self)
{
if (gateInfo.OpenSequence == null)
return;
if (gate.Position == gate.OpenPosition ^ renderOpen)
{
renderOpen = gate.Position == gate.OpenPosition;
UpdateState(self);
}
}
int GetGateFrame()
{
return int2.Lerp(0, DefaultAnimation.CurrentSequence.Length - 1, gate.Position, gate.OpenPosition);
@@ -61,12 +85,12 @@ namespace OpenRA.Mods.Common.Traits.Render
public override void DamageStateChanged(Actor self, AttackInfo e)
{
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
UpdateState(self);
}
public override void BuildingComplete(Actor self)
{
DefaultAnimation.PlayFetchIndex(NormalizeSequence(self, Info.Sequence), GetGateFrame);
UpdateState(self);
UpdateNeighbours(self);
}

BIN
mods/ts/bits/isodepth.shp Normal file

Binary file not shown.

View File

@@ -839,6 +839,7 @@
-MustBeDestroyed:
-WithSpriteBody:
WithGateSpriteBody:
OpenSequence: open
Tooltip:
Description: Automated barrier that opens for allied units.
Gate:

File diff suppressed because it is too large Load Diff

View File

@@ -188,6 +188,8 @@ gghunt:
BlendMode: Additive
smech:
Defaults:
Offset: 0,0,8
stand:
Start: 96
Facings: -8