1671 lines
54 KiB
C#
1671 lines
54 KiB
C#
/*
|
|
Tom's Terrain Tools for Unity 3D
|
|
Version 2.6 (2015)
|
|
(C)2015 by Tom Vogt <tom@lemuria.org> & Unitycoder.com <support@unitycoder.com>
|
|
http://lemuria.org/Unity/TTT/
|
|
http://unitycoder.com/blog/2014/08/14/asset-store-terrain-tools/
|
|
*/
|
|
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System.IO;
|
|
|
|
namespace TTT
|
|
{
|
|
public class TerrainTools : EditorWindow
|
|
{
|
|
public Terrain myTerrain;
|
|
|
|
private Object splatMap;
|
|
private bool defaultsDone = false;
|
|
private Texture2D[] splatTextures = new Texture2D[4];
|
|
private int tileSizeX=8;
|
|
private int tileSizeY=8;
|
|
private Texture2D[] splatTexNormals = new Texture2D[4];
|
|
private GameObject[] treeObjects = new GameObject[3];
|
|
private Texture2D[] grassTextures = new Texture2D[3];
|
|
private GameObject[] detailObjects = new GameObject[3]; // bushes, stones
|
|
|
|
public Texture2D splatA;
|
|
public Texture2D splatB;
|
|
|
|
public Texture2D treemap;
|
|
public bool resetTrees = true;
|
|
public float treeDensity = 0.4f;
|
|
public float treeThreshold = 0.1f;
|
|
public float treeSize = 1f;
|
|
public float sizeVariation = 0.2f;
|
|
|
|
public Texture2D grassmap;
|
|
public Texture2D bushmap;
|
|
public float grassDensity = 0.15f;
|
|
public float grassclumping = 0.5f;
|
|
public float bushDensity = 0.02f;
|
|
|
|
public Texture2D overlayMap;
|
|
public float overlayThreshold = 0.1f;
|
|
public Texture2D overlayTexture;
|
|
public int tileSize = 15;
|
|
public bool clearTrees = false;
|
|
public float clearRadius = 1.0f;
|
|
public bool clearGrass = true;
|
|
public float changeTerrain = 0.0f;
|
|
|
|
// internal variables
|
|
GUIStyle boldTitleStyle;
|
|
Vector2 scrollPosition = new Vector2(0,0);
|
|
bool toggleSplat = true;
|
|
bool toggleTrees = true;
|
|
bool toggleGrass = true;
|
|
bool toggleOverlay=false;
|
|
bool toggleAutoMagic=false;
|
|
bool toggleAdvanced=false;
|
|
|
|
// default terrain settings
|
|
int terrainInitWidth=2000;
|
|
int terrainInitLength=2000;
|
|
int terrainInitHeight=200;
|
|
float windSettingsSpeed = 0.2f;
|
|
float windSettingsSize = 0.2f;
|
|
float windSettingsBending = 0.2f;
|
|
|
|
bool assignNormalMapsIfFounded=true; // checks if texture has normals map in same folder: texturename.png & texturename_normal.png
|
|
|
|
string[] texturePackOptionTitles = new string[] {"Default", "Plain colors#1", "Debug"};
|
|
int selectedTexturePack = 0; // which texture pack is selected (file names are hardcoded at AutoMagicDefaults()
|
|
|
|
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); // timer for timing method durations
|
|
|
|
|
|
bool timeStampFileNames=false; // if enabled, every time you generate, new terrain is saved with a new name (as new terrain)
|
|
|
|
[MenuItem("Window/Terrain Tools/Terrain Tools",false,0)]
|
|
static void Init()
|
|
{
|
|
TerrainTools window = (TerrainTools)EditorWindow.GetWindow(typeof(TerrainTools));
|
|
window.titleContent = new GUIContent("TerrainTools");
|
|
window.minSize = new Vector2(410,400);
|
|
window.Show();
|
|
}
|
|
|
|
void OnInspectorUpdate()
|
|
{
|
|
Repaint();
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
|
|
|
DrawTitleGUI();
|
|
|
|
boldTitleStyle = new GUIStyle(EditorStyles.foldout);
|
|
boldTitleStyle.fontStyle = FontStyle.Bold;
|
|
|
|
EditorGUILayout.Separator();
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.Label("Terrain");
|
|
myTerrain = (Terrain)EditorGUILayout.ObjectField("", myTerrain, typeof(Terrain),true);
|
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Separator();
|
|
|
|
DrawAutoMagicGUI();
|
|
DrawTexturingGUI();
|
|
DrawTreeDistributionGUI();
|
|
DrawGrassGUI();
|
|
DrawOverlayGUI();
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
|
|
static void DrawTitleGUI ()
|
|
{
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Terrain Tools v2.6", EditorStyles.boldLabel);
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Read Manual")) {
|
|
Help.BrowseURL ("http://lemuria.org/Unity/TTT/");
|
|
}
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Separator ();
|
|
}
|
|
|
|
|
|
void DrawAutoMagicGUI()
|
|
{
|
|
toggleAutoMagic = EditorGUILayout.Foldout (toggleAutoMagic, "AutoMagic", boldTitleStyle);
|
|
if (toggleAutoMagic)
|
|
{
|
|
if (!defaultsDone) AutoMagicDefaults ();
|
|
GUILayout.BeginHorizontal ();
|
|
splatMap = EditorGUILayout.ObjectField (new GUIContent ("Splatmap Object", "Heightmap, Splatmap, Treemap, Grassmap"), splatMap, typeof(Object), false);
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel("Textures");
|
|
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();
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel("Trees");
|
|
GUILayout.FlexibleSpace();
|
|
treeObjects[0] = (GameObject)EditorGUILayout.ObjectField (treeObjects [0], typeof(GameObject), false);
|
|
treeObjects[1] = (GameObject)EditorGUILayout.ObjectField (treeObjects [1], typeof(GameObject), false);
|
|
treeObjects[2] = (GameObject)EditorGUILayout.ObjectField (treeObjects [2], typeof(GameObject), false);
|
|
GUILayout.EndHorizontal();
|
|
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel ("Grass");
|
|
GUILayout.FlexibleSpace ();
|
|
grassTextures[0] = (Texture2D)EditorGUILayout.ObjectField (grassTextures [0], typeof(Texture2D), false, GUILayout.Height (32));
|
|
grassTextures[1] = (Texture2D)EditorGUILayout.ObjectField (grassTextures [1], typeof(Texture2D), false, GUILayout.Height (32));
|
|
grassTextures[2] = (Texture2D)EditorGUILayout.ObjectField (grassTextures [2], typeof(Texture2D), false, GUILayout.Height (32));
|
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Separator();
|
|
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUILayout.PrefixLabel("Detail meshes");
|
|
GUILayout.FlexibleSpace();
|
|
detailObjects[0] = (GameObject)EditorGUILayout.ObjectField (detailObjects[0], typeof(GameObject), false);
|
|
detailObjects[1] = (GameObject)EditorGUILayout.ObjectField (detailObjects[1], typeof(GameObject), false);
|
|
detailObjects[2] = (GameObject)EditorGUILayout.ObjectField (detailObjects[2], typeof(GameObject), false);
|
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Separator();
|
|
|
|
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button ("Run AutoMagic", GUILayout.Width (200), GUILayout.Height (32)))
|
|
{
|
|
AutoMagic();
|
|
}
|
|
|
|
GUILayout.FlexibleSpace ();
|
|
GUILayout.EndHorizontal ();
|
|
|
|
|
|
toggleAdvanced = EditorGUILayout.Foldout(toggleAdvanced, "Advanced Settings", boldTitleStyle);
|
|
if (toggleAdvanced)
|
|
{
|
|
// texture pack selection
|
|
GUI.changed = false;
|
|
selectedTexturePack = EditorGUILayout.Popup("Texture pack",selectedTexturePack, texturePackOptionTitles);
|
|
if (GUI.changed)
|
|
{
|
|
AutoMagicDefaults(); // reload texture on change
|
|
}else{
|
|
}
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Apply Textures to Terrain", GUILayout.Width (200), GUILayout.Height (22)))
|
|
{
|
|
ApplySelectedTexturesToTerrain();
|
|
}
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Separator();
|
|
|
|
// create timestamped filenames for terrain (unique terrains for each generation)
|
|
timeStampFileNames = EditorGUILayout.Toggle("Unique terrain files",timeStampFileNames);
|
|
EditorGUILayout.Separator();
|
|
|
|
// terrain default settings
|
|
terrainInitWidth = EditorGUILayout.IntSlider ("Terrain Width",terrainInitWidth,100,8000);
|
|
terrainInitLength = EditorGUILayout.IntSlider ("Terrain Length",terrainInitLength,100,8000);
|
|
terrainInitHeight = EditorGUILayout.IntSlider ("Terrain Height",terrainInitHeight,100,8000);
|
|
tileSizeY = EditorGUILayout.IntField("Texture Tiling Y",(int)Mathf.Clamp(tileSizeY,1,9999));
|
|
tileSizeX = EditorGUILayout.IntField("Texture Tiling X",(int)Mathf.Clamp(tileSizeX,1,9999));
|
|
EditorGUILayout.Separator();
|
|
|
|
// misc options
|
|
assignNormalMapsIfFounded = EditorGUILayout.Toggle("Use normal maps if founded",assignNormalMapsIfFounded);
|
|
|
|
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace ();
|
|
EditorGUILayout.Separator ();
|
|
}
|
|
|
|
void DrawTexturingGUI ()
|
|
{
|
|
toggleSplat = EditorGUILayout.Foldout (toggleSplat, "Texturing", boldTitleStyle);
|
|
if (toggleSplat) {
|
|
GUILayout.BeginHorizontal ();
|
|
EditorGUILayout.PrefixLabel ("First Splatmap");
|
|
GUILayout.FlexibleSpace ();
|
|
splatA = (Texture2D)EditorGUILayout.ObjectField ("", splatA, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Separator ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Second Splatmap (optional)");
|
|
GUILayout.FlexibleSpace ();
|
|
splatB = (Texture2D)EditorGUILayout.ObjectField ("", splatB, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Apply Splatmap(s)", GUILayout.Width (200), GUILayout.Height (32))) {
|
|
if (CheckSplatmap())
|
|
{
|
|
ApplySplatmap();
|
|
}else{
|
|
Debug.LogError("No first splatmap assigned");
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace ();
|
|
GUILayout.EndHorizontal ();
|
|
}
|
|
EditorGUILayout.Separator ();
|
|
}
|
|
|
|
void DrawTreeDistributionGUI ()
|
|
{
|
|
toggleTrees = EditorGUILayout.Foldout (toggleTrees, "Tree Distribution", boldTitleStyle);
|
|
if (toggleTrees) {
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Tree map");
|
|
GUILayout.FlexibleSpace ();
|
|
treemap = (Texture2D)EditorGUILayout.ObjectField ("", treemap, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Clear Existing Trees?");
|
|
GUILayout.FlexibleSpace ();
|
|
resetTrees = EditorGUILayout.Toggle ("remove trees", resetTrees);
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Separator ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Tree Density");
|
|
GUILayout.FlexibleSpace ();
|
|
treeDensity = EditorGUILayout.Slider (treeDensity, 0.05f, 3f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Threshold");
|
|
GUILayout.FlexibleSpace ();
|
|
treeThreshold = EditorGUILayout.Slider (treeThreshold, 0.01f, 0.99f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Tree Size");
|
|
GUILayout.FlexibleSpace ();
|
|
treeSize = EditorGUILayout.Slider (treeSize, 0.2f, 5f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Size Variation");
|
|
GUILayout.FlexibleSpace ();
|
|
sizeVariation = EditorGUILayout.Slider (sizeVariation, 0f, 1f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Generate Trees", GUILayout.Width (200), GUILayout.Height (32)))
|
|
{
|
|
if (CheckTreemap()) ApplyTreemap();
|
|
}
|
|
GUILayout.FlexibleSpace ();
|
|
GUILayout.EndHorizontal ();
|
|
}
|
|
EditorGUILayout.Separator ();
|
|
}
|
|
|
|
void DrawGrassGUI ()
|
|
{
|
|
toggleGrass = EditorGUILayout.Foldout (toggleGrass, "Grass and Details", boldTitleStyle);
|
|
if (toggleGrass) {
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Grass map");
|
|
GUILayout.FlexibleSpace ();
|
|
grassmap = (Texture2D)EditorGUILayout.ObjectField ("", grassmap, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
EditorGUILayout.Separator ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Bush/Detail map");
|
|
GUILayout.FlexibleSpace ();
|
|
bushmap = (Texture2D)EditorGUILayout.ObjectField ("", bushmap, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Grass Density");
|
|
GUILayout.FlexibleSpace ();
|
|
grassDensity = EditorGUILayout.Slider (grassDensity, 0.01f, 3f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Grass Clumping");
|
|
GUILayout.FlexibleSpace ();
|
|
grassclumping = EditorGUILayout.Slider (grassclumping, 0f, 1f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Bush/Detail Density");
|
|
GUILayout.FlexibleSpace ();
|
|
bushDensity = EditorGUILayout.Slider (bushDensity, 0.01f, 2f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Generate Grass and Details", GUILayout.Width (200), GUILayout.Height (32)))
|
|
{
|
|
if (CheckGrassmap ()) ApplyGrassmap();
|
|
}
|
|
GUILayout.FlexibleSpace ();
|
|
GUILayout.EndHorizontal ();
|
|
}
|
|
EditorGUILayout.Separator ();
|
|
}
|
|
|
|
void DrawOverlayGUI ()
|
|
{
|
|
toggleOverlay = EditorGUILayout.Foldout (toggleOverlay, "Overlays (roads, rivers, etc)", boldTitleStyle);
|
|
if (toggleOverlay)
|
|
{
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Overlay map");
|
|
GUILayout.FlexibleSpace ();
|
|
overlayMap = (Texture2D)EditorGUILayout.ObjectField ("", overlayMap, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Threshold");
|
|
GUILayout.FlexibleSpace ();
|
|
overlayThreshold = EditorGUILayout.Slider (overlayThreshold, 0.1f, 0.9f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Overlay texture");
|
|
GUILayout.FlexibleSpace ();
|
|
overlayTexture = (Texture2D)EditorGUILayout.ObjectField ("", overlayTexture, typeof(Texture2D), false);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Tile size");
|
|
GUILayout.FlexibleSpace ();
|
|
tileSize = EditorGUILayout.IntSlider (tileSize, 3, 127);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Clear trees");
|
|
GUILayout.FlexibleSpace ();
|
|
clearTrees = EditorGUILayout.Toggle ("", clearTrees);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Clear tree radius");
|
|
GUILayout.FlexibleSpace ();
|
|
clearRadius = EditorGUILayout.Slider (clearRadius, 0.5f, 10.0f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Clear grass/detail");
|
|
GUILayout.FlexibleSpace ();
|
|
clearGrass = EditorGUILayout.Toggle ("", clearGrass);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.Label ("Raise/lower terrain");
|
|
GUILayout.FlexibleSpace ();
|
|
changeTerrain = EditorGUILayout.Slider (changeTerrain, -50f, 50f);
|
|
GUILayout.EndHorizontal ();
|
|
GUILayout.BeginHorizontal ();
|
|
GUILayout.FlexibleSpace ();
|
|
if (GUILayout.Button ("Generate Overlay", GUILayout.Width (200), GUILayout.Height (32)))
|
|
{
|
|
if (CheckOverlaymap()) ApplyOverlaymap ();
|
|
}
|
|
GUILayout.FlexibleSpace ();
|
|
GUILayout.EndHorizontal ();
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
public static string Reverse(string text)
|
|
{
|
|
if (text == null) return null;
|
|
|
|
// this was posted by petebob as well
|
|
char[] array = text.ToCharArray();
|
|
System.Array.Reverse(array);
|
|
return new string(array);
|
|
}
|
|
|
|
public string FindFile(string basename)
|
|
{
|
|
string[] extensions = {"tif", "tiff", "png", "jpg", "jpeg","TIF", "TIFF", "PNG", "JPG", "JPEG"};
|
|
foreach (string ext in extensions)
|
|
{
|
|
string filename = basename + "." + ext;
|
|
|
|
// Debug.Log(filename);
|
|
if (File.Exists(filename))
|
|
{
|
|
return filename;
|
|
}
|
|
}
|
|
return null;
|
|
|
|
}
|
|
|
|
void AutoMagic()
|
|
{
|
|
if (splatMap == null) {Debug.LogError("No splatmap assigned"); return;}
|
|
if (!ValidateAutomagicTextures()) {Debug.LogError("Not all textures are assigned"); return;}
|
|
if (!ValidateAutomagicTrees()) {Debug.LogError("Not all trees are assigned"); return;}
|
|
// TODO: validate grass and bushes?
|
|
|
|
string origPath = AssetDatabase.GetAssetPath(splatMap);
|
|
string pathrev = Reverse(Path.GetFileNameWithoutExtension(origPath));
|
|
string[] parts = pathrev.Split(new char[] { '-' }, 2);
|
|
|
|
if (parts==null || parts.Length<2) {Debug.LogError("Filenames must start with \"number\" and \"-\" character. Example: 1-splatmap.tif"); return;}
|
|
|
|
string basename = Reverse(parts[1]);
|
|
string dirname = Path.GetDirectoryName(origPath);
|
|
string fixedPath = Path.Combine(dirname, basename);
|
|
|
|
string splatmap_name = FindFile(fixedPath+"-splatmap");
|
|
|
|
if (!File.Exists(splatmap_name)) {Debug.LogError("Splatmap file not found:"+splatmap_name+" \n Make sure that your filenames start with \"number\" and \"-\" character. Example: 1-splatmap.tif"); return;}
|
|
splatA = AssetDatabase.LoadAssetAtPath(splatmap_name, typeof(Texture2D)) as Texture2D;
|
|
CheckSplatmap();
|
|
|
|
string treemap_name = FindFile(fixedPath+"-treemap");
|
|
if (File.Exists(treemap_name))
|
|
{
|
|
//Debug.LogError("Treemap file not found:"+treemap_name+" \n Make sure that your filenames start with \"number\" and \"-\" character. Example: 1-splatmap.tif"); return;}
|
|
treemap = AssetDatabase.LoadAssetAtPath(treemap_name, typeof(Texture2D)) as Texture2D;
|
|
CheckTreemap();
|
|
}else{
|
|
treemap_name=null;
|
|
}
|
|
|
|
string grassmap_name = FindFile(fixedPath+"-grassmap");
|
|
if (File.Exists(grassmap_name)) {
|
|
//Debug.LogError("Grassmap file not found:"+grassmap_name+" \n Make sure that your filenames start with \"number\" and \"-\" character. Example: 1-splatmap.tif"); return;}
|
|
grassmap = AssetDatabase.LoadAssetAtPath(grassmap_name, typeof(Texture2D)) as Texture2D;
|
|
CheckGrassmap();
|
|
}else{
|
|
grassmap_name=null;
|
|
}
|
|
|
|
string bushmap_name = FindFile(fixedPath+"-bushmap");
|
|
if (File.Exists(bushmap_name)) {
|
|
//Debug.LogError("Grassmap file not found:"+grassmap_name+" \n Make sure that your filenames start with \"number\" and \"-\" character. Example: 1-splatmap.tif"); return;}
|
|
bushmap = AssetDatabase.LoadAssetAtPath(bushmap_name, typeof(Texture2D)) as Texture2D;
|
|
CheckGrassmap();
|
|
}else{
|
|
grassmap_name=null;
|
|
}
|
|
|
|
|
|
CreateTerrain(fixedPath, splatA.width);
|
|
|
|
if (myTerrain.terrainData==null) {Debug.LogError("Terrain generation failed.."); return;};
|
|
|
|
string heightmap_name = fixedPath+"-heightmap.raw";
|
|
myTerrain.terrainData.SetHeights(0, 0, ReadRawHeightMap(heightmap_name, splatA.width+1,myTerrain.terrainData.heightmapResolution,myTerrain.terrainData.heightmapResolution));
|
|
|
|
ApplySelectedTexturesToTerrain();
|
|
ApplySplatmap();
|
|
|
|
if (treemap_name!=null)
|
|
{
|
|
myTerrain.terrainData.treePrototypes = SetupTrees();
|
|
myTerrain.terrainData.RefreshPrototypes();
|
|
ApplyTreemap();
|
|
}
|
|
|
|
if (grassmap_name!=null)
|
|
{
|
|
myTerrain.terrainData.detailPrototypes = SetupGrassAndDetails();
|
|
myTerrain.terrainData.RefreshPrototypes();
|
|
ApplyGrassmap();
|
|
}
|
|
/*
|
|
if (bushmap_name!=null)
|
|
{
|
|
myTerrain.terrainData.detailPrototypes = SetupDetailMeshes();
|
|
myTerrain.terrainData.RefreshPrototypes();
|
|
ApplyBushmap();
|
|
}*/
|
|
|
|
}
|
|
|
|
void AutoMagicDefaults()
|
|
{
|
|
// TODO: add normals also, for now just clear them
|
|
for (int i = 0; i < splatTexNormals.Length; i++) {
|
|
splatTexNormals[i]=null;
|
|
}
|
|
|
|
|
|
|
|
string tempFilePath = "";
|
|
string tempFileName = "";
|
|
switch (selectedTexturePack)
|
|
{
|
|
// Default texture set #1
|
|
case 0:
|
|
tempFilePath = "Assets/Terrain Assets/Textures/";
|
|
tempFileName = "Grass (Meadows).jpg";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[0] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "Grass (Hill).jpg";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[1] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "Grass&Rock.jpg";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[2] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "Grass (Muddy).jpg";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[3] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
break;
|
|
|
|
// Default texture set #2
|
|
case 1:
|
|
tempFilePath = "Assets/Terrain Assets/Textures/SingleColor/";
|
|
tempFileName = "green_color_tile-1.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[0] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "brown_color_tile-2.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[1] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "lite_green_color_tile-3.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[2] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "lite_green2_color_tile-4.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[3] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
break;
|
|
|
|
// Default texture set #3
|
|
case 2:
|
|
tempFilePath = "Assets/Terrain Assets/Textures/Debug/";
|
|
tempFileName = "debug_red.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[0] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "debug_green.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[1] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "debug_blue.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[2] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
tempFileName = "debug_white.png";
|
|
if (File.Exists(tempFilePath+tempFileName)) splatTextures[3] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
break;
|
|
|
|
default:
|
|
Debug.LogError("Invalid selectedTexturePack value : "+selectedTexturePack);
|
|
break;
|
|
}
|
|
|
|
// default trees
|
|
tempFilePath = "Assets/Terrain Assets/Trees Ambient-Occlusion/";
|
|
tempFileName = "Alder.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName)) treeObjects[0] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
|
|
tempFileName = "Sycamore.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName)) treeObjects[1] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
|
|
tempFileName = "Mimosa.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName)) treeObjects[2] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
|
|
// default grass
|
|
tempFilePath = "Assets/Standard Assets/Environment/TerrainAssets/BillboardTextures/";
|
|
tempFileName = "GrassFrond01AlbedoAlpha.psd";
|
|
if (File.Exists(tempFilePath+tempFileName)) grassTextures[0] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
|
|
tempFilePath = "Assets/Terrain Assets/Grass/";
|
|
tempFileName = "Fern.psd";
|
|
if (File.Exists(tempFilePath+tempFileName)) grassTextures[1] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
|
|
tempFilePath = "Assets/Standard Assets/Environment/TerrainAssets/BillboardTextures/";
|
|
tempFileName = "GrassFrond02AlbedoAlpha.psd";
|
|
if (File.Exists(tempFilePath+tempFileName)) grassTextures[2] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(Texture2D));
|
|
|
|
|
|
// default detail meshes
|
|
tempFilePath = "Assets/Terrain Assets/Bushes/";
|
|
tempFileName = "Bush5.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName))
|
|
{
|
|
detailObjects[0] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
}else{
|
|
Debug.LogWarning("Default object missing : "+tempFilePath+tempFileName);
|
|
}
|
|
tempFileName = "Bush7.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName)) {
|
|
detailObjects[1] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
}else{
|
|
Debug.LogWarning("Default object missing : "+tempFilePath+tempFileName);
|
|
}
|
|
tempFilePath = "Assets/Terrain Assets/Rocks/";
|
|
tempFileName = "RockMesh.fbx";
|
|
if (File.Exists(tempFilePath+tempFileName)) {
|
|
detailObjects[2] = (GameObject)AssetDatabase.LoadAssetAtPath(tempFilePath+tempFileName, typeof(GameObject));
|
|
}else{
|
|
Debug.LogWarning("Default object missing : "+tempFilePath+tempFileName);
|
|
}
|
|
|
|
defaultsDone = true;
|
|
}
|
|
|
|
bool ValidateAutomagicTextures()
|
|
{
|
|
if (splatTextures[0]==null || splatTextures[1]==null || splatTextures[2]==null || splatTextures[3]==null) return false;
|
|
return true;
|
|
}
|
|
|
|
bool ValidateAutomagicTrees()
|
|
{
|
|
if (treeObjects[0]==null || treeObjects[1]==null || treeObjects[2]==null) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
SplatPrototype[] SetupTextures()
|
|
{
|
|
SetupTextureNormals();
|
|
|
|
//Vector2 tilesize = new Vector2(40f, 40f);
|
|
|
|
SplatPrototype[] Splat = new SplatPrototype[4];
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
Splat[i] = new SplatPrototype();
|
|
Splat[i].texture = splatTextures[i];
|
|
if (assignNormalMapsIfFounded) Splat[i].normalMap = splatTexNormals[i];
|
|
Splat[i].tileSize = new Vector2(tileSizeX,tileSizeY);
|
|
}
|
|
|
|
return Splat;
|
|
}
|
|
|
|
void SetupTextureNormals()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
string tempFileName = AssetDatabase.GetAssetPath(splatTextures[i]);
|
|
string tempFileWithoutExtension = Path.GetFileNameWithoutExtension(tempFileName);
|
|
if (assignNormalMapsIfFounded)
|
|
{
|
|
string tempNormalMapFile = Path.GetDirectoryName(tempFileName)+Path.DirectorySeparatorChar+tempFileWithoutExtension+"_normal"+Path.GetExtension(tempFileName);
|
|
if (File.Exists(tempNormalMapFile)) splatTexNormals[i] = (Texture2D)AssetDatabase.LoadAssetAtPath(tempNormalMapFile, typeof(Texture2D));
|
|
}
|
|
}
|
|
}
|
|
|
|
TreePrototype[] SetupTrees() {
|
|
TreePrototype[] trees = new TreePrototype[3];
|
|
trees[0] = new TreePrototype();
|
|
trees[0].prefab = treeObjects[0];
|
|
trees[1] = new TreePrototype();
|
|
trees[1].prefab = treeObjects[1];
|
|
trees[2] = new TreePrototype();
|
|
trees[2].prefab = treeObjects[2];
|
|
return trees;
|
|
}
|
|
|
|
DetailPrototype[] SetupGrassAndDetails()
|
|
{
|
|
// TODO: only take used amount from automagic, skip nulls
|
|
DetailPrototype[] details = new DetailPrototype[6];
|
|
|
|
|
|
// grass
|
|
details[0] = new DetailPrototype();
|
|
if (grassTextures[0]!=null)
|
|
{
|
|
details[0].prototypeTexture = grassTextures[0];
|
|
details[0].renderMode = DetailRenderMode.Grass;
|
|
details[0].usePrototypeMesh=false;
|
|
details[0].minHeight = 0.55f;
|
|
details[0].maxHeight = 0.77f;
|
|
details[0].healthyColor = Color.white;
|
|
details[0].dryColor = Color.gray;
|
|
|
|
}
|
|
details[1] = new DetailPrototype();
|
|
if (grassTextures[1]!=null)
|
|
{
|
|
details[1].prototypeTexture = grassTextures[1];
|
|
details[1].renderMode = DetailRenderMode.Grass;
|
|
details[1].usePrototypeMesh=false;
|
|
details[1].minHeight = 0.66f;
|
|
details[1].maxHeight = 0.88f;
|
|
details[1].healthyColor = Color.white;
|
|
details[1].dryColor = Color.gray;
|
|
}
|
|
details[2] = new DetailPrototype();
|
|
if (grassTextures[2]!=null)
|
|
{
|
|
details[2].prototypeTexture = grassTextures[2];
|
|
details[2].renderMode = DetailRenderMode.Grass;
|
|
details[2].usePrototypeMesh=false;
|
|
details[2].minHeight = 0.77f;
|
|
details[2].maxHeight = 0.99f;
|
|
details[2].healthyColor = Color.white;
|
|
details[2].dryColor = Color.gray;
|
|
}
|
|
|
|
|
|
// meshes
|
|
details[3] = new DetailPrototype();
|
|
if (detailObjects[0]!=null)
|
|
{
|
|
details[3].prototype = detailObjects[0];
|
|
details[3].renderMode = DetailRenderMode.Grass; // for default grass
|
|
details[3].usePrototypeMesh=true;
|
|
details[3].healthyColor = Color.white;
|
|
details[3].dryColor = Color.gray;
|
|
}
|
|
|
|
details[4] = new DetailPrototype();
|
|
if (detailObjects[1]!=null)
|
|
{
|
|
details[4].prototype = detailObjects[1];
|
|
details[4].renderMode = DetailRenderMode.Grass; // for default grass
|
|
details[4].usePrototypeMesh=true;
|
|
details[4].healthyColor = Color.white;
|
|
details[4].dryColor = Color.gray;
|
|
}
|
|
|
|
details[5] = new DetailPrototype();
|
|
if (detailObjects[2]!=null)
|
|
{
|
|
details[5].prototype = detailObjects[2];
|
|
details[5].renderMode = DetailRenderMode.VertexLit; // for default mesh rock
|
|
details[5].usePrototypeMesh=true;
|
|
details[5].healthyColor = Color.white;
|
|
details[5].dryColor = Color.gray;
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
/*
|
|
DetailPrototype[] SetupDetailMeshes()
|
|
{
|
|
// if we already have some details keep them, for now just skip default 3 grass details
|
|
DetailPrototype[] detailMeshes = new DetailPrototype[3];
|
|
|
|
detailMeshes[0] = new DetailPrototype();
|
|
detailMeshes[0].prototype = detailObjects[0];
|
|
detailMeshes[1] = new DetailPrototype();
|
|
if (detailMeshes[1]!=null)
|
|
{
|
|
detailMeshes[1].prototype = detailObjects[1];
|
|
}
|
|
detailMeshes[2] = new DetailPrototype();
|
|
if (detailMeshes[2]!=null)
|
|
{
|
|
detailMeshes[2].prototype = detailObjects[2];
|
|
}
|
|
return detailMeshes;
|
|
}*/
|
|
|
|
|
|
bool CheckSplatmap()
|
|
{
|
|
if (splatA==null) return false;
|
|
FixTextureSettings(splatA);
|
|
|
|
if (splatA.height != splatA.width)
|
|
{
|
|
EditorUtility.DisplayDialog("Wrong size", "First splatmap must be square (width and height must be the same)", "Cancel");
|
|
return false;
|
|
}
|
|
|
|
if (Mathf.ClosestPowerOfTwo(splatA.width) != splatA.width) {
|
|
EditorUtility.DisplayDialog("Wrong size", "Splatmap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
|
|
if (splatB!=null)
|
|
{
|
|
FixTextureSettings(splatB);
|
|
if (splatB.height != splatB.width)
|
|
{
|
|
EditorUtility.DisplayDialog("Wrong size", "Second splatmap must be square (width and height must be the same)", "Cancel");
|
|
return false;
|
|
}
|
|
|
|
if (Mathf.ClosestPowerOfTwo(splatB.width) != splatB.width)
|
|
{
|
|
EditorUtility.DisplayDialog("Wrong size", "Second splatmap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ApplySelectedTexturesToTerrain()
|
|
{
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
if (myTerrain==null) {Debug.LogError("No terrain selected"); return;}
|
|
myTerrain.terrainData.splatPrototypes = SetupTextures();
|
|
}
|
|
|
|
|
|
void ApplySplatmap()
|
|
{
|
|
StartTimer();
|
|
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
if (myTerrain==null) {Debug.LogError("No terrain selected"); return;}
|
|
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
if (terrainData==null) {Debug.LogError("Failed getting terrain data"); return;}
|
|
|
|
if (terrainData.alphamapLayers<4)
|
|
{
|
|
EditorUtility.DisplayDialog("Missing Terrain Textures", "Please set up at least 4 textures in the terrain painter dialog", "Cancel");
|
|
return;
|
|
}
|
|
|
|
if (splatB!=null && terrainData.alphamapLayers<8)
|
|
{
|
|
EditorUtility.DisplayDialog("Missing Terrain Textures", "Please set up at least 8 textures in the terrain painter dialog", "Cancel");
|
|
return;
|
|
}
|
|
|
|
int width = splatA.width;
|
|
bool TwoMaps = false;
|
|
|
|
if (splatB==null)
|
|
{
|
|
//splatB = new Texture2D(w, w, TextureFormat.ARGB32, false);
|
|
} else {
|
|
if (splatA.width != splatB.width && splatA.height != splatB.height) {
|
|
Debug.LogError("Both splatmaps must have same resolution ("+splatA.width+" != "+splatB.width+")");
|
|
return;
|
|
}
|
|
TwoMaps=true;
|
|
}
|
|
|
|
|
|
Undo.RecordObject(terrainData, "Apply splatmap(s)"); // very slow..
|
|
|
|
terrainData.alphamapResolution = width;
|
|
|
|
float[,,] splatmapData = terrainData.GetAlphamaps(0, 0, width, width);
|
|
Color[] splatmapColors = splatA.GetPixels();
|
|
Color[] splatmapColors_b=null; //= splatB.GetPixels();
|
|
|
|
if (TwoMaps)
|
|
{
|
|
splatmapColors_b = splatB.GetPixels();
|
|
}else{
|
|
// DestroyImmediate(splatB);
|
|
//splatB = null;
|
|
}
|
|
|
|
Color col=Color.clear;
|
|
Color col_b=Color.clear;
|
|
|
|
for (int y = 0; y < width; y++)
|
|
{
|
|
|
|
if (y%10 == 0) // update progress bar every now and then
|
|
{
|
|
if (EditorUtility.DisplayCancelableProgressBar("Applying splatmap", "calculating...", Mathf.InverseLerp(0.0f, (float)width, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
float sum;
|
|
|
|
// Color col = splatmapColors[((w-1)-x)*w + y];
|
|
// Color col_b = splatmapColors_b[((w-1)-x)*w + y];
|
|
//col = splatmapColors[ ((w-1)-y)*w + x]; // TODO: get rid of flip
|
|
col = splatmapColors[x*width+y];
|
|
|
|
|
|
if (TwoMaps)
|
|
{
|
|
// col_b = splatmapColors_b[((w-1)-y)*w + x];
|
|
col_b = splatmapColors_b[y*width+x];
|
|
sum = col.r+col.g+col.b+col_b.r+col_b.g+col_b.b;
|
|
} else {
|
|
sum = col.r+col.g+col.b;
|
|
//sum = col.r+col.g+col.b+(splatHasAlpha?col.a:0);
|
|
}
|
|
|
|
if (sum>1.0f)
|
|
{
|
|
// no final channel, and scale down
|
|
splatmapData[x,y,0] = col.r / sum;
|
|
splatmapData[x,y,1] = col.g / sum;
|
|
splatmapData[x,y,2] = col.b / sum;
|
|
//splatmapData[x,y,3] = col.a / sum;
|
|
splatmapData[x,y,3] = 1f-sum;
|
|
|
|
// splatmapData[y,x,0] = col.r / sum; // TODO: these parts will be later used for invert options
|
|
// splatmapData[y,x,1] = col.g / sum;
|
|
// splatmapData[y,x,2] = col.b / sum;
|
|
if (TwoMaps)
|
|
{
|
|
splatmapData[x,y,4] = col_b.r / sum;
|
|
splatmapData[x,y,5] = col_b.g / sum;
|
|
splatmapData[x,y,6] = col_b.b / sum;
|
|
|
|
|
|
splatmapData[x,y,7] = 1f-sum;
|
|
// splatmapData[x,y,6] = 0.0f;
|
|
} else { // single map
|
|
// TODO: need to reset old splats, if previously had 2 splatmaps?
|
|
|
|
// splatmapData[x,y,4] = 0.0f;
|
|
// reset old maps
|
|
// splatmapData[x,y,4] = 0;
|
|
//splatmapData[x,y,5] = 0;
|
|
//splatmapData[x,y,6] = 0;
|
|
//splatmapData[x,y,7] = 0;
|
|
}
|
|
|
|
} else { // not over 1.0
|
|
|
|
// splatmapData[w-1-y,x,0] = col.r;
|
|
// splatmapData[w-1-y,x,1] = col.g;
|
|
// splatmapData[w-1-y,x,2] = col.b;
|
|
// derive final channel from white
|
|
splatmapData[x,y,0] = col.r;
|
|
splatmapData[x,y,1] = col.g;
|
|
splatmapData[x,y,2] = col.b;
|
|
splatmapData[x,y,3] = 1f-sum;
|
|
|
|
if (TwoMaps)
|
|
{
|
|
//Debug.Log("asddfasdf");
|
|
//return;
|
|
splatmapData[x,y,4] = col_b.r;
|
|
splatmapData[x,y,5] = col_b.g;
|
|
splatmapData[x,y,6] = col_b.b;
|
|
splatmapData[x,y,7] = 1f-sum;
|
|
} else {
|
|
//splatmapData[x,y,3] = 1.0f - sum;
|
|
// reset old maps
|
|
//splatmapData[x,y,4] = 0;
|
|
//splatmapData[x,y,5] = 0;
|
|
//splatmapData[x,y,6] = 0;
|
|
//splatmapData[x,y,7] = 0;
|
|
}
|
|
}
|
|
} // for splatmap x pixels
|
|
} // for splatmap y pixels
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
terrainData.SetAlphamaps(0, 0, splatmapData);
|
|
Debug.Log("Splatmaps applied "+GetTimerTime());
|
|
}
|
|
|
|
|
|
bool CheckTreemap()
|
|
{
|
|
if (treemap == null) return false;
|
|
FixTextureSettings(treemap);
|
|
|
|
if (treemap.height != treemap.width) {
|
|
EditorUtility.DisplayDialog("Wrong size", "treemap width and height must be the same", "Cancel");
|
|
return false;
|
|
}
|
|
if (Mathf.ClosestPowerOfTwo(treemap.width) != treemap.width) {
|
|
EditorUtility.DisplayDialog("Wrong size", "treemap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void ApplyTreemap()
|
|
{
|
|
StartTimer();
|
|
// set up my data
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
|
|
if (myTerrain==null) {Debug.LogError("No terrain selected"); return;}
|
|
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
|
|
// check if trees are assigned
|
|
if (terrainData.treePrototypes.Length<3)
|
|
{
|
|
if (terrainData.treePrototypes.Length<1)
|
|
{
|
|
Debug.LogError("You should assign at least 1 tree to Terrain (at Place Trees tab)");
|
|
return;
|
|
}else{
|
|
Debug.LogWarning("You should assign 3 trees to Terrain to use all 3 splatmap color channels (at Place Trees tab)");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Undo.RecordObject(terrainData, "Apply tree map"); // slow..
|
|
|
|
int width = treemap.width;
|
|
|
|
Color[] mapColors = treemap.GetPixels();
|
|
|
|
int index = -1;
|
|
int maxIndex=terrainData.treePrototypes.Length;
|
|
int trees = 0;
|
|
|
|
float Step = 1.0f/treeDensity;
|
|
float PositionVariation = (float)(Step*0.5f/(float)width);
|
|
|
|
if (resetTrees) terrainData.treeInstances = new TreeInstance[0];
|
|
|
|
int progress = 0;
|
|
|
|
|
|
|
|
for (float y = 1; y < width-1; y+=Step)
|
|
{
|
|
progress++;
|
|
if (progress%10 == 0)
|
|
{
|
|
progress=0;
|
|
if (EditorUtility.DisplayCancelableProgressBar("Placing trees", "placed "+trees+" trees so far", Mathf.InverseLerp(0.0f, (float)width, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (float x = 1; x < width-1; x+=Step)
|
|
{
|
|
// place the chosen tree, if the colours are right
|
|
index = -1;
|
|
//Color col = mapColors[Mathf.RoundToInt(y)*w + Mathf.RoundToInt(x)];
|
|
Color col = mapColors[(width-Mathf.RoundToInt(y))*width+Mathf.RoundToInt(x)];
|
|
//width-x,length-y
|
|
|
|
if (col.r>treeThreshold+Random.Range(0.0f, 1.0f))
|
|
{
|
|
index = 0;
|
|
}
|
|
else if (maxIndex>1 && col.g>treeThreshold+Random.Range(0.0f, 1.0f))
|
|
{
|
|
index = 1;
|
|
}
|
|
else if (maxIndex>2 && col.b>treeThreshold+Random.Range(0.0f, 1.0f))
|
|
{
|
|
index = 2;
|
|
}
|
|
|
|
|
|
if (index>=0)
|
|
{
|
|
// place a tree
|
|
trees++;
|
|
TreeInstance treeInstance = new TreeInstance();
|
|
|
|
// random placement modifier for a more natural look
|
|
float xpos = x/(float)width;
|
|
float ypos = y/(float)width;
|
|
xpos = Mathf.Clamp01(xpos+Random.Range(-PositionVariation, PositionVariation));
|
|
ypos = Mathf.Clamp01(1-ypos+Random.Range(-PositionVariation, PositionVariation));
|
|
|
|
treeInstance.position = new Vector3(xpos, 0f, ypos);
|
|
|
|
treeInstance.color = Color.white;
|
|
treeInstance.lightmapColor = Color.white;
|
|
treeInstance.prototypeIndex = index;
|
|
|
|
treeInstance.widthScale = treeSize * (1f + Random.Range(-sizeVariation, sizeVariation));
|
|
treeInstance.heightScale = treeSize * (1f + Random.Range(-sizeVariation, sizeVariation));
|
|
|
|
myTerrain.AddTreeInstance(treeInstance);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
Debug.Log("placed "+trees+" trees "+GetTimerTime());
|
|
}
|
|
|
|
|
|
bool CheckGrassmap()
|
|
{
|
|
if (grassmap != null) {
|
|
FixTextureSettings(grassmap);
|
|
|
|
int w = grassmap.width;
|
|
if (grassmap.height != w)
|
|
{
|
|
EditorUtility.DisplayDialog("Wrong size", "grassmap width and height must be the same", "Cancel");
|
|
return false;
|
|
}
|
|
if (Mathf.ClosestPowerOfTwo(w) != w) {
|
|
EditorUtility.DisplayDialog("Wrong size", "grassmap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (bushmap != null) {
|
|
FixTextureSettings(bushmap);
|
|
|
|
int w = bushmap.width;
|
|
if (bushmap.height != w) {
|
|
EditorUtility.DisplayDialog("Wrong size", "bushmap width and height must be the same", "Cancel");
|
|
return false;
|
|
}
|
|
if (Mathf.ClosestPowerOfTwo(w) != w) {
|
|
EditorUtility.DisplayDialog("Wrong size", "bushmap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ApplyGrassmap()
|
|
{
|
|
StartTimer();
|
|
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
if (myTerrain==null) {Debug.LogError("No terrain selected"); return;}
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
Undo.RecordObject(terrainData, "Apply grass and bush maps");
|
|
|
|
if (grassmap!=null)
|
|
{
|
|
if (SetDetailmap(grassmap, grassDensity, 0, grassclumping, "Grass map")) Debug.Log("Grass map applied "+GetTimerTime());
|
|
}
|
|
|
|
if (bushmap!=null)
|
|
{
|
|
//if (SetDetailmap(bushmap, bushmod, 3, 0.0f, "Bush map")) Debug.Log("Bush map applied.");
|
|
if (SetDetailmap(bushmap, bushDensity, grassmap==null?0:3, 0.0f, "Bush map")) Debug.Log("Bush map applied "+GetTimerTime());
|
|
}
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
void ApplyBushmap()
|
|
{
|
|
StartTimer();
|
|
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
if (myTerrain==null) {Debug.LogError("No terrain selected"); return;}
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
Undo.RecordObject(terrainData, "Apply grass and bush maps");
|
|
/*
|
|
if (grassmap!=null)
|
|
{
|
|
if (SetDetailmap(grassmap, grassDensity, 0, grassclumping, "Grass map")) Debug.Log("Grass map applied "+GetTimerTime());
|
|
}*/
|
|
|
|
if (bushmap!=null)
|
|
{
|
|
//if (SetDetailmap(bushmap, bushmod, 3, 0.0f, "Bush map")) Debug.Log("Bush map applied.");
|
|
if (SetDetailmap(bushmap, bushDensity, grassmap==null?0:3, 0.0f, "Bush map")) Debug.Log("Bush map applied "+GetTimerTime());
|
|
}
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
|
|
bool SetDetailmap(Texture2D map, float mod, int firstlayer, float clumping, string MapName)
|
|
{
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
if (terrainData.detailPrototypes.Length<3)
|
|
{
|
|
if (terrainData.detailPrototypes.Length<1)
|
|
{
|
|
Debug.LogError ("You need to add at least 1 detail textures or 1 detail meshes to Terrain (at Paint Details)");
|
|
return false;
|
|
}
|
|
Debug.LogWarning ("You should add 3 detail textures or 3 detail meshes to Terrain to use all splat map color channels (at Paint Details)");
|
|
}
|
|
|
|
// validate terrain details count
|
|
int detailTextureCount=0;
|
|
int detailMeshCount=0;
|
|
int maxDetailMeshes = terrainData.detailPrototypes.Length;
|
|
|
|
for (int nn=0; nn<terrainData.detailPrototypes.Length;nn++)
|
|
{
|
|
if (terrainData.detailPrototypes[nn].usePrototypeMesh)
|
|
{
|
|
detailMeshCount++;
|
|
}else{
|
|
detailTextureCount++;
|
|
}
|
|
}
|
|
|
|
// check if there are any details for terrain
|
|
if (MapName == "Grass map")
|
|
{
|
|
if (detailTextureCount<1)
|
|
{
|
|
Debug.LogError ("Grass map needs at least 1 detail texture at Terrain - Paint Details tab");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
if (MapName == "Bush map")
|
|
{
|
|
if (detailMeshCount<1)
|
|
{
|
|
Debug.LogError ("Bush map at least 1 detail meshes at Terrain - Paint Details tab");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Color[] detailColors = map.GetPixels();
|
|
int width = map.width;
|
|
int res = terrainData.detailResolution;
|
|
|
|
int[,] detail_a = new int[res,res];
|
|
int[,] detail_b = new int[res,res];
|
|
int[,] detail_c = new int[res,res];
|
|
|
|
float scale = (float)width/(float)res;
|
|
|
|
for (int y = 0; y < res; y++)
|
|
{
|
|
|
|
if (EditorUtility.DisplayCancelableProgressBar("Applying "+MapName, "Calculating...", Mathf.InverseLerp(0.0f, (float)res, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return false;
|
|
}
|
|
|
|
for (int x = 0; x < res; x++)
|
|
{
|
|
// place detail, depending on colours in map image
|
|
int sx = Mathf.FloorToInt((float)(x)*scale);
|
|
int sy = Mathf.FloorToInt((float)(y)*scale);
|
|
//Color col = detailColors[((width-1)-sx)*width+sy];
|
|
Color col = detailColors[sx*width+sy];
|
|
|
|
|
|
detail_a[x,y] = DetailValue(col.r*mod);
|
|
if (maxDetailMeshes>2) detail_b[x,y] = DetailValue(col.g*mod);
|
|
if (maxDetailMeshes>3) detail_c[x,y] = DetailValue(col.b*mod);
|
|
}
|
|
}
|
|
|
|
if (clumping>0.01f)
|
|
{
|
|
detail_a = MakeClumps(detail_a, clumping);
|
|
if (maxDetailMeshes>2) detail_b = MakeClumps(detail_b, clumping);
|
|
if (maxDetailMeshes>3) detail_c = MakeClumps(detail_c, clumping);
|
|
}
|
|
|
|
terrainData.SetDetailLayer(0, 0, firstlayer+0, detail_a);
|
|
if (maxDetailMeshes>2) terrainData.SetDetailLayer(0, 0, firstlayer+1, detail_b);
|
|
if (maxDetailMeshes>3) terrainData.SetDetailLayer(0, 0, firstlayer+2, detail_c);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
int DetailValue(float col)
|
|
{
|
|
// linearly translates a 0.0-1.0 number to a 0-16 integer
|
|
int c = Mathf.FloorToInt(col*16);
|
|
float r = col*16 - Mathf.FloorToInt(col*16);
|
|
|
|
if (r>Random.Range(0.0f, 1.0f)) c++;
|
|
return Mathf.Clamp(c, 0, 16);
|
|
}
|
|
|
|
int[,] MakeClumps(int[,] map, float clumping) {
|
|
int res = map.GetLength(0);
|
|
int [,] clumpmap = new int[res,res];
|
|
|
|
// init - there's probably a better way to do this in C# that I just don't know
|
|
for (int y = 0; y < res; y++) {
|
|
for (int x = 0; x < res; x++) {
|
|
clumpmap[x,y]=0;
|
|
}
|
|
}
|
|
|
|
for (int y = 0; y < res; y++) {
|
|
for (int x = 0; x < res; x++) {
|
|
clumpmap[x,y]+=map[x,y];
|
|
if (map[x,y]>0) {
|
|
// there's grass here, we might want to raise the grass value of our neighbours
|
|
for (int a=-1;a<=1;a++) for (int b=-1;b<=1;b++) {
|
|
if (x+a<0||x+a>=res||y+b<0||y+b>=res) continue;
|
|
if (a!=0||b!=0&&Random.Range(0.0f, 1.0f)<clumping) clumpmap[x+a,y+b]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// values above 9 yield strange results
|
|
for (int y = 0; y < res; y++) {
|
|
for (int x = 0; x < res; x++) {
|
|
if (clumpmap[x,y]>9) clumpmap[x,y]=9;
|
|
}
|
|
}
|
|
|
|
return clumpmap;
|
|
}
|
|
|
|
|
|
bool CheckOverlaymap()
|
|
{
|
|
if (overlayMap==null) {Debug.LogError("No overlay map assigned"); return false;}
|
|
|
|
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
|
|
FixTextureSettings(overlayMap);
|
|
|
|
if (overlayMap.height != overlayMap.width) {
|
|
EditorUtility.DisplayDialog("Wrong size", "OverlayMap width and height must be the same", "Cancel");
|
|
return false;
|
|
}
|
|
if (Mathf.ClosestPowerOfTwo(overlayMap.width) != overlayMap.width) {
|
|
EditorUtility.DisplayDialog("Wrong size", "OverlayMap width and height must be a power of two", "Cancel");
|
|
return false;
|
|
}
|
|
|
|
if (overlayMap.width!=myTerrain.terrainData.alphamapResolution) {
|
|
EditorUtility.DisplayDialog("Wrong size", "OverlayMap must have same size as existing splatmap ("+myTerrain.terrainData.alphamapResolution+")", "Cancel");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ApplyOverlaymap()
|
|
{
|
|
StartTimer();
|
|
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
TerrainData terrainData = myTerrain.terrainData;
|
|
|
|
Undo.RecordObject(terrainData, "Apply overlay map");
|
|
|
|
int w = overlayMap.width;
|
|
Color[] OverlayMapColors = overlayMap.GetPixels();
|
|
int layer = myTerrain.terrainData.alphamapLayers;
|
|
|
|
int detailRes = terrainData.detailWidth;
|
|
int[] detailLayers = terrainData.GetSupportedLayers(0, 0, detailRes, detailRes);
|
|
int LayerCount = detailLayers.Length;
|
|
|
|
AddTexture();
|
|
|
|
float[,,] splatmapData = terrainData.GetAlphamaps(0, 0, w, w);
|
|
|
|
float terrainScale = (float)w / ((float)terrainData.heightmapResolution-1);
|
|
float terrainHeight = terrainData.size.y;
|
|
int terrainSample = Mathf.CeilToInt(terrainScale);
|
|
|
|
ArrayList NewTrees = new ArrayList(terrainData.treeInstances);
|
|
int TreesRemoved = 0;
|
|
|
|
for (int y = 0; y < w; y++)
|
|
{
|
|
if (y%10 == 0)
|
|
{
|
|
if (clearTrees)
|
|
{
|
|
if (EditorUtility.DisplayCancelableProgressBar("Overlay map", "updating terrain and trees ("+TreesRemoved+" trees removed)", Mathf.InverseLerp(0.0f, (float)w, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
} else {
|
|
|
|
if (EditorUtility.DisplayCancelableProgressBar("Overlay map", "updating terrain", Mathf.InverseLerp(0.0f, (float)w, (float)y)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
// float value = OverlayMapColors[((w-1)-x)*w + y].grayscale;
|
|
float value = OverlayMapColors[x*w+y].grayscale;
|
|
if (value>overlayThreshold)
|
|
{
|
|
splatmapData[x,y,layer] = value;
|
|
// fix the other layers
|
|
|
|
for (int l = 0; l<layer; l++) {
|
|
splatmapData[x,y,l] *= (1.0f-value);
|
|
}
|
|
|
|
if (changeTerrain>0.01f || changeTerrain<-0.01f)
|
|
{
|
|
if (value>overlayThreshold) {
|
|
float change = changeTerrain * value / terrainHeight;
|
|
int sx = Mathf.FloorToInt((float)y*terrainScale);
|
|
int sy = Mathf.FloorToInt((float)x*terrainScale);
|
|
float [,] data = terrainData.GetHeights(sx, sy, terrainSample, terrainSample);
|
|
for (int a=0;a<terrainSample;a++) for (int b=0;b<terrainSample;b++) {
|
|
data[a,b]=Mathf.Max(0.0f, data[a,b]+change);
|
|
}
|
|
terrainData.SetHeights(sx, sy, data);
|
|
}
|
|
}
|
|
|
|
if (clearTrees)
|
|
{
|
|
for (int i = NewTrees.Count -1; i >= 0; i--) {
|
|
TreeInstance MyTree = (TreeInstance)NewTrees[i];
|
|
float distance = Vector2.Distance(new Vector2(MyTree.position.z*w, MyTree.position.x*w), new Vector2((float)x, (float)y));
|
|
if (distance < clearRadius) {
|
|
NewTrees.RemoveAt(i);
|
|
TreesRemoved++;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
splatmapData[x,y,layer] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
if (clearTrees)
|
|
{
|
|
terrainData.treeInstances = (TreeInstance[])NewTrees.ToArray(typeof(TreeInstance));
|
|
}
|
|
|
|
terrainData.SetAlphamaps(0, 0, splatmapData);
|
|
Debug.Log("Overlay map applied "+GetTimerTime());
|
|
|
|
if (clearGrass)
|
|
{
|
|
float scale = (float)w / (float)detailRes;
|
|
for (int l = 0; l<LayerCount; l++)
|
|
{
|
|
if (EditorUtility.DisplayCancelableProgressBar("Overlay map", "clearing away grass", Mathf.InverseLerp(0.0f, (float)l, (float)LayerCount)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return;
|
|
}
|
|
int[,] grass = terrainData.GetDetailLayer(0, 0, detailRes, detailRes, l);
|
|
for (int y = 0; y < detailRes; y++) {
|
|
for (int x = 0; x < detailRes; x++) {
|
|
int sx = Mathf.FloorToInt((float)(x)*scale);
|
|
int sy = Mathf.FloorToInt((float)(y)*scale);
|
|
//float value = OverlayMapColors[((w-1)-sx)*w + sy].grayscale;
|
|
float value = OverlayMapColors[sx*w+sy].grayscale;
|
|
if (value > overlayThreshold && grass[x,y]>0)
|
|
{
|
|
if (value > 0.5f) grass[x,y]=0; else grass[x,y]=1;
|
|
}
|
|
}
|
|
}
|
|
terrainData.SetDetailLayer(0, 0, l, grass);
|
|
}
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
void AddTexture()
|
|
{
|
|
if (!myTerrain) myTerrain = Terrain.activeTerrain;
|
|
|
|
SplatPrototype[] oldPrototypes = myTerrain.terrainData.splatPrototypes;
|
|
SplatPrototype[] newPrototypes = new SplatPrototype[oldPrototypes.Length + 1];
|
|
|
|
for (int x=0;x<oldPrototypes.Length;x++)
|
|
{
|
|
newPrototypes[x] = oldPrototypes[x];
|
|
}
|
|
newPrototypes[oldPrototypes.Length] = new SplatPrototype();
|
|
newPrototypes[oldPrototypes.Length].texture = overlayTexture;
|
|
Vector2 vector = new Vector2(tileSize, tileSize);
|
|
newPrototypes[oldPrototypes.Length].tileSize = vector;
|
|
myTerrain.terrainData.splatPrototypes = newPrototypes;
|
|
EditorUtility.SetDirty(myTerrain);
|
|
}
|
|
|
|
|
|
|
|
// these are copied from UnityEditor.dll
|
|
private void CreateTerrain(string name, int size)
|
|
{
|
|
Selection.activeObject = null;
|
|
|
|
TerrainData newTerrain = new TerrainData();
|
|
|
|
newTerrain.heightmapResolution = size+1;
|
|
newTerrain.size = new Vector3(terrainInitWidth, terrainInitHeight, terrainInitLength);
|
|
newTerrain.heightmapResolution = size;
|
|
newTerrain.baseMapResolution = 1024;
|
|
newTerrain.SetDetailResolution(1024, 16); // recommended value from documentation
|
|
|
|
newTerrain.wavingGrassSpeed = windSettingsSize;
|
|
newTerrain.wavingGrassAmount = windSettingsBending;
|
|
newTerrain.wavingGrassStrength = windSettingsSpeed;
|
|
|
|
string timeStamp = System.DateTime.Now.ToString("ddMMHHmm");
|
|
AssetDatabase.CreateAsset(newTerrain, name+ "Terrain"+(timeStampFileNames?timeStamp:"") +".asset");
|
|
AssetDatabase.SaveAssets();
|
|
|
|
Selection.activeObject = Terrain.CreateTerrainGameObject(newTerrain);
|
|
|
|
|
|
myTerrain = Terrain.activeTerrains[Terrain.activeTerrains.Length-1];
|
|
|
|
}
|
|
|
|
|
|
public static float[,] ReadRawHeightMap(string path, int widthPlusOne, int heightmapWidth, int heightmapHeight)
|
|
{
|
|
if (!File.Exists(path))
|
|
{
|
|
// try with .r16 extension next
|
|
path = path.Replace(".raw",".r16");
|
|
if (!File.Exists(path))
|
|
{
|
|
Debug.LogWarning("Heightmap file not found: "+path);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
byte[] buffer;
|
|
using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read)))
|
|
{
|
|
buffer = reader.ReadBytes(widthPlusOne * widthPlusOne * 2);
|
|
reader.Close();
|
|
}
|
|
|
|
// int heightmapWidth = MyTerrain.terrainData.heightmapWidth;
|
|
// int heightmapHeight = MyTerrain.terrainData.heightmapHeight;
|
|
|
|
float[,] heights = new float[heightmapHeight, heightmapWidth];
|
|
|
|
float deltaMac = -999.0f;
|
|
float deltaPc = -999.0f;
|
|
|
|
int morePc=0;
|
|
int moreMac=0;
|
|
int jj=0;
|
|
|
|
// guess endian format by comparing height changes
|
|
for (int n=0;n<heightmapHeight;n+=4)
|
|
{
|
|
float b1 = buffer[jj++];
|
|
float b2 = buffer[jj++];
|
|
float b3 = buffer[jj++];
|
|
float b4 = buffer[jj++];
|
|
float ht1 = (b1*256.0f + b2);
|
|
float ht1mac = (b1*256.0f + b2);
|
|
float ht2 = (b3*256.0f + b4);
|
|
deltaMac=Mathf.Max (deltaMac,Mathf.Abs(ht1/65535.0f-ht2/65535.0f));
|
|
ht1 = (b1+b2*256.0f);
|
|
float ht1pc = (b1+b2*256.0f);
|
|
ht2 = (b3+b4*256.0f);
|
|
deltaPc=Mathf.Max (deltaPc,Mathf.Abs(ht1/65535.0f-ht2/65535.0f));
|
|
if (ht1mac>ht1pc) moreMac++;else morePc+=1;
|
|
}
|
|
|
|
int ii=0;
|
|
bool mac = deltaMac<deltaPc;
|
|
|
|
for (int i = 0; i < heightmapHeight; i++)
|
|
{
|
|
if (i%10 == 0)
|
|
{
|
|
|
|
if (EditorUtility.DisplayCancelableProgressBar("Importing heightmap", "calculating...", Mathf.InverseLerp(0.0f, (float)heightmapHeight, (float)i)))
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < heightmapWidth; j++)
|
|
{
|
|
float bufferVal1 = buffer[ii++];
|
|
float bufferVal2 = buffer[ii++];
|
|
float ht = (mac) ? (bufferVal1*256.0f + bufferVal2) : (bufferVal1 + bufferVal2*256.0f);
|
|
|
|
//heights[j , i] = (ht / 65535.0f); // not flipped * worked before ??
|
|
//heights[j , heightmapWidth-i-1] = (ht / 65535.0f); // flip * needs -90 deg
|
|
//heights[heightmapWidth-j-1, i] = (ht / 65535.0f); // flip original *needs +90 deg
|
|
//heights[heightmapWidth-j-1 , heightmapWidth-i-1] = (ht / 65535.0f); // * needs flip y?
|
|
|
|
//heights[i , j] = (ht / 65535.0f); // needs flip y?
|
|
//heights[i , heightmapWidth-j-1] = (ht / 65535.0f); // flip * needs -180 deg?
|
|
heights[heightmapWidth-i-1, j] = (ht / 65535.0f); // new best, with flip fix
|
|
//heights[heightmapWidth-j-1 , heightmapWidth-i-1] = (ht / 65535.0f); // * needs flip y?
|
|
|
|
}
|
|
}
|
|
return heights;
|
|
} // ReadRaw()
|
|
|
|
void StartTimer()
|
|
{
|
|
stopwatch.Reset();
|
|
stopwatch.Start();
|
|
}
|
|
|
|
string GetTimerTime()
|
|
{
|
|
stopwatch.Stop();
|
|
return " ("+stopwatch.Elapsed.Milliseconds+"ms)";
|
|
}
|
|
|
|
} // class
|
|
} // namespace
|