using UnityEngine; using UnityEngine.UI; // Required for UI elements like Text using System.Collections.Generic; // Required for List using System.Linq; // Required for LINQ operations like ToList() using System; // Required for Math.Pow // PlanetaryInstabilityVisualizer.cs // This script visualizes the Earth spheroid, handles camera controls, // simulates tidal effects via mesh deformation, and provides interactive core data readouts. // Attach this script to an empty GameObject in your Unity scene, // and assign the necessary references in the Inspector. public class PlanetaryInstabilityVisualizer : MonoBehaviour { [Header("Spheroid References")] [Tooltip("The GameObject representing the Earth spheroid. This should be the 'Earth' object created by NBodySimulator.")] public GameObject earthSpheroid; [Tooltip("Material for the 'water' layer of the Earth spheroid.")] public Material waterMaterial; [Tooltip("Material for the 'land' layer of the Earth spheroid.")] public Material landMaterial; [Header("Camera Settings")] [Tooltip("The main camera in the scene.")] public Camera mainCamera; [Tooltip("Distance of the camera from the Earth's center.")] [Range(1.5f, 5.0f)] public float cameraDistance = 3.0f; [Tooltip("Sensitivity for camera rotation.")] public float rotationSpeed = 1.0f; [Tooltip("Sensitivity for camera zoom.")] public float zoomSpeed = 5.0f; [Header("UI References")] [Tooltip("UI Text element to display general simulation data.")] public Text generalInfoText; [Tooltip("UI Text element to display detailed layer data on click.")] public Text layerInfoText; [Header("Data Source References")] [Tooltip("Reference to the NBodySimulator in the scene.")] public NBodySimulator nBodySimulator; [Tooltip("Reference to the Earth's PlanetController (on the Earth GameObject).")] public PlanetController earthController; [Tooltip("Reference to the Sun's PlanetController.")] public PlanetController sunController; [Tooltip("Reference to the Moon's PlanetController.")] public PlanetController moonController; [Tooltip("Reference to the EarthInternalStructure script (on the Earth GameObject).")] public EarthInternalStructure earthInternalStructure; // Removed redundant public TrenchInstabilitySimulator trenchSimulator; [Header("Tidal Deformation Settings")] [Tooltip("Enable or disable visual tidal deformation.")] public bool enableTidalDeformation = true; // Removed deformationStrength as it's now calculated empirically private Mesh earthMesh; private Vector3[] baseVertices; // Original vertices private Vector3[] displacedVertices; // Deformed vertices void Awake() { // Auto-assign references if not set in Inspector if (mainCamera == null) mainCamera = Camera.main; if (nBodySimulator == null) nBodySimulator = FindObjectOfType(); if (earthSpheroid != null) { earthController = earthSpheroid.GetComponent(); earthInternalStructure = earthSpheroid.GetComponent(); // trenchSimulator = earthSpheroid.GetComponent(); // Now auto-assigned to local variable if needed // Get MeshFilter and Mesh for deformation MeshFilter mf = earthSpheroid.GetComponent(); if (mf != null) { earthMesh = mf.mesh; baseVertices = earthMesh.vertices; displacedVertices = new Vector3[baseVertices.Length]; } else { Debug.LogWarning("Earth Spheroid does not have a MeshFilter. Tidal deformation disabled."); enableTidalDeformation = false; } } else { Debug.LogError("Earth Spheroid reference not set in PlanetaryInstabilityVisualizer!"); enabled = false; } // Try to find Sun and Moon controllers if not assigned if (sunController == null && nBodySimulator != null) { GameObject sunObj = nBodySimulator.transform.Find("Sun")?.gameObject; if (sunObj != null) sunController = sunObj.GetComponent(); } if (moonController == null && nBodySimulator != null) { GameObject moonObj = nBodySimulator.transform.Find("Moon")?.gameObject; if (moonObj != null) moonController = moonObj.GetComponent(); } } void Update() { HandleCameraInput(); UpdateGeneralInfoUI(); HandleLayerClick(); if (enableTidalDeformation && earthMesh != null && earthController != null && sunController != null && moonController != null) { ApplyTidalDeformation(); } } void HandleCameraInput() { // Camera Rotation (Orbit around Earth) if (Input.GetMouseButton(0)) // Left mouse button { float mouseX = Input.GetAxis("Mouse X") * rotationSpeed; float mouseY = Input.GetAxis("Mouse Y") * rotationSpeed; // Rotate around Earth's center transform.RotateAround(earthSpheroid.transform.position, Vector3.up, -mouseX); transform.RotateAround(earthSpheroid.transform.position, mainCamera.transform.right, mouseY); } // Camera Zoom float scroll = Input.GetAxis("Mouse ScrollWheel"); cameraDistance -= scroll * zoomSpeed; cameraDistance = Mathf.Clamp(cameraDistance, 1.5f, 5.0f); // Keep camera within a reasonable range // Position the camera Vector3 desiredPosition = earthSpheroid.transform.position - mainCamera.transform.forward * cameraDistance; mainCamera.transform.position = desiredPosition; } void UpdateGeneralInfoUI() { if (generalInfoText == null || nBodySimulator == null || earthController == null) return; string info = $"Simulation Time Step: {nBodySimulator.simulationTimeStep_s} s\n" + $"Distance Scale: 1 Unity Unit = {nBodySimulator.distanceScale_m_per_unity_unit:E2} m\n" + $"Earth Position: {(earthController.currentPosition_m / 1000):F2} km\n" + $"Earth Velocity: {earthController.currentVelocity_m_per_s.magnitude:F2} m/s\n"; if (sunController != null) { info += $"Sun Position: {(sunController.currentPosition_m / 1000):F2} km\n"; info += $"Sun Velocity: {sunController.currentVelocity_m_per_s.magnitude:F2} m/s\n"; } if (moonController != null) { info += $"Moon Position: {(moonController.currentPosition_m / 1000):F2} km\n"; info += $"Moon Velocity: {moonController.currentVelocity_m_per_s.magnitude:F2} m/s\n"; } generalInfoText.text = info; } void HandleLayerClick() { if (Input.GetMouseButtonDown(1) && mainCamera != null && layerInfoText != null) // Right mouse button { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // Ensure Earth is on a Raycastable Layer // For example, if Earth is on "Default" layer, use LayerMask.GetMask("Default") if (Physics.Raycast(ray, out hit, Mathf.Infinity, LayerMask.GetMask("Default"))) { EarthLayerProperties layerData = hit.collider.GetComponent(); if (layerData != null) { DisplayLayerInfo(layerData); } else { layerInfoText.text = "Click on an Earth layer for details."; } } else { layerInfoText.text = "Click on an Earth layer for details."; } } } void DisplayLayerInfo(EarthLayerProperties layerData) { if (layerInfoText == null) return; layerInfoText.text = $"Layer: {layerData.layerName}\n" + $"Outer Radius: {layerData.outerRadius_m / 1000:F2} km\n" + $"Mass Percentage: {layerData.massPercentage * 100:F2}%\n" + $"Density: {layerData.averageDensity_kg_m3:F2} kg/m³\n" + $"Pressure: {layerData.averagePressure_GPa:F2} GPa\n" + $"Temperature: {layerData.averageTemperature_C:F2} °C\n" + $"Viscosity: {layerData.viscosity_cP:E2} cP"; } void ApplyTidalDeformation() { if (earthMesh == null || baseVertices == null || displacedVertices == null) return; if (earthSpheroid == null || sunController == null || moonController == null) return; // Get current Earth, Sun, Moon positions and masses in meters (real-world coordinates) Vector3d earthPos = earthController.currentPosition_m; double earthMass = earthController.mass; Vector3d sunPos = sunController.currentPosition_m; double sunMass = sunController.mass; Vector3d moonPos = moonController.currentPosition_m; double moonMass = moonController.mass; // Earth's mean radius for tidal potential calculation double earthMeanRadius = EarthConstantsAndCalculations.WGS84_EQUATORIAL_RADIUS_M; double G = NBodySimulator.gravitationalConstant; // Iterate through each vertex and apply deformation based on Love numbers for (int i = 0; i < baseVertices.Length; i++) { Vector3 vertex = baseVertices[i]; Vector3 vertexWorldPosUnity = earthSpheroid.transform.TransformPoint(vertex); // Convert vertex position to meters relative to Earth's center Vector3d vertexLocalPos_m = (Vector3d)(vertexWorldPosUnity - earthSpheroid.transform.position) * nBodySimulator.distanceScale_m_per_unity_unit; // Calculate tidal potential from Sun at this vertex Vector3d r_sun_vertex = sunPos - (earthPos + vertexLocalPos_m); // Vector from sun to vertex double r_sun_vertex_mag_sq = r_sun_vertex.sqrMagnitude; double r_sun_vertex_mag = Math.Sqrt(r_sun_vertex_mag_sq); Vector3d r_sun_earth = sunPos - earthPos; // Vector from sun to Earth's center double r_sun_earth_mag_sq = r_sun_earth.sqrMagnitude; double r_sun_earth_mag = Math.Sqrt(r_sun_earth_mag_sq); // Tidal potential from Sun: U_Tide = -GM_ext * (R^2/r^3) * P2(cos(theta)) // P2(cos(theta)) = (3/2 * cos^2(theta) - 1/2), where theta is angle between r_sun_earth and vertexLocalPos_m double cos_theta_sun = Vector3d.Dot(vertexLocalPos_m.normalized, r_sun_earth.normalized); double P2_cos_theta_sun = (1.5 * Math.Pow(cos_theta_sun, 2)) - 0.5; double tidalPotentialSun = -G * sunMass * (Math.Pow(earthMeanRadius, 2) / Math.Pow(r_sun_earth_mag, 3)) * P2_cos_theta_sun; // Calculate tidal potential from Moon at this vertex (similar calculation) Vector3d r_moon_vertex = moonPos - (earthPos + vertexLocalPos_m); double r_moon_vertex_mag_sq = r_moon_vertex.sqrMagnitude; double r_moon_vertex_mag = Math.Sqrt(r_moon_vertex_mag_sq); Vector3d r_moon_earth = moonPos - earthPos; double r_moon_earth_mag_sq = r_moon_earth.sqrMagnitude; double r_moon_earth_mag = Math.Sqrt(r_moon_earth_mag_sq); double cos_theta_moon = Vector3d.Dot(vertexLocalPos_m.normalized, r_moon_earth.normalized); double P2_cos_theta_moon = (1.5 * Math.Pow(cos_theta_moon, 2)) - 0.5; double tidalPotentialMoon = -G * moonMass * (Math.Pow(earthMeanRadius, 2) / Math.Pow(r_moon_earth_mag, 3)) * P2_cos_theta_moon; // Total tidal potential at this vertex double totalTidalPotential = tidalPotentialSun + tidalPotentialMoon; // Apply deformation using Love number h (radial displacement) // Displacement = h * (Tidal Potential / g) // Where g is surface gravity (GM_Earth / R_Earth^2) double surfaceGravity = EarthConstantsAndCalculations.WGS84_GM / Math.Pow(earthMeanRadius, 2); double radialDisplacement_m = EarthConstantsAndCalculations.LOVE_NUMBER_H * (totalTidalPotential / surfaceGravity); // Convert displacement to Unity units float radialDisplacement_unity = (float)(radialDisplacement_m / nBodySimulator.distanceScale_m_per_unity_unit); // Displace vertex along its normal (outward from Earth's center) Vector3 vertexNormal = baseVertices[i].normalized; // Assuming base vertices are relative to center displacedVertices[i] = baseVertices[i] + vertexNormal * radialDisplacement_unity; } earthMesh.vertices = displacedVertices; earthMesh.RecalculateNormals(); // Recalculate normals after deformation for correct lighting earthMesh.RecalculateBounds(); // Recalculate bounds for culling efficiency } // Helper method to set material rendering mode (for transparency if needed) private void SetMaterialTransparency(Material mat, Color color, float alpha) { if (mat == null) return; Color newColor = color; newColor.a = alpha; mat.color = newColor; if (alpha < 1.0f) { mat.SetFloat("_Mode", 2); mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); mat.SetInt("_ZWrite", 0); mat.DisableKeyword("_ALPHATEST_ON"); mat.EnableKeyword("_ALPHABLEND_ON"); mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; } else { mat.SetFloat("_Mode", 0); mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); mat.SetInt("_ZWrite", 1); mat.DisableKeyword("_ALPHABLEND_ON"); mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); mat.EnableKeyword("_ALPHATEST_ON"); mat.renderQueue = -1; } } } Planetary Instability Model PIM - Copyright (C) 2025 James Pacha