Files
Fishing2/Assets/Infinity Code/Real World Terrain/Scripts/Containers/RealWorldTerrainMonoBase.cs
2025-06-09 00:11:54 +08:00

474 lines
17 KiB
C#

/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// This class contains all the information about the terrain and Real World Terrain settings.
/// </summary>
public abstract class RealWorldTerrainMonoBase : MonoBehaviour
{
/// <summary>
/// Specifies whether to generate grass on the terrain.
/// </summary>
public bool generateGrass;
/// <summary>
/// Specifies whether rivers have been generated on the terrain.
/// </summary>
public bool generatedRivers;
/// <summary>
/// Specifies whether to generate roads on the terrain.
/// </summary>
public bool generateRoads;
/// <summary>
/// Specifies whether to create textures
/// </summary>
public bool generateTextures;
/// <summary>
/// Specifies whether to generate trees on the terrain.
/// </summary>
public bool generateTrees;
/// <summary>
/// Specifies whether to create buildings
/// </summary>
public bool generatedBuildings;
/// <summary>
/// Specifies whether to create grass
/// </summary>
public bool generatedGrass;
/// <summary>
/// Specifies whether to create textures
/// </summary>
public bool generatedTextures;
/// <summary>
/// Specifies whether to create trees
/// </summary>
public bool generatedTrees;
/// <summary>
/// Maximal value of elevation
/// </summary>
public double maxElevation;
/// <summary>
/// Minimal value of elevation
/// </summary>
public double minElevation;
/// <summary>
/// Reference to the preferences
/// </summary>
public RealWorldTerrainPrefsBase prefs;
/// <summary>
/// Scale of terrains
/// </summary>
public Vector3 scale;
/// <summary>
/// Size of terrains in world units
/// </summary>
public Vector3 size;
/// <summary>
/// Top latitude
/// </summary>
public double topLatitude;
/// <summary>
/// Top latitude in Mercator projection (0-1)
/// </summary>
public double topMercator;
/// <summary>
/// Left longitude
/// </summary>
public double leftLongitude;
/// <summary>
/// Left longitude in Mercator projection (0-1)
/// </summary>
public double leftMercator;
/// <summary>
/// Bottom latitude
/// </summary>
public double bottomLatitude;
/// <summary>
/// Bottom latitude in Mercator projection (0-1)
/// </summary>
public double bottomMercator;
/// <summary>
/// Right longitude
/// </summary>
public double rightLongitude;
/// <summary>
/// Right longitude in Mercator projection (0-1)
/// </summary>
public double rightMercator;
/// <summary>
/// Width. Right longitude - left longitude
/// </summary>
public double width;
/// <summary>
/// Height. Top latitude - bottom latitude
/// </summary>
public double height;
#if BUILDR2
public List<RealWorldTerrainBuildR2Material> buildR2Facades;
#endif
private Dictionary<string, object> customFields;
/// <summary>
/// The width of the terrain in Mercator projection units.
/// </summary>
public double mercatorWidth;
/// <summary>
/// The height of the terrain in Mercator projection units.
/// </summary>
public double mercatorHeight;
/// <summary>
/// Gets or sets the value associated with the specified key in the custom fields.
/// </summary>
/// <param name="key">The key of the value to get or set.</param>
/// <returns>The value associated with the specified key.</returns>
public object this[string key]
{
get
{
if (customFields == null) return null;
object value;
if (!customFields.TryGetValue(key, out value)) return null;
return value;
}
set
{
if (customFields == null) customFields = new Dictionary<string, object>();
if (value != null) customFields[key] = value;
else customFields.Remove(key);
}
}
/// <summary>
/// Gets the bounds of the terrain.
/// </summary>
public Bounds bounds
{
get
{
return new Bounds(transform.position + size / 2, size);
}
}
/// <summary>
/// Clear custom fields.
/// </summary>
public void ClearCustomFields()
{
customFields = null;
}
/// <summary>
/// Checks whether the coordinate in terrain area.
/// </summary>
/// <param name="coordinates">Coordinate</param>
/// <returns>True - coordinate in area, False - otherwise.</returns>
public bool Contains(Vector2 coordinates)
{
return Contains(coordinates.x, coordinates.y);
}
/// <summary>
/// Checks whether the coordinate in terrain area.
/// </summary>
/// <param name="lng">Longitude</param>
/// <param name="lat">Latitude</param>
/// <returns>True - coordinate in area, False - otherwise.</returns>
public bool Contains(double lng, double lat)
{
return leftLongitude <= lng && rightLongitude >= lng && topLatitude >= lat && bottomLatitude <= lat;
}
/// <summary>
/// Get altitude by location (coordinates).
/// </summary>
/// <param name="lng">Longitude</param>
/// <param name="lat">Latitude</param>
/// <returns>Altitude (meters)</returns>
public double GetAltitudeByCoordinates(double lng, double lat)
{
if (!Contains(lng, lat)) return 0;
Vector3 worldPosition;
GetWorldPosition(lng, lat, out worldPosition);
Bounds b = new Bounds(bounds.center, bounds.size);
Vector3 offset = worldPosition - b.min;
if (prefs.resultType == RealWorldTerrainResultType.terrain)
{
RealWorldTerrainItem currentItem = GetItemByWorldPosition(worldPosition);
return offset.y / currentItem.terrainData.size.y * (currentItem.maxElevation - currentItem.minElevation);
}
return worldPosition.y / b.size.y * (maxElevation - minElevation) + minElevation;
}
/// <summary>
/// Get altitude by location (coordinates).
/// </summary>
/// <param name="lng">Longitude</param>
/// <param name="lat">Latitude</param>
/// <returns>Altitude (meters)</returns>
[Obsolete("Use GetAltitudeByCoordinates")]
public double GetAltitudeByLocation(double lng, double lat)
{
return GetAltitudeByCoordinates(lng, lat);
}
/// <summary>
/// Get altitude by Unity World Position.
/// </summary>
/// <param name="worldPosition">Unity World Position</param>
/// <returns>Altitude (meters)</returns>
public double GetAltitudeByWorldPosition(Vector3 worldPosition)
{
Bounds b = new Bounds(bounds.center, bounds.size);
Vector3 offset = worldPosition - b.min;
if (offset.x < 0 || offset.z < 0) return 0;
if (offset.x > b.size.x || offset.z > b.size.z) return 0;
if (prefs.resultType == RealWorldTerrainResultType.terrain)
{
RealWorldTerrainItem currentItem = GetItemByWorldPosition(worldPosition);
return offset.y / currentItem.terrainData.size.y * (currentItem.maxElevation - currentItem.minElevation);
}
return worldPosition.y / b.size.y * (maxElevation - minElevation) + minElevation;
}
/// <summary>
/// Get coordinates under mouse cursor
/// </summary>
/// <param name="coordinates">Geographical coordinates</param>
/// <param name="cam">Camera</param>
/// <returns>True - success, False - otherwise</returns>
public bool GetCoordinatesUnderCursor(out Vector2 coordinates, Camera cam = null)
{
return GetCoordinatesByScreenPosition(Input.mousePosition, out coordinates, cam);
}
/// <summary>
/// Converts the screen coordinates into geographic coordinates.
/// </summary>
/// <param name="screenPosition">Position in screen space</param>
/// <param name="coordinates">Geographic coordinates</param>
/// <param name="cam">Camera</param>
/// <returns>True - screen coordinates on terrains, False - otherwise</returns>
public bool GetCoordinatesByScreenPosition(Vector2 screenPosition, out Vector2 coordinates, Camera cam = null)
{
if (cam == null) cam = Camera.main;
coordinates = Vector2.zero;
RaycastHit[] hits = Physics.RaycastAll(cam.ScreenPointToRay(screenPosition));
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider is TerrainCollider || hit.collider is MeshCollider)
{
RealWorldTerrainItem item = hit.transform.GetComponent<RealWorldTerrainItem>();
if (item != null) return GetCoordinatesByWorldPosition(hit.point, out coordinates);
}
}
return false;
}
/// <summary>
/// Converts the screen coordinates into geographic coordinates.
/// </summary>
/// <param name="screenPosition">Position in screen space</param>
/// <param name="longitude">Longitude</param>
/// <param name="latitude">Latitude</param>
/// <param name="altitude">Altitude</param>
/// <param name="cam">Camera</param>
/// <returns>True - screen coordinates on terrains, False - otherwise</returns>
public bool GetCoordinatesByScreenPosition(Vector2 screenPosition, out double longitude, out double latitude, out double altitude, Camera cam = null)
{
if (cam == null) cam = Camera.main;
longitude = latitude = altitude = 0;
RaycastHit[] hits = Physics.RaycastAll(cam.ScreenPointToRay(screenPosition));
for (int i = 0; i < hits.Length; i++)
{
RaycastHit hit = hits[i];
if (hit.collider is TerrainCollider || hit.collider is MeshCollider)
{
RealWorldTerrainItem item = hit.transform.GetComponent<RealWorldTerrainItem>();
if (item != null) return GetCoordinatesByWorldPosition(hit.point, out longitude, out latitude, out altitude);
}
}
return false;
}
/// <summary>
/// Converts the world coordinates into geographic coordinates.
/// </summary>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <param name="coordinates">Geographic coordinates</param>
/// <returns>True - world coordinates on terrains, False - otherwise</returns>
public bool GetCoordinatesByWorldPosition(Vector3 worldPosition, out Vector2 coordinates)
{
coordinates = new Vector2();
double lng, lat;
bool result = GetCoordinatesByWorldPosition(worldPosition, out lng, out lat);
coordinates.x = (float)lng;
coordinates.y = (float)lat;
return result;
}
/// <summary>
/// Converts the world coordinates into geographic coordinates.
/// </summary>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <param name="longitude">Longitude</param>
/// <param name="latitude">Latitude</param>
/// <returns>True - world coordinates on terrains, False - otherwise</returns>
public bool GetCoordinatesByWorldPosition(Vector3 worldPosition, out double longitude, out double latitude)
{
Bounds b = new Bounds(bounds.center, bounds.size);
double wrx = (worldPosition.x - b.min.x) / b.size.x;
double wrz = (b.max.z - worldPosition.z) / b.size.z;
double px = (rightMercator - leftMercator) * wrx + leftMercator;
double pz = (bottomMercator - topMercator) * wrz + topMercator;
RealWorldTerrainGeo.MercatToLatLong(px, pz, out longitude, out latitude);
return b.Contains(worldPosition);
}
/// <summary>
/// Converts the world coordinates into geographic coordinates.
/// </summary>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <param name="longitude">Longitude</param>
/// <param name="latitude">Latitude</param>
/// <param name="altitude">Altitude</param>
/// <returns>True - world coordinates on terrains, False - otherwise</returns>
public bool GetCoordinatesByWorldPosition(Vector3 worldPosition, out double longitude, out double latitude, out double altitude)
{
altitude = 0;
Bounds b = new Bounds(bounds.center, bounds.size);
double wrx = (worldPosition.x - b.min.x) / b.size.x;
double wrz = (b.max.z - worldPosition.z) / b.size.z;
double px1 = (rightMercator - leftMercator) * wrx + leftMercator;
double pz = (bottomMercator - topMercator) * wrz + topMercator;
RealWorldTerrainGeo.MercatToLatLong(px1, pz, out longitude, out latitude);
if (b.min.x > worldPosition.x || b.max.x < worldPosition.x || b.min.z > worldPosition.z || b.max.z < worldPosition.z)
{
return false;
}
Vector3 offset = worldPosition - b.min;
if (prefs.resultType == RealWorldTerrainResultType.terrain)
{
RealWorldTerrainItem currentItem = GetItemByWorldPosition(worldPosition);
altitude = (offset.y + currentItem.transform.position.y) / currentItem.terrainData.size.y * (currentItem.maxElevation - currentItem.minElevation) + currentItem.minElevation;
}
else
{
altitude = worldPosition.y / b.size.y * (maxElevation - minElevation) + minElevation;
}
return true;
}
/// <summary>
/// Get RealWorldTerrainItem by Unity World Position.
/// </summary>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <returns>Instance of RealWorldTerrainItem</returns>
public abstract RealWorldTerrainItem GetItemByWorldPosition(Vector3 worldPosition);
/// <summary>
/// Get Unity World Position by geographic coordinates.
/// </summary>
/// <param name="lng">Longitude</param>
/// <param name="lat">Latitde</param>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <returns>True - success, False - otherwise</returns>
public abstract bool GetWorldPosition(double lng, double lat, out Vector3 worldPosition);
/// <summary>
/// Get Unity World Position by geographic coordinates.
/// </summary>
/// <param name="lng">Longitude</param>
/// <param name="lat">Latitude</param>
/// <param name="altitude">Altitude</param>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <returns>True - success, False - otherwise</returns>
public bool GetWorldPosition(double lng, double lat, double altitude, out Vector3 worldPosition)
{
bool result = GetWorldPosition(lng, lat, out worldPosition);
if (result) worldPosition.y = (float)(bounds.size.y * ((altitude - minElevation) / (maxElevation - minElevation)));
return result;
}
/// <summary>
/// Get Unity World Position by geographic coordinates.
/// </summary>
/// <param name="coordinates">Geographic coordinates</param>
/// <param name="worldPosition">Position in Unity World Space</param>
/// <returns></returns>
public abstract bool GetWorldPosition(Vector2 coordinates, out Vector3 worldPosition);
public void SetCoordinates(double x1, double y1, double x2, double y2, double tlx, double tly, double brx, double bry)
{
leftMercator = x1;
topMercator = y1;
rightMercator = x2;
bottomMercator = y2;
leftLongitude = tlx;
rightLongitude = brx;
topLatitude = tly;
bottomLatitude = bry;
width = rightLongitude - leftLongitude;
height = bottomLatitude - topLatitude;
mercatorWidth = rightMercator - leftMercator;
mercatorHeight = bottomMercator - topMercator;
}
}
}