Esempio n. 1
0
        public static void ClosestPointOnPolygon(RenderVertex3D[] rgv, Vertex2D pvin, bool fClosed, out Vertex2D pvOut, out int piSeg)
        {
            var count   = rgv.Length;
            var minDist = Constants.FloatMax;

            piSeg = -1;             // in case we are not next to the line
            pvOut = new Vertex2D();
            var loopCount = count;

            if (!fClosed)
            {
                --loopCount;                 // Don"t check segment running from the end point to the beginning point
            }

            // Go through line segment, calculate distance from point to the line
            // then pick the shortest distance
            for (var i = 0; i < loopCount; ++i)
            {
                var p2 = i < count - 1 ? i + 1 : 0;

                var rgvi = new RenderVertex3D();
                rgvi.Set(rgv[i].X, rgv[i].Y, rgv[i].Z);
                var rgvp2 = new RenderVertex3D();
                rgvp2.Set(rgv[p2].X, rgv[p2].Y, rgv[p2].Z);
                var a = rgvi.Y - rgvp2.Y;
                var b = rgvp2.X - rgvi.X;
                var c = -(a * rgvi.X + b * rgvi.Y);

                var dist = MathF.Abs(a * pvin.X + b * pvin.Y + c) / MathF.Sqrt(a * a + b * b);

                if (dist < minDist)
                {
                    // Assuming we got a segment that we are closet to, calculate the intersection
                    // of the line with the perpendicular line projected from the point,
                    // to find the closest point on the line
                    var d = -b;
                    var f = -(d * pvin.X + a * pvin.Y);

                    var det        = a * a - b * d;
                    var invDet     = det != 0.0f ? 1.0f / det : 0.0f;
                    var intersectX = (b * f - a * c) * invDet;
                    var intersectY = (c * d - a * f) * invDet;

                    // If the intersect point lies on the polygon segment
                    // (not out in space), then make this the closest known point
                    if (intersectX >= MathF.Min(rgvi.X, rgvp2.X) - 0.1 &&
                        intersectX <= MathF.Max(rgvi.X, rgvp2.X) + 0.1 &&
                        intersectY >= MathF.Min(rgvi.Y, rgvp2.Y) - 0.1 &&
                        intersectY <= MathF.Max(rgvi.Y, rgvp2.Y) + 0.1)
                    {
                        minDist = dist;
                        var seg = i;

                        pvOut.X = intersectX;
                        pvOut.Y = intersectY;
                        piSeg   = seg;
                    }
                }
            }
        }
        private Matrix3D GetVertexTransformationMatrix()
        {
            var rotX    = 0f;
            var offsetZ = 0f;
            var scale   = new Vertex3D(_data.ScaleX, _data.ScaleY, 1.0f);

            switch (_data.Shape)
            {
            case TriggerShape.TriggerWireB:
                rotX = -23.0f;
                break;

            case TriggerShape.TriggerWireC:
                rotX    = 140.0f;
                offsetZ = -19.0f;
                break;

            case TriggerShape.TriggerButton:
                offsetZ = 5.0f;
                scale.X = _data.Radius;
                scale.Y = _data.Radius;
                scale.Z = _data.Radius;
                break;

            case TriggerShape.TriggerStar:
                scale.X = _data.Radius;
                scale.Y = _data.Radius;
                scale.Z = _data.Radius;
                break;
            }

            // scale matrix
            var scaleMatrix = new Matrix3D();

            scaleMatrix.SetScaling(scale.X, scale.Y, scale.Z);

            // translation matrix
            var transMatrix = new Matrix3D();

            transMatrix.SetTranslation(0f, 0f, offsetZ);

            // rotation matrix
            var rotMatrix = new Matrix3D();

            rotMatrix.RotateXMatrix(MathF.DegToRad(rotX));

            var fullMatrix = scaleMatrix;

            fullMatrix.Multiply(rotMatrix);
            fullMatrix.Multiply(transMatrix);

            return(fullMatrix);
        }
Esempio n. 3
0
        Matrix3D IRenderable.TransformationMatrix(Table.Table table, Origin origin)
        {
            switch (origin)
            {
            case Origin.Original:
                var rotMatrix   = new Matrix3D().RotateZMatrix(MathF.DegToRad(Data.Orientation));
                var transMatrix = new Matrix3D().SetTranslation(Data.Center.X, Data.Center.Y, 0f);
                return(rotMatrix.Multiply(transMatrix));

            case Origin.Global:
                return(Matrix3D.Identity);

            default:
                throw new ArgumentOutOfRangeException(nameof(origin), origin, "Unknown origin " + origin);
            }
        }
Esempio n. 4
0
        private static bool AdvancePoint(IReadOnlyList <IRenderVertex> rgv, IReadOnlyList <int> poly, int a, int b, int c, int pre, int post)
        {
            var pv1 = rgv[a];
            var pv2 = rgv[b];
            var pv3 = rgv[c];

            var pvPre  = rgv[pre];
            var pvPost = rgv[post];

            if (GetDot(pv1, pv2, pv3) < 0 ||
                // Make sure angle created by new triangle line falls inside existing angles
                // If the existing angle is a concave angle, then new angle must be smaller,
                // because our triangle can"t have angles greater than 180
                GetDot(pvPre, pv1, pv2) > 0 &&
                GetDot(pvPre, pv1, pv3) < 0 ||             // convex angle, make sure new angle is smaller than it
                GetDot(pv2, pv3, pvPost) > 0 && GetDot(pv1, pv3, pvPost) < 0)
            {
                return(false);
            }

            // Now make sure the interior segment of this triangle (line ac) does not
            // intersect the polygon anywhere

            // sort our static line segment
            var minX = MathF.Min(pv1.GetX(), pv3.GetX());
            var maxX = MathF.Max(pv1.GetX(), pv3.GetX());
            var minY = MathF.Min(pv1.GetY(), pv3.GetY());
            var maxY = MathF.Max(pv1.GetY(), pv3.GetY());

            for (var i = 0; i < poly.Count; ++i)
            {
                var pvCross1 = rgv[poly[i]];
                var pvCross2 = rgv[poly[i < poly.Count - 1 ? i + 1 : 0]];

                if (pvCross1 != pv1 && pvCross2 != pv1 && pvCross1 != pv3 && pvCross2 != pv3 &&
                    (pvCross1.GetY() >= minY || pvCross2.GetY() >= minY) &&
                    (pvCross1.GetY() <= maxY || pvCross2.GetY() <= maxY) &&
                    (pvCross1.GetX() >= minX || pvCross2.GetX() >= minX) &&
                    (pvCross1.GetX() <= maxX || pvCross2.GetX() <= maxX) &&
                    LinesIntersect(pv1, pv3, pvCross1, pvCross2))
                {
                    return(false);
                }
            }

            return(true);
        }
