/// <summary> /// Creates an array of Sonic Heroes native spline vertices from a supplied parsed OBJ file. /// </summary> /// <param name="parser">An instance of the OBJ parser containing spline points as its list of vertices.</param> /// <returns>List of processed spline vertices.</returns> private static SplineVertex[] MakeSplineVertices(ref ObjParser parser) { // Generate list of vertices for spline. var vertices = new SplineVertex[parser.Vertices.Count]; for (int x = 0; x < parser.Vertices.Count; x++) { vertices[x] = new SplineVertex(parser.Vertices[x]); } // Calculate total spline length. int loopMaximum = vertices.Length - 1; // Set distance to each next vertex. for (int x = 0; x < loopMaximum; x++) { vertices[x].DistanceToNextVertex = vertices[x].CalculateDistanceToNext(vertices[x + 1]); } return(vertices); }
internal Mesh GetMesh(Table.Table table, int acc = -1, bool createHitShape = false) { var mesh = new Mesh(_data.Name); const int accuracy = (int)(10.0f * 1.2f); // see also above var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float)(1.0 / 1.5)) : -1.0f; var sv = new SplineVertex(_data.DragPoints, _data.Thickness, table.GetDetailLevel(), (int)splineAccuracy); var numRings = sv.VertexCount - 1; var numSegments = accuracy; var numVertices = numRings * numSegments; var numIndices = 6 * numVertices; //m_numVertices*2+2; var height = _data.HitHeight + table.TableHeight; mesh.Vertices = new Vertex3DNoTex2[numVertices]; mesh.Indices = new int[numIndices]; var prevB = new Vertex3D(); var invNr = 1.0f / numRings; var invNs = 1.0f / numSegments; var index = 0; for (var i = 0; i < numRings; i++) { var i2 = i == numRings - 1 ? 0 : i + 1; var tangent = new Vertex3D(sv.MiddlePoints[i2].X - sv.MiddlePoints[i].X, sv.MiddlePoints[i2].Y - sv.MiddlePoints[i].Y, 0.0f); Vertex3D biNormal; Vertex3D normal; if (i == 0) { var up = new Vertex3D(sv.MiddlePoints[i2].X + sv.MiddlePoints[i].X, sv.MiddlePoints[i2].Y + sv.MiddlePoints[i].Y, height * 2.0f); normal = new Vertex3D(tangent.Y * up.Z, -tangent.X * up.Z, tangent.X * up.Y - tangent.Y * up.X); // = CrossProduct(tangent, up) biNormal = new Vertex3D(tangent.Y * normal.Z, -tangent.X * normal.Z, tangent.X * normal.Y - tangent.Y * normal.X); // = CrossProduct(tangent, normal) } else { normal = prevB.Clone().Cross(tangent); biNormal = tangent.Clone().Cross(normal); } biNormal.Normalize(); normal.Normalize(); prevB = biNormal; var u = i * invNr; for (var j = 0; j < numSegments; j++) { var v = ((float)j + u) * invNs; var tmp = Vertex3D.GetRotatedAxis(j * (360.0f * invNs), tangent, normal) .MultiplyScalar(_data.Thickness * 0.5f); mesh.Vertices[index] = new Vertex3DNoTex2 { X = sv.MiddlePoints[i].X + tmp.X, Y = sv.MiddlePoints[i].Y + tmp.Y }; if (createHitShape && (j == 0 || j == 3)) { //!! hack, adapt if changing detail level for hitshape // for a hit shape create a more rectangle mesh and not a smooth one tmp.Z *= 0.6f; } mesh.Vertices[index].Z = height + tmp.Z; //texel mesh.Vertices[index].Tu = u; mesh.Vertices[index].Tv = v; index++; } } // calculate faces for (var i = 0; i < numRings; i++) { for (var j = 0; j < numSegments; j++) { var quad = new int[4]; quad[0] = i * numSegments + j; if (j != numSegments - 1) { quad[1] = i * numSegments + j + 1; } else { quad[1] = i * numSegments; } if (i != numRings - 1) { quad[2] = (i + 1) * numSegments + j; if (j != numSegments - 1) { quad[3] = (i + 1) * numSegments + j + 1; } else { quad[3] = (i + 1) * numSegments; } } else { quad[2] = j; if (j != numSegments - 1) { quad[3] = j + 1; } else { quad[3] = 0; } } mesh.Indices[(i * numSegments + j) * 6] = quad[0]; mesh.Indices[(i * numSegments + j) * 6 + 1] = quad[1]; mesh.Indices[(i * numSegments + j) * 6 + 2] = quad[2]; mesh.Indices[(i * numSegments + j) * 6 + 3] = quad[3]; mesh.Indices[(i * numSegments + j) * 6 + 4] = quad[2]; mesh.Indices[(i * numSegments + j) * 6 + 5] = quad[1]; } } Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices); var maxX = Constants.FloatMin; var minX = Constants.FloatMax; var maxY = Constants.FloatMin; var minY = Constants.FloatMax; var maxZ = Constants.FloatMin; var minZ = Constants.FloatMax; for (var i = 0; i < numVertices; i++) { if (maxX < mesh.Vertices[i].X) { maxX = mesh.Vertices[i].X; } if (minX > mesh.Vertices[i].X) { minX = mesh.Vertices[i].X; } if (maxY < mesh.Vertices[i].Y) { maxY = mesh.Vertices[i].Y; } if (minY > mesh.Vertices[i].Y) { minY = mesh.Vertices[i].Y; } if (maxZ < mesh.Vertices[i].Z) { maxZ = mesh.Vertices[i].Z; } if (minZ > mesh.Vertices[i].Z) { minZ = mesh.Vertices[i].Z; } } MiddlePoint.X = (maxX + minX) * 0.5f; MiddlePoint.Y = (maxY + minY) * 0.5f; MiddlePoint.Z = (maxZ + minZ) * 0.5f; return(mesh); }
private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, int acc = -1, bool createHitShape = false, float margin = 0f) { var mesh = new Mesh(); // i dont understand the calculation of splineaccuracy here /cupiii var accuracy = (int)(10.0f * 1.2f); if (acc != -1) { // hit shapes and UI display have the same, static, precision accuracy = acc; } var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float)(1.0 / 1.5)) : -1.0f; SplineVertex sv = new SplineVertex(_data.DragPoints, (int)(_data.Thickness + 0.5), detailLevel, splineAccuracy, margin: margin, loop: true); var height = playfieldHeight + meshHeight; // one ring for each Splinevertex var numRings = sv.VertexCount; var numSegments = accuracy; var up = new Vertex3D(0f, 0f, 1f); var points = new Vertex3D[numRings]; // middlepoints of rings var tangents = new Vertex3D[numRings]; // pointing into the direction of the spline, even first and last var right = new Vertex3D[numRings]; // pointing right, looking into tangent direction var down = new Vertex3D[numRings]; // pointing down from tangent view var accLength = new float[numRings]; // accumulated length of the wire beginning at 0; // copy the data from the pline into the middle of the new variables for (int i = 0; i < sv.VertexCount; i++) { points[i] = new Vertex3D(sv.MiddlePoints[i].X, sv.MiddlePoints[i].Y, height); right[i] = new Vertex3D(sv.RgvLocal[i].X - sv.MiddlePoints[i].X, sv.RgvLocal[i].Y - sv.MiddlePoints[i].Y, 0f); right[i].Normalize(); tangents[i] = Vertex3D.CrossProduct(right[i], new Vertex3D(0f, 0f, 1f)); tangents[i].Normalize(); } // calculate downvectors for (int i = 0; i < numRings; i++) { down[i] = Vertex3D.CrossProduct(right[i], tangents[i]); down[i].Normalize(); } // For UV calculation we need the whole length of the rubber accLength[0] = 0.0f; for (int i = 1; i < numRings; i++) { accLength[i] = accLength[i - 1] + (points[i] - points[i - 1]).Length(); } // add the lenth from the last ring to the first ring var totalLength = accLength[numRings - 1] + (points[0] - points[numRings - 1]).Length();; var numVertices = (numRings + 1) * numSegments; var numIndices = numRings * numSegments * 6; mesh.Vertices = new Vertex3DNoTex2[numVertices]; mesh.Indices = new int[numIndices]; // precalculate the rings (positive X is left, positive Y is up) Starting at the bottom clockwise (X=0, Y=1) var ringsX = new float[numSegments]; var ringsY = new float[numSegments]; for (int i = 0; i < numSegments; i++) { ringsX[i] = -1.0f * (float)System.Math.Sin(System.Math.PI * 2 * i / numSegments) * _data.Thickness; ringsY[i] = -1.0f * (float)System.Math.Cos(System.Math.PI + System.Math.PI * 2 * i / numSegments) * _data.Thickness; } var verticesIndex = 0; var indicesIndex = 0; // calculate Vertices first for (int currentRing = 0; currentRing < numRings; currentRing++) { // calculate one ring for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2 { X = points[currentRing].X + right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], Y = points[currentRing].Y + right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], Z = points[currentRing].Z + right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], //normals seem to be somehow off, but are caculated again at the end of mesh creation. Nx = right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], Ny = right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], Nz = right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], Tu = accLength[currentRing] / totalLength, Tv = (float)currentSegment / ((float)numSegments - 1) }; } // could be integrated in above for loop, but better to read and will be optimized anyway by compiler if (currentRing > 0) { for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { var csp1 = currentSegment + 1; if (csp1 >= numSegments) { csp1 = 0; } mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + csp1; } } } // copy first ring into last ring for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2 { X = points[0].X + right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment], Y = points[0].Y + right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment], Z = points[0].Z + right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment], //normals seem to be somehow off, but are caculated again at the end of mesh creation. Nx = right[0].X * ringsX[currentSegment] + down[0].X * ringsY[currentSegment], Ny = right[0].Y * ringsX[currentSegment] + down[0].Y * ringsY[currentSegment], Nz = right[0].Z * ringsX[currentSegment] + down[0].Z * ringsY[currentSegment], Tu = 1f, Tv = (float)currentSegment / ((float)numSegments - 1) }; } for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { var csp1 = currentSegment + 1; if (csp1 >= numSegments) { csp1 = 0; } mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = (numRings) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1; mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = (numRings) * numSegments + csp1; mesh.Indices[indicesIndex++] = (numRings - 1) * numSegments + csp1; } Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices); var maxX = Constants.FloatMin; var minX = Constants.FloatMax; var maxY = Constants.FloatMin; var minY = Constants.FloatMax; var maxZ = Constants.FloatMin; var minZ = Constants.FloatMax; for (var i = 0; i < numVertices; i++) { MathF.Max(maxX, mesh.Vertices[i].X); MathF.Max(maxY, mesh.Vertices[i].Y); MathF.Max(maxZ, mesh.Vertices[i].Z); MathF.Min(minX, mesh.Vertices[i].X); MathF.Min(minY, mesh.Vertices[i].X); MathF.Min(minZ, mesh.Vertices[i].X); } _middlePoint.X = (maxX + minX) * 0.5f; _middlePoint.Y = (maxY + minY) * 0.5f; _middlePoint.Z = (maxZ + minZ) * 0.5f; return(mesh); }
private Mesh GetMesh(float playfieldHeight, float meshHeight, int detailLevel, float bendradius, int acc = -1, bool createHitShape = false, float margin = 0f) { var mesh = new Mesh(); // i dont understand the calculation of splineaccuracy here /cupiii var accuracy = (int)(10.0f * 1.2f); if (acc != -1) // hit shapes and UI display have the same, static, precision { accuracy = acc; } var splineAccuracy = acc != -1 ? 4.0f * MathF.Pow(10.0f, (10.0f - PhysicsConstants.HitShapeDetailLevel) * (float)(1.0 / 1.5)) : -1.0f; SplineVertex sv = new SplineVertex(_data.DragPoints, (int)(_data.Thickness + 0.5), detailLevel, splineAccuracy, margin: margin, loop: false); var height = playfieldHeight + meshHeight; var standheight = _data.Standheight; // dont lat the Collider be higher than the visible mesh, just shift the top of the MWG. if (createHitShape) { standheight = standheight - _data.Height + height; } // one ring for each Splinevertex, two for the stands, and "bendradius" tomes two for the bend (should be enough) // todo: could be better, if accuracy was taken into account var numRingsInBend = (int)(bendradius + 1); var numRings = sv.VertexCount - 1 + numRingsInBend * 2 + 2; var numSegments = accuracy; var up = new Vertex3D(0f, 0f, 1f); var points = new Vertex3D[numRings]; // middlepoints of rings var tangents = new Vertex3D[numRings]; // pointing into the direction of the spline, even first and last var right = new Vertex3D[numRings]; // pointing right, looking into tangent direction var down = new Vertex3D[numRings]; // pointing down from tangent view var accLength = new float[numRings]; // accumulated length of the wire beginning at 0; // copy the data from the pline into the middle of the new variables for (int i = 0; i < sv.VertexCount - 1; i++) { points[i + numRingsInBend + 1] = new Vertex3D(sv.MiddlePoints[i].X, sv.MiddlePoints[i].Y, height); right[i + numRingsInBend + 1] = new Vertex3D(sv.RgvLocal[i].X - sv.MiddlePoints[i].X, sv.RgvLocal[i].Y - sv.MiddlePoints[i].Y, 0f); right[i + numRingsInBend + 1].Normalize(); tangents[i + numRingsInBend + 1] = Vertex3D.CrossProduct(right[i + numRingsInBend + 1], new Vertex3D(0f, 0f, 1f)); tangents[i + numRingsInBend + 1].Normalize(); } // first set up beginning of the stand points[0] = points[numRingsInBend + 1] + tangents[numRingsInBend + 1] * bendradius * -1 + up * standheight * -1f; tangents[0] = new Vertex3D(0f, 0f, 1f); right[0] = right[numRingsInBend + 1]; // set up the first point of the bend points[1] = points[numRingsInBend + 1] + tangents[numRingsInBend + 1] * bendradius * -1 + up * bendradius * -1f; tangents[1] = tangents[0]; right[1] = right[0]; // now bend from point 1 to numRingsInBend+1(-1) var diffXY = points[numRingsInBend + 1] - points[1]; diffXY.Z = 0; var diffZ = points[numRingsInBend + 1] - points[1]; diffZ.X = 0; diffZ.Y = 0; for (int i = 1; i < (numRingsInBend + 1); i++) { points[numRingsInBend - i + 1] = points[1] + diffXY - (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * diffXY + (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) * diffZ; var tmp = tangents[numRingsInBend + 1]; tmp.Normalize(); tangents[numRingsInBend - i + 1] = tmp * (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) + (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * up; right[numRingsInBend - i + 1] = right[0]; } // set up last point points[numRings - 1] = points[(numRings - 1) - numRingsInBend - 1] + tangents[numRings - 1 - numRingsInBend - 1] * bendradius + up * standheight * -1f; tangents[numRings - 1] = new Vertex3D(0f, 0f, -1f); right[numRings - 1] = right[(numRings - 1) - numRingsInBend - 1]; // and the point before points[numRings - 2] = points[(numRings - 1) - numRingsInBend - 1] + tangents[numRings - 1 - numRingsInBend - 1] * bendradius + up * bendradius * -1f; tangents[numRings - 2] = tangents[numRings - 1]; right[numRings - 2] = right[numRings - 1]; // now bend again diffXY = points[numRings - 1 - numRingsInBend - 1] - points[numRings - 2]; diffXY.Z = 0; diffZ = points[numRings - 1 - numRingsInBend - 1] - points[numRings - 2]; diffZ.X = 0; diffZ.Y = 0; for (int i = 1; i < (numRingsInBend + 1); i++) { points[numRings - 2 - numRingsInBend + i] = points[numRings - 2] + diffXY - (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * diffXY + (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) * diffZ; var tmp = tangents[numRings - 1 - numRingsInBend - 1]; tmp.Normalize(); tangents[numRings - 2 - numRingsInBend + i] = tmp * (float)System.Math.Cos(System.Math.PI / 2 / numRingsInBend * i) + (float)System.Math.Sin(System.Math.PI / 2 / numRingsInBend * i) * up * -1; right[numRings - 2 - numRingsInBend + i] = right[numRings - 1]; } // calculate downvectors for (int i = 0; i < numRings; i++) { down[i] = Vertex3D.CrossProduct(right[i], tangents[i]); down[i].Normalize(); } // For UV calculation we need the whole length of the wire accLength[0] = 0.0f; for (int i = 1; i < numRings; i++) { accLength[i] = accLength[i - 1] + (points[i] - points[i - 1]).Length(); } var totalLength = accLength[numRings - 1]; var numVertices = numRings * numSegments; var numIndices = (numRings - 1) * numSegments * 6; mesh.Vertices = new Vertex3DNoTex2[numVertices]; mesh.Indices = new int[numIndices]; // precalculate the rings (positive X is left, positive Y is up) Starting at the bottom clockwise (X=0, Y=1) var ringsX = new float[numSegments]; var ringsY = new float[numSegments]; for (int i = 0; i < numSegments; i++) { ringsX[i] = -1.0f * (float)System.Math.Sin(System.Math.PI * 2 * i / numSegments) * _data.Thickness; ringsY[i] = -1.0f * (float)System.Math.Cos(System.Math.PI + System.Math.PI * 2 * i / numSegments) * _data.Thickness; } var verticesIndex = 0; var indicesIndex = 0; // calculate Vertices first for (int currentRing = 0; currentRing < numRings; currentRing++) { // calculate one ring for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { mesh.Vertices[verticesIndex++] = new Vertex3DNoTex2 { X = points[currentRing].X + right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], Y = points[currentRing].Y + right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], Z = points[currentRing].Z + right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], //normals seem to be somehow off, but are caculated again at the end of mesh creation. Nx = right[currentRing].X * ringsX[currentSegment] + down[currentRing].X * ringsY[currentSegment], Ny = right[currentRing].Y * ringsX[currentSegment] + down[currentRing].Y * ringsY[currentSegment], Nz = right[currentRing].Z * ringsX[currentSegment] + down[currentRing].Z * ringsY[currentSegment], Tu = accLength[currentRing] / totalLength, Tv = (float)currentSegment / ((float)numSegments - 1) }; } // could be integrated in above for loop, but better to read and will be optimized anyway by compiler if (currentRing > 0) { for (int currentSegment = 0; currentSegment < numSegments; currentSegment++) { var csp1 = currentSegment + 1; if (csp1 >= numSegments) { csp1 = 0; } mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + currentSegment; mesh.Indices[indicesIndex++] = currentRing * numSegments + csp1; mesh.Indices[indicesIndex++] = (currentRing - 1) * numSegments + csp1; } } } Mesh.ComputeNormals(mesh.Vertices, numVertices, mesh.Indices, numIndices); var maxX = Constants.FloatMin; var minX = Constants.FloatMax; var maxY = Constants.FloatMin; var minY = Constants.FloatMax; var maxZ = Constants.FloatMin; var minZ = Constants.FloatMax; for (var i = 0; i < numVertices; i++) { MathF.Max(maxX, mesh.Vertices[i].X); MathF.Max(maxY, mesh.Vertices[i].Y); MathF.Max(maxZ, mesh.Vertices[i].Z); MathF.Min(minX, mesh.Vertices[i].X); MathF.Min(minY, mesh.Vertices[i].X); MathF.Min(minZ, mesh.Vertices[i].X); } _middlePoint.X = (maxX + minX) * 0.5f; _middlePoint.Y = (maxY + minY) * 0.5f; _middlePoint.Z = (maxZ + minZ) * 0.5f; return(mesh); }