Beispiel #1
0
        public void ApplyFriction(Vertex3D hitNormal, float dTime, float frictionCoeff, PlayerPhysics physics)
        {
            // surface contact point relative to center of mass
            var surfP   = hitNormal.Clone().MultiplyScalar(-_data.Radius);
            var surfVel = SurfaceVelocity(surfP);

            // calc the tangential slip velocity
            var slip = surfVel.Clone().Sub(hitNormal.Clone().MultiplyScalar(surfVel.Dot(hitNormal)));

            var maxFriction = frictionCoeff * _data.Mass * -physics.Gravity.Dot(hitNormal);

            var      slipSpeed = slip.Length();
            Vertex3D slipDir;
            float    numer;

            //#ifdef C_BALL_SPIN_HACK
            var normVel = Vel.Dot(hitNormal);

            if (normVel <= 0.025 || slipSpeed < PhysicsConstants.Precision)
            {
                // check for <=0.025 originated from ball<->rubber collisions pushing the ball upwards, but this is still not enough, some could even use <=0.2
                // slip speed zero - static friction case

                var surfAcc = SurfaceAcceleration(surfP, physics);
                // calc the tangential slip acceleration
                var slipAcc = surfAcc.Clone()
                              .Sub(hitNormal.Clone().MultiplyScalar(surfAcc.Dot(hitNormal)));

                // neither slip velocity nor slip acceleration? nothing to do here
                if (slipAcc.LengthSq() < 1e-6)
                {
                    return;
                }

                slipDir = slipAcc.Clone().Normalize();
                numer   = -slipDir.Dot(surfAcc);
            }
            else
            {
                // nonzero slip speed - dynamic friction case
                slipDir = slip.Clone().DivideScalar(slipSpeed);
                numer   = -slipDir.Dot(surfVel);
            }

            var cp       = Vertex3D.CrossProduct(surfP, slipDir);
            var p1       = cp.Clone().DivideScalar(Inertia);
            var denom    = InvMass + slipDir.Dot(Vertex3D.CrossProduct(p1, surfP));
            var friction = Functions.Clamp(numer / denom, -maxFriction, maxFriction);

            if (!float.IsNaN(friction) && !float.IsInfinity(friction))
            {
                ApplySurfaceImpulse(
                    cp.Clone().MultiplyScalar(dTime * friction),
                    slipDir.Clone().MultiplyScalar(dTime * friction)
                    );
            }
        }
Beispiel #2
0
        public static void ComputeNormals(Vertex3DNoTex2[] vertices, int numVertices, int[] indices, int numIndices)
        {
            for (var i = 0; i < numVertices; i++)
            {
                var v = vertices[i];
                v.Nx = v.Ny = v.Nz = 0.0f;
            }

            for (var i = 0; i < numIndices; i += 3)
            {
                var a = vertices[indices[i]];
                var b = vertices[indices[i + 1]];
                var c = vertices[indices[i + 2]];

                var e0     = new Vertex3D(b.X - a.X, b.Y - a.Y, b.Z - a.Z);
                var e1     = new Vertex3D(c.X - a.X, c.Y - a.Y, c.Z - a.Z);
                var normal = e0.Clone().Cross(e1).Normalize();

                a.Nx += normal.X; a.Ny += normal.Y; a.Nz += normal.Z;
                b.Nx += normal.X; b.Ny += normal.Y; b.Nz += normal.Z;
                c.Nx += normal.X; c.Ny += normal.Y; c.Nz += normal.Z;
            }

            for (var i = 0; i < numVertices; i++)
            {
                var v    = vertices[i];
                var l    = v.Nx * v.Nx + v.Ny * v.Ny + v.Nz * v.Nz;
                var invL = l >= Constants.FloatMin ? 1.0f / MathF.Sqrt(l) : 0.0f;
                v.Nx *= invL;
                v.Ny *= invL;
                v.Nz *= invL;
            }
        }
Beispiel #3
0
        public void ApplySurfaceImpulse(Vertex3D rotI, Vertex3D impulse)
        {
            Vel.Add(impulse.Clone().MultiplyScalar(InvMass));
            AngularMomentum.Add(rotI);
            var angularMomentum = AngularMomentum.Clone();

            AngularVelocity.Set(angularMomentum.DivideScalar(Inertia));
        }
        public HitLine3D(Vertex3D v1, Vertex3D v2, ItemType itemType, IItem item) : base(new Vertex2D(), itemType, item)
        {
            var vLine = v2.Clone().Sub(v1);

            vLine.Normalize();

            // Axis of rotation to make 3D cylinder a cylinder along the z-axis
            var transAxis = new Vertex3D(vLine.Y, -vLine.X, 0);
            var l         = transAxis.LengthSq();

            if (l <= 1e-6)
            {
                // line already points in z axis?
                transAxis.Set(1, 0, 0);                 // choose arbitrary rotation vector
            }
            else
            {
                transAxis.DivideScalar(MathF.Sqrt(l));
            }

            // Angle to rotate the line into the z-axis
            var dot = vLine.Z;             //vLine.Dot(&vup);

            Matrix.RotationAroundAxis(transAxis, -MathF.Sqrt(1 - dot * dot), dot);

            var vTrans1  = v1.Clone().ApplyMatrix2D(Matrix);
            var vTrans2  = v2.Clone().ApplyMatrix2D(Matrix);
            var vTrans2Z = vTrans2.Z;

            // set up HitLineZ parameters
            Xy.Set(vTrans1.X, vTrans1.Y);
            ZLow  = MathF.Min(vTrans1.Z, vTrans2Z);
            ZHigh = MathF.Max(vTrans1.Z, vTrans2Z);

            V1 = v1;
            V2 = v2;

            HitBBox.Left   = MathF.Min(v1.X, v2.X);
            HitBBox.Right  = MathF.Max(v1.X, v2.X);
            HitBBox.Top    = MathF.Min(v1.Y, v2.Y);
            HitBBox.Bottom = MathF.Max(v1.Y, v2.Y);
            HitBBox.ZLow   = MathF.Min(v1.Z, v2.Z);
            HitBBox.ZHigh  = MathF.Max(v1.Z, v2.Z);
        }
