This commit is contained in:
2025-06-09 00:11:54 +08:00
parent d8c6eb0bd6
commit c773a6bb8d
11207 changed files with 48929 additions and 304 deletions

View File

@@ -0,0 +1,34 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Class of BuildR preset.
/// </summary>
[Serializable]
public class RealWorldTerrainBuildRPresetsItem
{
/// <summary>
/// Facade preset.
/// </summary>
public string facade;
/// <summary>
/// Roof preset.
/// </summary>
public string roof;
/// <summary>
/// Texture preset.
/// </summary>
public string texture;
public RealWorldTerrainBuildRPresetsItem()
{
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fa25264269a21a14baff7b1dcd92a30d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Building material class.
/// </summary>
[Serializable]
public class RealWorldTerrainBuildingMaterial
{
/// <summary>
/// Roof material.
/// </summary>
public Material roof;
/// <summary>
/// Wall material.
/// </summary>
public Material wall;
//Size of a tile texture in meters.
public Vector2 tileSize = new Vector2(30, 30);
public RealWorldTerrainBuildingMaterial()
{
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fab0ea989fa6a174aa9d7416f9c57671
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
[Serializable]
public class RealWorldTerrainBuildingPrefab
{
public GameObject prefab;
public List<OSMTag> tags;
public SizeMode sizeMode = SizeMode.fitToBounds;
public HeightMode heightMode = HeightMode.levelBased;
public PlacementMode placementMode = PlacementMode.lowerCorner;
public float fixedHeight = 15;
public bool hasBounds
{
get { return prefab.GetComponent<Collider>() != null; }
}
[Serializable]
public class OSMTag
{
public string key;
public string value;
public bool hasEmptyKey
{
get { return string.IsNullOrEmpty(key); }
}
public bool hasEmptyValue
{
get { return string.IsNullOrEmpty(value); }
}
public bool isEmpty
{
get { return hasEmptyKey && hasEmptyValue; }
}
}
public enum SizeMode
{
originalSize,
fitToBounds,
}
public enum HeightMode
{
original,
averageXZ,
levelBased,
fixedHeight
}
public enum PlacementMode
{
lowerCorner,
highestCorner,
average,
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 16508243d0543e74ca7f16691a79501f
timeCreated: 1539976693
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.Globalization;
namespace InfinityCode.RealWorldTerrain
{
public static class RealWorldTerrainCultureInfo
{
public static CultureInfo cultureInfo
{
get { return CultureInfo.InvariantCulture; }
}
public static NumberFormatInfo numberFormat
{
get { return cultureInfo.NumberFormat; }
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 88a4d83277de8ab4697b76ec5ff49be4
timeCreated: 1543692552
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,260 @@
/* INFINITY CODE */
/* https://infinity-code.com */
namespace InfinityCode.RealWorldTerrain
{
public enum RealWorldTerrainBuildR2Collider
{
none,
primitive,
simple,
complex
}
public enum RealWorldTerrainBuildR2RenderMode
{
none,
box,
simple,
full
}
public enum RealWorldTerrainBuildingBottomMode
{
followRealWorldData,
followTerrain
}
public enum RealWorldTerrainByteOrder
{
Windows,
Mac
}
public enum RealWorldTerrainElevationProvider
{
SRTM,
BingMaps,
SRTM30,
Mapbox,
//ArcGIS
}
public enum RealWorldTerrainElevationRange
{
autoDetect,
fixedValue,
realWorldValue
}
public enum RealWorldTerrainElevationType
{
realWorld,
heightmap
}
public enum RealWorldTerrainGenerateBuildingPhaseEnum
{
house,
wall
}
public enum RealWorldTerrainGenerateType
{
full,
terrain,
texture,
additional
}
public enum RealWorldTerrainMapboxLayer
{
building,
landuse_overlay,
landuse,
road,
water,
waterway,
structure
}
public enum RealWorldTerrainMapboxLanduse
{
aboriginal_lands = 1,
agriculture = 1 << 1,
airport = 1 << 2,
cemetery = 1 << 3,
glacier = 1 << 4,
grass = 1 << 5,
hospital = 1 << 6,
park = 1 << 7,
piste = 1 << 8,
pitch = 1 << 9,
rock = 1 << 10,
sand = 1 << 11,
school = 1 << 12,
scrub = 1 << 13,
wood = 1 << 14,
facility = 1 << 15
}
public enum RealWorldTerrainMapboxLanduseOverlay
{
national_park = 1,
wetland = 1 << 1,
wetland_noveg = 1 << 2
}
public enum RealWorldTerrainMapboxStructure
{
cliff = 1,
fence = 1 << 1,
gate = 1 << 2,
hedge = 1 << 3,
land = 1 << 4,
steps = 1 << 5,
tower = 1 << 6
}
public enum RealWorldTerrainMapboxWaterway
{
river = 1,
canal = 1 << 1,
stream = 1 << 2,
stream_intermittent = 1 << 3,
drain = 1 << 4,
ditch = 1 << 5
}
public enum RealWorldTerrainMaxElevation
{
autoDetect,
realWorldValue
}
public enum RealWorldTerrainOSMOverpassServer
{
main = 0,
main2 = 1,
french = 2,
taiwan = 3,
kumiSystems = 4,
}
public enum RealWorldTerrainRawType
{
RAW,
mapboxRGB
}
public enum RealWorldTerrainResultType
{
terrain,
mesh,
gaiaStamp,
rawFile
}
public enum RealWorldTerrainRoadType
{
motorway = 1,
trunk = 1 << 1,
primary = 1 << 2,
secondary = 1 << 3,
tertiary = 1 << 4,
unclassified = 1 << 5,
residential = 1 << 6,
service = 1 << 7,
motorway_link = 1 << 8,
trunk_link = 1 << 9,
primary_link = 1 << 10,
secondary_link = 1 << 11,
tertiary_link = 1 << 12,
living_street = 1 << 13,
pedestrian = 1 << 14,
track = 1 << 15,
bus_guideway = 1 << 16,
raceway = 1 << 17,
road = 1 << 18,
footway = 1 << 19,
cycleway = 1 << 20,
bridleway = 1 << 21,
steps = 1 << 22,
path = 1 << 23
}
public enum RealWorldTerrainRoadTypeMode
{
simple,
advanced
}
/// <summary>
/// Type of building roof.
/// </summary>
public enum RealWorldTerrainRoofType
{
/// <summary>
/// Dome roof.
/// </summary>
dome,
/// <summary>
/// Flat roof.
/// </summary>
flat
}
public enum RealWorldTerrainTextureFileType
{
png,
jpg
}
public enum RealWorldTerrainTextureProvider
{
arcGIS,
google,
mapQuest,
nokia,
virtualEarth,
openStreetMap,
custom = 999
}
public enum RealWorldTerrainTextureResultType
{
regularTexture = 0,
hugeTexture = 1,
terrainLayers = 2
}
public enum RealWorldTerrainTextureType
{
satellite,
terrain,
relief
}
public enum RealWorldTerrainUpdateType
{
all,
alpha,
beta,
releaseCandidate,
stable
}
public enum RealWorldTerrainVolumeGrassOutsidePoints
{
removeOutsidePoints,
noMakeAllMeshes,
noMakeMeshesWithOutsidePoints
}
public enum RealWorldTerrainWaterDetectionSource
{
texture,
bitMask
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2417ac73ed88c124d91dfab1b0b3e611
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.IO;
using System.Linq;
using System.Threading;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Provides utility methods for file system operations.
/// </summary>
public static class RealWorldTerrainFileSystem
{
/// <summary>
/// The number of bytes in megabyte.
/// </summary>
public const int MB = 1048576;
/// <summary>
/// Calculates the total size of a directory in bytes.
/// </summary>
/// <param name="folder">The directory for which to calculate the size.</param>
/// <returns>The total size of the directory in bytes.</returns>
public static long GetDirectorySize(DirectoryInfo folder)
{
return folder.GetFiles().Sum(fi => fi.Length) + folder.GetDirectories().Sum(dir => GetDirectorySize(dir));
}
/// <summary>
/// Calculates the total size of a directory in bytes.
/// </summary>
/// <param name="folderPath">The path of the directory for which to calculate the size.</param>
/// <returns>The total size of the directory in bytes.</returns>
public static long GetDirectorySize(string folderPath)
{
return GetDirectorySize(new DirectoryInfo(folderPath));
}
/// <summary>
/// Calculates the total size of a directory in megabytes.
/// </summary>
/// <param name="folderPath">The path of the directory for which to calculate the size.</param>
/// <returns>The total size of the directory in megabytes.</returns>
public static long GetDirectorySizeMB(string folderPath)
{
return GetDirectorySize(folderPath) / MB;
}
/// <summary>
/// Safely deletes a directory.
/// </summary>
/// <param name="directoryName">The name of the directory to delete.</param>
public static void SafeDeleteDirectory(string directoryName)
{
try
{
Directory.Delete(directoryName, true);
}
catch
{ }
}
/// <summary>
/// Safely deletes a file.
/// </summary>
/// <param name="filename">The path of the file to delete.</param>
/// <param name="tryCount">The number of attempts to delete the file. Default is 10.</param>
public static void SafeDeleteFile(string filename, int tryCount = 10)
{
while (tryCount-- > 0)
{
try
{
File.Delete(filename);
break;
}
catch (Exception)
{
#if !NETFX_CORE
Thread.Sleep(10);
#endif
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02f59f613aaea0e4fb996176fbe47ccf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,961 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using InfinityCode.RealWorldTerrain.XML;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Utils
{
/// <summary>
/// Class for working with GPX.
/// </summary>
public class RealWorldTerrainGPXObject
{
/// <summary>
/// GPX document version.
/// </summary>
public string version = "1.1";
/// <summary>
/// Name or URL of the software that created your GPX document. \n
/// This allows others to inform the creator of a GPX instance document that fails to validate.
/// </summary>
public string creator = "RealWorldTerrain";
/// <summary>
/// Metadata about the gpx.
/// </summary>
public Meta metadata;
/// <summary>
/// A list of waypoints.
/// </summary>
public List<Waypoint> waypoints;
/// <summary>
/// A list of routes.
/// </summary>
public List<Route> routes;
/// <summary>
/// A list of tracks.
/// </summary>
public List<Track> tracks;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
private RealWorldTerrainGPXObject()
{
waypoints = new List<Waypoint>();
routes = new List<Route>();
tracks = new List<Track>();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="creator">Name or URL of the software that created your GPX document.</param>
/// <param name="version">GPX document version.</param>
public RealWorldTerrainGPXObject(string creator, string version = "1.1") : this()
{
this.creator = creator;
this.version = version;
}
/// <summary>
/// Load GPX Object from string.
/// </summary>
/// <param name="content">A string containing GPX content.</param>
/// <returns>Instance of GPX Object</returns>
public static RealWorldTerrainGPXObject Load(string content)
{
RealWorldTerrainGPXObject instance = new RealWorldTerrainGPXObject();
try
{
RealWorldTerrainXML xml = RealWorldTerrainXML.Load(content);
instance.version = xml.A("version");
instance.creator = xml.A("creator");
foreach (RealWorldTerrainXML n in xml)
{
if (n.name == "wpt") instance.waypoints.Add(new Waypoint(n));
else if (n.name == "rte") instance.routes.Add(new Route(n));
else if (n.name == "trk") instance.tracks.Add(new Track(n));
else if (n.name == "metadata") instance.metadata = new Meta(n);
else if (n.name == "extensions") instance.extensions = n;
else Debug.Log(n.name);
}
}
catch (Exception exception)
{
Debug.Log(exception.Message + "\n" + exception.StackTrace);
}
return instance;
}
/// <summary>
/// Returns RealWorldTerrainXML, contains full information about GPX Object.
/// </summary>
/// <returns>Instance of RealWorldTerrainXML.</returns>
public RealWorldTerrainXML ToXML()
{
RealWorldTerrainXML xml = new RealWorldTerrainXML("gpx");
xml.A("version", version);
xml.A("creator", creator);
if (metadata != null) metadata.AppendToNode(xml.Create("metadata"));
if (waypoints != null) foreach (Waypoint i in waypoints) i.AppendToNode(xml.Create("wpt"));
if (routes != null) foreach (Route i in routes) i.AppendToNode(xml.Create("rte"));
if (tracks != null) foreach (Track i in tracks) i.AppendToNode(xml.Create("trk"));
if (extensions != null) xml.AppendChild(extensions);
return xml;
}
public override string ToString()
{
return ToXML().outerXml;
}
/// <summary>
/// Information about the copyright holder and any license governing use of this file.
/// By linking to an appropriate license, you may place your data into the public domain or grant additional usage rights.
/// </summary>
public class Copyright
{
/// <summary>
/// Copyright holder
/// </summary>
public string author;
/// <summary>
/// Year of copyright.
/// </summary>
public int? year;
/// <summary>
/// Link to external file containing license text.
/// </summary>
public string license;
/// <summary>
/// Constructor
/// </summary>
/// <param name="author">Copyright holder</param>
public Copyright(string author)
{
this.author = author;
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Copyright node</param>
public Copyright(RealWorldTerrainXML node)
{
author = node.A("author");
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "year") year = n.Value<int>();
else if (n.name == "license") license = n.Value();
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
node.A("author", author);
if (year.HasValue) node.Create("year", year.Value);
if (!string.IsNullOrEmpty(license)) node.Create("license", license);
}
}
/// <summary>
/// Two lat/lon pairs defining the extent of an element.
/// </summary>
public class Bounds
{
/// <summary>
/// The minimum latitude.
/// </summary>
public double minlat
{
get { return _minlat; }
set { _minlat = RealWorldTerrainMath.Clip(value, -90, 90); }
}
/// <summary>
/// The minimum longitude.
/// </summary>
public double minlon
{
get { return _minlon; }
set { _minlon = RealWorldTerrainMath.Repeat(value, -180, 180); }
}
/// <summary>
/// The maximum latitude.
/// </summary>
public double maxlat
{
get { return _maxlat; }
set { _maxlat = RealWorldTerrainMath.Clip(value, -90, 90); }
}
/// <summary>
/// The maximum longitude.
/// </summary>
public double maxlon
{
get { return _maxlon; }
set { _maxlon = RealWorldTerrainMath.Repeat(value, -180, 180); }
}
private double _minlat;
private double _minlon;
private double _maxlat;
private double _maxlon;
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Bounds node</param>
public Bounds(RealWorldTerrainXML node)
{
minlat = node.A<double>("minlat");
minlon = node.A<double>("minlon");
maxlat = node.A<double>("maxlat");
maxlon = node.A<double>("maxlon");
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="minlon">The minimum longitude.</param>
/// <param name="minlat">The minimum latitude.</param>
/// <param name="maxlon">The maximum longitude.</param>
/// <param name="maxlat">The maximum latitude.</param>
public Bounds(double minlon, double minlat, double maxlon, double maxlat)
{
this.minlat = minlat;
this.minlon = minlon;
this.maxlat = maxlat;
this.maxlon = maxlon;
}
public void AppendToNode(RealWorldTerrainXML node)
{
node.A("minlat", minlat);
node.A("minlon", minlon);
node.A("maxlat", maxlat);
node.A("maxlon", maxlon);
}
}
/// <summary>
/// An email address. Broken into two parts (id and domain) to help prevent email harvesting.
/// </summary>
public class EMail
{
/// <summary>
/// ID half of email address
/// </summary>
public string id;
/// <summary>
/// Domain half of email address
/// </summary>
public string domain;
/// <summary>
/// Constructor
/// </summary>
/// <param name="id">ID half of email address</param>
/// <param name="domain">Domain half of email address</param>
public EMail(string id, string domain)
{
this.id = id;
this.domain = domain;
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">EMail node</param>
public EMail(RealWorldTerrainXML node)
{
id = node.A("id");
domain = node.A("domain");
}
public void AppendToNode(RealWorldTerrainXML node)
{
node.A("id", id);
node.A("domain", domain);
}
}
/// <summary>
/// A link to an external resource (Web page, digital photo, video clip, etc) with additional information.
/// </summary>
public class Link
{
/// <summary>
/// URL of hyperlink.
/// </summary>
public string href;
/// <summary>
/// Text of hyperlink.
/// </summary>
public string text;
/// <summary>
/// Mime type of content (image/jpeg)
/// </summary>
public string type;
/// <summary>
/// Constructor
/// </summary>
/// <param name="href">URL of hyperlink.</param>
public Link(string href)
{
this.href = href;
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Link node</param>
public Link(RealWorldTerrainXML node)
{
href = node.A("href");
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "text") text = n.Value();
else if (n.name == "type") type = n.Value();
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
node.A("href", href);
if (!string.IsNullOrEmpty(text)) node.Create("text", text);
if (!string.IsNullOrEmpty(type)) node.Create("type", type);
}
}
/// <summary>
/// Information about the GPX file, author, and copyright restrictions goes in the metadata section. \n
/// Providing rich, meaningful information about your GPX files allows others to search for and use your GPS data.
/// </summary>
public class Meta
{
/// <summary>
/// The name of the GPX file.
/// </summary>
public string name;
/// <summary>
/// A description of the contents of the GPX file.
/// </summary>
public string description;
/// <summary>
/// The person or organization who created the GPX file.
/// </summary>
public Person author;
/// <summary>
/// Copyright and license information governing use of the file.
/// </summary>
public Copyright copyright;
/// <summary>
/// URLs associated with the location described in the file.
/// </summary>
public List<Link> links;
/// <summary>
/// The creation date of the file.
/// </summary>
public DateTime? time;
/// <summary>
/// Keywords associated with the file. Search engines or databases can use this information to classify the data.
/// </summary>
public string keywords;
/// <summary>
/// Minimum and maximum coordinates which describe the extent of the coordinates in the file.
/// </summary>
public Bounds bounds;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
/// <summary>
/// Constructor
/// </summary>
public Meta()
{
links = new List<Link>();
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Meta node</param>
public Meta(RealWorldTerrainXML node) : this()
{
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "name") name = n.Value();
else if (n.name == "desc") description = n.Value();
else if (n.name == "author") author = new Person(n);
else if (n.name == "copyright") copyright = new Copyright(n);
else if (n.name == "link") links.Add(new Link(n));
else if (n.name == "time") time = DateTime.Parse(n.Value());
else if (n.name == "keywords") keywords = n.Value();
else if (n.name == "bounds") bounds = new Bounds(n);
else if (n.name == "extensions") extensions = n;
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
if (!string.IsNullOrEmpty(name)) node.Create("name", name);
if (!string.IsNullOrEmpty(description)) node.Create("desc", description);
if (author != null) author.AppendToNode(node);
if (copyright != null) copyright.AppendToNode(node.Create("copyright"));
if (links != null && links.Count > 0) foreach (Link l in links) l.AppendToNode(node.Create("link"));
if (time.HasValue) node.Create("time", time.Value.ToUniversalTime().ToString("s") + "Z");
if (!string.IsNullOrEmpty(keywords)) node.Create("keywords", keywords);
if (bounds != null) bounds.AppendToNode(node.Create("bounds"));
if (extensions != null) node.AppendChild(extensions);
}
}
/// <summary>
/// A person or organization.
/// </summary>
public class Person
{
/// <summary>
/// Name of person or organization.
/// </summary>
public string name;
/// <summary>
/// Email address.
/// </summary>
public EMail email;
/// <summary>
/// Link to Web site or other external information about person.
/// </summary>
public Link link;
/// <summary>
/// Constructor
/// </summary>
public Person()
{
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Person node</param>
public Person(RealWorldTerrainXML node)
{
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "name") name = n.Value();
else if (n.name == "email") email = new EMail(n);
else if (n.name == "link") link = new Link(n);
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
if (!string.IsNullOrEmpty(name)) node.Create("name", name);
if (email != null) email.AppendToNode(node.Create("email"));
if (link != null) link.AppendToNode(node.Create("link"));
}
}
/// <summary>
/// Route - an ordered list of waypoints representing a series of turn points leading to a destination.
/// </summary>
public class Route
{
/// <summary>
/// GPS name of route.
/// </summary>
public string name;
/// <summary>
/// GPS comment for route.
/// </summary>
public string comment;
/// <summary>
/// Text description of route for user. Not sent to GPS.
/// </summary>
public string description;
/// <summary>
/// Source of data. Included to give user some idea of reliability and accuracy of data.
/// </summary>
public string source;
/// <summary>
/// Links to external information about the route.
/// </summary>
public List<Link> links;
/// <summary>
/// GPS route number.
/// </summary>
public uint? number;
/// <summary>
/// Type (classification) of route.
/// </summary>
public string type;
/// <summary>
/// A list of route points.
/// </summary>
public List<Waypoint> points;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
/// <summary>
/// Constructor
/// </summary>
public Route()
{
links = new List<Link>();
points = new List<Waypoint>();
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Route node</param>
public Route(RealWorldTerrainXML node) : this()
{
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "name") name = n.Value();
else if (n.name == "cmt") comment = n.Value();
else if (n.name == "desc") description = n.Value();
else if (n.name == "src") source = n.Value();
else if (n.name == "link") links.Add(new Link(n));
else if (n.name == "number") number = n.Value<uint>();
else if (n.name == "type") type = n.Value();
else if (n.name == "rtept") points.Add(new Waypoint(n));
else if (n.name == "extensions") extensions = n;
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
if (!string.IsNullOrEmpty(name)) node.Create("name", name);
if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment);
if (!string.IsNullOrEmpty(description)) node.Create("desc", description);
if (!string.IsNullOrEmpty(source)) node.Create("src", source);
if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link"));
if (number.HasValue) node.Create("number", number.Value);
if (!string.IsNullOrEmpty(type)) node.Create("type", type);
foreach (Waypoint p in points) p.AppendToNode(node.Create("rtept"));
if (extensions != null) node.AppendChild(extensions);
}
}
/// <summary>
/// Track - an ordered list of points describing a path.
/// </summary>
public class Track
{
/// <summary>
/// GPS name of track.
/// </summary>
public string name;
/// <summary>
/// GPS comment for track.
/// </summary>
public string comment;
/// <summary>
/// User description of track.
/// </summary>
public string description;
/// <summary>
/// Source of data. Included to give user some idea of reliability and accuracy of data.
/// </summary>
public string source;
/// <summary>
/// Links to external information about track.
/// </summary>
public List<Link> links;
/// <summary>
/// GPS track number.
/// </summary>
public uint? number;
/// <summary>
/// Type (classification) of track.
/// </summary>
public string type;
/// <summary>
/// A Track Segment holds a list of Track Points which are logically connected in order. \n
/// To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data.
/// </summary>
public List<TrackSegment> segments;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
/// <summary>
/// Constructor
/// </summary>
public Track()
{
links = new List<Link>();
segments = new List<TrackSegment>();
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Track node</param>
public Track(RealWorldTerrainXML node) : this()
{
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "name") name = n.Value();
else if (n.name == "cmt") comment = n.Value();
else if (n.name == "desc") description = n.Value();
else if (n.name == "src") source = n.Value();
else if (n.name == "link") links.Add(new Link(n));
else if (n.name == "number") number = n.Value<uint>();
else if (n.name == "type") type = n.Value();
else if (n.name == "trkseg") segments.Add(new TrackSegment(n));
else if (n.name == "extensions") extensions = n;
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
if (!string.IsNullOrEmpty(name)) node.Create("name", name);
if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment);
if (!string.IsNullOrEmpty(description)) node.Create("desc", description);
if (!string.IsNullOrEmpty(source)) node.Create("src", source);
if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link"));
if (number.HasValue) node.Create("number", number.Value);
if (!string.IsNullOrEmpty(type)) node.Create("type", type);
foreach (TrackSegment p in segments) p.AppendToNode(node.Create("trkseg"));
if (extensions != null) node.AppendChild(extensions);
}
}
/// <summary>
/// A Track Segment holds a list of Track Points which are logically connected in order. \n
/// To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data.
/// </summary>
public class TrackSegment
{
/// <summary>
/// A Track Point holds the coordinates, elevation, timestamp, and metadata for a single point in a track.
/// </summary>
public List<Waypoint> points;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
/// <summary>
/// Constructor
/// </summary>
public TrackSegment()
{
points = new List<Waypoint>();
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">TrackSegment node</param>
public TrackSegment(RealWorldTerrainXML node) : this()
{
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "trkpt") points.Add(new Waypoint(n));
else if (n.name == "extensions") extensions = n;
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
foreach (Waypoint p in points) p.AppendToNode(node.Create("trkpt"));
if (extensions != null) node.AppendChild(extensions);
}
}
/// <summary>
/// Waypoint, point of interest, or named feature on a map.
/// </summary>
public class Waypoint
{
/// <summary>
/// Elevation (in meters) of the point.
/// </summary>
public double? elevation;
/// <summary>
/// Creation/modification timestamp for element. \n
/// Date and time in are in Univeral Coordinated Time (UTC), not local time! \n
/// Conforms to ISO 8601 specification for date/time representation. \n
/// Fractional seconds are allowed for millisecond timing in tracklogs.
/// </summary>
public DateTime? time;
/// <summary>
/// Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid. As defined in NMEA GGA message.
/// </summary>
public double? geoidheight;
/// <summary>
/// The GPS name of the waypoint. This field will be transferred to and from the GPS. \n
/// GPX does not place restrictions on the length of this field or the characters contained in it. \n
/// It is up to the receiving application to validate the field before sending it to the GPS.
/// </summary>
public string name;
/// <summary>
/// GPS waypoint comment. Sent to GPS as comment.
/// </summary>
public string comment;
/// <summary>
/// A text description of the element. Holds additional information about the element intended for the user, not the GPS.
/// </summary>
public string description;
/// <summary>
/// Source of data. Included to give user some idea of reliability and accuracy of data. "Garmin eTrex", "USGS quad Boston North", e.g.
/// </summary>
public string source;
/// <summary>
/// Link to additional information about the waypoint.
/// </summary>
public List<Link> links;
/// <summary>
/// Text of GPS symbol name. For interchange with other programs, use the exact spelling of the symbol as displayed on the GPS. If the GPS abbreviates words, spell them out.
/// </summary>
public string symbol;
/// <summary>
/// Type (classification) of the waypoint.
/// </summary>
public string type;
/// <summary>
/// Type of GPX fix.
/// </summary>
public string fix;
/// <summary>
/// Number of satellites used to calculate the GPX fix.
/// </summary>
public uint? sat;
/// <summary>
/// Horizontal dilution of precision.
/// </summary>
public double? hdop;
/// <summary>
/// Vertical dilution of precision.
/// </summary>
public double? vdop;
/// <summary>
/// Position dilution of precision.
/// </summary>
public double? pdop;
/// <summary>
/// Number of seconds since last DGPS update.
/// </summary>
public double? ageofdgpsdata;
/// <summary>
/// You can add extend GPX by adding your own elements from another schema here.
/// </summary>
public RealWorldTerrainXML extensions;
private double _lat;
private double _lon;
private double? _magvar;
private short? _dgpsid;
/// <summary>
/// The latitude of the point. Decimal degrees, WGS84 datum.
/// </summary>
public double lat
{
get { return _lat; }
set { _lat = RealWorldTerrainMath.Clip(value, -90, 90); }
}
/// <summary>
/// The longitude of the point. Decimal degrees, WGS84 datum.
/// </summary>
public double lon
{
get { return _lon; }
set { _lon = RealWorldTerrainMath.Repeat(value, -180, 180); }
}
/// <summary>
/// Magnetic variation (in degrees) at the point
/// </summary>
public double? magvar
{
get { return _magvar; }
set
{
if (value.HasValue) _magvar = RealWorldTerrainMath.Clip(value.Value, 0, 360);
else _magvar = null;
}
}
/// <summary>
/// ID of DGPS station used in differential correction.
/// </summary>
public short? dgpsid
{
get { return _dgpsid; }
set
{
if (value.HasValue)
{
if (value.Value < 0) _dgpsid = 0;
else if (value.Value > 1023) _dgpsid = 1023;
else _dgpsid = value.Value;
}
else _dgpsid = null;
}
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="lon">The longitude of the point.</param>
/// <param name="lat">The latitude of the point.</param>
public Waypoint(double lon, double lat)
{
links = new List<Link>();
this.lat = lat;
this.lon = lon;
}
/// <summary>
/// Creates instance and loads the data from the node.
/// </summary>
/// <param name="node">Waypoint node</param>
public Waypoint(RealWorldTerrainXML node)
{
links = new List<Link>();
lat = node.A<double>("lat");
lon = node.A<double>("lon");
foreach (RealWorldTerrainXML n in node)
{
if (n.name == "ele") elevation = n.Value<double>();
else if (n.name == "time") time = DateTime.Parse(n.Value());
else if (n.name == "magvar") magvar = n.Value<double>();
else if (n.name == "geoidheight") geoidheight = n.Value<double>();
else if (n.name == "name") name = n.Value();
else if (n.name == "cmt") comment = n.Value();
else if (n.name == "desc") description = n.Value();
else if (n.name == "src") source = n.Value();
else if (n.name == "link") links.Add(new Link(n));
else if (n.name == "sym") symbol = n.Value();
else if (n.name == "type") type = n.Value();
else if (n.name == "fix") fix = n.Value();
else if (n.name == "sat") sat = n.Value<uint>();
else if (n.name == "hdop") hdop = n.Value<double>();
else if (n.name == "vdop") vdop = n.Value<double>();
else if (n.name == "pdop") pdop = n.Value<double>();
else if (n.name == "ageofdgpsdata") ageofdgpsdata = n.Value<double>();
else if (n.name == "dgpsid") dgpsid = n.Value<short>();
else if (n.name == "extensions") extensions = n;
else Debug.Log(n.name);
}
}
public void AppendToNode(RealWorldTerrainXML node)
{
node.A("lat", lat);
node.A("lon", lon);
if (elevation.HasValue) node.Create("ele", elevation.Value);
if (time.HasValue) node.Create("time", time.Value.ToUniversalTime().ToString("s") + "Z");
if (magvar.HasValue) node.Create("magvar", magvar.Value);
if (geoidheight.HasValue) node.Create("geoidheight", geoidheight.Value);
if (!string.IsNullOrEmpty(name)) node.Create("name", name);
if (!string.IsNullOrEmpty(comment)) node.Create("cmt", comment);
if (!string.IsNullOrEmpty(description)) node.Create("desc", description);
if (!string.IsNullOrEmpty(source)) node.Create("src", source);
if (links != null) foreach (Link l in links) l.AppendToNode(node.Create("link"));
if (!string.IsNullOrEmpty(symbol)) node.Create("sym", symbol);
if (!string.IsNullOrEmpty(type)) node.Create("type", type);
if (!string.IsNullOrEmpty(fix)) node.Create("fix", fix);
if (sat.HasValue) node.Create("sat", sat.Value);
if (hdop.HasValue) node.Create("hdop", hdop.Value);
if (vdop.HasValue) node.Create("vdop", vdop.Value);
if (pdop.HasValue) node.Create("pdop", pdop.Value);
if (ageofdgpsdata.HasValue) node.Create("ageofdgpsdata", ageofdgpsdata.Value);
if (dgpsid.HasValue) node.Create("dgpsid", dgpsid.Value);
if (extensions != null) node.AppendChild(extensions);
}
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ee793fdc001b6e34eac523dff87845bc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,145 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Text;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Provides utility methods for geographic and Mercator coordinate conversions.
/// </summary>
public static class RealWorldTerrainGeo
{
/// <summary>
/// The radius of the Earth.
/// </summary>
public const float EARTH_RADIUS = 6371;
/// <summary>
/// The length of the equator.
/// </summary>
public const int EQUATOR_LENGTH = 40075;
/// <summary>
/// The maximum elevation in the world.
/// </summary>
public const int MAX_ELEVATION = 15000;
/// <summary>
/// Converts geographic coordinates to SRTM data index.
/// </summary>
/// <param name="pos">Geographic coordinates</param>
/// <returns>SRTM data index</returns>
public static Vector2 LanLongToFlat(Vector2 pos)
{
return new Vector2(Mathf.FloorToInt(pos.x / 5.0f) * 5 + 180, 90 - Mathf.FloorToInt(pos.y / 5.0f) * 5);
}
/// <summary>
/// Converts geographic coordinates to Mercator coordinates.
/// </summary>
/// <param name="x">Longitude</param>
/// <param name="y">Latitude</param>
public static void LatLongToMercat(ref double x, ref double y)
{
double sy = Math.Sin(y * RealWorldTerrainMath.DEG2RAD);
x = (x + 180) / 360;
y = 0.5 - Math.Log((1 + sy) / (1 - sy)) / (Math.PI * 4);
}
/// <summary>
/// Converts geographic coordinates to Mercator coordinates.
/// </summary>
/// <param name="x">Longitude</param>
/// <param name="y">Latitude</param>
/// <param name="mx">Output Mercator X</param>
/// <param name="my">Output Mercator Y</param>
public static void LatLongToMercat(double x, double y, out double mx, out double my)
{
double sy = Math.Sin(y * RealWorldTerrainMath.DEG2RAD);
mx = (x + 180) / 360;
my = 0.5 - Math.Log((1 + sy) / (1 - sy)) / (Math.PI * 4);
}
/// <summary>
/// Converts geographic coordinates to the index of the tile.
/// What is the tiles, and how it works, you can read here:
/// https://developers.google.com/maps/documentation/javascript/v2/overlays?csw=1#Google_Maps_Coordinates
/// </summary>
/// <param name="dx">Longitude</param>
/// <param name="dy">Latitude</param>
/// <param name="zoom">Zoom</param>
/// <param name="tx">Output tile X</param>
/// <param name="ty">Output tile Y</param>
public static void LatLongToTile(double dx, double dy, int zoom, out double tx, out double ty)
{
LatLongToMercat(ref dx, ref dy);
uint mapSize = (uint)RealWorldTerrainUtils.TILE_SIZE << zoom;
double px = RealWorldTerrainMath.Clamp(dx * mapSize + 0.5, 0, mapSize - 1);
double py = RealWorldTerrainMath.Clamp(dy * mapSize + 0.5, 0, mapSize - 1);
tx = px / RealWorldTerrainUtils.TILE_SIZE;
ty = py / RealWorldTerrainUtils.TILE_SIZE;
}
/// <summary>
/// Converts Mercator coordinates to geographic coordinates.
/// </summary>
/// <param name="mx">Mercator X</param>
/// <param name="my">Mercator Y</param>
/// <param name="x">Output longitude</param>
/// <param name="y">Output latitude</param>
public static void MercatToLatLong(double mx, double my, out double x, out double y)
{
uint mapSize = (uint)RealWorldTerrainUtils.TILE_SIZE << 20;
double px = RealWorldTerrainMath.Clamp(mx * mapSize + 0.5, 0, mapSize - 1);
double py = RealWorldTerrainMath.Clamp(my * mapSize + 0.5, 0, mapSize - 1);
mx = px / RealWorldTerrainUtils.TILE_SIZE;
my = py / RealWorldTerrainUtils.TILE_SIZE;
TileToLatLong(mx, my, 20, out x, out y);
}
/// <summary>
/// Converts tile coordinates to geographic coordinates.
/// </summary>
/// <param name="tx">Tile X</param>
/// <param name="ty">Tile Y</param>
/// <param name="zoom">Zoom level</param>
/// <param name="lx">Output longitude</param>
/// <param name="ly">Output latitude</param>
public static void TileToLatLong(double tx, double ty, int zoom, out double lx, out double ly)
{
double mapSize = RealWorldTerrainUtils.TILE_SIZE << zoom;
lx = 360 * (RealWorldTerrainMath.Repeat(tx * RealWorldTerrainUtils.TILE_SIZE, 0, mapSize - 1) / mapSize - 0.5);
ly = 90 - 360 * Math.Atan(Math.Exp(-(0.5 - RealWorldTerrainMath.Clamp(ty * RealWorldTerrainUtils.TILE_SIZE, 0, mapSize - 1) / mapSize) * 2 * Math.PI)) / Math.PI;
}
/// <summary>
/// Converts tile index to quadkey.
/// What is the tiles and quadkey, and how it works, you can read here:
/// http://msdn.microsoft.com/en-us/library/bb259689.aspx
/// </summary>
/// <param name="x">Tile X</param>
/// <param name="y">Tile Y</param>
/// <param name="zoom">Zoom</param>
/// <returns>Quadkey</returns>
public static string TileToQuadKey(int x, int y, int zoom)
{
StringBuilder quadKey = new StringBuilder();
for (int i = zoom; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((x & mask) != 0) digit++;
if ((y & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f03ad35cae8fd8f4e970507d8079dbf1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,584 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Provides utility methods for mathematical operations related to terrain generation.
/// </summary>
public static class RealWorldTerrainMath
{
/// <summary>
/// Degrees-to-radians conversion constant.
/// </summary>
public const double DEG2RAD = Math.PI / 180;
/// <summary>
/// PI * 4
/// </summary>
public const float PI4 = 4 * Mathf.PI;
/// <summary>
/// The angle between the two points in degree.
/// </summary>
/// <param name="point1">Point 1</param>
/// <param name="point2">Point 2</param>
/// <returns>Angle in degree</returns>
public static float Angle2D(Vector2 point1, Vector2 point2)
{
return Mathf.Atan2((point2.y - point1.y), (point2.x - point1.x)) * Mathf.Rad2Deg;
}
/// <summary>
/// The angle between the two points in degree.
/// </summary>
/// <param name="point1">Point 1</param>
/// <param name="point2">Point 2</param>
/// <returns>Angle in degree</returns>
public static float Angle2D(Vector3 point1, Vector3 point2)
{
return Mathf.Atan2((point2.z - point1.z), (point2.x - point1.x)) * Mathf.Rad2Deg;
}
/// <summary>
/// The angle between the three points in degree.
/// </summary>
/// <param name="point1">Point 1</param>
/// <param name="point2">Point 2</param>
/// <param name="point3">Point 3</param>
/// <param name="unsigned">Return a positive result.</param>
/// <returns>Angle in degree</returns>
public static float Angle2D(Vector3 point1, Vector3 point2, Vector3 point3, bool unsigned = true)
{
float angle1 = Angle2D(point1, point2);
float angle2 = Angle2D(point2, point3);
float angle = angle1 - angle2;
if (angle > 180) angle -= 360;
if (angle < -180) angle += 360;
if (unsigned) angle = Mathf.Abs(angle);
return angle;
}
/// <summary>
/// The angle between the two points in radians.
/// </summary>
/// <param name="point1">Point 1</param>
/// <param name="point2">Point 2</param>
/// <param name="offset">Result offset in degrees.</param>
/// <returns>Angle in radians</returns>
public static float Angle2DRad(Vector3 point1, Vector3 point2, float offset)
{
return Mathf.Atan2((point2.z - point1.z), (point2.x - point1.x)) + offset * Mathf.Deg2Rad;
}
/// <summary>
/// Clamps value between min and max and returns value.
/// </summary>
/// <param name="n">Value</param>
/// <param name="minValue">Min</param>
/// <param name="maxValue">Max</param>
/// <returns>Value in the range between the min and max.</returns>
public static double Clamp(double n, double minValue, double maxValue)
{
if (n < minValue) return minValue;
if (n > maxValue) return maxValue;
return n;
}
/// <summary>
/// Clamps a value between a minimum double and maximum double value.
/// </summary>
/// <param name="n">Value</param>
/// <param name="minValue">Minimum</param>
/// <param name="maxValue">Maximum</param>
/// <returns>Value between a minimum and maximum.</returns>
public static double Clip(double n, double minValue, double maxValue)
{
if (n < minValue) return minValue;
if (n > maxValue) return maxValue;
return n;
}
/// <summary>
/// The distance between two geographical coordinates.
/// </summary>
/// <param name="point1">Coordinate (X - Lng, Y - Lat)</param>
/// <param name="point2">Coordinate (X - Lng, Y - Lat)</param>
/// <returns>Distance (km).</returns>
public static Vector2 DistanceBetweenPoints(Vector2 point1, Vector2 point2)
{
Vector2 range = point1 - point2;
double scfY = Math.Sin(point1.y * Mathf.Deg2Rad);
double sctY = Math.Sin(point2.y * Mathf.Deg2Rad);
double ccfY = Math.Cos(point1.y * Mathf.Deg2Rad);
double cctY = Math.Cos(point2.y * Mathf.Deg2Rad);
double cX = Math.Cos(range.x * Mathf.Deg2Rad);
double sizeX1 = Math.Abs(RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(scfY * scfY + ccfY * ccfY * cX));
double sizeX2 = Math.Abs(RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(sctY * sctY + cctY * cctY * cX));
float sizeX = (float)((sizeX1 + sizeX2) / 2.0);
float sizeY = (float)(RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(scfY * sctY + ccfY * cctY));
return new Vector2(sizeX, sizeY);
}
/// <summary>
/// Calculates the distance between two geographical points.
/// </summary>
/// <param name="x1">Longitude of the first point</param>
/// <param name="y1">Latitude of the first point</param>
/// <param name="x2">Longitude of the second point</param>
/// <param name="y2">Latitude of the second point</param>
/// <param name="dx">Output distance in the x direction (longitude)</param>
/// <param name="dy">Output distance in the y direction (latitude)</param>
public static void DistanceBetweenPoints(double x1, double y1, double x2, double y2, out double dx, out double dy)
{
double rx = x1 - x2;
double scfY = Math.Sin(y1 * Mathf.Deg2Rad);
double sctY = Math.Sin(y2 * Mathf.Deg2Rad);
double ccfY = Math.Cos(y1 * Mathf.Deg2Rad);
double cctY = Math.Cos(y2 * Mathf.Deg2Rad);
double cX = Math.Cos(rx * Mathf.Deg2Rad);
double sizeX1 = Math.Abs(RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(scfY * scfY + ccfY * ccfY * cX));
double sizeX2 = Math.Abs(RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(sctY * sctY + cctY * cctY * cX));
dx = (sizeX1 + sizeX2) / 2.0;
dy = RealWorldTerrainGeo.EARTH_RADIUS * Math.Acos(scfY * sctY + ccfY * cctY);
}
/// <summary>
/// Calculates the center point and zoom level for a given set of geographic coordinates.
/// </summary>
/// <param name="positions">Array of geographic coordinates (longitude and latitude).</param>
/// <param name="center">Output center point of the geographic coordinates.</param>
/// <param name="zoom">Output zoom level that encompasses all the geographic coordinates.</param>
public static void GetCenterPointAndZoom(double[] positions, out Vector2 center, out int zoom)
{
double minX = Single.MaxValue;
double minY = Single.MaxValue;
double maxX = Single.MinValue;
double maxY = Single.MinValue;
for (int i = 0; i < positions.Length; i += 2)
{
double lng = positions[i];
double lat = positions[i + 1];
if (lng < minX) minX = lng;
if (lat < minY) minY = lat;
if (lng > maxX) maxX = lng;
if (lat > maxY) maxY = lat;
}
double rx = maxX - minX;
double ry = maxY - minY;
double cx = rx / 2 + minX;
double cy = ry / 2 + minY;
center = new Vector2((float)cx, (float)cy);
int width = 1024;
int height = 1024;
float countX = width / (float)RealWorldTerrainUtils.TILE_SIZE / 2;
float countY = height / (float)RealWorldTerrainUtils.TILE_SIZE / 2;
for (int z = 20; z > 4; z--)
{
bool success = true;
double tcx, tcy;
RealWorldTerrainGeo.LatLongToTile(cx, cy, z, out tcx, out tcy);
for (int i = 0; i < positions.Length; i += 2)
{
double lng = positions[i];
double lat = positions[i + 1];
double px, py;
RealWorldTerrainGeo.LatLongToTile(lng, lat, z, out px, out py);
px -= tcx - countX;
py -= tcy - countY;
if (px < 0 || py < 0 || px > width || py > height)
{
success = false;
break;
}
}
if (success)
{
zoom = z;
return;
}
}
zoom = 3;
}
/// <summary>
/// Calculates the center point and zoom level for a given set of geographic coordinates.
/// </summary>
/// <param name="positions">Array of geographic coordinates (longitude and latitude).</param>
/// <param name="center">Output center point of the geographic coordinates.</param>
/// <param name="zoom">Output zoom level that encompasses all the geographic coordinates.</param>
public static void GetCenterPointAndZoom(Vector2[] positions, out Vector2 center, out int zoom)
{
float minX = Single.MaxValue;
float minY = Single.MaxValue;
float maxX = Single.MinValue;
float maxY = Single.MinValue;
foreach (Vector2 p in positions)
{
if (p.x < minX) minX = p.x;
if (p.y < minY) minY = p.y;
if (p.x > maxX) maxX = p.x;
if (p.y > maxY) maxY = p.y;
}
float rx = maxX - minX;
float ry = maxY - minY;
double cx = rx / 2 + minX;
double cy = ry / 2 + minY;
center = new Vector2((float)cx, (float)cy);
int width = 1024;
int height = 1024;
float countX = width / (float)RealWorldTerrainUtils.TILE_SIZE / 2;
float countY = height / (float)RealWorldTerrainUtils.TILE_SIZE / 2;
for (int z = 20; z > 4; z--)
{
bool success = true;
double tcx, tcy;
RealWorldTerrainGeo.LatLongToTile(cx, cy, z, out tcx, out tcy);
foreach (Vector2 pos in positions)
{
double px, py;
RealWorldTerrainGeo.LatLongToTile(pos.x, pos.y, z, out px, out py);
px -= tcx - countX;
py -= tcy - countY;
if (px < 0 || py < 0 || px > width || py > height)
{
success = false;
break;
}
}
if (success)
{
zoom = z;
return;
}
}
zoom = 3;
}
/// <summary>
/// Calculates the intersection point of two lines in 2D space.
/// </summary>
/// <param name="p11">Start point of the first line.</param>
/// <param name="p12">End point of the first line.</param>
/// <param name="p21">Start point of the second line.</param>
/// <param name="p22">End point of the second line.</param>
/// <param name="state">Output state indicating the result of the calculation. -2: Not calculated yet, -1: Lines are parallel, 0: Lines are coincident, 1: Intersection point found.</param>
/// <returns>The intersection point if it exists, otherwise a default Vector2.</returns>
public static Vector2 GetIntersectionPointOfTwoLines(Vector2 p11, Vector2 p12, Vector2 p21, Vector2 p22,
out int state)
{
state = -2;
Vector2 result = new Vector2();
float m = (p22.x - p21.x) * (p11.y - p21.y) - (p22.y - p21.y) * (p11.x - p21.x);
float n = (p22.y - p21.y) * (p12.x - p11.x) - (p22.x - p21.x) * (p12.y - p11.y);
float Ua = m / n;
if (n == 0 && m != 0) state = -1;
else if (m == 0 && n == 0) state = 0;
else
{
result.x = p11.x + Ua * (p12.x - p11.x);
result.y = p11.y + Ua * (p12.y - p11.y);
if (((result.x >= p11.x || result.x <= p11.x) && (result.x >= p21.x || result.x <= p21.x))
&& ((result.y >= p11.y || result.y <= p11.y) && (result.y >= p21.y || result.y <= p21.y)))
state = 1;
}
return result;
}
/// <summary>
/// Calculates the intersection point of two lines in 3D space, projected onto the XZ plane.
/// </summary>
/// <param name="p11">Start point of the first line.</param>
/// <param name="p12">End point of the first line.</param>
/// <param name="p21">Start point of the second line.</param>
/// <param name="p22">End point of the second line.</param>
/// <param name="state">Output state indicating the result of the calculation. -2: Not calculated yet, -1: Lines are parallel, 0: Lines are coincident, 1: Intersection point found.</param>
/// <returns>The intersection point if it exists, otherwise a default Vector2.</returns>
public static Vector2 GetIntersectionPointOfTwoLines(Vector3 p11, Vector3 p12, Vector3 p21, Vector3 p22,
out int state)
{
return GetIntersectionPointOfTwoLines(new Vector2(p11.x, p11.z), new Vector2(p12.x, p12.z),
new Vector2(p21.x, p21.z), new Vector2(p22.x, p22.z), out state);
}
/// <summary>
/// Determines if three points in 3D space form a clockwise rotation when projected onto the XZ plane.
/// </summary>
/// <param name="A">First point</param>
/// <param name="B">Second point</param>
/// <param name="C">Third point</param>
/// <returns>True if the points form a clockwise rotation, false otherwise.</returns>
public static bool IsClockWise(Vector3 A, Vector3 B, Vector3 C)
{
return (B.x - A.x) * (C.z - A.z) - (C.x - A.x) * (B.z - A.z) > 0;
}
/// <summary>
/// Determines if a sequence of points in 3D space forms a clockwise rotation when projected onto the XZ plane.
/// </summary>
/// <param name="points">Array of points in 3D space.</param>
/// <param name="count">Number of points to consider from the start of the array.</param>
/// <returns>True if the points form a clockwise rotation, false otherwise.</returns>
public static bool IsClockwise(Vector3[] points, int count)
{
double sum = 0d;
for (int i = 0; i < count; i++)
{
Vector3 v1 = points[i];
Vector3 v2 = points[(i + 1) % count];
sum += (v2.x - v1.x) * (v2.z + v1.z);
}
return sum > 0d;
}
/// <summary>
/// Determines if a point is inside a polygon in 3D space, considering only the XZ plane.
/// </summary>
/// <param name="poly">Array of points forming the polygon.</param>
/// <param name="x">X coordinate of the point.</param>
/// <param name="y">Y coordinate of the point (considered as Z in 3D space).</param>
/// <returns>True if the point is inside the polygon, false otherwise.</returns>
public static bool IsPointInPolygon(Vector3[] poly, float x, float y)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Length - 1; i < poly.Length; j = i++)
{
if ((poly[i].z <= y && y < poly[j].z ||
poly[j].z <= y && y < poly[i].z) &&
x < (poly[j].x - poly[i].x) * (y - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x)
{
c = !c;
}
}
return c;
}
/// <summary>
/// Determines if a point is inside a polygon in 3D space, considering only the XZ plane.
/// </summary>
/// <param name="poly">Array of points forming the polygon.</param>
/// <param name="length">Number of points to consider from the start of the array.</param>
/// <param name="x">X coordinate of the point.</param>
/// <param name="y">Y coordinate of the point (considered as Z in 3D space).</param>
/// <returns>True if the point is inside the polygon, false otherwise.</returns>
public static bool IsPointInPolygon(Vector3[] poly, int length, float x, float y)
{
int i, j;
bool c = false;
for (i = 0, j = length - 1; i < length; j = i++)
{
if ((poly[i].z <= y && y < poly[j].z ||
poly[j].z <= y && y < poly[i].z) &&
x < (poly[j].x - poly[i].x) * (y - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x)
{
c = !c;
}
}
return c;
}
/// <summary>
/// Determines if a point is inside a polygon in 3D space, considering only the XZ plane.
/// </summary>
/// <param name="poly">List of points forming the polygon.</param>
/// <param name="x">X coordinate of the point.</param>
/// <param name="y">Y coordinate of the point (considered as Z in 3D space).</param>
/// <returns>True if the point is inside the polygon, false otherwise.</returns>
public static bool IsPointInPolygon(List<Vector3> poly, float x, float y)
{
int i, j;
bool c = false;
for (i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
if ((poly[i].z <= y && y < poly[j].z ||
poly[j].z <= y && y < poly[i].z) &&
x < (poly[j].x - poly[i].x) * (y - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x)
{
c = !c;
}
}
return c;
}
/// <summary>
/// Determines if a point is inside a polygon in 3D space, considering only the XZ plane.
/// </summary>
/// <param name="poly">List of points forming the polygon.</param>
/// <param name="point">Point to check.</param>
/// <returns>True if the point is inside the polygon, false otherwise.</returns>
public static bool IsPointInPolygon(List<Vector3> poly, Vector3 point)
{
return IsPointInPolygon(poly, point.x, point.z);
}
/// <summary>
/// Clamps a value between a minimum and maximum integer value.
/// </summary>
/// <param name="val">Value to be clamped.</param>
/// <param name="min">Minimum value. Default is 32.</param>
/// <param name="max">Maximum value. Default is 4096.</param>
/// <returns>Value clamped between the min and max.</returns>
public static int Limit(int val, int min = 32, int max = 4096)
{
return Mathf.Clamp(val, min, max);
}
/// <summary>
/// Clamps a value between a minimum and maximum integer value, ensuring the result is a power of two.
/// </summary>
/// <param name="val">Value to be clamped and adjusted to the nearest power of two.</param>
/// <param name="min">Minimum value. Default is 32.</param>
/// <param name="max">Maximum value. Default is 4096.</param>
/// <returns>Value clamped between the min and max, adjusted to the nearest power of two.</returns>
public static int LimitPowTwo(int val, int min = 32, int max = 4096)
{
return Mathf.Clamp(Mathf.ClosestPowerOfTwo(val), min, max);
}
/// <summary>
/// Calculates the nearest point on a line segment to a given point in 2D space.
/// </summary>
/// <param name="point">The point to find the nearest point on the line segment to.</param>
/// <param name="lineStart">The start point of the line segment.</param>
/// <param name="lineEnd">The end point of the line segment.</param>
/// <returns>The nearest point on the line segment to the given point.</returns>
public static Vector2 NearestPointStrict(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 fullDirection = lineEnd - lineStart;
Vector2 lineDirection = fullDirection.normalized;
float closestPoint = Vector2.Dot(point - lineStart, lineDirection) / Vector2.Dot(lineDirection, lineDirection);
return lineStart + Mathf.Clamp(closestPoint, 0, fullDirection.magnitude) * lineDirection;
}
/// <summary>
/// Repeats the value in the range from minValue to maxValue.
/// </summary>
/// <param name="n">The value to repeat.</param>
/// <param name="minValue">The minimum value in the range.</param>
/// <param name="maxValue">The maximum value in the range.</param>
/// <returns>The repeated value in the range from minValue to maxValue.</returns>
public static double Repeat(double n, double minValue, double maxValue)
{
if (double.IsInfinity(n) || double.IsInfinity(minValue) || double.IsInfinity(maxValue) || double.IsNaN(n) || double.IsNaN(minValue) || double.IsNaN(maxValue)) return n;
double range = maxValue - minValue;
while (n < minValue || n > maxValue)
{
if (n < minValue) n += range;
else if (n > maxValue) n -= range;
}
return n;
}
/// <summary>
/// Triangulates a polygon defined by a list of points in 2D space.
/// </summary>
/// <param name="points">List of points forming the polygon.</param>
/// <returns>An enumerable of indices representing the triangles that make up the polygon.</returns>
public static IEnumerable<int> Triangulate(List<Vector2> points)
{
List<int> indices = new List<int>();
int n = points.Count;
if (n < 3) return indices;
int[] V = new int[n];
if (TriangulateArea(points) > 0) for (int v = 0; v < n; v++) V[v] = v;
else for (int v = 0; v < n; v++) V[v] = (n - 1) - v;
int nv = n;
int count = 2 * nv;
for (int v = nv - 1; nv > 2;)
{
if ((count--) <= 0) return indices;
int u = v;
if (nv <= u) u = 0;
v = u + 1;
if (nv <= v) v = 0;
int w = v + 1;
if (nv <= w) w = 0;
if (TriangulateSnip(points, u, v, w, nv, V))
{
int s, t;
indices.Add(V[u]);
indices.Add(V[v]);
indices.Add(V[w]);
for (s = v, t = v + 1; t < nv; s++, t++) V[s] = V[t];
nv--;
count = 2 * nv;
}
}
indices.Reverse();
return indices;
}
private static float TriangulateArea(List<Vector2> points)
{
int n = points.Count;
float A = 0.0f;
for (int p = n - 1, q = 0; q < n; p = q++)
{
Vector2 pval = points[p];
Vector2 qval = points[q];
A += pval.x * qval.y - qval.x * pval.y;
}
return (A * 0.5f);
}
private static bool TriangulateInsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
float bp = (C.x - B.x) * (P.y - B.y) - (C.y - B.y) * (P.x - B.x);
float ap = (B.x - A.x) * (P.y - A.y) - (B.y - A.y) * (P.x - A.x);
float cp = (A.x - C.x) * (P.y - C.y) - (A.y - C.y) * (P.x - C.x);
return bp >= 0.0f && cp >= 0.0f && ap >= 0.0f;
}
private static bool TriangulateSnip(List<Vector2> points, int u, int v, int w, int n, int[] V)
{
Vector2 A = points[V[u]];
Vector2 B = points[V[v]];
Vector2 C = points[V[w]];
if (Mathf.Epsilon > (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x)) return false;
for (int p = 0; p < n; p++)
{
if (p == u || p == v || p == w) continue;
if (TriangulateInsideTriangle(A, B, C, points[V[p]])) return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37d0f9793343c564dbb97a853ede5f28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Xml;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Class points of interest.
/// </summary>
[Serializable]
public class RealWorldTerrainPOI
{
/// <summary>
/// The title of the POI.
/// </summary>
public string title;
/// <summary>
/// Longitude.
/// </summary>
public double x;
/// <summary>
/// Latitude.
/// </summary>
public double y;
/// <summary>
/// Altitude
/// </summary>
public float altitude;
public GameObject prefab;
public RealWorldTerrainPOI()
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="title">POI title.</param>
/// <param name="x">Longitude.</param>
/// <param name="y">Latitude.</param>
public RealWorldTerrainPOI(string title, double x, double y, float altitude = 0)
{
this.title = title;
this.x = x;
this.y = y;
this.altitude = altitude;
}
public RealWorldTerrainPOI(XmlNode node)
{
try
{
x = RealWorldTerrainXMLExt.GetAttribute<double>(node, "x");
y = RealWorldTerrainXMLExt.GetAttribute<double>(node, "y");
title = node.InnerText;
}
catch (Exception e)
{
Debug.Log(e.Message);
throw;
}
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 42d7e4b482738574e9653d37839d86d9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1 @@
/* Dummy file for correct asset update */

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3da24486d7c62e24db6750bc813201a4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Class of integer range.
/// </summary>
[Serializable]
public class RealWorldTerrainRangeI
{
/// <summary>
/// Minimum value.
/// </summary>
public int min = 1;
/// <summary>
/// Minimum limit.
/// </summary>
public int minLimit = int.MinValue;
/// <summary>
/// Maximum value.
/// </summary>
public int max = 50;
/// <summary>
/// Maximum limit.
/// </summary>
public int maxLimit = int.MaxValue;
public RealWorldTerrainRangeI()
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min">Minimum value.</param>
/// <param name="max">Maximum value.</param>
/// <param name="minLimit">Minimum limit.</param>
/// <param name="maxLimit">Maximum limit.</param>
public RealWorldTerrainRangeI(int min, int max, int minLimit = int.MinValue, int maxLimit = int.MaxValue)
{
this.min = min;
this.max = max;
this.minLimit = minLimit;
this.maxLimit = maxLimit;
}
/// <summary>
/// Sets new minimum and maximum values.
/// </summary>
/// <param name="min">New minimum value.</param>
/// <param name="max">New maximum value.</param>
public void Set(float min, float max)
{
this.min = Mathf.Max(minLimit, (int)min);
this.max = Mathf.Min(maxLimit, (int)max);
}
/// <summary>
/// Gets random integer between the minimum and maximum.
/// </summary>
/// <returns>Rendom integer value.</returns>
public int Random()
{
return UnityEngine.Random.Range(min, max);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 72aa9ff10863f064eb2b9aeb9a2e0026
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,237 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace InfinityCode.RealWorldTerrain.Utils
{
/// <summary>
/// Helper class for compatibility of reflection on WSA.
/// </summary>
public static class RealWorldTerrainReflectionHelper
{
private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
/// <summary>
/// Checks whether the type is anonymous.
/// </summary>
/// <param name="type">Type</param>
/// <returns>True - type is anonymous, false - otherwise</returns>
public static bool CheckIfAnonymousType(Type type)
{
if (type == null) throw new ArgumentNullException("type");
return IsGenericType(type)
&& (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType"))
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& (GetAttributes(type) & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
}
/// <summary>
/// Gets the attributes associated with the Type.
/// </summary>
/// <param name="type">Type</param>
/// <returns>Attributes of type.</returns>
public static TypeAttributes GetAttributes(Type type)
{
#if !NETFX_CORE
return type.Attributes;
#else
return type.GetTypeInfo().Attributes;
#endif
}
/// <summary>
/// Searches for the fields defined for the current Type, using the specified binding constraints.
/// </summary>
/// <param name="type">Type</param>
/// <param name="bindingAttr">A bitmask comprised of one or more BindingFlags that specify how the search is conducted.</param>
/// <returns>An array of FieldInfo objects representing all fields defined for the current Type that match the specified binding constraints.</returns>
public static IEnumerable<FieldInfo> GetFields(Type type, BindingFlags bindingAttr = DefaultLookup)
{
#if !NETFX_CORE
return type.GetFields(bindingAttr);
#else
return type.GetTypeInfo().DeclaredFields;
#endif
}
/// <summary>
/// Returns an array of Type objects that represent the type arguments of a generic type or the type parameters of a generic type definition.
/// </summary>
/// <param name="type">Type</param>
/// <returns>An array of Type objects that represent the type arguments of a generic type. Returns an empty array if the current type is not a generic type.</returns>
public static Type[] GetGenericArguments(Type type)
{
#if !NETFX_CORE
return type.GetGenericArguments();
#else
return type.GetTypeInfo().GenericTypeArguments;
#endif
}
/// <summary>
/// Searches for the public members with the specified name.
/// </summary>
/// <param name="type">Type</param>
/// <param name="name">The String containing the name of the public members to get. </param>
/// <returns>An array of MemberInfo objects representing the public members with the specified name, if found; otherwise, an empty array.</returns>
public static MemberInfo GetMember(Type type, string name)
{
#if !NETFX_CORE
MemberInfo[] infos = type.GetMember(name);
if (infos.Length > 0) return infos[0];
return null;
#else
IEnumerable<MemberInfo> members = type.GetTypeInfo().DeclaredMembers;
foreach (MemberInfo member in members) if (member.Name == name) return member;
return null;
#endif
}
/// <summary>
/// searches for the members defined for the current Type, using the specified binding constraints.
/// </summary>
/// <param name="type">Type</param>
/// <param name="bindingAttr">A bitmask comprised of one or more BindingFlags that specify how the search is conducted.</param>
/// <returns>An array of MemberInfo objects representing all members defined for the current Type that match the specified binding constraints.</returns>
public static IEnumerable<MemberInfo> GetMembers(Type type, BindingFlags bindingAttr = DefaultLookup)
{
#if !NETFX_CORE
return type.GetMembers(bindingAttr);
#else
return type.GetTypeInfo().DeclaredMembers;
#endif
}
/// <summary>
/// Gets a MemberTypes value indicating the type of the member — method, constructor, event, and so on.
/// </summary>
/// <param name="member">MemberInfo</param>
/// <returns>MemberTypes value</returns>
public static MemberTypes GetMemberType(MemberInfo member)
{
#if !NETFX_CORE
return member.MemberType;
#else
if (member is PropertyInfo) return MemberTypes.Property;
if (member is FieldInfo) return MemberTypes.Field;
if (member is MethodInfo) return MemberTypes.Method;
if (member is EventInfo) return MemberTypes.Event;
if (member is ConstructorInfo) return MemberTypes.Constructor;
return MemberTypes.All;
#endif
}
/// <summary>
/// Searches for the public method with the specified name.
/// </summary>
/// <param name="type">Type</param>
/// <param name="name">The String containing the name of the public method to get. </param>
/// <returns>A MethodInfo object representing the public method with the specified name, if found; otherwise, null.</returns>
public static MethodInfo GetMethod(Type type, string name)
{
#if !NETFX_CORE
return type.GetMethod(name);
#else
return type.GetTypeInfo().GetDeclaredMethod(name);
#endif
}
/// <summary>
/// Searches for the specified public method whose parameters match the specified argument types.
/// </summary>
/// <param name="type">Type</param>
/// <param name="name">The String containing the name of the public method to get. </param>
/// <param name="types">An array of Type objects representing the number, order, and type of the parameters for the method to get.</param>
/// <returns>A MethodInfo object representing the public method whose parameters match the specified argument types, if found; otherwise, null.</returns>
public static MethodInfo GetMethod(Type type, string name, Type[] types)
{
#if !NETFX_CORE
return type.GetMethod(name, types);
#else
var methods = type.GetTypeInfo().GetDeclaredMethods(name);
foreach(var m in methods)
{
var parms = m.GetParameters();
if (parms != null && parms.Length == types.Length && parms[0].ParameterType == typeof(string))
{
bool success = true;
for(int i = 0; i < parms.Length; i++)
{
if (parms[i].ParameterType != types[i])
{
success = false;
break;
}
}
if (success) return m;
}
}
return null;
#endif
}
/// <summary>
/// Returns all the public properties of the current Type.
/// </summary>
/// <param name="type">Type</param>
/// <returns>An array of PropertyInfo objects representing all public properties of the current Type.</returns>
public static PropertyInfo[] GetProperties(Type type)
{
#if !NETFX_CORE
return type.GetProperties();
#else
return type.GetTypeInfo().DeclaredProperties.ToArray();
#endif
}
/// <summary>
/// Gets a value indicating whether the Type is a class; that is, not a value type or interface.
/// </summary>
/// <param name="type">Type</param>
/// <returns>True if the Type is a class; otherwise, false.</returns>
public static bool IsClass(Type type)
{
#if !NETFX_CORE
return type.IsClass;
#else
return type.GetTypeInfo().IsClass;
#endif
}
/// <summary>
/// Gets a value indicating whether the current type is a generic type.
/// </summary>
/// <param name="type">Type</param>
/// <returns>True if the current type is a generic type; otherwise, false.</returns>
public static bool IsGenericType(Type type)
{
#if !NETFX_CORE
return type.IsGenericType;
#else
return type.GetTypeInfo().IsGenericType;
#endif
}
/// <summary>
/// Gets a value indicating whether the Type is a value type.
/// </summary>
/// <param name="type">Type</param>
/// <returns>True if the Type is a value type; otherwise, false.</returns>
public static bool IsValueType(Type type)
{
#if !NETFX_CORE
return type.IsValueType;
#else
return type.GetTypeInfo().IsValueType;
#endif
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 533ede9d19f4c9d4c8bb36cfbbc4bef2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,25 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
namespace InfinityCode.RealWorldTerrain
{
public struct RealWorldTerrainTimer
{
private long start;
public double seconds
{
get { return (DateTime.Now.Ticks - start) / 10000000d; }
}
public static RealWorldTerrainTimer Start()
{
return new RealWorldTerrainTimer
{
start = DateTime.Now.Ticks
};
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6fc56d722009cc04b8f15c2a299105e6
timeCreated: 1520680858
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,415 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
public class RealWorldTerrainTriangulator
{
private static void AddHole(List<Vector3> input, List<Vector3> hole)
{
if (hole == null || hole.Count < 3) return;
float closestDistance = float.MaxValue;
int closestIndex1 = -1;
int closestIndex2 = -1;
int holeCount = hole.Count;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minZ = float.MaxValue;
float maxZ = float.MinValue;
for (int i = 0; i < holeCount; i++)
{
Vector3 p = hole[i];
float px = p.x;
float pz = p.z;
if (px < minX) minX = px;
if (px > maxX) maxX = px;
if (pz < minZ) minZ = pz;
if (pz > maxZ) maxZ = pz;
}
float cx = (maxX + minX) / 2;
float cz = (maxZ + minZ) / 2;
for (int i = 0; i < input.Count; i++)
{
Vector3 p = input[i];
float px = p.x;
float pz = p.z;
float distance = (px - cx) * (px - cx) + (pz - cz) * (pz - cz);
if (distance < closestDistance)
{
closestDistance = distance;
closestIndex1 = i;
}
}
cx = input[closestIndex1].x;
cz = input[closestIndex1].z;
closestDistance = float.MaxValue;
for (int i = 0; i < holeCount; i++)
{
Vector3 p = hole[i];
float px = p.x;
float pz = p.z;
float distance = (px - cx) * (px - cx) + (pz - cz) * (pz - cz);
if (distance < closestDistance)
{
closestDistance = distance;
closestIndex2 = i;
}
}
int firstPartSize = holeCount - closestIndex2;
input.Insert(closestIndex1, input[closestIndex1]);
closestIndex1++;
input.InsertRange(closestIndex1, hole.Skip(closestIndex2).Take(firstPartSize));
input.InsertRange(closestIndex1 + firstPartSize, hole.Take(closestIndex2 + 1));
}
private static void AddHoles(List<Vector3> input, List<List<Vector3>> holes)
{
if (holes == null) return;
int holeVertices = 0;
foreach (List<Vector3> hole in holes)
{
if (hole == null || hole.Count < 3) continue;
holeVertices += hole.Count + 1;
}
if (input.Capacity < input.Count + holeVertices) input.Capacity = input.Count + holeVertices;
foreach (List<Vector3> hole in holes) AddHole(input, hole);
}
private static int[] GenerateTriangles(Point[] points, bool clockwise)
{
int count = points.Length;
int total = count;
int[] results = new int[(total - 2) * 3];
for (int i = 0; i < total; i++) points[i].UpdateWeight();
Sort(points, 0, count - 1);
for (int i = 0; i < total; i++) points[i].pindex = i;
int rindex = 0;
int start = 0;
int si = start;
while (count > 2)
{
bool cannotFindPoint = true;
for (int i = si; i < total; i++)
{
Point v = points[i];
if (v.isExternal) continue;
if (v.IsExternal(clockwise)) continue;
if (count > 4 && v.HasIntersections()) continue;
si = i + 1;
v.WriteToResult(results, ref rindex);
Point next = v.next;
Point prev = v.prev;
count--;
v.Dispose();
next.SetPrev(prev);
if (count > 3)
{
next.isExternal = prev.isExternal = false;
for (int j = i; j > start; j--)
{
Point o = points[j - 1];
points[j] = o;
o.pindex = j;
}
start++;
int nsi = UpdateWeight(points, prev, start, total);
if (si > nsi) si = nsi;
nsi = UpdateWeight(points, next, start, total);
if (si > nsi) si = nsi;
}
else
{
next.WriteToResult(results, ref rindex);
prev.Dispose();
next.Dispose();
count--;
}
cannotFindPoint = false;
break;
}
if (cannotFindPoint)
{
//Debug.Log("Triangulate Failed");
return null;
}
}
return results;
}
private static void Sort(Point[] points, int left, int right)
{
int i = left, j = right;
Point pivot = points[(left + right) / 2];
float weight = pivot.weight;
while (i <= j)
{
while (points[i].weight < weight)
{
i++;
}
while (points[j].weight > weight)
{
j--;
}
if (i <= j)
{
Point tmp = points[i];
points[i] = points[j];
points[j] = tmp;
i++;
j--;
}
}
if (left < j) Sort(points, left, j);
if (i < right) Sort(points, i, right);
}
public static int[] Triangulate(List<Vector2> input, List<List<Vector3>> holes = null, bool clockwise = true)
{
if (input == null) return null;
if (input.Count < 3) return null;
List<Vector3> input3 = input.Select(i => new Vector3(i.x, 0, i.y)).ToList();
return Triangulate(input3, holes, clockwise);
}
public static int[] Triangulate(List<Vector3> input, List<List<Vector3>> holes = null, bool clockwise = true)
{
if (input == null) return null;
if (input.Count < 3) return null;
AddHoles(input, holes);
int count = input.Count;
if (count == 3) return new[] { 0, 1, 2 };
if (count == 4) return new[] { 0, 1, 2, 0, 2, 3 };
Point[] points = new Point[count];
Point prev = null;
for (int i = 0; i < count; i++)
{
Point current = new Point(i, input[i]);
current.SetPrev(prev);
points[i] = current;
prev = current;
}
points[0].SetPrev(prev);
return GenerateTriangles(points, clockwise);
}
private static int UpdateWeight(Point[] points, Point point, int start, int total)
{
float oldWeight = point.weight;
point.UpdateWeight();
float newWeight = point.weight;
int index = point.pindex;
int i = index;
if (newWeight < oldWeight)
{
index--;
while (index >= start)
{
Point o = points[index];
if (o.weight < newWeight)
{
points[i] = point;
break;
}
points[i] = o;
o.pindex = i;
i = index;
index--;
}
if (i == start) points[i] = point;
}
else
{
index++;
while (index < total)
{
Point o = points[index];
if (o.weight > newWeight)
{
points[i] = point;
break;
}
points[i] = o;
o.pindex = i;
i = index;
index++;
}
if (index == total) points[i] = point;
}
point.pindex = i;
return i;
}
private class Point
{
public bool isExternal = false;
public Point next;
public Point prev;
public int pindex;
public float weight;
private int index;
private float x;
private float y;
public Point(int index, float x, float y)
{
weight = 0;
this.index = index;
this.x = x;
this.y = y;
}
public Point(int index, Vector3 p) : this(index, p.x, p.z)
{
}
public void Dispose()
{
next = null;
prev = null;
}
private bool EqualTo(Point p)
{
return Math.Abs(p.x - x) < float.Epsilon && Math.Abs(p.y - y) < float.Epsilon;
}
public bool HasIntersections()
{
Point p1 = prev;
Point p2 = next;
float rx = p2.x - p1.x;
float ry = p2.y - p1.y;
Point p3 = p2.next;
Point p4 = p3.next;
while (p4 != p1)
{
float d = (p4.y - p3.y) * rx - (p4.x - p3.x) * ry;
if (d > 0)
{
float u_a = (p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x);
float u_b = rx * (p1.y - p3.y) - ry * (p1.x - p3.x);
if (u_a >= 0 && u_a <= d && u_b >= 0 && u_b <= d)
{
if (!p1.EqualTo(p3) && !p1.EqualTo(p4) && !p2.EqualTo(p3) && !p2.EqualTo(p4))
{
return true;
}
}
}
p3 = p4;
p4 = p4.next;
}
return false;
}
public bool IsExternal(bool clockwise)
{
Point a = prev;
Point b = next;
isExternal = ((b.x - a.x) * (y - a.y) - (b.y - a.y) * (x - a.x) >= 0) ^ clockwise;
return isExternal;
}
public void SetPrev(Point other)
{
if (other == null) return;
prev = other;
other.next = this;
}
public override string ToString()
{
if (prev == null) return "Point i:" + index + ". Disposed";
return "Point i:" + index + ", p:" + prev.index + ", n:" + next.index + ", w:" + weight + ", pi:" + pindex;
}
public void UpdateWeight()
{
float p1x = prev.x;
float p1y = prev.y;
float p2x = next.x;
float p2y = next.y;
float ax = p1x - x;
float ay = p1y - y;
float bx = p2x - x;
float by = p2y - y;
float cx = p2x - p1x;
float cy = p2y - p1y;
float a = (float)Math.Sqrt(ax * ax + ay * ay);
float b = (float)Math.Sqrt(bx * bx + by * by);
float c = (float)Math.Sqrt(cx * cx + cy * cy);
float p = (a + b + c) / 2;
weight = p * (p - a) * (p - b) * (p - c);
}
public void WriteToResult(int[] results, ref int rindex)
{
results[rindex++] = index;
results[rindex++] = next.index;
results[rindex++] = prev.index;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d82d5f6b9292f0b47982f51a0c749135
timeCreated: 1524053217
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,208 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Text.RegularExpressions;
namespace InfinityCode.RealWorldTerrain
{
public static class RealWorldTerrainUTM
{
const double flattening = 298.257223563;
const double equatorialRadius = 6378137;
private static double DegToRad(double deg)
{
double pi = Math.PI;
return deg / 180 * pi;
}
private static double FootpointLatitude(double y)
{
const double r = equatorialRadius;
const double v1 = r * (1.0 - 1.0 / flattening);
const double x = (r - v1) / (r + v1);
double v2 = (r + v1) / 2.0 * (1.0 + Math.Pow(x, 2.0) / 4.0 + Math.Pow(x, 4.0) / 64.0);
double v3 = y / v2;
double v4 = 3.0 * x / 2.0 + -27.0 * Math.Pow(x, 3.0) / 32.0 + 269.0 * Math.Pow(x, 5.0) / 512.0;
double v5 = 21.0 * Math.Pow(x, 2.0) / 16.0 + -55.0 * Math.Pow(x, 4.0) / 32.0;
double v6 = 151.0 * Math.Pow(x, 3.0) / 96.0 + -417.0 * Math.Pow(x, 5.0) / 128.0;
double v7 = 1097.0 * Math.Pow(x, 4.0) / 512.0;
return v3 + v4 * Math.Sin(2.0 * v3) + v5 * Math.Sin(4.0 * v3) + v6 * Math.Sin(6.0 * v3) + v7 * Math.Sin(8.0 * v3);
}
private static void Geodetic_To_UPS(double lng, double lat, out string latZone, out int lngZone, out double easting, out double northing)
{
if (Math.Abs(lng + 180.0) < double.Epsilon) lng = 180.0;
double v1 = lat * Math.PI / 180.0;
double v2 = lng * Math.PI / 180.0;
const double x = 1.0 / flattening;
const double v3 = equatorialRadius * (1.0 - x);
double v4 = Math.Sqrt(2.0 * x - Math.Pow(x, 2.0));
double v5 = 2.0 * (equatorialRadius * equatorialRadius) / v3 * Math.Pow((1.0 - v4) / (1.0 + v4), v4 / 2.0) * Math.Tan(Math.PI / 4.0 - Math.Abs(v1) / 2.0) * Math.Pow((1.0 + v4 * Math.Sin(Math.Abs(v1))) / (1.0 - v4 * Math.Sin(Math.Abs(v1))), v4 / 2.0);
double v6 = 2000000.0 + 0.994 * v5 * Math.Sin(v2);
double v7 = lat <= 0.0 ? 2000000.0 + 0.994 * v5 * Math.Cos(v2) : 2000000.0 - 0.994 * v5 * Math.Cos(v2);
string str = lat < 0.0 ? (lng >= 0.0 || lng == -180.0 || lat == -90.0 ? "B" : "A") : (lng >= 0.0 || lng == -180.0 || lat == 90.0 ? "Z" : "Y");
easting = v6;
northing = v7;
lngZone = 0;
latZone = str;
}
private static double RadToDeg(double rad)
{
return rad / Math.PI * 180.0;
}
public static void ToLngLat(string latZone, int lngZone, double easting, double northing, out double lng, out double lat)
{
if (new Regex("[AaBbYyZz]").IsMatch(latZone))
{
UPS_To_Geodetic(latZone, easting, northing, out lng, out lat);
return;
}
bool isNorthHemisphere = new Regex("[CcDdEeFfGgHhJjKkLlMm]").IsMatch(latZone);
double v1 = easting - 500000.0;
const double v2 = 0.9996;
double x = v1 / v2;
if (isNorthHemisphere) northing -= 10000000.0;
UTMtoLatLong(x, northing / v2, UTMCentralMeridian(lngZone), out lng, out lat);
}
public static void ToUTM(double lng, double lat, out string latZone, out int lngZone, out double easting, out double northing)
{
if (lat < -80.0 || lat > 84.0)
{
Geodetic_To_UPS(lng, lat, out latZone, out lngZone, out easting, out northing);
return;
}
const double c0 = 0.9996;
const double c1 = Math.PI / 180.0;
const double c2 = 1.0 / flattening;
const double c3 = equatorialRadius * (1.0 - c2);
const double c4 = 1.0 - c3 / equatorialRadius * (c3 / equatorialRadius);
int v1 = (int)Math.Floor(lng / 6.0 + 31.0);
string str = lat >= -72.0 ? (lat >= -64.0 ? (lat >= -56.0 ? (lat >= -48.0 ? (lat >= -40.0 ? (lat >= -32.0 ? (lat >= -24.0 ? (lat >= -16.0 ? (lat >= -8.0 ? (lat >= 0.0 ? (lat >= 8.0 ? (lat >= 16.0 ? (lat >= 24.0 ? (lat >= 32.0 ? (lat >= 40.0 ? (lat >= 48.0 ? (lat >= 56.0 ? (lat >= 64.0 ? (lat >= 72.0 ? "X" : "W") : "V") : "U") : "T") : "S") : "R") : "Q") : "P") : "N") : "M") : "L") : "K") : "J") : "H") : "G") : "F") : "E") : "D") : "C";
double v2 = Math.Sqrt(1.0 - Math.Pow(c3, 2.0) / Math.Pow(equatorialRadius, 2.0));
double v3 = lat * c1;
double v4 = 3.0 + 6.0 * (1.0 + Math.Floor((lng + 180.0) / 6.0) - 1.0) - 180.0;
double v5 = v2 * v2 / (1.0 - Math.Pow(v2, 2.0));
double v6 = equatorialRadius / Math.Sqrt(1.0 - Math.Pow(v2 * Math.Sin(v3), 2.0));
double v7 = Math.Pow(Math.Tan(v3), 2.0);
double v8 = v5 * Math.Pow(Math.Cos(v3), 2.0);
double v9 = (lng - v4) * c1 * Math.Cos(v3);
double v10 = (v3 * (1.0 - c4 * (0.25 + c4 * (3.0 / 64.0 + 5.0 * c4 / 256.0))) - Math.Sin(2.0 * v3) * (c4 * (0.375 + c4 * (3.0 / 32.0 + 45.0 * c4 / 1024.0))) + Math.Sin(4.0 * v3) * (c4 * c4 * (15.0 / 256.0 + c4 * 45.0 / 1024.0)) - Math.Sin(6.0 * v3) * (c4 * c4 * c4 * 0.0113932291666667)) * equatorialRadius;
double v11 = c0 * v6 * v9 * (1.0 + v9 * v9 * ((1.0 - v7 + v8) / 6.0 + v9 * v9 * (5.0 - 18.0 * v7 + v7 * v7 + 72.0 * v8 - 58.0 * v5) / 120.0)) + 500000.0;
double v12 = c0 * (v10 + v6 * Math.Tan(v3) * (v9 * v9 * (0.5 + v9 * v9 * ((5.0 - v7 + 9.0 * v8 + 4.0 * v8 * v8) / 24.0 + v9 * v9 * (61.0 - 58.0 * v7 + v7 * v7 + 600.0 * v8 - 330.0 * v5) / 720.0))));
latZone = str;
lngZone = v1 == 61? 1: v1;
easting = v11;
northing = v12 < 0.0? 10000000.0 + v12: v12;
}
private static void UPS_To_Geodetic(string latZone, double easting, double northing, out double lng, out double lat)
{
const double x = 1.0 / flattening;
const double v1 = equatorialRadius * (1.0 - x);
double v2 = Math.Sqrt(2 * x - Math.Pow(x, 2));
double v3 = (easting - 2000000) / 0.994;
double v4 = (northing - 2000000) / 0.994;
double v5 = v4;
if (v4 == 0.0) v4 = 1;
bool flag = latZone.ToUpper() != "Z" && latZone.ToUpper() != "Y";
double d1;
double d2;
if (flag)
{
d1 = Math.PI + Math.Atan(v3 / v5);
d2 = Math.PI + Math.Atan(v3 / v4);
}
else
{
d1 = Math.PI - Math.Atan(v3 / v5);
d2 = Math.PI - Math.Atan(v3 / v4);
}
double v6 = 2.0 * Math.Pow(equatorialRadius, 2.0) / v1 * Math.Pow((1.0 - v2) / (1.0 + v2), v2 / 2.0);
double v7 = Math.Abs(v4);
double v8 = Math.Abs(Math.Cos(d2));
double v9 = v6 * v8;
double y = Math.Log(v7 / v9) / Math.Log(Math.E) * -1.0;
double v10 = 2.0 * Math.Atan(Math.Pow(Math.E, y)) - Math.PI / 2.0;
double d3;
double v11;
double v12;
for (d3 = 0.0; Math.Abs(v10 - d3) > 1E-07 && !double.IsInfinity(v10); v10 -= v11 / v12)
{
d3 = v10;
double d4 = (1.0 + Math.Sin(v10)) / (1.0 - Math.Sin(v10)) * Math.Pow((1.0 - v2 * Math.Sin(v10)) / (1.0 + v2 * Math.Sin(v10)), v2);
v11 = -y + 0.5 * Math.Log(d4);
v12 = (1.0 - Math.Pow(v2, 2.0)) / ((1.0 - Math.Pow(v2, 2.0) * Math.Pow(Math.Sin(v10), 2.0)) * Math.Cos(v10));
}
if (!double.IsInfinity(v10)) d3 = v10;
lat = !double.IsNaN(d3) ? d3 * (180.0 / Math.PI) : 90.0;
if (flag) lat *= -1.0;
lng = !double.IsNaN(d1) ? d1 * (180.0 / Math.PI) : 0.0;
if (easting < 2000000.0) lng = (180.0 - lng % 180.0) * -1.0;
else if (lng > 180.0) lng -= 180.0;
else if (lng < -180.0) lng += 180.0;
if (((northing < 2000000.0 ? 0 : v3 == 0.0 ? 1 : 0) & (flag ? 1 : 0)) != 0) lng = 0.0;
if (northing < 2000000.0 && v3 == 0.0 && !flag) lng = 0.0;
}
private static double UTMCentralMeridian(double zone)
{
return DegToRad(zone * 6 - 183);
}
private static void UTMtoLatLong(double x, double y, double zone, out double lng, out double lat)
{
const double r = equatorialRadius;
const double x2 = r * (1 - 1 / flattening);
const double v1 = (r * r - x2 * x2) / (x2 * x2);
double fpLat = FootpointLatitude(y);
double x3 = Math.Cos(fpLat);
double v2 = Math.Pow(x3, 2);
double v3 = v1 * v2;
double v4 = Math.Pow(r, 2) / (x2 * Math.Sqrt(v3 + 1));
double v5 = v4;
double v6 = Math.Tan(fpLat);
double v7 = v6 * v6;
double v8 = v7 * v7;
double v9 = 1 / (v5 * x3);
double v10 = v5 * v4;
double v11 = v6 / (2 * v10);
double v12 = v10 * v4;
double v13 = 1 / (6 * v12 * x3);
double v14 = v12 * v4;
double v15 = v6 / (24 * v14);
double v16 = v14 * v4;
double v17 = 1 / (120 * v16 * x3);
double v18 = v16 * v4;
double v19 = v6 / (720 * v18);
double v20 = v18 * v4;
double v21 = 1.0 / (5040 * v20 * x3);
double v22 = v6 / (40320 * (v20 * v4));
double v23 = -1 - v3;
double v24 = -1 - 2 * v7 - v3;
double v25 = 5 + 3 * v7 + 6 * v3 - 6 * v7 * v3 - 3 * (v3 * v3) - 9 * v7 * (v3 * v3);
double v26 = 5 + 28 * v7 + 24 * v8 + 6 * v3 + 8 * v7 * v3;
double v27 = -61 - 90 * v7 - 45 * v8 - 107 * v3 + 162 * v7 * v3;
double v28 = -61 - 662 * v7 - 1320 * v8 - 720 * (v8 * v7);
double v29 = 1385 + 3633 * v7 + 4095 * v8 + 1575 * (v8 * v7);
double rad1 = fpLat + v11 * v23 * (x * x) + v15 * v25 * Math.Pow(x, 4) + v19 * v27 * Math.Pow(x, 6) + v22 * v29 * Math.Pow(x, 8);
double rad2 = zone + v9 * x + v13 * v24 * Math.Pow(x, 3) + v17 * v26 * Math.Pow(x, 5) + v21 * v28 * Math.Pow(x, 7);
lat = RadToDeg(rad1);
lng = RadToDeg(rad2);
if (lat > 90) lat = 90;
else if (lat < -90) lat = -90;
if (lng > 180) lng = 180;
else if (lng < -180) lng = -180;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f164369da34861849a262c61b3be72b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,338 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using UnityEngine;
using Object = UnityEngine.Object;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// This class contains utility methods.
/// </summary>
public static class RealWorldTerrainUtils
{
/// <summary>
/// The average size of the texture of the tile.
/// </summary>
public const int AVERAGE_TEXTURE_SIZE = 20000;
/// <summary>
/// Maximum the size of the download for Google Maps.
/// </summary>
public const int DOWNLOAD_TEXTURE_LIMIT = 90000000;
/// <summary>
/// Size of tile.
/// </summary>
public const short TILE_SIZE = 256;
/// <summary>
/// Gets Hex value of Color.
/// </summary>
/// <param name="color">Color</param>
/// <returns>Hex value</returns>
public static string ColorToHex(Color32 color)
{
return color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2");
}
/// <summary>
/// Creates a new GameObject with the specified name, sets it as a child of the parent GameObject, and positions it at the origin.
/// </summary>
/// <param name="parent">The MonoBehaviour whose GameObject should be the parent of the new GameObject.</param>
/// <param name="name">The name of the new GameObject.</param>
/// <returns>The new GameObject.</returns>
public static GameObject CreateGameObject(MonoBehaviour parent, string name)
{
return CreateGameObject(parent.gameObject, name, Vector3.zero);
}
/// <summary>
/// Creates a new GameObject with the specified name, sets it as a child of the parent GameObject.
/// </summary>
/// <param name="parent">The GameObject that should be the parent of the new GameObject.</param>
/// <param name="name">The name of the new GameObject.</param>
/// <returns>The new GameObject.</returns>
public static GameObject CreateGameObject(GameObject parent, string name)
{
return CreateGameObject(parent, name, Vector3.zero);
}
/// <summary>
/// Creates a new GameObject with the specified name, sets it as a child of the parent GameObject, and positions it at the specified position.
/// </summary>
/// <param name="parent">The GameObject that should be the parent of the new GameObject.</param>
/// <param name="name">The name of the new GameObject.</param>
/// <param name="position">The local position of the new GameObject.</param>
/// <returns>The new GameObject.</returns>
public static GameObject CreateGameObject(GameObject parent, string name, Vector3 position)
{
GameObject container = new GameObject(name);
container.transform.parent = parent.transform;
container.transform.localPosition = position;
return container;
}
/// <summary>
/// Deletes a GameObject from the scene hierarchy.
/// </summary>
/// <param name="current">The parent transform from which to delete the GameObject.</param>
/// <param name="name">The name of the GameObject to delete.</param>
public static void DeleteGameObject(Transform current, string name)
{
for (int i = current.childCount - 1; i >= 0; i--)
{
Transform child = current.GetChild(i);
if (child.name == name) Object.DestroyImmediate(child.gameObject);
else DeleteGameObject(child, name);
}
}
/// <summary>
/// Exports a collection of MeshFilters to a file in the .obj format.
/// </summary>
/// <param name="filename">The name of the file to which the meshes will be exported.</param>
/// <param name="mfs">An array of MeshFilters whose meshes will be exported.</param>
/// <remarks>
/// This method exports the meshes of the provided MeshFilters to a file in the .obj format.
/// Each mesh is exported as a separate group, and the name of the group is the name of the GameObject to which the MeshFilter is attached.
/// The method also exports the normals and UVs of the meshes.
/// </remarks>
public static void ExportMesh(string filename, params MeshFilter[] mfs)
{
StringBuilder builder = new StringBuilder();
int nextNormalIndex = 0;
foreach (MeshFilter mf in mfs)
{
Mesh m = mf.sharedMesh;
Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
builder.Append("g ").Append(mf.name).Append("\n");
for (int i = 0; i < m.vertices.Length; i++)
{
Vector3 v = m.vertices[i];
builder.Append("v ").Append(v.x).Append(" ").Append(v.y).Append(" ").Append(v.z).Append("\n");
}
builder.Append("\n");
for (int i = 0; i < m.normals.Length; i++)
{
Vector3 v = m.normals[i];
builder.Append("vn ").Append(v.x).Append(" ").Append(v.y).Append(" ").Append(v.z).Append("\n");
}
builder.Append("\n");
for (int i = 0; i < m.uv.Length; i++)
{
Vector2 v = m.uv[i];
builder.Append("vt ").Append(v.x).Append(" ").Append(v.y).Append("\n");
}
for (int material = 0; material < m.subMeshCount; material++)
{
builder.Append("\nusemtl ").Append(mats[material].name).Append("\n");
builder.Append("usemap ").Append(mats[material].name).Append("\n");
int[] triangles = m.GetTriangles(material);
for (int i = 0; i < triangles.Length; i += 3)
{
int tni1 = triangles[i] + 1 + nextNormalIndex;
int tni2 = triangles[i + 1] + 1 + nextNormalIndex;
int tni3 = triangles[i + 2] + 1 + nextNormalIndex;
builder.Append("f ").Append(tni1).Append("/").Append(tni1).Append("/").Append(tni1);
builder.Append(" ").Append(tni2).Append("/").Append(tni2).Append("/").Append(tni2);
builder.Append(" ").Append(tni3).Append("/").Append(tni3).Append("/").Append(tni3).Append("\n");
}
}
builder.Append("\n");
nextNormalIndex += m.normals.Length;
}
#if !NETFX_CORE
StreamWriter stream = new StreamWriter(filename);
stream.Write(builder.ToString());
stream.Close();
#endif
}
/// <summary>
/// Calculates the bounding rectangle from a list of Vector3 points.
/// </summary>
/// <param name="points">A list of Vector3 points.</param>
/// <returns>A Rect structure that contains the smallest axis-aligned rectangle that can be drawn to encompass all the points in the list.</returns>
public static Rect GetRectFromPoints(List<Vector3> points)
{
return new Rect
{
x = points.Min(p => p.x),
y = points.Min(p => p.z),
xMax = points.Max(p => p.x),
yMax = points.Max(p => p.z)
};
}
/// <summary>
/// Finds an object of type T in the scene.
/// </summary>
/// <typeparam name="T">The type of the object to find. This type parameter should be a subclass of UnityEngine.Object.</typeparam>
/// <returns>The object of type T found in the scene, or null if no such object exists.</returns>
public static T FindObjectOfType<T>() where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object.FindFirstObjectByType<T>();
#else
return Object.FindObjectOfType<T>();
#endif
}
/// <summary>
/// Finds all objects of type T in the scene.
/// </summary>
/// <typeparam name="T">The type of the objects to find. This type parameter should be a subclass of UnityEngine.Object.</typeparam>
/// <returns>An array of objects of type T found in the scene. If no such objects exist, an empty array is returned.</returns>
public static T[] FindObjectsOfType<T>() where T : Object
{
#if UNITY_2023_1_OR_NEWER
return Object.FindObjectsByType<T>(FindObjectsSortMode.None);
#else
return Object.FindObjectsOfType<T>();
#endif
}
/// <summary>
/// Converts a hexadecimal color string to a Color object.
/// </summary>
/// <param name="hex">A string representing a color in hexadecimal format. The string should be 6 characters long and consist of pairs of hexadecimal digits representing the red, green, and blue components of the color, in that order.</param>
/// <returns>A Color object that represents the color specified by the hexadecimal string. The alpha component of the color is always set to 255.</returns>
public static Color HexToColor(string hex)
{
byte r = Byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
byte g = Byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
byte b = Byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
return new Color32(r, g, b, 255);
}
/// <summary>
/// Replaces all occurrences of specified strings within the original string with a new string.
/// </summary>
/// <param name="str">The original string.</param>
/// <param name="oldValues">An array of strings to be replaced.</param>
/// <param name="newValue">The string to replace all occurrences of the old values.</param>
/// <returns>A new string that is identical to the original string, except that all occurrences of strings in the oldValues array have been replaced by the newValue string.</returns>
public static string ReplaceString(string str, string[] oldValues, string newValue)
{
foreach (string oldValue in oldValues) str = str.Replace(oldValue, newValue);
return str;
}
/// <summary>
/// Replaces all occurrences of specified strings within the original string with corresponding new strings.
/// </summary>
/// <param name="str">The original string.</param>
/// <param name="oldValues">An array of strings to be replaced.</param>
/// <param name="newValues">An array of new strings that correspond to the old values. Each string in oldValues is replaced by the string at the same index in newValues.</param>
/// <returns>A new string that is identical to the original string, except that all occurrences of strings in the oldValues array have been replaced by the corresponding strings in the newValues array.</returns>
public static string ReplaceString(string str, string[] oldValues, string[] newValues)
{
for (int i = 0; i < oldValues.Length; i++) str = str.Replace(oldValues[i], newValues[i]);
return str;
}
/// <summary>
/// Removes a specified number of elements from a list at a specified position and returns them.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <param name="list">The list from which to remove elements.</param>
/// <param name="offset">The zero-based index at which to start removing elements.</param>
/// <param name="count">The number of elements to remove. The default value is 1.</param>
/// <returns>A new list that contains the removed elements.</returns>
public static List<T> SpliceList<T>(List<T> list, int offset, int count = 1)
{
List<T> newList = list.Skip(offset).Take(count).ToList();
list.RemoveRange(offset, count);
return newList;
}
/// <summary>
/// Converts a string representation of a color to a Color object.
/// </summary>
/// <param name="str">A string representing a color. This can be a named color (e.g. "red", "blue", "green", etc.) or a hexadecimal color string (e.g. "FF0000" for red).</param>
/// <returns>A Color object that represents the color specified by the string. If the string does not represent a valid color, Color.white is returned.</returns>
public static Color StringToColor(string str)
{
str = str.ToLower();
if (str == "black") return Color.black;
if (str == "blue") return Color.blue;
if (str == "cyan") return Color.cyan;
if (str == "gray") return Color.gray;
if (str == "green") return Color.green;
if (str == "magenta") return Color.magenta;
if (str == "red") return Color.red;
if (str == "white") return Color.white;
if (str == "yellow") return Color.yellow;
try
{
string hex = (str + "000000").Substring(1, 6);
byte[] cb =
Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
return new Color32(cb[0], cb[1], cb[2], 255);
}
catch
{
return Color.white;
}
}
#region Obsolete
[Obsolete("Use RealWorldTerrainGeo.LatLongToMercat instead.")]
public static void LatLongToMercat(ref double x, ref double y)
{
RealWorldTerrainGeo.LatLongToMercat(ref x, ref y);
}
[Obsolete("Use RealWorldTerrainGeo.LatLongToMercat instead.")]
public static void LatLongToMercat(double x, double y, out double mx, out double my)
{
RealWorldTerrainGeo.LatLongToMercat(x, y, out mx, out my);
}
[Obsolete("Use RealWorldTerrainGeo.LatLongToTile instead.")]
public static void LatLongToTile(double dx, double dy, int zoom, out double tx, out double ty)
{
RealWorldTerrainGeo.LatLongToTile(dx, dy, zoom, out tx, out ty);
}
[Obsolete("Use RealWorldTerrainGeo.MercatToLatLong instead.")]
public static void MercatToLatLong(double mx, double my, out double x, out double y)
{
RealWorldTerrainGeo.MercatToLatLong(mx, my, out x, out y);
}
[Obsolete("Use RealWorldTerrainGeo.TileToLatLong instead.")]
public static void TileToLatLong(double tx, double ty, int zoom, out double lx, out double ly)
{
RealWorldTerrainGeo.TileToLatLong(tx, ty, zoom, out lx, out ly);
}
[Obsolete("Use RealWorldTerrainGeo.TileToQuadKey instead.")]
public static string TileToQuadKey(int x, int y, int zoom)
{
return RealWorldTerrainGeo.TileToQuadKey(x, y, zoom);
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0ed211d619c02ff4591f4549a3153d59
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
/// <summary>
/// Integer version of Vector2 struct.
/// </summary>
[Serializable]
public struct RealWorldTerrainVector2i
{
/// <summary>
/// Gets the RealWorldTerrainVector2i where x=1 and y=1.
/// </summary>
public static RealWorldTerrainVector2i one
{
get
{
return new RealWorldTerrainVector2i(1, 1);
}
}
/// <summary>
/// The x value.
/// </summary>
public int x;
/// <summary>
/// The y value.
/// </summary>
public int y;
/// <summary>
/// Returns the count items (X * Y).
/// </summary>
public int count
{
get { return x * y; }
}
/// <summary>
/// Returns the maximum value of x and y.
/// </summary>
public int max
{
get { return Mathf.Max(x, y); }
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="X">X value.</param>
/// <param name="Y">Y value.</param>
public RealWorldTerrainVector2i(int X = 0, int Y = 0)
{
x = X;
y = Y;
}
public override string ToString()
{
return string.Format("X: {0}, Y: {1}", x, y);
}
public static implicit operator Vector2(RealWorldTerrainVector2i val)
{
return new Vector2(val.x, val.y);
}
public static implicit operator int(RealWorldTerrainVector2i val)
{
return val.count;
}
public static RealWorldTerrainVector2i operator +(RealWorldTerrainVector2i v1, RealWorldTerrainVector2i v2)
{
return new RealWorldTerrainVector2i(v1.x + v2.x, v1.y + v2.y);
}
public static RealWorldTerrainVector2i operator -(RealWorldTerrainVector2i v1, RealWorldTerrainVector2i v2)
{
return new RealWorldTerrainVector2i(v1.x - v2.x, v1.y - v2.y);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fff27cb1c0305c64fbb889633f1665cc
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,130 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
[Serializable]
public class RealWorldTerrainVectorTerrainLayerFeature
{
public const float TerrainLayerLineHeight = 20;
private static string[] _layerNames;
private static List<string> _landuseNames;
private static List<string> _landuseOverlayNames;
private static List<string> _structureNames;
private static List<string> _waterwayNames;
public List<TerrainLayer> terrainLayers;
public Vector2 noiseOffset = Vector2.zero;
public float noiseScale = 16;
public List<Rule> rules;
[NonSerialized]
private float? _height;
public float height
{
get
{
if (!_height.HasValue) UpdateHeight();
return _height.Value;
}
}
public static List<string> landuseNames
{
get
{
if (_landuseNames == null) _landuseNames = Enum.GetNames(typeof(RealWorldTerrainMapboxLanduse)).ToList();
return _landuseNames;
}
}
public static List<string> landuseOverlayNames
{
get
{
if (_landuseOverlayNames == null) _landuseOverlayNames = Enum.GetNames(typeof(RealWorldTerrainMapboxLanduseOverlay)).ToList();
return _landuseOverlayNames;
}
}
public static string[] layerNames
{
get
{
if (_layerNames == null) _layerNames = Enum.GetNames(typeof(RealWorldTerrainMapboxLayer));
return _layerNames;
}
}
public static List<string> structureNames
{
get
{
if (_structureNames == null) _structureNames = Enum.GetNames(typeof(RealWorldTerrainMapboxStructure)).ToList();
return _structureNames;
}
}
public static List<string> waterwayNames
{
get
{
if (_waterwayNames == null) _waterwayNames = Enum.GetNames(typeof(RealWorldTerrainMapboxWaterway)).ToList();
return _waterwayNames;
}
}
public void UpdateHeight()
{
int rows = 3;
if (terrainLayers == null) rows += 1;
else if (terrainLayers.Count == 1) rows += 1;
else rows += terrainLayers.Count + 2;
if (rules != null)
{
foreach (Rule rule in rules) rows += rule.hasExtra ? 3 : 2;
}
_height = rows * TerrainLayerLineHeight + 5;
}
[Serializable]
public class Rule
{
public RealWorldTerrainMapboxLayer layer = RealWorldTerrainMapboxLayer.building;
public int extra = ~0;
[NonSerialized]
private string _layerName;
public string layerName
{
get
{
if (_layerName == null) _layerName = layer.ToString();
return _layerName;
}
}
public bool hasExtra
{
get
{
return layer == RealWorldTerrainMapboxLayer.landuse_overlay ||
layer == RealWorldTerrainMapboxLayer.landuse ||
layer == RealWorldTerrainMapboxLayer.waterway ||
layer == RealWorldTerrainMapboxLayer.structure;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2875eab44961cd546b710b79a2c58428
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Globalization;
using System.Reflection;
using System.Xml;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
public static class RealWorldTerrainXMLExt
{
public static T GetAttribute<T>(XmlNode node, string name)
{
XmlAttribute attribute = node.Attributes[name];
if (attribute == null) return default(T);
string value = attribute.Value;
if (string.IsNullOrEmpty(value)) return default(T);
Type type = typeof(T);
if (type == typeof(string)) return (T)Convert.ChangeType(value, type);
T obj = default(T);
PropertyInfo[] properties = type.GetProperties();
Type underlyingType = type;
#if !UNITY_WSA
if (properties.Length == 2 && string.Equals(properties[0].Name, "HasValue", StringComparison.InvariantCultureIgnoreCase)) underlyingType = properties[1].PropertyType;
#else
if (properties.Length == 2 && string.Equals(properties[0].Name, "HasValue", StringComparison.OrdinalIgnoreCase)) underlyingType = properties[1].PropertyType;
#endif
try
{
MethodInfo method = underlyingType.GetMethod("Parse", new[] { typeof(string), typeof(IFormatProvider) });
if (method != null) obj = (T)method.Invoke(null, new object[] { value, RealWorldTerrainCultureInfo.numberFormat });
else
{
method = underlyingType.GetMethod("Parse", new[] { typeof(string) });
obj = (T)method.Invoke(null, new[] { value });
}
}
catch (Exception exception)
{
Debug.Log(exception.Message + "\n" + exception.StackTrace);
throw;
}
return obj;
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 06bbe1880a45585418e10c85791711b9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: