示例#1
0
 public Vertex3D SurfaceVelocity(Vertex3D surfP)
 {
     // linear velocity plus tangential velocity due to rotation
     return(Vel
            .Clone()
            .Add(Vertex3D.CrossProduct(AngularVelocity, surfP)));
 }
示例#2
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)
                    );
            }
        }
示例#3
0
        public Vertex3D SurfaceAcceleration(Vertex3D surfP, PlayerPhysics physics)
        {
            // if we had any external torque, we would have to add "(deriv. of ang.Vel.) x surfP" here
            var p2           = Vertex3D.CrossProduct(AngularVelocity, surfP);
            var acceleration = physics.Gravity
                               .Clone()
                               .MultiplyScalar(InvMass)                                        // linear acceleration
                               .Add(Vertex3D.CrossProduct(AngularVelocity, p2));               // centripetal acceleration

            return(acceleration);
        }
        private static List <HitObject> CheckJoint(HitTriangle ph3d1, HitTriangle ph3d2, IItem item)
        {
            var hitObjects = new List <HitObject>();

            if (ph3d1 != null)                 // may be null in case of degenerate triangles
            {
                var jointNormal = Vertex3D.CrossProduct(ph3d1.Normal, ph3d2.Normal);
                if (jointNormal.LengthSq() < 1e-8)                   // coplanar triangles need no joints
                {
                    return(hitObjects);
                }
            }
            // By convention of the calling function, points 1 [0] and 2 [1] of the second polygon will
            // be the common-edge points
            hitObjects.Add(GenerateJoint(ph3d2.Rgv[0], ph3d2.Rgv[1], item));
            return(hitObjects);
        }
示例#5
0
        public HitTriangle(Vertex3D[] rgv, ItemType itemType, IItem item) : base(itemType, item)
        {
            Rgv = rgv;

            /* NB: due to the swapping of the order of e0 and e1,
             * the vertices must be passed in counterclockwise order
             * (but rendering uses clockwise order!)
             */
            var e0 = Rgv[2].Clone().Sub(Rgv[0]);
            var e1 = Rgv[1].Clone().Sub(Rgv[0]);

            Normal = Vertex3D.CrossProduct(e0, e1);
            Normal.NormalizeSafe();

            Elasticity = 0.3f;
            SetFriction(0.3f);
            Scatter = 0;
        }
        public void OrthoNormalize()
        {
            var vX = new Vertex3D(Matrix[0][0], Matrix[1][0], Matrix[2][0]);
            var vY = new Vertex3D(Matrix[0][1], Matrix[1][1], Matrix[2][1]);
            var vZ = Vertex3D.CrossProduct(vX, vY);

            vX.Normalize();
            vZ.Normalize();
            var vYY = Vertex3D.CrossProduct(vZ, vX);

            Matrix[0][0] = vX.X;
            Matrix[0][1] = vYY.X;
            Matrix[0][2] = vZ.X;
            Matrix[1][0] = vX.Y;
            Matrix[1][1] = vYY.Y;
            Matrix[1][2] = vZ.Y;
            Matrix[2][0] = vX.Z;
            Matrix[2][1] = vYY.Z;
            Matrix[2][2] = vZ.Z;
        }
示例#7
0
        public static void ComputeNormals(Vertex3DNoTex2[] vertices, int numVertices, int[] indices, int numIndices)
        {
            for (var i = 0; i < numVertices; i++)
            {
                vertices[i].Nx = vertices[i].Ny = vertices[i].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 = Vertex3D.CrossProduct(e0, e1);
                normal.NormalizeSafe();

                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;

                vertices[indices[i]]     = a;
                vertices[indices[i + 1]] = b;
                vertices[indices[i + 2]] = c;
            }

            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;

                vertices[i] = v;
            }
        }
示例#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));
            }
        }