Beispiel #5
0
        public override void Collide(CollisionEvent coll, PlayerPhysics physics)
        {
            coll.Ball.Hit.Collide3DWall(coll.HitNormal, Elasticity, ElasticityFalloff, Friction,
                                        Scatter);

            // distance from plane to ball surface
            var bnd = _normal.Dot(coll.Ball.State.Pos) - coll.Ball.Data.Radius - _d;

            if (bnd < 0)
            {
                // if ball has penetrated, push it out of the plane
                var v = _normal.Clone().MultiplyScalar(bnd);
                coll.Ball.State.Pos.Add(v);
            }
        }
Beispiel #6
0
        private void GetRelativeVelocity(Vertex3D normal, Ball.Ball ball, Vertex3D vRel, Vertex3D rB, Vertex3D rF)
        {
            rB.Set(normal.Clone().MultiplyScalar(-ball.Data.Radius));
            var hitPos = ball.State.Pos.Clone().Add(rB);

            var cF = new Vertex3D(
                _mover.HitCircleBase.Center.X,
                _mover.HitCircleBase.Center.Y,
                ball.State.Pos.Z                 // make sure collision happens in same z plane where ball is
                );

            rF.Set(hitPos.Clone().Sub(cF));             // displacement relative to flipper center
            var vB = ball.Hit.SurfaceVelocity(rB);
            var vF = _mover.SurfaceVelocity(rF);

            vRel.Set(vB.Clone().Sub(vF));
        }
        internal Mesh GetMesh(Table.Table table, int acc = -1, bool createHitShape = false)
        {
            var       mesh     = new Mesh(_data.Name);
            const int accuracy = (int)(10.0f * 1.2f);             // see also above

            var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float)(1.0 / 1.5)) : -1.0f;
            var sv             = new SplineVertex(_data.DragPoints, _data.Thickness, table.GetDetailLevel(), (int)splineAccuracy);

            var numRings    = sv.VertexCount - 1;
            var numSegments = accuracy;

            var numVertices = numRings * numSegments;
            var numIndices  = 6 * numVertices;            //m_numVertices*2+2;
            var height      = _data.HitHeight + table.TableHeight;

            mesh.Vertices = new Vertex3DNoTex2[numVertices];
            mesh.Indices  = new int[numIndices];

            var prevB = new Vertex3D();
            var invNr = 1.0f / numRings;
            var invNs = 1.0f / numSegments;
            var index = 0;

            for (var i = 0; i < numRings; i++)
            {
                var i2      = i == numRings - 1 ? 0 : i + 1;
                var tangent = new Vertex3D(sv.MiddlePoints[i2].X - sv.MiddlePoints[i].X,
                                           sv.MiddlePoints[i2].Y - sv.MiddlePoints[i].Y, 0.0f);

                Vertex3D biNormal;
                Vertex3D normal;
                if (i == 0)
                {
                    var up = new Vertex3D(sv.MiddlePoints[i2].X + sv.MiddlePoints[i].X, sv.MiddlePoints[i2].Y + sv.MiddlePoints[i].Y, height * 2.0f);
                    normal   = new Vertex3D(tangent.Y * up.Z, -tangent.X * up.Z, tangent.X * up.Y - tangent.Y * up.X);                   // = CrossProduct(tangent, up)
                    biNormal = new Vertex3D(tangent.Y * normal.Z, -tangent.X * normal.Z, tangent.X * normal.Y - tangent.Y * normal.X);   // = CrossProduct(tangent, normal)
                }
                else
                {
                    normal   = prevB.Clone().Cross(tangent);
                    biNormal = tangent.Clone().Cross(normal);
                }

                biNormal.Normalize();
                normal.Normalize();
                prevB = biNormal;
                var u = i * invNr;
                for (var j = 0; j < numSegments; j++)
                {
                    var v   = ((float)j + u) * invNs;
                    var tmp = Vertex3D.GetRotatedAxis(j * (360.0f * invNs), tangent, normal)
                              .MultiplyScalar(_data.Thickness * 0.5f);

                    mesh.Vertices[index] = new Vertex3DNoTex2 {
                        X = sv.MiddlePoints[i].X + tmp.X,
                        Y = sv.MiddlePoints[i].Y + tmp.Y
                    };
                    if (createHitShape && (j == 0 || j == 3))
                    {
                        //!! hack, adapt if changing detail level for hitshape
                        // for a hit shape create a more rectangle mesh and not a smooth one
                        tmp.Z *= 0.6f;
                    }

                    mesh.Vertices[index].Z = height + tmp.Z;
                    //texel
                    mesh.Vertices[index].Tu = u;
                    mesh.Vertices[index].Tv = v;
                    index++;
                }
            }

            // calculate faces
            for (var i = 0; i < numRings; i++)
            {
                for (var j = 0; j < numSegments; j++)
                {
                    var quad = new int[4];
                    quad[0] = i * numSegments + j;

                    if (j != numSegments - 1)
                    {
                        quad[1] = i * numSegments + j + 1;
                    }
                    else
                    {
                        quad[1] = i * numSegments;
                    }

                    if (i != numRings - 1)
                    {
                        quad[2] = (i + 1) * numSegments + j;
                        if (j != numSegments - 1)
                        {
                            quad[3] = (i + 1) * numSegments + j + 1;
                        }
                        else
                        {
                            quad[3] = (i + 1) * numSegments;
                        }
                    }
                    else
                    {
                        quad[2] = j;
                        if (j != numSegments - 1)
                        {
                            quad[3] = j + 1;
                        }
                        else
                        {
                            quad[3] = 0;
                        }
                    }

                    mesh.Indices[(i * numSegments + j) * 6]     = quad[0];
                    mesh.Indices[(i * numSegments + j) * 6 + 1] = quad[1];
                    mesh.Indices[(i * numSegments + j) * 6 + 2] = quad[2];
                    mesh.Indices[(i * numSegments + j) * 6 + 3] = quad[3];
                    mesh.Indices[(i * numSegments + j) * 6 + 4] = quad[2];
                    mesh.Indices[(i * numSegments + j) * 6 + 5] = quad[1];
                }
            }

            Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices);

            var maxX = Constants.FloatMin;
            var minX = Constants.FloatMax;
            var maxY = Constants.FloatMin;
            var minY = Constants.FloatMax;
            var maxZ = Constants.FloatMin;
            var minZ = Constants.FloatMax;

            for (var i = 0; i < numVertices; i++)
            {
                if (maxX < mesh.Vertices[i].X)
                {
                    maxX = mesh.Vertices[i].X;
                }

                if (minX > mesh.Vertices[i].X)
                {
                    minX = mesh.Vertices[i].X;
                }

                if (maxY < mesh.Vertices[i].Y)
                {
                    maxY = mesh.Vertices[i].Y;
                }

                if (minY > mesh.Vertices[i].Y)
                {
                    minY = mesh.Vertices[i].Y;
                }

                if (maxZ < mesh.Vertices[i].Z)
                {
                    maxZ = mesh.Vertices[i].Z;
                }

                if (minZ > mesh.Vertices[i].Z)
                {
                    minZ = mesh.Vertices[i].Z;
                }
            }

            MiddlePoint.X = (maxX + minX) * 0.5f;
            MiddlePoint.Y = (maxY + minY) * 0.5f;
            MiddlePoint.Z = (maxZ + minZ) * 0.5f;

            return(mesh);
        }