Esempio n. 5
0
        private static bool LinesIntersect(IRenderVertex start1, IRenderVertex start2, IRenderVertex end1, IRenderVertex end2)
        {
            var x1 = start1.GetX();
            var y1 = start1.GetY();
            var x2 = start2.GetX();
            var y2 = start2.GetY();
            var x3 = end1.GetX();
            var y3 = end1.GetY();
            var x4 = end2.GetX();
            var y4 = end2.GetY();

            var d123 = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);

            if (d123 == 0.0)               // p3 lies on the same line as p1 and p2
            {
                return(x3 >= MathF.Min(x1, x2) && x3 <= MathF.Max(x2, x1));
            }

            var d124 = (x2 - x1) * (y4 - y1) - (x4 - x1) * (y2 - y1);

            if (d124 == 0.0)               // p4 lies on the same line as p1 and p2
            {
                return(x4 >= MathF.Min(x1, x2) && x4 <= MathF.Max(x2, x1));
            }

            if (d123 * d124 >= 0.0)
            {
                return(false);
            }

            var d341 = (x3 - x1) * (y4 - y1) - (x4 - x1) * (y3 - y1);

            if (d341 == 0.0)               // p1 lies on the same line as p3 and p4
            {
                return(x1 >= MathF.Min(x3, x4) && x1 <= MathF.Max(x3, x4));
            }

            var d342 = d123 - d124 + d341;

            if (d342 == 0.0)               // p1 lies on the same line as p3 and p4
            {
                return(x2 >= MathF.Min(x3, x4) && x2 <= MathF.Max(x3, x4));
            }

            return(d341 * d342 < 0.0);
        }
Esempio n. 6
0
        public static Light GetDefault(string name, float x, float y)
        {
            var lightData = new LightData(name, x, y)
            {
                DragPoints = new[] {
                    new DragPointData(x, y - 50f)
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x - 50f * MathF.Cos(MathF.PI / 4), y - 50f * MathF.Sin(MathF.PI / 4))
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x - 50f, y)
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x - 50f * MathF.Cos(MathF.PI / 4), y + 50f * MathF.Sin(MathF.PI / 4))
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x, y + 50f)
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x + 50f * MathF.Cos(MathF.PI / 4), y + 50f * MathF.Sin(MathF.PI / 4))
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x + 50f, y)
                    {
                        IsSmooth = true
                    },
                    new DragPointData(x + 50f * MathF.Cos(MathF.PI / 4), y - 50f * MathF.Sin(MathF.PI / 4))
                    {
                        IsSmooth = true
                    },
                }
            };

            return(new Light(lightData));
        }
Esempio n. 7
0
        protected override Tuple <Matrix3D, Matrix3D> GetTransformationMatrix(float height)
        {
            // scale matrix
            var scaleMatrix = new Matrix3D();

            scaleMatrix.SetScaling(Scale.X, Scale.Y, Scale.Z);

            // translation matrix
            var transMatrix = new Matrix3D();

            transMatrix.SetTranslation(Position.X, Position.Y, Position.Z + height);

            // translation + rotation matrix
            var rotTransMatrix = new Matrix3D();

            rotTransMatrix.SetTranslation(_data.RotAndTra[3], _data.RotAndTra[4], _data.RotAndTra[5]);

            var tempMatrix = new Matrix3D();

            tempMatrix.RotateZMatrix(MathF.DegToRad(_data.RotAndTra[2]));
            rotTransMatrix.Multiply(tempMatrix);
            tempMatrix.RotateYMatrix(MathF.DegToRad(_data.RotAndTra[1]));
            rotTransMatrix.Multiply(tempMatrix);
            tempMatrix.RotateXMatrix(MathF.DegToRad(_data.RotAndTra[0]));
            rotTransMatrix.Multiply(tempMatrix);

            tempMatrix.RotateZMatrix(MathF.DegToRad(_data.RotAndTra[8]));
            rotTransMatrix.Multiply(tempMatrix);
            tempMatrix.RotateYMatrix(MathF.DegToRad(_data.RotAndTra[7]));
            rotTransMatrix.Multiply(tempMatrix);
            tempMatrix.RotateXMatrix(MathF.DegToRad(_data.RotAndTra[6]));
            rotTransMatrix.Multiply(tempMatrix);

            var fullMatrix = scaleMatrix.Clone();

            fullMatrix.Multiply(rotTransMatrix);
            fullMatrix.Multiply(transMatrix);              // fullMatrix = Smatrix * RTmatrix * Tmatrix
            scaleMatrix.SetScaling(1.0f, 1.0f, 1f);
            fullMatrix.Multiply(scaleMatrix);

            return(new Tuple <Matrix3D, Matrix3D>(fullMatrix, null));
        }
Esempio n. 8
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;
            }
        }
Esempio n. 9
0
        internal void GenerateColliders(float collisionReductionFactor, List <ICollider> colliders)
        {
            var mesh = _meshGenerator.GetMesh();

            if (mesh == null)
            {
                Logger.Warn($"Primitive {_meshGenerator.name} did not return a mesh for collider generation.");
                return;
            }
            mesh = mesh.Transform(_meshGenerator.GetTransformationMatrix());

            var reducedVertices = math.max(
                (uint)MathF.Pow(mesh.Vertices.Length,
                                MathF.Clamp(1f - collisionReductionFactor, 0f, 1f) * 0.25f + 0.75f),
                420u                 //!! 420 = magic
                );

            if (reducedVertices < mesh.Vertices.Length)
            {
                mesh = ComputeReducedMesh(mesh, reducedVertices);
            }

            ColliderUtils.GenerateCollidersFromMesh(mesh, _api.GetColliderInfo(), colliders);
        }
