diff --git a/VisualPinball.Engine/VPT/MetalWireGuide/MetalWireGuideMeshGenerator.cs b/VisualPinball.Engine/VPT/MetalWireGuide/MetalWireGuideMeshGenerator.cs index eb63c3592..f7bca76bf 100644 --- a/VisualPinball.Engine/VPT/MetalWireGuide/MetalWireGuideMeshGenerator.cs +++ b/VisualPinball.Engine/VPT/MetalWireGuide/MetalWireGuideMeshGenerator.cs @@ -238,6 +238,26 @@ private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, f Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices); + var maxX = Constants.FloatMin; + var minX = Constants.FloatMax; + var maxY = Constants.FloatMin; + var minY = Constants.FloatMax; + var maxZ = Constants.FloatMin; + var minZ = Constants.FloatMax; + for (var i = 0; i < numVertices; i++) + { + MathF.Max(maxX, mesh.Vertices[i].X); + MathF.Max(maxY, mesh.Vertices[i].Y); + MathF.Max(maxZ, mesh.Vertices[i].Z); + MathF.Min(minX, mesh.Vertices[i].X); + MathF.Min(minY, mesh.Vertices[i].X); + MathF.Min(minZ, mesh.Vertices[i].X); + } + + _middlePoint.X = (maxX + minX) * 0.5f; + _middlePoint.Y = (maxY + minY) * 0.5f; + _middlePoint.Z = (maxZ + minZ) * 0.5f; + return mesh; } } diff --git a/VisualPinball.Engine/VPT/Rubber/RubberMeshGenerator.cs b/VisualPinball.Engine/VPT/Rubber/RubberMeshGenerator.cs index e96c28131..3571253f6 100644 --- a/VisualPinball.Engine/VPT/Rubber/RubberMeshGenerator.cs +++ b/VisualPinball.Engine/VPT/Rubber/RubberMeshGenerator.cs @@ -79,110 +79,141 @@ private Matrix3D GetRotationMatrix() private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, int acc = -1, bool createHitShape = false, float margin = 0f) { var mesh = new Mesh(); + // i dont understand the calculation of splineaccuracy here /cupiii var accuracy = (int)(10.0f * 1.2f); - if (acc != -1) { // hit shapes and UI display have the same, static, precision + if (acc != -1) + { // hit shapes and UI display have the same, static, precision accuracy = acc; } - var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float) (1.0 / 1.5)) : -1.0f; - var sv = new SplineVertex(_data.DragPoints, _data.Thickness, detailLevel, splineAccuracy, margin: margin); + var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float)(1.0 / 1.5)) : -1.0f; + SplineVertex sv = new SplineVertex(_data.DragPoints, (int)(_data.Thickness + 0.5), detailLevel, splineAccuracy, margin: margin, loop: true); - var numRings = sv.VertexCount - 1; - var numSegments = accuracy; - - var numVertices = numRings * numSegments; - var numIndices = 6 * numVertices; //m_numVertices*2+2; var height = playfieldHeight + meshHeight; - mesh.Vertices = new Vertex3DNoTex2[numVertices]; - mesh.Indices = new int[numIndices]; - var prevB = new Vertex3D(); - var invNr = 1.0f / numRings; - var invNs = 1.0f / numSegments; - var index = 0; - for (var i = 0; i < numRings; i++) { - var i2 = i == numRings - 1 ? 0 : i + 1; - var tangent = new Vertex3D(sv.MiddlePoints[i2].X - sv.MiddlePoints[i].X, - sv.MiddlePoints[i2].Y - sv.MiddlePoints[i].Y, 0.0f); - - Vertex3D biNormal; - Vertex3D normal; - if (i == 0) { - var up = new Vertex3D(sv.MiddlePoints[i2].X + sv.MiddlePoints[i].X, sv.MiddlePoints[i2].Y + sv.MiddlePoints[i].Y, height * 2.0f); - normal = new Vertex3D(tangent.Y * up.Z, -tangent.X * up.Z, tangent.X * up.Y - tangent.Y * up.X); // = CrossProduct(tangent, up) - biNormal = new Vertex3D(tangent.Y * normal.Z, -tangent.X * normal.Z, tangent.X * normal.Y - tangent.Y * normal.X); // = CrossProduct(tangent, normal) - - } else { - normal = Vertex3D.CrossProduct(prevB, tangent); - biNormal = Vertex3D.CrossProduct(tangent, normal); - } + // one ring for each Splinevertex + + var numRings = sv.VertexCount; + var numSegments = accuracy; - biNormal.Normalize(); - normal.Normalize(); - prevB = biNormal; - var u = i * invNr; - for (var j = 0; j < numSegments; j++) { - var v = ((float)j + u) * invNs; - var tmp = Vertex3D.GetRotatedAxis(j * (360.0f * invNs), tangent, normal) * ((_data.Thickness + margin) * 0.5f); - - mesh.Vertices[index] = new Vertex3DNoTex2 { - X = sv.MiddlePoints[i].X + tmp.X, - Y = sv.MiddlePoints[i].Y + tmp.Y - }; - if (createHitShape && (j == 0 || j == 3)) { - //!! hack, adapt if changing detail level for hitshape - // for a hit shape create a more rectangle mesh and not a smooth one - tmp.Z *= 0.6f; - } - mesh.Vertices[index].Z = height + tmp.Z; + var up = new Vertex3D(0f, 0f, 1f); + var points = new Vertex3D[numRings]; // middlepoints of rings + var tangents = new Vertex3D[numRings]; // pointing into the direction of the spline, even first and last + var right = new Vertex3D[numRings]; // pointing right, looking into tangent direction + var down = new Vertex3D[numRings]; // pointing down from tangent view + var accLength = new float[numRings]; // accumulated length of the wire beginning at 0; + + // copy the data from the pline into the middle of the new variables + for (int i = 0; i < sv.VertexCount; i++) + { + points[i] = new Vertex3D(sv.MiddlePoints[i].X, sv.MiddlePoints[i].Y, height); + right[i] = new Vertex3D(sv.RgvLocal[i].X - sv.MiddlePoints[i].X, sv.RgvLocal[i].Y - sv.MiddlePoints[i].Y, 0f); + right[i].Normalize(); + tangents[i] = Vertex3D.CrossProduct(right[i], new Vertex3D(0f, 0f, 1f)); + tangents[i].Normalize(); + } - //texel - mesh.Vertices[index].Tu = u; - mesh.Vertices[index].Tv = v; - index++; - } + // calculate downvectors + for (int i = 0; i < numRings; i++) + { + down[i] = Vertex3D.CrossProduct(right[i], tangents[i]); + down[i].Normalize(); } - // calculate faces - for (var i = 0; i < numRings; i++) { - for (var j = 0; j < numSegments; j++) { - var quad = new int[4]; - quad[0] = i * numSegments + j; + // For UV calculation we need the whole length of the rubber + accLength[0] = 0.0f; + for (int i = 1; i < numRings; i++) + accLength[i] = accLength[i - 1] + (points[i] - points[i - 1]).Length(); + // add the lenth from the last ring to the first ring + var totalLength = accLength[numRings - 1] + (points[0] - points[numRings - 1]).Length(); ; - if (j != numSegments - 1) { - quad[1] = i * numSegments + j + 1; + var numVertices = (numRings + 1) * numSegments; + var numIndices = numRings * numSegments * 6; + mesh.Vertices = new Vertex3DNoTex2[numVertices]; + mesh.Indices = new int[numIndices]; - } else { - quad[1] = i * numSegments; - } + // precalculate the rings (positive X is left, positive Y is up) Starting at the bottom clockwise (X=0, Y=1) + var ringsX = new float[numSegments]; + var ringsY = new float[numSegments]; + for (int i = 0; i < numSegments; i++) + { + ringsX[i] = -1.0f * (float)System.Math.Sin(System.Math.PI * 2 * i / numSegments) * _data.Thickness; + ringsY[i] = -1.0f * (float)System.Math.Cos(System.Math.PI + System.Math.PI * 2 * i / numSegments) * _data.Thickness; + } - if (i != numRings - 1) { - quad[2] = (i + 1) * numSegments + j; - if (j != numSegments - 1) { - quad[3] = (i + 1) * numSegments + j + 1; + var verticesIndex = 0; + var indicesIndex = 0; + + // calculate Vertices first + for (int currentRing = 0; currentRing < numRings; currentRing++) + { + // calculate one ring + for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) + { + mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2 + { + X = points[currentRing].X + right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], + Y = points[currentRing].Y + right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], + Z = points[currentRing].Z + right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], + //normals seem to be somehow off, but are caculated again at the end of mesh creation. + Nx = right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], + Ny = right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], + Nz = right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], + Tu = accLength[currentRing] / totalLength, + Tv = (float)currentSegment / ((float)numSegments - 1) - } else { - quad[3] = (i + 1) * numSegments; - } + }; + } - } else { - quad[2] = j; - if (j != numSegments - 1) { - quad[3] = j + 1; - } else { - quad[3] = 0; - } + // could be integrated in above for loop, but better to read and will be optimized anyway by compiler + if (currentRing > 0) + { + for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) + { + var csp1 = currentSegment + 1; + if (csp1 >= numSegments) + csp1 = 0; + mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = currentRing * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; + mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; + mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + csp1; } + } + } - mesh.Indices[(i * numSegments + j) * 6] = quad[0]; - mesh.Indices[(i * numSegments + j) * 6 + 1] = quad[1]; - mesh.Indices[(i * numSegments + j) * 6 + 2] = quad[2]; - mesh.Indices[(i * numSegments + j) * 6 + 3] = quad[3]; - mesh.Indices[(i * numSegments + j) * 6 + 4] = quad[2]; - mesh.Indices[(i * numSegments + j) * 6 + 5] = quad[1]; - } + // copy first ring into last ring + for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) + { + mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2 + { + X = points[0].X + right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment], + Y = points[0].Y + right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment], + Z = points[0].Z + right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment], + //normals seem to be somehow off, but are caculated again at the end of mesh creation. + Nx = right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment], + Ny = right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment], + Nz = right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment], + Tu = 1f, + Tv = (float)currentSegment / ((float)numSegments - 1) + + }; + } + + for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) + { + var csp1 = currentSegment + 1; + if (csp1 >= numSegments) + csp1 = 0; + mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = (numRings) * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1; + mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment; + mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1; + mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + csp1; } Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices); @@ -193,30 +224,14 @@ private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, i var minY = Constants.FloatMax; var maxZ = Constants.FloatMin; var minZ = Constants.FloatMax; - for (var i = 0; i < numVertices; i++) { - if (maxX < mesh.Vertices[i].X) { - maxX = mesh.Vertices[i].X; - } - - if (minX > mesh.Vertices[i].X) { - minX = mesh.Vertices[i].X; - } - - if (maxY < mesh.Vertices[i].Y) { - maxY = mesh.Vertices[i].Y; - } - - if (minY > mesh.Vertices[i].Y) { - minY = mesh.Vertices[i].Y; - } - - if (maxZ < mesh.Vertices[i].Z) { - maxZ = mesh.Vertices[i].Z; - } - - if (minZ > mesh.Vertices[i].Z) { - minZ = mesh.Vertices[i].Z; - } + for (var i = 0; i < numVertices; i++) + { + MathF.Max(maxX, mesh.Vertices[i].X); + MathF.Max(maxY, mesh.Vertices[i].Y); + MathF.Max(maxZ, mesh.Vertices[i].Z); + MathF.Min(minX, mesh.Vertices[i].X); + MathF.Min(minY, mesh.Vertices[i].X); + MathF.Min(minZ, mesh.Vertices[i].X); } _middlePoint.X = (maxX + minX) * 0.5f; @@ -224,6 +239,8 @@ private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, i _middlePoint.Z = (maxZ + minZ) * 0.5f; return mesh; + } + } }