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,9 @@
fileFormatVersion: 2
guid: 4156a9b5bd1a63a4ea34c9b0d1c801d8
folderAsset: yes
timeCreated: 1470870903
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,169 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools.Base
{
public class RealWorldTerrainBaseContainerTool:EditorWindow
{
protected static RealWorldTerrainBaseContainerTool wnd;
public RealWorldTerrainMonoBase item;
protected Texture2D image;
protected Texture2D preview;
protected Color[] originalColors;
protected Color[] previewColors;
private Vector2 scrollPosition;
protected int countX
{
get
{
if (item == null) return 0;
return item is RealWorldTerrainContainer ? (item as RealWorldTerrainContainer).terrainCount.x : 1;
}
}
protected int countY
{
get
{
if (item == null) return 0;
return item is RealWorldTerrainContainer ? (item as RealWorldTerrainContainer).terrainCount.y : 1;
}
}
protected RealWorldTerrainItem[] terrains
{
get
{
if (item == null) return null;
return item is RealWorldTerrainContainer ? (item as RealWorldTerrainContainer).terrains : new[] { item as RealWorldTerrainItem };
}
}
protected virtual void Apply()
{
}
protected virtual Color ApplyFilters(Color color)
{
throw new Exception();
}
public void GetImage()
{
if (item == null)
{
image = null;
preview = null;
OnGetEmptyImage();
return;
}
image = GetImage(item, 512);
preview = new Texture2D(image.width, image.height);
originalColors = image.GetPixels();
previewColors = new Color[originalColors.Length];
UpdatePreview();
OnGetImageLate();
}
public static Texture2D GetImage(RealWorldTerrainMonoBase target, int imageWidth)
{
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
int imageHeight = Mathf.CeilToInt((float)imageWidth / cx * cy);
Texture2D texture = new Texture2D(imageWidth, imageHeight);
Color[] colors = new Color[imageWidth * imageHeight];
for (int y = 0; y < imageHeight; y++)
{
float py = (float)y / imageHeight * cy;
int ty = (int)py;
float fy = py - ty;
for (int x = 0; x < imageWidth; x++)
{
float px = (float)x / imageWidth * cx;
int tx = (int)px;
float fx = px - tx;
int tIndex = ty * cx + tx;
Texture2D terrainTexture = terrains[tIndex].texture;
if (terrainTexture == null) continue;
colors[y * imageWidth + x] = terrainTexture.GetPixelBilinear(fx, fy);
}
}
texture.SetPixels(colors);
texture.Apply();
return texture;
}
protected virtual void OnGetImageLate()
{
}
protected virtual void OnGetEmptyImage()
{
}
protected virtual void OnContentGUI()
{
}
private void OnDestroy()
{
wnd = null;
item = null;
image = null;
preview = null;
OnDestoyLate();
}
protected virtual void OnDestoyLate()
{
}
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
EditorGUI.BeginChangeCheck();
item = EditorGUILayout.ObjectField("Target: ", item, typeof (RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
if (EditorGUI.EndChangeCheck()) GetImage();
if (item == null) return;
OnContentGUI();
EditorGUILayout.EndScrollView();
if (GUILayout.Button("Apply")) Apply();
}
protected virtual void UpdatePreview()
{
for (int i = 0; i < originalColors.Length; i++) previewColors[i] = ApplyFilters(originalColors[i]);
preview.SetPixels(previewColors);
preview.Apply();
}
}
}

View File

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

View File

@@ -0,0 +1,130 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainAlphamapExporter : EditorWindow
{
private RealWorldTerrainMonoBase target;
private int layer;
private int depth = 16;
private RealWorldTerrainByteOrder order = RealWorldTerrainByteOrder.Windows;
private GUIContent[] depthLabels;
private int[] depthValues;
private void OnEnable()
{
depthLabels = new[] { new GUIContent("8"), new GUIContent("16") };
depthValues = new[] { 8, 16 };
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target: ", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
int maxLayer = !disabled ? terrains[0].terrainData.alphamapLayers : 0;
int minLayer = maxLayer > 0 ? 1 : 0;
layer = EditorGUILayout.IntSlider("Alphamap Layer", layer, minLayer, maxLayer);
depth = EditorGUILayout.IntPopup(new GUIContent("Depth"), depth, depthLabels, depthValues);
order = (RealWorldTerrainByteOrder)EditorGUILayout.EnumPopup("Byte Order", order);
EditorGUI.BeginDisabledGroup(maxLayer == 0);
if (GUILayout.Button("Export")) ExportAlphamap();
EditorGUI.EndDisabledGroup();
EditorGUI.EndDisabledGroup();
}
private void ExportAlphamap()
{
string filename = EditorUtility.SaveFilePanel("Export RAW Alphamap", Application.dataPath, String.Format("alphamap-layer-{0}.raw", layer), "raw");
if (string.IsNullOrEmpty(filename)) return;
int alphaResolution = -1;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
foreach (RealWorldTerrainItem terrain in terrains)
{
if (alphaResolution == -1) alphaResolution = terrain.terrainData.alphamapResolution;
else if (alphaResolution != terrain.terrainData.alphamapResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains have different alphamap resolution.", "OK");
return;
}
}
FileStream stream = new FileStream(filename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(stream);
int textureWidth = cx * alphaResolution;
int coof = depth == 8 ? 1 : 2;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int tIndex = y * cx + x;
float[,,] alpha = terrains[tIndex].terrainData.GetAlphamaps(0, 0, alphaResolution, alphaResolution);
for (int dy = 0; dy < alphaResolution; dy++)
{
float progress = ((y * cx + x) * alphaResolution + dy) / (float)(cx * cy * alphaResolution);
EditorUtility.DisplayProgressBar("Export RAW Alphamap", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = y * alphaResolution + dy;
int seek = (row * textureWidth + x * alphaResolution) * coof;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < alphaResolution; dx++)
{
if (depth == 8) writer.Write((byte)Mathf.RoundToInt(alpha[dy, dx, layer - 1] * 255));
else
{
short v = (short)Mathf.RoundToInt(alpha[dy, dx, layer - 1] * 65536);
if (order == RealWorldTerrainByteOrder.Windows) writer.Write(v);
else
{
writer.Write((byte)(v / 256));
writer.Write((byte)(v % 256));
}
}
}
}
}
}
stream.Close();
EditorUtility.ClearProgressBar();
EditorUtility.RevealInFinder(filename);
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainAlphamapExporter wnd = GetWindow<RealWorldTerrainAlphamapExporter>(true, "Alphamap Exporter");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,184 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainAlphamapImporter : EditorWindow
{
private RealWorldTerrainMonoBase target;
private int layer;
private int depth = 16;
private RealWorldTerrainByteOrder order = RealWorldTerrainByteOrder.Windows;
private GUIContent[] depthLabels;
private int[] depthValues;
private void OnEnable()
{
depthLabels = new[] { new GUIContent("8"), new GUIContent("16") };
depthValues = new[] { 8, 16 };
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target: ", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
int maxLayer = !disabled ? terrains[0].terrainData.alphamapLayers : 0;
int minLayer = maxLayer > 0 ? 1 : 0;
layer = EditorGUILayout.IntSlider("Alphamap layer", layer, minLayer, maxLayer);
depth = EditorGUILayout.IntPopup(new GUIContent("Depth"), depth, depthLabels, depthValues);
order = (RealWorldTerrainByteOrder)EditorGUILayout.EnumPopup("Byte Order", order);
EditorGUI.BeginDisabledGroup(maxLayer == 0);
if (GUILayout.Button("Import"))
{
ImportAlphamap();
}
EditorGUI.EndDisabledGroup();
EditorGUI.EndDisabledGroup();
}
private void ImportAlphamap()
{
string filename = EditorUtility.OpenFilePanel("Import RAW Alphamap", Application.dataPath, "raw");
if (string.IsNullOrEmpty(filename)) return;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
long fileSize = new FileInfo(filename).Length;
if (depth == 16) fileSize /= 2;
fileSize /= cx * cy;
int alphamapResolution = (int)Mathf.Sqrt(fileSize);
if (alphamapResolution * alphamapResolution != fileSize)
{
EditorUtility.DisplayDialog("Error", "Invalid file size.", "OK");
return;
}
if (Mathf.ClosestPowerOfTwo(alphamapResolution) != alphamapResolution)
{
EditorUtility.DisplayDialog("Error", "Invalid file size.", "OK");
return;
}
FileStream stream = new FileStream(filename, FileMode.Open);
int textureWidth = cx * alphamapResolution;
int coof = depth == 8 ? 1 : 2;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int tIndex = y * cx + x;
float[,,] alphamap = terrains[tIndex].terrainData.GetAlphamaps(0, 0, alphamapResolution, alphamapResolution);
int alphaCount = terrains[tIndex].terrainData.alphamapLayers;
for (int dy = 0; dy < alphamapResolution; dy++)
{
float progress = ((y * cx + x) * alphamapResolution + dy) / (float)(cx * cy * alphamapResolution);
EditorUtility.DisplayProgressBar("Import RAW Alphamap", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = y * alphamapResolution + dy;
int seek = (row * textureWidth + x * alphamapResolution) * coof;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < alphamapResolution; dx++)
{
float v;
if (depth == 8) v = alphamap[dy, dx, layer - 1] = stream.ReadByte() / 256f;
else
{
int b1 = stream.ReadByte();
int b2 = stream.ReadByte();
if (order == RealWorldTerrainByteOrder.Windows) v = alphamap[dy, dx, layer - 1] = (b2 * 256 + b1) / 65536f;
else v = alphamap[dy, dx, layer - 1] = (b1 * 256 + b2) / 65536f;
}
float other = 1 - v;
if (Math.Abs(v - 1) < 0.0001)
{
for (int l = 0; l < alphaCount; l++)
{
if (l != layer - 1)
{
alphamap[dy, dx, l] = 0;
}
}
}
else
{
float total = 0;
for (int l = 0; l < alphaCount; l++)
{
if (l == layer - 1) continue;
total += alphamap[dy, dx, l];
}
if (total > 0)
{
float scale = other / total;
for (int l = 0; l < alphaCount; l++)
{
if (l == layer) continue;
alphamap[dy, dx, l] *= scale;
}
}
else
{
for (int l = 0; l < alphaCount; l++)
{
if (l == layer) continue;
alphamap[dy, dx, l] = 1f / (alphaCount - 1);
}
}
}
}
}
if (terrains[tIndex].terrainData.alphamapResolution != alphamapResolution)
{
Vector3 size = terrains[tIndex].terrainData.size;
terrains[tIndex].terrainData.alphamapResolution = alphamapResolution;
terrains[tIndex].terrainData.size = size;
}
terrains[tIndex].terrainData.SetAlphamaps(0, 0, alphamap);
}
}
stream.Close();
EditorUtility.ClearProgressBar();
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainAlphamapImporter wnd = GetWindow<RealWorldTerrainAlphamapImporter>(true, "Alphamap Importer");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,132 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System.Collections.Generic;
using System.Linq;
using InfinityCode.RealWorldTerrain.OSM;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainBuildingManager : EditorWindow
{
private RealWorldTerrainOSMMeta[] buildings;
private Vector2 scrollPosition;
private string filter;
private FilterType filterType = FilterType.KeyAndValue;
private RealWorldTerrainOSMMeta[] filteredBuildings;
private int page = 0;
private void FilterBuildings()
{
if (string.IsNullOrEmpty(filter)) filteredBuildings = buildings;
else
{
string f = filter.ToUpperInvariant();
bool searchInKey = filterType != FilterType.Value;
bool searchInValue = filterType != FilterType.Key;
filteredBuildings = buildings.Where(b => b.ContainKeyOrValue(f, searchInKey, searchInValue)).ToArray();
}
}
private void OnEnable()
{
UpdateBuildings();
}
private void OnGUI()
{
EditorGUILayout.HelpBox("Filters buildings by tag (in whole or in part).", MessageType.Info);
EditorGUI.BeginChangeCheck();
filter = EditorGUILayout.TextField("Filter", filter);
filterType = (FilterType)EditorGUILayout.EnumPopup("Search in", filterType);
if (EditorGUI.EndChangeCheck()) FilterBuildings();
int pageSize = 500;
bool showPaginator = filteredBuildings.Length > pageSize;
if (!showPaginator) page = 0;
else if (page * pageSize >= filteredBuildings.Length) page = filteredBuildings.Length / pageSize;
int start = page * pageSize;
int end = Mathf.Min(start + pageSize, filteredBuildings.Length);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
bool hasNullItems = false;
for (int i = start; i < end; i++)
{
RealWorldTerrainOSMMeta building = filteredBuildings[i];
GUILayout.BeginHorizontal();
string buildingName = "";
if (building == null)
{
buildingName = "Missed";
hasNullItems = true;
}
else buildingName = building.name;
GUILayout.Label(buildingName);
if (GUILayout.Button(new GUIContent(">", "Select"), GUILayout.ExpandWidth(false)))
{
Selection.activeGameObject = building.gameObject;
}
GUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
if (showPaginator)
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginDisabledGroup(page == 0);
if (GUILayout.Button("<")) page--;
EditorGUI.EndDisabledGroup();
EditorGUILayout.LabelField((start + 1) + "-" + end + " from " + filteredBuildings.Length);
EditorGUI.BeginDisabledGroup(page == filteredBuildings.Length / pageSize);
if (GUILayout.Button(">")) page++;
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("Select All"))
{
Selection.objects = filteredBuildings.Select(b => b.gameObject).ToArray();
}
if (hasNullItems)
{
UpdateBuildings();
Repaint();
}
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Building Manager")]
public static void OpenWindow()
{
GetWindow<RealWorldTerrainBuildingManager>(true, "Building Manager");
}
private void UpdateBuildings()
{
buildings = RealWorldTerrainUtils.FindObjectsOfType<RealWorldTerrainBuilding>().Select(b => b.GetComponent<RealWorldTerrainOSMMeta>()).OrderBy(b => b.name).ToArray();
FilterBuildings();
}
public enum FilterType
{
KeyAndValue,
Key,
Value
}
}
}

View File

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

View File

@@ -0,0 +1,164 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainColorBalance : Base.RealWorldTerrainBaseContainerTool
{
private float cyanRed;
private float magentaGreen;
private float yellowBlue;
protected override void Apply()
{
int tick = 0;
int sx = countX;
int sy = countY;
for (int ty = 0; ty < sy; ty++)
{
for (int tx = 0; tx < sx; tx++)
{
int tIndex = ty * sy + tx;
Texture2D texture = terrains[tIndex].texture;
if (texture == null) continue;
Color[] colors = texture.GetPixels();
int cl = colors.Length;
for (int i = 0; i < cl; i++)
{
tick++;
if (tick >= 1000)
{
float progress = ((ty * sx + tx) * cl + i) / (float) (sx * sy * cl);
EditorUtility.DisplayProgressBar("Apply Color Ballance", Mathf.RoundToInt(progress * 100) + "%", progress);
tick = 0;
}
colors[i] = ApplyFilters(colors[i]);
}
Texture2D newTexture = new Texture2D(texture.width, texture.height);
newTexture.SetPixels(colors);
newTexture.Apply();
string path = AssetDatabase.GetAssetPath(texture);
File.WriteAllBytes(path, newTexture.EncodeToPNG());
}
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
Close();
EditorUtility.ClearProgressBar();
}
protected override Color ApplyFilters(Color color)
{
float r = 0;
float g = 0;
float b = 0;
if (cyanRed > 0) r += cyanRed / 256;
if (magentaGreen > 0) g += magentaGreen / 256;
if (yellowBlue > 0) b += yellowBlue / 256;
if (cyanRed < 0)
{
g += cyanRed / -512;
b += cyanRed / -512;
}
if (magentaGreen < 0)
{
r += magentaGreen / -512;
b += magentaGreen / -512;
}
if (yellowBlue < 0)
{
r += yellowBlue / -512;
g += yellowBlue / -512;
}
color.r = Mathf.Clamp01(color.r + r);
color.g = Mathf.Clamp01(color.g + g);
color.b = Mathf.Clamp01(color.b + b);
return color;
}
protected override void OnContentGUI()
{
EditorGUI.BeginChangeCheck();
bool reset = false;
EditorGUILayout.BeginHorizontal();
cyanRed = EditorGUILayout.Slider("Cyan / Red", cyanRed, -100, 100);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
cyanRed = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
magentaGreen = EditorGUILayout.Slider("Magenta / Green", magentaGreen, -100, 100);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
magentaGreen = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
yellowBlue = EditorGUILayout.Slider("Yellow / Blue", yellowBlue, -100, 100);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
yellowBlue = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
if (EditorGUI.EndChangeCheck() || reset) UpdatePreview();
if (image != null && preview != null)
{
int width = (int)position.width;
GUILayout.Box("", GUILayout.Height(width / 2), GUILayout.ExpandWidth(true));
Rect lastRect = GUILayoutUtility.GetLastRect();
if (Event.current.type != EventType.Layout)
{
float imgWidth = lastRect.width / 2 - 10;
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5, lastRect.y + 5, imgWidth, lastRect.height - 10), image, null, ScaleMode.ScaleToFit);
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5 + lastRect.width / 2, lastRect.y + 5, imgWidth, lastRect.height - 10), preview, null, ScaleMode.ScaleToFit);
}
}
}
public static void OpenWindow(RealWorldTerrainMonoBase item)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainColorBalance>(true, "Color Balance", true);
wnd.item = item;
wnd.GetImage();
}
protected override void UpdatePreview()
{
for (int i = 0; i < originalColors.Length; i++) previewColors[i] = ApplyFilters(originalColors[i]);
preview.SetPixels(previewColors);
preview.Apply();
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 94a3fa1b581867748a154b2674770e67
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 System.Xml;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
[Serializable]
public class RealWorldTerrainColorItem
{
public bool deleted;
private Color32 color;
private bool expanded = true;
private int rangeB = 10;
private int rangeG = 255;
private int rangeR = 10;
public RealWorldTerrainColorItem()
{
color = Color.red;
}
public bool EqualWithRange(Color32 clr)
{
int fr = Mathf.Abs(clr.r - color.r);
int fg = Mathf.Abs(clr.g - color.g);
int fb = Mathf.Abs(clr.b - color.b);
return fr < rangeR / 2f && fg < rangeG / 2f && fb < rangeB / 2f;
}
public XmlNode GetNode(XmlDocument doc)
{
XmlElement node = doc.CreateElement("Color");
node.SetAttribute("color", RealWorldTerrainUtils.ColorToHex(color));
node.SetAttribute("range", rangeR + "," + rangeG + "," + rangeB);
return node;
}
public void OnGUI(int index)
{
GUILayout.BeginHorizontal();
expanded = EditorGUILayout.Foldout(expanded, "Color " + index + ": ");
color = EditorGUILayout.ColorField(color);
if (GUILayout.Button(new GUIContent(RealWorldTerrainResources.deleteIcon, "Remove color"), GUIStyle.none,
GUILayout.Width(24), GUILayout.Height(20)))
deleted = true;
GUILayout.EndHorizontal();
if (expanded)
{
rangeR = EditorGUILayout.IntSlider("Range red: ", rangeR, 0, 255);
rangeG = EditorGUILayout.IntSlider("Range green: ", rangeG, 0, 255);
rangeB = EditorGUILayout.IntSlider("Range blue: ", rangeB, 0, 255);
}
}
public void SetNode(XmlElement node)
{
color = RealWorldTerrainUtils.HexToColor(node.GetAttribute("color"));
string range = node.GetAttribute("range");
string[] rs = range.Split(',');
rangeR = int.Parse(rs[0]);
rangeG = int.Parse(rs[1]);
rangeB = int.Parse(rs[2]);
}
}
}

