将Spline移出Plugin,以调整SplineRenderer的OnWillCameraRender
This commit is contained in:
291
Assets/Dreamteck/Splines/Editor/Tools/BakeTool.cs
Normal file
291
Assets/Dreamteck/Splines/Editor/Tools/BakeTool.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
namespace Dreamteck.Splines.Editor
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
public class BakeTool : SplineTool
|
||||
{
|
||||
public enum BakeGroup { All, Selected, AllExcluding }
|
||||
BakeGroup bakeGroup = BakeGroup.All;
|
||||
MeshGenerator[] found = new MeshGenerator[0];
|
||||
List<MeshGenerator> selected = new List<MeshGenerator>();
|
||||
List<MeshGenerator> excluded = new List<MeshGenerator>();
|
||||
|
||||
bool isStatic = true;
|
||||
bool removeComputer = false;
|
||||
bool permanent = false;
|
||||
bool copy = false;
|
||||
BakeMeshWindow.SaveFormat format = BakeMeshWindow.SaveFormat.MeshAsset;
|
||||
|
||||
string savePath = "";
|
||||
|
||||
DirectoryInfo dirInfo;
|
||||
|
||||
Vector2 scroll1, scroll2;
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Bake Meshes";
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
bakeGroup = (BakeGroup)EditorGUILayout.EnumPopup("Bake Mode", bakeGroup);
|
||||
if (bakeGroup == BakeGroup.Selected)
|
||||
{
|
||||
MeshGenSelector(ref selected, "Selected");
|
||||
} else if(bakeGroup == BakeGroup.AllExcluding)
|
||||
{
|
||||
MeshGenSelector(ref excluded, "Excluded");
|
||||
}
|
||||
|
||||
|
||||
format = (BakeMeshWindow.SaveFormat)EditorGUILayout.EnumPopup("Save Format", format);
|
||||
bool saveMesh = format != BakeMeshWindow.SaveFormat.Scene;
|
||||
|
||||
if (format != BakeMeshWindow.SaveFormat.Scene)
|
||||
{
|
||||
copy = EditorGUILayout.Toggle("Save without baking", copy);
|
||||
}
|
||||
bool isCopy = format != BakeMeshWindow.SaveFormat.Scene && copy;
|
||||
switch (format)
|
||||
{
|
||||
case BakeMeshWindow.SaveFormat.Scene: EditorGUILayout.HelpBox("Saves the mesh inside the scene", MessageType.Info); break;
|
||||
case BakeMeshWindow.SaveFormat.MeshAsset: EditorGUILayout.HelpBox("Saves the mesh as an .asset file inside the project. This makes using the mesh in prefabs and across scenes possible.", MessageType.Info); break;
|
||||
case BakeMeshWindow.SaveFormat.OBJ: EditorGUILayout.HelpBox("Exports the mesh as an OBJ file which can be imported in a third-party modeling application.", MessageType.Info); break;
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (!isCopy)
|
||||
{
|
||||
isStatic = EditorGUILayout.Toggle("Make Static", isStatic);
|
||||
permanent = EditorGUILayout.Toggle("Permanent", permanent);
|
||||
if (permanent)
|
||||
{
|
||||
removeComputer = EditorGUILayout.Toggle("Remove SplineComputer", removeComputer);
|
||||
if (removeComputer) EditorGUILayout.HelpBox("WARNING: Removing Spline Computers may cause other SplineUsers to stop working. Select this if you are sure that no other SplineUser uses the selected Spline Computers.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Bake"))
|
||||
{
|
||||
if (saveMesh)
|
||||
{
|
||||
savePath = EditorUtility.OpenFolderPanel("Save Directory", Application.dataPath, "folder");
|
||||
if (!Directory.Exists(savePath) || savePath == "")
|
||||
{
|
||||
EditorUtility.DisplayDialog("Save error", "Invalid save directory. Please select a valid save directory and try again", "OK");
|
||||
return;
|
||||
}
|
||||
if (format == BakeMeshWindow.SaveFormat.OBJ && !savePath.StartsWith(Application.dataPath) && !copy)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Save error", "OBJ files can be saved outside of the project folder only when \"Save without baking\" is selected. Please select a directory inside the project in order to save.", "OK");
|
||||
return;
|
||||
}
|
||||
if (format == BakeMeshWindow.SaveFormat.MeshAsset && !savePath.StartsWith(Application.dataPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Save error", "Asset files cannot be saved outside of the project directory. Please select a path inside the project directory.", "OK");
|
||||
return;
|
||||
}
|
||||
}
|
||||
string suff = "all";
|
||||
if (bakeGroup == BakeGroup.Selected) suff = "selected";
|
||||
if (bakeGroup == BakeGroup.AllExcluding) suff = "all excluding";
|
||||
if(EditorUtility.DisplayDialog("Bake " + suff, "This operation cannot be undone. Are you sure you want to bake the meshes?", "Yes", "No"))
|
||||
{
|
||||
switch (bakeGroup)
|
||||
{
|
||||
case BakeGroup.All: BakeAll(); break;
|
||||
case BakeGroup.Selected: BakeSelected(); break;
|
||||
case BakeGroup.AllExcluding: BakeExcluding(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BakeAll()
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < found.Length; i++)
|
||||
{
|
||||
float percent = (float)i / (found.Length - 1);
|
||||
EditorUtility.DisplayProgressBar("Baking progress", "Baking generator " + i, percent);
|
||||
Bake(found[i]);
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void BakeSelected()
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < selected.Count; i++)
|
||||
{
|
||||
float percent = (float)i / (selected.Count - 1);
|
||||
EditorUtility.DisplayProgressBar("Baking progress", "Baking generator " + i, percent);
|
||||
Bake(selected[i]);
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void BakeExcluding()
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < found.Length; i++)
|
||||
{
|
||||
float percent = (float)i / (found.Length - 1);
|
||||
EditorUtility.DisplayProgressBar("Baking progress", "Baking generator " + i, percent);
|
||||
Bake(found[i]);
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void Bake(MeshGenerator gen)
|
||||
{
|
||||
MeshFilter filter = gen.GetComponent<MeshFilter>();
|
||||
if(filter == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Save error", "No mesh present in " + gen.name, "OK");
|
||||
return;
|
||||
}
|
||||
if (copy)
|
||||
{
|
||||
UnityEditor.MeshUtility.Optimize(filter.sharedMesh);
|
||||
Unwrapping.GenerateSecondaryUVSet(filter.sharedMesh);
|
||||
}
|
||||
else gen.Bake(isStatic, true);
|
||||
|
||||
if(format == BakeMeshWindow.SaveFormat.OBJ)
|
||||
{
|
||||
MeshRenderer renderer = gen.GetComponent<MeshRenderer>();
|
||||
dirInfo = new DirectoryInfo(savePath);
|
||||
FileInfo[] files = dirInfo.GetFiles(filter.sharedMesh.name + "*.obj");
|
||||
string meshName = filter.sharedMesh.name;
|
||||
if (files.Length > 0) meshName += "_" + files.Length;
|
||||
string path = savePath + "/" + meshName + ".obj";
|
||||
string objString = Dreamteck.MeshUtility.ToOBJString(filter.sharedMesh, renderer.sharedMaterials);
|
||||
File.WriteAllText(path, objString);
|
||||
if (copy)
|
||||
{
|
||||
string relativepath = "Assets" + path.Substring(Application.dataPath.Length);
|
||||
AssetDatabase.ImportAsset(relativepath, ImportAssetOptions.ForceSynchronousImport);
|
||||
filter.sharedMesh = AssetDatabase.LoadAssetAtPath<Mesh>(relativepath);
|
||||
}
|
||||
}
|
||||
|
||||
if(format == BakeMeshWindow.SaveFormat.MeshAsset)
|
||||
{
|
||||
dirInfo = new DirectoryInfo(savePath);
|
||||
FileInfo[] files = dirInfo.GetFiles(filter.sharedMesh.name + "*.asset");
|
||||
string meshName = filter.sharedMesh.name;
|
||||
if (files.Length > 0) meshName += "_" + files.Length;
|
||||
string path = savePath + "/" + meshName + ".asset";
|
||||
string relativepath = "Assets" + path.Substring(Application.dataPath.Length);
|
||||
if (copy)
|
||||
{
|
||||
Mesh assetMesh = Dreamteck.MeshUtility.Copy(filter.sharedMesh);
|
||||
AssetDatabase.CreateAsset(assetMesh, relativepath);
|
||||
} else AssetDatabase.CreateAsset(filter.sharedMesh, relativepath);
|
||||
}
|
||||
|
||||
if (permanent && !copy)
|
||||
{
|
||||
SplineComputer meshGenComputer = gen.spline;
|
||||
if (permanent)
|
||||
{
|
||||
meshGenComputer.Unsubscribe(gen);
|
||||
Object.DestroyImmediate(gen);
|
||||
}
|
||||
if (removeComputer)
|
||||
{
|
||||
if (meshGenComputer.GetComponents<Component>().Length == 2) Object.DestroyImmediate(meshGenComputer.gameObject);
|
||||
else Object.DestroyImmediate(meshGenComputer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
found = Object.FindObjectsOfType<MeshGenerator>();
|
||||
}
|
||||
|
||||
void OnFocus()
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
isStatic = LoadBool("isStatic", true);
|
||||
format = (BakeMeshWindow.SaveFormat)LoadInt("format", 0);
|
||||
removeComputer = LoadBool("removeComputer", false);
|
||||
copy = LoadBool("copy", false);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
SaveBool("isStatic", isStatic);
|
||||
SaveInt("format", (int)format);
|
||||
SaveBool("copy", copy);
|
||||
SaveBool("removeComputer", removeComputer);
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "BakeTool";
|
||||
}
|
||||
|
||||
private void MeshGenSelector(ref List<MeshGenerator> list, string title)
|
||||
{
|
||||
List<MeshGenerator> availalbe = new List<MeshGenerator>(found);
|
||||
for (int i = availalbe.Count-1; i >= 0; i--)
|
||||
{
|
||||
for (int n = 0; n < list.Count; n++)
|
||||
{
|
||||
if (list[n] == availalbe[i])
|
||||
{
|
||||
availalbe.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.Box("Available", GUILayout.Width(Screen.width - 15 - Screen.width/3f), GUILayout.Height(100));
|
||||
Rect rect = GUILayoutUtility.GetLastRect();
|
||||
rect.y += 15;
|
||||
rect.height -= 15;
|
||||
scroll1 = GUI.BeginScrollView(rect, scroll1, new Rect(0, 0, rect.width, 22 * availalbe.Count));
|
||||
for (int i = 0; i < availalbe.Count; i++)
|
||||
{
|
||||
GUI.Label(new Rect(5, 22 * i, rect.width - 30, 22), availalbe[i].name);
|
||||
if (GUI.Button(new Rect(rect.width - 29, 22 * i, 22, 22), "+"))
|
||||
{
|
||||
list.Add(availalbe[i]);
|
||||
availalbe.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GUI.EndScrollView();
|
||||
EditorGUILayout.Space();
|
||||
GUILayout.Box(title, GUILayout.Width(Screen.width - 15 - Screen.width / 3f), GUILayout.Height(100));
|
||||
|
||||
rect = GUILayoutUtility.GetLastRect();
|
||||
rect.y += 15;
|
||||
rect.height -= 15;
|
||||
scroll2 = GUI.BeginScrollView(rect, scroll2, new Rect(0, 0, rect.width, 22 * list.Count));
|
||||
for (int i = list.Count-1; i >= 0; i--)
|
||||
{
|
||||
GUI.Label(new Rect(5, 22 * i, rect.width - 30, 22), list[i].name);
|
||||
if (GUI.Button(new Rect(rect.width - 29, 22 * i, 22, 22), "x"))
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
GUI.EndScrollView();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/BakeTool.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/BakeTool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 659dbb6c3db842b4bbe63915d9097d5a
|
||||
timeCreated: 1466788468
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
121
Assets/Dreamteck/Splines/Editor/Tools/CatenaryTool.cs
Normal file
121
Assets/Dreamteck/Splines/Editor/Tools/CatenaryTool.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
public class CatenaryTool : SplineTool
|
||||
{
|
||||
protected GameObject obj;
|
||||
protected ObjectController spawner;
|
||||
private float _sag = 0f;
|
||||
private float _minSagDistance = 0f;
|
||||
private float _maxSagDistance = 10f;
|
||||
private Dictionary<SplineComputer, SplinePoint[]> _editSplines = new Dictionary<SplineComputer, SplinePoint[]>();
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Catenary Tool";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "CatenaryTool";
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
_sag = EditorPrefs.GetFloat("DreamteckSplines.CatenaryTool._sag", 0f);
|
||||
_minSagDistance = EditorPrefs.GetFloat("DreamteckSplines.CatenaryTool._minSagDistance", 0f);
|
||||
_maxSagDistance = EditorPrefs.GetFloat("DreamteckSplines.CatenaryTool._maxSagDistance", 10f);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
EditorPrefs.SetFloat("DreamteckSplines.CatenaryTool._sag", _sag);
|
||||
EditorPrefs.SetFloat("DreamteckSplines.CatenaryTool._minSagDistance", _minSagDistance);
|
||||
EditorPrefs.SetFloat("DreamteckSplines.CatenaryTool._maxSagDistance", _minSagDistance);
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
base.Draw(windowRect);
|
||||
if(_editSplines.Keys.Count == 0 && splines.Count > 0)
|
||||
{
|
||||
if(GUILayout.Button("Convert Selected"))
|
||||
{
|
||||
ConvertSelected();
|
||||
}
|
||||
} else
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
_sag = EditorGUILayout.FloatField("Sag", _sag);
|
||||
_minSagDistance = EditorGUILayout.FloatField("Min Distance", _minSagDistance);
|
||||
_maxSagDistance = EditorGUILayout.FloatField("Max Distance", _maxSagDistance);
|
||||
|
||||
var keys = _editSplines.Keys;
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
SceneView.RepaintAll();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
for (int i = 0; i < key.pointCount; i++)
|
||||
{
|
||||
ModifyPoint(key, i);
|
||||
}
|
||||
key.SetPoints(_editSplines[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Apply"))
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
EditorUtility.SetDirty(key);
|
||||
}
|
||||
_editSplines.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ModifyPoint(SplineComputer spline, int index)
|
||||
{
|
||||
var current = _editSplines[spline][index];
|
||||
if(index > 0)
|
||||
{
|
||||
var previous = _editSplines[spline][index - 1];
|
||||
Vector3 prevDirection = (previous.position - current.position)/3f;
|
||||
float sagAmount = Mathf.InverseLerp(_minSagDistance, _maxSagDistance, prevDirection.magnitude) * _sag;
|
||||
current.SetTangentPosition(current.position + prevDirection + Vector3.down * sagAmount);
|
||||
}
|
||||
|
||||
if(index < _editSplines[spline].Length - 1)
|
||||
{
|
||||
var next = _editSplines[spline][index + 1];
|
||||
Vector3 nextDirection = (next.position - current.position) / 3f;
|
||||
float sagAmount = Mathf.InverseLerp(_minSagDistance, _maxSagDistance, nextDirection.magnitude) * _sag;
|
||||
current.SetTangent2Position(current.position + nextDirection + Vector3.down * sagAmount);
|
||||
}
|
||||
_editSplines[spline][index] = current;
|
||||
}
|
||||
|
||||
private void ConvertSelected()
|
||||
{
|
||||
_editSplines.Clear();
|
||||
foreach(var spline in splines)
|
||||
{
|
||||
var points = spline.GetPoints();
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
points[i].type = SplinePoint.Type.Broken;
|
||||
}
|
||||
spline.type = Spline.Type.Bezier;
|
||||
_editSplines.Add(spline, points);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/CatenaryTool.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/CatenaryTool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62aeb649c30348f4ea6cec4a6481b8fd
|
||||
timeCreated: 1456934986
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
200
Assets/Dreamteck/Splines/Editor/Tools/Explorer.cs
Normal file
200
Assets/Dreamteck/Splines/Editor/Tools/Explorer.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
namespace Dreamteck.Splines.Editor
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
public class Explorer : SplineTool
|
||||
{
|
||||
GUIStyle normalRow;
|
||||
GUIStyle selectedRow;
|
||||
List<SplineComputer> sceneSplines = new List<SplineComputer>();
|
||||
List<int> selected = new List<int>();
|
||||
Vector2 scroll = Vector2.zero;
|
||||
bool mouseLeft = false;
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Spline Explorer";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "SplineExplorer";
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
normalRow = new GUIStyle(GUI.skin.box);
|
||||
normalRow.normal.background = null;
|
||||
normalRow.alignment = TextAnchor.MiddleLeft;
|
||||
selectedRow = new GUIStyle(normalRow);
|
||||
selectedRow.normal.background = SplineEditorGUI.white;
|
||||
selectedRow.normal.textColor = SplinePrefs.highlightContentColor;
|
||||
GetSceneSplines();
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui += OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate += OnScene;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= OnScene;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void OnScene(SceneView current)
|
||||
{
|
||||
if(selected.Count > 1)
|
||||
{
|
||||
for (int i = 0; i < selected.Count; i++)
|
||||
{
|
||||
if (!sceneSplines[selected[i]].editorAlwaysDraw)
|
||||
{
|
||||
DSSplineDrawer.DrawSplineComputer(sceneSplines[selected[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetSceneSplines()
|
||||
{
|
||||
sceneSplines = new List<SplineComputer>(Resources.FindObjectsOfTypeAll<SplineComputer>());
|
||||
}
|
||||
|
||||
public override void Draw(Rect rect)
|
||||
{
|
||||
switch (Event.current.type)
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (Event.current.button == 0) mouseLeft = true;
|
||||
break;
|
||||
case EventType.MouseUp: if (Event.current.button == 0) mouseLeft = false; break;
|
||||
}
|
||||
|
||||
Rect lastRect;
|
||||
scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Width(rect.width), GUILayout.Height(rect.height));
|
||||
EditorGUILayout.BeginHorizontal(normalRow);
|
||||
EditorGUILayout.LabelField("Name", EditorStyles.boldLabel, GUILayout.Width(rect.width - 200));
|
||||
EditorGUILayout.LabelField("Color", EditorStyles.boldLabel, GUILayout.Width(65));
|
||||
EditorGUILayout.LabelField("Draw", EditorStyles.boldLabel, GUILayout.Width(40));
|
||||
EditorGUILayout.LabelField("Thickness", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
for (int i = 0; i < sceneSplines.Count; i++)
|
||||
{
|
||||
bool isSelected = selected.Contains(i);
|
||||
if (isSelected) GUI.backgroundColor = SplinePrefs.highlightColor;
|
||||
|
||||
EditorGUILayout.BeginHorizontal(isSelected ? selectedRow : normalRow);
|
||||
EditorGUILayout.LabelField(sceneSplines[i].name, isSelected ? selectedRow : normalRow, GUILayout.Width(rect.width-200));
|
||||
GUI.backgroundColor = Color.white;
|
||||
Color pathColor = sceneSplines[i].editorPathColor;
|
||||
pathColor = EditorGUILayout.ColorField(pathColor, GUILayout.Width(65));
|
||||
if(pathColor != sceneSplines[i].editorPathColor)
|
||||
{
|
||||
foreach (int index in selected) sceneSplines[index].editorPathColor = pathColor;
|
||||
}
|
||||
bool alwaysDraw = sceneSplines[i].editorAlwaysDraw;
|
||||
alwaysDraw = EditorGUILayout.Toggle(alwaysDraw, GUILayout.Width(40));
|
||||
if(alwaysDraw != sceneSplines[i].editorAlwaysDraw)
|
||||
{
|
||||
foreach (int index in selected)
|
||||
{
|
||||
if (alwaysDraw)
|
||||
{
|
||||
DSSplineDrawer.RegisterComputer(sceneSplines[index]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DSSplineDrawer.UnregisterComputer(sceneSplines[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool thickness = sceneSplines[i].editorDrawThickness;
|
||||
thickness = EditorGUILayout.Toggle(thickness, GUILayout.Width(40));
|
||||
if(thickness != sceneSplines[i].editorDrawThickness)
|
||||
{
|
||||
foreach (int index in selected) sceneSplines[index].editorDrawThickness = thickness;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
lastRect = GUILayoutUtility.GetLastRect();
|
||||
if (mouseLeft)
|
||||
{
|
||||
if (lastRect.Contains(Event.current.mousePosition))
|
||||
{
|
||||
if (Event.current.control)
|
||||
{
|
||||
if (!selected.Contains(i)) selected.Add(i);
|
||||
}
|
||||
else if (selected.Count > 0 && Event.current.shift)
|
||||
{
|
||||
int closest = selected[0];
|
||||
int delta = sceneSplines.Count;
|
||||
for (int j = 0; j < selected.Count; j++)
|
||||
{
|
||||
int d = Mathf.Abs(i - selected[j]);
|
||||
if (d < delta)
|
||||
{
|
||||
delta = d;
|
||||
closest = selected[j];
|
||||
}
|
||||
}
|
||||
if (closest < i)
|
||||
{
|
||||
for (int j = closest + 1; j <= i; j++)
|
||||
{
|
||||
if (selected.Contains(j)) continue;
|
||||
selected.Add(j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = closest - 1; j >= i; j--)
|
||||
{
|
||||
if (selected.Contains(j)) continue;
|
||||
selected.Add(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else selected = new List<int>(new int[] { i });
|
||||
List<GameObject> selectGo = new List<GameObject>();
|
||||
foreach(int index in selected) selectGo.Add(sceneSplines[index].gameObject);
|
||||
Selection.objects = selectGo.ToArray();
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck()) SceneView.RepaintAll();
|
||||
EditorGUILayout.EndScrollView();
|
||||
if(Event.current.type == EventType.KeyDown)
|
||||
{
|
||||
if (Event.current.keyCode == KeyCode.DownArrow)
|
||||
{
|
||||
if (selected.Count > 0) selected = new List<int>(new int[] { selected[0] });
|
||||
else selected[0]++;
|
||||
}
|
||||
else if (Event.current.keyCode == KeyCode.UpArrow)
|
||||
{
|
||||
if (selected.Count > 0) selected = new List<int>(new int[] { selected[selected.Count - 1] });
|
||||
else selected[0]++;
|
||||
}
|
||||
if (selected.Count == 0) return;
|
||||
if (selected[0] < 0) selected[0] = sceneSplines.Count - 1;
|
||||
if (selected[0] >= sceneSplines.Count) selected[0] = 0;
|
||||
if (sceneSplines.Count > 0) Selection.activeGameObject = sceneSplines[selected[0]].gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/Explorer.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/Explorer.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa5e1620a9cb5114c9f0581fbafceb67
|
||||
timeCreated: 1496308185
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
391
Assets/Dreamteck/Splines/Editor/Tools/ImportTool.cs
Normal file
391
Assets/Dreamteck/Splines/Editor/Tools/ImportTool.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
namespace Dreamteck.Splines.Editor
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
using Dreamteck.Splines.IO;
|
||||
|
||||
public class ImportExportTool : SplineTool
|
||||
{
|
||||
private float scaleFactor = 1f;
|
||||
private bool alwaysDraw = true;
|
||||
private string importPath = "";
|
||||
private string exportPath = "";
|
||||
List<SplinePoint[]> originalPoints = new List<SplinePoint[]>();
|
||||
List<SplineComputer> imported = new List<SplineComputer>();
|
||||
List<SplineComputer> exported = new List<SplineComputer>();
|
||||
GameObject importedParent = null;
|
||||
|
||||
enum Mode { None, Import, Export }
|
||||
enum ExportFormat { SVG, CSV }
|
||||
enum Axis { X, Y, Z }
|
||||
|
||||
Mode mode = Mode.None;
|
||||
ExportFormat format = ExportFormat.SVG;
|
||||
Axis importAxis = Axis.Z;
|
||||
Axis exportAxis = Axis.Z;
|
||||
|
||||
bool importOptions = false;
|
||||
|
||||
List<CSV.ColumnType> exportColumns = new List<CSV.ColumnType>();
|
||||
List<CSV.ColumnType> importColumns = new List<CSV.ColumnType>();
|
||||
bool flatCSV = false;
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Import/Export";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "ImportExport";
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
importPath = LoadString("importPath", "");
|
||||
exportPath = LoadString("exportPath", "");
|
||||
alwaysDraw = LoadBool("alwaysDraw", true);
|
||||
flatCSV = LoadBool("flatCSV", false);
|
||||
importAxis = (Axis)LoadInt("importAxis", 2);
|
||||
exportAxis = (Axis)LoadInt("exportAxis", 2);
|
||||
LoadColumns("importColumns", ref importColumns);
|
||||
LoadColumns("exportColumns", ref exportColumns);
|
||||
}
|
||||
|
||||
void LoadColumns(string name, ref List<CSV.ColumnType> destination)
|
||||
{
|
||||
string text = LoadString(name, "");
|
||||
destination = new List<CSV.ColumnType>();
|
||||
if (text == "")
|
||||
{
|
||||
destination.Add(CSV.ColumnType.Position);
|
||||
destination.Add(CSV.ColumnType.Tangent);
|
||||
destination.Add(CSV.ColumnType.Tangent2);
|
||||
destination.Add(CSV.ColumnType.Normal);
|
||||
destination.Add(CSV.ColumnType.Size);
|
||||
destination.Add(CSV.ColumnType.Color);
|
||||
return;
|
||||
}
|
||||
string[] elements = text.Split(',');
|
||||
foreach (string element in elements)
|
||||
{
|
||||
int i = 0;
|
||||
if (int.TryParse(element, out i)) destination.Add((CSV.ColumnType)i);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
if(importPath != "") SaveString("importPath", Path.GetDirectoryName(importPath));
|
||||
if (exportPath != "") SaveString("exportPath", Path.GetDirectoryName(exportPath));
|
||||
string columnString = "";
|
||||
foreach(CSV.ColumnType col in importColumns)
|
||||
{
|
||||
if (columnString != "") columnString += ",";
|
||||
columnString += ((int)col).ToString();
|
||||
}
|
||||
SaveString("importColumns", columnString);
|
||||
columnString = "";
|
||||
foreach (CSV.ColumnType col in exportColumns)
|
||||
{
|
||||
if (columnString != "") columnString += ",";
|
||||
columnString += ((int)col).ToString();
|
||||
}
|
||||
SaveString("exportColumns", columnString);
|
||||
SaveBool("alwaysDraw", alwaysDraw);
|
||||
SaveBool("flatCSV", flatCSV);
|
||||
SaveInt("importAxis", (int)importAxis);
|
||||
SaveInt("exportAxis", (int)exportAxis);
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= OnScene;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
protected override void Save()
|
||||
{
|
||||
base.Save();
|
||||
if (importedParent != null)
|
||||
{
|
||||
Selection.activeGameObject = importedParent;
|
||||
importedParent = null;
|
||||
} else
|
||||
{
|
||||
foreach(SplineComputer comp in imported)
|
||||
{
|
||||
if(comp != null)
|
||||
{
|
||||
Selection.activeGameObject = comp.gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
imported.Clear();
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= OnScene;
|
||||
#endif
|
||||
|
||||
mode = Mode.None;
|
||||
}
|
||||
|
||||
protected override void Cancel()
|
||||
{
|
||||
base.Cancel();
|
||||
foreach (SplineComputer spline in imported) GameObject.DestroyImmediate(spline.gameObject);
|
||||
GameObject.DestroyImmediate(importedParent);
|
||||
imported.Clear();
|
||||
SceneView.RepaintAll();
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= OnScene;
|
||||
#endif
|
||||
|
||||
mode = Mode.None;
|
||||
}
|
||||
|
||||
void CSVColumnUI(List<CSV.ColumnType> columns)
|
||||
{
|
||||
EditorGUILayout.LabelField("Dataset Columns");
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("-", GUILayout.MaxWidth(30)) && columns.Count > 0) columns.RemoveAt(columns.Count - 1);
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
columns[i] = (CSV.ColumnType)EditorGUILayout.EnumPopup(columns[i]);
|
||||
}
|
||||
if (GUILayout.Button("+", GUILayout.MaxWidth(30)) && columns.Count > 0) columns.Add(CSV.ColumnType.Position);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
void OnScene(SceneView current)
|
||||
{
|
||||
for (int i = 0; i < imported.Count; i++)
|
||||
{
|
||||
DSSplineDrawer.DrawSplineComputer(imported[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
scaleFactor = EditorGUILayout.FloatField("Scale Factor", scaleFactor);
|
||||
importAxis = (Axis)EditorGUILayout.EnumPopup("Facing Axis", importAxis);
|
||||
alwaysDraw = EditorGUILayout.Toggle("Always Draw", alwaysDraw);
|
||||
if (EditorGUI.EndChangeCheck()) ApplyPoints();
|
||||
SaveCancelUI();
|
||||
}
|
||||
|
||||
void ExportUI()
|
||||
{
|
||||
if(exported.Count == 0)
|
||||
{
|
||||
mode = Mode.None;
|
||||
return;
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
format = (ExportFormat)EditorGUILayout.EnumPopup("Format", format);
|
||||
if (format == ExportFormat.SVG)
|
||||
{
|
||||
exportAxis = (Axis)EditorGUILayout.EnumPopup("Projection Axis", exportAxis);
|
||||
EditorGUILayout.HelpBox("The SVG is a 2D vector format so the exported spline will be flattened along the selected axis", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
CSVColumnUI(exportColumns);
|
||||
flatCSV = EditorGUILayout.Toggle("Flat", flatCSV);
|
||||
if(flatCSV) exportAxis = (Axis)EditorGUILayout.EnumPopup("Projection Axis", exportAxis);
|
||||
EditorGUILayout.HelpBox("The exported splined will be flattened along the selected axis.", MessageType.Info);
|
||||
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save File"))
|
||||
{
|
||||
string extension = "*";
|
||||
switch (format)
|
||||
{
|
||||
case ExportFormat.SVG: extension = "svg"; break;
|
||||
case ExportFormat.CSV: extension = "csv"; break;
|
||||
}
|
||||
exportPath = EditorUtility.SaveFilePanel("Export splines", exportPath, "spline", extension);
|
||||
if (exportPath != "")
|
||||
{
|
||||
if (Directory.Exists(Path.GetDirectoryName(exportPath)))
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case ExportFormat.SVG: ExportSVG(exportPath); break;
|
||||
case ExportFormat.CSV: ExportCSV(exportPath); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
if (mode == Mode.Import)
|
||||
{
|
||||
ImportUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
importOptions = EditorGUILayout.Foldout(importOptions, "Import Options");
|
||||
if (importOptions) CSVColumnUI(importColumns);
|
||||
if (GUILayout.Button("Import"))
|
||||
{
|
||||
importPath = EditorUtility.OpenFilePanel("Browse File", importPath, "svg,csv");
|
||||
if (File.Exists(importPath))
|
||||
{
|
||||
splines.Clear();
|
||||
string ext = Path.GetExtension(importPath).ToLower();
|
||||
switch (ext)
|
||||
{
|
||||
case ".svg": ImportSVG(importPath); break;
|
||||
case ".csv": ImportCSV(importPath); break;
|
||||
case ".xml": ImportSVG(importPath); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
exported = GetSelectedSplines();
|
||||
if (exported.Count == 0) GUI.color = new Color(1f, 1f, 1f, 0.5f);
|
||||
if (mode == Mode.Export) ExportUI();
|
||||
if (mode != Mode.Export)
|
||||
{
|
||||
if (GUILayout.Button("Export") && exported.Count > 0) mode = Mode.Export;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<SplineComputer> GetSelectedSplines()
|
||||
{
|
||||
List<SplineComputer> selected = new List<SplineComputer>();
|
||||
foreach(GameObject obj in Selection.gameObjects)
|
||||
{
|
||||
SplineComputer comp = obj.GetComponent<SplineComputer>();
|
||||
if (comp != null) selected.Add(comp);
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
void ApplyPoints()
|
||||
{
|
||||
if (originalPoints.Count != imported.Count) return;
|
||||
if (imported.Count == 0) return;
|
||||
Quaternion lookRot = Quaternion.identity;
|
||||
switch (importAxis)
|
||||
{
|
||||
case Axis.X: lookRot = Quaternion.LookRotation(Vector3.right); break;
|
||||
case Axis.Y: lookRot = Quaternion.LookRotation(Vector3.down); break;
|
||||
case Axis.Z: lookRot = Quaternion.LookRotation(Vector3.forward); break;
|
||||
}
|
||||
for (int i = 0; i < imported.Count; i++)
|
||||
{
|
||||
SplinePoint[] transformed = new SplinePoint[originalPoints[i].Length];
|
||||
originalPoints[i].CopyTo(transformed, 0);
|
||||
for (int j = 0; j < transformed.Length; j++)
|
||||
{
|
||||
transformed[j].position *= scaleFactor;
|
||||
transformed[j].tangent *= scaleFactor;
|
||||
transformed[j].tangent2 *= scaleFactor;
|
||||
|
||||
transformed[j].position = lookRot * transformed[j].position;
|
||||
transformed[j].tangent = lookRot * transformed[j].tangent;
|
||||
transformed[j].tangent2 = lookRot * transformed[j].tangent2;
|
||||
transformed[j].normal = lookRot * transformed[j].normal;
|
||||
}
|
||||
imported[i].SetPoints(transformed);
|
||||
if (alwaysDraw)
|
||||
{
|
||||
DSSplineDrawer.RegisterComputer(imported[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DSSplineDrawer.UnregisterComputer(imported[i]);
|
||||
}
|
||||
}
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
void GetImportedPoints()
|
||||
{
|
||||
foreach (SplineComputer comp in imported)
|
||||
{
|
||||
if (comp != null)
|
||||
{
|
||||
originalPoints.Add(comp.GetPoints(SplineComputer.Space.Local));
|
||||
mode = Mode.Import;
|
||||
} else imported.Remove(comp);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportSVG(string file)
|
||||
{
|
||||
SVG svg = new SVG(file);
|
||||
originalPoints.Clear();
|
||||
imported = svg.CreateSplineComputers(Vector3.zero, Quaternion.identity);
|
||||
if (imported.Count == 0) return;
|
||||
importedParent = new GameObject(svg.name);
|
||||
foreach (SplineComputer comp in imported) comp.transform.parent = importedParent.transform;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui += OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate += OnScene;
|
||||
#endif
|
||||
|
||||
GetImportedPoints();
|
||||
ApplyPoints();
|
||||
promptSave = true;
|
||||
}
|
||||
|
||||
void ExportSVG(string file)
|
||||
{
|
||||
SVG svg = new SVG(exported);
|
||||
svg.Write(file, (SVG.Axis)((int)exportAxis));
|
||||
}
|
||||
|
||||
void ExportCSV(string file)
|
||||
{
|
||||
CSV csv = new CSV(exported[0]);
|
||||
csv.columns = exportColumns;
|
||||
if (flatCSV)
|
||||
{
|
||||
switch (exportAxis)
|
||||
{
|
||||
case Axis.X: csv.FlatX(); break;
|
||||
case Axis.Y: csv.FlatY(); break;
|
||||
case Axis.Z: csv.FlatZ(); break;
|
||||
}
|
||||
}
|
||||
csv.Write(file);
|
||||
}
|
||||
|
||||
|
||||
void ImportCSV(string file)
|
||||
{
|
||||
CSV csv = new CSV(file, importColumns);
|
||||
originalPoints.Clear();
|
||||
imported.Clear();
|
||||
imported.Add(csv.CreateSplineComputer(Vector3.zero, Quaternion.identity));
|
||||
if (imported.Count == 0) return;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui += OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate += OnScene;
|
||||
#endif
|
||||
|
||||
GetImportedPoints();
|
||||
ApplyPoints();
|
||||
promptSave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/ImportTool.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/ImportTool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9fac175ae39d238478fae9bbe2683559
|
||||
timeCreated: 1456934986
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
656
Assets/Dreamteck/Splines/Editor/Tools/LevelTerrainTool.cs
Normal file
656
Assets/Dreamteck/Splines/Editor/Tools/LevelTerrainTool.cs
Normal file
@@ -0,0 +1,656 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
public class LevelTerrainTool : SplineTool
|
||||
{
|
||||
public override string GetName()
|
||||
{
|
||||
return "Level Terrain";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "LevelTerrainTool";
|
||||
}
|
||||
|
||||
public struct Point
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public Vector2 vector
|
||||
{
|
||||
get { return new Vector2(x, y); }
|
||||
set {
|
||||
x = (int)value.x;
|
||||
y = (int)value.y;
|
||||
}
|
||||
}
|
||||
|
||||
public Point(int newX, int newY)
|
||||
{
|
||||
x = newX;
|
||||
y = newY;
|
||||
}
|
||||
|
||||
public Point(Vector2 input)
|
||||
{
|
||||
x = Mathf.RoundToInt(input.x);
|
||||
y = Mathf.RoundToInt(input.y);
|
||||
}
|
||||
}
|
||||
|
||||
public class TerrainPaintPoint
|
||||
{
|
||||
public Point leftPoint;
|
||||
public Point rightPoint;
|
||||
public float leftHeight = 0f;
|
||||
public float rightHeight = 0f;
|
||||
public Point center;
|
||||
public float floatDiameter = 0f;
|
||||
|
||||
public float GetHeight(float percent)
|
||||
{
|
||||
return Mathf.Lerp(leftHeight, rightHeight, percent);
|
||||
}
|
||||
}
|
||||
|
||||
public float size = 1f;
|
||||
public int feather = 1;
|
||||
public float offset = 0f;
|
||||
public float clipFrom = 0f;
|
||||
public float clipTo = 1f;
|
||||
private float[,] heights = null;
|
||||
private Texture2D brushPreview = null;
|
||||
private Texture2D basePreview = null;
|
||||
private Texture2D drawPreview = null;
|
||||
|
||||
private float maxDrawHeight = 0f;
|
||||
|
||||
|
||||
private bool init = false;
|
||||
|
||||
Terrain terrain = null;
|
||||
|
||||
|
||||
void GetSplinesAndTerrain()
|
||||
{
|
||||
if(splines.Count == 0) GetSplines();
|
||||
for (int i = 0; i < Selection.gameObjects.Length; i++)
|
||||
{
|
||||
if (terrain == null) terrain = Selection.gameObjects[i].GetComponent<Terrain>();
|
||||
}
|
||||
|
||||
Terrain[] terrains = GameObject.FindObjectsOfType<Terrain>();
|
||||
if(terrains.Length == 1)
|
||||
{
|
||||
//if there is only one terrain in the scene, automatically select it
|
||||
terrain = terrains[0];
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
// Draw();
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
GetSplinesAndTerrain();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
if (promptSave)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Apply changes?", "Changes to the terrain have been made. Do you want to keep them?", "Yes", "No"))
|
||||
{
|
||||
SaveChanges();
|
||||
}
|
||||
else RevertToBase();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
base.Draw(windowRect);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
terrain = (Terrain)EditorGUILayout.ObjectField("Terrain", terrain, typeof(Terrain), true);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
heights = null;
|
||||
}
|
||||
|
||||
if (splines.Count == 0) EditorGUILayout.HelpBox("No spline selected! Select an object with a SplineComputer component.", MessageType.Warning);
|
||||
if (terrain == null) EditorGUILayout.HelpBox("No terrain selected! You need to select a terrain.", MessageType.Warning);
|
||||
if (splines.Count == 0 || terrain == null) return;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
brushPreview = GenerateBrushThumbnail();
|
||||
}
|
||||
if (heights == null)
|
||||
{
|
||||
GetBase();
|
||||
}
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.BeginVertical();
|
||||
float lastSize = size;
|
||||
size = EditorGUILayout.FloatField("Brush radius", size);
|
||||
if (size < 0f) size = 0f;
|
||||
if(lastSize != size) brushPreview = GenerateBrushThumbnail();
|
||||
int lastBlur = feather;
|
||||
int maxFeatherCount = Mathf.Max(heights.GetLength(0)/64, 2);
|
||||
feather = EditorGUILayout.IntSlider("Feather", feather, 0, maxFeatherCount);
|
||||
if (lastBlur != feather) brushPreview = GenerateBrushThumbnail();
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.Box("", GUILayout.Width(64), GUILayout.Height(64));
|
||||
Rect rect = GUILayoutUtility.GetLastRect();
|
||||
GUI.DrawTexture(rect, brushPreview);
|
||||
GUILayout.EndHorizontal();
|
||||
offset = EditorGUILayout.FloatField("Height offset", offset);
|
||||
EditorGUILayout.MinMaxSlider(new GUIContent("Spline range"), ref clipFrom, ref clipTo, 0f, 1f);
|
||||
if (GUILayout.Button("Level")) CarveTerrain();
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Terrain heightmap:");
|
||||
GUILayout.Label("Path heightmap:");
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Box("", GUILayout.Width((windowRect.width-10)/2), GUILayout.Height((windowRect.width - 10) / 2));
|
||||
rect = GUILayoutUtility.GetLastRect();
|
||||
GUI.DrawTexture(rect, basePreview);
|
||||
GUILayout.Box("", GUILayout.Width((windowRect.width - 10) / 2), GUILayout.Height((windowRect.width - 10) / 2));
|
||||
rect = GUILayoutUtility.GetLastRect();
|
||||
GUI.DrawTexture(rect, drawPreview);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (promptSave)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Revert")) RevertToBase();
|
||||
if (GUILayout.Button("Apply")) SaveChanges();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
void OnFocus()
|
||||
{
|
||||
GetSplinesAndTerrain();
|
||||
if (promptSave)
|
||||
{
|
||||
bool isChanged = false;
|
||||
float[,] newHeights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution);
|
||||
if (newHeights.GetLength(0) != heights.GetLength(0) || newHeights.GetLength(1) != heights.GetLength(1))
|
||||
{
|
||||
isChanged = true;
|
||||
} else {
|
||||
for (int x = 0; x < heights.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < heights.GetLength(1); y++)
|
||||
{
|
||||
if (heights[x,y] != newHeights[x, y])
|
||||
{
|
||||
isChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isChanged)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Preserve terrain ?", "The terrain has been edited from outside. Do you want to load the new height data? \r\n WARNING: Doing so will apply your leveled data to the terrain.", "Yes", "No"))
|
||||
{
|
||||
GetBase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OnLostFocus()
|
||||
{
|
||||
// RevertToBase();
|
||||
}
|
||||
|
||||
void CarveTerrain()
|
||||
{
|
||||
float[,] drawLayer = new float[terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution];
|
||||
float[,] alphaLayer = new float[terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution];
|
||||
Undo.RecordObject(terrain, "Carve");
|
||||
for (int i = 0; i < splines.Count; i++)
|
||||
{
|
||||
PaintHeightMap(terrain, splines[i], ref drawLayer, ref alphaLayer);
|
||||
}
|
||||
|
||||
float[,] blurLayer = new float[drawLayer.GetLength(0), drawLayer.GetLength(1)];
|
||||
GaussBlur(ref drawLayer, ref blurLayer, feather);
|
||||
float[,] blurAlphaLayer = new float[drawLayer.GetLength(0), drawLayer.GetLength(1)];
|
||||
GaussBlur(ref alphaLayer, ref blurAlphaLayer, feather);
|
||||
float[,] finalLayer = new float[drawLayer.GetLength(0), drawLayer.GetLength(1)];
|
||||
|
||||
|
||||
Color[] pixels = drawPreview.GetPixels();
|
||||
|
||||
|
||||
drawPreview = new Texture2D(drawLayer.GetLength(0), drawLayer.GetLength(1));
|
||||
for (int x = 0; x < drawLayer.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < drawLayer.GetLength(1); y++)
|
||||
{
|
||||
finalLayer[x, y] = Mathf.Lerp(heights[x, y], blurLayer[x, y], blurAlphaLayer[x,y]);
|
||||
pixels[x * drawPreview.width + y] = Color.Lerp(Color.black, Color.white, blurLayer[x, y]/maxDrawHeight*blurAlphaLayer[x,y]);
|
||||
}
|
||||
}
|
||||
terrain.terrainData.SetHeights(0, 0, finalLayer);
|
||||
drawPreview.SetPixels(pixels);
|
||||
drawPreview.Apply();
|
||||
}
|
||||
|
||||
Texture2D GenerateBrushThumbnail()
|
||||
{
|
||||
Texture2D tex = new Texture2D(65, 65, TextureFormat.RGB24, false);
|
||||
Color[] colors = tex.GetPixels();
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
colors[i] = Color.white;
|
||||
}
|
||||
//Get the brush size, compared to the blur amount
|
||||
int hmSize = ToHeightmapSize(size);
|
||||
float percent = 1f;
|
||||
if(hmSize > 0) percent = Mathf.Clamp01((float)(feather* feather) / hmSize);
|
||||
int r = Mathf.RoundToInt(30 * (1f - percent));
|
||||
int center = 32;
|
||||
for (int x = center - 30; x <= center; x++)
|
||||
{
|
||||
for (int y = center - 30; y <= center; y++)
|
||||
{
|
||||
float value = (x - center) * (x - center) + (y - center) * (y - center);
|
||||
int xSym = center - (x - center);
|
||||
int ySym = center - (y - center);
|
||||
|
||||
if (value <= r * r)
|
||||
{
|
||||
colors[x * tex.width + y] = Color.black;
|
||||
colors[xSym * tex.width + y] = Color.black;
|
||||
colors[x * tex.width + ySym] = Color.black;
|
||||
colors[xSym * tex.width + ySym] = Color.black;
|
||||
} else
|
||||
if (value <= 30 * 30 && value > r*r)
|
||||
{
|
||||
float rr = r * r;
|
||||
float val = value - rr;
|
||||
float div = 30 * 30 - rr;
|
||||
float alpha = Mathf.Clamp01(val / div);
|
||||
//Debug.Log(val + "/" + div + " = " + alpha);
|
||||
Color col = Color.Lerp(Color.black, Color.white, alpha);
|
||||
colors[x * tex.width + y] = col;
|
||||
colors[xSym * tex.width + y] = col;
|
||||
colors[x * tex.width + ySym] = col;
|
||||
colors[xSym * tex.width + ySym] = col;
|
||||
}
|
||||
if (value <= 30 * 30 && value >= 29 * 29)
|
||||
{
|
||||
Color col = Color.Lerp(Color.gray, Color.white, 1f-percent);
|
||||
colors[x * tex.width + y] = col;
|
||||
colors[xSym * tex.width + y] = col;
|
||||
colors[x * tex.width + ySym] = col;
|
||||
colors[xSym * tex.width + ySym] = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tex.SetPixels(colors);
|
||||
tex.Apply();
|
||||
return tex;
|
||||
}
|
||||
|
||||
void GetBase()
|
||||
{
|
||||
GetSplinesAndTerrain();
|
||||
if (terrain == null) return;
|
||||
heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapResolution, terrain.terrainData.heightmapResolution);
|
||||
basePreview = new Texture2D(heights.GetLength(0), heights.GetLength(1));
|
||||
drawPreview = new Texture2D(heights.GetLength(0), heights.GetLength(1));
|
||||
Color[] pixels = new Color[basePreview.width * basePreview.height];
|
||||
Color[] blackPixels = new Color[basePreview.width * basePreview.height];
|
||||
float maxHeight = 0f;
|
||||
for (int x = 0; x < basePreview.width; x++)
|
||||
{
|
||||
for(int y = 0; y < basePreview.height; y++)
|
||||
{
|
||||
if (heights[x, y] > maxHeight) maxHeight = heights[x, y];
|
||||
pixels[x * basePreview.width + y] = Color.Lerp(Color.black, Color.white, heights[x, y]);
|
||||
blackPixels[x * basePreview.width + y] = Color.black;
|
||||
}
|
||||
}
|
||||
if(maxHeight > 0f)
|
||||
{
|
||||
for (int x = 0; x < basePreview.width; x++)
|
||||
{
|
||||
for (int y = 0; y < basePreview.height; y++)
|
||||
{
|
||||
pixels[x * basePreview.width + y] /= maxHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
basePreview.SetPixels(pixels);
|
||||
basePreview.Apply();
|
||||
drawPreview.SetPixels(blackPixels);
|
||||
drawPreview.Apply();
|
||||
promptSave = false;
|
||||
}
|
||||
|
||||
void SaveChanges()
|
||||
{
|
||||
GetBase();
|
||||
}
|
||||
|
||||
void RevertToBase()
|
||||
{
|
||||
if (terrain == null) return;
|
||||
terrain.terrainData.SetHeights(0, 0, heights);
|
||||
heights = null;
|
||||
}
|
||||
|
||||
void PaintHeightMap(Terrain terrain, SplineComputer computer, ref float[,] drawLayer, ref float[,] alphaLayer)
|
||||
{
|
||||
if (heights == null) GetBase();
|
||||
SplineSample[] results = new SplineSample[computer.iterations];
|
||||
computer.Evaluate(ref results, clipFrom, clipTo);
|
||||
Draw(results, ref drawLayer, ref alphaLayer);
|
||||
}
|
||||
|
||||
|
||||
int ToHeightmapSize(float value)
|
||||
{
|
||||
float avgSize = (terrain.terrainData.size.x + terrain.terrainData.size.z) / 2f;
|
||||
int result = Mathf.RoundToInt(value / avgSize * terrain.terrainData.heightmapResolution);
|
||||
return result;
|
||||
}
|
||||
|
||||
Point ToHeightmapCoords(Vector3 pos)
|
||||
{
|
||||
Vector3 terrainPos = pos - terrain.transform.position;
|
||||
terrainPos.x /= terrain.terrainData.size.x;
|
||||
terrainPos.z /= terrain.terrainData.size.z;
|
||||
terrainPos.x = Mathf.Clamp01(terrainPos.x);
|
||||
terrainPos.z = Mathf.Clamp01(terrainPos.z);
|
||||
int x = Mathf.RoundToInt(terrainPos.z * terrain.terrainData.heightmapResolution);
|
||||
int y = Mathf.RoundToInt(terrainPos.x * terrain.terrainData.heightmapResolution);
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
float ToHeightmapValue(float y)
|
||||
{
|
||||
float terrainHeight = y - terrain.transform.position.y;
|
||||
terrainHeight /= terrain.terrainData.size.y;
|
||||
return terrainHeight;
|
||||
}
|
||||
|
||||
void PaintSegment(TerrainPaintPoint fromPoint, TerrainPaintPoint toPoint, ref float[,] layer, ref float[,] alphaLayer, bool writeAlpha = true, bool overWriteHeight = true)
|
||||
{
|
||||
//Flip the points if the forward one has a bigger radius so the lerp can work well
|
||||
if (Vector2.Distance(fromPoint.leftPoint.vector, fromPoint.rightPoint.vector) < Vector2.Distance(toPoint.leftPoint.vector, toPoint.rightPoint.vector))
|
||||
{
|
||||
TerrainPaintPoint temp = fromPoint;
|
||||
fromPoint = toPoint;
|
||||
toPoint = temp;
|
||||
}
|
||||
|
||||
List<Point> drawn = new List<Point>();
|
||||
Vector2 currentPosition = fromPoint.leftPoint.vector;
|
||||
Vector2 fromRight = fromPoint.rightPoint.vector;
|
||||
|
||||
float alphaStartPercent = 0f;
|
||||
float alphaEndPercent = 1f;
|
||||
if(feather > 0)
|
||||
{
|
||||
currentPosition += (fromPoint.leftPoint.vector - fromPoint.center.vector).normalized * feather * 4f;
|
||||
fromRight += (fromPoint.rightPoint.vector - fromPoint.center.vector).normalized * feather * 4f;
|
||||
float span = (fromPoint.leftPoint.vector - fromPoint.rightPoint.vector).magnitude / (fromRight - currentPosition).magnitude;
|
||||
float rest = (1f - span) / 2f;
|
||||
alphaStartPercent = rest;
|
||||
alphaEndPercent = 1f - rest;
|
||||
}
|
||||
float armLength = Vector2.Distance(currentPosition, fromRight);
|
||||
if (armLength < 1f) return;
|
||||
while (true)
|
||||
{
|
||||
float armDistance = Vector2.Distance(currentPosition, fromRight);
|
||||
float armPercent = 1f-armDistance / armLength;
|
||||
//This can be optimized, take it outside of the cycle
|
||||
Point fromPos = new Point(currentPosition);
|
||||
Vector2 leftvector = toPoint.leftPoint.vector;
|
||||
Vector2 rightVector = toPoint.rightPoint.vector;
|
||||
if (feather > 0)
|
||||
{
|
||||
leftvector += (toPoint.leftPoint.vector - toPoint.center.vector).normalized * feather * 4f;
|
||||
rightVector += (toPoint.rightPoint.vector - toPoint.center.vector).normalized * feather * 4f;
|
||||
}
|
||||
Vector2 toArm = Vector2.Lerp(leftvector, rightVector, armPercent);
|
||||
|
||||
Point toPos = new Point(toArm);
|
||||
int dx = Mathf.Abs(toPos.x - fromPos.x), sx = fromPos.x < toPos.x ? 1 : -1;
|
||||
int dy = -Mathf.Abs(toPos.y - fromPos.y), sy = fromPos.y < toPos.y ? 1 : -1;
|
||||
int err = dx + dy, e2;
|
||||
Point current = fromPos;
|
||||
Vector2 target = new Vector2(toPos.x - fromPos.x, toPos.y - fromPos.y);
|
||||
|
||||
float fromHeight = fromPoint.GetHeight(armPercent);
|
||||
float toHeight = toPoint.GetHeight(armPercent);
|
||||
while (true)
|
||||
{
|
||||
if (current.x >= 0 && current.x < layer.GetLength(0) && current.y >= 0 && current.y < layer.GetLength(1))
|
||||
{
|
||||
if (overWriteHeight || layer[current.x, current.y] == 0f)
|
||||
{
|
||||
if (!ContainsPoint(ref drawn, current))
|
||||
{
|
||||
Vector2 currentDist = new Vector2(current.x - fromPos.x, current.y - fromPos.y);
|
||||
float positionPercent = Mathf.Clamp01(currentDist.magnitude / target.magnitude);
|
||||
float height = Mathf.Lerp(fromHeight, toHeight, positionPercent);
|
||||
float alphaValue = 0f;
|
||||
if (armPercent >= alphaStartPercent && armPercent <= alphaEndPercent) alphaValue = 1f;
|
||||
if (writeAlpha) Plot(current.x, current.y, height, alphaValue, ref alphaLayer, ref layer);
|
||||
else Plot(current.x, current.y, height, alphaLayer[current.x, current.y], ref alphaLayer, ref layer);
|
||||
drawn.Add(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current.x == toPos.x && current.y == toPos.y) break;
|
||||
e2 = 2 * err;
|
||||
if (e2 > dy)
|
||||
{
|
||||
err += dy;
|
||||
current.x += sx;
|
||||
} else if (e2 < dx)
|
||||
{
|
||||
err += dx;
|
||||
current.y += sy;
|
||||
}
|
||||
}
|
||||
if (currentPosition == fromRight) break;
|
||||
currentPosition = Vector2.MoveTowards(currentPosition, fromRight, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ContainsPoint(ref List<Point> list, Point point)
|
||||
{
|
||||
for(int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (list[i].x == point.x && list[i].y == point.y) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Draw(SplineSample[] points, ref float[,] drawLayer, ref float[,] alphaLayer)
|
||||
{
|
||||
List<SplineSample> selectedPoints = new List<SplineSample>();
|
||||
Point last = new Point();
|
||||
//Filter out points that are too close to each other
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
Point current = ToHeightmapCoords(points[i].position + points[i].up * offset);
|
||||
if (i == 0 || i == points.Length-1)
|
||||
{
|
||||
last = new Point(current.x, current.y);
|
||||
selectedPoints.Add(points[i]);
|
||||
} else if (Vector2.Distance(new Vector2(current.x, current.y), new Vector2(last.x, last.y)) >= 1.5f)
|
||||
{
|
||||
selectedPoints.Add(points[i]);
|
||||
last = new Point(current.x, current.y);
|
||||
}
|
||||
}
|
||||
if (selectedPoints.Count <= 1) return;
|
||||
TerrainPaintPoint[] paintPoints = new TerrainPaintPoint[selectedPoints.Count];
|
||||
for (int i = 0; i < selectedPoints.Count; i++)
|
||||
{
|
||||
ConvertToPaintPoint(selectedPoints[i], ref paintPoints[i]);
|
||||
}
|
||||
//Paint the points
|
||||
for (int i = 0; i < paintPoints.Length - 1; i++)
|
||||
{
|
||||
promptSave = true;
|
||||
PaintSegment(paintPoints[i], paintPoints[i + 1], ref drawLayer, ref alphaLayer);
|
||||
}
|
||||
|
||||
SplineSample exResult = selectedPoints[0];
|
||||
exResult.position += exResult.position - selectedPoints[1].position;
|
||||
TerrainPaintPoint exPoint = null;
|
||||
ConvertToPaintPoint(exResult, ref exPoint);
|
||||
PaintSegment(paintPoints[0], exPoint, ref drawLayer, ref alphaLayer, false, false);
|
||||
|
||||
exResult = selectedPoints[selectedPoints.Count-1];
|
||||
exResult.position += exResult.position - selectedPoints[selectedPoints.Count - 2].position;
|
||||
ConvertToPaintPoint(exResult, ref exPoint);
|
||||
PaintSegment(paintPoints[paintPoints.Length-1], exPoint, ref drawLayer, ref alphaLayer, false, false);
|
||||
//Extrapolate the ending and the begining
|
||||
}
|
||||
|
||||
TerrainPaintPoint ConvertToPaintPoint(SplineSample result, ref TerrainPaintPoint paintPoint)
|
||||
{
|
||||
paintPoint = new TerrainPaintPoint();
|
||||
Vector3 right = -Vector3.Cross(result.forward, result.up).normalized * size * 0.5f * result.size;
|
||||
Vector3 leftPoint = result.position - right + result.up * offset;
|
||||
Vector3 rightPoint = result.position + right + result.up * offset;
|
||||
paintPoint.center = ToHeightmapCoords(result.position + result.up * offset);
|
||||
paintPoint.leftPoint = ToHeightmapCoords(leftPoint);
|
||||
paintPoint.rightPoint = ToHeightmapCoords(rightPoint);
|
||||
paintPoint.leftHeight = ToHeightmapValue(leftPoint.y);
|
||||
paintPoint.rightHeight = ToHeightmapValue(rightPoint.y);
|
||||
paintPoint.floatDiameter = Vector2.Distance(new Vector2(leftPoint.x, leftPoint.z), new Vector2(rightPoint.x, rightPoint.z));
|
||||
if (paintPoint.leftHeight > maxDrawHeight) maxDrawHeight = paintPoint.leftHeight;
|
||||
if (paintPoint.rightHeight > maxDrawHeight) maxDrawHeight = paintPoint.rightHeight;
|
||||
return paintPoint;
|
||||
}
|
||||
|
||||
|
||||
Point Project(Point fromPoint, Point toPoint, int x, int y)
|
||||
{
|
||||
Vector2 dir = toPoint.vector - fromPoint.vector;
|
||||
Vector2 point = new Vector2(x, y);
|
||||
dir.Normalize();
|
||||
Vector2 v = point - fromPoint.vector;
|
||||
float d = Vector2.Dot(v, dir);
|
||||
return new Point(fromPoint.vector + dir * d);
|
||||
}
|
||||
|
||||
void GaussBlur(ref float[,] source, ref float[,] target, int r)
|
||||
{
|
||||
int w = source.GetLength(0);
|
||||
int h = source.GetLength(1);
|
||||
int[] bxs = GBGetBoxes(r, 3);
|
||||
float[] flatSource = new float[source.GetLength(0) * source.GetLength(1)];
|
||||
float[] flatTarget = new float[source.GetLength(0) * source.GetLength(1)];
|
||||
for (int x = 0; x < source.GetLength(0); x++)
|
||||
{
|
||||
for (int y = 0; y < source.GetLength(1); y++)
|
||||
{
|
||||
if (r == 0) target[x, y] = source[x, y];
|
||||
else flatSource[x * source.GetLength(0) + y] = source[x, y];
|
||||
}
|
||||
}
|
||||
if (r == 0) return;
|
||||
BoxBlur(ref flatSource, ref flatTarget, w, h, (bxs[0] - 1) / 2);
|
||||
BoxBlur(ref flatTarget, ref flatSource, w, h, (bxs[1] - 1) / 2);
|
||||
BoxBlur(ref flatSource, ref flatTarget, w, h, (bxs[2] - 1) / 2);
|
||||
|
||||
for (int i = 0; i < flatSource.Length; i++)
|
||||
{
|
||||
int x = Mathf.FloorToInt(i / source.GetLength(0));
|
||||
int y = i - x * source.GetLength(0);
|
||||
target[x, y] = flatTarget[i];
|
||||
}
|
||||
}
|
||||
|
||||
int[] GBGetBoxes(int sigma, int n)
|
||||
{
|
||||
float wIdeal = Mathf.Sqrt((12 * sigma * sigma / n) + 1);
|
||||
int wl = Mathf.FloorToInt(wIdeal); if (wl % 2 == 0) wl--;
|
||||
int wu = wl + 2;
|
||||
|
||||
float mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
|
||||
float m = Mathf.Round(mIdeal);
|
||||
|
||||
int[] sizes = new int[n];
|
||||
for (int i = 0; i < n; i++) sizes[i] = i < m ? wl : wu;
|
||||
return sizes;
|
||||
}
|
||||
|
||||
void BoxBlur(ref float[] source, ref float[] target, int w, int h, int r)
|
||||
{
|
||||
for (int i = 0; i < source.Length; i++) target[i] = source[i];
|
||||
HorizontalBlur(ref target, ref source, w, h, r);
|
||||
VerticalBlur(ref source, ref target, w, h, r);
|
||||
}
|
||||
|
||||
void HorizontalBlur(ref float[] source, ref float[] target, int w, int h, int r)
|
||||
{
|
||||
float iarr = 1f / (r*2f + 1f);
|
||||
for (int i = 0; i < h; i++)
|
||||
{
|
||||
int ti = i * w, li = ti, ri = ti + r;
|
||||
float fv = source[ti], lv = source[ti + w - 1], val = (r + 1) * fv;
|
||||
for (int j = 0; j < r; j++) val += source[ti + j];
|
||||
for (int j = 0; j <= r; j++) { val += source[ri++] - fv; target[ti++] = val * iarr; }
|
||||
for (int j = r + 1; j < w - r; j++) { val += source[ri++] - source[li++]; target[ti++] = val * iarr; }
|
||||
for (int j = w - r; j < w; j++) { val += lv - source[li++]; target[ti++] = val * iarr; }
|
||||
}
|
||||
}
|
||||
|
||||
void VerticalBlur(ref float[] source, ref float[] target, int w, int h, int r)
|
||||
{
|
||||
float iarr = 1f / (r * 2f + 1f);
|
||||
for (int i = 0; i < w; i++)
|
||||
{
|
||||
int ti = i, li = ti, ri = ti + r * w;
|
||||
float fv = source[ti], lv = source[ti + w * (h - 1)], val = (r + 1) * fv;
|
||||
for (var j = 0; j < r; j++) val += source[ti + j * w];
|
||||
for (var j = 0; j <= r; j++) { val += source[ri] - fv; target[ti] = val * iarr; ri += w; ti += w; }
|
||||
for (var j = r + 1; j < h - r; j++) { val += source[ri] - source[li]; target[ti] = val * iarr; li += w; ri += w; ti += w; }
|
||||
for (var j = h - r; j < h; j++) { val += lv - source[li]; target[ti] = val * iarr; li += w; ti += w; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Plot(int x, int y, float value, float alpha, ref float[,] alphaTarget, ref float[,] target)
|
||||
{
|
||||
if (x < 0 || x >= target.GetLength(0)) return;
|
||||
if (y < 0 || y >= target.GetLength(1)) return;
|
||||
if (value > target[x, y])
|
||||
{
|
||||
target[x, y] = value;
|
||||
alphaTarget[x, y] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 055df9d61bd3d9e4382c517846ff1502
|
||||
timeCreated: 1457202793
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
415
Assets/Dreamteck/Splines/Editor/Tools/ObjectSpawnTool.cs
Normal file
415
Assets/Dreamteck/Splines/Editor/Tools/ObjectSpawnTool.cs
Normal file
@@ -0,0 +1,415 @@
|
||||
namespace Dreamteck.Splines.Editor
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
public class ObjectSpawnTool : SplineTool
|
||||
{
|
||||
internal class SpawnCollection
|
||||
{
|
||||
public class SpawnObject
|
||||
{
|
||||
public GameObject instance;
|
||||
public GameObject source;
|
||||
|
||||
public SpawnObject(GameObject instance, GameObject source)
|
||||
{
|
||||
this.instance = instance;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
|
||||
internal SplineComputer spline;
|
||||
internal List<SpawnObject> objects = new List<SpawnObject>();
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
for (int i = 0; i < objects.Count; i++) Object.DestroyImmediate(objects[i].instance);
|
||||
objects.Clear();
|
||||
}
|
||||
|
||||
internal void Spawn(GameObject obj, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
GameObject go = null;
|
||||
bool isPrefab = PrefabUtility.GetPrefabAssetType(obj) != PrefabAssetType.NotAPrefab;
|
||||
|
||||
if (isPrefab) go = (GameObject)PrefabUtility.InstantiatePrefab(obj);
|
||||
else go = Object.Instantiate(obj, position, rotation);
|
||||
go.transform.parent = spline.transform;
|
||||
objects.Add(new SpawnObject(go, obj));
|
||||
}
|
||||
|
||||
internal void Destroy(int index)
|
||||
{
|
||||
Object.DestroyImmediate(objects[index].instance);
|
||||
objects.RemoveAt(index);
|
||||
}
|
||||
|
||||
internal SpawnCollection(SplineComputer spline)
|
||||
{
|
||||
this.spline = spline;
|
||||
}
|
||||
}
|
||||
|
||||
internal List<SpawnCollection> collections = new List<SpawnCollection>();
|
||||
double clipFrom = 0.0, clipTo = 1.0;
|
||||
List<GameObject> objects = new List<GameObject>();
|
||||
int spawnCount = 1;
|
||||
enum Iteration { Ordered, Random }
|
||||
Iteration iteration = Iteration.Ordered;
|
||||
private int offsetSeed = 0;
|
||||
private int rotationSeed = 0;
|
||||
private int scaleSeed = 0;
|
||||
private int orderSeed = 0;
|
||||
private float positionOffset = 0f;
|
||||
private Vector2 randomSize = Vector2.one;
|
||||
private Vector2 offset = Vector2.zero;
|
||||
private Vector3 minRotationOffset = Vector3.zero;
|
||||
private Vector3 maxRotationOffset = Vector3.zero;
|
||||
private Vector3 minScaleMultiplier = Vector3.one;
|
||||
private Vector3 maxScaleMultiplier = Vector3.one;
|
||||
private bool randomizeOffset = false;
|
||||
private bool useRandomOffsetRotation = false;
|
||||
private bool shellOffset = true;
|
||||
private bool applyRotation = true;
|
||||
private bool applyScale = false;
|
||||
bool uniform = false;
|
||||
|
||||
SplineSample result = new SplineSample();
|
||||
|
||||
System.Random orderRandom, offsetRandom, rotationRandom, scaleRandom;
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Spawn Objects";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "ObjectSpawnTool";
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
for (int i = 0; i < splines.Count; i++) splines[i].onRebuild -= Rebuild;
|
||||
if (promptSave)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Save changes?", "You are about to close the Object Spawn Tool, do you want to save the generated objects?", "Yes", "No")) Save();
|
||||
else Cancel();
|
||||
}
|
||||
else Cancel();
|
||||
promptSave = false;
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui -= OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate -= OnScene;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
public override void Open(EditorWindow window)
|
||||
{
|
||||
base.Open(window);
|
||||
GetSplines();
|
||||
collections.Clear();
|
||||
for (int i = 0; i < splines.Count; i++)
|
||||
{
|
||||
collections.Add(new SpawnCollection(splines[i]));
|
||||
splines[i].onRebuild += Rebuild;
|
||||
}
|
||||
Rebuild();
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
SceneView.duringSceneGui += OnScene;
|
||||
#else
|
||||
SceneView.onSceneGUIDelegate += OnScene;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void OnScene(SceneView current)
|
||||
{
|
||||
for (int i = 0; i < collections.Count; i++)
|
||||
{
|
||||
if (collections[i].spline != null) DSSplineDrawer.DrawSplineComputer(collections[i].spline);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSplineAdded(SplineComputer spline)
|
||||
{
|
||||
base.OnSplineAdded(spline);
|
||||
collections.Add(new SpawnCollection(spline));
|
||||
spline.onRebuild += Rebuild;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
protected override void OnSplineRemoved(SplineComputer spline)
|
||||
{
|
||||
base.OnSplineRemoved(spline);
|
||||
for (int i = 0; i < collections.Count; i++)
|
||||
{
|
||||
if (collections[i].spline == spline)
|
||||
{
|
||||
collections[i].Clear();
|
||||
collections.RemoveAt(i);
|
||||
spline.onRebuild -= Rebuild;
|
||||
Rebuild();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
base.Draw(windowRect);
|
||||
if (splines.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No spline selected! Select an object with a SplineComputer component.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
ClipUI(ref clipFrom, ref clipTo);
|
||||
uniform = EditorGUILayout.Toggle("Uniform Samples", uniform);
|
||||
EditorGUILayout.Space();
|
||||
float labelWidth = EditorGUIUtility.labelWidth;
|
||||
float fieldWidth = EditorGUIUtility.fieldWidth;
|
||||
EditorGUIUtility.labelWidth = 0;
|
||||
EditorGUIUtility.fieldWidth = 0;
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
objects[i] = (GameObject)EditorGUILayout.ObjectField(objects[i], typeof(GameObject), true);
|
||||
if (GUILayout.Button("x", GUILayout.Width(20)))
|
||||
{
|
||||
objects.RemoveAt(i);
|
||||
i--;
|
||||
Rebuild();
|
||||
Repaint();
|
||||
continue;
|
||||
}
|
||||
if (i > 0)
|
||||
{
|
||||
if (GUILayout.Button("▲", GUILayout.Width(20)))
|
||||
{
|
||||
GameObject temp = objects[i - 1];
|
||||
objects[i - 1] = objects[i];
|
||||
objects[i] = temp;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
if (i < objects.Count - 1)
|
||||
{
|
||||
if (GUILayout.Button("▼", GUILayout.Width(20)))
|
||||
{
|
||||
GameObject temp = objects[i + 1];
|
||||
objects[i + 1] = objects[i];
|
||||
objects[i] = temp;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
GameObject newObj = null;
|
||||
newObj = (GameObject)EditorGUILayout.ObjectField("Add Object", newObj, typeof(GameObject), true);
|
||||
if (newObj != null)
|
||||
{
|
||||
objects.Add(newObj);
|
||||
Rebuild();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
EditorGUIUtility.fieldWidth = fieldWidth;
|
||||
bool hasObj = false;
|
||||
for (int i = 0; i < objects.Count; i++)
|
||||
{
|
||||
if (objects[i] != null)
|
||||
{
|
||||
hasObj = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasObj) spawnCount = EditorGUILayout.IntField("Spawn count", spawnCount);
|
||||
else spawnCount = 0;
|
||||
iteration = (Iteration)EditorGUILayout.EnumPopup("Iteration", iteration);
|
||||
if (iteration == Iteration.Random) orderSeed = EditorGUILayout.IntField("Order Seed", orderSeed);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Transform", EditorStyles.boldLabel);
|
||||
applyRotation = EditorGUILayout.Toggle("Apply Rotation", applyRotation);
|
||||
if (applyRotation)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
minRotationOffset = EditorGUILayout.Vector3Field("Min. Rotation Offset", minRotationOffset);
|
||||
maxRotationOffset = EditorGUILayout.Vector3Field("Max. Rotation Offset", maxRotationOffset);
|
||||
rotationSeed = EditorGUILayout.IntField("Rotation Seed", rotationSeed);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
applyScale = EditorGUILayout.Toggle("Apply Scale", applyScale);
|
||||
if (applyScale)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
minScaleMultiplier = EditorGUILayout.Vector3Field("Min. Scale Multiplier", minScaleMultiplier);
|
||||
maxScaleMultiplier = EditorGUILayout.Vector3Field("Max. Scale Multiplier", maxScaleMultiplier);
|
||||
scaleSeed = EditorGUILayout.IntField("Scale Seed", scaleSeed);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
positionOffset = EditorGUILayout.Slider("Evaluate Offset", positionOffset, -1f, 1f);
|
||||
|
||||
offset = EditorGUILayout.Vector2Field("Offset", offset);
|
||||
randomizeOffset = EditorGUILayout.Toggle("Randomize Offset", randomizeOffset);
|
||||
if (randomizeOffset)
|
||||
{
|
||||
randomSize = EditorGUILayout.Vector2Field("Size", randomSize);
|
||||
offsetSeed = EditorGUILayout.IntField("Offset Seed", offsetSeed);
|
||||
shellOffset = EditorGUILayout.Toggle("Shell", shellOffset);
|
||||
useRandomOffsetRotation = EditorGUILayout.Toggle("Apply offset rotation", useRandomOffsetRotation);
|
||||
}
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
promptSave = true;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if(collections.Count > 0)
|
||||
{
|
||||
if (GUILayout.Button("Save"))
|
||||
{
|
||||
Save();
|
||||
}
|
||||
if (GUILayout.Button("Cancel"))
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (GUILayout.Button("New")) Open(windowInstance);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected override void Save()
|
||||
{
|
||||
base.Save();
|
||||
//register created object undo for each object in collections
|
||||
collections.Clear();
|
||||
//Set scene dirty
|
||||
}
|
||||
|
||||
protected override void Cancel()
|
||||
{
|
||||
base.Cancel();
|
||||
foreach (SpawnCollection collection in collections) collection.Clear();
|
||||
collections.Clear();
|
||||
}
|
||||
|
||||
void InitializeRandomization()
|
||||
{
|
||||
orderRandom = new System.Random(orderSeed);
|
||||
if (randomizeOffset) offsetRandom = new System.Random(offsetSeed);
|
||||
if(applyRotation) rotationRandom = new System.Random(rotationSeed);
|
||||
if(applyScale) scaleRandom = new System.Random(scaleSeed);
|
||||
}
|
||||
|
||||
protected override void Rebuild()
|
||||
{
|
||||
base.Rebuild();
|
||||
if (objects.Count == 0) return;
|
||||
InitializeRandomization();
|
||||
foreach (SpawnCollection c in collections)
|
||||
{
|
||||
if(c == null) continue;
|
||||
if (c.spline == null || spawnCount <= 0)
|
||||
{
|
||||
c.Clear();
|
||||
continue;
|
||||
}
|
||||
HandleCollection(c);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleCollection(SpawnCollection collection)
|
||||
{
|
||||
collection.Clear();
|
||||
if (collection.spline == null) return;
|
||||
while(collection.objects.Count > spawnCount && collection.objects.Count >= 0) collection.Destroy(collection.objects.Count - 1);
|
||||
int orderIndex = 0;
|
||||
while (collection.objects.Count < spawnCount)
|
||||
{
|
||||
switch (iteration)
|
||||
{
|
||||
case Iteration.Ordered:
|
||||
collection.Spawn(objects[orderIndex], Vector3.zero, Quaternion.identity);
|
||||
orderIndex++;
|
||||
if (orderIndex >= objects.Count) orderIndex = 0;
|
||||
break;
|
||||
case Iteration.Random:
|
||||
collection.Spawn(objects[orderRandom.Next(objects.Count)], Vector3.zero, Quaternion.identity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
float splineLength = 0f;
|
||||
if (uniform) splineLength = collection.spline.CalculateLength() * (float)(clipTo - clipFrom);
|
||||
for (int i = 0; i < spawnCount; i++)
|
||||
{
|
||||
double percent = 0.0;
|
||||
if(spawnCount > 1) percent = (double)i / (spawnCount - 1);
|
||||
double evaluate = 0.0;
|
||||
if (uniform) evaluate = collection.spline.Travel(clipFrom, splineLength * (float)percent, Spline.Direction.Forward);
|
||||
else evaluate = DMath.Lerp(clipFrom, clipTo, percent);
|
||||
//Handle uniform splines
|
||||
evaluate += positionOffset;
|
||||
if (evaluate > 1f) evaluate -= 1f;
|
||||
else if (evaluate < 0f) evaluate += 1f;
|
||||
collection.spline.Evaluate(evaluate, ref result);
|
||||
HandleObject(collection.objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleObject(SpawnCollection.SpawnObject obj)
|
||||
{
|
||||
Transform instanceTransform = obj.instance.transform;
|
||||
Transform sourceTransform = obj.source.transform;
|
||||
Vector3 right = result.right;
|
||||
instanceTransform.position = result.position;
|
||||
instanceTransform.position += -right * offset.x + result.up * offset.y;
|
||||
Quaternion offsetRot = Quaternion.Euler(minRotationOffset);
|
||||
|
||||
if (applyRotation)
|
||||
{
|
||||
offsetRot = Quaternion.Euler(Mathf.Lerp(minRotationOffset.x, maxRotationOffset.x, (float)rotationRandom.NextDouble()), Mathf.Lerp(minRotationOffset.y, maxRotationOffset.y, (float)rotationRandom.NextDouble()), Mathf.Lerp(minRotationOffset.z, maxRotationOffset.z, (float)rotationRandom.NextDouble()));
|
||||
instanceTransform.rotation = result.rotation * offsetRot;
|
||||
}
|
||||
|
||||
if (randomizeOffset)
|
||||
{
|
||||
float distance = (float)offsetRandom.NextDouble();
|
||||
float angleInRadians = (float)offsetRandom.NextDouble() * 360f * Mathf.Deg2Rad;
|
||||
Vector2 randomCircle = new Vector2(distance * Mathf.Cos(angleInRadians), distance * Mathf.Sin(angleInRadians));
|
||||
if (shellOffset) randomCircle.Normalize();
|
||||
else randomCircle = Vector2.ClampMagnitude(randomCircle, 1f);
|
||||
instanceTransform.position += randomCircle.x * right * randomSize.x * result.size * 0.5f + randomCircle.y * result.up * randomSize.y * result.size * 0.5f;
|
||||
if (useRandomOffsetRotation) instanceTransform.rotation = Quaternion.LookRotation(result.forward, instanceTransform.position - result.position) * offsetRot;
|
||||
}
|
||||
|
||||
if (applyScale)
|
||||
{
|
||||
Vector3 scale = sourceTransform.localScale * result.size;
|
||||
scale.x *= Mathf.Lerp(minScaleMultiplier.x, maxScaleMultiplier.x, (float)scaleRandom.NextDouble());
|
||||
scale.y *= Mathf.Lerp(minScaleMultiplier.y, maxScaleMultiplier.y, (float)scaleRandom.NextDouble());
|
||||
scale.z *= Mathf.Lerp(minScaleMultiplier.z, maxScaleMultiplier.z, (float)scaleRandom.NextDouble());
|
||||
instanceTransform.localScale = scale;
|
||||
} else instanceTransform.localScale = sourceTransform.localScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e6dd39825fd0634e9f633f90371a585
|
||||
timeCreated: 1456934986
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
208
Assets/Dreamteck/Splines/Editor/Tools/SplineTool.cs
Normal file
208
Assets/Dreamteck/Splines/Editor/Tools/SplineTool.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
public class SplineTool
|
||||
{
|
||||
protected List<SplineComputer> splines = new List<SplineComputer>();
|
||||
protected bool promptSave = false;
|
||||
protected EditorWindow windowInstance = null;
|
||||
|
||||
public virtual string GetName()
|
||||
{
|
||||
return "Tool";
|
||||
}
|
||||
|
||||
public virtual void Open(EditorWindow window)
|
||||
{
|
||||
windowInstance = window;
|
||||
GetSplines();
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if(promptSave) ClosingDialog();
|
||||
}
|
||||
|
||||
private void ClosingDialog()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Unsaved Changes", ClosingDialogText(), "Yes", "No")) Save();
|
||||
else Cancel();
|
||||
}
|
||||
|
||||
protected virtual string ClosingDialogText()
|
||||
{
|
||||
return "There are unsaved changes. Do you wish to save them?";
|
||||
}
|
||||
|
||||
protected virtual void Save()
|
||||
{
|
||||
promptSave = false;
|
||||
}
|
||||
|
||||
protected virtual void Cancel()
|
||||
{
|
||||
promptSave = false;
|
||||
}
|
||||
|
||||
protected virtual string GetPrefix()
|
||||
{
|
||||
return "SplineTool";
|
||||
}
|
||||
|
||||
public virtual void Draw(Rect rect)
|
||||
{
|
||||
//EditorGUILayout.LabelField("Spline User", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.LabelField("Selected Splines", EditorStyles.boldLabel);
|
||||
for (int i = 0; i < splines.Count; i++)
|
||||
{
|
||||
SplineComputer lastComputer = splines[i];
|
||||
splines[i] = (SplineComputer)EditorGUILayout.ObjectField(splines[i], typeof(SplineComputer), true);
|
||||
if (splines[i] == null)
|
||||
{
|
||||
splines.RemoveAt(i);
|
||||
i--;
|
||||
OnSplineRemoved(lastComputer);
|
||||
continue;
|
||||
}
|
||||
if (lastComputer != splines[i])
|
||||
{
|
||||
for (int j = 0; j < splines.Count; j++)
|
||||
{
|
||||
if (j == i) continue;
|
||||
if (splines[j] == splines[i])
|
||||
{
|
||||
splines[i] = lastComputer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SplineComputer newComp = null;
|
||||
newComp = (SplineComputer)EditorGUILayout.ObjectField(newComp, typeof(SplineComputer), true);
|
||||
if(newComp != null)
|
||||
{
|
||||
for (int i = 0; i < splines.Count; i++)
|
||||
{
|
||||
if (splines[i] == newComp)
|
||||
{
|
||||
newComp = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newComp != null)
|
||||
{
|
||||
splines.Add(newComp);
|
||||
OnSplineAdded(newComp);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
protected virtual void OnSplineAdded(SplineComputer spline)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnSplineRemoved(SplineComputer spline)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void ClipUI(SplineUser user)
|
||||
{
|
||||
float fclipFrom = (float)user.clipFrom, fclipTo = (float)user.clipTo;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.MinMaxSlider(new GUIContent("Clip range:"), ref fclipFrom, ref fclipTo, 0f, 1f);
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(30));
|
||||
user.clipFrom = EditorGUILayout.FloatField(fclipFrom);
|
||||
user.clipTo = EditorGUILayout.FloatField(fclipTo);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected void ClipUI(ref double from, ref double to)
|
||||
{
|
||||
float fclipFrom = (float)from, fclipTo = (float)to;
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.MinMaxSlider(new GUIContent("Clip range:"), ref fclipFrom, ref fclipTo, 0f, 1f);
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(30));
|
||||
from = EditorGUILayout.FloatField(fclipFrom);
|
||||
to = EditorGUILayout.FloatField(fclipTo);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected void SaveCancelUI()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Save")) Save();
|
||||
if (GUILayout.Button("Cancel")) Cancel();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
protected virtual void Rebuild()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void Repaint()
|
||||
{
|
||||
windowInstance.Repaint();
|
||||
}
|
||||
|
||||
protected void GetSplines()
|
||||
{
|
||||
splines.Clear();
|
||||
for (int i = 0; i < Selection.gameObjects.Length; i++)
|
||||
{
|
||||
splines.Add(Selection.gameObjects[i].GetComponent<SplineComputer>());
|
||||
}
|
||||
}
|
||||
|
||||
protected float LoadFloat(string name, float d)
|
||||
{
|
||||
return EditorPrefs.GetFloat(GetPrefix() + "_" + name, d);
|
||||
}
|
||||
|
||||
protected string LoadString(string name, string d)
|
||||
{
|
||||
return EditorPrefs.GetString(GetPrefix() + "_" + name, d);
|
||||
}
|
||||
|
||||
protected bool LoadBool(string name, bool d)
|
||||
{
|
||||
return EditorPrefs.GetBool(GetPrefix() + "_" + name, d);
|
||||
}
|
||||
|
||||
protected int LoadInt(string name, int d)
|
||||
{
|
||||
return EditorPrefs.GetInt(GetPrefix() + "_" + name, d);
|
||||
}
|
||||
|
||||
protected void SaveFloat(string name, float value)
|
||||
{
|
||||
EditorPrefs.SetFloat(GetPrefix() + "_" + name, value);
|
||||
}
|
||||
|
||||
protected void SaveString(string name, string value)
|
||||
{
|
||||
EditorPrefs.SetString(GetPrefix() + "_" + name, value);
|
||||
}
|
||||
|
||||
protected void SaveBool(string name, bool value)
|
||||
{
|
||||
EditorPrefs.SetBool(GetPrefix() + "_" + name, value);
|
||||
}
|
||||
|
||||
protected void SaveInt(string name, int value)
|
||||
{
|
||||
EditorPrefs.SetInt(GetPrefix() + "_" + name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/SplineTool.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/SplineTool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d43ab6043eb5d1a429ba1bfac215730e
|
||||
timeCreated: 1450642283
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Assets/Dreamteck/Splines/Editor/Tools/SplineToolsWindow.cs
Normal file
75
Assets/Dreamteck/Splines/Editor/Tools/SplineToolsWindow.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace Dreamteck.Splines.Editor
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
public class SplineToolsWindow : EditorWindow
|
||||
{
|
||||
private static SplineTool[] tools;
|
||||
private int toolIndex = -1;
|
||||
private Vector2 scroll = Vector2.zero;
|
||||
private const float menuWidth = 150f;
|
||||
[MenuItem("Window/Dreamteck/Splines/Tools")]
|
||||
static void Init()
|
||||
{
|
||||
SplineToolsWindow window = (SplineToolsWindow)EditorWindow.GetWindow(typeof(SplineToolsWindow));
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
titleContent = new GUIContent("Spline Tools");
|
||||
name = "Spline tools";
|
||||
autoRepaintOnSceneChange = true;
|
||||
|
||||
List<Type> types = FindDerivedClasses.GetAllDerivedClasses(typeof(SplineTool));
|
||||
tools = new SplineTool[types.Count];
|
||||
int count = 0;
|
||||
foreach (Type t in types)
|
||||
{
|
||||
tools[count] = (SplineTool)Activator.CreateInstance(t);
|
||||
count++;
|
||||
}
|
||||
if (toolIndex >= 0 && toolIndex < tools.Length) tools[toolIndex].Open(this);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (toolIndex >= 0 && toolIndex < tools.Length) tools[toolIndex].Close();
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (tools == null) Awake();
|
||||
GUI.color = new Color(0f, 0f, 0f, 0.15f);
|
||||
GUI.DrawTexture(new Rect(0, 0, menuWidth, position.height), SplineEditorGUI.white, ScaleMode.StretchToFill);
|
||||
GUI.color = Color.white;
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.BeginScrollView(scroll, GUILayout.Width(menuWidth), GUILayout.Height(position.height-10));
|
||||
if (tools == null) Init();
|
||||
SplineEditorGUI.SetHighlightColors(SplinePrefs.highlightColor, SplinePrefs.highlightContentColor);
|
||||
for (int i = 0; i < tools.Length; i ++)
|
||||
{
|
||||
if (SplineEditorGUI.EditorLayoutSelectableButton(new GUIContent(tools[i].GetName()), true, toolIndex == i))
|
||||
{
|
||||
if (toolIndex >= 0 && toolIndex < tools.Length) tools[toolIndex].Close();
|
||||
toolIndex = i;
|
||||
if (toolIndex < tools.Length) tools[toolIndex].Open(this);
|
||||
}
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
|
||||
if(toolIndex >= 0 && toolIndex < tools.Length)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
tools[toolIndex].Draw(new Rect(menuWidth, 0, position.width - menuWidth - 5f, position.height - 10));
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e87a0b749a684c34c84ee3aab5993592
|
||||
timeCreated: 1450551681
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
118
Assets/Dreamteck/Splines/Editor/Tools/UpdateTool.cs
Normal file
118
Assets/Dreamteck/Splines/Editor/Tools/UpdateTool.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
public class UpdateTool : SplineTool
|
||||
{
|
||||
protected GameObject obj;
|
||||
protected ObjectController spawner;
|
||||
private string updated = "";
|
||||
|
||||
public override string GetName()
|
||||
{
|
||||
return "Update Components";
|
||||
}
|
||||
|
||||
protected override string GetPrefix()
|
||||
{
|
||||
return "UpdateTool";
|
||||
}
|
||||
|
||||
public override void Draw(Rect windowRect)
|
||||
{
|
||||
if (GUILayout.Button("Update All Spline Components"))
|
||||
{
|
||||
updated = "";
|
||||
UpdateComputers();
|
||||
UpdateNodes();
|
||||
UpdateUsers();
|
||||
}
|
||||
if (GUILayout.Button("Update SplineUsers"))
|
||||
{
|
||||
updated = "";
|
||||
UpdateUsers();
|
||||
}
|
||||
if (GUILayout.Button("Update MeshGenerators"))
|
||||
{
|
||||
updated = "";
|
||||
UpdateMeshGenerators();
|
||||
}
|
||||
if (GUILayout.Button("Update SplineComputers"))
|
||||
{
|
||||
updated = "";
|
||||
UpdateComputers();
|
||||
}
|
||||
if (GUILayout.Button("Update Nodes In Scene"))
|
||||
{
|
||||
updated = "";
|
||||
UpdateNodes();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
GUILayout.Label(updated);
|
||||
}
|
||||
|
||||
private void UpdateNodes()
|
||||
{
|
||||
Node[] nodes = GameObject.FindObjectsOfType<Node>();
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Updating nodes", "Updating node " + nodes[i].name, (float)i / (nodes.Length - 1));
|
||||
nodes[i].UpdateConnectedComputers();
|
||||
EditorUtility.SetDirty(nodes[i]);
|
||||
updated += i + " - " + nodes[i].name + System.Environment.NewLine;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (nodes.Length == 0) updated += System.Environment.NewLine+"No active Nodes found in the scene.";
|
||||
}
|
||||
|
||||
private void UpdateUsers()
|
||||
{
|
||||
SplineUser[] users = GameObject.FindObjectsOfType<SplineUser>();
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < users.Length; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Updating users", "Updating user " + users[i].name, (float)i/(users.Length-1));
|
||||
users[i].Rebuild();
|
||||
EditorUtility.SetDirty(users[i]);
|
||||
updated += i + " - " + users[i].name + System.Environment.NewLine;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (users.Length == 0) updated += System.Environment.NewLine+"No active SplineUsers found in the scene.";
|
||||
}
|
||||
|
||||
private void UpdateMeshGenerators()
|
||||
{
|
||||
MeshGenerator[] users = GameObject.FindObjectsOfType<MeshGenerator>();
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < users.Length; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Updating mesh generators", "Updating generator " + users[i].name, (float)i / (users.Length - 1));
|
||||
users[i].Rebuild();
|
||||
EditorUtility.SetDirty(users[i]);
|
||||
updated += i + " - " + users[i].name + System.Environment.NewLine;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (users.Length == 0) updated += System.Environment.NewLine + "No active MeshGenerators found in the scene.";
|
||||
}
|
||||
|
||||
private void UpdateComputers()
|
||||
{
|
||||
SplineComputer[] computers = GameObject.FindObjectsOfType<SplineComputer>();
|
||||
EditorUtility.ClearProgressBar();
|
||||
for (int i = 0; i < computers.Length; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Updating spline computers", "Updating computer " + computers[i].name, (float)i / (computers.Length - 1));
|
||||
computers[i].RebuildImmediate();
|
||||
EditorUtility.SetDirty(computers[i]);
|
||||
updated += i + " - " + computers[i].name + System.Environment.NewLine;
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (computers.Length == 0) updated += System.Environment.NewLine+"No active SplineComputers found in the scene.";
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Editor/Tools/UpdateTool.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Editor/Tools/UpdateTool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b86089eb8b849648b253ed7b5be27a7
|
||||
timeCreated: 1456934986
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user