Beispiel #8
0
        public override void Contact(CollisionEvent coll, float dTime, PlayerPhysics physics)
        {
            var ball   = coll.Ball;
            var normal = coll.HitNormal;

            //#ifdef PhysicsConstants.EMBEDDED
            if (coll.HitDistance < -PhysicsConstants.Embedded)
            {
                // magic to avoid balls being pushed by each other through resting flippers!
                ball.Hit.Vel.Add(normal.Clone().MultiplyScalar(0.1f));
            }
            //#endif

            var vRel = new Vertex3D();
            var rB   = new Vertex3D();
            var rF   = new Vertex3D();

            GetRelativeVelocity(normal, ball, vRel, rB, rF);

            // this should be zero, but only up to +/- C_CONTACTVEL
            var normVel = vRel.Dot(normal);

            // If some collision has changed the ball's velocity, we may not have to do anything.
            if (normVel <= PhysicsConstants.ContactVel)
            {
                // compute accelerations of point on ball and flipper
                var aB   = ball.Hit.SurfaceAcceleration(rB, physics);
                var aF   = _mover.SurfaceAcceleration(rF);
                var aRel = aB.Clone().Sub(aF);

                // time derivative of the normal vector
                var normalDeriv = Vertex3D.CrossZ(_mover.AngleSpeed, normal);

                // relative acceleration in the normal direction
                var normAcc = aRel.Dot(normal) + 2.0f * normalDeriv.Dot(vRel);

                if (normAcc >= 0)
                {
                    return;                     // objects accelerating away from each other, nothing to do
                }

                // hypothetical accelerations arising from a unit contact force in normal direction
                var aBc             = normal.Clone().MultiplyScalar(ball.Hit.InvMass);
                var pv2             = normal.Clone().MultiplyScalar(-1);
                var cross           = Vertex3D.CrossProduct(rF, pv2);
                var pv1             = cross.Clone().DivideScalar(_mover.Inertia);
                var aFc             = Vertex3D.CrossProduct(pv1, rF);
                var contactForceAcc = normal.Dot(aBc.Clone().Sub(aFc));

                // find j >= 0 such that normAcc + j * contactForceAcc >= 0  (bodies should not accelerate towards each other)
                var j = -normAcc / contactForceAcc;

                // kill any existing normal velocity
                ball.Hit.Vel.Add(normal.Clone().MultiplyScalar(j * dTime * ball.Hit.InvMass - coll.HitOrgNormalVelocity));
                _mover.ApplyImpulse(cross.Clone().MultiplyScalar(j * dTime));

                // apply friction

                // first check for slippage
                var      slip        = vRel.Clone().Sub(normal.Clone().MultiplyScalar(normVel));     // calc the tangential slip velocity
                var      maxFriction = j * Friction;
                var      slipSpeed   = slip.Length();
                Vertex3D slipDir;
                Vertex3D crossF;
                float    numer;
                float    denomF;
                Vertex3D pv13;

                if (slipSpeed < PhysicsConstants.Precision)
                {
                    // slip speed zero - static friction case
                    var slipAcc = aRel.Clone().Sub(normal.Clone().MultiplyScalar(aRel.Dot(normal)));                     // calc the tangential slip acceleration

                    // neither slip velocity nor slip acceleration? nothing to do here
                    if (slipAcc.LengthSq() < 1e-6)
                    {
                        return;
                    }

                    slipDir = slipAcc.Normalize();
                    numer   = -slipDir.Dot(aRel);
                    crossF  = Vertex3D.CrossProduct(rF, slipDir);
                    pv13    = crossF.Clone().DivideScalar(-_mover.Inertia);
                    denomF  = slipDir.Dot(Vertex3D.CrossProduct(pv13, rF));
                }
                else
                {
                    // nonzero slip speed - dynamic friction case
                    slipDir = slip.Clone().DivideScalar(slipSpeed);

                    numer  = -slipDir.Dot(vRel);
                    crossF = Vertex3D.CrossProduct(rF, slipDir);
                    pv13   = crossF.Clone().DivideScalar(_mover.Inertia);
                    denomF = slipDir.Dot(Vertex3D.CrossProduct(pv13, rF));
                }

                var crossB   = Vertex3D.CrossProduct(rB, slipDir);
                var pv12     = crossB.Clone().DivideScalar(ball.Hit.Inertia);
                var denomB   = ball.Hit.InvMass + slipDir.Dot(Vertex3D.CrossProduct(pv12, rB));
                var friction = Functions.Clamp(numer / (denomB + denomF), -maxFriction, maxFriction);

                ball.Hit.ApplySurfaceImpulse(
                    crossB.Clone().MultiplyScalar(dTime * friction),
                    slipDir.Clone().MultiplyScalar(dTime * friction)
                    );
                _mover.ApplyImpulse(crossF.Clone().MultiplyScalar(-dTime * friction));
            }
        }