View File

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

View File

@@ -0,0 +1,177 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainColorLevels : Base.RealWorldTerrainBaseContainerTool
{
private Texture2D levels;
private float minLevel = 0;
private float maxLevel = 1;
private float center = 0.5f;
private float minInput = 0;
private float maxInput = 1;
protected override void Apply()
{
int tick = 0;
int sx = countX;
int sy = countY;
for (int ty = 0; ty < sy; ty++)
{
for (int tx = 0; tx < sx; tx++)
{
int tIndex = ty * sx + tx;
Texture2D texture = terrains[tIndex].texture;
if (texture == null) continue;
Color[] colors = texture.GetPixels();
int cl = colors.Length;
for (int i = 0; i < cl; i++)
{
tick++;
if (tick >= 1000)
{
float progress = ((ty * sx + tx) * cl + i) / (float)(sx * sy * cl);
EditorUtility.DisplayProgressBar("Apply Color Levels", Mathf.RoundToInt(progress * 100) + "%", progress);
tick = 0;
}
colors[i] = ApplyFilters(colors[i]);
}
Texture2D newTexture = new Texture2D(texture.width, texture.height);
newTexture.SetPixels(colors);
newTexture.Apply();
string path = AssetDatabase.GetAssetPath(texture);
File.WriteAllBytes(path, newTexture.EncodeToPNG());
}
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
Close();
EditorUtility.ClearProgressBar();
}
protected override Color ApplyFilters(Color color)
{
float g = color.grayscale;
float ng = Mathf.Clamp01((g - minLevel) / (maxLevel - minLevel));
ng = (ng + 0.5f) * (1.5f - center) - 0.5f;
float scale = ng / g;
color.r = (Mathf.Clamp01(color.r * scale) - minInput) * (maxInput - minInput);
color.g = (Mathf.Clamp01(color.g * scale) - minInput) * (maxInput - minInput);
color.b = (Mathf.Clamp01(color.b * scale) - minInput) * (maxInput - minInput);
return color;
}
protected override void OnDestoyLate()
{
levels = null;
}
protected override void OnContentGUI()
{
EditorGUI.BeginChangeCheck();
GUILayout.Box(levels, GUILayout.Height(260), GUILayout.ExpandWidth(true));
Rect lastRect = GUILayoutUtility.GetLastRect();
if (Event.current.type != EventType.Layout)
{
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 2, lastRect.y + 2, lastRect.width - 4, lastRect.height - 4), levels, null, ScaleMode.StretchToFill);
}
EditorGUILayout.MinMaxSlider(ref minLevel, ref maxLevel, 0, 1);
center = EditorGUILayout.Slider(center, 0.01f, 0.99f);
EditorGUILayout.MinMaxSlider(new GUIContent(string.Format("Output Colors ({0}-{1})", Mathf.RoundToInt(minInput * 256), Mathf.RoundToInt(maxInput * 256))), ref minInput, ref maxInput, 0, 1);
if (EditorGUI.EndChangeCheck()) UpdatePreview();
if (image != null && preview != null)
{
int width = (int)position.width;
GUILayout.Box("", GUILayout.Height(width / 2), GUILayout.ExpandWidth(true));
lastRect = GUILayoutUtility.GetLastRect();
if (Event.current.type != EventType.Layout)
{
float imgWidth = lastRect.width / 2 - 10;
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5, lastRect.y + 5, imgWidth, lastRect.height - 10), image, null, ScaleMode.ScaleToFit);
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5 + lastRect.width / 2, lastRect.y + 5, imgWidth, lastRect.height - 10), preview, null, ScaleMode.ScaleToFit);
}
}
}
protected override void OnGetEmptyImage()
{
levels = null;
}
protected override void OnGetImageLate()
{
UpdateLevels();
}
public static void OpenWindow(RealWorldTerrainMonoBase item)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainColorLevels>(true, "Color Levels", true);
wnd.item = item;
wnd.GetImage();
}
private void UpdateLevels()
{
int[] lR = new int[256];
int[] lG = new int[256];
int[] lB = new int[256];
int[] lGr = new int[256];
levels = new Texture2D(512, 256);
for (int i = 0; i < originalColors.Length; i++)
{
Color32 clr = originalColors[i];
lR[clr.r]++;
lG[clr.g]++;
lB[clr.b]++;
lGr[Mathf.RoundToInt(originalColors[i].grayscale * 255)]++;
}
Color[] levelColors = new Color[512 * 256];
for (int i = 0; i < 512 * 256; i++) levelColors[i] = Color.white;
int maxValue = lGr.Max();
float scale = 256f / maxValue;
for (int x = 0; x < 256; x++)
{
int ty = Mathf.RoundToInt(lGr[x] * scale);
for (int y = 0; y < ty; y++)
{
levelColors[y * 512 + x * 2] = Color.black;
levelColors[y * 512 + x * 2 + 1] = Color.black;
}
}
levels.SetPixels(levelColors);
levels.Apply();
}
}
}

View File

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

View File

