private void InitializePlates() { // Initialize vertexToPlate array to a value that isn't a valid plate index VertexToPlates = new int[geometry.Mesh.Length]; for (int i = 0; i < geometry.Mesh.Length; ++i) { VertexToPlates[i] = -1; } borders.Clear(); for (int plateIndex = 0; plateIndex < GetPlates().Length; ++plateIndex) { int plate = -1; do { int vertexIndex = rand.Next(geometry.Mesh.Length); // prevent 2 plates spawning at the same vertex. plate = VertexToPlates[vertexIndex]; if (plate == -1) { var traits = new PlatePhysicsTraits(); traits.Pivot = new Vector3(rand.Next(100) - 50, rand.Next(100) - 50, rand.Next(100) - 50); traits.Pivot.Normalize(); traits.Center = geometry.Mesh.GetPosition(vertexIndex); if (Vector3.Dot(traits.Center, traits.Pivot) < 0) { traits.Pivot *= -1.0f; // Ensure pivot is at least in the same hemisphere as the plate center. } traits.CenterRotation = (rand.Next(200) - 100) * (float)Math.PI / 6000.0f; // -3 -> 3 degrees traits.PivotRotation = (rand.Next(200) - 100) * (float)Math.PI / 6000.0f; traits.Elevation = 0.0f; GetPlates()[plateIndex] = new Plate(VertexToPlates, geometry, traits, plateIndex, vertexIndex, ref rand); break; } } while (plate != -1); } }
public void CalculateBorderTileHeights() { // if perpendicular motion is +ve, it's a collision. // -ve, it's a rift (which would form basaltic volcanoes) // If collision, then // if heights sufficiently close & same sign, treat as height increase // mountain formation; folding; // if heights opposite sign; subduct the lower under the higher. // tilt upper plate (calc for all boundaries) foreach (int borderCornerKey in borderCorners.Keys) { List <Int64> borderIndices = borderCorners[borderCornerKey].borderIndices; if (borderIndices.Count == 3) { // At 3 plate boundaries, just take some maxima / averages: Plate plate0 = GetPlates()[VertexToPlates[geometry.Topology.Centroids[borderCornerKey].Faces[0]]]; Plate plate1 = GetPlates()[VertexToPlates[geometry.Topology.Centroids[borderCornerKey].Faces[1]]]; Plate plate2 = GetPlates()[VertexToPlates[geometry.Topology.Centroids[borderCornerKey].Faces[2]]]; Stress stress = borderCorners[borderCornerKey].stress; if (stress.pressure > 0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, Math.Max(plate1.Traits.Elevation, plate2.Traits.Elevation)) + stress.pressure; } else if (stress.pressure < -0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, Math.Max(plate1.Traits.Elevation, plate2.Traits.Elevation)) + stress.pressure / 4; } else if (stress.shear > 0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, Math.Max(plate1.Traits.Elevation, plate2.Traits.Elevation)) + stress.shear / 8; } else { borderCorners[borderCornerKey].elevation = (plate0.Traits.Elevation + plate1.Traits.Elevation + plate2.Traits.Elevation) / 3.0f; } } else { Border border0 = borders[borderIndices[0]]; Border border1 = borders[borderIndices[1]]; int plateIndex0 = border0.plate0; int plateIndex1 = border1.plate0 == plateIndex0 ? border1.plate1 : border0.plate1; Plate plate0 = GetPlates()[plateIndex0]; Plate plate1 = GetPlates()[plateIndex1]; ElevationCalculation elevationCalculation = ElevationCalculation.DORMANT; Stress stress = borderCorners[borderCornerKey].stress; if (stress.pressure > 0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, plate1.Traits.Elevation) + stress.pressure; if (plate0.Traits.Elevation < 0 && plate0.Traits.Elevation < 0) { elevationCalculation = ElevationCalculation.COLLIDING; } else if (plate0.Traits.Elevation < 0) { elevationCalculation = ElevationCalculation.SUBDUCTING; } else if (plate1.Traits.Elevation < 0) { elevationCalculation = ElevationCalculation.SUPERDUCTING; } else { elevationCalculation = ElevationCalculation.COLLIDING; } } else if (stress.pressure < -0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, plate1.Traits.Elevation) + stress.pressure / 4; elevationCalculation = ElevationCalculation.DIVERGING; } else if (stress.shear > 0.3) { borderCorners[borderCornerKey].elevation = Math.Max(plate0.Traits.Elevation, plate1.Traits.Elevation) + stress.shear / 8; elevationCalculation = ElevationCalculation.SHEARING; } else { borderCorners[borderCornerKey].elevation = (plate0.Traits.Elevation + plate1.Traits.Elevation) / 2.0f; elevationCalculation = ElevationCalculation.DORMANT; } // Queue up: // next corner: Inner corner: the corner that isn't the opposite corner of border0 and border1 // (i.e. remove the opposite corners from Centroid neighbours, and it's the remaining one). // origin: { this corner, stress, plate, elevationType } // border: inner border // corner: this corner // distanceToPlateBoundary: inner border length, i.e. of next corner. } } }
public void CalculateStresses() { // for each vertex in plate boundaries, // calculate relative motion between tiles // both parallel to (shear) and perpendicular to (pressure) edge. foreach (int borderCornerKey in borderCorners.Keys) { List <Int64> borderIndices = borderCorners[borderCornerKey].borderIndices; var centroid = geometry.Topology.Centroids[borderCornerKey]; var pos = centroid.position; Dictionary <int, Vector3> plateMovement = new Dictionary <int, Vector3>(); foreach (Int64 borderKey in borderIndices) { Border border = borders[borderKey]; // Calculate movement only once for each plate for (int i = 0; i < 2; ++i) { int plateIndex = (i == 0) ? border.plate0 : border.plate1; Vector3 movement; if (!plateMovement.TryGetValue(plateIndex, out movement)) { Plate plate = GetPlates()[plateIndex]; movement = plate.CalculateSpin(pos) + plate.CalculateDrift(pos); plateMovement[plateIndex] = movement; } } } if (borderIndices.Count == 3) { // 3 separate plates. Find movement from each plate at this corner and average it Stress[] stresses = new Stress[3]; int stressIdx = 0; foreach (Int64 borderKey in borderIndices) { Border border = borders[borderKey]; int oppositeCornerIndex = border.OppositeCorner(borderCornerKey); var oppositeCornerPosition = geometry.Topology.Centroids[oppositeCornerIndex].position; Vector3 boundary = oppositeCornerPosition - pos; Vector3 boundaryNormal = Vector3.Cross(boundary, pos); stresses[stressIdx++] = calculateStress(plateMovement[border.plate0], plateMovement[border.plate1], boundary, boundaryNormal); } borderCorners[borderCornerKey].stress.pressure = (stresses[0].pressure + stresses[1].pressure + stresses[2].pressure) / 3.0f; borderCorners[borderCornerKey].stress.shear = (stresses[0].shear + stresses[1].shear + stresses[2].shear) / 3.0f; } else // Border between only 2 plates. { // generate average vector, calculate stress once. Border border0 = borders[borderIndices[0]]; Border border1 = borders[borderIndices[1]]; int plate0 = border0.plate0; int plate1 = border1.plate0 == plate0 ? border1.plate1 : border0.plate1; int oppositeCornerIndex0 = border0.OppositeCorner(borderCornerKey); int oppositeCornerIndex1 = border0.OppositeCorner(borderCornerKey); var oppositeCornerPosition0 = geometry.Topology.Centroids[oppositeCornerIndex0].position; var oppositeCornerPosition1 = geometry.Topology.Centroids[oppositeCornerIndex1].position; Vector3 boundary = oppositeCornerPosition1 = oppositeCornerPosition0; Vector3 boundaryNormal = Vector3.Cross(boundary, pos); borderCorners[borderCornerKey].stress = calculateStress(plateMovement[plate0], plateMovement[plate1], boundary, boundaryNormal); } } }