示例#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
        }
        private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, int acc = -1, bool createHitShape = false, float margin = 0f)
        {
            var mesh = new Mesh();
            // i dont understand the calculation of splineaccuracy here /cupiii
            var accuracy = (int)(10.0f * 1.2f);

            if (acc != -1)
            {             // hit shapes and UI display have the same, static, precision
                accuracy = acc;
            }

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

            var height = playfieldHeight + meshHeight;


            // one ring for each Splinevertex

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

            var up        = new Vertex3D(0f, 0f, 1f);
            var points    = new Vertex3D[numRings];        // middlepoints of rings
            var tangents  = new Vertex3D[numRings];        // pointing into the direction of the spline, even first and last
            var right     = new Vertex3D[numRings];        // pointing right, looking into tangent direction
            var down      = new Vertex3D[numRings];        // pointing down from tangent view
            var accLength = new float[numRings];           // accumulated length of the wire beginning at 0;

            // copy the data from the pline into the middle of the new variables
            for (int i = 0; i < sv.VertexCount; i++)
            {
                points[i] = new Vertex3D(sv.MiddlePoints[i].X, sv.MiddlePoints[i].Y, height);
                right[i]  = new Vertex3D(sv.RgvLocal[i].X - sv.MiddlePoints[i].X, sv.RgvLocal[i].Y - sv.MiddlePoints[i].Y, 0f);
                right[i].Normalize();
                tangents[i] = Vertex3D.CrossProduct(right[i], new Vertex3D(0f, 0f, 1f));
                tangents[i].Normalize();
            }

            // calculate downvectors
            for (int i = 0; i < numRings; i++)
            {
                down[i] = Vertex3D.CrossProduct(right[i], tangents[i]);
                down[i].Normalize();
            }

            // For UV calculation we need the whole length of the rubber
            accLength[0] = 0.0f;
            for (int i = 1; i < numRings; i++)
            {
                accLength[i] = accLength[i - 1] + (points[i] - points[i - 1]).Length();
            }
            // add the lenth from the last ring to the first ring
            var totalLength = accLength[numRings - 1] + (points[0] - points[numRings - 1]).Length();;

            var numVertices = (numRings + 1) * numSegments;
            var numIndices  = numRings * numSegments * 6;

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

            // precalculate the rings (positive X is left, positive Y is up) Starting at the bottom clockwise (X=0, Y=1)
            var ringsX = new float[numSegments];
            var ringsY = new float[numSegments];

            for (int i = 0; i < numSegments; i++)
            {
                ringsX[i] = -1.0f * (float)System.Math.Sin(System.Math.PI * 2 * i / numSegments) * _data.Thickness;
                ringsY[i] = -1.0f * (float)System.Math.Cos(System.Math.PI + System.Math.PI * 2 * i / numSegments) * _data.Thickness;
            }

            var verticesIndex = 0;
            var indicesIndex  = 0;

            // calculate Vertices first
            for (int currentRing = 0; currentRing < numRings; currentRing++)
            {
                // calculate one ring
                for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
                {
                    mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2
                    {
                        X = points[currentRing].X + right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment],
                        Y = points[currentRing].Y + right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment],
                        Z = points[currentRing].Z + right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment],
                        //normals seem to be somehow off, but are caculated again at the end of mesh creation.
                        Nx = right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment],
                        Ny = right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment],
                        Nz = right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment],
                        Tu = accLength[currentRing] / totalLength,
                        Tv = (float)currentSegment / ((float)numSegments - 1)
                    };
                }


                // could be integrated in above for loop, but better to read and will be optimized anyway by compiler
                if (currentRing > 0)
                {
                    for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
                    {
                        var csp1 = currentSegment + 1;
                        if (csp1 >= numSegments)
                        {
                            csp1 = 0;
                        }
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1;
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1;
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + csp1;
                    }
                }
            }

            // copy first ring into last ring
            for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
            {
                mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2
                {
                    X = points[0].X + right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment],
                    Y = points[0].Y + right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment],
                    Z = points[0].Z + right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment],
                    //normals seem to be somehow off, but are caculated again at the end of mesh creation.
                    Nx = right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment],
                    Ny = right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment],
                    Nz = right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment],
                    Tu = 1f,
                    Tv = (float)currentSegment / ((float)numSegments - 1)
                };
            }

            for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
            {
                var csp1 = currentSegment + 1;
                if (csp1 >= numSegments)
                {
                    csp1 = 0;
                }
                mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment;
                mesh.Indices[indicesIndex++] = (numRings) * numSegments + currentSegment;
                mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1;
                mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment;
                mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1;
                mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + csp1;
            }

            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++)
            {
                MathF.Max(maxX, mesh.Vertices[i].X);
                MathF.Max(maxY, mesh.Vertices[i].Y);
                MathF.Max(maxZ, mesh.Vertices[i].Z);
                MathF.Min(minX, mesh.Vertices[i].X);
                MathF.Min(minY, mesh.Vertices[i].X);
                MathF.Min(minZ, mesh.Vertices[i].X);
            }

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

            return(mesh);
        }