@@ -0,0 +1,90 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainCurrentLatLon : EditorWindow
{
private RealWorldTerrainContainer rwt;
private Vector3 lastCursorPosition;
private static RealWorldTerrainCurrentLatLon wnd;
private void OnDestroy()
{
EditorApplication.update -= OnUpdate;
SceneView.duringSceneGui -= OnSceneGUI;
wnd = null;
}
private void OnEnable()
{
OnDestroy();
wnd = this;
EditorApplication.update += OnUpdate;
SceneView.duringSceneGui += OnSceneGUI;
}
private void OnGUI()
{
rwt = (RealWorldTerrainContainer)EditorGUILayout.ObjectField("Real World Terrain", rwt, typeof(RealWorldTerrainContainer), true);
if (rwt == null) return;
SceneView view = SceneView.lastActiveSceneView;
if (view == null) return;
Vector3 cp = view.camera.transform.position;
double longitude, latitude, altitude;
rwt.GetCoordinatesByWorldPosition(cp, out longitude, out latitude, out altitude);
EditorGUILayout.LabelField("Scene camera latitude: " + latitude);
EditorGUILayout.LabelField("Scene camera longitude: " + longitude);
EditorGUILayout.LabelField("Scene camera altitude: " + altitude);
if (lastCursorPosition == Vector3.zero) return;
rwt.GetCoordinatesByWorldPosition(lastCursorPosition, out longitude, out latitude, out altitude);
EditorGUILayout.LabelField("Scene cursor latitude: " + latitude);
EditorGUILayout.LabelField("Scene cursor longitude: " + longitude);
EditorGUILayout.LabelField("Scene cursor altitude: " + altitude.ToString("F2") + " meters");
}
private void OnSceneGUI(SceneView view)
{
RaycastHit hit;
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hit)) lastCursorPosition = hit.point;
else lastCursorPosition = Vector3.zero;
}
private void OnUpdate()
{
Repaint();
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Current Position")]
public static void OpenWindow()
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainCurrentLatLon>(false, "Current Position");
wnd.rwt = RealWorldTerrainUtils.FindObjectOfType<RealWorldTerrainContainer>();
}
public static void OpenWindow(RealWorldTerrainContainer container)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainCurrentLatLon>(false, "Current Position");
wnd.rwt = container;
}
}
}

View File

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

View File

@@ -0,0 +1,111 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainDetailmapExporter:EditorWindow
{
private RealWorldTerrainMonoBase target;
private int layer;
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target: ", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
int maxLayer = !disabled ? terrains[0].terrainData.detailPrototypes.Length : 0;
int minLayer = maxLayer > 0 ? 1 : 0;
layer = EditorGUILayout.IntSlider("Detail layer", layer, minLayer, maxLayer);
EditorGUI.BeginDisabledGroup(maxLayer == 0);
if (GUILayout.Button("Export"))
{
ExportDetailmap();
}
EditorGUI.EndDisabledGroup();
EditorGUI.EndDisabledGroup();
}
private void ExportDetailmap()
{
string filename = EditorUtility.SaveFilePanel("Export RAW Detail map", Application.dataPath, String.Format("detailmap-layer-{0}.raw", layer), "raw");
if (string.IsNullOrEmpty(filename)) return;
int detailResolution = -1;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
foreach (RealWorldTerrainItem terrain in terrains)
{
if (detailResolution == -1) detailResolution = terrain.terrainData.detailResolution;
else if (detailResolution != terrain.terrainData.detailResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains have different detail map resolution.", "OK");
return;
}
}
FileStream stream = new FileStream(filename, FileMode.Create);
int textureWidth = cx * detailResolution;
int textureHeight = cy * detailResolution;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int tIndex = y * cx + x;
int[,] detailLayer = terrains[tIndex].terrainData.GetDetailLayer(0, 0, detailResolution, detailResolution, layer - 1);
for (int dy = 0; dy < detailResolution; dy++)
{
float progress = ((y * cx + x) * detailResolution + dy) / (float) (cx * cy * detailResolution);
EditorUtility.DisplayProgressBar("Export RAW Detail map", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = textureHeight - y * detailResolution - dy - 1;
int seek = (row * textureWidth + x * detailResolution) * 3;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < detailResolution; dx++)
{
byte v = (byte)detailLayer[dy, dx];
stream.WriteByte(v);
stream.WriteByte(v);
stream.WriteByte(v);
}
}
}
}
stream.Close();
EditorUtility.ClearProgressBar();
EditorUtility.RevealInFinder(filename);
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainDetailmapExporter wnd = GetWindow<RealWorldTerrainDetailmapExporter>(true, "Detail map Exporter");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,110 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainDetailmapImporter:EditorWindow
{
private RealWorldTerrainMonoBase target;
private int layer;
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target: ", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
int maxLayer = !disabled ? terrains[0].terrainData.detailPrototypes.Length : 0;
int minLayer = maxLayer > 0 ? 1 : 0;
layer = EditorGUILayout.IntSlider("Detail layer", layer, minLayer, maxLayer);
EditorGUI.BeginDisabledGroup(maxLayer == 0);
if (GUILayout.Button("Import"))
{
ImportDetailmap();
}
EditorGUI.EndDisabledGroup();
EditorGUI.EndDisabledGroup();
}
private void ImportDetailmap()
{
string filename = EditorUtility.OpenFilePanel("Import RAW Detailmap", Application.dataPath, "raw");
if (string.IsNullOrEmpty(filename)) return;
int detailResolution = -1;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
foreach (RealWorldTerrainItem terrain in terrains)
{
if (detailResolution == -1) detailResolution = terrain.terrainData.detailResolution;
else if (detailResolution != terrain.terrainData.detailResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains have different detailmap resolution.", "OK");
return;
}
}
FileStream stream = new FileStream(filename, FileMode.Open);
int textureWidth = cx * detailResolution;
int textureHeight = cy * detailResolution;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int[,] detailLayer = new int[detailResolution, detailResolution];
for (int dy = 0; dy < detailResolution; dy++)
{
float progress = ((y * cx + x) * detailResolution + dy) / (float)(cx * cy * detailResolution);
EditorUtility.DisplayProgressBar("Import RAW Detailmap", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = textureHeight - y * detailResolution - dy - 1;
int seek = (row * textureWidth + x * detailResolution) * 3;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < detailResolution; dx++)
{
int r = stream.ReadByte();
int g = stream.ReadByte();
int b = stream.ReadByte();
detailLayer[dy, dx] = (r + g + b) / 3;
}
}
int tIndex = y * cx + x;
terrains[tIndex].terrainData.SetDetailLayer(0, 0, layer - 1, detailLayer);
}
}
stream.Close();
EditorUtility.ClearProgressBar();
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainDetailmapImporter wnd = GetWindow<RealWorldTerrainDetailmapImporter>(true, "Detailmap Importer");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,260 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainErosionFilter : EditorWindow
{
struct Neighbour
{
public float decline;
public int x;
public int y;
}
private RealWorldTerrainMonoBase target;
private int iterations = 15;
private float rainfall = 1.0f;
private float coneThreshold = 2.0f;
private int flowIterations = 100;
private Vector2 scrollPosition;
private Neighbour[] neighbours = new Neighbour[8];
private float[,] heightmap;
private float[,] watermap;
private float[,] sedimentmap;
private int width;
private int height;
private void AddSediment(int x, int y)
{
float v = sedimentmap[y, x];
float sign = Mathf.Sign(v);
float absVal = Mathf.Abs(v);
float threshold = 2e-4f * coneThreshold;
if (absVal < threshold)
{
heightmap[y, x] += sedimentmap[y, x];
return;
}
float radius = absVal / threshold;
radius = Mathf.Sqrt(radius);
int r = Mathf.CeilToInt(radius) - 1;
for (int oy = -r; oy <= r; oy++)
{
int cy = y + oy;
if (cy < 0 || cy >= height) continue;
for (int ox = -r; ox <= r; ox++)
{
int cx = x + ox;
if (cx < 0 || cx >= width) continue;
float normDiff = 1 - Mathf.Sqrt(oy * oy + ox * ox) / radius;
if (normDiff < 0) normDiff = 0;
else if (normDiff > 1) normDiff = 1;
heightmap[cy, cx] += threshold * sign * normDiff;
}
}
}
private void Apply()
{
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
int heightmapResolution = -1;
foreach (RealWorldTerrainItem terrain in terrains)
{
if (heightmapResolution == -1) heightmapResolution = terrain.terrainData.heightmapResolution;
else if (heightmapResolution != terrain.terrainData.heightmapResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains have different heightmap resolution.", "OK");
return;
}
}
width = cx * heightmapResolution;
height = cy * heightmapResolution;
heightmap = new float[height, width];
sedimentmap = new float[height, width];
watermap = new float[height, width];
float[,] heights;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int tIndex = y * cx + x;
heights = terrains[tIndex].terrainData.GetHeights(0, 0, heightmapResolution, heightmapResolution);
for (int dy = 0; dy < heightmapResolution; dy++)
{
int row = y * heightmapResolution + dy;
for (int dx = 0; dx < heightmapResolution; dx++)
{
heightmap[row, x * heightmapResolution + dx] = heights[dy, dx];
}
}
}
}
if (!WaterErosion()) return;
heights = new float[heightmapResolution, heightmapResolution];
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
for (int dy = 0; dy < heightmapResolution; dy++)
{
int row = y * heightmapResolution + dy;
for (int dx = 0; dx < heightmapResolution; dx++)
{
heights[dy, dx] = heightmap[row, x * heightmapResolution + dx];
}
}
int tIndex = y * cx + x;
terrains[tIndex].terrainData.SetHeights(0, 0, heights);
}
}
}
private void MoveWater(int x, int y)
{
if (watermap[y, x] <= 0) return;
int cn = 0;
float sd = 0;
float v = heightmap[y, x];
int x1 = Mathf.Max(x - 1, 0);
int x2 = Mathf.Min(x + 2, width);
for (int cy = Mathf.Max(y - 1, 0); cy < Mathf.Min(y + 2, height); cy++)
{
for (int cx = x1; cx < x2; cx++)
{
if (cy == y && cx == x) continue;
float decline = v - heightmap[cy, cx];
float lenCoeff = 0.714f;
if (decline > 0)
{
float d = decline * lenCoeff;
sd += d;
neighbours[cn].decline = d;
neighbours[cn].x = cx;
neighbours[cn].y = cy;
cn++;
}
}
}
if (cn > 0)
{
if (sd < 0.00001f) sd = 0.00001f;
float waterNorm = 1 / sd;
float waterHere = watermap[y, x];
for (int i = 0; i < cn; i++)
{
Neighbour n = neighbours[i];
float waterFlow = waterHere * n.decline * waterNorm;
float sandAmount = 0.05f * waterFlow * n.decline;
watermap[n.y, n.x] += waterFlow;
sedimentmap[y, x] -= sandAmount;
sedimentmap[n.y, n.x] += sandAmount;
}
}
watermap[y, x] = 0;
}
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
target = EditorGUILayout.ObjectField("Target", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
iterations = EditorGUILayout.IntField("Erosion iterations", iterations);
rainfall = EditorGUILayout.FloatField("Rain fall", rainfall);
coneThreshold = EditorGUILayout.FloatField("Cone threshold", coneThreshold);
flowIterations = EditorGUILayout.IntField("Flow iterations", flowIterations);
EditorGUILayout.EndScrollView();
if (GUILayout.Button("Apply")) Apply();
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainErosionFilter wnd = GetWindow<RealWorldTerrainErosionFilter>(true, "Erosion Filter");
wnd.target = target;
}
private bool WaterErosion()
{
for (int i = 0; i < iterations; i++)
{
if (EditorUtility.DisplayCancelableProgressBar("Applying erosion", "Please wait...", (float) i / iterations))
{
EditorUtility.ClearProgressBar();
return false;
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
watermap[y, x] = rainfall;
sedimentmap[y, x] = 0;
}
}
for (int j = 0; j < flowIterations; j++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
MoveWater(x, y);
}
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
AddSediment(x, y);
}
}
}
EditorUtility.ClearProgressBar();
return true;
}
}
}

View File

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

View File

