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); }
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); } }
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); }
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); }
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)); }
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)); }
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; } }
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); }
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)); }
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 }; } }
public new float Length() { return(MathF.Sqrt(X * X + Y * Y + Z * Z)); }
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); }
public bool IsZero() { return(MathF.Abs(X) < Constants.FloatMin && MathF.Abs(Y) < Constants.FloatMin && MathF.Abs(Z) < Constants.FloatMin); }
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); }
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); }
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); }
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); }
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); }