1
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.BackupTool;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GFoliageRemover))]
|
||||
public class GFoliageRemoverInspector : Editor
|
||||
{
|
||||
private GFoliageRemover instance;
|
||||
private Dictionary<GStylizedTerrain, RenderTexture> previewTextures;
|
||||
|
||||
private bool isUrp;
|
||||
private ScriptableRenderContext urpContext;
|
||||
|
||||
private const string HISTORY_PREFIX = "Remove Foliage Along Path";
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Undo.undoRedoPerformed += OnUndoRedo;
|
||||
instance = target as GFoliageRemover;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (GStylizedTerrain t in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[t];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUndoRedo()
|
||||
{
|
||||
if (Selection.activeGameObject != instance.gameObject)
|
||||
return;
|
||||
if (string.IsNullOrEmpty(GUndoCompatibleBuffer.Instance.CurrentBackupName))
|
||||
return;
|
||||
GBackup.Restore(GUndoCompatibleBuffer.Instance.CurrentBackupName);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
DrawFalloffGUI();
|
||||
DrawMaskGUI();
|
||||
DrawTreesGUI();
|
||||
DrawGrassesGUI();
|
||||
DrawActionGUI();
|
||||
}
|
||||
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component which this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview on the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.foliageRemover = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.foliageRemover);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-foliage-remover-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GMaskGUI
|
||||
{
|
||||
public static readonly string LABEL = "Mask";
|
||||
public static readonly string ID = "spline-foliage-remover-mask";
|
||||
public static readonly GUIContent MASK_RESOLUTION = new GUIContent("Mask Resolution", "Resolution of the mask which is rendered by the spline, this mask will be used for sample foliage instances");
|
||||
}
|
||||
|
||||
private void DrawMaskGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GMaskGUI.LABEL, false, GMaskGUI.ID, () =>
|
||||
{
|
||||
instance.MaskResolution = EditorGUILayout.DelayedIntField(GMaskGUI.MASK_RESOLUTION, instance.MaskResolution);
|
||||
});
|
||||
}
|
||||
|
||||
private class GTreesGUI
|
||||
{
|
||||
public static readonly string LABEL = "Trees";
|
||||
public static readonly string ID = "spline-foliage-remover-trees";
|
||||
|
||||
public static readonly GUIContent REMOVE_TREES = new GUIContent("Remove Trees", "Toggle trees removing along the spline");
|
||||
public static readonly GUIContent PROTOTYPES = new GUIContent("Prototypes", "The tree types to remove");
|
||||
}
|
||||
|
||||
private void DrawTreesGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GTreesGUI.LABEL, false, GTreesGUI.ID, () =>
|
||||
{
|
||||
instance.RemoveTrees = EditorGUILayout.Toggle(GTreesGUI.REMOVE_TREES, instance.RemoveTrees);
|
||||
if (instance.RemoveTrees)
|
||||
{
|
||||
EditorGUILayout.LabelField(GTreesGUI.PROTOTYPES);
|
||||
instance.TreePrototypeIndices = GEditorCommon.TreeSetMultiSelectionGrid(instance.SplineCreator.GroupId, instance.TreePrototypeIndices);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class GGrassesGUI
|
||||
{
|
||||
public static readonly string LABEL = "Grasses";
|
||||
public static readonly string ID = "spline-foliage-remover-grasses";
|
||||
|
||||
public static readonly GUIContent REMOVE_GRASSES = new GUIContent("Remove Grasses", "Toggle grasses removing along the spline");
|
||||
public static readonly GUIContent PROTOTYPES = new GUIContent("Prototypes", "The grass types to remove");
|
||||
}
|
||||
|
||||
private void DrawGrassesGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GGrassesGUI.LABEL, false, GGrassesGUI.ID, () =>
|
||||
{
|
||||
instance.RemoveGrasses = EditorGUILayout.Toggle(GGrassesGUI.REMOVE_GRASSES, instance.RemoveGrasses);
|
||||
if (instance.RemoveGrasses)
|
||||
{
|
||||
EditorGUILayout.LabelField(GGrassesGUI.PROTOTYPES);
|
||||
instance.GrassPrototypeIndices = GEditorCommon.GrassSetMultiSelectionGrid(instance.SplineCreator.GroupId, instance.GrassPrototypeIndices);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-foliage-remover-action";
|
||||
|
||||
public static readonly GUIContent REMOVE_TREES = new GUIContent("Remove Trees");
|
||||
public static readonly GUIContent REMOVE_GRASSES = new GUIContent("Remove Grasses");
|
||||
public static readonly GUIContent REMOVE_TREES_GRASSES = new GUIContent("Remove Trees & Grasses");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
GUIContent btnLabel;
|
||||
if (instance.RemoveTrees && instance.RemoveGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.REMOVE_TREES_GRASSES;
|
||||
}
|
||||
else if (instance.RemoveTrees && !instance.RemoveGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.REMOVE_TREES;
|
||||
}
|
||||
else if (!instance.RemoveTrees && instance.RemoveGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.REMOVE_GRASSES;
|
||||
}
|
||||
else
|
||||
{
|
||||
btnLabel = GActionGUI.REMOVE_TREES_GRASSES;
|
||||
}
|
||||
|
||||
GUI.enabled = instance.RemoveTrees || instance.RemoveGrasses;
|
||||
if (GUILayout.Button(btnLabel))
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(instance.SplineCreator.SweepTest());
|
||||
CreateInitialBackup(terrains);
|
||||
Apply();
|
||||
CreateBackupAfterApply(terrains);
|
||||
}
|
||||
GUI.enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateInitialBackup(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeInitialBackup(HISTORY_PREFIX, terrains, GCommon.FoliageInstancesResourceFlags, true);
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Removing foliage...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void CreateBackupAfterApply(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeBackup(HISTORY_PREFIX, terrains, GCommon.FoliageInstancesResourceFlags, true);
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
isUrp = false;
|
||||
urpContext = default;
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.foliageRemover)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTests = instance.SplineCreator.SweepTest();
|
||||
foreach (GOverlapTestResult st in sweepTests)
|
||||
{
|
||||
if (!st.IsOverlapped)
|
||||
continue;
|
||||
DrawLivePreview(st.Terrain, cam, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
if (t.transform.rotation != Quaternion.identity ||
|
||||
t.transform.lossyScale != Vector3.one)
|
||||
return;
|
||||
|
||||
RenderTexture rt = GetPreviewTexture(t);
|
||||
instance.Internal_Apply(t, rt);
|
||||
|
||||
GLivePreviewDrawer.DrawMasksLivePreview(
|
||||
t, cam,
|
||||
new Texture[] { rt },
|
||||
new Color[] { GEditorSettings.Instance.splineTools.negativeHighlightColor },
|
||||
chunkCulling);
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<GStylizedTerrain, RenderTexture>();
|
||||
}
|
||||
|
||||
int resolution = instance.MaskResolution;
|
||||
if (!previewTextures.ContainsKey(t) ||
|
||||
previewTextures[t] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
else if (previewTextures[t].width != resolution || previewTextures[t].height != resolution)
|
||||
{
|
||||
previewTextures[t].Release();
|
||||
Object.DestroyImmediate(previewTextures[t]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
|
||||
previewTextures[t].wrapMode = TextureWrapMode.Clamp;
|
||||
return previewTextures[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2193db762e1d63142b706eb944248806
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,352 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.BackupTool;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GFoliageSpawner))]
|
||||
public class GFoliageSpawnerInspector : Editor
|
||||
{
|
||||
private GFoliageSpawner instance;
|
||||
private Dictionary<GStylizedTerrain, RenderTexture> previewTextures;
|
||||
private MaterialPropertyBlock previewPropertyBlock;
|
||||
|
||||
private bool isUrp;
|
||||
private ScriptableRenderContext urpContext;
|
||||
|
||||
private const string HISTORY_PREFIX = "Spawn Foliage Along Path";
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Undo.undoRedoPerformed += OnUndoRedo;
|
||||
instance = target as GFoliageSpawner;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
previewPropertyBlock = new MaterialPropertyBlock();
|
||||
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (GStylizedTerrain t in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[t];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUndoRedo()
|
||||
{
|
||||
if (Selection.activeGameObject != instance.gameObject)
|
||||
return;
|
||||
if (string.IsNullOrEmpty(GUndoCompatibleBuffer.Instance.CurrentBackupName))
|
||||
return;
|
||||
GBackup.Restore(GUndoCompatibleBuffer.Instance.CurrentBackupName);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
DrawFalloffGUI();
|
||||
DrawMaskGUI();
|
||||
DrawRotationScaleGUI();
|
||||
DrawTreesGUI();
|
||||
DrawGrassesGUI();
|
||||
DrawActionGUI();
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component which this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview on the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.foliageSpawner = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.foliageSpawner);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-foliage-spawner-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GMaskGUI
|
||||
{
|
||||
public static readonly string LABEL = "Mask";
|
||||
public static readonly string ID = "spline-foliage-spawner-mask";
|
||||
public static readonly GUIContent MASK_RESOLUTION = new GUIContent("Mask Resolution", "Resolution of the mask which is rendered by the spline, this mask will be used for sample foliage instances");
|
||||
}
|
||||
|
||||
private void DrawMaskGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GMaskGUI.LABEL, false, GMaskGUI.ID, () =>
|
||||
{
|
||||
instance.MaskResolution = EditorGUILayout.DelayedIntField(GMaskGUI.MASK_RESOLUTION, instance.MaskResolution);
|
||||
});
|
||||
}
|
||||
|
||||
private class GTreesGUI
|
||||
{
|
||||
public static readonly string LABEL = "Trees";
|
||||
public static readonly string ID = "spline-foliage-spawner-trees";
|
||||
|
||||
public static readonly GUIContent SPAWN_TREES = new GUIContent("Spawn Trees", "Toggle trees spawning along the spline");
|
||||
public static readonly GUIContent PROTOTYPES = new GUIContent("Prototypes", "The tree types to spawn");
|
||||
public static readonly GUIContent DENSITY = new GUIContent("Density", "Number of instances per a meter-squared");
|
||||
}
|
||||
|
||||
private void DrawTreesGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GTreesGUI.LABEL, false, GTreesGUI.ID, () =>
|
||||
{
|
||||
instance.SpawnTrees = EditorGUILayout.Toggle(GTreesGUI.SPAWN_TREES, instance.SpawnTrees);
|
||||
if (instance.SpawnTrees)
|
||||
{
|
||||
EditorGUILayout.LabelField(GTreesGUI.PROTOTYPES);
|
||||
instance.TreePrototypeIndices = GEditorCommon.TreeSetMultiSelectionGrid(instance.SplineCreator.GroupId, instance.TreePrototypeIndices);
|
||||
instance.TreeDensity = EditorGUILayout.FloatField(GTreesGUI.DENSITY, instance.TreeDensity);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private class GGrassesGUI
|
||||
{
|
||||
public static readonly string LABEL = "Grasses";
|
||||
public static readonly string ID = "spline-foliage-spawner-grasses";
|
||||
|
||||
public static readonly GUIContent SPAWN_GRASSES = new GUIContent("Spawn Grasses", "Toggle grasses spawning along the spline");
|
||||
public static readonly GUIContent PROTOTYPES = new GUIContent("Prototypes", "The grass types to spawn");
|
||||
public static readonly GUIContent DENSITY = new GUIContent("Density", "Number of instances per a meter-squared");
|
||||
}
|
||||
|
||||
private void DrawGrassesGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GGrassesGUI.LABEL, false, GGrassesGUI.ID, () =>
|
||||
{
|
||||
instance.SpawnGrasses = EditorGUILayout.Toggle(GGrassesGUI.SPAWN_GRASSES, instance.SpawnGrasses);
|
||||
if (instance.SpawnGrasses)
|
||||
{
|
||||
EditorGUILayout.LabelField(GGrassesGUI.PROTOTYPES);
|
||||
instance.GrassPrototypeIndices = GEditorCommon.GrassSetMultiSelectionGrid(instance.SplineCreator.GroupId, instance.GrassPrototypeIndices);
|
||||
instance.GrassDensity = EditorGUILayout.FloatField(GGrassesGUI.DENSITY, instance.GrassDensity);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private class GRotationScaleGUI
|
||||
{
|
||||
public static readonly string LABEL = "Rotation & Scale";
|
||||
public static readonly string ID = "spline-foliage-spawner-rotation-scale";
|
||||
|
||||
public static readonly GUIContent MIN_ROTATION = new GUIContent("Min Rotation", "Minimum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MAX_ROTATION = new GUIContent("Max Rotation", "Maximum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MIN_SCALE = new GUIContent("Min Scale", "Minimum scale of each instance");
|
||||
public static readonly GUIContent MAX_SCALE = new GUIContent("Max Scale", "Maximum scale of each instance");
|
||||
}
|
||||
|
||||
private void DrawRotationScaleGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GRotationScaleGUI.LABEL, false, GRotationScaleGUI.ID, () =>
|
||||
{
|
||||
instance.MinRotation = EditorGUILayout.FloatField(GRotationScaleGUI.MIN_ROTATION, instance.MinRotation);
|
||||
instance.MaxRotation = EditorGUILayout.FloatField(GRotationScaleGUI.MAX_ROTATION, instance.MaxRotation);
|
||||
instance.MinScale = GEditorCommon.InlineVector3Field(GRotationScaleGUI.MIN_SCALE, instance.MinScale);
|
||||
instance.MaxScale = GEditorCommon.InlineVector3Field(GRotationScaleGUI.MAX_SCALE, instance.MaxScale);
|
||||
});
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-foliage-spawner-action";
|
||||
|
||||
public static readonly GUIContent SPAWN_TREES = new GUIContent("Spawn Trees");
|
||||
public static readonly GUIContent SPAWN_GRASSES = new GUIContent("Spawn Grasses");
|
||||
public static readonly GUIContent SPAWN_TREES_GRASSES = new GUIContent("Spawn Trees & Grasses");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
GUIContent btnLabel;
|
||||
if (instance.SpawnTrees && instance.SpawnGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.SPAWN_TREES_GRASSES;
|
||||
}
|
||||
else if (instance.SpawnTrees && !instance.SpawnGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.SPAWN_TREES;
|
||||
}
|
||||
else if (!instance.SpawnTrees && instance.SpawnGrasses)
|
||||
{
|
||||
btnLabel = GActionGUI.SPAWN_GRASSES;
|
||||
}
|
||||
else
|
||||
{
|
||||
btnLabel = GActionGUI.SPAWN_TREES_GRASSES;
|
||||
}
|
||||
|
||||
GUI.enabled = instance.SpawnTrees || instance.SpawnGrasses;
|
||||
if (GUILayout.Button(btnLabel))
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(instance.SplineCreator.SweepTest());
|
||||
CreateInitialBackup(terrains);
|
||||
Apply();
|
||||
CreateBackupAfterApply(terrains);
|
||||
}
|
||||
GUI.enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateInitialBackup(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeInitialBackup(HISTORY_PREFIX, terrains, GCommon.FoliageInstancesResourceFlags, true);
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Spawning foliage...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void CreateBackupAfterApply(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeBackup(HISTORY_PREFIX, terrains, GCommon.FoliageInstancesResourceFlags, true);
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
isUrp = false;
|
||||
urpContext = default;
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.foliageSpawner)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTests = instance.SplineCreator.SweepTest();
|
||||
foreach (GOverlapTestResult st in sweepTests)
|
||||
{
|
||||
if (!st.IsOverlapped)
|
||||
continue;
|
||||
DrawLivePreview(st.Terrain, cam, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
if (t.transform.rotation != Quaternion.identity ||
|
||||
t.transform.lossyScale != Vector3.one)
|
||||
return;
|
||||
|
||||
RenderTexture rt = GetPreviewTexture(t);
|
||||
instance.Internal_Apply(t, rt);
|
||||
GLivePreviewDrawer.DrawMasksLivePreview(
|
||||
t, cam,
|
||||
new Texture[] { rt },
|
||||
new Color[] { GEditorSettings.Instance.splineTools.positiveHighlightColor },
|
||||
chunkCulling);
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<GStylizedTerrain, RenderTexture>();
|
||||
}
|
||||
|
||||
int resolution = instance.MaskResolution;
|
||||
if (!previewTextures.ContainsKey(t) ||
|
||||
previewTextures[t] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
else if (previewTextures[t].width != resolution || previewTextures[t].height != resolution)
|
||||
{
|
||||
previewTextures[t].Release();
|
||||
Object.DestroyImmediate(previewTextures[t]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
|
||||
previewTextures[t].wrapMode = TextureWrapMode.Clamp;
|
||||
return previewTextures[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 614c28b376274cc47ae3afad00ad3139
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,305 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GObjectRemover))]
|
||||
public class GObjectRemoverInspector : Editor
|
||||
{
|
||||
private GObjectRemover instance;
|
||||
private Dictionary<GStylizedTerrain, RenderTexture> previewTextures;
|
||||
|
||||
private bool isUrp;
|
||||
private ScriptableRenderContext urpContext;
|
||||
|
||||
private const string HISTORY_PREFIX = "Remove Foliage Along Path";
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
instance = target as GObjectRemover;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (GStylizedTerrain t in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[t];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
DrawFalloffGUI();
|
||||
DrawMaskGUI();
|
||||
DrawObjectGUI();
|
||||
DrawActionGUI();
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component which this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview on the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.objectRemover = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.objectRemover);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-object-remover-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GMaskGUI
|
||||
{
|
||||
public static readonly string LABEL = "Mask";
|
||||
public static readonly string ID = "spline-object-remover-mask";
|
||||
public static readonly GUIContent MASK_RESOLUTION = new GUIContent("Mask Resolution", "Resolution of the mask which is rendered by the spline, this mask will be used for sample object instances");
|
||||
}
|
||||
|
||||
private void DrawMaskGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GMaskGUI.LABEL, false, GMaskGUI.ID, () =>
|
||||
{
|
||||
instance.MaskResolution = EditorGUILayout.DelayedIntField(GMaskGUI.MASK_RESOLUTION, instance.MaskResolution);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private class GObjectGUI
|
||||
{
|
||||
public static readonly string LABEL = "Objects";
|
||||
public static readonly string ID = "spline-object-remover-objects";
|
||||
|
||||
public static readonly GUIContent MIN_ROTATION = new GUIContent("Min Rotation", "Minimum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MAX_ROTATION = new GUIContent("Max Rotation", "Maximum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MIN_SCALE = new GUIContent("Min Scale", "Minimum scale of each instance");
|
||||
public static readonly GUIContent MAX_SCALE = new GUIContent("Max Scale", "Maximum scale of each instance");
|
||||
public static readonly GUIContent RAYCAST_LAYER = new GUIContent("Raycast Layer", "Object layer to perform raycast for snapping");
|
||||
public static readonly GUIContent ALIGN_TO_SURFACE = new GUIContent("Align To Surface", "Align the object for terrain surface");
|
||||
public static readonly GUIContent DENSITY = new GUIContent("Density", "Number of object instances per a meter-squared");
|
||||
}
|
||||
|
||||
private void DrawObjectGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GObjectGUI.LABEL, false, GObjectGUI.ID, () =>
|
||||
{
|
||||
DrawObjectSelectorGUI();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void DrawObjectSelectorGUI()
|
||||
{
|
||||
if (instance.Prototypes.Count > 0)
|
||||
{
|
||||
GSelectionGridArgs args = new GSelectionGridArgs();
|
||||
args.collection = instance.Prototypes;
|
||||
args.selectedIndices = instance.PrototypeIndices;
|
||||
args.itemSize = GEditorCommon.selectionGridTileSizeSmall;
|
||||
args.itemPerRow = 4;
|
||||
args.drawPreviewFunction = GEditorCommon.DrawGameObjectPreview;
|
||||
args.contextClickFunction = OnObjectSelectorContextClick;
|
||||
instance.PrototypeIndices = GEditorCommon.MultiSelectionGrid(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("No Game Object found!", GEditorCommon.WordWrapItalicLabel);
|
||||
}
|
||||
GEditorCommon.Separator();
|
||||
|
||||
Rect r1 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight));
|
||||
GameObject prefab = GEditorCommon.ObjectSelectorDragDrop<GameObject>(r1, "Drop a Game Object here!", "t:GameObject");
|
||||
if (prefab != null)
|
||||
{
|
||||
instance.Prototypes.AddIfNotContains(prefab);
|
||||
}
|
||||
|
||||
GEditorCommon.SpacePixel(0);
|
||||
Rect r2 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight));
|
||||
GPrefabPrototypeGroup group = GEditorCommon.ObjectSelectorDragDrop<GPrefabPrototypeGroup>(r2, "Drop a Prefab Prototype Group here!", "t:GPrefabPrototypeGroup");
|
||||
if (group != null)
|
||||
{
|
||||
instance.Prototypes.AddIfNotContains(group.Prototypes);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnObjectSelectorContextClick(Rect r, int index, ICollection collection)
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(
|
||||
new GUIContent("Remove"),
|
||||
false,
|
||||
() =>
|
||||
{
|
||||
instance.Prototypes.RemoveAt(index);
|
||||
});
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-object-remover-action";
|
||||
|
||||
public static readonly GUIContent SPAWN = new GUIContent("Remove");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
if (GUILayout.Button(GActionGUI.SPAWN))
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Removing objects...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
isUrp = false;
|
||||
urpContext = default;
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.objectRemover)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTests = instance.SplineCreator.SweepTest();
|
||||
foreach (GOverlapTestResult st in sweepTests)
|
||||
{
|
||||
if (!st.IsOverlapped)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawLivePreview(st.Terrain, cam, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
if (t.transform.rotation != Quaternion.identity ||
|
||||
t.transform.lossyScale != Vector3.one)
|
||||
return;
|
||||
|
||||
RenderTexture rt = GetPreviewTexture(t);
|
||||
instance.Internal_Apply(t, rt);
|
||||
GLivePreviewDrawer.DrawMasksLivePreview(
|
||||
t, cam,
|
||||
new Texture[] { rt },
|
||||
new Color[] { GEditorSettings.Instance.splineTools.negativeHighlightColor },
|
||||
chunkCulling);
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<GStylizedTerrain, RenderTexture>();
|
||||
}
|
||||
|
||||
int resolution = instance.MaskResolution;
|
||||
if (!previewTextures.ContainsKey(t) ||
|
||||
previewTextures[t] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
else if (previewTextures[t].width != resolution || previewTextures[t].height != resolution)
|
||||
{
|
||||
previewTextures[t].Release();
|
||||
Object.DestroyImmediate(previewTextures[t]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
|
||||
previewTextures[t].wrapMode = TextureWrapMode.Clamp;
|
||||
return previewTextures[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14435b783456cd14680d1acdb4dd62c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,306 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GObjectSpawner))]
|
||||
public class GObjectSpawnerInspector : Editor
|
||||
{
|
||||
private GObjectSpawner instance;
|
||||
private Dictionary<GStylizedTerrain, RenderTexture> previewTextures;
|
||||
|
||||
private bool isUrp;
|
||||
private ScriptableRenderContext urpContext;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
instance = target as GObjectSpawner;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (GStylizedTerrain t in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[t];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
DrawFalloffGUI();
|
||||
DrawMaskGUI();
|
||||
DrawObjectGUI();
|
||||
DrawActionGUI();
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component which this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview on the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.objectSpawner = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.objectSpawner);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-object-spawner-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GMaskGUI
|
||||
{
|
||||
public static readonly string LABEL = "Mask";
|
||||
public static readonly string ID = "spline-object-spawner-mask";
|
||||
public static readonly GUIContent MASK_RESOLUTION = new GUIContent("Mask Resolution", "Resolution of the mask which is rendered by the spline, this mask will be used for sample object instances");
|
||||
}
|
||||
|
||||
private void DrawMaskGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GMaskGUI.LABEL, false, GMaskGUI.ID, () =>
|
||||
{
|
||||
instance.MaskResolution = EditorGUILayout.DelayedIntField(GMaskGUI.MASK_RESOLUTION, instance.MaskResolution);
|
||||
});
|
||||
}
|
||||
|
||||
private class GObjectGUI
|
||||
{
|
||||
public static readonly string LABEL = "Objects";
|
||||
public static readonly string ID = "spline-object-spawner-objects";
|
||||
|
||||
public static readonly GUIContent MIN_ROTATION = new GUIContent("Min Rotation", "Minimum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MAX_ROTATION = new GUIContent("Max Rotation", "Maximum rotation of each instance, in degree, on Y axis");
|
||||
public static readonly GUIContent MIN_SCALE = new GUIContent("Min Scale", "Minimum scale of each instance");
|
||||
public static readonly GUIContent MAX_SCALE = new GUIContent("Max Scale", "Maximum scale of each instance");
|
||||
public static readonly GUIContent RAYCAST_LAYER = new GUIContent("Raycast Layer", "Object layer to perform raycast for snapping");
|
||||
public static readonly GUIContent ALIGN_TO_SURFACE = new GUIContent("Align To Surface", "Align the object for terrain surface");
|
||||
public static readonly GUIContent DENSITY = new GUIContent("Density", "Number of object instances per a meter-squared");
|
||||
}
|
||||
|
||||
private void DrawObjectGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GObjectGUI.LABEL, false, GObjectGUI.ID, () =>
|
||||
{
|
||||
DrawObjectSelectorGUI();
|
||||
instance.Density = EditorGUILayout.FloatField(GObjectGUI.DENSITY, instance.Density);
|
||||
instance.MinRotation = EditorGUILayout.FloatField(GObjectGUI.MIN_ROTATION, instance.MinRotation);
|
||||
instance.MaxRotation = EditorGUILayout.FloatField(GObjectGUI.MAX_ROTATION, instance.MaxRotation);
|
||||
instance.MinScale = GEditorCommon.InlineVector3Field(GObjectGUI.MIN_SCALE, instance.MinScale);
|
||||
instance.MaxScale = GEditorCommon.InlineVector3Field(GObjectGUI.MAX_SCALE, instance.MaxScale);
|
||||
instance.WorldRaycastMask = GEditorCommon.LayerMaskField(GObjectGUI.RAYCAST_LAYER, instance.WorldRaycastMask);
|
||||
instance.AlignToSurface = EditorGUILayout.Toggle(GObjectGUI.ALIGN_TO_SURFACE, instance.AlignToSurface);
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawObjectSelectorGUI()
|
||||
{
|
||||
if (instance.Prototypes.Count > 0)
|
||||
{
|
||||
GSelectionGridArgs args = new GSelectionGridArgs();
|
||||
args.collection = instance.Prototypes;
|
||||
args.selectedIndices = instance.PrototypeIndices;
|
||||
args.itemSize = GEditorCommon.selectionGridTileSizeSmall;
|
||||
args.itemPerRow = 4;
|
||||
args.drawPreviewFunction = GEditorCommon.DrawGameObjectPreview;
|
||||
args.contextClickFunction = OnObjectSelectorContextClick;
|
||||
instance.PrototypeIndices = GEditorCommon.MultiSelectionGrid(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("No Game Object found!", GEditorCommon.WordWrapItalicLabel);
|
||||
}
|
||||
GEditorCommon.Separator();
|
||||
|
||||
Rect r1 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight));
|
||||
GameObject prefab = GEditorCommon.ObjectSelectorDragDrop<GameObject>(r1, "Drop a Game Object here!", "t:GameObject");
|
||||
if (prefab != null)
|
||||
{
|
||||
instance.Prototypes.AddIfNotContains(prefab);
|
||||
}
|
||||
|
||||
GEditorCommon.SpacePixel(0);
|
||||
Rect r2 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight));
|
||||
GPrefabPrototypeGroup group = GEditorCommon.ObjectSelectorDragDrop<GPrefabPrototypeGroup>(r2, "Drop a Prefab Prototype Group here!", "t:GPrefabPrototypeGroup");
|
||||
if (group != null)
|
||||
{
|
||||
instance.Prototypes.AddIfNotContains(group.Prototypes);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnObjectSelectorContextClick(Rect r, int index, ICollection collection)
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(
|
||||
new GUIContent("Remove"),
|
||||
false,
|
||||
() =>
|
||||
{
|
||||
instance.Prototypes.RemoveAt(index);
|
||||
});
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-object-spawner-action";
|
||||
|
||||
public static readonly GUIContent SPAWN = new GUIContent("Spawn");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
if (GUILayout.Button(GActionGUI.SPAWN))
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Apply()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Spawning objects...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
isUrp = false;
|
||||
urpContext = default;
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.objectSpawner)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTests = instance.SplineCreator.SweepTest();
|
||||
foreach (GOverlapTestResult st in sweepTests)
|
||||
{
|
||||
if (!st.IsOverlapped)
|
||||
continue;
|
||||
DrawLivePreview(st.Terrain, cam, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
if (t.transform.rotation != Quaternion.identity ||
|
||||
t.transform.lossyScale != Vector3.one)
|
||||
return;
|
||||
|
||||
RenderTexture rt = GetPreviewTexture(t);
|
||||
instance.Internal_Apply(t, rt);
|
||||
GLivePreviewDrawer.DrawMasksLivePreview(
|
||||
t, cam,
|
||||
new Texture[] { rt },
|
||||
new Color[] { GEditorSettings.Instance.splineTools.positiveHighlightColor },
|
||||
chunkCulling);
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<GStylizedTerrain, RenderTexture>();
|
||||
}
|
||||
|
||||
int resolution = instance.MaskResolution;
|
||||
if (!previewTextures.ContainsKey(t) ||
|
||||
previewTextures[t] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
else if (previewTextures[t].width != resolution || previewTextures[t].height != resolution)
|
||||
{
|
||||
previewTextures[t].Release();
|
||||
Object.DestroyImmediate(previewTextures[t]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
|
||||
previewTextures[t].wrapMode = TextureWrapMode.Clamp;
|
||||
return previewTextures[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1222651ce344a240b32a91b9329f727
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,343 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.BackupTool;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GPathPainter))]
|
||||
public class GPathPainterInspector : Editor
|
||||
{
|
||||
private GPathPainter instance;
|
||||
private Dictionary<string, RenderTexture> previewTextures;
|
||||
private bool isUrp;
|
||||
private ScriptableRenderContext urpContext;
|
||||
|
||||
private static readonly string HISTORY_PREFIX_ALBEDO_METALLIC = "Make Path Albedo Metallic";
|
||||
private static readonly string HISTORY_PREFIX_SPLAT = "Make Path Splat";
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
Undo.undoRedoPerformed += OnUndoRedo;
|
||||
instance = (GPathPainter)target;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
GCommon.UpdateMaterials(instance.SplineCreator.GroupId);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (string k in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[k];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUndoRedo()
|
||||
{
|
||||
if (Selection.activeGameObject != instance.gameObject)
|
||||
return;
|
||||
if (string.IsNullOrEmpty(GUndoCompatibleBuffer.Instance.CurrentBackupName))
|
||||
return;
|
||||
GBackup.Restore(GUndoCompatibleBuffer.Instance.CurrentBackupName);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
|
||||
DrawFalloffGUI();
|
||||
DrawPathGUI();
|
||||
DrawActionGUI();
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview over the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.pathPainter = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.pathPainter);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-path-painter-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GPathGUI
|
||||
{
|
||||
public static readonly string LABEL = "Path";
|
||||
public static readonly string ID = "spline-path-painter-path";
|
||||
|
||||
public static readonly GUIContent CHANNEL = new GUIContent("Channel", "The target texture to paint the path on");
|
||||
public static readonly GUIContent COLOR = new GUIContent("Color", "Color of the path");
|
||||
public static readonly GUIContent METALLIC = new GUIContent("Metallic", "Metallic value of the path");
|
||||
public static readonly GUIContent SMOOTHNESS = new GUIContent("Smoothness", "Smoothness value of the path");
|
||||
public static readonly GUIContent AMS_NOTE = new GUIContent("Use a material that utilizes Albedo & Metallic map to see the result!");
|
||||
public static readonly GUIContent SPLAT_PROTOTYPE = new GUIContent("Prototype");
|
||||
public static readonly GUIContent SPLAT_NOTE = new GUIContent("Use a material that utilizes Splat map to see the result!");
|
||||
}
|
||||
|
||||
private void DrawPathGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GPathGUI.LABEL, false, GPathGUI.ID, () =>
|
||||
{
|
||||
instance.Channel = (GPathPainter.PaintChannel)EditorGUILayout.EnumPopup(GPathGUI.CHANNEL, instance.Channel);
|
||||
|
||||
if (instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic)
|
||||
{
|
||||
instance.Color = EditorGUILayout.ColorField(GPathGUI.COLOR, instance.Color);
|
||||
instance.Metallic = EditorGUILayout.Slider(GPathGUI.METALLIC, instance.Metallic, 0f, 1f);
|
||||
instance.Smoothness = EditorGUILayout.Slider(GPathGUI.SMOOTHNESS, instance.Smoothness, 0f, 1f);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField(GPathGUI.AMS_NOTE, GEditorCommon.WordWrapItalicLabel);
|
||||
}
|
||||
else if (instance.Channel == GPathPainter.PaintChannel.Splat)
|
||||
{
|
||||
EditorGUILayout.LabelField(GPathGUI.SPLAT_PROTOTYPE);
|
||||
instance.SplatIndex = GEditorCommon.SplatSetSelectionGrid(instance.SplineCreator.GroupId, instance.SplatIndex);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField(GPathGUI.SPLAT_NOTE, GEditorCommon.WordWrapItalicLabel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-path-painter-action";
|
||||
|
||||
public static readonly GUIContent APPLY_BTN = new GUIContent("Apply");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
if (GUILayout.Button(GActionGUI.APPLY_BTN))
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(instance.SplineCreator.SweepTest());
|
||||
CreateInitialBackup(terrains);
|
||||
ApplyPath();
|
||||
CreateBackupAfterApplyPath(terrains);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void CreateInitialBackup(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
string historyPrefix =
|
||||
instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic ? HISTORY_PREFIX_ALBEDO_METALLIC :
|
||||
instance.Channel == GPathPainter.PaintChannel.Splat ? HISTORY_PREFIX_SPLAT : "Unknown Action";
|
||||
List<GTerrainResourceFlag> resourceFlag = new List<GTerrainResourceFlag>();
|
||||
if (instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic)
|
||||
{
|
||||
resourceFlag.Add(GTerrainResourceFlag.AlbedoMap);
|
||||
resourceFlag.Add(GTerrainResourceFlag.MetallicMap);
|
||||
}
|
||||
else if (instance.Channel == GPathPainter.PaintChannel.Splat)
|
||||
{
|
||||
resourceFlag.Add(GTerrainResourceFlag.SplatControlMaps);
|
||||
}
|
||||
|
||||
GBackupInternal.TryCreateAndMergeInitialBackup(historyPrefix, terrains, resourceFlag, true);
|
||||
}
|
||||
|
||||
private void ApplyPath()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Creating path...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void CreateBackupAfterApplyPath(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
string historyPrefix =
|
||||
instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic ? HISTORY_PREFIX_ALBEDO_METALLIC :
|
||||
instance.Channel == GPathPainter.PaintChannel.Splat ? HISTORY_PREFIX_SPLAT : "Unknown Action";
|
||||
List<GTerrainResourceFlag> resourceFlag = new List<GTerrainResourceFlag>();
|
||||
if (instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic)
|
||||
{
|
||||
resourceFlag.Add(GTerrainResourceFlag.AlbedoMap);
|
||||
resourceFlag.Add(GTerrainResourceFlag.MetallicMap);
|
||||
}
|
||||
else if (instance.Channel == GPathPainter.PaintChannel.Splat)
|
||||
{
|
||||
resourceFlag.Add(GTerrainResourceFlag.SplatControlMaps);
|
||||
}
|
||||
|
||||
GBackupInternal.TryCreateAndMergeBackup(historyPrefix, terrains, resourceFlag, true);
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
isUrp = false;
|
||||
urpContext = default;
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.pathPainter)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTest = instance.SplineCreator.SweepTest();
|
||||
for (int i = 0; i < sweepTest.Count; ++i)
|
||||
{
|
||||
GOverlapTestResult st = sweepTest[i];
|
||||
if (!st.IsOverlapped)
|
||||
continue;
|
||||
DrawLivePreview(st.Terrain, cam, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
if (instance.Channel == GPathPainter.PaintChannel.AlbedoAndMetallic)
|
||||
{
|
||||
SetupAlbedoMetallicPreview(t, cam, chunkCulling);
|
||||
}
|
||||
else if (instance.Channel == GPathPainter.PaintChannel.Splat)
|
||||
{
|
||||
SetupSplatPreview(t, cam, chunkCulling);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupAlbedoMetallicPreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
Material mat = t.TerrainData.Shading.MaterialToRender;
|
||||
if (mat == null)
|
||||
return;
|
||||
int albedoResolution = t.TerrainData.Shading.AlbedoMapResolution;
|
||||
FilterMode albedoFilter = t.TerrainData.Shading.AlbedoMapOrDefault.filterMode;
|
||||
RenderTexture rtAlbedo = GetPreviewTexture(t, "albedo", albedoResolution, albedoFilter);
|
||||
instance.Internal_ApplyAlbedo(t, rtAlbedo);
|
||||
|
||||
int metallicResolution = t.TerrainData.Shading.MetallicMapResolution;
|
||||
FilterMode metallicFilter = t.TerrainData.Shading.MetallicMapOrDefault.filterMode;
|
||||
RenderTexture rtMetallic = GetPreviewTexture(t, "metallic", metallicResolution, metallicFilter);
|
||||
instance.Internal_ApplyMetallic(t, rtMetallic);
|
||||
|
||||
GLivePreviewDrawer.DrawAMSLivePreview(t, cam, rtAlbedo, rtMetallic, chunkCulling);
|
||||
}
|
||||
|
||||
private void SetupSplatPreview(GStylizedTerrain t, Camera cam, bool[] chunkCulling)
|
||||
{
|
||||
Material mat = t.TerrainData.Shading.MaterialToRender;
|
||||
if (mat == null)
|
||||
return;
|
||||
int controlMapResolution = t.TerrainData.Shading.SplatControlResolution;
|
||||
int controlMapCount = t.TerrainData.Shading.SplatControlMapCount;
|
||||
if (controlMapCount == 0)
|
||||
return;
|
||||
RenderTexture[] rtControls = new RenderTexture[controlMapCount];
|
||||
for (int i = 0; i < controlMapCount; ++i)
|
||||
{
|
||||
rtControls[i] = GetPreviewTexture(t, "splatControl" + i, controlMapResolution, t.TerrainData.Shading.GetSplatControlOrDefault(i).filterMode);
|
||||
}
|
||||
instance.Internal_ApplySplat(t, rtControls);
|
||||
GLivePreviewDrawer.DrawSplatLivePreview(t, cam, rtControls, chunkCulling);
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t, string mapName, int resolution, FilterMode filter)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<string, RenderTexture>();
|
||||
}
|
||||
|
||||
string key = string.Format("{0}_{1}", t.GetInstanceID(), mapName);
|
||||
if (!previewTextures.ContainsKey(key) ||
|
||||
previewTextures[key] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
previewTextures[key] = rt;
|
||||
}
|
||||
else if (previewTextures[key].width != resolution || previewTextures[key].height != resolution)
|
||||
{
|
||||
previewTextures[key].Release();
|
||||
Object.DestroyImmediate(previewTextures[key]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
|
||||
rt.wrapMode = TextureWrapMode.Clamp;
|
||||
previewTextures[key] = rt;
|
||||
}
|
||||
|
||||
previewTextures[key].filterMode = filter;
|
||||
return previewTextures[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92073657896cefc45a0745cd4318e7c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,258 @@
|
||||
#if GRIFFIN
|
||||
using Pinwheel.Griffin.BackupTool;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GRampMaker))]
|
||||
public class GRampMakerInspector : Editor
|
||||
{
|
||||
private GRampMaker instance;
|
||||
private Dictionary<GStylizedTerrain, RenderTexture> previewTextures;
|
||||
|
||||
private static readonly string HISTORY_PREFIX = "Make Ramp";
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Undo.undoRedoPerformed += OnUndoRedo;
|
||||
instance = (GRampMaker)target;
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
|
||||
GCommon.RegisterBeginRender(OnCameraRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
GCommon.UnregisterBeginRender(OnCameraRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnCameraRenderSRP);
|
||||
if (previewTextures != null)
|
||||
{
|
||||
foreach (GStylizedTerrain t in previewTextures.Keys)
|
||||
{
|
||||
RenderTexture rt = previewTextures[t];
|
||||
if (rt == null)
|
||||
continue;
|
||||
rt.Release();
|
||||
Object.DestroyImmediate(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUndoRedo()
|
||||
{
|
||||
if (Selection.activeGameObject != instance.gameObject)
|
||||
return;
|
||||
if (string.IsNullOrEmpty(GUndoCompatibleBuffer.Instance.CurrentBackupName))
|
||||
return;
|
||||
GBackup.Restore(GUndoCompatibleBuffer.Instance.CurrentBackupName);
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawBaseGUI();
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
DrawFalloffGUI();
|
||||
DrawRampGUI();
|
||||
DrawActionGUI();
|
||||
|
||||
#if GRIFFIN_VEGETATION_STUDIO_PRO
|
||||
GEditorCommon.DrawVspIntegrationGUI();
|
||||
#endif
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent SPLINE_CREATOR = new GUIContent("Spline Creator", "The Spline Creator component this modifier belongs to");
|
||||
public static readonly GUIContent LIVE_PREVIEW = new GUIContent("Live Preview", "Draw a preview over the terrain");
|
||||
}
|
||||
|
||||
private void DrawBaseGUI()
|
||||
{
|
||||
instance.SplineCreator = EditorGUILayout.ObjectField(GBaseGUI.SPLINE_CREATOR, instance.SplineCreator, typeof(GSplineCreator), true) as GSplineCreator;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GEditorSettings.Instance.splineTools.livePreview.rampMaker = EditorGUILayout.Toggle(GBaseGUI.LIVE_PREVIEW, GEditorSettings.Instance.splineTools.livePreview.rampMaker);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
private class GFalloffGUI
|
||||
{
|
||||
public static readonly string LABEL = "Falloff";
|
||||
public static readonly string ID = "spline-ramp-maker-falloff";
|
||||
|
||||
public static readonly GUIContent FALL_OFF = new GUIContent("Falloff", "Falloff factor of the spline effect, from the center to the edge");
|
||||
public static readonly GUIContent FALL_OFF_NOISE = new GUIContent("Falloff Noise", "Noise map in world space to blend with the falloff curve");
|
||||
public static readonly GUIContent FALL_OFF_NOISE_SIZE = new GUIContent("Falloff Noise Size", "Size of the falloff noise in world space");
|
||||
}
|
||||
|
||||
private void DrawFalloffGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GFalloffGUI.LABEL, false, GFalloffGUI.ID, () =>
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Falloff = EditorGUILayout.CurveField(GFalloffGUI.FALL_OFF, instance.Falloff, Color.red, new Rect(0, 0, 1, 1));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.Internal_UpdateFalloffTexture();
|
||||
}
|
||||
instance.FalloffNoise = EditorGUILayout.ObjectField(GFalloffGUI.FALL_OFF_NOISE, instance.FalloffNoise, typeof(Texture2D), false) as Texture2D;
|
||||
if (instance.FalloffNoise != null)
|
||||
instance.FalloffNoiseSize = GEditorCommon.InlineVector2Field(GFalloffGUI.FALL_OFF_NOISE_SIZE, instance.FalloffNoiseSize);
|
||||
});
|
||||
}
|
||||
|
||||
private class GRampGUI
|
||||
{
|
||||
public static readonly string LABEL = "Ramp";
|
||||
public static readonly string ID = "spline-ramp-maker-ramp";
|
||||
|
||||
public static readonly GUIContent ADDITIONAL_MESH_RESOLUTION = new GUIContent("Additional Mesh Resolution", "Add more mesh detail to the ramp");
|
||||
public static readonly GUIContent HEIGHT_OFFSET = new GUIContent("Height Offset", "Offset the ramp height on Y axis, useful for making rivers");
|
||||
public static readonly GUIContent STEP_COUNT = new GUIContent("Step Count", "Adding a quantize/step effect to the ramp");
|
||||
public static readonly GUIContent RAISE_HEIGHT = new GUIContent("Raise Height", "Toggle height raising");
|
||||
public static readonly GUIContent LOWER_HEIGHT = new GUIContent("Lower Height", "Toggle height lowering");
|
||||
}
|
||||
|
||||
private void DrawRampGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GRampGUI.LABEL, false, GRampGUI.ID, () =>
|
||||
{
|
||||
instance.AdditionalMeshResolution = EditorGUILayout.IntField(GRampGUI.ADDITIONAL_MESH_RESOLUTION, instance.AdditionalMeshResolution);
|
||||
instance.HeightOffset = EditorGUILayout.FloatField(GRampGUI.HEIGHT_OFFSET, instance.HeightOffset);
|
||||
instance.StepCount = EditorGUILayout.IntField(GRampGUI.STEP_COUNT, instance.StepCount);
|
||||
instance.RaiseHeight = EditorGUILayout.Toggle(GRampGUI.RAISE_HEIGHT, instance.RaiseHeight);
|
||||
instance.LowerHeight = EditorGUILayout.Toggle(GRampGUI.LOWER_HEIGHT, instance.LowerHeight);
|
||||
});
|
||||
}
|
||||
|
||||
private class GActionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Action";
|
||||
public static readonly string ID = "spline-ramp-maker-action";
|
||||
|
||||
public static readonly GUIContent APPLY_BTN = new GUIContent("Apply");
|
||||
}
|
||||
|
||||
private void DrawActionGUI()
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GActionGUI.ID);
|
||||
GEditorCommon.Foldout(GActionGUI.LABEL, true, GActionGUI.ID, () =>
|
||||
{
|
||||
if (GUILayout.Button(GActionGUI.APPLY_BTN))
|
||||
{
|
||||
List<GStylizedTerrain> terrains = GUtilities.ExtractTerrainsFromOverlapTest(instance.SplineCreator.SweepTest());
|
||||
|
||||
CreateInitialBackup(terrains);
|
||||
ApplyRamp();
|
||||
CreateBackupAfterApplyRamp(terrains);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateInitialBackup(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeInitialBackup(HISTORY_PREFIX, terrains, GCommon.HeightMapAndFoliageResourceFlags, true);
|
||||
}
|
||||
|
||||
private void ApplyRamp()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Applying", "Creating ramp...", 1f);
|
||||
try
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
private void CreateBackupAfterApplyRamp(List<GStylizedTerrain> terrains)
|
||||
{
|
||||
GBackupInternal.TryCreateAndMergeBackup(HISTORY_PREFIX, terrains, GCommon.HeightMapAndFoliageResourceFlags, true);
|
||||
}
|
||||
|
||||
private void OnCameraRender(Camera cam)
|
||||
{
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (GEditorSettings.Instance.splineTools.livePreview.rampMaker)
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
|
||||
private void OnCameraRenderSRP(ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnCameraRender(cam);
|
||||
}
|
||||
|
||||
private IEnumerator IDrawLivePreviewEndOfFrameURP(Camera cam)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (cam != null)
|
||||
{
|
||||
DrawLivePreview(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLivePreview(Camera cam)
|
||||
{
|
||||
List<GOverlapTestResult> sweepTests = instance.SplineCreator.SweepTest();
|
||||
for (int i = 0; i < sweepTests.Count; ++i)
|
||||
{
|
||||
GOverlapTestResult st = sweepTests[i];
|
||||
if (st.IsOverlapped)
|
||||
{
|
||||
GStylizedTerrain t = st.Terrain;
|
||||
if (t.transform.rotation != Quaternion.identity ||
|
||||
t.transform.lossyScale != Vector3.one)
|
||||
return;
|
||||
|
||||
RenderTexture rt = GetPreviewTexture(t);
|
||||
instance.Internal_DrawOnTexture(t, rt);
|
||||
|
||||
GLivePreviewDrawer.DrawGeometryLivePreview(t, cam, rt, st.IsChunkOverlapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RenderTexture GetPreviewTexture(GStylizedTerrain t)
|
||||
{
|
||||
if (previewTextures == null)
|
||||
{
|
||||
previewTextures = new Dictionary<GStylizedTerrain, RenderTexture>();
|
||||
}
|
||||
|
||||
int resolution = t.TerrainData.Geometry.HeightMapResolution;
|
||||
if (!previewTextures.ContainsKey(t) ||
|
||||
previewTextures[t] == null)
|
||||
{
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, GGeometry.HeightMapRTFormat, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
else if (previewTextures[t].width != resolution ||
|
||||
previewTextures[t].height != resolution ||
|
||||
previewTextures[t].format != GGeometry.HeightMapRTFormat)
|
||||
{
|
||||
previewTextures[t].Release();
|
||||
Object.DestroyImmediate(previewTextures[t]);
|
||||
RenderTexture rt = new RenderTexture(resolution, resolution, 0, GGeometry.HeightMapRTFormat, RenderTextureReadWrite.Linear);
|
||||
previewTextures[t] = rt;
|
||||
}
|
||||
|
||||
previewTextures[t].wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
return previewTextures[t];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f3daaf52765c3c48a47d9027af674b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,31 @@
|
||||
#if GRIFFIN
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
public class GSplineAnchorInspectorDrawer
|
||||
{
|
||||
private GSplineAnchor instance;
|
||||
|
||||
public GSplineAnchorInspectorDrawer(GSplineAnchor anchor)
|
||||
{
|
||||
instance = anchor;
|
||||
}
|
||||
|
||||
public static GSplineAnchorInspectorDrawer Create(GSplineAnchor anchor)
|
||||
{
|
||||
return new GSplineAnchorInspectorDrawer(anchor);
|
||||
}
|
||||
|
||||
public void DrawGUI()
|
||||
{
|
||||
EditorGUIUtility.wideMode = true;
|
||||
instance.Position = EditorGUILayout.Vector3Field("Position", instance.Position);
|
||||
instance.Rotation = Quaternion.Euler(EditorGUILayout.Vector3Field("Rotation", instance.Rotation.eulerAngles));
|
||||
instance.Scale = EditorGUILayout.Vector3Field("Scale", instance.Scale);
|
||||
EditorGUIUtility.wideMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4d774cb54e8dcc40b5416112355a269
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,457 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Type = System.Type;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GSplineCreator))]
|
||||
public class GSplineCreatorInspector : Editor
|
||||
{
|
||||
private List<Type> modifierTypes;
|
||||
public List<Type> ModifierTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (modifierTypes == null)
|
||||
{
|
||||
modifierTypes = new List<Type>();
|
||||
}
|
||||
return modifierTypes;
|
||||
}
|
||||
set
|
||||
{
|
||||
modifierTypes = value;
|
||||
}
|
||||
}
|
||||
|
||||
private GSplineCreator instance;
|
||||
private int selectedAnchorIndex = -1;
|
||||
private int selectedSegmentIndex = -1;
|
||||
|
||||
private Rect addModifierButtonRect;
|
||||
|
||||
private GSplineEditingGUIDrawer splineEditingDrawer;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
instance = (GSplineCreator)target;
|
||||
InitModifierClasses();
|
||||
Tools.hidden = true;
|
||||
splineEditingDrawer = new GSplineEditingGUIDrawer(instance);
|
||||
|
||||
GCommon.RegisterBeginRender(OnBeginRender);
|
||||
GCommon.RegisterBeginRenderSRP(OnBeginRenderSRP);
|
||||
SceneView.duringSceneGui += DuringSceneGUI;
|
||||
|
||||
GLayerInitializer.SetupSplineLayer();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Tools.hidden = false;
|
||||
|
||||
GCommon.UnregisterBeginRender(OnBeginRender);
|
||||
GCommon.UnregisterBeginRenderSRP(OnBeginRenderSRP);
|
||||
SceneView.duringSceneGui -= DuringSceneGUI;
|
||||
}
|
||||
|
||||
private void InitModifierClasses()
|
||||
{
|
||||
List<Type> loadedTypes = GCommon.GetAllLoadedTypes();
|
||||
ModifierTypes = loadedTypes.FindAll(
|
||||
t => t.IsSubclassOf(typeof(GSplineModifier)));
|
||||
}
|
||||
|
||||
public override bool RequiresConstantRepaint()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private class GBaseGUI
|
||||
{
|
||||
public static readonly GUIContent GROUP_ID = new GUIContent("Group Id", "Id of the terrain group this tool will work on");
|
||||
public static readonly GUIContent RAYCAST_LAYER = new GUIContent("Raycast Layers", "Game object layers to perform raycast when adding anchors");
|
||||
public static readonly GUIContent RAYCAST_LAYER_WARNING = new GUIContent("You can't edit the spline with Raycast Layers set to Nothing");
|
||||
public static readonly GUIContent AUTO_TANGENT = new GUIContent("Auto Tangent", "Smooth the spline automatically");
|
||||
public static readonly GUIContent ENABLE_TERRAIN_MASK = new GUIContent("Enable Terrain Mask", "Use the terrain Mask texture (R) to lock particular regions from being edited");
|
||||
public static readonly GUIContent ENABLE_TOPOGRAPHIC = new GUIContent("Enable Topographic", "Draw an overlay topographic view for better sense of elevation");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUI.enabled = !GEditorSettings.Instance.splineTools.showTransformGizmos;
|
||||
instance.GroupId = GEditorCommon.ActiveTerrainGroupPopupWithAllOption(GBaseGUI.GROUP_ID, instance.GroupId);
|
||||
|
||||
GEditorSettings.Instance.splineTools.raycastLayers = GEditorCommon.LayerMaskField(GBaseGUI.RAYCAST_LAYER, GEditorSettings.Instance.splineTools.raycastLayers);
|
||||
if (GEditorSettings.Instance.splineTools.raycastLayers == 0)
|
||||
{
|
||||
EditorGUILayout.LabelField(GBaseGUI.RAYCAST_LAYER_WARNING, GEditorCommon.WarningLabel);
|
||||
}
|
||||
|
||||
GEditorSettings.Instance.splineTools.autoTangent = EditorGUILayout.Toggle(GBaseGUI.AUTO_TANGENT, GEditorSettings.Instance.splineTools.autoTangent);
|
||||
|
||||
instance.EnableTerrainMask = EditorGUILayout.Toggle(GBaseGUI.ENABLE_TERRAIN_MASK, instance.EnableTerrainMask);
|
||||
GEditorSettings.Instance.topographic.enable = EditorGUILayout.Toggle(GBaseGUI.ENABLE_TOPOGRAPHIC, GEditorSettings.Instance.topographic.enable);
|
||||
|
||||
DrawInstructionGUI();
|
||||
GUI.enabled = true;
|
||||
DrawTransformGUI();
|
||||
GUI.enabled = !GEditorSettings.Instance.splineTools.showTransformGizmos;
|
||||
DrawAnchorDefaultValueGUI();
|
||||
DrawSelectedAnchorGUI();
|
||||
DrawSegmentDefaultValueGUI();
|
||||
DrawSelectedSegmentGUI();
|
||||
//DrawGizmosGUI();
|
||||
DrawModifiersGUI();
|
||||
GEditorCommon.DrawBackupHelpBox();
|
||||
//DrawDebugGUI();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
GSplineCreator.MarkSplineChanged(instance);
|
||||
GUtilities.MarkCurrentSceneDirty();
|
||||
}
|
||||
GEditorCommon.DrawCommonLinks();
|
||||
}
|
||||
|
||||
private class GInstructionGUI
|
||||
{
|
||||
public static readonly string LABEL = "Instruction";
|
||||
public static readonly string ID = "spline-creator-instruction";
|
||||
|
||||
public static readonly GUIContent INSTRUCTION = new GUIContent(
|
||||
string.Format(
|
||||
"Create a edit bezier spline.\n" +
|
||||
" - Left Click to select element.\n" +
|
||||
" - Ctrl & Left Click to delete element.\n" +
|
||||
" - Shift & Left Click to add element.\n" +
|
||||
"Use Add Modifier to do specific tasks with spline data."));
|
||||
}
|
||||
|
||||
private void DrawInstructionGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GInstructionGUI.LABEL, true, GInstructionGUI.ID, () =>
|
||||
{
|
||||
EditorGUILayout.LabelField(GInstructionGUI.INSTRUCTION, GEditorCommon.WordWrapItalicLabel);
|
||||
});
|
||||
}
|
||||
|
||||
private class GTransformGUI
|
||||
{
|
||||
public static readonly string LABEL = "Transform";
|
||||
public static readonly string ID = "spline-creator-transform";
|
||||
|
||||
public static readonly GUIContent SET_PIVOT_TO_MEDIAN_POINT = new GUIContent("Set Pivot To Median Point");
|
||||
public static readonly GUIContent POSITION = new GUIContent("Position", "Position of the Spline object");
|
||||
public static readonly GUIContent ROTATION = new GUIContent("Rotation", "Rotation of the Spline object");
|
||||
public static readonly GUIContent SCALE = new GUIContent("Scale", "Scale of the Spline object");
|
||||
public static readonly GUIContent SHOW_TRANSFORM_GIZMOS = new GUIContent("Show Transform Gizmos", "Show gizmos to move, rotate and scale the Spline object");
|
||||
}
|
||||
|
||||
private void DrawTransformGUI()
|
||||
{
|
||||
if (GEditorSettings.Instance.splineTools.showTransformGizmos)
|
||||
{
|
||||
GEditorCommon.ExpandFoldout(GTransformGUI.ID);
|
||||
}
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(
|
||||
GTransformGUI.SET_PIVOT_TO_MEDIAN_POINT,
|
||||
false,
|
||||
() => { SetPivotToMedianPoint(); });
|
||||
|
||||
GEditorCommon.Foldout(GTransformGUI.LABEL, true, GTransformGUI.ID, () =>
|
||||
{
|
||||
instance.transform.localPosition = GEditorCommon.InlineVector3Field(GTransformGUI.POSITION, instance.transform.localPosition);
|
||||
instance.transform.localRotation = GEditorCommon.InlineEulerRotationField(GTransformGUI.ROTATION, instance.transform.localRotation);
|
||||
instance.transform.localScale = GEditorCommon.InlineVector3Field(GTransformGUI.SCALE, instance.transform.localScale);
|
||||
|
||||
GEditorSettings.Instance.splineTools.showTransformGizmos = EditorGUILayout.Toggle(GTransformGUI.SHOW_TRANSFORM_GIZMOS, GEditorSettings.Instance.splineTools.showTransformGizmos);
|
||||
},
|
||||
menu);
|
||||
}
|
||||
|
||||
private class GAnchorDefaultGUI
|
||||
{
|
||||
public static readonly string LABEL = "Anchor Defaults";
|
||||
public static readonly string ID = "spline-creator-anchor-defaults";
|
||||
|
||||
public static readonly GUIContent POSITION_OFFSET = new GUIContent("Position Offset", "Offset from the raycast point of the anchor");
|
||||
public static readonly GUIContent INITIAL_ROTATION = new GUIContent("Rotation", "Initial rotation of the new anchor");
|
||||
public static readonly GUIContent INITIAL_SCALE = new GUIContent("Scale", "Initial scale of the new anchor");
|
||||
}
|
||||
|
||||
private void DrawAnchorDefaultValueGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GAnchorDefaultGUI.LABEL, true, GAnchorDefaultGUI.ID, () =>
|
||||
{
|
||||
instance.PositionOffset = GEditorCommon.InlineVector3Field(GAnchorDefaultGUI.POSITION_OFFSET, instance.PositionOffset);
|
||||
instance.InitialRotation = GEditorCommon.InlineEulerRotationField(GAnchorDefaultGUI.INITIAL_ROTATION, instance.InitialRotation);
|
||||
instance.InitialScale = GEditorCommon.InlineVector3Field(GAnchorDefaultGUI.INITIAL_SCALE, instance.InitialScale);
|
||||
});
|
||||
}
|
||||
|
||||
private class GSelectedAnchorGUI
|
||||
{
|
||||
public static readonly string LABEL = "Selected Anchor";
|
||||
public static readonly string ID = "spline-creator-selected-anchor";
|
||||
|
||||
public static readonly GUIContent POSITION = new GUIContent("Position", "Position of the anchor");
|
||||
public static readonly GUIContent ROTATION = new GUIContent("Rotation", "Rotation of the anchor, you can't change this if Auto Tangent is enable");
|
||||
public static readonly GUIContent SCALE = new GUIContent("Scale", "Scale of the anchor");
|
||||
|
||||
public static readonly GUIContent NO_ANCHOR_SELECTED = new GUIContent("No Anchor selected!");
|
||||
}
|
||||
|
||||
private void DrawSelectedAnchorGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GSelectedAnchorGUI.LABEL, true, GSelectedAnchorGUI.ID, () =>
|
||||
{
|
||||
selectedAnchorIndex = splineEditingDrawer.selectedAnchorIndex;
|
||||
if (selectedAnchorIndex >= 0 && selectedAnchorIndex < instance.Spline.Anchors.Count)
|
||||
{
|
||||
GSplineAnchor a = instance.Spline.Anchors[selectedAnchorIndex];
|
||||
EditorGUI.BeginChangeCheck();
|
||||
a.Position = GEditorCommon.InlineVector3Field(GSelectedAnchorGUI.POSITION, a.Position);
|
||||
GUI.enabled = !GEditorSettings.Instance.splineTools.autoTangent;
|
||||
a.Rotation = GEditorCommon.InlineEulerRotationField(GSelectedAnchorGUI.ROTATION, a.Rotation);
|
||||
GUI.enabled = true;
|
||||
a.Scale = GEditorCommon.InlineVector3Field(GSelectedAnchorGUI.SCALE, a.Scale);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (GEditorSettings.Instance.splineTools.autoTangent)
|
||||
{
|
||||
instance.Spline.SmoothTangents(selectedAnchorIndex);
|
||||
}
|
||||
List<int> segmentIndices = instance.Spline.FindSegments(selectedAnchorIndex);
|
||||
instance.UpdateMesh(segmentIndices);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField(GSelectedAnchorGUI.NO_ANCHOR_SELECTED, GEditorCommon.ItalicLabel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class GSegmentDefaultGUI
|
||||
{
|
||||
public static readonly string LABEL = "Segment Defaults";
|
||||
public static readonly string ID = "spline-creator-segment-defaults";
|
||||
|
||||
public static readonly GUIContent SMOOTHNESS = new GUIContent("Smoothness", "Spline subdivision factor, larger number produces smoother spline");
|
||||
public static readonly GUIContent WIDTH = new GUIContent("Width", "Width of the inner part of the spline body where there is no falloff");
|
||||
public static readonly GUIContent FALLOFF_WIDTH = new GUIContent("Falloff Width", "Width of the outer part of the spline body where falloff takes into account");
|
||||
}
|
||||
|
||||
private void DrawSegmentDefaultValueGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GSegmentDefaultGUI.LABEL, true, GSegmentDefaultGUI.ID, () =>
|
||||
{
|
||||
EditorGUIUtility.wideMode = true;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
instance.Smoothness = EditorGUILayout.IntField(GSegmentDefaultGUI.SMOOTHNESS, instance.Smoothness);
|
||||
instance.Width = EditorGUILayout.FloatField(GSegmentDefaultGUI.WIDTH, instance.Width);
|
||||
instance.FalloffWidth = EditorGUILayout.FloatField(GSegmentDefaultGUI.FALLOFF_WIDTH, instance.FalloffWidth);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
instance.UpdateMeshes();
|
||||
}
|
||||
EditorGUIUtility.wideMode = false;
|
||||
});
|
||||
}
|
||||
|
||||
private class GSelectedSegmentGUI
|
||||
{
|
||||
public static readonly string LABEL = "Selected Segment";
|
||||
public static readonly string ID = "spline-creator-selected-segment";
|
||||
|
||||
public static readonly GUIContent START_TANGENT = new GUIContent("Start Tangent", "Position of the first tangent, you can't change this if Auto Tangent is on");
|
||||
public static readonly GUIContent END_TANGENT = new GUIContent("End Tangent", "Position of the second tangent, you can't change this if Auto Tangent is on");
|
||||
public static readonly GUIContent FLIP_DIRECTION_BTN = new GUIContent("Flip Direction", "Flip the direction of this segment");
|
||||
public static readonly GUIContent NO_SEGMENT_SELECTED = new GUIContent("No Segment selected!");
|
||||
}
|
||||
|
||||
private void DrawSelectedSegmentGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GSelectedSegmentGUI.LABEL, true, GSelectedSegmentGUI.ID, () =>
|
||||
{
|
||||
selectedSegmentIndex = splineEditingDrawer.selectedSegmentIndex;
|
||||
if (selectedSegmentIndex >= 0 && selectedSegmentIndex < instance.Spline.Segments.Count)
|
||||
{
|
||||
GSplineSegment s = instance.Spline.Segments[selectedSegmentIndex];
|
||||
GUI.enabled = !GEditorSettings.Instance.splineTools.autoTangent;
|
||||
s.StartTangent = GEditorCommon.InlineVector3Field(GSelectedSegmentGUI.START_TANGENT, s.StartTangent);
|
||||
s.EndTangent = GEditorCommon.InlineVector3Field(GSelectedSegmentGUI.END_TANGENT, s.EndTangent);
|
||||
GUI.enabled = true;
|
||||
if (GUILayout.Button(GSelectedSegmentGUI.FLIP_DIRECTION_BTN))
|
||||
{
|
||||
s.FlipDirection();
|
||||
if (GEditorSettings.Instance.splineTools.autoTangent)
|
||||
{
|
||||
instance.Spline.SmoothTangents(s.StartIndex, s.EndIndex);
|
||||
}
|
||||
instance.UpdateMesh(selectedSegmentIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField(GSelectedSegmentGUI.NO_SEGMENT_SELECTED, GEditorCommon.ItalicLabel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class GGizmosGUI
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void DrawGizmosGUI()
|
||||
{
|
||||
string label = "Gizmos";
|
||||
string id = "gizmos" + instance.GetInstanceID().ToString();
|
||||
GEditorCommon.Foldout(label, true, id, () =>
|
||||
{
|
||||
|
||||
EditorUtility.SetDirty(GEditorSettings.Instance);
|
||||
});
|
||||
}
|
||||
|
||||
private class GModifiersGUI
|
||||
{
|
||||
public static readonly string LABEL = "Modifiers";
|
||||
public static readonly string ID = "spline-creator-modifiers";
|
||||
|
||||
public static readonly GUIContent ADD_MODIFIER_BTN = new GUIContent("Add Modifier");
|
||||
}
|
||||
|
||||
private void DrawModifiersGUI()
|
||||
{
|
||||
GEditorCommon.Foldout(GModifiersGUI.LABEL, true, GModifiersGUI.ID, () =>
|
||||
{
|
||||
if (ModifierTypes.Count == 0)
|
||||
return;
|
||||
Rect r = EditorGUILayout.GetControlRect();
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
addModifierButtonRect = r;
|
||||
if (GUI.Button(r, GModifiersGUI.ADD_MODIFIER_BTN))
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
for (int i = 0; i < ModifierTypes.Count; ++i)
|
||||
{
|
||||
Type t = ModifierTypes[i];
|
||||
string menuLabel = string.Empty;
|
||||
object[] alternativeClassNameAttributes = t.GetCustomAttributes(typeof(GDisplayName), false);
|
||||
if (alternativeClassNameAttributes != null && alternativeClassNameAttributes.Length > 0)
|
||||
{
|
||||
GDisplayName att = alternativeClassNameAttributes[0] as GDisplayName;
|
||||
if (att.DisplayName == null ||
|
||||
att.DisplayName.Equals(string.Empty))
|
||||
menuLabel = t.Name;
|
||||
else
|
||||
menuLabel = att.DisplayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
menuLabel = t.Name;
|
||||
}
|
||||
|
||||
menu.AddItem(
|
||||
new GUIContent(ObjectNames.NicifyVariableName(menuLabel)),
|
||||
false,
|
||||
() =>
|
||||
{
|
||||
AddModifier(t);
|
||||
});
|
||||
}
|
||||
menu.DropDown(addModifierButtonRect);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void AddModifier(Type t)
|
||||
{
|
||||
GSplineModifier modifier = instance.gameObject.AddComponent(t) as GSplineModifier;
|
||||
modifier.SplineCreator = instance;
|
||||
}
|
||||
|
||||
private void DuringSceneGUI(SceneView sv)
|
||||
{
|
||||
if (!instance.isActiveAndEnabled)
|
||||
return;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
splineEditingDrawer.Draw();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
GSplineCreator.MarkSplineChanged(instance);
|
||||
GUtilities.MarkCurrentSceneDirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPivotToMedianPoint()
|
||||
{
|
||||
Vector3 localMedian = Vector3.zero;
|
||||
List<GSplineAnchor> anchors = instance.Spline.Anchors;
|
||||
if (anchors.Count == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < anchors.Count; ++i)
|
||||
{
|
||||
localMedian += anchors[i].Position;
|
||||
}
|
||||
localMedian = localMedian / anchors.Count;
|
||||
|
||||
Vector3 worldMedian = instance.transform.TransformPoint(localMedian);
|
||||
Matrix4x4 medianToLocal = Matrix4x4.TRS(
|
||||
worldMedian,
|
||||
instance.transform.rotation,
|
||||
instance.transform.lossyScale).inverse;
|
||||
Matrix4x4 localToWorld = instance.transform.localToWorldMatrix;
|
||||
Matrix4x4 transformationMatrix = medianToLocal * localToWorld;
|
||||
|
||||
for (int i = 0; i < anchors.Count; ++i)
|
||||
{
|
||||
anchors[i].Position = transformationMatrix.MultiplyPoint(anchors[i].Position);
|
||||
}
|
||||
|
||||
List<GSplineSegment> segments = instance.Spline.Segments;
|
||||
for (int i = 0; i < segments.Count; ++i)
|
||||
{
|
||||
segments[i].StartTangent = transformationMatrix.MultiplyPoint(segments[i].StartTangent);
|
||||
segments[i].EndTangent = transformationMatrix.MultiplyPoint(segments[i].EndTangent);
|
||||
}
|
||||
|
||||
instance.transform.position = worldMedian;
|
||||
instance.UpdateMeshes();
|
||||
GSplineCreator.MarkSplineChanged(instance);
|
||||
}
|
||||
|
||||
private void OnBeginRender(Camera cam)
|
||||
{
|
||||
if (cam.cameraType != CameraType.SceneView)
|
||||
return;
|
||||
if (instance.EnableTerrainMask)
|
||||
{
|
||||
DrawTerrainMask(cam);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeginRenderSRP(ScriptableRenderContext context, Camera cam)
|
||||
{
|
||||
OnBeginRender(cam);
|
||||
}
|
||||
|
||||
private void DrawTerrainMask(Camera cam)
|
||||
{
|
||||
GCommon.ForEachTerrain(instance.GroupId, (t) =>
|
||||
{
|
||||
GLivePreviewDrawer.DrawTerrainMask(t, cam);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5183558bccf9aa644b43209408435eb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,376 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.EditorTools;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
public class GSplineEditingGUIDrawer
|
||||
{
|
||||
private const float BEZIER_SELECT_DISTANCE = 10;
|
||||
private const float BEZIER_WIDTH = 5;
|
||||
|
||||
public int selectedAnchorIndex = -1;
|
||||
public int selectedSegmentIndex = -1;
|
||||
|
||||
private GSplineCreator splineCreator;
|
||||
bool autoTangent;
|
||||
bool showTransformGizmos;
|
||||
|
||||
public GSplineEditingGUIDrawer(GSplineCreator splineCreator)
|
||||
{
|
||||
this.splineCreator = splineCreator;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (Event.current == null)
|
||||
return;
|
||||
|
||||
autoTangent = GEditorSettings.Instance.splineTools.autoTangent;
|
||||
showTransformGizmos = GEditorSettings.Instance.splineTools.showTransformGizmos;
|
||||
//DrawDebug();
|
||||
|
||||
HandleSelectTransformRemoveAnchor();
|
||||
HandleSelectTransformRemoveSegment();
|
||||
HandleAddAnchor();
|
||||
//DrawMesh();
|
||||
|
||||
DrawPivot();
|
||||
//DrawInstruction();
|
||||
CatchHotControl();
|
||||
}
|
||||
|
||||
private void HandleSelectTransformRemoveAnchor()
|
||||
{
|
||||
List<GSplineAnchor> anchors = splineCreator.Spline.Anchors;
|
||||
|
||||
for (int i = 0; i < anchors.Count; ++i)
|
||||
{
|
||||
GSplineAnchor a = anchors[i];
|
||||
Vector3 localPos = a.Position;
|
||||
Vector3 worldPos = splineCreator.transform.TransformPoint(localPos);
|
||||
float handleSize = HandleUtility.GetHandleSize(worldPos) * 0.2f;
|
||||
if (i == selectedAnchorIndex && !showTransformGizmos)
|
||||
{
|
||||
Handles.color = GEditorSettings.Instance.splineTools.selectedElementColor;
|
||||
Handles.SphereHandleCap(0, worldPos, Quaternion.identity, handleSize, EventType.Repaint);
|
||||
bool isGlobalRotation = Tools.pivotRotation == PivotRotation.Global;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
if (Tools.current == Tool.Move)
|
||||
{
|
||||
worldPos = Handles.PositionHandle(worldPos, isGlobalRotation ? Quaternion.identity : a.Rotation);
|
||||
localPos = splineCreator.transform.InverseTransformPoint(worldPos);
|
||||
a.Position = localPos;
|
||||
}
|
||||
else if (Tools.current == Tool.Rotate && !GEditorSettings.Instance.splineTools.autoTangent)
|
||||
{
|
||||
a.Rotation = Handles.RotationHandle(a.Rotation, worldPos);
|
||||
}
|
||||
else if (Tools.current == Tool.Scale)
|
||||
{
|
||||
a.Scale = Handles.ScaleHandle(a.Scale, worldPos, isGlobalRotation ? Quaternion.identity : a.Rotation, HandleUtility.GetHandleSize(worldPos));
|
||||
}
|
||||
anchors[i] = a;
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (autoTangent)
|
||||
{
|
||||
splineCreator.Spline.SmoothTangents(selectedAnchorIndex);
|
||||
GUI.changed = true;
|
||||
}
|
||||
List<int> segmentIndices = splineCreator.Spline.FindSegments(selectedAnchorIndex);
|
||||
splineCreator.UpdateMesh(segmentIndices);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Handles.color = GEditorSettings.Instance.splineTools.anchorColor;
|
||||
if (Handles.Button(worldPos, Quaternion.identity, handleSize, handleSize * 0.5f, Handles.SphereHandleCap) && !showTransformGizmos)
|
||||
{
|
||||
if (!Event.current.alt)
|
||||
{
|
||||
if (Event.current.control)
|
||||
{
|
||||
selectedAnchorIndex = -1;
|
||||
selectedSegmentIndex = -1;
|
||||
splineCreator.Spline.RemoveAnchor(i);
|
||||
GUI.changed = true;
|
||||
}
|
||||
else if (Event.current.shift)
|
||||
{
|
||||
if (selectedAnchorIndex != i &&
|
||||
selectedAnchorIndex >= 0 &&
|
||||
selectedAnchorIndex < anchors.Count)
|
||||
{
|
||||
splineCreator.Spline.AddSegment(selectedAnchorIndex, i);
|
||||
if (autoTangent)
|
||||
{
|
||||
int[] segmentsIndices = splineCreator.Spline.SmoothTangents(selectedAnchorIndex, i);
|
||||
splineCreator.UpdateMesh(segmentsIndices);
|
||||
}
|
||||
else
|
||||
{
|
||||
splineCreator.UpdateMesh(splineCreator.Spline.Segments.Count - 1);
|
||||
}
|
||||
selectedAnchorIndex = i;
|
||||
selectedSegmentIndex = -1;
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedAnchorIndex = i;
|
||||
selectedSegmentIndex = -1;
|
||||
GUI.changed = true;
|
||||
}
|
||||
Event.current.Use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAddAnchor()
|
||||
{
|
||||
if (showTransformGizmos)
|
||||
return;
|
||||
bool isLeftMouseUp = Event.current.type == EventType.MouseUp && Event.current.button == 0;
|
||||
bool isShift = Event.current.shift;
|
||||
bool isAlt = Event.current.alt;
|
||||
if (!isLeftMouseUp)
|
||||
return;
|
||||
LayerMask raycastLayers = GEditorSettings.Instance.splineTools.raycastLayers;
|
||||
Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
|
||||
RaycastHit hit;
|
||||
if (Physics.Raycast(r, out hit, 10000, raycastLayers))
|
||||
{
|
||||
if (isAlt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!isShift)
|
||||
{
|
||||
selectedAnchorIndex = -1;
|
||||
return;
|
||||
}
|
||||
Vector3 offset = splineCreator.PositionOffset;
|
||||
Vector3 worldPos = hit.point + offset;
|
||||
Vector3 localPos = splineCreator.transform.InverseTransformPoint(worldPos);
|
||||
GSplineAnchor a = new GSplineAnchor(localPos);
|
||||
splineCreator.Spline.AddAnchor(a);
|
||||
|
||||
if (selectedAnchorIndex >= 0 && selectedAnchorIndex < splineCreator.Spline.Anchors.Count - 1)
|
||||
{
|
||||
splineCreator.Spline.AddSegment(selectedAnchorIndex, splineCreator.Spline.Anchors.Count - 1);
|
||||
if (autoTangent)
|
||||
{
|
||||
int[] segmentIndices = splineCreator.Spline.SmoothTangents(selectedAnchorIndex, splineCreator.Spline.Anchors.Count - 1);
|
||||
splineCreator.UpdateMesh(segmentIndices);
|
||||
}
|
||||
else
|
||||
{
|
||||
splineCreator.UpdateMesh(splineCreator.Spline.Segments.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
selectedAnchorIndex = splineCreator.Spline.Anchors.Count - 1;
|
||||
GUI.changed = true;
|
||||
Event.current.Use();
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedAnchorIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSelectTransformRemoveSegment()
|
||||
{
|
||||
List<GSplineSegment> segments = splineCreator.Spline.Segments;
|
||||
List<GSplineAnchor> anchors = splineCreator.Spline.Anchors;
|
||||
for (int i = 0; i < segments.Count; ++i)
|
||||
{
|
||||
if (!splineCreator.Spline.IsSegmentValid(i))
|
||||
continue;
|
||||
if (i == selectedSegmentIndex && !autoTangent && !showTransformGizmos)
|
||||
HandleSelectedSegmentModifications();
|
||||
int i0 = segments[i].StartIndex;
|
||||
int i1 = segments[i].EndIndex;
|
||||
GSplineAnchor a0 = anchors[i0];
|
||||
GSplineAnchor a1 = anchors[i1];
|
||||
Vector3 startPosition = splineCreator.transform.TransformPoint(a0.Position);
|
||||
Vector3 endPosition = splineCreator.transform.TransformPoint(a1.Position);
|
||||
Vector3 startTangent = splineCreator.transform.TransformPoint(segments[i].StartTangent);
|
||||
Vector3 endTangent = splineCreator.transform.TransformPoint(segments[i].EndTangent);
|
||||
Color color = (i == selectedSegmentIndex) ?
|
||||
GEditorSettings.Instance.splineTools.selectedElementColor :
|
||||
GEditorSettings.Instance.splineTools.segmentColor; ;
|
||||
Color colorFade = new Color(color.r, color.g, color.b, color.a * 0.3f);
|
||||
|
||||
Vector3[] bezierPoints = Handles.MakeBezierPoints(startPosition, endPosition, startTangent, endTangent, Mathf.Min(5, splineCreator.Smoothness));
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.LessEqual;
|
||||
Handles.color = color;
|
||||
Handles.DrawPolyLine(bezierPoints);
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Greater;
|
||||
Handles.color = colorFade;
|
||||
Handles.DrawPolyLine(bezierPoints);
|
||||
|
||||
if (Event.current.type == EventType.MouseUp && Event.current.button == 0 && !showTransformGizmos && !Event.current.alt)
|
||||
{
|
||||
float d0 = DistanceMouseToSpline(Event.current.mousePosition, bezierPoints);
|
||||
float d1 = DistanceMouseToPoint(Event.current.mousePosition, bezierPoints[0]);
|
||||
float d2 = DistanceMouseToPoint(Event.current.mousePosition, bezierPoints[bezierPoints.Length - 1]);
|
||||
if (d0 <= BEZIER_SELECT_DISTANCE &&
|
||||
d1 > BEZIER_SELECT_DISTANCE &&
|
||||
d2 > BEZIER_SELECT_DISTANCE)
|
||||
{
|
||||
selectedSegmentIndex = i;
|
||||
if (Event.current.control)
|
||||
{
|
||||
splineCreator.Spline.RemoveSegment(selectedSegmentIndex);
|
||||
selectedSegmentIndex = -1;
|
||||
GUI.changed = true;
|
||||
}
|
||||
//don't Use() the event here
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedSegmentIndex == i)
|
||||
{
|
||||
selectedSegmentIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
|
||||
}
|
||||
|
||||
private void HandleSelectedSegmentModifications()
|
||||
{
|
||||
if (selectedSegmentIndex < 0 || selectedSegmentIndex >= splineCreator.Spline.Segments.Count)
|
||||
return;
|
||||
if (!splineCreator.Spline.IsSegmentValid(selectedSegmentIndex))
|
||||
return;
|
||||
GSplineSegment segment = splineCreator.Spline.Segments[selectedSegmentIndex];
|
||||
GSplineAnchor startAnchor = splineCreator.Spline.Anchors[segment.StartIndex];
|
||||
GSplineAnchor endAnchor = splineCreator.Spline.Anchors[segment.EndIndex];
|
||||
|
||||
Vector3 worldStartPosition = splineCreator.transform.TransformPoint(startAnchor.Position);
|
||||
Vector3 worldEndPosition = splineCreator.transform.TransformPoint(endAnchor.Position);
|
||||
Vector3 worldStartTangent = splineCreator.transform.TransformPoint(segment.StartTangent);
|
||||
Vector3 worldEndTangent = splineCreator.transform.TransformPoint(segment.EndTangent);
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
|
||||
worldStartTangent = Handles.PositionHandle(worldStartTangent, Quaternion.identity);
|
||||
worldEndTangent = Handles.PositionHandle(worldEndTangent, Quaternion.identity);
|
||||
|
||||
segment.StartTangent = splineCreator.transform.InverseTransformPoint(worldStartTangent);
|
||||
segment.EndTangent = splineCreator.transform.InverseTransformPoint(worldEndTangent);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
splineCreator.UpdateMesh(selectedSegmentIndex);
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
Handles.DrawLine(worldStartPosition, worldStartTangent);
|
||||
Handles.DrawLine(worldEndPosition, worldEndTangent);
|
||||
}
|
||||
|
||||
private void DrawPivot()
|
||||
{
|
||||
if (!showTransformGizmos)
|
||||
{
|
||||
Vector3 pivot = splineCreator.transform.position;
|
||||
float size = HandleUtility.GetHandleSize(pivot);
|
||||
|
||||
Vector3 xStart = pivot + Vector3.left * size;
|
||||
Vector3 xEnd = pivot + Vector3.right * size;
|
||||
Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
|
||||
Handles.color = Handles.xAxisColor;
|
||||
Handles.DrawLine(xStart, xEnd);
|
||||
|
||||
Vector3 yStart = pivot + Vector3.down * size;
|
||||
Vector3 yEnd = pivot + Vector3.up * size;
|
||||
Handles.color = Handles.yAxisColor;
|
||||
Handles.DrawLine(yStart, yEnd);
|
||||
|
||||
Vector3 zStart = pivot + Vector3.back * size;
|
||||
Vector3 zEnd = pivot + Vector3.forward * size;
|
||||
Handles.color = Handles.zAxisColor;
|
||||
Handles.DrawLine(zStart, zEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Tools.current == Tool.Move)
|
||||
{
|
||||
splineCreator.transform.position = Handles.PositionHandle(splineCreator.transform.position, splineCreator.transform.rotation);
|
||||
}
|
||||
else if (Tools.current == Tool.Rotate)
|
||||
{
|
||||
splineCreator.transform.rotation = Handles.RotationHandle(splineCreator.transform.rotation, splineCreator.transform.position);
|
||||
}
|
||||
else if (Tools.current == Tool.Scale)
|
||||
{
|
||||
splineCreator.transform.localScale = Handles.ScaleHandle(splineCreator.transform.localScale, splineCreator.transform.position, splineCreator.transform.rotation, HandleUtility.GetHandleSize(splineCreator.transform.position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CatchHotControl()
|
||||
{
|
||||
if (Event.current.alt)
|
||||
return;
|
||||
int controlId = GUIUtility.GetControlID(this.GetHashCode(), FocusType.Passive);
|
||||
if (Event.current.type == EventType.MouseDown)
|
||||
{
|
||||
if (Event.current.button == 0)
|
||||
{
|
||||
GUIUtility.hotControl = controlId;
|
||||
//OnMouseDown(Event.current);
|
||||
}
|
||||
}
|
||||
else if (Event.current.type == EventType.MouseUp)
|
||||
{
|
||||
if (GUIUtility.hotControl == controlId)
|
||||
{
|
||||
//OnMouseUp(Event.current);
|
||||
//Return the hot control back to Unity, use the default
|
||||
GUIUtility.hotControl = 0;
|
||||
}
|
||||
}
|
||||
else if (Event.current.type == EventType.KeyDown)
|
||||
{
|
||||
//OnKeyDown(Event.current);
|
||||
}
|
||||
}
|
||||
|
||||
public static float DistanceMouseToSpline(Vector2 mousePosition, params Vector3[] splinePoint)
|
||||
{
|
||||
float d = float.MaxValue;
|
||||
for (int i = 0; i < splinePoint.Length - 1; ++i)
|
||||
{
|
||||
float d1 = HandleUtility.DistancePointToLineSegment(
|
||||
mousePosition,
|
||||
HandleUtility.WorldToGUIPoint(splinePoint[i]),
|
||||
HandleUtility.WorldToGUIPoint(splinePoint[i + 1]));
|
||||
if (d1 < d)
|
||||
d = d1;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public static float DistanceMouseToPoint(Vector2 mousePosition, Vector3 worldPoint)
|
||||
{
|
||||
float d = Vector2.Distance(
|
||||
mousePosition,
|
||||
HandleUtility.WorldToGUIPoint(worldPoint));
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a260f948ec7700f4cbadbb102203e139
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,30 @@
|
||||
#if GRIFFIN
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
[CustomEditor(typeof(GSplineModifier), true)]
|
||||
public class GSplineModifierInspector : Editor
|
||||
{
|
||||
private GSplineModifier instance;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
instance = (GSplineModifier)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
if (instance.SplineCreator == null)
|
||||
return;
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("Apply"))
|
||||
{
|
||||
instance.Apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 672f37962a8bbbd4a896b3826b4b222a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
||||
#if GRIFFIN
|
||||
using UnityEditor;
|
||||
|
||||
namespace Pinwheel.Griffin.SplineTool
|
||||
{
|
||||
public class GSplineSegmentInspectorDrawer
|
||||
{
|
||||
private GSplineSegment instance;
|
||||
|
||||
public GSplineSegmentInspectorDrawer(GSplineSegment s)
|
||||
{
|
||||
instance = s;
|
||||
}
|
||||
|
||||
public static GSplineSegmentInspectorDrawer Create(GSplineSegment s)
|
||||
{
|
||||
return new GSplineSegmentInspectorDrawer(s);
|
||||
}
|
||||
|
||||
public void DrawGUI()
|
||||
{
|
||||
EditorGUIUtility.wideMode = true;
|
||||
instance.StartTangent = EditorGUILayout.Vector3Field("Start Tangent", instance.StartTangent);
|
||||
instance.EndTangent = EditorGUILayout.Vector3Field("End Tangent", instance.EndTangent);
|
||||
EditorGUIUtility.wideMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66f9ccfb9144d5d4d884c72e1c267058
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user