@@ -0,0 +1,111 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using InfinityCode.RealWorldTerrain.Webservices;
using InfinityCode.RealWorldTerrain.Webservices.Results;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainGeocodingWindow : EditorWindow
{
private static RealWorldTerrainGeocodingWindow wnd;
private RealWorldTerrainMonoBase target;
private string response;
private Vector2 scrollPosition;
private string address;
private string languageCode = "en";
private string key;
private GameObject resultGameObject;
private void OnDestroy()
{
wnd = null;
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Container", target, typeof(RealWorldTerrainContainer), true) as RealWorldTerrainContainer;
address = EditorGUILayout.TextField("Location Name", address);
key = EditorGUILayout.TextField("Google API key", key);
EditorGUILayout.BeginHorizontal();
languageCode = EditorGUILayout.TextField("Language Code", languageCode);
RealWorldTerrainWindowUI.DrawHelpButton("List of Languages", "https://developers.google.com/maps/faq?hl=en#languagesupport");
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Search"))
{
RealWorldTerrainGoogleGeocoding.Find(
new RealWorldTerrainGoogleGeocoding.GeocodingParams(address)
{
language = languageCode,
key = key
}
).OnComplete += OnRequestComplete;
}
EditorGUILayout.Space();
if (!string.IsNullOrEmpty(response))
{
GUILayout.Label("Full Response: ");
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
EditorGUILayout.TextArea(response);
EditorGUILayout.EndScrollView();
}
}
private void OnRequestComplete(string response)
{
this.response = response;
try
{
RealWorldTerrainGoogleGeocodingResult[] result = RealWorldTerrainGoogleGeocoding.GetResults(response);
if (result.Length > 0)
{
Vector3 pos;
target.GetWorldPosition(result[0].geometry_location, out pos);
if (pos != default(Vector3))
{
resultGameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
resultGameObject.name = "Geocoding Result";
resultGameObject.transform.position = pos;
resultGameObject.GetComponent<MeshRenderer>().sharedMaterial.color = Color.red;
RealWorldTerrainGeocodingObject geocodingObject = resultGameObject.AddComponent<RealWorldTerrainGeocodingObject>();
geocodingObject.info = result[0];
SceneView.duringSceneGui += OnSceneGUI;
}
}
}
catch
{
}
Repaint();
}
private void OnSceneGUI(SceneView view)
{
view.LookAt(resultGameObject.transform.position, view.camera.transform.rotation, 100);
SceneView.duringSceneGui -= OnSceneGUI;
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Geocoder")]
public static void OpenWindow()
{
OpenWindow(null);
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainGeocodingWindow>(true, "Geocoder");
if (target == null) wnd.target = RealWorldTerrainUtils.FindObjectOfType<RealWorldTerrainContainer>();
else wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,259 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainGrassGeneratorWindow : EditorWindow
{
private static RealWorldTerrainGrassGeneratorWindow wnd;
private List<RealWorldTerrainColorItem> colors;
private Color dryColor = new Color32(205, 188, 26, 255);
private Color healthyColor = new Color32(67, 249, 42, 255);
private RealWorldTerrainItem item;
private int maxHeight = 2;
private int maxWidth = 2;
private int minHeight = 1;
private int minWidth = 1;
private float noiseSpread = 0.1f;
private Texture2D previewTexture;
private Vector2 scrollPosition;
private Texture2D texture;
private void DrawPreview()
{
TerrainData tdata = item.terrainData;
Texture2D originalTexture = item.texture;
float[,,] alphamap = tdata.GetAlphamaps(0, 0, tdata.alphamapWidth, tdata.alphamapHeight);
Color[] originalColors = originalTexture.GetPixels();
int w = originalTexture.width;
int h = originalTexture.height;
float sw = w / (float)tdata.alphamapWidth;
float sh = h / (float)tdata.alphamapHeight;
int l = alphamap.GetLength(2) - 1;
float step = 1 / (sw * sh);
for (int x = 0; x < w; x++)
{
int fx = Mathf.FloorToInt(x / sw);
bool isFirstX = x % sw == 0;
for (int y = 0; y < h; y++)
{
int fy = Mathf.FloorToInt(y / sh);
bool isFirstY = y % sh == 0;
Color clr = originalColors[x * w + y];
if (isFirstX && isFirstY) alphamap[fx, fy, l] = 0;
alphamap[fx, fy, l] += colors.Any(c => c.EqualWithRange(clr)) ? step : 0;
}
}
tdata.SetAlphamaps(0, 0, alphamap);
}
private void ExportSettings()
{
string path = EditorUtility.SaveFilePanel("Export settings", Application.dataPath, "GrassGeneratorSettings.xml", "xml");
if (string.IsNullOrEmpty(path)) return;
XmlDocument doc = new XmlDocument();
XmlElement firstElement = (XmlElement)doc.AppendChild(doc.CreateElement("GrassGenerator"));
firstElement.SetAttribute("dryColor", RealWorldTerrainUtils.ColorToHex(dryColor));
firstElement.SetAttribute("healthyColor", RealWorldTerrainUtils.ColorToHex(healthyColor));
firstElement.SetAttribute("minWidth", minWidth.ToString());
firstElement.SetAttribute("maxWidth", maxWidth.ToString());
firstElement.SetAttribute("minHeight", minHeight.ToString());
firstElement.SetAttribute("maxHeight", maxHeight.ToString());
firstElement.SetAttribute("noiseSpread", noiseSpread.ToString());
firstElement.SetAttribute("textureID", (texture != null) ? texture.GetInstanceID().ToString() : "-1");
foreach (RealWorldTerrainColorItem color in colors) firstElement.AppendChild(color.GetNode(doc));
doc.Save(path);
}
private void GenerateGrass()
{
DetailPrototype prototype = new DetailPrototype
{
prototypeTexture = texture,
renderMode = DetailRenderMode.GrassBillboard,
minWidth = minWidth,
minHeight = minHeight,
maxHeight = maxHeight,
maxWidth = maxWidth,
dryColor = dryColor,
healthyColor = healthyColor,
noiseSpread = noiseSpread
};
TerrainData tdata = item.terrainData;
Texture2D originalTexture = item.texture;
int[,] detailmap = new int[tdata.detailResolution, tdata.detailResolution];
Color[] originalColors = originalTexture.GetPixels();
int w = originalTexture.width;
int h = originalTexture.height;
float sw = w / (float)tdata.detailResolution;
float sh = h / (float)tdata.detailResolution;
for (int x = 0; x < w; x++)
{
int fx = Mathf.FloorToInt(x / sw);
bool isFirstX = x % sw == 0;
for (int y = 0; y < h; y++)
{
int fy = Mathf.FloorToInt(y / sh);
bool isFirstY = y % sh == 0;
Color clr = originalColors[x * w + y];
if (isFirstX && isFirstY) detailmap[fx, fy] = 0;
detailmap[fx, fy] += colors.Any(c => c.EqualWithRange(clr)) ? 1 : 0;
}
}
List<DetailPrototype> prototypes = tdata.detailPrototypes.ToList();
prototypes.Add(prototype);
tdata.detailPrototypes = prototypes.ToArray();
tdata.SetDetailLayer(0, 0, tdata.detailPrototypes.Length - 1, detailmap);
}
private void GeneratePreview()
{
TerrainData tdata = item.terrainData;
RealWorldTerrainEditorUtils.GeneratePreviewTexture(tdata, ref previewTexture);
DrawPreview();
GC.Collect();
}
private void ImportSettings()
{
string path = EditorUtility.OpenFilePanel("Export settings", Application.dataPath, "xml");
if (string.IsNullOrEmpty(path)) return;
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement firstElement = (XmlElement)doc.FirstChild;
if (firstElement.Name != "GrassGenerator") return;
dryColor = RealWorldTerrainUtils.HexToColor(firstElement.GetAttribute("dryColor"));
healthyColor = RealWorldTerrainUtils.HexToColor(firstElement.GetAttribute("healthyColor"));
minWidth = int.Parse(firstElement.GetAttribute("minWidth"));
maxWidth = int.Parse(firstElement.GetAttribute("maxWidth"));
minHeight = int.Parse(firstElement.GetAttribute("minHeight"));
maxHeight = int.Parse(firstElement.GetAttribute("maxHeight"));
noiseSpread = float.Parse(firstElement.GetAttribute("noiseSpread"));
int textureID = int.Parse(firstElement.GetAttribute("textureID"));
if (textureID != -1) texture = (Texture2D)EditorUtility.InstanceIDToObject(textureID);
else texture = null;
colors = new List<RealWorldTerrainColorItem>();
foreach (XmlElement node in firstElement.ChildNodes)
{
RealWorldTerrainColorItem color = new RealWorldTerrainColorItem();
color.SetNode(node);
colors.Add(color);
}
}
private void OnDestroy()
{
OnDisable();
wnd = null;
}
private void OnDisable()
{
if (previewTexture != null)
{
List<TerrainLayer> tls = item.terrainData.terrainLayers.ToList();
tls.RemoveAll(l => l.diffuseTexture == previewTexture);
item.terrainData.terrainLayers = tls.ToArray();
previewTexture = null;
EditorUtility.UnloadUnusedAssetsImmediate();
}
}
private void OnGUI()
{
OnGUIToolbar();
item = EditorGUILayout.ObjectField("Terrain Item: ", item, typeof(RealWorldTerrainItem), true) as RealWorldTerrainItem;
if (item == null) return;
OnGUIProps();
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
for (int i = 0; i < colors.Count; i++) colors[i].OnGUI(i + 1);
EditorGUILayout.EndScrollView();
colors.RemoveAll(c => c.deleted);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Add color")) colors.Add(new RealWorldTerrainColorItem());
if (GUILayout.Button("Generate preview")) GeneratePreview();
if (GUILayout.Button("Remove preview") && previewTexture != null) OnDisable();
if (GUILayout.Button("Generate grass"))
{
GenerateGrass();
Close();
GC.Collect();
}
GUILayout.EndHorizontal();
}
private void OnGUIProps()
{
if (colors == null) colors = new List<RealWorldTerrainColorItem>();
texture = EditorGUILayout.ObjectField("Grass texture: ", texture, typeof(Texture2D), true) as Texture2D;
minWidth = EditorGUILayout.IntField("Min width: ", minWidth);
maxWidth = EditorGUILayout.IntField("Max width: ", maxWidth);
minHeight = EditorGUILayout.IntField("Min height: ", minHeight);
maxHeight = EditorGUILayout.IntField("Max height: ", maxHeight);
noiseSpread = EditorGUILayout.FloatField("Noise spread: ", noiseSpread);
healthyColor = EditorGUILayout.ColorField("Healthy color: ", healthyColor);
dryColor = EditorGUILayout.ColorField("Dry color: ", dryColor);
}
private void OnGUIToolbar()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUIStyle toolbarButtonStyle = new GUIStyle(EditorStyles.toolbarButton) { padding = new RectOffset(5, 5, 2, 2) };
if (GUILayout.Button(new GUIContent(RealWorldTerrainResources.openIcon, "Import settings"), toolbarButtonStyle,
GUILayout.ExpandWidth(false)))
ImportSettings();
if (GUILayout.Button(new GUIContent(RealWorldTerrainResources.saveIcon, "Export settings"), toolbarButtonStyle,
GUILayout.ExpandWidth(false)))
ExportSettings();
GUILayout.Label("", EditorStyles.toolbarButton);
GUILayout.EndHorizontal();
}
public static void OpenWindow()
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainGrassGeneratorWindow>("Grass RealWorldTerrainWindow", true);
}
public static void OpenWindow(RealWorldTerrainItem item)
{
OpenWindow();
wnd.item = item;
}
}
}

View File

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

View File

