diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 51436e01ff..d92e2f6998 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -204,6 +204,23 @@ namespace OpenRA yield return renderable; } + public IEnumerable ScreenBounds(WorldRenderer wr) + { + var bounds = Bounds(wr); + foreach (var modifier in renderModifiers) + bounds = modifier.ModifyScreenBounds(this, wr, bounds); + return bounds; + } + + IEnumerable Bounds(WorldRenderer wr) + { + // PERF: Avoid LINQ. See comments for Renderables + foreach (var render in renders) + foreach (var r in render.ScreenBounds(this, wr)) + if (!r.IsEmpty) + yield return r; + } + public void QueueActivity(bool queued, Activity nextActivity) { if (!queued) diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index e9daf54e2a..14da553a33 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -31,7 +31,6 @@ namespace OpenRA.Traits { public readonly PPos[] Footprint; public readonly WPos CenterPosition; - public readonly Rectangle RenderBounds; public readonly Rectangle SelectableBounds; public readonly HashSet TargetTypes; readonly Actor actor; @@ -51,7 +50,10 @@ namespace OpenRA.Traits public bool Shrouded { get; private set; } public bool NeedRenderables { get; set; } public IRenderable[] Renderables = NoRenderables; + public Rectangle[] ScreenBounds = NoBounds; + static readonly IRenderable[] NoRenderables = new IRenderable[0]; + static readonly Rectangle[] NoBounds = new Rectangle[0]; int flashTicks; @@ -78,7 +80,6 @@ namespace OpenRA.Traits footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|"))); CenterPosition = self.CenterPosition; - RenderBounds = self.RenderBounds; SelectableBounds = self.SelectableBounds; TargetTypes = self.GetEnabledTargetTypes().ToHashSet(); diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index 49994627db..dc439fe7dd 100644 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -61,22 +61,6 @@ namespace OpenRA.Traits public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; } - Rectangle FrozenActorBounds(FrozenActor fa) - { - var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition); - var bounds = fa.RenderBounds; - bounds.Offset(pos.X, pos.Y); - return bounds; - } - - Rectangle ActorBounds(Actor a) - { - var pos = worldRenderer.ScreenPxPosition(a.CenterPosition); - var bounds = a.RenderBounds; - bounds.Offset(pos.X, pos.Y); - return bounds; - } - public void AddOrUpdate(Player viewer, FrozenActor fa) { if (removeFrozenActors[viewer].Contains(fa)) @@ -200,11 +184,23 @@ namespace OpenRA.Traits return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid); } + Rectangle AggregateBounds(IEnumerable screenBounds) + { + if (!screenBounds.Any()) + return Rectangle.Empty; + + var bounds = screenBounds.First(); + foreach (var b in screenBounds.Skip(1)) + bounds = Rectangle.Union(bounds, b); + + return bounds; + } + public void Tick() { foreach (var a in addOrUpdateActors) { - var bounds = ActorBounds(a); + var bounds = AggregateBounds(a.ScreenBounds(worldRenderer)); if (!bounds.Size.IsEmpty) { if (partitionedActors.Contains(a)) @@ -226,7 +222,7 @@ namespace OpenRA.Traits { foreach (var fa in kv.Value) { - var bounds = FrozenActorBounds(fa); + var bounds = AggregateBounds(fa.ScreenBounds); if (!bounds.Size.IsEmpty) { if (partitionedFrozenActors[kv.Key].Contains(fa)) diff --git a/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs b/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs index 14e3bdbf4d..5e8732c1db 100644 --- a/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs +++ b/OpenRA.Mods.Cnc/Traits/Render/WithCargo.cs @@ -40,6 +40,7 @@ namespace OpenRA.Mods.Cnc.Traits.Render readonly Cargo cargo; readonly BodyOrientation body; readonly IFacing facing; + int cachedFacing; Dictionary previews = new Dictionary(); @@ -58,6 +59,15 @@ namespace OpenRA.Mods.Cnc.Traits.Render if (actorPreviews != null) foreach (var preview in actorPreviews) preview.Tick(); + + // HACK: We don't have an efficient way to know when the preview + // bounds change, so assume that we need to update the screen map + // (only) when the facing changes + if (facing.Facing != cachedFacing && previews.Any()) + { + self.World.ScreenMap.AddOrUpdate(self); + cachedFacing = facing.Facing; + } } IEnumerable IRender.Render(Actor self, WorldRenderer wr) @@ -110,12 +120,16 @@ namespace OpenRA.Mods.Cnc.Traits.Render void INotifyPassengerEntered.OnPassengerEntered(Actor self, Actor passenger) { if (info.DisplayTypes.Contains(passenger.Trait().Info.CargoType)) + { previews.Add(passenger, null); + self.World.ScreenMap.AddOrUpdate(self); + } } void INotifyPassengerExited.OnPassengerExited(Actor self, Actor passenger) { previews.Remove(passenger); + self.World.ScreenMap.AddOrUpdate(self); } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Traits/Carryall.cs b/OpenRA.Mods.Common/Traits/Carryall.cs index 0a2af06724..4fa6da2e0b 100644 --- a/OpenRA.Mods.Common/Traits/Carryall.cs +++ b/OpenRA.Mods.Common/Traits/Carryall.cs @@ -61,6 +61,7 @@ namespace OpenRA.Mods.Common.Traits [Sync] public Actor Carryable { get; private set; } public CarryallState State { get; private set; } + int cachedFacing; IActorPreview[] carryablePreview = null; /// Offset between the carryall's and the carried actor's CenterPositions @@ -84,6 +85,15 @@ namespace OpenRA.Mods.Common.Traits // Cargo may be killed in the same tick as, but after they are attached if (Carryable != null && Carryable.IsDead) DetachCarryable(self); + + // HACK: We don't have an efficient way to know when the preview + // bounds change, so assume that we need to update the screen map + // (only) when the facing changes + if (facing.Facing != cachedFacing && carryablePreview != null) + { + self.World.ScreenMap.AddOrUpdate(self); + cachedFacing = facing.Facing; + } } void INotifyActorDisposing.Disposing(Actor self) @@ -126,6 +136,7 @@ namespace OpenRA.Mods.Common.Traits Carryable = carryable; State = CarryallState.Carrying; + self.World.ScreenMap.AddOrUpdate(self); CarryableOffset = OffsetForCarryable(self, carryable); return true; @@ -134,6 +145,7 @@ namespace OpenRA.Mods.Common.Traits public virtual void DetachCarryable(Actor self) { UnreserveCarryable(self); + self.World.ScreenMap.AddOrUpdate(self); carryablePreview = null; CarryableOffset = WVec.Zero; diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index 8343bebac6..4c85716329 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -127,6 +127,7 @@ namespace OpenRA.Mods.Common.Traits void ITickRender.TickRender(WorldRenderer wr, Actor self) { IRenderable[] renderables = null; + Rectangle[] bounds = null; for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++) { var frozen = frozenStates[playerIndex].FrozenActor; @@ -137,11 +138,15 @@ namespace OpenRA.Mods.Common.Traits { isRendering = true; renderables = self.Render(wr).ToArray(); + bounds = self.ScreenBounds(wr).ToArray(); + isRendering = false; } frozen.NeedRenderables = false; frozen.Renderables = renderables; + frozen.ScreenBounds = bounds; + self.World.ScreenMap.AddOrUpdate(self.World.Players[playerIndex], frozen); } } diff --git a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs index 0153ae5128..4327ebbbd0 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs @@ -101,6 +101,10 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly bool IsPlayerPalette; public PaletteReference PaletteReference { get; private set; } + bool cachedVisible; + WVec cachedOffset; + ISpriteSequence cachedSequence; + public AnimationWrapper(AnimationWithOffset animation, string palette, bool isPlayerPalette) { Animation = animation; @@ -127,6 +131,24 @@ namespace OpenRA.Mods.Common.Traits.Render return Animation.DisableFunc == null || !Animation.DisableFunc(); } } + + public bool Tick() + { + // Tick the animation + Animation.Animation.Tick(); + + // Return to the caller whether the renderable position or size has changed + var visible = IsVisible; + var offset = Animation.OffsetFunc != null ? Animation.OffsetFunc() : WVec.Zero; + var sequence = Animation.Animation.CurrentSequence; + + var updated = visible != cachedVisible || offset != cachedOffset || sequence != cachedSequence; + cachedVisible = visible; + cachedOffset = offset; + cachedSequence = sequence; + + return updated; + } } readonly string faction; @@ -196,8 +218,12 @@ namespace OpenRA.Mods.Common.Traits.Render protected virtual void Tick(Actor self) { + var updated = false; foreach (var a in anims) - a.Animation.Animation.Tick(); + updated |= a.Tick(); + + if (updated) + self.World.ScreenMap.AddOrUpdate(self); } public void Add(AnimationWithOffset anim, string palette = null, bool isPlayerPalette = false) diff --git a/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs b/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs index 56afd11ec4..b4916945d6 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderVoxels.cs @@ -70,9 +70,36 @@ namespace OpenRA.Mods.Common.Traits.Render } } - public class RenderVoxels : IRender, INotifyOwnerChanged + public class RenderVoxels : IRender, ITick, INotifyOwnerChanged { + class AnimationWrapper + { + readonly ModelAnimation model; + bool cachedVisible; + WVec cachedOffset; + + public AnimationWrapper(ModelAnimation model) + { + this.model = model; + } + + public bool Tick() + { + // Return to the caller whether the renderable position or size has changed + var visible = model.IsVisible; + var offset = model.OffsetFunc != null ? model.OffsetFunc() : WVec.Zero; + + var updated = visible != cachedVisible || offset != cachedOffset; + cachedVisible = visible; + cachedOffset = offset; + + return updated; + } + } + readonly List components = new List(); + readonly Dictionary wrappers = new Dictionary(); + readonly Actor self; readonly RenderVoxelsInfo info; readonly BodyOrientation body; @@ -91,6 +118,16 @@ namespace OpenRA.Mods.Common.Traits.Render bool initializePalettes = true; public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { initializePalettes = true; } + void ITick.Tick(Actor self) + { + var updated = false; + foreach (var w in wrappers.Values) + updated |= w.Tick(); + + if (updated) + self.World.ScreenMap.AddOrUpdate(self); + } + protected PaletteReference colorPalette, normalsPalette, shadowPalette; IEnumerable IRender.Render(Actor self, WorldRenderer wr) { @@ -118,7 +155,16 @@ namespace OpenRA.Mods.Common.Traits.Render } public string Image { get { return info.Image ?? self.Info.Name; } } - public void Add(ModelAnimation v) { components.Add(v); } - public void Remove(ModelAnimation v) { components.Remove(v); } + public void Add(ModelAnimation m) + { + components.Add(m); + wrappers.Add(m, new AnimationWrapper(m)); + } + + public void Remove(ModelAnimation m) + { + components.Remove(m); + wrappers.Remove(m); + } } }