Esempio n. 10
0
        public RenderVertex3D[] GetCentralCurve(float acc = -1.0f)
        {
            float accuracy;

            // as solid ramps are rendered into the static buffer, always use maximum precision
            if (acc != -1.0)
            {
                accuracy = acc;                 // used for hit shape calculation, always!
            }
            else
            {
                // var mat = table.GetMaterial(_data.Material);
                // if (mat == null || !mat.IsOpacityActive) {
                accuracy = 10.0f;

                // } else {
                //  accuracy = table.GetDetailLevel();
                // }
            }

            // min = 4 (highest accuracy/detail level), max = 4 * 10^(10/1.5) = ~18.000.000 (lowest accuracy/detail level)
            accuracy = 4.0f * MathF.Pow(10.0f, (10.0f - accuracy) * (1.0f / 1.5f));
            return(DragPoint.GetRgVertex <RenderVertex3D, CatmullCurve3DCatmullCurveFactory>(_data.DragPoints, false, accuracy));
        }
Esempio n. 11
0
        public Vertex3DNoTex2[] BuildRodVertices(int frame)
        {
            if (_lathePoints == 0)
            {
                CalculateArraySizes();
            }
            var vertices = new Vertex3DNoTex2[_latheVts];
            var yTip     = _beginY + _dyPerFrame * frame;

            var tu    = 0.51f;
            var stepU = 1.0f / _circlePoints;
            var i     = 0;

            for (var l = 0; l < _circlePoints; l++, tu += stepU)
            {
                // Go down the long axis, adding a vertex for each point
                // in the descriptor list at the current lathe angle.
                if (tu > 1.0f)
                {
                    tu -= 1.0f;
                }

                var angle = (float)(MathF.PI * 2.0) / _circlePoints * l;
                var sn    = MathF.Sin(angle);
                var cs    = MathF.Cos(angle);

                for (var m = 0; m < _lathePoints; m++)
                {
                    ref var c = ref _desc.c[m];

                    // get the current point's coordinates
                    var y  = c.y + yTip;
                    var r  = c.r;
                    var tv = c.tv;

                    // the last coordinate is always the bottom of the rod
                    if (m + 1 == _lathePoints)
                    {
                        // set the end point
                        y = _rodY;

                        // Figure the texture mapping for the rod position.  This is
                        // important because we draw the rod with varying length -
                        // the part that's pulled back beyond the 'rodY' point is
                        // hidden.  We want the texture to maintain the same apparent
                        // position and scale in each frame, so we need to figure the
                        // proportional point of the texture at our cut-off point on
                        // the object surface.
                        var ratio = frame * _invScale;
                        tv = vertices[m - 1].Tv + (tv - vertices[m - 1].Tv) * ratio;
                    }

                    vertices[i++] = new Vertex3DNoTex2 {
                        X  = r * (sn * _data.Width) + _data.Center.X,
                        Y  = y,
                        Z  = (r * (cs * _data.Width) + _data.Width + _zHeight) * _zScale,
                        Nx = c.nx * sn,
                        Ny = c.ny,
                        Nz = c.nx * cs,
                        Tu = tu,
                        Tv = tv
                    };
                }
            }
Esempio n. 12
0
 public new float Length()
 {
     return(MathF.Sqrt(X * X + Y * Y + Z * Z));
 }
Esempio n. 13
0
        public RampVertex GetRampVertex(float tableHeight, float accuracy, bool incWidth)
        {
            var result = new RampVertex();

            // vvertex are the 2D vertices forming the central curve of the ramp as seen from above
            var vertex = GetCentralCurve(accuracy);

            var numVertices = vertex.Length;

            result.VertexCount  = numVertices;
            result.PointHeights = new float[numVertices];
            result.Cross        = new bool[numVertices];
            result.PointRatios  = new float[numVertices];
            result.MiddlePoints = new Vertex2D[numVertices];
            result.RgvLocal     = new Vertex2D[_data.Type != RampType.RampTypeFlat ? (numVertices + 1) * 2 : numVertices * 2];

            // Compute an approximation to the length of the central curve
            // by adding up the lengths of the line segments.
            var totalLength  = 0f;
            var bottomHeight = _data.HeightBottom + tableHeight;
            var topHeight    = _data.HeightTop + tableHeight;

            for (var i = 0; i < numVertices - 1; i++)
            {
                var v1 = vertex[i];
                var v2 = vertex[i + 1];

                var dx     = v1.X - v2.X;
                var dy     = v1.Y - v2.Y;
                var length = MathF.Sqrt(dx * dx + dy * dy);

                totalLength += length;
            }

            var currentLength = 0f;

            for (var i = 0; i < numVertices; i++)
            {
                // clamp next and prev as ramps do not loop
                var prev   = vertex[i > 0 ? i - 1 : i];
                var next   = vertex[i < numVertices - 1 ? i + 1 : i];
                var middle = vertex[i];

                result.Cross[i] = middle.IsControlPoint;

                var normal = new Vertex2D();
                // Get normal at this point
                // Notice that these values equal the ones in the line
                // equation and could probably be substituted by them.
                var v1Normal = new Vertex2D(prev.Y - middle.Y, middle.X - prev.X);                 // vector vmiddle-vprev rotated RIGHT
                var v2Normal = new Vertex2D(middle.Y - next.Y, next.X - middle.X);                 // vector vnext-vmiddle rotated RIGHT

                // special handling for beginning and end of the ramp, as ramps do not loop
                if (i == numVertices - 1)
                {
                    v1Normal.Normalize();
                    normal = v1Normal;
                }
                else if (i == 0)
                {
                    v2Normal.Normalize();
                    normal = v2Normal;
                }
                else
                {
                    v1Normal.Normalize();
                    v2Normal.Normalize();

                    if (MathF.Abs(v1Normal.X - v2Normal.X) < 0.0001 && MathF.Abs(v1Normal.Y - v2Normal.Y) < 0.0001)
                    {
                        // Two parallel segments
                        normal = v1Normal;
                    }
                    else
                    {
                        // Find intersection of the two edges meeting this points, but
                        // shift those lines outwards along their normals

                        // First line
                        var a = prev.Y - middle.Y;
                        var b = middle.X - prev.X;

                        // Shift line along the normal
                        var c = -(a * (prev.X - v1Normal.X) + b * (prev.Y - v1Normal.Y));

                        // Second line
                        var d = next.Y - middle.Y;
                        var e = middle.X - next.X;

                        // Shift line along the normal
                        var f = -(d * (next.X - v2Normal.X) + e * (next.Y - v2Normal.Y));

                        var det    = a * e - b * d;
                        var invDet = det != 0.0 ? 1.0f / det : 0.0f;

                        var intersectX = (b * f - e * c) * invDet;
                        var intersectY = (c * d - a * f) * invDet;

                        normal.X = middle.X - intersectX;
                        normal.Y = middle.Y - intersectY;
                    }
                }

                // Update current length along the ramp.
                var dx     = prev.X - middle.X;
                var dy     = prev.Y - middle.Y;
                var length = MathF.Sqrt(dx * dx + dy * dy);

                currentLength += length;

                var percentage   = currentLength / totalLength;
                var currentWidth = percentage * (_data.WidthTop - _data.WidthBottom) + _data.WidthBottom;
                result.PointHeights[i] = middle.Z + percentage * (topHeight - bottomHeight) + bottomHeight;

                AssignHeightToControlPoint(new Vertex2D(vertex[i].X, vertex[i].Y), middle.Z + percentage * (topHeight - bottomHeight) + bottomHeight);
                result.PointRatios[i] = 1.0f - percentage;

                // only change the width if we want to create vertices for rendering or for the editor
                // the collision engine uses flat type ramps
                if (IsHabitrail() && _data.Type != RampType.RampType1Wire)
                {
                    currentWidth = _data.WireDistanceX;
                    if (incWidth)
                    {
                        currentWidth += 20.0f;
                    }
                }
                else if (_data.Type == RampType.RampType1Wire)
                {
                    currentWidth = _data.WireDiameter;
                }

                result.MiddlePoints[i] = new Vertex2D(middle.X, middle.Y) + normal;
                result.RgvLocal[i]     = new Vertex2D(middle.X, middle.Y) + currentWidth * 0.5f * normal;
                result.RgvLocal[numVertices * 2 - i - 1] = new Vertex2D(middle.X, middle.Y) - currentWidth * 0.5f * normal;
            }

            return(result);
        }
Esempio n. 14
0
 public bool IsZero()
 {
     return(MathF.Abs(X) < Constants.FloatMin && MathF.Abs(Y) < Constants.FloatMin &&
            MathF.Abs(Z) < Constants.FloatMin);
 }
Esempio n. 15
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);
        }
        private Mesh GenerateTopMesh(float tableWidth, float tableHeight, float zHeight)
        {
            var topMesh = new Mesh("Top");
            var vVertex = DragPoint.GetRgVertex <RenderVertex2D, CatmullCurve2DCatmullCurveFactory>(_data.DragPoints);

            var numVertices = vVertex.Length;
            var rgNormal    = new Vertex2D[numVertices];

            for (var i = 0; i < numVertices; i++)
            {
                var pv1 = vVertex[i];
                var pv2 = vVertex[i < numVertices - 1 ? i + 1 : 0];
                var dx  = pv1.X - pv2.X;
                var dy  = pv1.Y - pv2.Y;

                if (dx != 0.0f || dy != 0.0f)
                {
                    var invLen = 1.0f / MathF.Sqrt(dx * dx + dy * dy);
                    rgNormal[i] = new Vertex2D {
                        X = dy * invLen, Y = dx * invLen
                    };
                }
                else
                {
                    rgNormal[i] = new Vertex2D {
                        X = 0.0f, Y = 0.0f
                    };
                }
            }

            // draw top
            var vPoly = new List <int>(new int[numVertices]);

            for (var i = 0; i < numVertices; i++)
            {
                vPoly[i] = i;
            }

            topMesh.Indices = Mesh.PolygonToTriangles(vVertex, vPoly);
            var numPolys = topMesh.Indices.Length / 3;

            if (numPolys == 0)
            {
                // no polys to render leave vertex buffer undefined
                return(null);
            }

            var heightNotDropped = _data.HeightTop;
            var heightDropped    = _data.HeightBottom + 0.1;

            var invTableWidth  = 1.0f / tableWidth;
            var invTableHeight = 1.0f / tableHeight;

            Vertex3DNoTex2[][] vertsTop = { new Vertex3DNoTex2[numVertices], new Vertex3DNoTex2[numVertices], new Vertex3DNoTex2[numVertices] };
            for (var i = 0; i < numVertices; i++)
            {
                var pv0 = vVertex[i];

                vertsTop[0][i] = new Vertex3DNoTex2 {
                    X  = pv0.X,
                    Y  = pv0.Y,
                    Z  = heightNotDropped + zHeight,
                    Tu = pv0.X * invTableWidth,
                    Tv = pv0.Y * invTableHeight,
                    Nx = 0,
                    Ny = 0,
                    Nz = 1.0f
                };

                vertsTop[1][i] = new Vertex3DNoTex2 {
                    X  = pv0.X,
                    Y  = pv0.Y,
                    Z  = (float)heightDropped,
                    Tu = pv0.X * invTableWidth,
                    Tv = pv0.Y * invTableHeight,
                    Nx = 0,
                    Ny = 0,
                    Nz = 1.0f
                };

                vertsTop[2][i] = new Vertex3DNoTex2 {
                    X  = pv0.X,
                    Y  = pv0.Y,
                    Z  = _data.HeightBottom,
                    Tu = pv0.X * invTableWidth,
                    Tv = pv0.Y * invTableHeight,
                    Nx = 0,
                    Ny = 0,
                    Nz = -1.0f
                };
            }
            topMesh.Vertices = vertsTop[0];

            return(topMesh);
        }
