public void FillCircle(FPoint center, float radius, int sides, Color color) { #if DEBUG IncRenderSpriteCount(sides); #endif float angle = FloatMath.TAU / sides; float sideWidth = FloatMath.Asin(angle / 2) * 2 * radius; float rectHeight = FloatMath.Sin(FloatMath.RAD_POS_090 - angle / 2) * radius; var r = new FRectangle(center.X - sideWidth / 2, center.Y, sideWidth, rectHeight); for (int i = 0; i < sides; i++) { internalBatch.Draw( StaticTextures.SinglePixel.Texture, null, r.Round(), StaticTextures.SinglePixel.Bounds, new Vector2(0.5f, 0), angle * i, Vector2.One, color); } }
private List <Tuple <BulletPathBlueprint, float> > FindLaserPaths(LevelBlueprint lvl, World wBase, World wCollision, int sourceID, FPoint rcStart, List <Tuple <Vector2, Vector2> > sourcerays, float startRadians, float cannonRadians, int remainingRecasts, bool inGlassBlock, object objIgnore) { var none = new List <Tuple <BulletPathBlueprint, float> >(); if (remainingRecasts <= 0) { return(none); } var rays = sourcerays.ToList(); var rcEnd = rcStart + new Vector2(2048, 0).Rotate(startRadians); var traceResult = RayCastLaser(wBase, rcStart, rcEnd, objIgnore); var traceResult2 = RayCastLaser(wCollision, rcStart, rcEnd, objIgnore); if (traceResult2 != null && traceResult != null && traceResult2.Item1.UserData != traceResult.Item1.UserData) { // Dirty hit return(none); } if (traceResult == null) { return(none); } foreach (var r in rays) { if (Math2D.LineIntersectionExt(rcStart, traceResult.Item2, r.Item1.ToFPoint(), r.Item2.ToFPoint(), -0.1f, out _, out _, out _)) { return(none); } } var fCannon = traceResult.Item1.UserData as ICannonBlueprint; if (fCannon != null) { if (fCannon.CannonID == sourceID) { return(none); } var quality = Math2D.LinePointDistance(rcStart, traceResult.Item2, new FPoint(fCannon.X, fCannon.Y)); rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var path = new BulletPathBlueprint(fCannon.CannonID, cannonRadians, rays.ToArray()); return(new List <Tuple <BulletPathBlueprint, float> > { Tuple.Create(path, quality) }); } var fGlassBlock = traceResult.Item1.UserData as MarkerRefractionEdge; if (fGlassBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var normal = traceResult.Item3; var aIn = (rcStart - rcEnd).ToAngle() - normal.ToAngle(); // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var n = inGlassBlock ? (GlassBlockBlueprint.REFRACTION_INDEX / 1f) : (1f / GlassBlockBlueprint.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var dat = new List <Tuple <BulletPathBlueprint, float> >(); var isRefracting = sinaOut <1 && sinaOut> -1; var isReflecting = FloatMath.Abs(aIn) > LaserNetwork.MIN_REFRACT_ANGLE && (!inGlassBlock || (inGlassBlock && !isRefracting)); if (isRefracting) // refraction { // refraction var aOut = FloatMath.Asin(sinaOut); var pRefractAngle = (-normal).ToAngle() + aOut; var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pRefractAngle, cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlock); dat.AddRange(sub); } if (isReflecting) { // reflection var pReflectVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pReflectVec.ToAngle(), cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlock); dat.AddRange(sub); } return(dat); } var fGlassBlockCorner = traceResult.Item1.UserData as MarkerRefractionCorner; if (fGlassBlockCorner != null) { if (NO_LASER_CORNER_HITS) { return(none); } rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var dat = new List <Tuple <BulletPathBlueprint, float> >(); if (!inGlassBlock) { // reflection var pReflectVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pReflectVec.ToAngle(), cannonRadians, remainingRecasts - 1, !inGlassBlock, fGlassBlockCorner); dat.AddRange(sub); } return(dat); } var fMirrorBlock = traceResult.Item1.UserData as MirrorBlockBlueprint; if (fMirrorBlock != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fMirrorBlock)); } var fMirrorCircle = traceResult.Item1.UserData as MirrorCircleBlueprint; if (fMirrorCircle != null) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fMirrorCircle)); } var fVoidWall = traceResult.Item1.UserData as VoidWallBlueprint; if (fVoidWall != null) { return(none); } var fVoidCircle = traceResult.Item1.UserData as VoidCircleBlueprint; if (fVoidCircle != null) { return(none); } var fPortal = traceResult.Item1.UserData as PortalBlueprint; if (fPortal != null) { bool hit = FloatMath.DiffRadiansAbs(traceResult.Item3.ToAngle(), FloatMath.ToRadians(fPortal.Normal)) < FloatMath.RAD_POS_005; if (!hit) { return(none); } rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var dat = new List <Tuple <BulletPathBlueprint, float> >(); foreach (var outportal in lvl.BlueprintPortals.Where(p => p.Side != fPortal.Side && p.Group == fPortal.Group)) { var cIn = new FPoint(fPortal.X, fPortal.Y); var cOut = new FPoint(outportal.X, outportal.Y); var rot = FloatMath.ToRadians(outportal.Normal - fPortal.Normal + 180); var stretch = outportal.Length / fPortal.Length; var newAngle = FloatMath.NormalizeAngle(startRadians + rot); var newStart = cOut + stretch * (traceResult.Item2 - cIn).Rotate(rot); newStart = newStart.MirrorAtNormal(cOut, Vector2.UnitX.Rotate(FloatMath.ToRadians(outportal.Normal))); var sub = FindLaserPaths(lvl, wBase, wCollision, sourceID, newStart, rays, newAngle, cannonRadians, remainingRecasts - 1, inGlassBlock, outportal); dat.AddRange(sub); } return(dat); } var fBorder = traceResult.Item1.UserData as MarkerCollisionBorder; if (fBorder != null) { if (lvl.WrapMode == LevelBlueprint.WRAPMODE_DONUT) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStartX = traceResult.Item2.X; var pNewStartY = traceResult.Item2.Y; switch (fBorder.Side) { case FlatAlign4.NN: pNewStartY += lvl.LevelHeight; break; case FlatAlign4.EE: pNewStartX -= lvl.LevelWidth; break; case FlatAlign4.SS: pNewStartY -= lvl.LevelHeight; break; case FlatAlign4.WW: pNewStartX += lvl.LevelWidth; break; default: throw new ArgumentOutOfRangeException(); } var pVec = rcEnd - rcStart; var pNewStart = new FPoint(pNewStartX, pNewStartY); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fBorder)); } else if (lvl.WrapMode == LevelBlueprint.WRAPMODE_SOLID) { rays.Add(Tuple.Create(rcStart.ToVec2D(), traceResult.Item2.ToVec2D())); var pNewStart = traceResult.Item2; var pVec = Vector2.Reflect(rcEnd - rcStart, traceResult.Item3); return(FindLaserPaths(lvl, wBase, wCollision, sourceID, pNewStart, rays, pVec.ToAngle(), cannonRadians, remainingRecasts - 1, inGlassBlock, fBorder)); } throw new Exception("Unsupported WrapMode: " + lvl.WrapMode); } throw new Exception("Unknown rayTrace resturn ficture: " + traceResult.Item1.UserData); }
private void RecalcFromRay(LaserSource src, FPoint istart, FPoint iend, int idepth, bool iinglass, object iignore, LaserRay isrc, float idist, bool nofault) { Stack <Tuple <FPoint, FPoint, int, bool, object, LaserRay, float> > remaining = new Stack <Tuple <FPoint, FPoint, int, bool, object, LaserRay, float> >(); remaining.Push(Tuple.Create(istart, iend, idepth, iinglass, iignore, isrc, idist)); while (remaining.Any()) { var pop = remaining.Pop(); var start = pop.Item1; var end = pop.Item2; var depth = pop.Item3; var inglass = pop.Item4; var ignore = pop.Item5; var source = pop.Item6; var startdist = pop.Item7; if (src.Lasers.Count + 1 >= MAX_LASER_PER_SOURCE) { continue; } if (depth >= MAX_LASER_RAYCOUNT) { continue; } if (!start.IsValid) { continue; } if (!end.IsValid) { continue; } if (start.EpsilonEquals(end, FloatMath.EPSILON6)) { continue; } var result = RayCast(start, end, ignore); #region OOB if (result == null) { var ray = new LaserRay(start, end, source, LaserRayTerminator.OOB, depth, inglass, ignore, null, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region Cannon var resultCannon = result.Item1.UserData as Cannon; if (resultCannon != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultCannon, startdist, resultCannon, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region GlassBlockRefraction var resultGlassBlockRefrac = result.Item1.UserData as MarkerRefractionEdge; if (resultGlassBlockRefrac != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Glass, depth, inglass, ignore, resultGlassBlockRefrac, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var aIn = (start - end).ToAngle() - result.Item3.ToAngle(); var n = inglass ? (GlassBlock.REFRACTION_INDEX / 1f) : (1f / GlassBlock.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var isRefracting = sinaOut <1 && sinaOut> -1; var isReflecting = FloatMath.Abs(aIn) > MIN_REFRACT_ANGLE && (!inglass || (inglass && !isRefracting)); if (isRefracting) // refraction { var aOut = FloatMath.Asin(sinaOut); var pRefractAngle = (-result.Item3).ToAngle() + aOut; remaining.Push(Tuple.Create(result.Item2, result.Item2 + new Vector2(_rayLength, 0).Rotate(pRefractAngle), depth + 1, !inglass, (object)resultGlassBlockRefrac, ray, startdist + ray.Length)); } if (isReflecting) { var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultGlassBlockRefrac, ray, startdist + ray.Length)); } continue; } #endregion #region GlassBlockRefraction (Corner) var resultGlassCornerRefrac = result.Item1.UserData as MarkerRefractionCorner; if (resultGlassCornerRefrac != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultGlassCornerRefrac, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } // sin(aIn) / sin(aOut) = currRefractIdx / Glass.RefractIdx var aIn = (start - end).ToAngle() - result.Item3.ToAngle(); var n = inglass ? (GlassBlock.REFRACTION_INDEX / 1f) : (1f / GlassBlock.REFRACTION_INDEX); var sinaOut = FloatMath.Sin(aIn) * n; var isRefracting = sinaOut <1 && sinaOut> -1; if (isRefracting) // refraction { // No refrac in corner } if (!inglass) { var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultGlassCornerRefrac, ray, startdist + ray.Length)); continue; } else { // No funtime in corner continue; } } #endregion #region MirrorBlock var resultMirrorBlock = result.Item1.UserData as MirrorBlock; if (resultMirrorBlock != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultMirrorBlock, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultMirrorBlock, ray, startdist + ray.Length)); continue; } #endregion #region MirrorCircle var resultMirrorCircle = result.Item1.UserData as MirrorCircle; if (resultMirrorCircle != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultMirrorCircle, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultMirrorCircle, ray, startdist + ray.Length)); continue; } #endregion #region VoidWall var resultVoidWall = result.Item1.UserData as VoidWall; if (resultVoidWall != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultVoidWall, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region VoidCircle var resultVoidCircle = result.Item1.UserData as VoidCircle; if (resultVoidCircle != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.VoidObject, depth, inglass, ignore, resultVoidCircle, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region Shield var resultShield = result.Item1.UserData as ShieldCollisionMarker; if (resultShield != null) { if (resultShield.Active) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultShield, startdist, resultShield.Source, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } else if (src.Type == RayType.Shield && src.LaserFraction == resultShield.Source.Fraction) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Target, depth, inglass, ignore, resultShield, startdist, resultShield.Source, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } else { continue; } } #endregion #region Portal var resultPortal = result.Item1.UserData as Portal; if (resultPortal != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Portal, depth, inglass, ignore, resultPortal, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var inPortal = resultPortal; var normal = result.Item3; bool hit = FloatMath.DiffRadiansAbs(normal.ToAngle(), inPortal.Normal) < FloatMath.RAD_POS_001; if (!hit) { continue; // back-side hit } if (inPortal.Links.Count == 0) { continue; // void portal } foreach (var outportal in inPortal.Links) { var rot = outportal.Normal - inPortal.Normal + FloatMath.RAD_POS_180; var projec = result.Item2.ProjectOntoLine(inPortal.Position, inPortal.VecDirection); var newVelocity = (end - start).Rotate(rot); var newStart = outportal.Position + outportal.VecDirection * (-projec) + outportal.VecNormal * (Portal.WIDTH / 2f); var newEnd = newStart + newVelocity.WithLength(_rayLength); remaining.Push(Tuple.Create(newStart, newEnd, depth + 1, false, (object)outportal, ray, startdist + ray.Length)); } continue; } #endregion #region Border var resultBorderMarker = result.Item1.UserData as MarkerCollisionBorder; if (resultBorderMarker != null) { if (_wrapMode == GameWrapMode.Reflect) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Mirror, depth, inglass, ignore, resultBorderMarker, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var reflect_end = result.Item2 + Vector2.Reflect(end - start, result.Item3).WithLength(_rayLength); remaining.Push(Tuple.Create(result.Item2, reflect_end, depth + 1, inglass, (object)resultBorderMarker, ray, startdist + ray.Length)); continue; } else if (_wrapMode == GameWrapMode.Donut) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.Portal, depth, inglass, ignore, resultBorderMarker, startdist, null, src.Type); src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } var pNewStartX = result.Item2.X; var pNewStartY = result.Item2.Y; switch (resultBorderMarker.Side) { case FlatAlign4.NN: pNewStartY += _screen.Blueprint.LevelHeight; break; case FlatAlign4.EE: pNewStartX -= _screen.Blueprint.LevelWidth; break; case FlatAlign4.SS: pNewStartY -= _screen.Blueprint.LevelHeight; break; case FlatAlign4.WW: pNewStartX += _screen.Blueprint.LevelWidth; break; default: SAMLog.Error("LASER::EnumSwitch_RFR", "value: " + resultBorderMarker.Side); break; } var newStart = new FPoint(pNewStartX, pNewStartY); var newEnd = newStart + (end - start); remaining.Push(Tuple.Create(newStart, newEnd, depth + 1, inglass, (object)null, ray, startdist + ray.Length)); continue; } else { SAMLog.Error("LASER::UnknownWrap", "Unknown wrapmode: " + _wrapMode); } continue; } #endregion #region Bullet var resultBullet = result.Item1.UserData as Bullet; if (resultBullet != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.BulletTerm, depth, inglass, ignore, resultBullet, startdist, null, src.Type); ray.TerminatorBullet = resultBullet; src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion #region RemoteBullet var resultRemoteBullet = result.Item1.UserData as RemoteBullet; if (resultRemoteBullet != null) { var ray = new LaserRay(start, result.Item2, source, LaserRayTerminator.BulletTerm, depth, inglass, ignore, resultRemoteBullet, startdist, null, src.Type); ray.TerminatorBullet = resultRemoteBullet; src.Lasers.Add(ray); if (TestForLaserCollision(src, ray, nofault)) { continue; } continue; } #endregion // wud ??? SAMLog.Error("LASER::UnknownFixture", string.Format("Ray collided with unkown fixture: {0}", result?.Item1?.UserData ?? "<NULL>")); } }