311 lines
10 KiB
C#
311 lines
10 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
|
|
// Experimantal 2D terrain slice builder
|
|
|
|
namespace TTT
|
|
{
|
|
public class TTTools2D : EditorWindow
|
|
{
|
|
|
|
//public Object heightMap;
|
|
private Texture2D sourceSplatMap;
|
|
|
|
private int edgeColliderResolution = 16; // 1 = 1 pixel resolution
|
|
|
|
// private Color[] splatColor = new Color[3];
|
|
// private Color undergroundColor = Color.black;
|
|
private Texture2D[] splatTextures = new Texture2D[4];
|
|
|
|
private bool addEdgeCollider = true;
|
|
|
|
int slicePositionY=0; // FIXME: currently only works for 0
|
|
// bool splatHasAlpha=false; // FixTextureSettings() checks if texture contains alpha channel, if yes, it will be used when drawing splatmap
|
|
|
|
|
|
private const string appName = "2D Terrain Tools (Experimental)";
|
|
|
|
[MenuItem ("Window/Terrain Tools/"+appName,false,101)]
|
|
static void Init () {
|
|
TTTools2D window = (TTTools2D)EditorWindow.GetWindow (typeof (TTTools2D));
|
|
window.titleContent = new GUIContent(appName);
|
|
window.minSize = new Vector2(400,300);
|
|
}
|
|
|
|
void OnGUI ()
|
|
{
|
|
GUILayout.Label (appName, EditorStyles.boldLabel);
|
|
EditorGUILayout.Space();
|
|
|
|
GUILayout.BeginHorizontal ();
|
|
EditorGUILayout.PrefixLabel ("Source Splatmap");
|
|
sourceSplatMap = (Texture2D)EditorGUILayout.ObjectField (sourceSplatMap, typeof(Texture2D), false, GUILayout.Height (48));
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Space();
|
|
|
|
GUILayout.BeginHorizontal();
|
|
edgeColliderResolution = EditorGUILayout.IntField("EdgeCollider resolution",edgeColliderResolution);
|
|
edgeColliderResolution = (int)Mathf.Clamp(edgeColliderResolution,1,128);
|
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
|
|
|
/*
|
|
GUILayout.BeginHorizontal ();
|
|
EditorGUILayout.PrefixLabel ("Terrain Colors (R G B U)"); // U = underground
|
|
GUILayout.FlexibleSpace ();
|
|
|
|
splatColor[0] = EditorGUILayout.ColorField(splatColor[0],GUILayout.Height (48));
|
|
splatColor[1] = EditorGUILayout.ColorField(splatColor[1],GUILayout.Height (48));
|
|
splatColor[2] = EditorGUILayout.ColorField(splatColor[2],GUILayout.Height (48));
|
|
undergroundColor = EditorGUILayout.ColorField(undergroundColor,GUILayout.Height (48));
|
|
GUILayout.EndHorizontal ();
|
|
*/
|
|
|
|
GUILayout.BeginHorizontal ();
|
|
EditorGUILayout.PrefixLabel ("Terrain Textures (R G B)"); // U = underground
|
|
GUILayout.FlexibleSpace ();
|
|
splatTextures [0] = (Texture2D)EditorGUILayout.ObjectField (splatTextures [0], typeof(Texture2D), false, GUILayout.Height (48));
|
|
splatTextures [1] = (Texture2D)EditorGUILayout.ObjectField (splatTextures [1], typeof(Texture2D), false, GUILayout.Height (48));
|
|
splatTextures [2] = (Texture2D)EditorGUILayout.ObjectField (splatTextures [2], typeof(Texture2D), false, GUILayout.Height (48));
|
|
splatTextures [3] = (Texture2D)EditorGUILayout.ObjectField (splatTextures [3], typeof(Texture2D), false, GUILayout.Height (48));
|
|
GUILayout.EndHorizontal ();
|
|
|
|
if (sourceSplatMap==null || splatTextures[0]==null ||splatTextures[1]==null || splatTextures[2]==null ||splatTextures[3]==null) GUI.enabled = false;
|
|
if (GUILayout.Button ("Generate 2D slices", GUILayout.Width (200), GUILayout.Height (32)))
|
|
{
|
|
Generate2DSlices();
|
|
}
|
|
GUI.enabled = true;
|
|
|
|
} // ongui
|
|
|
|
|
|
|
|
void Generate2DSlices()
|
|
{
|
|
|
|
if (sourceSplatMap==null) {Debug.LogError("sourceSplatMap is null"); return;}
|
|
// TTT.TerrainTools.FixTextureSettings(sourceSplatMap);
|
|
|
|
// textures
|
|
if (splatTextures[0]!=null) FixTextureSettings(splatTextures[0]);
|
|
if (splatTextures[1]!=null) FixTextureSettings(splatTextures[1]);
|
|
if (splatTextures[2]!=null) FixTextureSettings(splatTextures[2]);
|
|
if (splatTextures[3]!=null) FixTextureSettings(splatTextures[3]);
|
|
|
|
|
|
Color[] splatmapColors = sourceSplatMap.GetPixels();
|
|
int splatWidth = sourceSplatMap.width;
|
|
|
|
string path = AssetDatabase.GetAssetPath(sourceSplatMap);
|
|
string pathrev = TTT.TerrainTools.Reverse(Path.GetFileNameWithoutExtension(path));
|
|
string[] parts = pathrev.Split(new char[] { '-' }, 2);
|
|
string basename = TTT.TerrainTools.Reverse(parts[1]);
|
|
string dirname = Path.GetDirectoryName(path);
|
|
path = Path.Combine(dirname, basename);
|
|
string heightmap_name = path+"-heightmap.raw";
|
|
|
|
int targetWidth = sourceSplatMap.width;
|
|
int targetHeight = 128; // TODO: allow adjusting this
|
|
|
|
|
|
float[,] heights = TTT.TerrainTools.ReadRawHeightMap(heightmap_name, targetWidth+1, targetWidth, targetWidth);
|
|
|
|
if (heights==null) {Debug.LogError("Failed to read raw file:"+heightmap_name); return;}
|
|
|
|
Texture2D sliceTex = new Texture2D(targetWidth,targetHeight,TextureFormat.ARGB32,false);
|
|
sliceTex.hideFlags = HideFlags.HideAndDontSave;
|
|
sliceTex.filterMode = FilterMode.Point;
|
|
sliceTex.wrapMode = TextureWrapMode.Clamp;
|
|
|
|
float surfaceDepth = targetHeight/3f;
|
|
|
|
for (int x = 0; x < targetWidth; x++)
|
|
{
|
|
Color splatMapColor = splatmapColors[((splatWidth-1)-x)*splatWidth + slicePositionY];
|
|
float height = heights[x,slicePositionY];
|
|
|
|
for (int y = 0; y < targetHeight; y++)
|
|
{
|
|
int textureScale = 8; // TODO: allow adjusting this
|
|
|
|
int yPoint = ((y*(splatTextures[0].height/targetHeight)) ) % splatTextures[0].height;
|
|
int yPointScaled = (yPoint*textureScale) % splatTextures[0].height;
|
|
|
|
int yPoint3 = ((y*(splatTextures[3].height/targetHeight)) ) % splatTextures[3].height;
|
|
int yPointScaled3 = (yPoint3) % splatTextures[3].height;
|
|
|
|
Color tex1c = splatTextures[0].GetPixel(x,yPointScaled);
|
|
Color tex2c = splatTextures[1].GetPixel(x,yPointScaled);
|
|
Color tex3c = splatTextures[2].GetPixel(x,yPointScaled);
|
|
Color tex4c = splatTextures[3].GetPixel(x,yPointScaled3); // underground
|
|
|
|
Color splatMixColor = ((tex1c * splatMapColor.r) + (tex2c * splatMapColor.g) + (tex3c * splatMapColor.b)/3f);
|
|
splatMixColor.a = 1;
|
|
|
|
float originalSum = splatMapColor.r+splatMapColor.g+splatMapColor.b;
|
|
float difference = 3f-originalSum;
|
|
float fixValue = difference/3f;
|
|
|
|
|
|
splatMixColor = CombineColors(tex1c*(splatMapColor.r+fixValue),tex2c*(splatMapColor.g+fixValue),tex3c*(splatMapColor.b+fixValue));
|
|
|
|
Color c = new Color(1,1,1,0);
|
|
|
|
// we are in the terrain
|
|
if (y<height*targetHeight)
|
|
{
|
|
c = tex4c;
|
|
}
|
|
|
|
if (y>height*targetHeight-surfaceDepth && y<=height*targetHeight) // we are at surface
|
|
{
|
|
float val = Mathf.Abs(y-height*targetHeight) / surfaceDepth;
|
|
c = Color.Lerp(splatMixColor, tex4c, val);
|
|
}
|
|
sliceTex.SetPixel(x,y,c);
|
|
|
|
} // y
|
|
} //x
|
|
EditorUtility.ClearProgressBar();
|
|
sliceTex.Apply(false);
|
|
|
|
|
|
|
|
|
|
// save to file
|
|
byte[] texBytes = sliceTex.EncodeToPNG();
|
|
string newName = Path.GetFileNameWithoutExtension(sourceSplatMap.name);
|
|
string savePath = Path.GetDirectoryName(path) + "/" + newName +"_sliceMap.png";
|
|
File.WriteAllBytes(savePath, texBytes);
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log("Slice generated to:"+savePath);
|
|
|
|
|
|
|
|
// set as sprite in importer settings
|
|
TextureImporter spriteImporter = AssetImporter.GetAtPath(savePath) as TextureImporter;
|
|
TextureImporterSettings spriteSettings = new TextureImporterSettings();
|
|
spriteImporter.textureType = TextureImporterType.Sprite;
|
|
spriteImporter.ReadTextureSettings(spriteSettings);
|
|
spriteSettings.filterMode = FilterMode.Point;
|
|
spriteSettings.spriteMode = (int)SpriteImportMode.Single;
|
|
spriteSettings.spritePixelsPerUnit = 100;
|
|
spriteSettings.spriteAlignment = (int)SpriteAlignment.BottomLeft;
|
|
spriteImporter.SetTextureSettings(spriteSettings);
|
|
AssetDatabase.ImportAsset(savePath, ImportAssetOptions.ForceUpdate );
|
|
|
|
|
|
// Add to Scene
|
|
GameObject goFolder = new GameObject();
|
|
goFolder.name = "Slices";
|
|
|
|
GameObject go = new GameObject();
|
|
go.name = "slice";
|
|
go.transform.parent = goFolder.transform;
|
|
go.AddComponent<SpriteRenderer>();
|
|
|
|
// take sprite from file
|
|
var tempTex = AssetDatabase.LoadAssetAtPath(savePath, typeof(Sprite)) as Sprite;
|
|
go.GetComponent<SpriteRenderer>().sprite = tempTex;
|
|
if (addEdgeCollider)
|
|
{
|
|
EdgeCollider2D edge = go.AddComponent<EdgeCollider2D>();
|
|
List<Vector2> verts = new List<Vector2>();
|
|
|
|
for (int x = 0; x < targetWidth+edgeColliderResolution-1; x+=edgeColliderResolution)
|
|
{
|
|
bool foundedSurface=false;
|
|
for (int y = 0; y < targetHeight; y++)
|
|
{
|
|
Color c = sliceTex.GetPixel((int)Mathf.Clamp(x,0,targetWidth),y);
|
|
|
|
if (Mathf.Approximately(c.a,0))
|
|
{
|
|
verts.Add(new Vector2(x/100f,y/100f));
|
|
foundedSurface = true;
|
|
break;
|
|
}
|
|
} // y
|
|
if (!foundedSurface)
|
|
{
|
|
verts.Add(new Vector2(x/100f,targetHeight/100f));
|
|
}
|
|
} // x
|
|
edge.points = verts.ToArray();
|
|
}
|
|
// cleanup
|
|
UnityEngine.Object.DestroyImmediate(sliceTex);
|
|
} // Generate2DSlices
|
|
|
|
|
|
|
|
// http://answers.unity3d.com/questions/725895/best-way-to-mix-color-values.html
|
|
Color CombineColors(params Color[] aColors)
|
|
{
|
|
Color result = new Color(0,0,0,0);
|
|
foreach(Color c in aColors)
|
|
{
|
|
result += c;
|
|
}
|
|
result /= aColors.Length;
|
|
result.a = 1;
|
|
return result;
|
|
}
|
|
|
|
|
|
void ProgressBar(int y, int w)
|
|
{
|
|
if (y%10 == 0)
|
|
{
|
|
if (EditorUtility.DisplayCancelableProgressBar("Generating 2D Slices", "Calculating...", Mathf.InverseLerp(0.0f, (float)w, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remap value from one range to another
|
|
float ReMap(float val, float from1, float from2, float to1, float to2)
|
|
{
|
|
return to1 + (val-from1)*(to2-to1)/(from2-from1);
|
|
}
|
|
|
|
// update editor window
|
|
void OnInspectorUpdate()
|
|
{
|
|
Repaint();
|
|
}
|
|
|
|
public void FixTextureSettings(Texture2D texture)
|
|
{
|
|
if (texture==null) {Debug.LogError("FixFormat failed - Texture is null"); return;}
|
|
|
|
string path = AssetDatabase.GetAssetPath(texture);
|
|
|
|
if (string.IsNullOrEmpty(path)) {Debug.LogError("FixFormat failed - Texture path is null"); return;}
|
|
|
|
|
|
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
|
|
|
if (!textureImporter.isReadable)
|
|
{
|
|
Debug.Log("File:"+path+" needs fixing: wrong texture format or not marked as read/write allowed");
|
|
textureImporter.mipmapEnabled = false;
|
|
textureImporter.isReadable = true;
|
|
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
|
Debug.Log("fixed texture format for "+path);
|
|
}
|
|
|
|
// splatHasAlpha = textureImporter.DoesSourceTextureHaveAlpha();
|
|
|
|
}
|
|
|
|
} //class
|
|
|
|
} // namespace
|