Skip to content

Commit 2c03b4f

Browse files
authored
Merge pull request CommunalHelper#255 from JaThePlayer/perf/ChainCulling
Chain camera culling
2 parents b78dc90 + d4939bf commit 2c03b4f

3 files changed

Lines changed: 39 additions & 8 deletions

File tree

Loenn/entities/chains/chain.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ chain.placements = {
1919
extraJoints = 0,
2020
outline = true,
2121
texture = "objects/CommunalHelper/chains/chain",
22+
updateOffscreen = true,
2223
}
2324
}
2425

Loenn/lang/en_gb.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ entities.CommunalHelper/Chain.placements.name.chain=Chain
548548
entities.CommunalHelper/Chain.attributes.description.outline=Whether this chain has an outline (black).
549549
entities.CommunalHelper/Chain.attributes.description.extraJoints=The additional 'chain nodes' this chain has. Used to make the chain longer without distancing the endpoints of the chain any further away from eachother.
550550
entities.CommunalHelper/Chain.attributes.description.texture=This chain's texture.
551+
entities.CommunalHelper/Chain.attributes.description.updateOffscreen=Whether the chain physics should be calculated even if the chain is currently off-screen.\nIf you don't expect the chain to get broken off-screen, this is likely safe to disable, reducing lag in large rooms.
551552

552553
# Core Mode Music Controller
553554
entities.CommunalHelper/CoreModeMusicController.placements.name.enable=Core Mode Music Controller

src/Entities/Chains/Chain.cs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using FMOD.Studio;
2-
using System.Linq;
1+
using Celeste.Mod.Helpers;
2+
using FMOD.Studio;
33

44
namespace Celeste.Mod.CommunalHelper.Entities;
55

@@ -55,18 +55,22 @@ public void ConstraintTo(Vector2 to, float distance, bool cancelAcceleration)
5555
private readonly MTexture texture;
5656
private readonly MTexture segment;
5757

58+
private readonly bool updateOffscreen;
59+
private readonly Vector2[] oldNodePositions;
60+
5861
public Chain(EntityData data, Vector2 offset)
5962
: this(
6063
data.Bool("outline", true),
6164
(int) ((Vector2.Distance(data.Position + offset, data.NodesOffset(offset)[0]) / 8) + 1 + data.Int("extraJoints")),
6265
8,
6366
() => data.Position + offset,
6467
() => data.Nodes[0] + offset,
65-
GFX.Game.GetOrDefault(data.Attr("texture", DEFAULT_CHAIN_PATH), DefaultChain)
68+
GFX.Game.GetOrDefault(data.Attr("texture", DEFAULT_CHAIN_PATH), DefaultChain),
69+
data.Bool("updateOffscreen", true)
6670
)
6771
{ }
6872

69-
public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2> attachedStartGetter, Func<Vector2> attachedEndGetter, MTexture texture)
73+
public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2> attachedStartGetter, Func<Vector2> attachedEndGetter, MTexture texture, bool updateOffscreen)
7074
: base(attachedStartGetter())
7175
{
7276
this.texture = texture;
@@ -76,6 +80,7 @@ public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2
7680
this.attachedStartGetter = attachedStartGetter;
7781
this.attachedEndGetter = attachedEndGetter;
7882
this.distanceConstraint = distanceConstraint;
83+
this.updateOffscreen = updateOffscreen;
7984

8085
this.outline = outline;
8186

@@ -89,6 +94,7 @@ public Chain(bool outline, int nodeCount, float distanceConstraint, Func<Vector2
8994

9095
UpdateChain();
9196
sfx = Audio.Play(CustomSFX.game_chain_move);
97+
oldNodePositions = new Vector2[nodes.Length];
9298
}
9399

94100
private void AttachedEndsToSolids(Scene scene)
@@ -181,9 +187,13 @@ public override void Update()
181187
{
182188
base.Update();
183189

184-
Vector2[] oldPositions = nodes.Select(node => node.Position).ToArray();
190+
if (!updateOffscreen && !IsVisible())
191+
return;
192+
193+
for (int i = 0; i < nodes.Length; i++)
194+
oldNodePositions[i] = nodes[i].Position;
185195
UpdateChain();
186-
UpdateSfx(oldPositions);
196+
UpdateSfx(oldNodePositions);
187197

188198
if (Vector2.Distance(nodes[0].Position, nodes[^1].Position) > (nodes.Length + 1) * distanceConstraint)
189199
BreakInHalf();
@@ -195,11 +205,11 @@ private void BreakInHalf()
195205
Vector2 middleNode = nodes[nodes.Length / 2].Position;
196206

197207
Chain a, b;
198-
Scene.Add(a = new Chain(outline, nodes.Length / 2, 8, () => middleNode, attachedStartGetter, texture));
208+
Scene.Add(a = new Chain(outline, nodes.Length / 2, 8, () => middleNode, attachedStartGetter, texture, updateOffscreen));
199209
a.AttachedEndsToSolids(Scene);
200210
a.ShakeImpulse();
201211

202-
Scene.Add(b = new Chain(outline, nodes.Length / 2, 8, () => middleNode, attachedEndGetter, texture));
212+
Scene.Add(b = new Chain(outline, nodes.Length / 2, 8, () => middleNode, attachedEndGetter, texture, updateOffscreen));
203213
b.AttachedEndsToSolids(Scene);
204214
b.ShakeImpulse();
205215

@@ -262,9 +272,28 @@ private void ShakeImpulse()
262272
}
263273
}
264274

275+
private bool IsVisible()
276+
{
277+
// Construct a rectangle containing the first, middle and last node, which acts as the bounding box of this chain.
278+
// Code adapted from CullHelper.IsCurveVisible
279+
Vector2 begin = nodes[0].Position;
280+
Vector2 control = nodes[nodes.Length / 2].Position;
281+
Vector2 end = nodes[^1].Position;
282+
283+
float x = Math.Min(begin.X, Math.Min(control.X, end.X));
284+
float right = Math.Max(begin.X, Math.Max(control.X, end.X));
285+
float y = Math.Min(begin.Y, Math.Min(control.Y, end.Y));
286+
float bottom = Math.Max(begin.Y, Math.Max(control.Y, end.Y));
287+
288+
return CullHelper.IsRectangleVisible(x, y, right - x, bottom - y, lenience: segment.Width + 2);
289+
}
290+
265291
public override void Render()
266292
{
267293
base.Render();
294+
295+
if (!IsVisible())
296+
return;
268297

269298
if (outline)
270299
{

0 commit comments

Comments
 (0)