@@ -0,0 +1,209 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainHUEWindow : Base.RealWorldTerrainBaseContainerTool
{
private int brightness;
private int contrast;
private int tone;
private int saturation;
protected override void Apply()
{
int tick = 0;
int sx = countX;
int sy = countY;
for (int ty = 0; ty < sy; ty++)
{
for (int tx = 0; tx < sx; tx++)
{
int tIndex = ty * sx + tx;
Texture2D texture = terrains[tIndex].texture;
if (texture == null) continue;
Color[] colors = texture.GetPixels();
int cl = colors.Length;
for (int i = 0; i < cl; i++)
{
tick++;
if (tick >= 1000)
{
float progress = ((ty * sx + tx) * cl + i) / (float)(sx * sy * cl);
EditorUtility.DisplayProgressBar("Apply Brightness, Contrast and HUE", Mathf.RoundToInt(progress * 100) + "%", progress);
tick = 0;
}
colors[i] = ApplyFilters(colors[i]);
}
Texture2D newTexture = new Texture2D(texture.width, texture.height);
newTexture.SetPixels(colors);
newTexture.Apply();
string path = AssetDatabase.GetAssetPath(texture);
File.WriteAllBytes(path, newTexture.EncodeToPNG());
}
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
Close();
EditorUtility.ClearProgressBar();
}
protected override Color ApplyFilters(Color color)
{
if (brightness < 0) color = Color.Lerp(color, Color.black, (float)brightness / -150);
else if (brightness > 0) color = Color.Lerp(color, Color.white, (float)brightness / 150);
if (contrast != 0)
{
float c = Mathf.Pow((contrast + 100f) / 100, 2);
float r = (color.r - 0.5f) * c + 0.5f;
float g = (color.g - 0.5f) * c + 0.5f;
float b = (color.b - 0.5f) * c + 0.5f;
color.r = Mathf.Clamp01(r);
color.g = Mathf.Clamp01(g);
color.b = Mathf.Clamp01(b);
}
if (tone != 0)
{
float t = tone >= 0 ? tone : tone + 360;
float rt = t / 120;
rt = rt - (int)rt;
float r = color.r;
float g = color.g;
float b = color.b;
float nr, ng, nb;
if (t < 120)
{
nr = Mathf.Lerp(r, b, rt);
ng = Mathf.Lerp(g, r, rt);
nb = Mathf.Lerp(b, g, rt);
}
else if (t < 240)
{
nr = Mathf.Lerp(b, g, rt);
ng = Mathf.Lerp(r, b, rt);
nb = Mathf.Lerp(g, r, rt);
}
else
{
nr = Mathf.Lerp(g, r, rt);
ng = Mathf.Lerp(b, g, rt);
nb = Mathf.Lerp(r, b, rt);
}
color.r = nr;
color.g = ng;
color.b = nb;
}
if (saturation != 0)
{
float s = Mathf.Pow((saturation + 100f) / 100, 2);
float averageColor = color.grayscale;
float difR = color.r - averageColor;
float difG = color.g - averageColor;
float difB = color.b - averageColor;
color.r = averageColor + difR * s;
color.g = averageColor + difG * s;
color.b = averageColor + difB * s;
}
return color;
}
protected override void OnContentGUI()
{
EditorGUI.BeginChangeCheck();
bool reset = false;
EditorGUILayout.BeginHorizontal();
brightness = EditorGUILayout.IntSlider("Brightness", brightness, -150, 150);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
brightness = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
contrast = EditorGUILayout.IntSlider("Contrast", contrast, -100, 100);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
contrast = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
tone = EditorGUILayout.IntSlider("Tone", tone, -180, 180);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
tone = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
saturation = EditorGUILayout.IntSlider("Saturation", saturation, -100, 100);
if (GUILayout.Button("Reset", GUILayout.ExpandWidth(false)))
{
saturation = 0;
reset = true;
}
EditorGUILayout.EndHorizontal();
if (EditorGUI.EndChangeCheck() || reset) UpdatePreview();
if (image != null && preview != null)
{
int width = (int)position.width;
GUILayout.Box("", GUILayout.Height(width / 2), GUILayout.ExpandWidth(true));
Rect lastRect = GUILayoutUtility.GetLastRect();
if (Event.current.type != EventType.Layout)
{
float imgWidth = lastRect.width / 2 - 10;
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5, lastRect.y + 5, imgWidth, lastRect.height - 10), image, null, ScaleMode.ScaleToFit);
EditorGUI.DrawPreviewTexture(new Rect(lastRect.x + 5 + lastRect.width / 2, lastRect.y + 5, imgWidth, lastRect.height - 10), preview, null, ScaleMode.ScaleToFit);
}
}
}
protected override void UpdatePreview()
{
for (int i = 0; i < originalColors.Length; i++) previewColors[i] = ApplyFilters(originalColors[i]);
preview.SetPixels(previewColors);
preview.Apply();
}
public static void OpenWindow(RealWorldTerrainMonoBase item)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainHUEWindow>(true, "Brightness, Contrast and HUE", true);
wnd.item = item;
wnd.GetImage();
}
}
}

View File

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

View File