Esempio n. 17
0
        public void UpdateData()
        {
            MaterialData.Name                    = Name;
            MaterialData.BaseColor               = BaseColor.ToInt(ColorFormat.Bgr);
            MaterialData.Glossiness              = Glossiness.ToInt(ColorFormat.Bgr);
            MaterialData.ClearCoat               = ClearCoat.ToInt(ColorFormat.Bgr);
            MaterialData.WrapLighting            = WrapLighting;
            MaterialData.Roughness               = Roughness;
            MaterialData.GlossyImageLerp         = (byte)BiffFloatAttribute.QuantizeUnsigned(8, MathF.Clamp(1f - GlossyImageLerp, 0f, 1f));
            MaterialData.Thickness               = (byte)BiffFloatAttribute.QuantizeUnsigned(8, MathF.Clamp(Thickness, 0f, 1f));
            MaterialData.Edge                    = Edge;
            MaterialData.Opacity                 = Opacity;
            MaterialData.IsMetal                 = IsMetal ? (byte)1 : (byte)0;
            MaterialData.OpacityActiveEdgeAlpha  = IsOpacityActive ? (byte)1 : (byte)0;
            MaterialData.OpacityActiveEdgeAlpha |= (byte)(BiffFloatAttribute.QuantizeUnsigned(7, MathF.Clamp(EdgeAlpha, 0f, 1f)) << 1);

            PhysicsMaterialData.Name              = Name;
            PhysicsMaterialData.Elasticity        = Elasticity;
            PhysicsMaterialData.ElasticityFallOff = ElasticityFalloff;
            PhysicsMaterialData.Friction          = Friction;
            PhysicsMaterialData.ScatterAngle      = ScatterAngle;
        }
        private Mesh GenerateSideMesh(float playfieldHeight)
        {
            var sideMesh = new Mesh("Side");

            var vVertex    = DragPoint.GetRgVertex <RenderVertex2D, CatmullCurve2DCatmullCurveFactory>(_data.DragPoints);
            var rgTexCoord = DragPoint.GetTextureCoords(_data.DragPoints, vVertex);

            var numVertices = vVertex.Length;
            var rgNormal    = new Vertex2D[numVertices];

            for (var i = 0; i < numVertices; i++)
            {
                var pv1 = vVertex[i];
                var pv2 = vVertex[i < numVertices - 1 ? i + 1 : 0];
                var dx  = pv1.X - pv2.X;
                var dy  = pv1.Y - pv2.Y;

                if (dx != 0.0f || dy != 0.0f)
                {
                    var invLen = 1.0f / MathF.Sqrt(dx * dx + dy * dy);
                    rgNormal[i] = new Vertex2D {
                        X = dy * invLen, Y = dx * invLen
                    };
                }
                else
                {
                    rgNormal[i] = new Vertex2D {
                        X = 0.0f, Y = 0.0f
                    };
                }
            }

            var bottom = _data.HeightBottom + playfieldHeight;
            var top    = _data.HeightTop + playfieldHeight;

            var offset = 0;

            // Render side
            sideMesh.Vertices = new Vertex3DNoTex2[numVertices * 4];
            for (var i = 0; i < numVertices; i++)
            {
                var pv1 = vVertex[i];
                var pv2 = vVertex[i < numVertices - 1 ? i + 1 : 0];

                var a = i == 0 ? numVertices - 1 : i - 1;
                var c = i < numVertices - 1 ? i + 1 : 0;

                var vNormal = new [] { new Vertex2D(), new Vertex2D() };
                if (pv1.Smooth)
                {
                    vNormal[0].X = (rgNormal[a].X + rgNormal[i].X) * 0.5f;
                    vNormal[0].Y = (rgNormal[a].Y + rgNormal[i].Y) * 0.5f;
                }
                else
                {
                    vNormal[0].X = rgNormal[i].X;
                    vNormal[0].Y = rgNormal[i].Y;
                }

                if (pv2.Smooth)
                {
                    vNormal[1].X = (rgNormal[i].X + rgNormal[c].X) * 0.5f;
                    vNormal[1].Y = (rgNormal[i].Y + rgNormal[c].Y) * 0.5f;
                }
                else
                {
                    vNormal[1].X = rgNormal[i].X;
                    vNormal[1].Y = rgNormal[i].Y;
                }

                vNormal[0].Normalize();
                vNormal[1].Normalize();

                sideMesh.Vertices[offset]     = new Vertex3DNoTex2();
                sideMesh.Vertices[offset + 1] = new Vertex3DNoTex2();
                sideMesh.Vertices[offset + 2] = new Vertex3DNoTex2();
                sideMesh.Vertices[offset + 3] = new Vertex3DNoTex2();

                sideMesh.Vertices[offset].X     = pv1.X;
                sideMesh.Vertices[offset].Y     = pv1.Y;
                sideMesh.Vertices[offset].Z     = bottom;
                sideMesh.Vertices[offset + 1].X = pv1.X;
                sideMesh.Vertices[offset + 1].Y = pv1.Y;
                sideMesh.Vertices[offset + 1].Z = top;
                sideMesh.Vertices[offset + 2].X = pv2.X;
                sideMesh.Vertices[offset + 2].Y = pv2.Y;
                sideMesh.Vertices[offset + 2].Z = top;
                sideMesh.Vertices[offset + 3].X = pv2.X;
                sideMesh.Vertices[offset + 3].Y = pv2.Y;
                sideMesh.Vertices[offset + 3].Z = bottom;

                sideMesh.Vertices[offset].Tu = rgTexCoord[i];
                sideMesh.Vertices[offset].Tv = 1.0f;

                sideMesh.Vertices[offset + 1].Tu = rgTexCoord[i];
                sideMesh.Vertices[offset + 1].Tv = 0f;

                sideMesh.Vertices[offset + 2].Tu = rgTexCoord[c];
                sideMesh.Vertices[offset + 2].Tv = 0f;

                sideMesh.Vertices[offset + 3].Tu = rgTexCoord[c];
                sideMesh.Vertices[offset + 3].Tv = 1.0f;

                sideMesh.Vertices[offset].Nx = vNormal[0].X;
                sideMesh.Vertices[offset].Ny = -vNormal[0].Y;
                sideMesh.Vertices[offset].Nz = 0f;

                sideMesh.Vertices[offset + 1].Nx = vNormal[0].X;
                sideMesh.Vertices[offset + 1].Ny = -vNormal[0].Y;
                sideMesh.Vertices[offset + 1].Nz = 0f;

                sideMesh.Vertices[offset + 2].Nx = vNormal[1].X;
                sideMesh.Vertices[offset + 2].Ny = -vNormal[1].Y;
                sideMesh.Vertices[offset + 2].Nz = 0f;

                sideMesh.Vertices[offset + 3].Nx = vNormal[1].X;
                sideMesh.Vertices[offset + 3].Ny = -vNormal[1].Y;
                sideMesh.Vertices[offset + 3].Nz = 0f;

                offset += 4;
            }

            // prepare index buffer for sides
            var offset2 = 0;

            sideMesh.Indices = new int[numVertices * 6];
            for (var i = 0; i < numVertices; i++)
            {
                sideMesh.Indices[i * 6]     = offset2;
                sideMesh.Indices[i * 6 + 1] = offset2 + 1;
                sideMesh.Indices[i * 6 + 2] = offset2 + 2;
                sideMesh.Indices[i * 6 + 3] = offset2;
                sideMesh.Indices[i * 6 + 4] = offset2 + 2;
                sideMesh.Indices[i * 6 + 5] = offset2 + 3;

                offset2 += 4;
            }

            return(sideMesh);
        }