Beispiel #9
0
        public override void Collide(CollisionEvent coll, PlayerPhysics physics)
        {
            var ball   = coll.Ball;
            var normal = coll.HitNormal;
            var vRel   = new Vertex3D();
            var rB     = new Vertex3D();
            var rF     = new Vertex3D();

            GetRelativeVelocity(normal, ball, vRel, rB, rF);

            var bnv = normal.Dot(vRel);             // relative normal velocity

            if (bnv >= -PhysicsConstants.LowNormVel)
            {
                // nearly receding ... make sure of conditions
                if (bnv > PhysicsConstants.LowNormVel)
                {
                    // otherwise if clearly approaching .. process the collision
                    // is this velocity clearly receding (i.E must > a minimum)
                    return;
                }

                //#ifdef PhysicsConstants.EMBEDDED
                if (coll.HitDistance < -PhysicsConstants.Embedded)
                {
                    bnv = -PhysicsConstants.EmbedShot;                     // has ball become embedded???, give it a kick
                }
                else
                {
                    return;
                }

                //#endif
            }

            //#ifdef PhysicsConstants.DISP_GAIN
            // correct displacements, mostly from low velocity blindness, an alternative to true acceleration processing
            var hitDist = -PhysicsConstants.DispGain * coll.HitDistance;             // distance found in hit detection

            if (hitDist > 1.0e-4)
            {
                if (hitDist > PhysicsConstants.DispLimit)
                {
                    hitDist = PhysicsConstants.DispLimit;                     // crossing ramps, delta noise
                }

                // push along norm, back to free area; use the norm, but is not correct
                ball.State.Pos.Add(coll.HitNormal.Clone().MultiplyScalar(hitDist));
            }
            //#endif

            // angular response to impulse in normal direction
            var angResp = Vertex3D.CrossProduct(rF, normal);

            /*
             * Check if flipper is in contact with its stopper and the collision impulse
             * would push it beyond the stopper. In that case, don"t allow any transfer
             * of kinetic energy from ball to flipper. This avoids overly dead bounces
             * in that case.
             */
            var angImp = -angResp.Z;             // minus because impulse will apply in -normal direction
            var flipperResponseScaling = 1.0f;

            if (_mover.IsInContact && _mover.ContactTorque * angImp >= 0f)
            {
                // if impulse pushes against stopper, allow no loss of kinetic energy to flipper
                // (still allow flipper recoil, but a diminished amount)
                angResp.SetZero();
                flipperResponseScaling = 0.5f;
            }

            /*
             * Rubber has a coefficient of restitution which decreases with the impact velocity.
             * We use a heuristic model which decreases the COR according to a falloff parameter:
             * 0 = no falloff, 1 = half the COR at 1 m/s (18.53 speed units)
             */
            var epsilon = Functions.ElasticityWithFalloff(Elasticity, ElasticityFalloff, bnv);

            var pv1        = angResp.Clone().DivideScalar(_mover.Inertia);
            var impulse    = -(1.0f + epsilon) * bnv / (ball.Hit.InvMass + normal.Dot(Vertex3D.CrossProduct(pv1, rF)));
            var flipperImp = normal.Clone().MultiplyScalar(-(impulse * flipperResponseScaling));

            var rotI = Vertex3D.CrossProduct(rF, flipperImp);

            if (_mover.IsInContact)
            {
                if (rotI.Z * _mover.ContactTorque < 0)
                {
                    // pushing against the solenoid?

                    // Get a bound on the time the flipper needs to return to static conditions.
                    // If it"s too short, we treat the flipper as static during the whole collision.
                    var recoilTime = -rotI.Z / _mover.ContactTorque;                     // time flipper needs to eliminate this impulse, in 10ms

                    // Check ball normal velocity after collision. If the ball rebounded
                    // off the flipper, we need to make sure it does so with full
                    // reflection, i.E., treat the flipper as static, otherwise
                    // we get overly dead bounces.
                    var bnvAfter = bnv + impulse * ball.Hit.InvMass;

                    if (recoilTime <= 0.5 || bnvAfter > 0)
                    {
                        // treat flipper as static for this impact
                        impulse = -(1.0f + epsilon) * bnv * ball.Data.Mass;
                        flipperImp.SetZero();
                        rotI.SetZero();
                    }
                }
            }

            ball.Hit.Vel.Add(normal.Clone().MultiplyScalar(impulse * ball.Hit.InvMass));             // new velocity for ball after impact
            _mover.ApplyImpulse(rotI);

            // apply friction
            var tangent = vRel.Clone().Sub(normal.Clone().MultiplyScalar(vRel.Dot(normal)));             // calc the tangential velocity

            var tangentSpSq = tangent.LengthSq();

            if (tangentSpSq > 1e-6)
            {
                tangent.DivideScalar(MathF.Sqrt(tangentSpSq)); // normalize to get tangent direction
                var vt = vRel.Dot(tangent);                    // get speed in tangential direction

                // compute friction impulse
                var crossB = Vertex3D.CrossProduct(rB, tangent);
                var pv12   = crossB.Clone().DivideScalar(ball.Hit.Inertia);
                var kt     = ball.Hit.InvMass + tangent.Dot(Vertex3D.CrossProduct(pv12, rB));

                var crossF = Vertex3D.CrossProduct(rF, tangent);
                var pv13   = crossF.Clone().DivideScalar(_mover.Inertia);
                kt += tangent.Dot(Vertex3D.CrossProduct(pv13, rF));                 // flipper only has angular response

                // friction impulse can't be greater than coefficient of friction times collision impulse (Coulomb friction cone)
                var maxFriction = Friction * impulse;
                var jt          = Functions.Clamp(-vt / kt, -maxFriction, maxFriction);

                ball.Hit.ApplySurfaceImpulse(
                    crossB.Clone().MultiplyScalar(jt),
                    tangent.Clone().MultiplyScalar(jt)
                    );
                _mover.ApplyImpulse(crossF.Clone().MultiplyScalar(-jt));
            }

            if (bnv < -0.25 && physics.TimeMsec - _lastHitTime > 250)
            {
                // limit rate to 250 milliseconds per event
                var flipperHit =
                    coll.HitMomentBit ? -1.0 : -bnv;                     // move event processing to end of collision handler...
                if (flipperHit < 0)
                {
                    _events.FireGroupEvent(Event.HitEventsHit);                     // simple hit event
                }
                else
                {
                    // collision velocity (normal to face)
                    _events.FireVoidEventParam(Event.FlipperEventsCollide, flipperHit);
                }
            }

            _lastHitTime = physics.TimeMsec;             // keep resetting until idle for 250 milliseconds
        }
        public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics)
        {
            if (!IsEnabled)
            {
                return(-1.0f);
            }

            // speed in Normal-vector direction
            var bnv = _normal.Dot(ball.Hit.Vel);

            // return if clearly ball is receding from object
            if (ObjType != CollisionType.Trigger && bnv > PhysicsConstants.LowNormVel)
            {
                return(-1.0f);
            }

            // Point on the ball that will hit the polygon, if it hits at all
            var normRadius  = _normal.Clone().MultiplyScalar(ball.Data.Radius);
            var hitPos      = ball.State.Pos.Clone().Sub(normRadius); // nearest point on ball ... projected radius along norm
            var planeToBall = hitPos.Clone().Sub(_rgv[0]);
            var bnd         = _normal.Dot(planeToBall);               // distance from plane to ball

            var   bUnHit = bnv > PhysicsConstants.LowNormVel;
            var   inside = bnd <= 0;           // in ball inside object volume
            var   rigid  = ObjType != CollisionType.Trigger;
            float hitTime;

            if (rigid)
            {
                // rigid polygon
                if (bnd < -ball.Data.Radius)
                {
                    // (ball normal distance) excessive penetration of object skin ... no collision HACK //!! *2 necessary?
                    return(-1.0f);
                }

                if (bnd <= PhysicsConstants.PhysTouch)
                {
                    if (inside || MathF.Abs(bnv) > PhysicsConstants.ContactVel                     // fast velocity, return zero time
                        //zero time for rigid fast bodies
                        || bnd <= -PhysicsConstants.PhysTouch)
                    {
                        // slow moving but embedded
                        hitTime = 0;
                    }
                    else
                    {
                        hitTime = bnd * (float)(1.0 / (2.0 * PhysicsConstants.PhysTouch)) + 0.5f;                         // don't compete for fast zero time events
                    }
                }
                else if (MathF.Abs(bnv) > PhysicsConstants.LowNormVel)
                {
                    // not velocity low?
                    hitTime = bnd / -bnv;                     // rate ok for safe divide
                }
                else
                {
                    return(-1.0f);                    // wait for touching
                }
            }
            else
            {
                // non-rigid polygon
                if (bnv * bnd >= 0)
                {
                    // outside-receding || inside-approaching
                    if (!ball.Hit.IsRealBall() ||                  // temporary ball
                        MathF.Abs(bnd) >= ball.Data.Radius * 0.5 ||                     // not too close ... nor too far away
                        inside == ball.Hit.VpVolObjs.Contains(Obj))
                    {
                        // ...Ball outside and hit set or ball inside and no hit set
                        return(-1.0f);
                    }

                    hitTime = 0;
                    bUnHit  = !inside;                    // ball on outside is UnHit, otherwise it"s a Hit
                }
                else
                {
                    hitTime = bnd / -bnv;
                }
            }

            if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime)
            {
                // time is outside this frame ... no collision
                return(-1.0f);
            }

            var adv = ball.Hit.Vel.Clone().MultiplyScalar(hitTime);

            hitPos.Add(adv);             // advance hit point to contact

            // Do a point in poly test, using the xy plane, to see if the hit point is inside the polygon
            // this need to be changed to a point in polygon on 3D plane
            var x2         = _rgv[0].X;
            var y2         = _rgv[0].Y;
            var hx2        = hitPos.X >= x2;
            var hy2        = hitPos.Y <= y2;
            var crossCount = 0;             // count of lines which the hit point is to the left of

            for (var i = 0; i < _rgv.Length; i++)
            {
                var x1  = x2;
                var y1  = y2;
                var hx1 = hx2;
                var hy1 = hy2;

                var j = i < _rgv.Length - 1 ? i + 1 : 0;
                x2  = _rgv[j].X;
                y2  = _rgv[j].Y;
                hx2 = hitPos.X >= x2;
                hy2 = hitPos.Y <= y2;

                if (y1 == y2 || hy1 && hy2 || !hy1 && !hy2 || hx1 && hx2)
                {
                    // Hit point is on the right of the line
                    continue;
                }

                if (!hx1 && !hx2)
                {
                    crossCount ^= 1;
                    continue;
                }

                if (x2 == x1)
                {
                    if (!hx2)
                    {
                        crossCount ^= 1;
                    }
                    continue;
                }

                // Now the hard part - the hit point is in the line bounding box
                if (x2 - (y2 - hitPos.Y) * (x1 - x2) / (y1 - y2) > hitPos.X)
                {
                    crossCount ^= 1;
                }
            }

            if ((crossCount & 1) != 0)
            {
                coll.HitNormal.Set(_normal);

                if (!rigid)
                {
                    // non rigid body collision? return direction
                    coll.HitFlag = bUnHit;                     // UnHit signal is receding from outside target
                }

                coll.HitDistance = bnd;                 // 3dhit actual contact distance ...
                //coll.M_hitRigid = rigid;                                         // collision type

                return(hitTime);
            }

            return(-1.0f);
        }
        private Vertex3DNoTex2[] CreateWire(int numRings, int numSegments, IReadOnlyList <Vertex2D> midPoints, IReadOnlyList <float> initialHeights)
        {
            var vertices = new Vertex3DNoTex2[numRings * numSegments];
            var prev     = new Vertex3D();
            var index    = 0;

            for (var i = 0; i < numRings; i++)
            {
                var i2     = i == numRings - 1 ? i : i + 1;
                var height = initialHeights[i];

                var tangent = new Vertex3D(
                    midPoints[i2].X - midPoints[i].X,
                    midPoints[i2].Y - midPoints[i].Y,
                    initialHeights[i2] - initialHeights[i]
                    );
                if (i == numRings - 1)
                {
                    // for the last spline point use the previous tangent again, otherwise we won"t see the complete wire (it stops one control point too early)
                    tangent.X = midPoints[i].X - midPoints[i - 1].X;
                    tangent.Y = midPoints[i].Y - midPoints[i - 1].Y;
                }

                Vertex3D biNormal;
                Vertex3D normal;
                if (i == 0)
                {
                    var up = new Vertex3D(
                        midPoints[i2].X + midPoints[i].X,
                        midPoints[i2].Y + midPoints[i].Y,
                        initialHeights[i2] - height
                        );
                    normal   = tangent.Clone().Cross(up);                   //normal
                    biNormal = tangent.Clone().Cross(normal);
                }
                else
                {
                    normal   = prev.Clone().Cross(tangent);
                    biNormal = tangent.Clone().Cross(normal);
                }

                biNormal.Normalize();
                normal.Normalize();
                prev = biNormal;

                var invNumRings    = 1.0f / numRings;
                var invNumSegments = 1.0f / numSegments;
                var u = i * invNumRings;
                for (var j = 0; j < numSegments; j++, index++)
                {
                    var v   = (j + u) * invNumSegments;
                    var tmp = Vertex3D
                              .GetRotatedAxis(j * (360.0f * invNumSegments), tangent, normal)
                              .MultiplyScalar(_data.WireDiameter * 0.5f);

                    vertices[index] = new Vertex3DNoTex2 {
                        X  = midPoints[i].X + tmp.X,
                        Y  = midPoints[i].Y + tmp.Y,
                        Z  = height + tmp.Z,
                        Tu = u,
                        Tv = v
                    };

                    // normals
                    var n = new Vertex3D(
                        vertices[index].X - midPoints[i].X,
                        vertices[index].Y - midPoints[i].Y,
                        vertices[index].Z - height
                        );
                    var len = 1.0f / MathF.Sqrt(n.X * n.X + n.Y * n.Y + n.Z * n.Z);
                    vertices[index].Nx = n.X * len;
                    vertices[index].Ny = n.Y * len;
                    vertices[index].Nz = n.Z * len;
                }
            }

            return(vertices);
        }