@@ -0,0 +1,129 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerraiHeightmapExporter : EditorWindow
{
private RealWorldTerrainMonoBase target;
private int depth = 16;
private RealWorldTerrainByteOrder order = RealWorldTerrainByteOrder.Windows;
private GUIContent[] depthLabels;
private int[] depthValues;
private void OnEnable()
{
depthLabels = new[] {new GUIContent("8"), new GUIContent("16")};
depthValues = new[] { 8, 16 };
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
depth = EditorGUILayout.IntPopup(new GUIContent("Depth"), depth, depthLabels, depthValues);
order = (RealWorldTerrainByteOrder) EditorGUILayout.EnumPopup("Byte Order", order);
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
if (GUILayout.Button("Export")) ExportHeightmap();
EditorGUI.EndDisabledGroup();
if (target != null)
{
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
int heightmapResolution = terrains[0].terrainData.heightmapResolution;
int textureWidth = cx * heightmapResolution;
int textureHeight = cy * heightmapResolution;
EditorGUILayout.HelpBox("Width: " + textureWidth + "px, height: " + textureHeight + "px, channel: 1", MessageType.Info);
}
}
private void ExportHeightmap()
{
string filename = EditorUtility.SaveFilePanel("Export RAW Heightmap", Application.dataPath, "heightmap.raw", "raw");
if (string.IsNullOrEmpty(filename)) return;
int heightmapResolution = -1;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
foreach (RealWorldTerrainItem terrain in terrains)
{
if (heightmapResolution == -1) heightmapResolution = terrain.terrainData.heightmapResolution;
else if (heightmapResolution != terrain.terrainData.heightmapResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains have different heightmap resolution.", "OK");
return;
}
}
FileStream stream = new FileStream(filename, FileMode.Create);
BinaryWriter writer = new BinaryWriter(stream);
int textureWidth = cx * heightmapResolution;
int coof = depth == 8 ? 1 : 2;
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
int tIndex = y * cx + x;
float[,] heightmap = terrains[tIndex].terrainData.GetHeights(0, 0, heightmapResolution, heightmapResolution);
for (int dy = 0; dy < heightmapResolution; dy++)
{
float progress = (tIndex * heightmapResolution + dy) / (float)(cx * cy * heightmapResolution);
EditorUtility.DisplayProgressBar("Export RAW Heightmap", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = cy * heightmapResolution - (y * heightmapResolution + dy) - 1;
int seek = (row * textureWidth + x * heightmapResolution) * coof;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < heightmapResolution; dx++)
{
if (depth == 8) writer.Write((byte) Mathf.RoundToInt(heightmap[dy, dx] * 255));
else
{
short v = (short)Mathf.RoundToInt(heightmap[dy, dx] * 65536);
if (order == RealWorldTerrainByteOrder.Windows) writer.Write(v);
else
{
writer.Write((byte)(v / 256));
writer.Write((byte)(v % 256));
}
}
}
}
}
}
stream.Close();
EditorUtility.ClearProgressBar();
EditorUtility.RevealInFinder(filename);
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerraiHeightmapExporter wnd = GetWindow<RealWorldTerraiHeightmapExporter>(true, "Heightmap Exporter");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,125 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainHeightmapImporter : EditorWindow
{
private RealWorldTerrainMonoBase target;
private int depth = 16;
private RealWorldTerrainByteOrder order = RealWorldTerrainByteOrder.Windows;
private GUIContent[] depthLabels;
private int[] depthValues;
private void OnEnable()
{
depthLabels = new[] { new GUIContent("8"), new GUIContent("16") };
depthValues = new[] { 8, 16 };
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Target: ", target, typeof(RealWorldTerrainMonoBase), true) as RealWorldTerrainMonoBase;
depth = EditorGUILayout.IntPopup(new GUIContent("Depth"), depth, depthLabels, depthValues);
order = (RealWorldTerrainByteOrder)EditorGUILayout.EnumPopup("Byte Order", order);
bool disabled = target == null || target.prefs.resultType != RealWorldTerrainResultType.terrain;
EditorGUI.BeginDisabledGroup(disabled);
if (GUILayout.Button("Import")) ImportHeightmap();
EditorGUI.EndDisabledGroup();
}
private void ImportHeightmap()
{
string filename = EditorUtility.OpenFilePanel("Import RAW Heightmap", Application.dataPath, "raw");
if (string.IsNullOrEmpty(filename)) return;
RealWorldTerrainContainer container = target as RealWorldTerrainContainer;
RealWorldTerrainItem item = target as RealWorldTerrainItem;
int cx = container != null ? container.terrainCount.x : 1;
int cy = container != null ? container.terrainCount.y : 1;
RealWorldTerrainItem[] terrains = container != null ? container.terrains : new[] { item };
long fileSize = new FileInfo(filename).Length;
if (depth == 16) fileSize /= 2;
fileSize /= cx * cy;
int heightmapResolution = (int)Mathf.Sqrt(fileSize);
if (heightmapResolution * heightmapResolution != fileSize)
{
EditorUtility.DisplayDialog("Error", "Invalid file size.", "OK");
return;
}
if (Mathf.ClosestPowerOfTwo(heightmapResolution) != heightmapResolution - 1)
{
EditorUtility.DisplayDialog("Error", "Invalid file size.", "OK");
return;
}
int textureWidth = cx * heightmapResolution;
int coof = depth == 8 ? 1 : 2;
FileStream stream = new FileStream(filename, FileMode.Open);
for (int y = 0; y < cy; y++)
{
for (int x = 0; x < cx; x++)
{
float[,] heightmap = new float[heightmapResolution, heightmapResolution];
for (int dy = 0; dy < heightmapResolution; dy++)
{
float progress = ((y * cx + x) * heightmapResolution + dy) / (float)(cx * cy * heightmapResolution);
EditorUtility.DisplayProgressBar("Import RAW Heightmap", Mathf.RoundToInt(progress * 100) + "%", progress);
int row = cy * heightmapResolution - (y * heightmapResolution + dy) - 1;
int seek = (row * textureWidth + x * heightmapResolution) * coof;
stream.Seek(seek, SeekOrigin.Begin);
for (int dx = 0; dx < heightmapResolution; dx++)
{
if (depth == 8) heightmap[dy, dx] = stream.ReadByte() / 256f;
else
{
int b1 = stream.ReadByte();
int b2 = stream.ReadByte();
if (order == RealWorldTerrainByteOrder.Windows) heightmap[dy, dx] = (b2 * 256 + b1) / 65536f;
else heightmap[dy, dx] = (b1 * 256 + b2) / 65536f;
}
}
}
int tIndex = y * cx + x;
if (terrains[tIndex].terrainData.heightmapResolution != heightmapResolution)
{
Vector3 size = terrains[tIndex].terrainData.size;
terrains[tIndex].terrainData.heightmapResolution = heightmapResolution;
terrains[tIndex].terrainData.size = size;
}
terrains[tIndex].terrainData.SetHeights(0, 0, heightmap);
}
}
stream.Close();
EditorUtility.ClearProgressBar();
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
RealWorldTerrainHeightmapImporter wnd = GetWindow<RealWorldTerrainHeightmapImporter>(true, "Heightmap Importer");
wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,103 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public static class RealWorldTerrainMeshOBJExporter
{
private static int index = 0;
public static void Export(RealWorldTerrainMonoBase item)
{
string meshName = item.gameObject.name;
string filename = EditorUtility.SaveFilePanel("Export .obj file", "", meshName, "obj");
if (string.IsNullOrEmpty(filename)) return;
StringBuilder meshString = new StringBuilder();
meshString.Append("#").Append(meshName).Append(".obj")
.Append("\n#").Append(System.DateTime.Now.ToLongDateString())
.Append("\n#").Append(System.DateTime.Now.ToLongTimeString())
.Append("\n#-------")
.Append("\n\n");
meshString.Append("g ").Append(meshName).Append("\n");
MeshFilter[] filters = item.GetComponentsInChildren<MeshFilter>();
index = 0;
for (int i = 0; i < filters.Length; i++)
{
MeshFilter filter = filters[i];
if (EditorUtility.DisplayCancelableProgressBar("Export OBJ", "Please wait", i / (float) filters.Length))
{
EditorUtility.ClearProgressBar();
return;
}
MeshToString(filter, meshString);
}
StreamWriter sw = new StreamWriter(filename);
sw.Write(meshString.ToString());
sw.Close();
EditorUtility.ClearProgressBar();
EditorUtility.RevealInFinder(filename);
}
private static void MeshToString(MeshFilter mf, StringBuilder meshString)
{
Transform t = mf.transform;
Quaternion r = t.localRotation;
meshString.Append("g ").Append(t.name).Append("\n");
int numVertices = 0;
Mesh m = mf.sharedMesh;
Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
foreach (Vector3 vv in m.vertices)
{
Vector3 v = t.TransformPoint(vv);
numVertices++;
meshString.Append("v ").Append(v.x).Append(" ").Append(v.y).Append(" ").Append(-v.z).Append("\n");
}
meshString.Append("\n");
foreach (Vector3 nn in m.normals)
{
Vector3 v = r * nn;
meshString.Append("vn ").Append(-v.x).Append(" ").Append(-v.y).Append(" ").Append(v.z).Append("\n");
}
meshString.Append("\n");
foreach (Vector3 v in m.uv)
{
meshString.Append("vt ").Append(v.x).Append(" ").Append(v.y).Append("\n");
}
for (int material = 0; material < m.subMeshCount; material++)
{
meshString.Append("\n");
meshString.Append("usemtl ").Append(mats[material].name).Append("\n");
meshString.Append("usemap ").Append(mats[material].name).Append("\n");
int[] triangles = m.GetTriangles(material);
for (int i = 0; i < triangles.Length; i += 3)
{
int ti1 = triangles[i] + 1 + index;
int ti2 = triangles[i + 1] + 1 + index;
int ti3 = triangles[i + 2] + 1 + index;
meshString.Append("f ").Append(ti1).Append("/").Append(ti1).Append("/").Append(ti1).Append(" ").
Append(ti2).Append("/").Append(ti2).Append("/").Append(ti2).Append(" ").
Append(ti3).Append("/").Append(ti3).Append("/").Append(ti3).Append(" ").Append("\n");
}
}
index += numVertices;
}
}
}

View File

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

View File

@@ -0,0 +1,341 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainObjectPlacerWindow : EditorWindow
{
private static string[] gridLabels = { "Place New Object", "Update Position" };
private static RealWorldTerrainObjectPlacerWindow wnd;
private int isNewGameobject = 0;
private GameObject obj;
private double latitude;
private double longitude;
private double altitude;
private RealWorldTerrainContainer container;
private bool selectGameObject = true;
private PlaceMode placeMode = PlaceMode.singleLocation;
private double[] locations;
private string multipleLocations;
private string multipleLocationsFile;
private bool hasCoordinates = false;
private bool useAltitude = false;
private double cursorLongitude;
private double cursorLatitude;
private double cursorAltitude;
private Vector2 scrollPosition;
private void DrawCursorLocation()
{
if (!hasCoordinates) return;
EditorGUILayout.LabelField("Cursor Coordinates:");
EditorGUILayout.LabelField("Latitude: ", cursorLatitude.ToString());
EditorGUILayout.LabelField("Longitude: ", cursorLongitude.ToString());
EditorGUILayout.LabelField("Altitude: ", cursorAltitude.ToString("F2") + " meters");
EditorGUILayout.LabelField("Use CTRL+SHIFT to insert the coordinates.");
if (Event.current.control && Event.current.shift)
{
latitude = cursorLatitude;
longitude = cursorLongitude;
altitude = cursorAltitude;
}
}
private void DrawMultipleLocations()
{
EditorGUILayout.HelpBox("Use one location in decimal per line in \"latitude;longitude\" format without quotes.\n", MessageType.None);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.MaxHeight(200));
multipleLocations = EditorGUILayout.TextArea(multipleLocations, GUILayout.ExpandHeight(true));
EditorGUILayout.EndScrollView();
}
private void DrawMultipleLocationsFromFile()
{
EditorGUILayout.HelpBox("Use one location in decimal per line in \"latitude;longitude\" format without quotes.\n", MessageType.None);
EditorGUILayout.BeginHorizontal();
multipleLocationsFile = EditorGUILayout.TextField("File: ", multipleLocationsFile);
if (GUILayout.Button("...", GUILayout.ExpandWidth(false)))
{
multipleLocationsFile = EditorUtility.OpenFilePanel("Select Input File", multipleLocationsFile, "txt");
}
EditorGUILayout.EndHorizontal();
}
private void DrawNewUI()
{
obj = EditorGUILayout.ObjectField("Prefab: ", obj, typeof(GameObject), true) as GameObject;
placeMode = (PlaceMode)EditorGUILayout.EnumPopup("Place Mode", placeMode);
if (placeMode == PlaceMode.singleLocation) DrawSingleLocation();
else if (placeMode == PlaceMode.multipleLocations) DrawMultipleLocations();
else if (placeMode == PlaceMode.multipleLocationsFromFile) DrawMultipleLocationsFromFile();
selectGameObject = EditorGUILayout.Toggle("Select GameObject(s)?", selectGameObject);
if (GUILayout.Button("Place") && ValidateFields()) PlaceItems();
}
private void DrawSingleLocation()
{
latitude = EditorGUILayout.DoubleField("Latitude", latitude);
longitude = EditorGUILayout.DoubleField("Longitude", longitude);
EditorGUILayout.BeginHorizontal();
useAltitude = GUILayout.Toggle(useAltitude, GUIContent.none, GUILayout.Width(16));
EditorGUI.BeginDisabledGroup(!useAltitude);
altitude = EditorGUILayout.DoubleField("Altitude", altitude);
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
}
private void DrawUpdateUI()
{
obj = EditorGUILayout.ObjectField("GameObject: ", obj, typeof(GameObject), true) as GameObject;
DrawSingleLocation();
selectGameObject = EditorGUILayout.Toggle("Select GameObject?", selectGameObject);
if (GUILayout.Button("Update") && ValidateFields())
{
UpdateGameObjectPosition(obj, longitude, latitude, altitude);
if (selectGameObject) SelectGameObject(obj);
}
}
private bool LoadMultipleLocations(string content)
{
if (string.IsNullOrEmpty(content))
{
ShowError("List of locations is empty.");
return false;
}
string[] lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 0)
{
ShowError("List of locations is empty.");
return false;
}
locations = new double[lines.Length * 2];
for (int i = 0; i < lines.Length; i++)
{
string[] coordinates = lines[i].Split(';');
if (coordinates.Length != 2)
{
ShowError(string.Format("Invalid coordinates in line {0}.", i + 1));
return false;
}
double lng, lat;
if (!double.TryParse(coordinates[0].Trim(), out lat) || !double.TryParse(coordinates[1].Trim(), out lng))
{
ShowError(string.Format("Invalid coordinates in line {0}.", i + 1));
return false;
}
locations[i * 2] = lng;
locations[i * 2 + 1] = lat;
}
return true;
}
private bool LoadMultipleLocationsFromFile()
{
if (!File.Exists(multipleLocationsFile))
{
ShowError("Input file does not exist.");
return false;
}
try
{
string content = File.ReadAllText(multipleLocationsFile);
return LoadMultipleLocations(content);
}
catch (Exception e)
{
ShowError("Exception: " + e.Message);
return false;
}
}
private void OnDestroy()
{
EditorApplication.update -= OnUpdate;
SceneView.duringSceneGui -= OnSceneGUI;
wnd = null;
}
private void OnEnable()
{
OnDestroy();
wnd = this;
EditorApplication.update += OnUpdate;
SceneView.duringSceneGui += OnSceneGUI;
}
private void OnGUI()
{
EditorGUI.BeginChangeCheck();
isNewGameobject = GUILayout.SelectionGrid(isNewGameobject, gridLabels, 2);
if (EditorGUI.EndChangeCheck()) obj = null;
container = EditorGUILayout.ObjectField("Container", container, typeof(RealWorldTerrainContainer), true) as RealWorldTerrainContainer;
if (isNewGameobject == 0) DrawNewUI();
else DrawUpdateUI();
DrawCursorLocation();
}
private void OnSceneGUI(SceneView view)
{
if (container == null) return;
Vector2 mp = Event.current.mousePosition;
mp.y = view.camera.pixelHeight - mp.y;
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
RaycastHit hit;
hasCoordinates = Physics.Raycast(ray, out hit);
if (hasCoordinates) container.GetCoordinatesByWorldPosition(hit.point, out cursorLongitude, out cursorLatitude, out cursorAltitude);
}
private void OnUpdate()
{
Repaint();
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Object Placer")]
public static void OpenWindow()
{
OpenWindow(null);
}
public static void OpenWindow(RealWorldTerrainContainer container)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainObjectPlacerWindow>(false, "Object Placer", true);
if (container == null)
{
wnd.container = RealWorldTerrainUtils.FindObjectOfType<RealWorldTerrainContainer>();
}
else wnd.container = container;
}
public static void OpenWindow(RealWorldTerrainContainer container, double lng, double lat)
{
OpenWindow(container);
wnd.latitude = lat;
wnd.longitude = lng;
}
private void PlaceItems()
{
if (placeMode == PlaceMode.singleLocation)
{
GameObject go = Instantiate(obj);
UpdateGameObjectPosition(go, longitude, latitude, altitude);
if (selectGameObject) SelectGameObject(go);
}
else
{
int count = locations.Length / 2;
GameObject[] gos = new GameObject[count];
for (int i = 0; i < count; i++)
{
GameObject go = Instantiate(obj);
double lng = locations[i * 2];
double lat = locations[i * 2 + 1];
UpdateGameObjectPosition(go, lng, lat);
gos[i] = go;
}
if (selectGameObject)
{
Selection.objects = gos;
EditorGUIUtility.PingObject(gos[0]);
}
}
}
private void SelectGameObject(GameObject go)
{
if (!selectGameObject) return;
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go);
}
private static void ShowError(string message)
{
EditorUtility.DisplayDialog("Error", message, "OK");
}
private void UpdateGameObjectPosition(GameObject go, double lng, double lat, double alt = 0)
{
Vector3 worldPosition;
bool status = false;
if (useAltitude) status = container.GetWorldPosition(lng, lat, alt, out worldPosition);
else status = container.GetWorldPosition(lng, lat, out worldPosition);
if (status) go.transform.position = worldPosition;
}
private bool ValidateFields()
{
if (container == null)
{
ShowError("Please select Real World Terrain Container.");
return false;
}
if (obj == null)
{
ShowError(string.Format("Please select {0}.", isNewGameobject == 0 ? "Prefab" : "GameObject"));
return false;
}
if (!container.Contains(longitude, latitude))
{
ShowError("These the coordinates outside terrain.");
return false;
}
if (placeMode == PlaceMode.multipleLocations)
{
if (!LoadMultipleLocations(multipleLocations)) return false;
}
else if (placeMode == PlaceMode.multipleLocationsFromFile)
{
if (!LoadMultipleLocationsFromFile()) return false;
}
return true;
}
private enum PlaceMode
{
singleLocation,
multipleLocations,
multipleLocationsFromFile
}
}
}

View File

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

View File

