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; } } } }
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; } }
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.0f; for (var l = startRenderPoint; l < endRenderPoint; ++l) { var pv1 = vv[l % numPoints]; var pv2 = vv[(l + 1) % numPoints]; var dx = pv1.GetX() - pv2.GetX(); var dy = pv1.GetY() - pv2.GetY(); var length = MathF.Sqrt(dx * dx + dy * dy); totalLength += length; } var partialLength = 0.0f; for (var l = startRenderPoint; l < endRenderPoint; ++l) { var pv1 = vv[l % numPoints]; var pv2 = vv[(l + 1) % numPoints]; var dx = pv1.GetX() - pv2.GetX(); var dy = pv1.GetY() - pv2.GetY(); var length = MathF.Sqrt(dx * dx + dy * dy); if (totalLength == 0.0) { totalLength = 1.0f; } var texCoord = partialLength / totalLength; coords[l % numPoints] = texCoord * deltacoord + startTexCoord; partialLength += length; } } return(coords); }
public float Length() { return(MathF.Sqrt(X * X + Y * Y)); }
public new float Length() { return(MathF.Sqrt(X * X + Y * Y + Z * Z)); }
public float Magnitude() => MathF.Sqrt(this.Dot(this));
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); }
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); }
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); }