Beispiel #12
0
        public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics)
        {
            var d  = _state.Pos.Clone().Sub(ball.State.Pos);                               // delta position
            var dv = Vel.Clone().Sub(ball.Hit.Vel);                                        // delta velocity

            var bcddSq = d.LengthSq();                                                     // square of ball center"s delta distance
            var bcdd   = MathF.Sqrt(bcddSq);                                               // length of delta

            if (bcdd < 1.0e-8)
            {
                // two balls center-over-center embedded
                d.Z = -1.0f;                                                                   // patch up
                ball.State.Pos.Z -= d.Z;                                                       // lift up

                bcdd            = 1.0f;                                                        // patch up
                bcddSq          = 1.0f;                                                        // patch up
                dv.Z            = 0.1f;                                                        // small speed difference
                ball.Hit.Vel.Z -= dv.Z;
            }

            var b   = dv.Dot(d);                                                           // inner product
            var bnv = b / bcdd;                                                            // normal speed of balls toward each other

            if (bnv > PhysicsConstants.LowNormVel)
            {
                // dot of delta velocity and delta displacement, positive if receding no collision
                return(-1.0f);
            }

            var totalRadius = ball.Data.Radius + _data.Radius;
            var bnd         = bcdd - totalRadius;                                          // distance between ball surfaces

            float hitTime;
            var   isContact = false;

            if (bnd <= PhysicsConstants.PhysTouch)
            {
                // in contact?
                if (bnd < ball.Data.Radius * -2.0f)
                {
                    return(-1.0f);                                                                 // embedded too deep?
                }

                if (MathF.Abs(bnv) > PhysicsConstants.ContactVel ||                            // >fast velocity, return zero time
                    bnd <= -PhysicsConstants.PhysTouch)
                {
                    // zero time for rigid fast bodies
                    hitTime = 0;                     // slow moving but embedded
                }
                else
                {
                    hitTime = bnd / -bnv;
                }

                if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel)
                {
                    isContact = true;
                }
            }
            else
            {
                var a = dv.LengthSq();                                                         // square of differential velocity
                if (a < 1.0e-8)
                {
                    // ball moving really slow, then wait for contact
                    return(-1.0f);
                }

                var sol = Functions.SolveQuadraticEq(a, 2.0f * b, bcddSq - totalRadius * totalRadius);
                if (sol == null)
                {
                    return(-1.0f);
                }

                var(time1, time2) = sol;
                hitTime           = time1 * time2 < 0
                                        ? MathF.Max(time1, time2)
                                        : MathF.Min(time1, time2);                                 // find smallest nonnegative solution
            }

            if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime)
            {
                // .. was some time previous || beyond the next physics tick
                return(-1.0f);
            }

            var hitPos = ball.State.Pos.Clone().Add(dv.MultiplyScalar(hitTime));             // new ball position

            // calc unit normal of collision
            var hitNormal = hitPos.Clone().Sub(_state.Pos);

            if (MathF.Abs(hitNormal.X) <= Constants.FloatMin && MathF.Abs(hitNormal.Y) <= Constants.FloatMin &&
                MathF.Abs(hitNormal.Z) <= Constants.FloatMin)
            {
                return(-1.0f);
            }

            coll.HitNormal.Set(hitNormal).Normalize();
            coll.HitDistance = bnd;                                                        // actual contact distance
            coll.IsContact   = isContact;
            if (isContact)
            {
                coll.HitOrgNormalVelocity = bnv;
            }

            return(hitTime);
        }