@@ -0,0 +1,74 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainPOIManager : EditorWindow
{
private static RealWorldTerrainPOIManager wnd;
private RealWorldTerrainContainer container;
private RealWorldTerrainPOIItem[] items;
private Vector2 scrollPosition;
private void OnDestroy()
{
wnd = null;
}
private void OnGUI()
{
container = EditorGUILayout.ObjectField("Container", container, typeof(RealWorldTerrainContainer), true) as RealWorldTerrainContainer;
if (container == null) return;
if (GUILayout.Button("Update POI") || items == null) UpdatePOI();
GUILayout.Label("POI:");
if (items == null || items.Length == 0)
{
GUILayout.Label("No POI.");
return;
}
int index = 0;
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
foreach (RealWorldTerrainPOIItem item in items)
{
index++;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(index + ": " + item.title);
EditorGUILayout.LabelField("lat: " + item.y, GUILayout.Width(90));
EditorGUILayout.LabelField("lng: " + item.x, GUILayout.Width(90));
if (GUILayout.Button(new GUIContent("S", "Select GameObject"), GUILayout.ExpandWidth(false))) Selection.activeGameObject = item.gameObject;
if (GUILayout.Button(new GUIContent("P", "Open in Object Placer"), GUILayout.ExpandWidth(false))) RealWorldTerrainObjectPlacerWindow.OpenWindow(container, item.x, item.y);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
private void UpdatePOI()
{
items = container.GetComponentsInChildren<RealWorldTerrainPOIItem>();
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/POI Manager")]
public static void OpenWindow()
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainPOIManager>(false, "POI Manager");
wnd.container = RealWorldTerrainUtils.FindObjectOfType<RealWorldTerrainContainer>();
}
public static void OpenWindow(RealWorldTerrainContainer container)
{
OpenWindow();
wnd.container = container;
}
}
}

View File

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

View File

@@ -0,0 +1,146 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using InfinityCode.RealWorldTerrain.Webservices;
using InfinityCode.RealWorldTerrain.Webservices.Results;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainReverseGeocodingWindow: EditorWindow
{
private static RealWorldTerrainReverseGeocodingWindow wnd;
private RealWorldTerrainMonoBase target;
private double cursorLatitude;
private double cursorLongitude;
private double cursorAltitude;
private float lat;
private float lng;
private bool hasCoordinates = false;
private string languageCode = "en";
private string formattedAddress;
private string response;
private Vector2 scrollPosition;
private string key;
private void OnDestroy()
{
EditorApplication.update -= OnUpdate;
SceneView.duringSceneGui -= OnSceneGUI;
wnd = null;
}
private void OnEnable()
{
EditorApplication.update += OnUpdate;
SceneView.duringSceneGui += OnSceneGUI;
}
private void OnGUI()
{
target = EditorGUILayout.ObjectField("Container", target, typeof(RealWorldTerrainContainer), true) as RealWorldTerrainContainer;
lat = EditorGUILayout.FloatField("Latitude", lat);
lng = EditorGUILayout.FloatField("Longitude", lng);
key = EditorGUILayout.TextField("Google API key", key);
EditorGUILayout.BeginHorizontal();
languageCode = EditorGUILayout.TextField("Language Code", languageCode);
RealWorldTerrainWindowUI.DrawHelpButton("List of Languages", "https://developers.google.com/maps/faq?hl=en#languagesupport");
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("What's here?"))
{
RealWorldTerrainGoogleGeocoding.Find(
new RealWorldTerrainGoogleGeocoding.ReverseGeocodingParams(lng, lat)
{
language = languageCode,
key = key
}
).OnComplete += OnRequestComplete;
}
if (hasCoordinates)
{
EditorGUILayout.LabelField("Cursor Coordinates:");
EditorGUILayout.LabelField("Latitude: ", cursorLatitude.ToString());
EditorGUILayout.LabelField("Longitude: ", cursorLongitude.ToString());
EditorGUILayout.LabelField("Altitude: ", cursorAltitude.ToString("F2") + " meters");
EditorGUILayout.LabelField("Use CTRL+SHIFT to insert the coordinates.");
if (Event.current.control && Event.current.shift)
{
lat = (float)cursorLatitude;
lng = (float)cursorLongitude;
}
}
EditorGUILayout.Space();
if (!string.IsNullOrEmpty(formattedAddress))
{
GUILayout.Label("Formatted Address: " + formattedAddress, EditorStyles.wordWrappedLabel);
}
if (!string.IsNullOrEmpty(response))
{
GUILayout.Label("Full Response: ");
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
EditorGUILayout.TextArea(response);
EditorGUILayout.EndScrollView();
}
}
private void OnRequestComplete(string response)
{
Debug.Log(response);
this.response = response;
try
{
RealWorldTerrainGoogleGeocodingResult[] result = RealWorldTerrainGoogleGeocoding.GetResults(response);
if (result.Length > 0)
{
formattedAddress = result[0].formatted_address;
}
}
catch
{
}
}
private void OnSceneGUI(SceneView view)
{
if (target == null) return;
Vector2 mp = Event.current.mousePosition;
mp.y = view.camera.pixelHeight - mp.y;
hasCoordinates = target.GetCoordinatesByScreenPosition(mp, out cursorLongitude, out cursorLatitude, out cursorAltitude, view.camera);
}
private void OnUpdate()
{
Repaint();
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Reverse Geocoder")]
public static void OpenWindow()
{
OpenWindow(null);
}
public static void OpenWindow(RealWorldTerrainMonoBase target)
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainReverseGeocodingWindow>(true, "Reverse Geocoder");
if (target == null) wnd.target = RealWorldTerrainUtils.FindObjectOfType<RealWorldTerrainContainer>();
else wnd.target = target;
}
}
}

View File

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

View File

@@ -0,0 +1,150 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using System;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainScalerWindow : EditorWindow
{
private static RealWorldTerrainScalerWindow wnd;
private RealWorldTerrainContainer item;
private Vector3 scale = Vector3.one;
private Vector3 size = Vector3.one;
private SizeType sizeType = SizeType.relative;
#if !RTP
private bool scaleFirstSP = true;
#endif
private void OnGUI()
{
item = (RealWorldTerrainContainer)EditorGUILayout.ObjectField("Real World Terrain", item, typeof(RealWorldTerrainContainer), true);
if (item == null) return;
sizeType = (SizeType)EditorGUILayout.EnumPopup("Size Type", sizeType);
if (sizeType == SizeType.relative) scale = EditorGUILayout.Vector3Field("Scale", scale);
else size = EditorGUILayout.Vector3Field("Size", size);
if (item.prefs.resultType == RealWorldTerrainResultType.terrain)
{
#if !RTP
scaleFirstSP = EditorGUILayout.Toggle("Scale first SplatPrototype", scaleFirstSP);
#endif
}
if (GUILayout.Button("Apply")) Scale();
}
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Scaler")]
public static void OpenWindow()
{
OpenWindow(null);
}
public static void OpenWindow(RealWorldTerrainContainer item)
{
wnd = GetWindow<RealWorldTerrainScalerWindow>("Scaler", true);
wnd.item = item;
if (item != null) wnd.size = item.size;
}
private void Scale()
{
if (sizeType == SizeType.relative)
{
if (!ValidateValue(scale, "Scale")) return;
}
else
{
if (!ValidateValue(size, "Size")) return;
}
Vector3 originalSize = item.bounds.size;
Vector3 newSize = originalSize;
Vector3 pscale;
if (sizeType == SizeType.relative)
{
pscale = scale;
newSize.Scale(pscale);
}
else
{
newSize = size;
pscale = new Vector3(newSize.x / originalSize.x, newSize.y / originalSize.y, newSize.z / originalSize.z);
}
item.size = newSize;
if (item.prefs.resultType == RealWorldTerrainResultType.mesh)
{
Vector3 localScale = item.transform.localScale;
localScale.Scale(pscale);
item.transform.localScale = localScale;
item.size = newSize;
}
else
{
foreach (RealWorldTerrainItem terrain in item.terrains)
{
Vector3 p = terrain.transform.position;
p.Scale(pscale);
terrain.transform.position = p;
Vector3 s = terrain.terrainData.size;
s.Scale(pscale);
terrain.terrainData.size = s;
terrain.size = s;
TreeInstance[] treeInstances = terrain.terrainData.treeInstances;
for (int i = 0; i < treeInstances.Length; i++)
{
TreeInstance treeInstance = treeInstances[i];
treeInstance.heightScale *= pscale.y;
treeInstance.widthScale *= (pscale.x + pscale.z) / 2;
treeInstances[i] = treeInstance;
}
terrain.terrain.terrainData.SetTreeInstances(treeInstances, true);
#if !RTP
if (scaleFirstSP && terrain.terrainData.terrainLayers.Length > 0)
{
TerrainLayer[] tls = terrain.terrainData.terrainLayers;
TerrainLayer l = tls[0];
l.tileSize = new Vector2(l.tileSize.x * pscale.x, l.tileSize.y * pscale.z);
l.tileOffset = new Vector2(l.tileOffset.x * pscale.x, l.tileOffset.y * pscale.z);
l.diffuseTexture = l.diffuseTexture;
terrain.terrainData.terrainLayers = tls;
}
#endif
terrain.terrain.terrainData.RefreshPrototypes();
EditorUtility.SetDirty(terrain);
}
}
Close();
}
private bool ValidateValue(Vector3 value, string variableName)
{
if (Math.Abs(value.x) < float.Epsilon || Math.Abs(value.y) < float.Epsilon || Math.Abs(value.z) < float.Epsilon)
{
Debug.LogError(variableName + " failed!!! Value can not be zero.");
return false;
}
if (scale.x < 0 || scale.y < 0 || scale.z < 0)
{
Debug.LogError(variableName + " failed!!! Value can not be lower zero.");
return false;
}
return true;
}
public enum SizeType
{
absolute,
relative
}
}
}

View File

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

View File

@@ -0,0 +1,114 @@
/* INFINITY CODE */
/* https://infinity-code.com */
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public class RealWorldTerrainSeamsFixer : EditorWindow
{
private bool createUndo = true;
private Terrain terrain1;
private Terrain terrain2;
private Vector2 scrollPosition;
[MenuItem("Window/Infinity Code/Real World Terrain/Tools/Seams Fixer")]
public static void CreateWizard()
{
GetWindow<RealWorldTerrainSeamsFixer>("Seams Fixer");
}
private void FixHeights(float[,] h1, float[,] h2)
{
for (int i = 0; i < h1.GetLength(0); i++)
{
for (int j = 0; j < h1.GetLength(1); j++)
{
h1[i, j] = (h1[i, j] + h2[i, j]) / 2;
}
}
}
private void OnGUI()
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
EditorGUILayout.HelpBox(@"A tool for repairing seams between adjacent Terrains.
Requirements:
1.Terrains must be located in the correct position to be neighbors.
2.Terrains must have the same heightmap resolution.", MessageType.Info);
terrain1 = EditorGUILayout.ObjectField("Terrain 1", terrain1, typeof(Terrain), true) as Terrain;
terrain2 = EditorGUILayout.ObjectField("Terrain 2", terrain2, typeof(Terrain), true) as Terrain;
createUndo = EditorGUILayout.ToggleLeft("Create Undo (May take a long time)", createUndo);
EditorGUILayout.EndScrollView();
EditorGUI.BeginDisabledGroup(terrain1 == null || terrain2 == null);
if (GUILayout.Button("Fix"))
{
OnFix();
}
EditorGUI.EndDisabledGroup();
}
public void OnFix()
{
if (terrain1 == terrain2)
{
EditorUtility.DisplayDialog("Error", "You have selected the same terrain.", "OK");
return;
}
TerrainData td1 = terrain1.terrainData;
TerrainData td2 = terrain2.terrainData;
if (td1.heightmapResolution != td2.heightmapResolution)
{
EditorUtility.DisplayDialog("Error", "Terrains cannot have different heightmap resolution.", "OK");
return;
}
if (createUndo) Undo.RecordObjects(new []{td1, td2}, "Fix seams");
int r = td1.heightmapResolution;
if (terrain1.transform.position + new Vector3(td1.size.x, 0, 0) == terrain2.transform.position) // Right
{
float[,] h1 = td1.GetHeights(r - 1, 0, 1, r);
float[,] h2 = td2.GetHeights(0, 0, 1, r);
FixHeights(h1, h2);
td1.SetHeights(r - 1, 0, h1);
td2.SetHeights(0, 0, h1);
}
else if (terrain2.transform.position + new Vector3(td2.size.x, 0, 0) == terrain1.transform.position) // Left
{
float[,] h1 = td2.GetHeights(r - 1, 0, 1, r);
float[,] h2 = td1.GetHeights(0, 0, 1, r);
FixHeights(h1, h2);
td2.SetHeights(r - 1, 0, h1);
td1.SetHeights(0, 0, h1);
}
else if (terrain1.transform.position + new Vector3(0, 0, td1.size.z) == terrain2.transform.position) // Down
{
float[,] h1 = td1.GetHeights(0, r - 1, r, 1);
float[,] h2 = td2.GetHeights(0, 0, r, 1);
FixHeights(h1, h2);
td1.SetHeights(0, r - 1, h1);
td2.SetHeights(0, 0, h1);
}
else if (terrain2.transform.position + new Vector3(0, 0, td2.size.z) == terrain1.transform.position) // Up
{
float[,] h1 = td2.GetHeights(0, r - 1, r, 1);
float[,] h2 = td1.GetHeights(0, 0, r, 1);
FixHeights(h1, h2);
td2.SetHeights(0, r - 1, h1);
td1.SetHeights(0, 0, h1);
}
else
{
EditorUtility.DisplayDialog("Error", "This is not neighboring terrains.", "OK");
return;
}
}
}
}

View File

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

View File

