Client LuaCsForBarotrauma
BarotraumaClient/ClientSource/Map/Levels/LevelObjects/LevelObjectManager.cs
3 using Microsoft.Xna.Framework;
4 using Microsoft.Xna.Framework.Graphics;
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 
9 namespace Barotrauma
10 {
11  partial class LevelObjectManager
12  {
13  private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
14  private readonly List<LevelObject> visibleObjectsMid = new List<LevelObject>();
15  private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
16 
17  private double NextRefreshTime;
18 
19  //Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay,
20  //but small enough to prevent wrecking performance when zooming out very far
21  const int MaxVisibleObjects = 600;
22 
23  private Rectangle currentGridIndices;
24 
26 
27  partial void RemoveProjSpecific()
28  {
29  visibleObjectsBack.Clear();
30  visibleObjectsMid.Clear();
31  visibleObjectsFront.Clear();
32  }
33 
34  partial void UpdateProjSpecific(float deltaTime)
35  {
36  foreach (LevelObject obj in visibleObjectsBack)
37  {
38  obj.Update(deltaTime);
39  }
40  foreach (LevelObject obj in visibleObjectsMid)
41  {
42  obj.Update(deltaTime);
43  }
44  foreach (LevelObject obj in visibleObjectsFront)
45  {
46  obj.Update(deltaTime);
47  }
48  }
49 
50  public IEnumerable<LevelObject> GetVisibleObjects()
51  {
52  return visibleObjectsBack.Union(visibleObjectsMid).Union(visibleObjectsFront);
53  }
54 
58  private void RefreshVisibleObjects(Rectangle currentIndices, float zoom)
59  {
60  visibleObjectsBack.Clear();
61  visibleObjectsMid.Clear();
62  visibleObjectsFront.Clear();
63 
64  float minSizeToDraw = MathHelper.Lerp(10.0f, 5.0f, Math.Min(zoom * 20.0f, 1.0f));
65 
66  //start from the grid cell at the center of the view
67  //(if objects needs to be culled, better to cull at the edges of the view)
68  int midIndexX = (currentIndices.X + currentIndices.Width) / 2;
69  int midIndexY = (currentIndices.Y + currentIndices.Height) / 2;
70  CheckIndex(midIndexX, midIndexY);
71 
72  for (int x = currentIndices.X; x <= currentIndices.Width; x++)
73  {
74  for (int y = currentIndices.Y; y <= currentIndices.Height; y++)
75  {
76  if (x != midIndexX || y != midIndexY) { CheckIndex(x, y); }
77  }
78  }
79 
80  void CheckIndex(int x, int y)
81  {
82  if (objectGrid[x, y] == null) { return; }
83  foreach (LevelObject obj in objectGrid[x, y])
84  {
85  if (!obj.CanBeVisible) { continue; }
86  if (obj.Prefab.HideWhenBroken && obj.Health <= 0.0f) { continue; }
87 
88  if (zoom < 0.05f)
89  {
90  //hide if the sprite is very small when zoomed this far out
91  if ((obj.Sprite != null && Math.Min(obj.Sprite.size.X * zoom, obj.Sprite.size.Y * zoom) < 5.0f) ||
92  (obj.ActivePrefab?.DeformableSprite != null && Math.Min(obj.ActivePrefab.DeformableSprite.Sprite.size.X * zoom, obj.ActivePrefab.DeformableSprite.Sprite.size.Y * zoom) < minSizeToDraw))
93  {
94  continue;
95  }
96 
97  float zCutoff = MathHelper.Lerp(5000.0f, 500.0f, (0.05f - zoom) * 20.0f);
98  if (obj.Position.Z > zCutoff)
99  {
100  continue;
101  }
102  }
103 
104  var objectList =
105  obj.Position.Z >= 0 ?
106  visibleObjectsBack :
107  (obj.Position.Z < -1 ? visibleObjectsFront : visibleObjectsMid);
108  if (objectList.Count >= MaxVisibleObjects) { continue; }
109 
110  int drawOrderIndex = 0;
111  for (int i = 0; i < objectList.Count; i++)
112  {
113  if (objectList[i] == obj)
114  {
115  drawOrderIndex = -1;
116  break;
117  }
118 
119  if (objectList[i].Position.Z > obj.Position.Z)
120  {
121  break;
122  }
123  else
124  {
125  drawOrderIndex = i + 1;
126  if (drawOrderIndex >= MaxVisibleObjects) { break; }
127  }
128  }
129 
130  if (drawOrderIndex >= 0 && drawOrderIndex < MaxVisibleObjects)
131  {
132  objectList.Insert(drawOrderIndex, obj);
133  }
134  }
135  }
136 
137  //object grid is sorted in an ascending order
138  //(so we prefer the objects in the foreground instead of ones in the background if some need to be culled)
139  //rendering needs to be done in a descending order though to get the background objects to be drawn first -> reverse the lists
140  visibleObjectsBack.Reverse();
141  visibleObjectsMid.Reverse();
142  visibleObjectsFront.Reverse();
143 
144  currentGridIndices = currentIndices;
145  }
146 
150  public void DrawObjectsBack(SpriteBatch spriteBatch, Camera cam)
151  {
152  DrawObjects(spriteBatch, cam, visibleObjectsBack);
153  }
154 
158  public void DrawObjectsMid(SpriteBatch spriteBatch, Camera cam)
159  {
160  DrawObjects(spriteBatch, cam, visibleObjectsMid);
161  }
162 
166  public void DrawObjectsFront(SpriteBatch spriteBatch, Camera cam)
167  {
168  DrawObjects(spriteBatch, cam, visibleObjectsFront);
169  }
170 
171  private void DrawObjects(SpriteBatch spriteBatch, Camera cam, List<LevelObject> objectList)
172  {
173  Rectangle indices = Rectangle.Empty;
174  indices.X = (int)Math.Floor(cam.WorldView.X / (float)GridSize);
175  if (indices.X >= objectGrid.GetLength(0)) { return; }
176  indices.Y = (int)Math.Floor((cam.WorldView.Y - cam.WorldView.Height - Level.Loaded.BottomPos) / (float)GridSize);
177  if (indices.Y >= objectGrid.GetLength(1)) { return; }
178 
179  indices.Width = (int)Math.Floor(cam.WorldView.Right / (float)GridSize) + 1;
180  if (indices.Width < 0) { return; }
181  indices.Height = (int)Math.Floor((cam.WorldView.Y - Level.Loaded.BottomPos) / (float)GridSize) + 1;
182  if (indices.Height < 0) { return; }
183 
184  indices.X = Math.Max(indices.X, 0);
185  indices.Y = Math.Max(indices.Y, 0);
186  indices.Width = Math.Min(indices.Width, objectGrid.GetLength(0) - 1);
187  indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1);
188 
189  float z = 0.0f;
190  if (ForceRefreshVisibleObjects || (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime))
191  {
192  RefreshVisibleObjects(indices, cam.Zoom);
194  if (cam.Zoom < 0.1f)
195  {
196  //when zoomed very far out, refresh a little less often
197  NextRefreshTime = Timing.TotalTime + MathHelper.Lerp(1.0f, 0.0f, cam.Zoom * 10.0f);
198  }
199  }
200 
201  foreach (LevelObject obj in objectList)
202  {
203  Vector2 camDiff = new Vector2(obj.Position.X, obj.Position.Y) - cam.WorldViewCenter;
204  camDiff.Y = -camDiff.Y;
205 
206  Sprite activeSprite = obj.Sprite;
207  activeSprite?.Draw(
208  spriteBatch,
209  new Vector2(obj.Position.X, -obj.Position.Y) - camDiff * obj.Position.Z * ParallaxStrength,
210  Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / 3000.0f),
211  activeSprite.Origin,
212  obj.CurrentRotation,
213  obj.CurrentScale,
214  SpriteEffects.None,
215  z);
216 
217  if (obj.ActivePrefab.DeformableSprite != null)
218  {
219  if (obj.CurrentSpriteDeformation != null)
220  {
221  obj.ActivePrefab.DeformableSprite.Deform(obj.CurrentSpriteDeformation);
222  }
223  else
224  {
225  obj.ActivePrefab.DeformableSprite.Reset();
226  }
227  obj.ActivePrefab.DeformableSprite?.Draw(cam,
228  new Vector3(new Vector2(obj.Position.X, obj.Position.Y) - camDiff * obj.Position.Z * ParallaxStrength, z * 10.0f),
229  obj.ActivePrefab.DeformableSprite.Origin,
230  obj.CurrentRotation,
231  obj.CurrentScale,
232  Color.Lerp(obj.Prefab.SpriteColor, obj.Prefab.SpriteColor.Multiply(Level.Loaded.BackgroundTextureColor), obj.Position.Z / 5000.0f));
233  }
234 
235 
236  if (GameMain.DebugDraw)
237  {
238  GUI.DrawRectangle(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(10.0f, 10.0f), GUIStyle.Red, true);
239 
240  if (obj.Triggers == null) { continue; }
241  foreach (LevelTrigger trigger in obj.Triggers)
242  {
243  if (trigger.PhysicsBody == null) continue;
244  GUI.DrawLine(spriteBatch, new Vector2(obj.Position.X, -obj.Position.Y), new Vector2(trigger.WorldPosition.X, -trigger.WorldPosition.Y), Color.Cyan, 0, 3);
245 
246  Vector2 flowForce = trigger.GetWaterFlowVelocity();
247  if (flowForce.LengthSquared() > 1)
248  {
249  flowForce.Y = -flowForce.Y;
250  GUI.DrawLine(spriteBatch, new Vector2(trigger.WorldPosition.X, -trigger.WorldPosition.Y), new Vector2(trigger.WorldPosition.X, -trigger.WorldPosition.Y) + flowForce * 10, GUIStyle.Orange, 0, 5);
251  }
252  trigger.PhysicsBody.UpdateDrawPosition();
253  trigger.PhysicsBody.DebugDraw(spriteBatch, trigger.IsTriggered ? Color.Cyan : Color.DarkCyan);
254  }
255  }
256 
257  z += 0.0001f;
258  }
259  }
260 
261  public void ClientEventRead(IReadMessage msg, float sendingTime)
262  {
263  int objIndex = msg.ReadRangedInteger(0, objects.Count);
264  objects[objIndex].ClientRead(msg);
265  }
266  }
267 }
float? Zoom
Definition: Camera.cs:78
Rectangle WorldView
Definition: Camera.cs:123
Vector2 WorldViewCenter
Definition: Camera.cs:126
virtual Vector2 Position
Definition: Entity.cs:47
void DrawObjectsBack(SpriteBatch spriteBatch, Camera cam)
Draw the objects behind the level walls
void DrawObjectsFront(SpriteBatch spriteBatch, Camera cam)
Draw the objects in front of the level walls and characters
void DrawObjectsMid(SpriteBatch spriteBatch, Camera cam)
Draw the objects in front of the level walls, but behind characters
int ReadRangedInteger(int min, int max)