Esempio n. 19
0
        public static float[] GetTextureCoords(DragPointData[] dragPoints, IRenderVertex[] vv)
        {
            var texPoints    = new List <int>();
            var renderPoints = new List <int>();
            var noCoords     = false;

            var numPoints    = vv.Length;
            var controlPoint = 0;

            var coords = new float[numPoints];

            for (var i = 0; i < numPoints; ++i)
            {
                var prv = vv[i];
                if (prv.IsControlPoint)
                {
                    if (!dragPoints[controlPoint].HasAutoTexture)
                    {
                        texPoints.Add(controlPoint);
                        renderPoints.Add(i);
                    }

                    ++controlPoint;
                }
            }

            if (texPoints.Count == 0)
            {
                // Special case - no texture coordinates were specified
                // Make them up starting at point 0
                texPoints.Add(0);
                renderPoints.Add(0);

                noCoords = true;
            }

            // Wrap the array around so we cover the last section
            texPoints.Add(texPoints[0] + dragPoints.Length);
            renderPoints.Add(renderPoints[0] + numPoints);

            for (var i = 0; i < texPoints.Count - 1; ++i)
            {
                var startRenderPoint = renderPoints[i] % numPoints;
                var endRenderPoint   = renderPoints[i < numPoints - 1 ? i + 1 : 0] % numPoints;

                float startTexCoord;
                float endTexCoord;
                if (noCoords)
                {
                    startTexCoord = 0.0f;
                    endTexCoord   = 1.0f;
                }
                else
                {
                    startTexCoord = dragPoints[texPoints[i] % dragPoints.Length].TextureCoord;
                    endTexCoord   = dragPoints[texPoints[i + 1] % dragPoints.Length].TextureCoord;
                }

                var deltacoord = endTexCoord - startTexCoord;

                if (endRenderPoint <= startRenderPoint)
                {
                    endRenderPoint += numPoints;
                }

                var totalLength = 0.0;
                for (var l = startRenderPoint; l < endRenderPoint; ++l)
                {
                    var pv1 = vv[l % numPoints];
                    var pv2 = vv[(l + 1) % numPoints];

                    var dx     = pv1.X - pv2.X;
                    var dy     = pv1.Y - pv2.Y;
                    var length = MathF.Sqrt(dx * dx + dy * dy);

                    totalLength += length;
                }

                var partialLength = 0.0;
                for (var l = startRenderPoint; l < endRenderPoint; ++l)
                {
                    var pv1 = vv[l % numPoints];
                    var pv2 = vv[(l + 1) % numPoints];

                    var dx     = pv1.X - pv2.X;
                    var dy     = pv1.Y - pv2.Y;
                    var length = MathF.Sqrt(dx * dx + dy * dy);
                    if (totalLength == 0.0)
                    {
                        totalLength = 1.0;
                    }

                    var texCoord = (float)(partialLength / totalLength);

                    coords[l % numPoints] = texCoord * deltacoord + startTexCoord;
                    partialLength        += length;
                }
            }

            return(coords);
        }