@@ -0,0 +1,260 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
public class RealWorldTerrainSplatPrototypeGenerator : EditorWindow
{
private static RealWorldTerrainSplatPrototypeGenerator wnd;
private static Texture2D previewTexture;
private RealWorldTerrainSplatPrototypeItem baseTexture;
private RealWorldTerrainItem item;
private List<RealWorldTerrainSplatPrototypeItem> prototypes;
private Vector2 scrollPosition;
private void ExportSettings()
{
string path = EditorUtility.SaveFilePanel("Export settings", Application.dataPath,
"SplatGeneratorSettings.xml", "xml");
if (string.IsNullOrEmpty(path)) return;
XmlDocument doc = new XmlDocument();
XmlElement firstElement = (XmlElement)doc.AppendChild(doc.CreateElement("SplatGenerator"));
firstElement.AppendChild(baseTexture.GetNode(doc));
foreach (RealWorldTerrainSplatPrototypeItem sp in prototypes) firstElement.AppendChild(sp.GetNode(doc));
doc.Save(path);
}
public static void GeneratePreview(RealWorldTerrainSplatPrototypeItem sp)
{
TerrainData tdata = wnd.item.terrainData;
RealWorldTerrainEditorUtils.GeneratePreviewTexture(tdata, ref previewTexture);
Texture2D originalTexture = tdata.terrainLayers[0].diffuseTexture;
float[,,] alphamap = tdata.GetAlphamaps(0, 0, tdata.alphamapWidth, tdata.alphamapHeight);
Color[] originalColors = originalTexture.GetPixels();
int w = originalTexture.width;
int h = originalTexture.height;
float sw = w / (float)tdata.alphamapWidth;
float sh = h / (float)tdata.alphamapHeight;
int l = alphamap.GetLength(2) - 1;
float step = 1 / (sw * sh);
for (int x = 0; x < w; x++)
{
int fx = Mathf.FloorToInt(x / sw);
bool isFirstX = x % sw == 0;
for (int y = 0; y < h; y++)
{
int fy = Mathf.FloorToInt(y / sh);
bool isFirstY = y % sh == 0;
Color clr = originalColors[x * w + y];
if (isFirstX && isFirstY) alphamap[fx, fy, l] = 0;
alphamap[fx, fy, l] += sp.colors.Any(c => c.EqualWithRange(clr)) ? step : 0;
}
}
tdata.SetAlphamaps(0, 0, alphamap);
alphamap = null;
originalColors = null;
GC.Collect();
}
private void GenerateSplatPrototypes()
{
TerrainData tdata = item.terrainData;
List<TerrainLayer> spsList = new List<TerrainLayer>(tdata.terrainLayers);
int startIndex = spsList.Count;
int endIndex = startIndex + prototypes.Count + 1;
spsList.Add(baseTexture.terrainLayer);
spsList.AddRange(prototypes.Select(prototype => prototype.terrainLayer));
tdata.terrainLayers = spsList.ToArray();
tdata.RefreshPrototypes();
Texture2D originalTexture = tdata.terrainLayers[0].diffuseTexture;
float[,,] alphamap = tdata.GetAlphamaps(0, 0, tdata.alphamapWidth, tdata.alphamapHeight);
Color[] originalColors = originalTexture.GetPixels();
int w = originalTexture.width;
int h = originalTexture.height;
float sw = w / (float)tdata.alphamapWidth;
float sh = h / (float)tdata.alphamapHeight;
float step = 1 / (sw * sh);
for (int x = 0; x < alphamap.GetLength(0); x++)
{
for (int y = 0; y < alphamap.GetLength(1); y++)
alphamap[x, y, startIndex] = 1;
}
for (int l = startIndex + 1; l < endIndex; l++)
{
RealWorldTerrainSplatPrototypeItem prototype = prototypes[l - startIndex - 1];
for (int x = 0; x < w; x++)
{
int fx = Mathf.FloorToInt(x / sw);
bool isFirstX = x % sw == 0;
for (int y = 0; y < h; y++)
{
int fy = Mathf.FloorToInt(y / sh);
bool isFirstY = y % sh == 0;
Color clr = originalColors[x * w + y];
if (isFirstX && isFirstY) alphamap[fx, fy, l] = 0;
alphamap[fx, fy, l] += prototype.colors.Any(c => c.EqualWithRange(clr)) ? step : 0;
}
}
}
for (int x = 0; x < alphamap.GetLength(0); x++)
{
for (int y = 0; y < alphamap.GetLength(1); y++)
{
float total = 0;
for (int l = startIndex + 1; l < endIndex; l++) total += alphamap[x, y, l];
for (int l = 0; l < alphamap.GetLength(2); l++)
{
if (l < startIndex || l >= endIndex) alphamap[x, y, l] = 0;
else if (l == startIndex)
{
if (total < 1) alphamap[x, y, l] = 1 - total;
else alphamap[x, y, l] = 0;
}
else if (total != 0)
{
if (total < 1)
alphamap[x, y, l] = total;
else alphamap[x, y, l] /= total;
}
}
}
}
tdata.SetAlphamaps(0, 0, alphamap);
alphamap = null;
originalColors = null;
GC.Collect();
}
private void ImportSettings()
{
string path = EditorUtility.OpenFilePanel("Export settings", Application.dataPath, "xml");
if (string.IsNullOrEmpty(path)) return;
XmlDocument doc = new XmlDocument();
doc.Load(path);
XmlElement firstElement = (XmlElement)doc.FirstChild;
if (firstElement.Name != "SplatGenerator") return;
baseTexture = new RealWorldTerrainSplatPrototypeItem(true);
baseTexture.SetNode((XmlElement)firstElement.ChildNodes[0]);
prototypes = new List<RealWorldTerrainSplatPrototypeItem>();
for (int i = 1; i < firstElement.ChildNodes.Count; i++)
{
RealWorldTerrainSplatPrototypeItem sp = new RealWorldTerrainSplatPrototypeItem();
sp.SetNode((XmlElement)firstElement.ChildNodes[i]);
prototypes.Add(sp);
}
}
private void OnDestroy()
{
OnDisable();
wnd = null;
}
private void OnDisable()
{
if (previewTexture != null)
{
List<TerrainLayer> tls = item.terrainData.terrainLayers.ToList();
tls.RemoveAll(l => l.diffuseTexture == previewTexture);
item.terrainData.terrainLayers = tls.ToArray();
previewTexture = null;
EditorUtility.UnloadUnusedAssetsImmediate();
}
}
private void OnEnable()
{
wnd = this;
}
private void OnGUI()
{
if (baseTexture == null) baseTexture = new RealWorldTerrainSplatPrototypeItem(true);
if (prototypes == null) prototypes = new List<RealWorldTerrainSplatPrototypeItem>();
OnGUIToolbar();
item = EditorGUILayout.ObjectField("Terrain Item: ", item, typeof(RealWorldTerrainItem), true) as RealWorldTerrainItem;
if (item == null) return;
baseTexture.OnGUI();
int index = prototypes.Count;
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
foreach (RealWorldTerrainSplatPrototypeItem prototype in prototypes) prototype.OnGUI(index--);
GUILayout.EndScrollView();
prototypes.RemoveAll(p => p.deleted);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Add SplatPrototype"))
prototypes.Insert(0, new RealWorldTerrainSplatPrototypeItem());
if (GUILayout.Button("Clear preview"))
OnDisable();
if (GUILayout.Button("Generate SplatPrototypes"))
{
GenerateSplatPrototypes();
Close();
}
GUILayout.EndHorizontal();
}
private void OnGUIToolbar()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUIStyle toolbarButtonStyle = new GUIStyle(EditorStyles.toolbarButton) { padding = new RectOffset(5, 5, 2, 2) };
if (GUILayout.Button(new GUIContent(RealWorldTerrainResources.openIcon, "Import settings"), toolbarButtonStyle,
GUILayout.ExpandWidth(false)))
ImportSettings();
if (GUILayout.Button(new GUIContent(RealWorldTerrainResources.saveIcon, "Export settings"), toolbarButtonStyle,
GUILayout.ExpandWidth(false)))
ExportSettings();
GUILayout.Label("", EditorStyles.toolbarButton);
GUILayout.EndHorizontal();
}
public static void OpenWindow()
{
if (wnd != null) wnd.Close();
wnd = GetWindow<RealWorldTerrainSplatPrototypeGenerator>("SplatPrototype RealWorldTerrainWindow", true);
}
public static void OpenWindow(RealWorldTerrainItem item)
{
OpenWindow();
wnd.item = item;
}
}
}

View File

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

View File

@@ -0,0 +1,117 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using System;
using System.Collections.Generic;
using System.Xml;
using InfinityCode.RealWorldTerrain.Tools;
using UnityEditor;
using UnityEngine;
namespace InfinityCode.RealWorldTerrain
{
[Serializable]
public class RealWorldTerrainSplatPrototypeItem
{
public List<RealWorldTerrainColorItem> colors;
public bool deleted;
private readonly bool isBase;
private bool expanded = true;
private Texture2D texture;
private Vector2 tileOffset = Vector2.zero;
private Vector2 tileSize = new Vector2(15, 15);
public SplatPrototype splat
{
get
{
return new SplatPrototype { texture = texture, tileSize = tileSize, tileOffset = tileOffset };
}
}
public TerrainLayer terrainLayer
{
get
{
return new TerrainLayer { diffuseTexture = texture, tileSize = tileSize, tileOffset = tileOffset };
}
}
public RealWorldTerrainSplatPrototypeItem(bool isBase = false)
{
this.isBase = isBase;
colors = new List<RealWorldTerrainColorItem>();
}
public XmlNode GetNode(XmlDocument doc)
{
XmlElement node = doc.CreateElement("SplatPrototype");
node.SetAttribute("tileSizeX", tileSize.x.ToString());
node.SetAttribute("tileSizeY", tileSize.y.ToString());
node.SetAttribute("tileOffsetX", tileOffset.x.ToString());
node.SetAttribute("tileOffsetY", tileOffset.y.ToString());
node.SetAttribute("textureID", (texture != null) ? texture.GetInstanceID().ToString() : "-1");
foreach (RealWorldTerrainColorItem color in colors) node.AppendChild(color.GetNode(doc));
return node;
}
public void OnGUI(int index = 0)
{
if (!isBase)
{
expanded = EditorGUILayout.Foldout(expanded, "SplatPrototype " + index);
if (expanded)
{
OnGUIProp("Texture: ");
int colorIndex = 1;
foreach (RealWorldTerrainColorItem color in colors) color.OnGUI(colorIndex++);
colors.RemoveAll(c => c.deleted);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Add color"))
colors.Add(new RealWorldTerrainColorItem());
if (GUILayout.Button("Generate preview"))
RealWorldTerrainSplatPrototypeGenerator.GeneratePreview(this);
if (GUILayout.Button("Remove SplatPrototype"))
deleted = true;
GUILayout.EndHorizontal();
}
}
else
OnGUIProp("Base texture: ");
}
private void OnGUIProp(string label)
{
texture = (Texture2D)EditorGUILayout.ObjectField(label, texture, typeof(Texture2D), false);
tileSize = EditorGUILayout.Vector2Field("Tile size", tileSize);
tileOffset = EditorGUILayout.Vector2Field("Tile offset", tileOffset);
}
public void SetNode(XmlElement node)
{
tileSize.x = float.Parse(node.GetAttribute("tileSizeX"));
tileSize.y = float.Parse(node.GetAttribute("tileSizeY"));
tileOffset.x = float.Parse(node.GetAttribute("tileOffsetX"));
tileOffset.y = float.Parse(node.GetAttribute("tileOffsetY"));
int textureID = int.Parse(node.GetAttribute("textureID"));
if (textureID != -1) texture = (Texture2D)EditorUtility.InstanceIDToObject(textureID);
else texture = null;
colors = new List<RealWorldTerrainColorItem>();
foreach (XmlElement cNode in node.ChildNodes)
{
RealWorldTerrainColorItem color = new RealWorldTerrainColorItem();
color.SetNode(cNode);
colors.Add(color);
}
}
}
}

View File

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

View File

@@ -0,0 +1,30 @@
/* INFINITY CODE 2013-2019 */
/* http://www.infinity-code.com */
using UnityEngine;
namespace InfinityCode.RealWorldTerrain.Tools
{
public static class RealWorldTerrainUpdateNeighbors
{
public static void Update(RealWorldTerrainContainer container)
{
RealWorldTerrainVector2i terrainCount = container.terrainCount;
RealWorldTerrainItem[] terrains = container.terrains;
for (int x = 0; x < terrainCount.x; x++)
{
for (int y = 0; y < terrainCount.y; y++)
{
int index = y * terrainCount.x + x;
Terrain bottom = y > 0 ? terrains[index - terrainCount.x].terrain : null;
Terrain top = y < terrainCount.y - 1 ? terrains[index + terrainCount.x].terrain : null;
Terrain left = x > 0 ? terrains[index - 1].terrain : null;
Terrain right = x < terrainCount.x - 1 ? terrains[index + 1].terrain : null;
terrains[index].terrain.SetNeighbors(left, top, right, bottom);
}
}
foreach (RealWorldTerrainItem terrain in terrains) terrain.terrain.Flush();
}
}
}

View File

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