示例#11
0
        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   = Vertex3D.CrossProduct(tangent, up);                       //normal
                    biNormal = Vertex3D.CrossProduct(tangent, normal);
                }
                else
                {
                    normal   = Vertex3D.CrossProduct(prev, tangent);
                    biNormal = Vertex3D.CrossProduct(tangent, 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) * (_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);
        }
        public Mesh GetMesh(Table.Table table, int acc = -1, bool createHitShape = false)
        {
            var mesh     = new Mesh(_data.Name);
            var accuracy = (int)(10.0f * 1.2f);

            if (acc != -1)               // hit shapes and UI display have the same, static, precision
            {
                accuracy = acc;
            }

            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(), 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   = Vertex3D.CrossProduct(prevB, tangent);
                    biNormal = Vertex3D.CrossProduct(tangent, 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) * (_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;
                }
            }

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

            return(mesh);
        }
示例#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;
            }
        }
示例#14
0
        private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, float bendradius, int acc = -1, bool createHitShape = false, float margin = 0f)
        {
            var mesh = new Mesh();
            // i dont understand the calculation of splineaccuracy here /cupiii
            var accuracy = (int)(10.0f * 1.2f);

            if (acc != -1)               // hit shapes and UI display have the same, static, precision
            {
                accuracy = acc;
            }

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

            var height = playfieldHeight + meshHeight;

            var standheight = _data.Standheight;

            // dont lat the Collider be higher than the visible mesh, just shift the top of the MWG.
            if (createHitShape)
            {
                standheight = standheight - _data.Height + height;
            }


            // one ring for each Splinevertex, two for the stands, and "bendradius" tomes two for the bend (should be enough)
            // todo: could be better, if accuracy was taken into account
            var numRingsInBend = (int)(bendradius + 1);
            var numRings       = sv.VertexCount - 1 + numRingsInBend * 2 + 2;
            var numSegments    = accuracy;

            var up        = new Vertex3D(0f, 0f, 1f);
            var points    = new Vertex3D[numRings];        // middlepoints of rings
            var tangents  = new Vertex3D[numRings];        // pointing into the direction of the spline, even first and last
            var right     = new Vertex3D[numRings];        // pointing right, looking into tangent direction
            var down      = new Vertex3D[numRings];        // pointing down from tangent view
            var accLength = new float[numRings];           // accumulated length of the wire beginning at 0;

            // copy the data from the pline into the middle of the new variables
            for (int i = 0; i < sv.VertexCount - 1; i++)
            {
                points[i + numRingsInBend + 1] = new Vertex3D(sv.MiddlePoints[i].X, sv.MiddlePoints[i].Y, height);
                right[i + numRingsInBend + 1]  = new Vertex3D(sv.RgvLocal[i].X - sv.MiddlePoints[i].X, sv.RgvLocal[i].Y - sv.MiddlePoints[i].Y, 0f);
                right[i + numRingsInBend + 1].Normalize();
                tangents[i + numRingsInBend + 1] = Vertex3D.CrossProduct(right[i + numRingsInBend + 1], new Vertex3D(0f, 0f, 1f));
                tangents[i + numRingsInBend + 1].Normalize();
            }

            // first set up beginning of the stand
            points[0]   = points[numRingsInBend + 1] + tangents[numRingsInBend + 1] * bendradius * -1 + up * standheight * -1f;
            tangents[0] = new Vertex3D(0f, 0f, 1f);
            right[0]    = right[numRingsInBend + 1];
            // set up the first point of the bend
            points[1]   = points[numRingsInBend + 1] + tangents[numRingsInBend + 1] * bendradius * -1 + up * bendradius * -1f;
            tangents[1] = tangents[0];
            right[1]    = right[0];
            // now bend from point 1 to numRingsInBend+1(-1)
            var diffXY = points[numRingsInBend + 1] - points[1];

            diffXY.Z = 0;
            var diffZ = points[numRingsInBend + 1] - points[1];

            diffZ.X = 0;
            diffZ.Y = 0;
            for (int i = 1; i < (numRingsInBend + 1); i++)
            {
                points[numRingsInBend - i + 1] = points[1] + diffXY - (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * diffXY + (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) * diffZ;
                var tmp = tangents[numRingsInBend + 1];
                tmp.Normalize();
                tangents[numRingsInBend - i + 1] = tmp * (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) + (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * up;
                right[numRingsInBend - i + 1]    = right[0];
            }
            // set up last point
            points[numRings - 1]   = points[(numRings - 1) - numRingsInBend - 1] + tangents[numRings - 1 - numRingsInBend - 1] * bendradius + up * standheight * -1f;
            tangents[numRings - 1] = new Vertex3D(0f, 0f, -1f);
            right[numRings - 1]    = right[(numRings - 1) - numRingsInBend - 1];
            // and the point before
            points[numRings - 2]   = points[(numRings - 1) - numRingsInBend - 1] + tangents[numRings - 1 - numRingsInBend - 1] * bendradius + up * bendradius * -1f;
            tangents[numRings - 2] = tangents[numRings - 1];
            right[numRings - 2]    = right[numRings - 1];
            // now bend again
            diffXY   = points[numRings - 1 - numRingsInBend - 1] - points[numRings - 2];
            diffXY.Z = 0;
            diffZ    = points[numRings - 1 - numRingsInBend - 1] - points[numRings - 2];
            diffZ.X  = 0;
            diffZ.Y  = 0;
            for (int i = 1; i < (numRingsInBend + 1); i++)
            {
                points[numRings - 2 - numRingsInBend + i] = points[numRings - 2] + diffXY - (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * diffXY + (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) * diffZ;
                var tmp = tangents[numRings - 1 - numRingsInBend - 1];
                tmp.Normalize();
                tangents[numRings - 2 - numRingsInBend + i] = tmp * (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) + (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * up * -1;
                right[numRings - 2 - numRingsInBend + i]    = right[numRings - 1];
            }

            // calculate downvectors
            for (int i = 0; i < numRings; i++)
            {
                down[i] = Vertex3D.CrossProduct(right[i], tangents[i]);
                down[i].Normalize();
            }

            // For UV calculation we need the whole length of the wire
            accLength[0] = 0.0f;
            for (int i = 1; i < numRings; i++)
            {
                accLength[i] = accLength[i - 1] + (points[i] - points[i - 1]).Length();
            }
            var totalLength = accLength[numRings - 1];

            var numVertices = numRings * numSegments;
            var numIndices  = (numRings - 1) * numSegments * 6;

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

            // precalculate the rings (positive X is left, positive Y is up) Starting at the bottom clockwise (X=0, Y=1)
            var ringsX = new float[numSegments];
            var ringsY = new float[numSegments];

            for (int i = 0; i < numSegments; i++)
            {
                ringsX[i] = -1.0f * (float)System.Math.Sin(System.Math.PI * 2 * i / numSegments) * _data.Thickness;
                ringsY[i] = -1.0f * (float)System.Math.Cos(System.Math.PI + System.Math.PI * 2 * i / numSegments) * _data.Thickness;
            }

            var verticesIndex = 0;
            var indicesIndex  = 0;

            // calculate Vertices first
            for (int currentRing = 0; currentRing < numRings; currentRing++)
            {
                // calculate one ring
                for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
                {
                    mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2
                    {
                        X = points[currentRing].X + right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment],
                        Y = points[currentRing].Y + right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment],
                        Z = points[currentRing].Z + right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment],
                        //normals seem to be somehow off, but are caculated again at the end of mesh creation.
                        Nx = right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment],
                        Ny = right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment],
                        Nz = right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment],
                        Tu = accLength[currentRing] / totalLength,
                        Tv = (float)currentSegment / ((float)numSegments - 1)
                    };
                }


                // could be integrated in above for loop, but better to read and will be optimized anyway by compiler
                if (currentRing > 0)
                {
                    for (int currentSegment = 0; currentSegment < numSegments; currentSegment++)
                    {
                        var csp1 = currentSegment + 1;
                        if (csp1 >= numSegments)
                        {
                            csp1 = 0;
                        }
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1;
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment;
                        mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1;
                        mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + csp1;
                    }
                }
            }

            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++)
            {
                MathF.Max(maxX, mesh.Vertices[i].X);
                MathF.Max(maxY, mesh.Vertices[i].Y);
                MathF.Max(maxZ, mesh.Vertices[i].Z);
                MathF.Min(minX, mesh.Vertices[i].X);
                MathF.Min(minY, mesh.Vertices[i].X);
                MathF.Min(minZ, mesh.Vertices[i].X);
            }

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

            return(mesh);
        }