Esempio n. 20
0
        private Mesh CalculateBuiltinOriginal()
        {
            var mesh = new Mesh(_data.Name);

            // this recalculates the Original Vertices -> should be only called, when sides are altered.
            var outerRadius = -0.5f / MathF.Cos(MathF.PI / _data.Sides);
            var addAngle    = 2.0f * MathF.PI / _data.Sides;
            var offsAngle   = MathF.PI / _data.Sides;
            var minX        = Constants.FloatMax;
            var minY        = Constants.FloatMax;
            var maxX        = -Constants.FloatMax;
            var maxY        = -Constants.FloatMax;

            mesh.Vertices = new Vertex3DNoTex2[4 * _data.Sides + 2];

            // middle point top
            mesh.Vertices[0] = new Vertex3DNoTex2 {
                X = 0.0f, Y = 0.0f, Z = 0.5f
            };
            // middle point bottom
            mesh.Vertices[_data.Sides + 1] = new Vertex3DNoTex2 {
                X = 0.0f, Y = 0.0f, Z = -0.5f
            };

            for (var i = 0; i < _data.Sides; ++i)
            {
                var currentAngle = addAngle * i + offsAngle;

                // calculate Top
                var topVert = new Vertex3DNoTex2 {                 // top point at side
                    X = MathF.Sin(currentAngle) * outerRadius,
                    Y = MathF.Cos(currentAngle) * outerRadius,
                    Z = 0.5f
                };
                mesh.Vertices[i + 1] = topVert;

                // calculate bottom
                var bottomVert = new Vertex3DNoTex2 {                 // bottom point at side
                    X = topVert.X,
                    Y = topVert.Y,
                    Z = -0.5f
                };
                mesh.Vertices[i + 1 + _data.Sides + 1] = bottomVert;

                // calculate sides
                mesh.Vertices[_data.Sides * 2 + 2 + i] = topVert;                 // sideTopVert
                mesh.Vertices[_data.Sides * 3 + 2 + i] = bottomVert;              // sideBottomVert

                // calculate bounds for X and Y
                if (topVert.X < minX)
                {
                    minX = topVert.X;
                }

                if (topVert.X > maxX)
                {
                    maxX = topVert.X;
                }

                if (topVert.Y < minY)
                {
                    minY = topVert.Y;
                }

                if (topVert.Y > maxY)
                {
                    maxY = topVert.Y;
                }
            }

            // these have to be replaced for image mapping
            var middle = mesh.Vertices[0];              // middle point top

            middle.Tu = 0.25f;                          // /4
            middle.Tv = 0.25f;                          // /4
            middle    = mesh.Vertices[_data.Sides + 1]; // middle point bottom
            middle.Tu = 0.25f * 3.0f;                   // /4*3
            middle.Tv = 0.25f;                          // /4
            var invX = 0.5f / (maxX - minX);
            var invY = 0.5f / (maxY - minY);
            var invS = 1.0f / _data.Sides;

            for (var i = 0; i < _data.Sides; i++)
            {
                var topVert = mesh.Vertices[i + 1];                 // top point at side
                topVert.Tu = (topVert.X - minX) * invX;
                topVert.Tv = (topVert.Y - minY) * invY;

                var bottomVert = mesh.Vertices[i + 1 + _data.Sides + 1];                 // bottom point at side
                bottomVert.Tu = topVert.Tu + 0.5f;
                bottomVert.Tv = topVert.Tv;

                var sideTopVert    = mesh.Vertices[_data.Sides * 2 + 2 + i];
                var sideBottomVert = mesh.Vertices[_data.Sides * 3 + 2 + i];

                sideTopVert.Tu    = i * invS;
                sideTopVert.Tv    = 0.5f;
                sideBottomVert.Tu = sideTopVert.Tu;
                sideBottomVert.Tv = 1.0f;
            }

            // So how many indices are needed?
            // 3 per Triangle top - we have m_sides triangles -> 0, 1, 2, 0, 2, 3, 0, 3, 4, ...
            // 3 per Triangle bottom - we have m_sides triangles
            // 6 per Side at the side (two triangles form a rectangle) - we have m_sides sides
            // == 12 * m_sides
            // * 2 for both cullings (m_DrawTexturesInside == true)
            // == 24 * m_sides
            // this will also be the initial sorting, when depths, Vertices and Indices are recreated, because calculateRealTimeOriginal is called.

            // 2 restore indices
            //   check if anti culling is enabled:
            if (_data.DrawTexturesInside)
            {
                mesh.Indices = new int[_data.Sides * 24];
                // draw yes everything twice
                // restore indices
                for (var i = 0; i < _data.Sides; i++)
                {
                    var tmp = i == _data.Sides - 1 ? 1 : i + 2;                     // wrapping around
                    // top
                    mesh.Indices[i * 6]     = 0;
                    mesh.Indices[i * 6 + 1] = i + 1;
                    mesh.Indices[i * 6 + 2] = tmp;
                    mesh.Indices[i * 6 + 3] = 0;
                    mesh.Indices[i * 6 + 4] = tmp;
                    mesh.Indices[i * 6 + 5] = i + 1;

                    var tmp2 = tmp + 1;

                    // bottom
                    mesh.Indices[6 * (i + _data.Sides)]     = _data.Sides + 1;
                    mesh.Indices[6 * (i + _data.Sides) + 1] = _data.Sides + tmp2;
                    mesh.Indices[6 * (i + _data.Sides) + 2] = _data.Sides + 2 + i;
                    mesh.Indices[6 * (i + _data.Sides) + 3] = _data.Sides + 1;
                    mesh.Indices[6 * (i + _data.Sides) + 4] = _data.Sides + 2 + i;
                    mesh.Indices[6 * (i + _data.Sides) + 5] = _data.Sides + tmp2;

                    // sides
                    mesh.Indices[12 * (i + _data.Sides)]      = _data.Sides * 2 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 1]  = _data.Sides * 2 + 2 + i;
                    mesh.Indices[12 * (i + _data.Sides) + 2]  = _data.Sides * 3 + 2 + i;
                    mesh.Indices[12 * (i + _data.Sides) + 3]  = _data.Sides * 2 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 4]  = _data.Sides * 3 + 2 + i;
                    mesh.Indices[12 * (i + _data.Sides) + 5]  = _data.Sides * 3 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 6]  = _data.Sides * 2 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 7]  = _data.Sides * 3 + 2 + i;
                    mesh.Indices[12 * (i + _data.Sides) + 8]  = _data.Sides * 2 + 2 + i;
                    mesh.Indices[12 * (i + _data.Sides) + 9]  = _data.Sides * 2 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 10] = _data.Sides * 3 + tmp2;
                    mesh.Indices[12 * (i + _data.Sides) + 11] = _data.Sides * 3 + 2 + i;
                }
            }
            else
            {
                // only no out-facing polygons
                // restore indices
                mesh.Indices = new int[_data.Sides * 12];
                for (var i = 0; i < _data.Sides; i++)
                {
                    var tmp = i == _data.Sides - 1 ? 1 : i + 2;                     // wrapping around
                    // top
                    mesh.Indices[i * 3]     = 0;
                    mesh.Indices[i * 3 + 2] = i + 1;
                    mesh.Indices[i * 3 + 1] = tmp;

                    //SetNormal(mesh.Vertices[0], &mesh.Indices[i+3], 3); // see below

                    var tmp2 = tmp + 1;
                    // bottom
                    mesh.Indices[3 * (i + _data.Sides)]     = _data.Sides + 1;
                    mesh.Indices[3 * (i + _data.Sides) + 1] = _data.Sides + 2 + i;
                    mesh.Indices[3 * (i + _data.Sides) + 2] = _data.Sides + tmp2;

                    //SetNormal(mesh.Vertices[0], &mesh.Indices[3*(i+_data.Sides)], 3); // see below

                    // sides
                    mesh.Indices[6 * (i + _data.Sides)]     = _data.Sides * 2 + tmp2;
                    mesh.Indices[6 * (i + _data.Sides) + 1] = _data.Sides * 3 + 2 + i;
                    mesh.Indices[6 * (i + _data.Sides) + 2] = _data.Sides * 2 + 2 + i;
                    mesh.Indices[6 * (i + _data.Sides) + 3] = _data.Sides * 2 + tmp2;
                    mesh.Indices[6 * (i + _data.Sides) + 4] = _data.Sides * 3 + tmp2;
                    mesh.Indices[6 * (i + _data.Sides) + 5] = _data.Sides * 3 + 2 + i;
                }
            }

            //SetNormal(mesh.Vertices[0], &mesh.Indices[0], m_mesh.NumIndices()); // SetNormal only works for plane polygons
            Mesh.ComputeNormals(mesh.Vertices, mesh.Vertices.Length, mesh.Indices, mesh.Indices.Length);

            return(mesh);
        }
