예제 #1
0
        public LaserRay SetLaserIntersect(LaserSource src, FPoint e, LaserRay otherRay, LaserSource otherSource, LaserRayTerminator t)
        {
            if ((Start - e).LengthSquared() < FloatMath.EPSILON4)
            {
                src.Lasers.Remove(this);
                return(Source?.SetLaserIntersect(src, Source.End, otherRay, otherSource, t));
            }

            End        = e;
            Terminator = t;
            _length    = null;
            _angle     = null;

            TerminatorRays.Add(Tuple.Create(otherRay, otherSource));

#if DEBUG
            if (!Start.IsValid)
            {
                SAMLog.Error("LASER::Assert_2-SV", "!Start.IsValid");
            }
            if (!End.IsValid)
            {
                SAMLog.Error("LASER::Assert_2-EV", "!End.IsValid");
            }
            if ((End - Start).LengthSquared() < FloatMath.EPSILON7 * FloatMath.EPSILON7)
            {
                SAMLog.Error("LASER::Assert_2-ESV", "(End - Start).LengthSquared() < FloatMath.EPSILON7 * FloatMath.EPSILON7");
            }
#endif

            return(this);
        }
예제 #2
0
        public LaserRay(FPoint s, FPoint e, LaserRay src, LaserRayTerminator t, int d, bool g, object sign, object eign, float sd, Cannon tc, RayType rt)
        {
            Depth          = d;
            InGlass        = g;
            StartIgnoreObj = sign;
            EndIgnoreObj   = eign;
            Start          = s;
            End            = e;
            Source         = src;
            Terminator     = t;
            TargetCannon   = tc;
            TerminatorRays = new List <Tuple <LaserRay, LaserSource> >();
            SourceDistance = sd;
            RayType        = rt;

#if DEBUG
            if (!Start.IsValid)
            {
                SAMLog.Error("LASER::Assert_1-SV", "!Start.IsValid");
            }
            if (!End.IsValid)
            {
                SAMLog.Error("LASER::Assert_1-EV", "!End.IsValid");
            }
            if ((End - Start).LengthSquared() < FloatMath.EPSILON7 * FloatMath.EPSILON7)
            {
                SAMLog.Error("LASER::Assert_1-ESV", "(End - Start).LengthSquared() < FloatMath.EPSILON7 * FloatMath.EPSILON7");
            }
#endif
        }
예제 #3
0
        private void CutRayAndUpdate(LaserSource src, LaserRay ray, FPoint intersect, LaserSource srcOther, LaserRay rayOther)
        {
            if (ray.Terminator == LaserRayTerminator.LaserMultiTerm)
            {
                foreach (var tray in ray.TerminatorRays)
                {
                    _faultRays.Add(tray);                                                      // Update Rays that hit this one later
                }
            }

            // cut this ray

            ray.SetLaserIntersect(src, intersect, rayOther, srcOther, LaserRayTerminator.LaserMultiTerm);
            foreach (var r in ray.SelfCollRays)
            {
                _faultRays.Add(Tuple.Create(r, src));
            }
            ray.SelfCollRays.Clear();

            // remove all child rays

            Stack <LaserRay> rem = new Stack <LaserRay>();

            rem.Push(ray);
            while (rem.Any())
            {
                var elim = rem.Pop();

                for (int i = src.Lasers.Count - 1; i >= 0; i--)
                {
                    if (src.Lasers[i].Source != elim)
                    {
                        continue;
                    }

                    if (src.Lasers[i].Terminator == LaserRayTerminator.LaserMultiTerm)
                    {
                        foreach (var tray in src.Lasers[i].TerminatorRays)
                        {
                            _faultRays.Add(tray);
                        }
                    }

                    foreach (var r in src.Lasers[i].SelfCollRays)
                    {
                        _faultRays.Add(Tuple.Create(r, src));
                    }

                    rem.Push(src.Lasers[i]);
                    src.Lasers.RemoveAt(i);
                }
            }
        }
