Client LuaCsForBarotrauma
ConvexHull.cs
1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
3 using System;
4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using System.Linq;
7 
9 {
11  {
12 
13  public readonly Submarine Submarine;
14  public HashSet<ConvexHull> IsHidden = new HashSet<ConvexHull>();
15  public HashSet<ConvexHull> HasBeenVisible = new HashSet<ConvexHull>();
16  public readonly List<ConvexHull> List = new List<ConvexHull>();
17 
18  public ConvexHullList(Submarine submarine)
19  {
20  Submarine = submarine;
21  }
22  }
23 
24  class Segment
25  {
27  public SegmentPoint End;
28 
30 
31  public bool IsHorizontal;
32  public bool IsAxisAligned;
33 
34  public Vector2 SubmarineDrawPos;
35 
36  public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull)
37  {
38  if (start.Pos.Y > end.Pos.Y)
39  {
40  (end, start) = (start, end);
41  }
42 
43  Start = start;
44  End = end;
45  ConvexHull = convexHull;
46 
47  start.ConvexHull = convexHull;
48  end.ConvexHull = convexHull;
49 
50  IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y);
51  IsAxisAligned = Math.Abs(start.Pos.X - end.Pos.X) < 0.1f || Math.Abs(start.Pos.Y - end.Pos.Y) < 0.001f;
52  }
53  }
54 
55  struct SegmentPoint
56  {
57  public Vector2 Pos;
58  public Vector2 WorldPos;
59 
61 
62  public SegmentPoint(Vector2 pos, ConvexHull convexHull)
63  {
64  Pos = pos;
65  WorldPos = pos;
66  ConvexHull = convexHull;
67  }
68 
69  public override string ToString()
70  {
71  return Pos.ToString();
72  }
73  }
74 
75  class VectorPair
76  {
77  public Vector2? A = null;
78  public Vector2? B = null;
79  }
80 
81  class ConvexHull
82  {
83  public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
84  public static BasicEffect shadowEffect;
85  public static BasicEffect penumbraEffect;
86 
87  private readonly Segment[] segments = new Segment[4];
88  private readonly SegmentPoint[] vertices = new SegmentPoint[4];
89  private readonly SegmentPoint[] losVertices = new SegmentPoint[2];
90  private readonly Vector2[] losOffsets = new Vector2[2];
91 
92  private readonly bool isHorizontal;
93 
94  private readonly int thickness;
95 
96  public VertexPositionColor[] ShadowVertices { get; private set; }
97  public VertexPositionTexture[] PenumbraVertices { get; private set; }
98  public int ShadowVertexCount { get; private set; }
99  public int PenumbraVertexCount { get; private set; }
100 
104  public float? MaxMergeLosVerticesDist;
105 
106  private readonly HashSet<ConvexHull> overlappingHulls = new HashSet<ConvexHull>();
107 
108  public MapEntity ParentEntity { get; private set; }
109 
110  private bool enabled;
111  public bool Enabled
112  {
113  get
114  {
115  return enabled;
116  }
117  set
118  {
119  if (enabled == value) return;
120  enabled = value;
121  LastVertexChangeTime = (float)Timing.TotalTime;
122  }
123  }
124 
128  public float LastVertexChangeTime
129  {
130  get;
131  private set;
132  }
133 
134  public Rectangle BoundingBox { get; private set; }
135 
136  public bool IsInvalid { get; private set; }
137 
138  public ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
139  {
140  shadowEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
141  {
142  VertexColorEnabled = true
143  };
144  penumbraEffect ??= new BasicEffect(GameMain.Instance.GraphicsDevice)
145  {
146  TextureEnabled = true,
147  LightingEnabled = false,
148  Texture = TextureLoader.FromFile("Content/Lights/penumbra.png")
149  };
150 
151  ParentEntity = parent;
152 
153  ShadowVertices = new VertexPositionColor[6 * 4];
154  PenumbraVertices = new VertexPositionTexture[6 * 4];
155 
156  BoundingBox = rect;
157 
158  this.isHorizontal = isHorizontal;
159  Debug.Assert(!ParentEntity.Removed);
160 
161  Vector2[] verts = new Vector2[]
162  {
163  new Vector2(rect.X, rect.Bottom),
164  new Vector2(rect.Right, rect.Bottom),
165  new Vector2(rect.Right, rect.Y),
166  new Vector2(rect.X, rect.Y),
167  };
168 
169  Vector2[] losVerts;
170  if (this.isHorizontal)
171  {
172  thickness = rect.Height;
173  losVerts = new Vector2[] { new Vector2(rect.X, rect.Center.Y), new Vector2(rect.Right, rect.Center.Y) };
174  }
175  else
176  {
177  thickness = rect.Width;
178  losVerts = new Vector2[] { new Vector2(rect.Center.X, rect.Y), new Vector2(rect.Center.X, rect.Bottom) };
179  }
180  SetVertices(verts, losVerts);
181  Enabled = true;
182 
183  var chList = HullLists.Find(h => h.Submarine == parent.Submarine);
184  if (chList == null)
185  {
186  chList = new ConvexHullList(parent.Submarine);
187  HullLists.Add(chList);
188  }
189 
190  foreach (ConvexHull ch in chList.List)
191  {
192  MergeLosVertices(ch);
193  ch.MergeLosVertices(this);
194  }
195 
196  chList.List.Add(this);
197  }
198 
199  private void MergeLosVertices(ConvexHull ch, bool refreshOtherOverlappingHulls = true)
200  {
201  if (ch == this) { return; }
202 
203  //merge dist in the direction parallel to the segment
204  //(e.g. how far up/down we can stretch a vertical segment)
205  float mergeDistParallel = MathHelper.Clamp(ch.thickness * 0.65f, 16, 512);
206  if (MaxMergeLosVerticesDist.HasValue)
207  {
208  mergeDistParallel = Math.Max(mergeDistParallel, MaxMergeLosVerticesDist.Value);
209  }
210  else
211  {
212  Rectangle inflatedAABB = ch.BoundingBox;
213  inflatedAABB.Inflate(2, 2);
214  //if this los segment isn't touching the other's bounding box,
215  //don't extend the segment by more than 50% of it's length
216  if (!inflatedAABB.Contains(losVertices[0].Pos) &&
217  !inflatedAABB.Contains(losVertices[1].Pos))
218  {
219  mergeDistParallel = Math.Min(mergeDistParallel, Vector2.Distance(losVertices[0].Pos, losVertices[1].Pos) * 0.5f);
220  }
221  }
222  //merge dist in the direction perpendicular to the segment
223  //(e.g. how far right/left we can stretch a vertical segment)
224  //do not allow more than ~half of the thickness, because that'd make the segment go outside the convex hull
225  float mergeDistPerpendicular = Math.Min(mergeDistParallel, thickness * 0.35f);
226 
227  Vector2 center = (losVertices[0].Pos + losVertices[1].Pos) / 2;
228 
229  bool changed = false;
230  for (int i = 0; i < losVertices.Length; i++)
231  {
232  Vector2 segmentDir = Vector2.Normalize(losVertices[i].Pos - center);
233  //check if the closest point on the other convex hull segment is close enough, disregarding any offsets
234  //otherwise we might end up moving the vertex too much if we stretch it to an already-offset segment
235  if (!isCloseEnough(
236  MathUtils.GetClosestPointOnLineSegment(ch.losVertices[0].Pos, ch.losVertices[1].Pos, losVertices[i].Pos),
237  losVertices[i].Pos))
238  {
239  continue;
240  }
241 
242  //check the offset position of the segment next
243  Vector2 closest = MathUtils.GetClosestPointOnLineSegment(
244  ch.losVertices[0].Pos + ch.losOffsets[0],
245  ch.losVertices[1].Pos + ch.losOffsets[1],
246  losVertices[i].Pos);
247  if (!isCloseEnough(closest, losVertices[i].Pos)) { continue; }
248 
249  //find where the segments would intersect if they had infinite length
250  // if it's close to the closest point, let's use that instead to keep
251  // the direction of the segment unchanged (i.e. vertical segment stays vertical)
252  if (MathUtils.GetLineIntersection(
253  ch.losVertices[0].Pos + ch.losOffsets[0], ch.losVertices[1].Pos + ch.losOffsets[1],
254  losVertices[0].Pos, losVertices[1].Pos,
255  areLinesInfinite: true, out Vector2 intersection) &&
256  //the intersection needs to be outwards from the vertex we're checking
257  Vector2.Dot(segmentDir, intersection - losVertices[i].Pos) > 0 &&
258  //the intersection needs to be close enough to the default position of the vertex and the closest point
259  //(we don't want to merge the segments somewhere close to infinity!)
260  (Vector2.DistanceSquared(intersection, losVertices[i].Pos) < mergeDistParallel * mergeDistParallel ||
261  Vector2.DistanceSquared(intersection, closest) < 16.0f * 16.0f))
262  {
263  closest = intersection;
264  }
265 
266  //don't move the vertices of the segment too close to each other
267  if (Vector2.DistanceSquared(losVertices[1 - i].Pos + losOffsets[1 - i], closest) < mergeDistPerpendicular * mergeDistPerpendicular)
268  {
269  continue;
270  }
271 
272  losOffsets[i] = closest - losVertices[i].Pos;
273  overlappingHulls.Add(ch);
274  ch.overlappingHulls.Add(this);
275  changed = true;
276 
277  bool isCloseEnough(Vector2 closest, Vector2 vertex)
278  {
279  float dist = Vector2.Distance(closest, vertex);
280  if (dist < 0.001f) { return true; }
281  if (dist > mergeDistParallel) { return false; }
282 
283  Vector2 closestDir = (closest - vertex) / dist;
284 
285  float dot = Math.Abs(Vector2.Dot(segmentDir, closestDir));
286  float distAlongAxis = dist * dot;
287  if (distAlongAxis > mergeDistParallel) { return false; }
288 
289  float distPerpendicular = dist * (1.0f - dot);
290  if (distPerpendicular > mergeDistPerpendicular) { return false; }
291 
292  return true;
293  }
294  }
295  if (changed && refreshOtherOverlappingHulls)
296  {
297  foreach (var overlapping in overlappingHulls)
298  {
299  overlapping.MergeLosVertices(this, refreshOtherOverlappingHulls: false);
300  }
301  }
302  }
303 
304  public bool LosIntersects(Vector2 pos1, Vector2 pos2)
305  {
306  return MathUtils.LineSegmentsIntersect(
307  losVertices[0].Pos + losOffsets[0], losVertices[1].Pos + losOffsets[1],
308  pos1, pos2);
309  }
310 
311  public void Rotate(Vector2 origin, float amount)
312  {
313  Matrix rotationMatrix =
314  Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
315  Matrix.CreateRotationZ(amount) *
316  Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
317  SetVertices(vertices.Select(v => v.Pos).ToArray(), losVertices.Select(v => v.Pos).ToArray(), rotationMatrix: rotationMatrix);
318  }
319 
320  private void CalculateDimensions()
321  {
322  float minX = vertices[0].Pos.X, minY = vertices[0].Pos.Y, maxX = vertices[0].Pos.X, maxY = vertices[0].Pos.Y;
323 
324  for (int i = 1; i < vertices.Length; i++)
325  {
326  minX = Math.Min(minX, vertices[i].Pos.X);
327  minY = Math.Min(minY, vertices[i].Pos.Y);
328  maxX = Math.Max(maxX, vertices[i].Pos.X);
329  maxY = Math.Max(maxY, vertices[i].Pos.Y);
330  }
331 
332  BoundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
333  }
334 
335  public void Move(Vector2 amount)
336  {
337  for (int i = 0; i < vertices.Length; i++)
338  {
339  vertices[i].Pos += amount;
340  segments[i].Start.Pos += amount;
341  segments[i].End.Pos += amount;
342  }
343  for (int i = 0; i < losVertices.Length; i++)
344  {
345  losVertices[i].Pos += amount;
346  }
347 
348  LastVertexChangeTime = (float)Timing.TotalTime;
349 
350  overlappingHulls.Clear();
351 
352  CalculateDimensions();
353 
354  if (ParentEntity == null) { return; }
355 
356  var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
357  if (chList != null)
358  {
359  overlappingHulls.Clear();
360  foreach (ConvexHull ch in chList.List)
361  {
362  MergeLosVertices(ch);
363  ch.MergeLosVertices(this);
364  }
365  }
366  }
367 
368  public static void RecalculateAll(Submarine sub)
369  {
370  var chList = HullLists.Find(h => h.Submarine == sub);
371  if (chList != null)
372  {
373  foreach (ConvexHull ch in chList.List)
374  {
375  ch.overlappingHulls.Clear();
376  for (int i = 0; i < ch.losOffsets.Length; i++)
377  {
378  ch.losOffsets[i] = Vector2.Zero;
379  }
380  }
381  for (int i = 0; i < chList.List.Count; i++)
382  {
383  for (int j = i + 1; j < chList.List.Count; j++)
384  {
385  chList.List[i].MergeLosVertices(chList.List[j]);
386  chList.List[j].MergeLosVertices(chList.List[i]);
387  }
388  }
389  }
390  }
391 
392  public void SetVertices(Vector2[] points, Vector2[] losPoints, bool mergeOverlappingSegments = true, Matrix? rotationMatrix = null)
393  {
394  Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
395 
396  LastVertexChangeTime = (float)Timing.TotalTime;
397 
398  for (int i = 0; i < 4; i++)
399  {
400  vertices[i] = new SegmentPoint(points[i], this);
401  }
402  for (int i = 0; i < 2; i++)
403  {
404  losVertices[i] = new SegmentPoint(losPoints[i], this);
405  losOffsets[i] = Vector2.Zero;
406  }
407 
408  overlappingHulls.Clear();
409 
410  if (rotationMatrix.HasValue)
411  {
412  for (int i = 0; i < vertices.Length; i++)
413  {
414  vertices[i].Pos = Vector2.Transform(vertices[i].Pos, rotationMatrix.Value);
415  }
416  for (int i = 0; i < losVertices.Length; i++)
417  {
418  losVertices[i].Pos = Vector2.Transform(losVertices[i].Pos, rotationMatrix.Value);
419  }
420  }
421  for (int i = 0; i < 4; i++)
422  {
423  segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4], this);
424  }
425 
426  CalculateDimensions();
427 
428  if (ParentEntity == null) { return; }
429 
430  if (mergeOverlappingSegments)
431  {
432  var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
433  if (chList != null)
434  {
435  overlappingHulls.Clear();
436  foreach (ConvexHull ch in chList.List)
437  {
438  MergeLosVertices(ch);
439  }
440  }
441  }
442  }
443 
444  public bool Intersects(Rectangle rect)
445  {
446  if (!Enabled) return false;
447 
448  Rectangle transformedBounds = BoundingBox;
449  if (ParentEntity != null && ParentEntity.Submarine != null)
450  {
451  transformedBounds.X += (int)ParentEntity.Submarine.Position.X;
452  transformedBounds.Y += (int)ParentEntity.Submarine.Position.Y;
453  }
454  return transformedBounds.Intersects(rect);
455  }
456 
460  public void GetVisibleSegments(Vector2 viewPosition, List<Segment> visibleSegments)
461  {
462  for (int i = 0; i < 4; i++)
463  {
464  if (IsSegmentFacing(vertices[i].WorldPos, vertices[(i + 1) % 4].WorldPos, viewPosition))
465  {
466  visibleSegments.Add(segments[i]);
467  }
468  }
469  }
470 
471  public void RefreshWorldPositions()
472  {
473  for (int i = 0; i < 4; i++)
474  {
475  vertices[i].WorldPos = vertices[i].Pos;
476  ValidateVertex(vertices[i].WorldPos, "vertices[i].Pos");
477  segments[i].Start.WorldPos = segments[i].Start.Pos;
478  ValidateVertex(segments[i].Start.WorldPos, "segments[i].Start.Pos");
479  segments[i].End.WorldPos = segments[i].End.Pos;
480  ValidateVertex(segments[i].End.WorldPos, "segments[i].End.Pos");
481  }
482  if (ParentEntity == null || ParentEntity.Submarine == null) { return; }
483  for (int i = 0; i < 4; i++)
484  {
485  vertices[i].WorldPos += ParentEntity.Submarine.DrawPosition;
486  ValidateVertex(vertices[i].WorldPos, "vertices[i].WorldPos");
487  segments[i].Start.WorldPos += ParentEntity.Submarine.DrawPosition;
488  ValidateVertex(segments[i].Start.WorldPos, "segments[i].Start.WorldPos");
489  segments[i].End.WorldPos += ParentEntity.Submarine.DrawPosition;
490  ValidateVertex(segments[i].End.WorldPos, "segments[i].End.WorldPos");
491  }
492 
493  void ValidateVertex(Vector2 vertex, string debugName)
494  {
495  if (!MathUtils.IsValid(vertex))
496  {
497  IsInvalid = true;
498  string errorMsg = $"Invalid vertex on convex hull ({debugName}: {vertex}, parent entity: {ParentEntity?.ToString() ?? "null"}).";
499 #if DEBUG
500  DebugConsole.ThrowError(errorMsg);
501 #endif
502  GameAnalyticsManager.AddErrorEventOnce("ConvexHull.RefreshWorldPositions:InvalidVertex", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
503  }
504  }
505  }
506 
507  public void CalculateLosVertices(Vector2 lightSourcePos)
508  {
509  Vector3 offset = Vector3.Zero;
510  if (ParentEntity != null && ParentEntity.Submarine != null)
511  {
512  offset = new Vector3(ParentEntity.Submarine.DrawPosition.X, ParentEntity.Submarine.DrawPosition.Y, 0.0f);
513  }
514 
515  ShadowVertexCount = 0;
516 
517  for (int i = 0; i < losVertices.Length; i++)
518  {
519  int currentIndex = i;
520  int nextIndex = (currentIndex + 1) % 2;
521  Vector3 vertexPos0 = new Vector3(losVertices[currentIndex].Pos + losOffsets[currentIndex], 0.0f);
522  Vector3 vertexPos1 = new Vector3(losVertices[nextIndex].Pos + losOffsets[nextIndex], 0.0f);
523 
524  if (Vector3.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
525 
526  Vector3 L2P0 = vertexPos0 - new Vector3(lightSourcePos, 0);
527  L2P0.Normalize();
528  Vector3 extruded0 = new Vector3(lightSourcePos, 0) + L2P0 * 9000;
529 
530  Vector3 L2P1 = vertexPos1 - new Vector3(lightSourcePos, 0);
531  L2P1.Normalize();
532  Vector3 extruded1 = new Vector3(lightSourcePos, 0) + L2P1 * 9000;
533 
534  ShadowVertices[ShadowVertexCount + 0] = new VertexPositionColor
535  {
536  Color = Color.Black,
537  Position = vertexPos1 + offset
538  };
539 
540  ShadowVertices[ShadowVertexCount + 1] = new VertexPositionColor
541  {
542  Color = Color.Black,
543  Position = vertexPos0 + offset
544  };
545 
546  ShadowVertices[ShadowVertexCount + 2] = new VertexPositionColor
547  {
548  Color = Color.Black,
549  Position = extruded0 + offset
550  };
551 
552  ShadowVertices[ShadowVertexCount + 3] = new VertexPositionColor
553  {
554  Color = Color.Black,
555  Position = vertexPos1 + offset
556  };
557 
558  ShadowVertices[ShadowVertexCount + 4] = new VertexPositionColor
559  {
560  Color = Color.Black,
561  Position = extruded0 + offset
562  };
563 
564  ShadowVertices[ShadowVertexCount + 5] = new VertexPositionColor
565  {
566  Color = Color.Black,
567  Position = extruded1 + offset
568  };
569 
570  ShadowVertexCount += 6;
571  }
572 
573  if (IsSegmentFacing(losVertices[0].Pos, losVertices[1].Pos, lightSourcePos))
574  {
575  Array.Reverse(ShadowVertices, 0, ShadowVertexCount);
576  }
577 
578  CalculateLosPenumbraVertices(lightSourcePos);
579  }
580 
581  private static bool IsSegmentFacing(Vector2 segmentPos1, Vector2 segmentPos2, Vector2 viewPosition)
582  {
583  Vector2 segmentMid = (segmentPos1 + segmentPos2) / 2;
584  Vector2 segmentDiff = segmentPos2 - segmentPos1;
585  Vector2 segmentNormal = new Vector2(-segmentDiff.Y, segmentDiff.X);
586 
587  Vector2 viewDirection = viewPosition - segmentMid;
588  return Vector2.Dot(segmentNormal, viewDirection) > 0;
589  }
590 
591  private void CalculateLosPenumbraVertices(Vector2 lightSourcePos)
592  {
593  Vector3 offset = Vector3.Zero;
594  if (ParentEntity != null && ParentEntity.Submarine != null)
595  {
596  offset = new Vector3(ParentEntity.Submarine.DrawPosition.X, ParentEntity.Submarine.DrawPosition.Y, 0.0f);
597  }
598 
600  for (int i = 0; i < losVertices.Length; i++)
601  {
602  int currentIndex = i;
603  int nextIndex = (i + 1) % 2;
604  Vector2 vertexPos0 = losVertices[currentIndex].Pos + losOffsets[currentIndex];
605  Vector2 vertexPos1 = losVertices[nextIndex].Pos + losOffsets[nextIndex];
606 
607  if (Vector2.DistanceSquared(vertexPos0, vertexPos1) < 1.0f) { continue; }
608 
609  Vector3 penumbraStart = new Vector3(vertexPos0, 0.0f);
610 
611  PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
612  {
613  Position = penumbraStart + offset,
614  TextureCoordinate = new Vector2(0.0f, 1.0f)
615  };
616 
617  for (int j = 0; j < 2; j++)
618  {
619  PenumbraVertices[PenumbraVertexCount + j + 1] = new VertexPositionTexture();
620  Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
621  vertexDir.Normalize();
622 
623  Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
624 
625  vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
626  vertexDir.Normalize();
627  PenumbraVertices[PenumbraVertexCount + j + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
628 
629  PenumbraVertices[PenumbraVertexCount + j + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
630  }
631 
632  PenumbraVertexCount += 3;
633 
634  penumbraStart = new Vector3(vertexPos1, 0.0f);
635 
636  PenumbraVertices[PenumbraVertexCount] = new VertexPositionTexture
637  {
638  Position = penumbraStart + offset,
639  TextureCoordinate = new Vector2(0.0f, 1.0f)
640  };
641 
642  for (int j = 0; j < 2; j++)
643  {
644  PenumbraVertices[PenumbraVertexCount + (1 - j) + 1] = new VertexPositionTexture();
645  Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
646  vertexDir.Normalize();
647 
648  Vector3 normal = (j == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
649 
650  vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) + normal * 20.0f);
651  vertexDir.Normalize();
652  PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000 + offset;
653 
654  PenumbraVertices[PenumbraVertexCount + (1 - j) + 1].TextureCoordinate = (j == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
655  }
656 
657  PenumbraVertexCount += 3;
658  }
659  }
660 
661  public void DebugDraw(SpriteBatch spriteBatch)
662  {
663  //RecalculateAll(Submarine.MainSub);
664  //RefreshWorldPositions();
665 
666  DrawLine(losVertices[0].Pos, losVertices[1].Pos, Color.Gray * 0.5f, width: 3);
667  DrawLine(losVertices[0].Pos + losOffsets[0], losVertices[1].Pos + losOffsets[1], Color.LightGreen, width: 2);
668  DrawLine(GameMain.GameScreen.Cam.Position + Vector2.One * 1000, GameMain.GameScreen.Cam.Position - Vector2.One * 1000, Color.Magenta, width: 2);
669 
670  if (GameMain.LightManager.LightingEnabled)
671  {
672  for (int i = 0; i < vertices.Length; i++)
673  {
674  Vector2 start = vertices[i].Pos;
675  Vector2 end = vertices[(i + 1) % 4].Pos;
676  DrawLine(
677  start,
678  end, Color.Yellow * 0.5f,
679  width: 4);
680  }
681  }
682 
683  void DrawLine(Vector2 vertexPos0, Vector2 vertexPos1, Color color, int width)
684  {
685  if (ParentEntity != null && ParentEntity.Submarine != null)
686  {
687  vertexPos0 += ParentEntity.Submarine.DrawPosition;
688  vertexPos1 += ParentEntity.Submarine.DrawPosition;
689  }
690  float alpha = 1.0f;
691  if (LightManager.ViewTarget != null)
692  {
693  alpha = IsSegmentFacing(vertexPos0, vertexPos1, LightManager.ViewTarget.WorldPosition) ? 1.0f : 0.5f;
694  }
695  vertexPos0.Y = -vertexPos0.Y;
696  vertexPos1.Y = -vertexPos1.Y;
697  GUI.DrawLine(spriteBatch, vertexPos0, vertexPos1, color * alpha, width: width);
698  }
699  }
700 
701  public static List<ConvexHull> GetHullsInRange(Vector2 position, float range, Submarine ParentSub)
702  {
703  List<ConvexHull> list = new List<ConvexHull>();
704 
705  foreach (ConvexHullList chList in HullLists)
706  {
707  Vector2 lightPos = position;
708  if (ParentSub == null)
709  {
710  //light and the convexhull are both outside
711  if (chList.Submarine == null)
712  {
713  list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
714  }
715  //light is outside, convexhull inside a sub
716  else
717  {
718  Rectangle subBorders = chList.Submarine.Borders;
719  subBorders.Y -= chList.Submarine.Borders.Height;
720  if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, subBorders)) { continue; }
721 
722  lightPos -= chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition;
723 
724  list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
725  }
726  }
727  else
728  {
729  //light is inside, convexhull outside
730  if (chList.Submarine == null)
731  {
732  continue;
733  }
734  //light and convexhull are both inside the same sub
735  if (chList.Submarine == ParentSub)
736  {
737  list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
738  }
739  //light and convexhull are inside different subs
740  else
741  {
742  lightPos -= (chList.Submarine.Position - ParentSub.Position);
743 
744  Rectangle subBorders = chList.Submarine.Borders;
745  subBorders.Location += chList.Submarine.HiddenSubPosition.ToPoint() - new Point(0, chList.Submarine.Borders.Height);
746 
747  if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) continue;
748 
749  list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
750  }
751  }
752  }
753 
754  return list;
755  }
756 
757  public void Remove()
758  {
759  var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
760 
761  if (chList != null)
762  {
763  chList.List.Remove(this);
764  if (chList.List.Count == 0)
765  {
766  HullLists.Remove(chList);
767  }
768  //create a new list because MergeLosVertices can edit overlappingHulls
769  foreach (ConvexHull ch2 in overlappingHulls.ToList())
770  {
771  ch2.overlappingHulls.Remove(this);
772  foreach (ConvexHull ch in chList.List)
773  {
774  ch.MergeLosVertices(ch2);
775  }
776  }
777  }
778  }
779  }
780 }
Vector2 Position
Definition: Camera.cs:398
virtual Vector2 WorldPosition
Definition: Entity.cs:49
Submarine Submarine
Definition: Entity.cs:53
static Lights.LightManager LightManager
Definition: GameMain.cs:78
static GameScreen GameScreen
Definition: GameMain.cs:52
static GameMain Instance
Definition: GameMain.cs:144
VertexPositionTexture[] PenumbraVertices
Definition: ConvexHull.cs:97
bool Intersects(Rectangle rect)
Definition: ConvexHull.cs:444
void Move(Vector2 amount)
Definition: ConvexHull.cs:335
static List< ConvexHull > GetHullsInRange(Vector2 position, float range, Submarine ParentSub)
Definition: ConvexHull.cs:701
void SetVertices(Vector2[] points, Vector2[] losPoints, bool mergeOverlappingSegments=true, Matrix? rotationMatrix=null)
Definition: ConvexHull.cs:392
float? MaxMergeLosVerticesDist
Overrides the maximum distance a LOS vertex can be moved to make it align with a nearby LOS segment
Definition: ConvexHull.cs:104
bool LosIntersects(Vector2 pos1, Vector2 pos2)
Definition: ConvexHull.cs:304
ConvexHull(Rectangle rect, bool isHorizontal, MapEntity parent)
Definition: ConvexHull.cs:138
void Rotate(Vector2 origin, float amount)
Definition: ConvexHull.cs:311
float LastVertexChangeTime
The elapsed gametime when the vertices of this hull last changed
Definition: ConvexHull.cs:129
static List< ConvexHullList > HullLists
Definition: ConvexHull.cs:83
static BasicEffect shadowEffect
Definition: ConvexHull.cs:84
void DebugDraw(SpriteBatch spriteBatch)
Definition: ConvexHull.cs:661
static BasicEffect penumbraEffect
Definition: ConvexHull.cs:85
VertexPositionColor[] ShadowVertices
Definition: ConvexHull.cs:96
void CalculateLosVertices(Vector2 lightSourcePos)
Definition: ConvexHull.cs:507
void GetVisibleSegments(Vector2 viewPosition, List< Segment > visibleSegments)
Returns the segments that are facing towards viewPosition
Definition: ConvexHull.cs:460
static void RecalculateAll(Submarine sub)
Definition: ConvexHull.cs:368
readonly List< ConvexHull > List
Definition: ConvexHull.cs:16
HashSet< ConvexHull > IsHidden
Definition: ConvexHull.cs:14
readonly Submarine Submarine
Definition: ConvexHull.cs:13
HashSet< ConvexHull > HasBeenVisible
Definition: ConvexHull.cs:15
ConvexHullList(Submarine submarine)
Definition: ConvexHull.cs:18
Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull)
Definition: ConvexHull.cs:36
Rectangle? Borders
Extents of the solid items/structures (ones with a physics body) and hulls
override string ToString()
Definition: ConvexHull.cs:69
SegmentPoint(Vector2 pos, ConvexHull convexHull)
Definition: ConvexHull.cs:62