Beispiel #13
0
        public void Collide3DWall(Vertex3D hitNormal, float elasticity, float elasticityFalloff, float friction, float scatterAngle)
        {
            // speed normal to wall
            var dot = Vel.Dot(hitNormal);

            if (dot >= -PhysicsConstants.LowNormVel)
            {
                // nearly receding ... make sure of conditions
                if (dot > PhysicsConstants.LowNormVel)
                {
                    // otherwise if clearly approaching .. process the collision
                    return;                     // is this velocity clearly receding (i.E must > a minimum)
                }

                //#ifdef PhysicsConstants.Embedded
                if (Coll.HitDistance < -PhysicsConstants.Embedded)
                {
                    dot = -PhysicsConstants.EmbedShot;                     // has ball become embedded???, give it a kick
                }
                else
                {
                    return;
                }

                //#endif
            }

            //#ifdef PhysicsConstants.DispGain
            // correct displacements, mostly from low velocity, alternative to acceleration processing
            var hDist = -PhysicsConstants.DispGain * Coll.HitDistance;             // limit delta noise crossing ramps,

            if (hDist > 1.0e-4)
            {
                // when hit detection checked it what was the displacement
                if (hDist > PhysicsConstants.DispLimit)
                {
                    hDist = PhysicsConstants.DispLimit;                     // crossing ramps, delta noise
                }

                // push along norm, back to free area
                _state.Pos.Add(hitNormal.Clone().MultiplyScalar(hDist));
                // use the norm, but this is not correct, reverse time is correct
            }
            //#endif

            // magnitude of the impulse which is just sufficient to keep the ball from
            // penetrating the wall (needed for friction computations)
            var reactionImpulse = _data.Mass * MathF.Abs(dot);

            elasticity = Functions.ElasticityWithFalloff(elasticity, elasticityFalloff, dot);
            dot       *= -(1.0f + elasticity);
            Vel.Add(hitNormal.Clone().MultiplyScalar(dot));                                // apply collision impulse (along normal, so no torque)

            // compute friction impulse
            var surfP   = hitNormal.Clone().MultiplyScalar(-_data.Radius);                 // surface contact point relative to center of mass
            var surfVel = SurfaceVelocity(surfP);                                          // velocity at impact point
            var tangent = surfVel.Clone()                                                  // calc the tangential velocity
                          .Sub(hitNormal.Clone()
                               .MultiplyScalar(surfVel.Dot(hitNormal)));

            var tangentSpSq = tangent.LengthSq();

            if (tangentSpSq > 1e-6)
            {
                tangent.DivideScalar(MathF.Sqrt(tangentSpSq));                            // normalize to get tangent direction
                var vt = surfVel.Dot(tangent);                                            // get speed in tangential direction

                // compute friction impulse
                var cross        = Vertex3D.CrossProduct(surfP, tangent);
                var crossInertia = cross.Clone().DivideScalar(Inertia);
                var kt           = InvMass + tangent.Dot(Vertex3D.CrossProduct(crossInertia, surfP));

                // friction impulse can"t be greather than coefficient of friction times collision impulse (Coulomb friction cone)
                var maxFric = friction * reactionImpulse;
                var jt      = Functions.Clamp(-vt / kt, -maxFric, maxFric);

                if (!float.IsNaN(jt) && !float.IsInfinity(jt))
                {
                    ApplySurfaceImpulse(
                        cross.Clone().MultiplyScalar(jt),
                        tangent.Clone().MultiplyScalar(jt)
                        );
                }
            }

            if (scatterAngle < 0.0)
            {
                scatterAngle = HardScatter;
            }                                            // if < 0 use global value

            scatterAngle *= _tableData.GlobalDifficulty; // apply difficulty weighting

            if (dot > 1.0 && scatterAngle > 1.0e-5)
            {
                // no scatter at low velocity
                var scatter = MathF.Random() * 2 - 1;                                          // -1.0f..1.0f
                scatter *= (1.0f - scatter * scatter) * 2.59808f * scatterAngle;               // shape quadratic distribution and scale
                var radSin = MathF.Sin(scatter);                                               // Green's transform matrix... rotate angle delta
                var radCos = MathF.Cos(scatter);                                               // rotational transform from current position to position at time t
                var vxt    = Vel.X;
                var vyt    = Vel.Y;
                Vel.X = vxt * radCos - vyt * radSin;                                           // rotate to random scatter angle
                Vel.Y = vyt * radCos + vxt * radSin;
            }
        }