예제 #4
0
        private bool TestForLaserCollision(LaserSource src1, LaserRay ray1, bool nofault)
        {
            float       minU         = float.MaxValue;
            FPoint      minP1        = FPoint.Zero;
            FPoint      minP2        = FPoint.Zero;
            LaserRay    minRay2      = null;
            LaserSource minSrc2      = null;
            bool        minTermOther = false;


            foreach (var src2 in Sources)
            {
                foreach (var ray2 in src2.Lasers)
                {
                    if (src1 == src2 && ray1 == ray2)
                    {
                        continue;
                    }

                    // (Not only) Direct (parallel) Collision
                    FPoint intersect;
                    float  u;
                    if (RayParallality(ray1.Start, ray1.End, ray1.SourceDistance, ray2.Start, ray2.End, ray2.SourceDistance, src1 == src2, out intersect, out u))
                    {
                        if (u < minU)
                        {
                            var pp1 = intersect.ProjectPointOntoLineSegment(ray1.Start, ray1.End);
                            var pp2 = intersect.ProjectPointOntoLineSegment(ray2.Start, ray2.End);

                            if ((pp1 - pp2).LengthSquared() < RAY_WIDTH * RAY_WIDTH)
                            {
                                minU         = u;
                                minP1        = pp1;
                                minP2        = pp2;
                                minRay2      = ray2;
                                minSrc2      = src2;
                                minTermOther = true;
                                continue;
                            }
                        }
                    }

                    // Normal intersection collision
                    if (Math2D.LineIntersectionExt(ray1.Start, ray1.End, ray2.Start, ray2.End, -0.1f, out intersect, out u, out _))
                    {
                        if (u < minU)
                        {
                            var pp1 = intersect.ProjectPointOntoLineSegment(ray1.Start, ray1.End);
                            var pp2 = intersect.ProjectPointOntoLineSegment(ray2.Start, ray2.End);

                            if ((pp1 - pp2).LengthSquared() < RAY_WIDTH * RAY_WIDTH)
                            {
                                minU         = u;
                                minP1        = pp1;
                                minP2        = pp2;
                                minRay2      = ray2;
                                minSrc2      = src2;
                                minTermOther = true;
                                continue;
                            }
                        }
                    }
                }
            }

            // Collision with other intersection

            foreach (var src2 in Sources)
            {
                foreach (var ray2 in src2.Lasers)
                {
                    if (ray2.Terminator != LaserRayTerminator.LaserMultiTerm &&
                        ray2.Terminator != LaserRayTerminator.LaserFaultTerm &&
                        ray2.Terminator != LaserRayTerminator.LaserSelfTerm)
                    {
                        continue;
                    }

                    var hpu = ray2.End.ProjectOntoLine(ray1.Start, ray1.End);
                    if (hpu < minU)
                    {
                        FPoint hp = ray1.Start + (ray1.End - ray1.Start) * hpu;

                        if ((hp - ray2.End).LengthSquared() < RAY_WIDTH)
                        {
                            minU         = hpu;
                            minP1        = hp;
                            minRay2      = ray2;
                            minSrc2      = src2;
                            minTermOther = false;
                        }
                    }
                }
            }

            // Take best

            if (minRay2 != null)
            {
                if (!minTermOther)
                {
                    // we hit another ray intersection

                    ray1 = ray1.SetLaserIntersect(src1, minP1, minRay2, minSrc2, LaserRayTerminator.LaserMultiTerm);
                    if (ray1 != null)
                    {
                        foreach (var termray in minRay2.TerminatorRays)
                        {
                            if (termray.Item2 != minSrc2)
                            {
                                termray.Item1.TerminatorRays.Add(Tuple.Create(ray1, src1));
                            }
                        }
                        minRay2.TerminatorRays.Add(Tuple.Create(ray1, src1));
                    }

                    return(true);
                }
                else if (nofault)
                {
                    // nofault mode - just terminate this one

                    ray1 = ray1.SetLaserCollisionlessIntersect(src1, minP1, LaserRayTerminator.LaserFaultTerm);

                    return(true);
                }
                else if (src1 == minSrc2)
                {
                    // we hit ourself

                    ray1 = ray1.SetLaserCollisionlessIntersect(src1, minP1, LaserRayTerminator.LaserSelfTerm);
                    if (ray1 != null)
                    {
                        minRay2.SelfCollRays.Add(ray1);
                        foreach (var r in ray1.SelfCollRays)
                        {
                            _faultRays.Add(Tuple.Create(r, src1));
                        }
                        ray1.SelfCollRays.Clear();
                    }

                    return(true);
                }
                else
                {
                    // we hit someone else

                    ray1 = ray1.SetLaserIntersect(src1, minP1, minRay2, minSrc2, LaserRayTerminator.LaserMultiTerm);
                    if (ray1 != null)
                    {
                        foreach (var r in ray1.SelfCollRays)
                        {
                            _faultRays.Add(Tuple.Create(r, src1));
                        }
                        ray1.SelfCollRays.Clear();
                    }

                    CutRayAndUpdate(minSrc2, minRay2, minP2, src1, ray1);

                    return(true);
                }
            }


            return(false);
        }
예제 #5
0
        private bool TestCast(LaserRay ray)
        {
            bool newobstaclefound = false;
            bool correctendfound  = false;

            //     return -1:       ignore this fixture and continue
            //     return  0:       terminate the ray cast
            //     return fraction: clip the ray to this point
            //     return 1:        don't clip the ray and continue
            float rcCallback(Fixture f, Vector2 pos, Vector2 normal, float frac)
            {
                if (f.UserData is GlassBlock)
                {
                    return(-1);                                          // ignore;
                }
                if (_wrapMode == GameWrapMode.Death && f.UserData is MarkerCollisionBorder)
                {
                    return(-1);                                                                                        // ignore
                }
                if (ray.StartIgnoreObj == f.UserData)
                {
                    return(-1);                                                  // ignore
                }
                if (ray.EndIgnoreObj == f.UserData)
                {
                    var p = ConvertUnits.ToDisplayUnits(pos).ToFPoint();
                    if (p.EpsilonEquals(ray.End, 0.5f))
                    {
                        correctendfound = true;
                        return(frac);                        // limit;
                    }
                    else
                    {
                        correctendfound = false;
                        return(frac);                        // limit;
                    }
                }
                else
                {
                    if (correctendfound)
                    {
                        newobstaclefound = true;
                        return(0);                        // terminate
                    }
                    else
                    {
                        return(frac);                        // limit;
                    }
                }
            }

            var ss = ConvertUnits.ToSimUnits(ray.Start.ToVec2D());
            var ee = ConvertUnits.ToSimUnits(ray.End.ToVec2D());

            ee = ee + (ee - ss).WithLength(1f);

            _world.RayCast(rcCallback, ss, ee);

            if (!correctendfound)
            {
                return(true);
            }
            if (newobstaclefound)
            {
                return(true);
            }
            return(false);
        }
예제 #6
0
        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>"));
            }
        }