Esempio n. 21
0
 public float Magnitude() => MathF.Sqrt(this.Dot(this));
        private Dictionary <string, Mesh> GenerateMeshes(float height, float margin = 0f)
        {
            var meshes     = new Dictionary <string, Mesh>();
            var fullMatrix = new Matrix3D();

            fullMatrix.RotateZMatrix(MathF.DegToRad(180.0f));

            var baseRadius = _data.BaseRadius - _data.RubberThickness + margin;
            var endRadius  = _data.EndRadius - _data.RubberThickness + margin;

            // calc angle needed to fix P0 location
            var sinAngle = (baseRadius - endRadius) / _data.FlipperRadius;

            if (sinAngle > 1.0)
            {
                sinAngle = 1.0f;
            }
            if (sinAngle < -1.0)
            {
                sinAngle = -1.0f;
            }
            var fixAngle      = MathF.Asin(sinAngle);
            var fixAngleScale = fixAngle / (float)(System.Math.PI * 0.5);             // scale (in relation to 90 deg.)

            // fixAngleScale = 0.0; // note: if you force fixAngleScale = 0.0 then all will look as old version

            // lambda used to apply fix
            void ApplyFix(ref Vertex3DNoTex2 vert, Vertex2D center, float midAngle, float radius, Vertex2D newCenter)
            {
                var vAngle = MathF.Atan2(vert.Y - center.Y, vert.X - center.X);
                var nAngle = MathF.Atan2(vert.Ny, vert.Nx);

                // we want have angles with same sign as midAngle, fix it:
                if (midAngle < 0.0)
                {
                    if (vAngle > 0.0)
                    {
                        vAngle -= (float)(System.Math.PI * 2.0);
                    }
                    if (nAngle > 0.0)
                    {
                        nAngle -= (float)(System.Math.PI * 2.0);
                    }
                }
                else
                {
                    if (vAngle < 0.0)
                    {
                        vAngle += (float)(System.Math.PI * 2.0);
                    }
                    if (nAngle < 0.0)
                    {
                        nAngle += (float)(System.Math.PI * 2.0);
                    }
                }

                nAngle -= (vAngle - midAngle) * fixAngleScale * MathF.Sign(midAngle);
                vAngle -= (vAngle - midAngle) * fixAngleScale * MathF.Sign(midAngle);
                float nL = new Vertex2D(vert.Nx, vert.Ny).Length();

                vert.X  = MathF.Cos(vAngle) * radius + newCenter.X;
                vert.Y  = MathF.Sin(vAngle) * radius + newCenter.Y;
                vert.Nx = MathF.Cos(nAngle) * nL;
                vert.Ny = MathF.Sin(nAngle) * nL;
            }

            // base and tip
            var baseMesh = new Mesh(Base, Vertices, Indices).Clone();

            for (var t = 0; t < 13; t++)
            {
                for (var i = 0; i < baseMesh.Vertices.Length; i++)
                {
                    var v = baseMesh.Vertices[i];
                    if (v.X == VertsBaseBottom[t].X && v.Y == VertsBaseBottom[t].Y && v.Z == VertsBaseBottom[t].Z)
                    {
                        ApplyFix(ref baseMesh.Vertices[i], new Vertex2D(VertsBaseBottom[6].X, VertsBaseBottom[0].Y),
                                 (float)-(System.Math.PI * 0.5), baseRadius, new Vertex2D(0, 0));
                    }

                    if (v.X == VertsTipBottom[t].X && v.Y == VertsTipBottom[t].Y && v.Z == VertsTipBottom[t].Z)
                    {
                        ApplyFix(ref baseMesh.Vertices[i], new Vertex2D(VertsTipBottom[6].X, VertsTipBottom[0].Y),
                                 (float)(System.Math.PI * 0.5), endRadius, new Vertex2D(0, _data.FlipperRadius));
                    }

                    if (v.X == VertsBaseTop[t].X && v.Y == VertsBaseTop[t].Y && v.Z == VertsBaseTop[t].Z)
                    {
                        ApplyFix(ref baseMesh.Vertices[i], new Vertex2D(VertsBaseBottom[6].X, VertsBaseBottom[0].Y),
                                 (float)(-System.Math.PI * 0.5), baseRadius, new Vertex2D(0, 0));
                    }

                    if (v.X == VertsTipTop[t].X && v.Y == VertsTipTop[t].Y && v.Z == VertsTipTop[t].Z)
                    {
                        ApplyFix(ref baseMesh.Vertices[i], new Vertex2D(VertsTipBottom[6].X, VertsTipBottom[0].Y),
                                 (float)(System.Math.PI * 0.5), endRadius, new Vertex2D(0, _data.FlipperRadius));
                    }
                }
            }

            baseMesh.Transform(fullMatrix, null, z => z * _data.Height + height);
            meshes[Base] = baseMesh;

            // rubber
            var rubberMesh = new Mesh(Rubber, Vertices, Indices).Clone();

            for (var t = 0; t < 13; t++)
            {
                for (var i = 0; i < rubberMesh.Vertices.Length; i++)
                {
                    var v = rubberMesh.Vertices[i];
                    if (v.X == VertsBaseBottom[t].X && v.Y == VertsBaseBottom[t].Y && v.Z == VertsBaseBottom[t].Z)
                    {
                        ApplyFix(ref rubberMesh.Vertices[i], new Vertex2D(VertsBaseBottom[6].X, VertsBaseBottom[0].Y),
                                 (float)(-System.Math.PI * 0.5), baseRadius + _data.RubberThickness + margin,
                                 new Vertex2D(0, 0));
                    }

                    if (v.X == VertsTipBottom[t].X && v.Y == VertsTipBottom[t].Y && v.Z == VertsTipBottom[t].Z)
                    {
                        ApplyFix(ref rubberMesh.Vertices[i], new Vertex2D(VertsTipBottom[6].X, VertsTipBottom[0].Y),
                                 (float)(System.Math.PI * 0.5), endRadius + _data.RubberThickness + margin,
                                 new Vertex2D(0, _data.FlipperRadius));
                    }

                    if (v.X == VertsBaseTop[t].X && v.Y == VertsBaseTop[t].Y && v.Z == VertsBaseTop[t].Z)
                    {
                        ApplyFix(ref rubberMesh.Vertices[i], new Vertex2D(VertsBaseBottom[6].X, VertsBaseBottom[0].Y),
                                 (float)(-System.Math.PI * 0.5), baseRadius + _data.RubberThickness + margin,
                                 new Vertex2D(0, 0));
                    }

                    if (v.X == VertsTipTop[t].X && v.Y == VertsTipTop[t].Y && v.Z == VertsTipTop[t].Z)
                    {
                        ApplyFix(ref rubberMesh.Vertices[i], new Vertex2D(VertsTipBottom[6].X, VertsTipBottom[0].Y),
                                 (float)(System.Math.PI * 0.5), endRadius + _data.RubberThickness + margin,
                                 new Vertex2D(0, _data.FlipperRadius));
                    }
                }
            }
            rubberMesh.Transform(fullMatrix, null, z => z * _data.RubberWidth + (height + _data.RubberHeight + margin * 10f));
            meshes[Rubber] = rubberMesh;

            return(meshes);
        }