![Negin Soltani](/assets/img/avatar_default.png)
- Globe Asset - Spatial Anchors - Photon Implementation - Scripts for Globe Control and Initial Country Colorizing - Script for Reading csv file
591 lines
17 KiB
C#
591 lines
17 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace WPM {
|
|
|
|
[ExecuteInEditMode]
|
|
public class WorldMapTilesDownloader : EditorWindow {
|
|
|
|
const int MAX_CONCURRENT_DOWNLOADS = 128;
|
|
const int ONE_MEGABYTE = 1024 * 1024;
|
|
|
|
int concurrentDownloads = 16;
|
|
int zoomLevelMin = WorldMapGlobe.TILE_MIN_ZOOM_LEVEL;
|
|
int zoomLevelMax = WorldMapGlobe.TILE_MIN_ZOOM_LEVEL + 1;
|
|
|
|
enum RestrictMode {
|
|
FullWorld = 0,
|
|
Restricted = 1
|
|
}
|
|
|
|
struct DownloadSlot {
|
|
public CustomWWW www;
|
|
public bool busy;
|
|
public int retries;
|
|
public int x, y, zoomLevel;
|
|
public string path;
|
|
}
|
|
|
|
enum DownloadStatus {
|
|
Idle = 0,
|
|
Estimating = 1,
|
|
Downloading = 2
|
|
}
|
|
|
|
RestrictMode worldArea = RestrictMode.FullWorld;
|
|
float latMin = -10;
|
|
float lonMin = -20;
|
|
float latMax = 10;
|
|
float lonMax = 20;
|
|
Texture2D worldTex;
|
|
Vector4 worldRect;
|
|
Color32[] worldColors, tmp;
|
|
int tw, th;
|
|
DownloadStatus status;
|
|
DownloadSlot[] downloads;
|
|
WorldMapGlobe map;
|
|
int x, y, zoomLevel;
|
|
long numTiles, downloadedTilesCount, downloadedTilesSize;
|
|
float storageSize;
|
|
long estimationDownloads, estimationTotalSize;
|
|
int bytesDownloaded;
|
|
int xmin, ymin, xmax, ymax;
|
|
TileInfo ti;
|
|
int progressTileCount;
|
|
int countryIndex;
|
|
string[] countryNames;
|
|
|
|
|
|
string cachePath {
|
|
get {
|
|
return map.tileResourcePathBase + "/Tiles/" + (int)map.tileServer;
|
|
}
|
|
}
|
|
|
|
public static void ShowWindow () {
|
|
int w = 450;
|
|
int h = 540;
|
|
Rect rect = new Rect (Screen.currentResolution.width / 2 - w / 2, Screen.currentResolution.height / 2 - h / 2, w, h);
|
|
GetWindowWithRect<WorldMapTilesDownloader> (rect, true, "Tiles Downloader", true);
|
|
}
|
|
|
|
void OnEnable () {
|
|
Texture2D tex = Resources.Load<Texture2D> ("WorldMapArea");
|
|
if (tex != null) {
|
|
worldColors = tex.GetPixels32 ();
|
|
tmp = new Color32[worldColors.Length];
|
|
tw = tex.width;
|
|
th = tex.height;
|
|
worldTex = new Texture2D (tw, th, TextureFormat.RGBA32, false);
|
|
}
|
|
downloads = new DownloadSlot[MAX_CONCURRENT_DOWNLOADS];
|
|
map = WorldMapGlobe.instance;
|
|
countryNames = map.GetCountryNames (true);
|
|
ti = new TileInfo (0, 0, 0, 0, null);
|
|
numTiles = GetNumberOfTiles ();
|
|
GetDownloadedTilesCount ();
|
|
}
|
|
|
|
|
|
void OnGUI () {
|
|
if (map == null) {
|
|
Close ();
|
|
EditorGUIUtility.ExitGUI ();
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.HelpBox ("Download tiles and bundle them with application. Please check server license information for terms of use of tiles.", MessageType.Info);
|
|
EditorGUILayout.Separator ();
|
|
EditorGUILayout.HelpBox ("Using current server: " + map.tileServer.ToString () + "\nResources path: " + cachePath, MessageType.Info);
|
|
|
|
EditorGUI.BeginChangeCheck ();
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Zoom Level Min", GUILayout.Width (120));
|
|
zoomLevelMin = EditorGUILayout.IntSlider (zoomLevelMin, 0, WorldMapGlobe.TILE_MAX_ZOOM_LEVEL);
|
|
EditorGUILayout.EndHorizontal ();
|
|
if (zoomLevelMin < WorldMapGlobe.TILE_MIN_ZOOM_LEVEL) {
|
|
EditorGUILayout.HelpBox ("Zoom levels lower than " + WorldMapGlobe.TILE_MIN_ZOOM_LEVEL + " are not used by Globe.", MessageType.Warning);
|
|
}
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Zoom Level Max", GUILayout.Width (120));
|
|
zoomLevelMax = EditorGUILayout.IntSlider (zoomLevelMax, 0, WorldMapGlobe.TILE_MAX_ZOOM_LEVEL);
|
|
EditorGUILayout.EndHorizontal ();
|
|
if (zoomLevelMax < zoomLevelMin)
|
|
zoomLevelMax = zoomLevelMin;
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Concurrent Downloads", GUILayout.Width (120));
|
|
concurrentDownloads = EditorGUILayout.IntSlider (concurrentDownloads, 1, MAX_CONCURRENT_DOWNLOADS);
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("World Area", GUILayout.Width (120));
|
|
worldArea = (RestrictMode)EditorGUILayout.EnumPopup (worldArea);
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
if (worldArea == RestrictMode.Restricted) {
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField (" Country", GUILayout.Width (120));
|
|
countryIndex = EditorGUILayout.Popup (countryIndex, countryNames);
|
|
if (GUILayout.Button ("Pick LatLon Rect")) {
|
|
PickCountryLatLon ();
|
|
}
|
|
EditorGUILayout.EndHorizontal ();
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField (" Latitude Min", GUILayout.Width (120));
|
|
latMin = EditorGUILayout.Slider (latMin, -90, 90);
|
|
EditorGUILayout.EndHorizontal ();
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField (" Longitude Min", GUILayout.Width (120));
|
|
lonMin = EditorGUILayout.Slider (lonMin, -180, 180);
|
|
EditorGUILayout.EndHorizontal ();
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField (" Latitude Max", GUILayout.Width (120));
|
|
latMax = EditorGUILayout.Slider (latMax, -90, 90);
|
|
EditorGUILayout.EndHorizontal ();
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField (" Longitude Max", GUILayout.Width (120));
|
|
lonMax = EditorGUILayout.Slider (lonMax, -180, 180);
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
if (latMax < latMin)
|
|
latMax = latMin;
|
|
if (lonMax < lonMin)
|
|
lonMax = lonMin;
|
|
|
|
EditorGUILayout.Separator ();
|
|
Rect space = EditorGUILayout.BeginVertical ();
|
|
GUILayout.Space (Mathf.Min (160, EditorGUIUtility.currentViewWidth));
|
|
EditorGUILayout.EndVertical ();
|
|
EditorGUI.DrawPreviewTexture (space, GetWorldTexture (), null, ScaleMode.ScaleToFit);
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck ()) {
|
|
numTiles = GetNumberOfTiles ();
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Tile Count", GUILayout.Width (120));
|
|
EditorGUILayout.LabelField (numTiles.ToString ());
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Estimated Size", GUILayout.Width (120));
|
|
if (status == DownloadStatus.Estimating) {
|
|
EditorGUILayout.LabelField ("Sampling... " + ((int)storageSize).ToString () + " MB", GUILayout.Width (120));
|
|
if (GUILayout.Button ("Cancel", GUILayout.Width (80))) {
|
|
StopOperation ();
|
|
}
|
|
} else {
|
|
EditorGUILayout.LabelField (storageSize > 0 ? ((int)storageSize / 2).ToString () + "-" + ((int)storageSize + 1).ToString () + " MB" : "-", GUILayout.Width (120));
|
|
if (GUILayout.Button ("Estimate", GUILayout.Width (80))) {
|
|
StartEstimate ();
|
|
}
|
|
}
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
EditorGUILayout.BeginHorizontal ();
|
|
EditorGUILayout.LabelField ("Downloaded tiles", GUILayout.Width (120));
|
|
EditorGUILayout.LabelField (downloadedTilesCount.ToString () + " (" + ((float)downloadedTilesSize / ONE_MEGABYTE).ToString ("F2") + " MB)", GUILayout.Width (120));
|
|
GUI.enabled = downloadedTilesCount > 0;
|
|
if (GUILayout.Button ("Delete", GUILayout.Width (80))) {
|
|
if (EditorUtility.DisplayDialog ("Delete Downloaded Tiles", "Are you sure you want to delete " + numTiles + " tiles from " + cachePath + "?", "Yes", "Cancel")) {
|
|
DeleteResourcesTiles ();
|
|
}
|
|
}
|
|
GUI.enabled = true;
|
|
EditorGUILayout.EndHorizontal ();
|
|
|
|
EditorGUILayout.Separator ();
|
|
EditorGUILayout.Separator ();
|
|
EditorGUILayout.BeginHorizontal ();
|
|
if (status == DownloadStatus.Downloading) {
|
|
ShowProgressBar (progressTileCount / (float)numTiles);
|
|
if (GUILayout.Button ("Stop", GUILayout.Width (60))) {
|
|
StopOperation ();
|
|
}
|
|
} else {
|
|
EditorGUILayout.HelpBox ("This operation can take some time. Cancel at any time.", MessageType.Warning);
|
|
if (GUILayout.Button ("Start", GUILayout.Width (60))) {
|
|
StartDownload ();
|
|
}
|
|
}
|
|
if (GUILayout.Button ("Close", GUILayout.Width (60))) {
|
|
Close ();
|
|
}
|
|
}
|
|
|
|
Texture2D GetWorldTexture () {
|
|
if (worldRect.x == latMin && worldRect.y == lonMin && worldRect.z == latMax && worldRect.w == lonMax || worldColors == null) {
|
|
return worldTex;
|
|
}
|
|
worldRect.x = latMin;
|
|
worldRect.y = lonMin;
|
|
worldRect.z = latMax;
|
|
worldRect.w = lonMax;
|
|
|
|
Vector2 uv0 = Conversion.GetUVFromLatLon (latMin, lonMin);
|
|
Vector2 uv1 = Conversion.GetUVFromLatLon (latMax, lonMax);
|
|
int tx0 = (int)(uv0.x * tw);
|
|
int tx1 = (int)(uv1.x * tw);
|
|
int ty0 = (int)(uv0.y * th);
|
|
int ty1 = (int)(uv1.y * th);
|
|
|
|
Array.Copy (worldColors, tmp, tmp.Length);
|
|
for (int j = ty0; j <= ty1; j++) {
|
|
for (int k = tx0; k <= tx1; k++) {
|
|
tmp [j * tw + k].g = (byte)(255 - tmp [j * tw + k].g);
|
|
}
|
|
}
|
|
|
|
worldTex.SetPixels32 (tmp);
|
|
worldTex.Apply ();
|
|
return worldTex;
|
|
}
|
|
|
|
void PickCountryLatLon () {
|
|
string[] s = countryNames [countryIndex].Split (new char[] {
|
|
'(',
|
|
')'
|
|
}, System.StringSplitOptions.RemoveEmptyEntries);
|
|
if (s.Length >= 2) {
|
|
int i;
|
|
if (int.TryParse (s [1], out i)) {
|
|
Rect rect = map.countries [i].mainRegion.latlonRect2D;
|
|
latMin = rect.xMin;
|
|
lonMin = rect.yMin;
|
|
latMax = rect.xMax;
|
|
lonMax = rect.yMax;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShowProgressBar (float progress) {
|
|
Rect r = EditorGUILayout.BeginVertical ();
|
|
string text = (progress * 100).ToString ("F2") + "% " + ((float)bytesDownloaded / ONE_MEGABYTE).ToString ("F2") + " MB downloaded";
|
|
EditorGUI.ProgressBar (r, progress, text);
|
|
GUILayout.Space (18);
|
|
EditorGUILayout.EndVertical ();
|
|
}
|
|
|
|
|
|
void OnInspectorUpdate () {
|
|
switch (status) {
|
|
case DownloadStatus.Downloading:
|
|
Download ();
|
|
break;
|
|
case DownloadStatus.Estimating:
|
|
Estimate ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StartDownload () {
|
|
if (!Directory.Exists (cachePath)) {
|
|
Directory.CreateDirectory (cachePath);
|
|
}
|
|
zoomLevel = zoomLevelMin;
|
|
GetTileRect (zoomLevel, out xmin, out ymin, out xmax, out ymax);
|
|
y = ymin;
|
|
x = xmin - 1;
|
|
for (int k = 0; k < downloads.Length; k++) {
|
|
downloads [k].busy = false;
|
|
downloads [k].retries = 0;
|
|
}
|
|
progressTileCount = 0;
|
|
status = DownloadStatus.Downloading;
|
|
}
|
|
|
|
void StopOperation () {
|
|
status = DownloadStatus.Idle;
|
|
RepaintStats ();
|
|
AssetDatabase.Refresh ();
|
|
}
|
|
|
|
|
|
void StartEstimate () {
|
|
estimationTotalSize = 0;
|
|
estimationDownloads = 0;
|
|
zoomLevel = zoomLevelMin;
|
|
for (int k = 0; k < downloads.Length; k++) {
|
|
downloads [k].busy = false;
|
|
downloads [k].retries = 0;
|
|
}
|
|
progressTileCount = 0;
|
|
status = DownloadStatus.Estimating;
|
|
}
|
|
|
|
|
|
void Download () {
|
|
|
|
// Manage downloads
|
|
int activeDownloads = 0;
|
|
for (int k = 0; k < concurrentDownloads; k++) {
|
|
if (downloads [k].busy) {
|
|
CustomWWW www = downloads [k].www;
|
|
if (www.isDone) {
|
|
bool good = string.IsNullOrEmpty (www.error);
|
|
// Check texture consistency
|
|
if (good) {
|
|
Texture2D tex = www.textureNonReadable;
|
|
if (tex == null) {
|
|
good = false;
|
|
} else if (tex.width < 16) {
|
|
good = false;
|
|
DestroyImmediate (tex);
|
|
}
|
|
}
|
|
if (good) {
|
|
// save to resources
|
|
bytesDownloaded += www.bytes.Length;
|
|
File.WriteAllBytes (downloads [k].path, www.bytes);
|
|
downloadedTilesCount++;
|
|
downloadedTilesSize += bytesDownloaded;
|
|
downloads [k].busy = false;
|
|
} else {
|
|
if (downloads [k].retries < 3) {
|
|
downloads [k].retries++;
|
|
Debug.LogError ("Retrying " + downloads [k].retries + " times due error on tile XYZ = " + downloads [k].x + "/" + downloads [k].y + "/" + downloads [k].zoomLevel + " : " + www.error);
|
|
downloads [k].www = new CustomWWW (www.url, 0);
|
|
activeDownloads++;
|
|
} else {
|
|
downloads [k].busy = false;
|
|
}
|
|
}
|
|
www.Dispose ();
|
|
} else {
|
|
activeDownloads++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int iterations = 0;
|
|
for (int k = 0; k < concurrentDownloads; k++) {
|
|
if (!downloads [k].busy) {
|
|
|
|
if (activeDownloads >= concurrentDownloads)
|
|
return;
|
|
|
|
// Get next tile
|
|
bool remainingTiles = GetNextTile ();
|
|
|
|
// Have we finished?
|
|
if (!remainingTiles) {
|
|
if (activeDownloads == 0) {
|
|
StopOperation ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
ti.x = x;
|
|
ti.y = y;
|
|
ti.zoomLevel = zoomLevel;
|
|
string url = map.GetTileURL (map.tileServer, ti);
|
|
// Is current tile already in Resources path?
|
|
string tilePath = map.GetTileResourcePath (x, y, zoomLevel);
|
|
if (File.Exists (tilePath)) {
|
|
if (++iterations < 128) {
|
|
k--;
|
|
}
|
|
} else {
|
|
downloads [k].busy = true;
|
|
downloads [k].retries = 0;
|
|
downloads [k].x = x;
|
|
downloads [k].y = y;
|
|
downloads [k].zoomLevel = zoomLevel;
|
|
downloads [k].path = tilePath;
|
|
downloads [k].www = new CustomWWW (url, 0);
|
|
activeDownloads++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Estimate () {
|
|
|
|
// Manage downloads
|
|
int activeDownloads = 0;
|
|
for (int k = 0; k < concurrentDownloads; k++) {
|
|
if (downloads [k].busy) {
|
|
CustomWWW www = downloads [k].www;
|
|
if (www.isDone) {
|
|
bool good = string.IsNullOrEmpty (www.error);
|
|
// Check texture consistency
|
|
if (good) {
|
|
Texture2D tex = www.textureNonReadable;
|
|
if (tex == null) {
|
|
good = false;
|
|
} else if (tex.width < 16) {
|
|
good = false;
|
|
DestroyImmediate (tex);
|
|
}
|
|
}
|
|
if (good) {
|
|
// save to resources
|
|
estimationTotalSize += www.bytesDownloaded * www.bytesDownloaded;
|
|
estimationDownloads++;
|
|
storageSize = numTiles * Mathf.Sqrt (estimationTotalSize / estimationDownloads) / ONE_MEGABYTE;
|
|
downloads [k].busy = false;
|
|
} else {
|
|
if (downloads [k].retries < 3) {
|
|
downloads [k].retries++;
|
|
Debug.LogError ("Retrying " + downloads [k].retries + " times due error on tile XYZ = " + downloads [k].x + "/" + downloads [k].y + "/" + downloads [k].zoomLevel + " : " + www.error);
|
|
downloads [k].www = new CustomWWW (www.url, 0);
|
|
activeDownloads++;
|
|
} else {
|
|
downloads [k].busy = false;
|
|
}
|
|
}
|
|
www.Dispose ();
|
|
} else {
|
|
activeDownloads++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int k = 0; k < concurrentDownloads; k++) {
|
|
if (!downloads [k].busy) {
|
|
|
|
if (activeDownloads >= concurrentDownloads)
|
|
return;
|
|
|
|
// Get next tile
|
|
bool remainingTiles = GetNextTileRandom ();
|
|
|
|
// Have we finished?
|
|
if (!remainingTiles) {
|
|
if (activeDownloads == 0) {
|
|
StopOperation ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
ti.x = x;
|
|
ti.y = y;
|
|
ti.zoomLevel = zoomLevel;
|
|
string url = map.GetTileURL (map.tileServer, ti);
|
|
downloads [k].busy = true;
|
|
downloads [k].retries = 0;
|
|
downloads [k].x = x;
|
|
downloads [k].y = y;
|
|
downloads [k].zoomLevel = zoomLevel;
|
|
downloads [k].www = new CustomWWW (url, 0);
|
|
activeDownloads++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RepaintStats () {
|
|
GetDownloadedTilesCount ();
|
|
Repaint ();
|
|
}
|
|
|
|
|
|
int GetNumberOfTiles () {
|
|
// Check if current tile is within restricted area
|
|
int count = 0;
|
|
for (int zl = zoomLevelMin; zl <= zoomLevelMax; zl++) {
|
|
if (worldArea == RestrictMode.Restricted) {
|
|
int x0, y0, x1, y1;
|
|
GetTileRect (zl, out x0, out y0, out x1, out y1);
|
|
count += (y1 - y0 + 1) * (x1 - x0 + 1);
|
|
} else {
|
|
count += (int)Mathf.Pow (4, zl);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
void GetDownloadedTilesCount () {
|
|
if (Directory.Exists (cachePath)) {
|
|
string[] files = Directory.GetFiles (cachePath, "*.png");
|
|
downloadedTilesCount = files.Length;
|
|
downloadedTilesSize = 0;
|
|
foreach (string file in files) {
|
|
FileInfo finfo = new FileInfo (file);
|
|
downloadedTilesSize += finfo.Length;
|
|
}
|
|
} else {
|
|
downloadedTilesCount = 0;
|
|
downloadedTilesSize = 0;
|
|
}
|
|
}
|
|
|
|
void GetTileRect (int zoomLevel, out int x0, out int y0, out int x1, out int y1) {
|
|
if (worldArea == RestrictMode.Restricted) {
|
|
Conversion.GetTileFromLatLon (zoomLevel, latMin, lonMin, out x0, out y1);
|
|
Conversion.GetTileFromLatLon (zoomLevel, latMax, lonMax, out x1, out y0);
|
|
} else {
|
|
x0 = y0 = 0;
|
|
x1 = y1 = (int)Mathf.Pow (2, zoomLevel) - 1;
|
|
}
|
|
}
|
|
|
|
bool GetNextTile () {
|
|
|
|
x++;
|
|
if (x > xmax) {
|
|
x = xmin;
|
|
y++;
|
|
if (y > ymax) {
|
|
if (zoomLevel >= zoomLevelMax) {
|
|
x = xmax;
|
|
return false;
|
|
} else {
|
|
zoomLevel++;
|
|
GetTileRect (zoomLevel, out xmin, out ymin, out xmax, out ymax);
|
|
y = ymin;
|
|
x = xmin;
|
|
}
|
|
}
|
|
}
|
|
progressTileCount++;
|
|
if (progressTileCount % 10 == 0) {
|
|
RepaintStats ();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GetNextTileRandom () {
|
|
GetTileRect (zoomLevel, out xmin, out ymin, out xmax, out ymax);
|
|
x = UnityEngine.Random.Range (xmin, xmax + 1);
|
|
y = UnityEngine.Random.Range (ymin, ymax + 1);
|
|
progressTileCount++;
|
|
if (progressTileCount % 10 == 0) {
|
|
RepaintStats ();
|
|
}
|
|
|
|
int t = (int)Mathf.Pow (4, zoomLevel);
|
|
int q = t < 64 ? t : 64;
|
|
if (progressTileCount > q) {
|
|
if (zoomLevel <= zoomLevelMax) {
|
|
zoomLevel++;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DeleteResourcesTiles () {
|
|
if (Directory.Exists (cachePath)) {
|
|
Directory.Delete (cachePath, true);
|
|
}
|
|
if (File.Exists (cachePath + ".meta")) {
|
|
File.Delete (cachePath + ".meta");
|
|
}
|
|
AssetDatabase.Refresh ();
|
|
RepaintStats ();
|
|
}
|
|
|
|
}
|
|
|
|
} |