756 lines
36 KiB
C#
756 lines
36 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEngine.TerrainTools;
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEditor.ShortcutManagement;
|
||
|
|
||
|
namespace UnityEditor.TerrainTools
|
||
|
{
|
||
|
internal class NoiseHeightTool : TerrainPaintTool<NoiseHeightTool>
|
||
|
{
|
||
|
#if UNITY_2019_1_OR_NEWER
|
||
|
[Shortcut("Terrain/Select Noise Height Brush", typeof(TerrainToolShortcutContext))] // tells shortcut manager what to call the shortcut and what to pass as args
|
||
|
static void SelectShortcut(ShortcutArguments args)
|
||
|
{
|
||
|
TerrainToolShortcutContext context = (TerrainToolShortcutContext)args.context; // gets interface to modify state of TerrainTools
|
||
|
context.SelectPaintTool<NoiseHeightTool>(); // set active tool
|
||
|
TerrainToolsAnalytics.OnShortcutKeyRelease("Select Noise Height Brush");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
[System.Serializable]
|
||
|
public enum CoordinateSpace
|
||
|
{
|
||
|
World = 0,
|
||
|
Brush,
|
||
|
}
|
||
|
|
||
|
[System.Serializable]
|
||
|
private struct NoiseToolSettings
|
||
|
{
|
||
|
public Vector2 worldHeightRemap;
|
||
|
public CoordinateSpace coordSpace;
|
||
|
// TODO(wyatt): what happens if they don't have an active noise settings asset?
|
||
|
public string noiseAssetGUID;
|
||
|
|
||
|
public void Reset()
|
||
|
{
|
||
|
worldHeightRemap = new Vector2(0, 1);
|
||
|
coordSpace = CoordinateSpace.World;
|
||
|
noiseAssetGUID = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private NoiseToolSettings m_toolSettings;
|
||
|
private RenderTexture m_previewRT;
|
||
|
|
||
|
private float m_simulationTime;
|
||
|
private bool m_showToolGUI = true;
|
||
|
private bool m_liveUpdate;
|
||
|
private bool m_simulate;
|
||
|
private float m_lastRotation;
|
||
|
private Vector3 m_lastBrushPosition;
|
||
|
private Matrix4x4 m_noiseToWorld;
|
||
|
|
||
|
[SerializeField]
|
||
|
IBrushUIGroup m_commonUI;
|
||
|
private IBrushUIGroup commonUI {
|
||
|
get
|
||
|
{
|
||
|
if (m_commonUI == null)
|
||
|
{
|
||
|
LoadSettings();
|
||
|
m_commonUI = new DefaultBrushUIGroup("NoiseHeightTool",
|
||
|
UpdateAnalyticParameters,
|
||
|
DefaultBrushUIGroup.Feature.All,
|
||
|
new DefaultBrushUIGroup.FeatureDefaults { Strength = 0.37f }
|
||
|
);
|
||
|
m_commonUI.OnEnterToolMode();
|
||
|
}
|
||
|
|
||
|
return m_commonUI;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private NoiseSettings m_noiseSettingsIfNull;
|
||
|
private NoiseSettings noiseSettingsIfNull {
|
||
|
get
|
||
|
{
|
||
|
if (m_noiseSettingsIfNull == null)
|
||
|
{
|
||
|
m_noiseSettingsIfNull = ScriptableObject.CreateInstance<NoiseSettings>();
|
||
|
}
|
||
|
|
||
|
return m_noiseSettingsIfNull;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private string getNoiseSettingsPath {
|
||
|
get { return Application.persistentDataPath + "/TerrainTools_NoiseHeightTool_NoiseSettings.noisesettings"; }
|
||
|
}
|
||
|
|
||
|
private NoiseSettings m_activeNoiseSettingsProfile;
|
||
|
private NoiseSettings m_noiseSettings;
|
||
|
private NoiseSettings noiseSettings {
|
||
|
get
|
||
|
{
|
||
|
if (m_noiseSettings == null)
|
||
|
{
|
||
|
if (System.IO.File.Exists(getNoiseSettingsPath))
|
||
|
{
|
||
|
UnityEngine.Object[] obs = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(getNoiseSettingsPath);
|
||
|
m_noiseSettings = obs[0] as NoiseSettings;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_noiseSettings = ScriptableObject.CreateInstance<NoiseSettings>();
|
||
|
m_noiseSettings.name = "NoiseHeightTool_NoiseSettings";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return m_noiseSettings;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private NoiseSettingsGUI m_noiseSettingsGUI;
|
||
|
private NoiseSettingsGUI noiseSettingsGUI {
|
||
|
get
|
||
|
{
|
||
|
if (m_noiseSettingsGUI == null)
|
||
|
{
|
||
|
m_noiseSettingsGUI = new NoiseSettingsGUI();
|
||
|
}
|
||
|
|
||
|
if (m_noiseSettingsGUI.target == null || m_noiseSettingsGUI.serializedNoise.targetObject == null)
|
||
|
{
|
||
|
m_noiseSettingsGUI.Init(noiseSettings);
|
||
|
}
|
||
|
|
||
|
return m_noiseSettingsGUI;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Allows overriding for unit testing purposes
|
||
|
/// </summary>
|
||
|
/// <param name="uiGroup"></param>
|
||
|
internal void ChangeCommonUI(IBrushUIGroup uiGroup)
|
||
|
{
|
||
|
m_commonUI = uiGroup;
|
||
|
}
|
||
|
|
||
|
public override void OnEnterToolMode()
|
||
|
{
|
||
|
base.OnEnterToolMode();
|
||
|
m_noiseToWorld = Matrix4x4.identity;
|
||
|
commonUI.OnEnterToolMode();
|
||
|
}
|
||
|
|
||
|
public override void OnExitToolMode()
|
||
|
{
|
||
|
base.OnExitToolMode();
|
||
|
commonUI.OnExitToolMode();
|
||
|
}
|
||
|
|
||
|
private static Material m_paintMaterial;
|
||
|
private static Material paintMaterial {
|
||
|
get
|
||
|
{
|
||
|
if (m_paintMaterial == null)
|
||
|
{
|
||
|
m_paintMaterial = new Material(Shader.Find("Hidden/TerrainTools/NoiseHeightTool"));
|
||
|
}
|
||
|
|
||
|
return m_paintMaterial;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void OnDisable()
|
||
|
{
|
||
|
EditorApplication.update -= NoiseSimulationCB;
|
||
|
}
|
||
|
|
||
|
public override string GetName()
|
||
|
{
|
||
|
return "Sculpt/Noise";
|
||
|
}
|
||
|
|
||
|
public override string GetDescription()
|
||
|
{
|
||
|
return "Increases or decreases the Terrain height using Noise and Fractal Types.";
|
||
|
}
|
||
|
|
||
|
private bool m_IsScrolling;
|
||
|
private double m_ScrollTime;
|
||
|
|
||
|
// GUI
|
||
|
public override void OnInspectorGUI(Terrain terrain, IOnInspectorGUI editContext)
|
||
|
{
|
||
|
EditorGUI.BeginChangeCheck();
|
||
|
{
|
||
|
GUILayout.Space(12);
|
||
|
|
||
|
// brush GUI
|
||
|
commonUI.OnInspectorGUI(terrain, editContext);
|
||
|
|
||
|
m_showToolGUI = TerrainToolGUIHelper.DrawHeaderFoldout(Styles.noiseToolSettings, m_showToolGUI);
|
||
|
|
||
|
if (m_showToolGUI)
|
||
|
{
|
||
|
GUILayout.Space(12);
|
||
|
|
||
|
noiseSettingsGUI.DrawPreviewTexture(256f, true, m_IsScrolling);
|
||
|
|
||
|
if (Event.current.type == EventType.ScrollWheel && !noiseSettingsGUI.isScrollingPreview)
|
||
|
{
|
||
|
m_IsScrolling = true;
|
||
|
m_ScrollTime = Time.realtimeSinceStartupAsDouble;
|
||
|
}
|
||
|
|
||
|
if (m_IsScrolling && (Time.realtimeSinceStartupAsDouble - m_ScrollTime) > .2)
|
||
|
{
|
||
|
m_IsScrolling = false;
|
||
|
}
|
||
|
|
||
|
GUILayout.Space(12);
|
||
|
|
||
|
// brush coordinate space toolbar
|
||
|
EditorGUILayout.BeginHorizontal();
|
||
|
{
|
||
|
EditorGUILayout.PrefixLabel(Styles.coordSpace);
|
||
|
|
||
|
if (GUILayout.Toggle(m_toolSettings.coordSpace == CoordinateSpace.World, Styles.worldSpace, GUI.skin.button))
|
||
|
{
|
||
|
m_toolSettings.coordSpace = CoordinateSpace.World;
|
||
|
}
|
||
|
|
||
|
if (GUILayout.Toggle(m_toolSettings.coordSpace == CoordinateSpace.Brush, Styles.brushSpace, GUI.skin.button))
|
||
|
{
|
||
|
m_toolSettings.coordSpace = CoordinateSpace.Brush;
|
||
|
}
|
||
|
}
|
||
|
EditorGUILayout.EndHorizontal();
|
||
|
|
||
|
GUILayout.Space(12);
|
||
|
|
||
|
DoNoiseSettingsObjectField();
|
||
|
|
||
|
noiseSettingsGUI.OnGUI(NoiseSettingsGUIFlags.Settings);
|
||
|
|
||
|
GUILayout.Space(12);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (EditorGUI.EndChangeCheck())
|
||
|
{
|
||
|
SaveSettings();
|
||
|
Save(true);
|
||
|
TerrainToolsAnalytics.OnParameterChange();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DoSimulationControls()
|
||
|
{
|
||
|
EditorGUILayout.BeginHorizontal();
|
||
|
{
|
||
|
GUILayout.Label(Styles.simulationLabel, TerrainToolGUIHelper.dontExpandWidth);
|
||
|
if (GUILayout.Toggle(m_simulate, Styles.simulationButton, GUI.skin.button, TerrainToolGUIHelper.dontExpandWidth))
|
||
|
{
|
||
|
m_simulate = !m_simulate;
|
||
|
|
||
|
EditorApplication.update -= NoiseSimulationCB;
|
||
|
|
||
|
if (m_simulate)
|
||
|
{
|
||
|
EditorApplication.update += NoiseSimulationCB;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
EditorGUILayout.EndHorizontal();
|
||
|
|
||
|
EditorGUILayout.BeginHorizontal();
|
||
|
{
|
||
|
GUILayout.Label(Styles.time);
|
||
|
m_simulationTime = EditorGUILayout.Slider(m_simulationTime, m_simulationTime - 1, m_simulationTime + 1, TerrainToolGUIHelper.dontExpandWidth);
|
||
|
}
|
||
|
EditorGUILayout.EndHorizontal();
|
||
|
|
||
|
GUILayout.Space(12);
|
||
|
}
|
||
|
|
||
|
private void DoNoiseSettingsObjectField()
|
||
|
{
|
||
|
bool profileInUse = m_activeNoiseSettingsProfile != null;
|
||
|
float buttonWidth = 60;
|
||
|
float indentOffset = EditorGUI.indentLevel * 15f;
|
||
|
Rect lineRect = GUILayoutUtility.GetRect(1, EditorGUIUtility.singleLineHeight);
|
||
|
Rect labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height);
|
||
|
Rect fieldRect = new Rect(labelRect.xMax, lineRect.y, lineRect.width - labelRect.width - buttonWidth * (profileInUse ? 3 : 2), lineRect.height);
|
||
|
Rect resetButtonRect = new Rect(fieldRect.xMax, lineRect.y, buttonWidth, lineRect.height);
|
||
|
Rect saveButtonRect = new Rect(resetButtonRect.xMax, lineRect.y, buttonWidth, lineRect.height);
|
||
|
Rect saveAsButtonRect = new Rect(profileInUse ? saveButtonRect.xMax : resetButtonRect.xMax, lineRect.y, buttonWidth, lineRect.height);
|
||
|
|
||
|
EditorGUI.PrefixLabel(labelRect, Styles.noiseSettingsProfile);
|
||
|
|
||
|
NoiseSettings settingsProfile = m_activeNoiseSettingsProfile;
|
||
|
settingsProfile = (NoiseSettings)EditorGUI.ObjectField(fieldRect, settingsProfile, typeof(NoiseSettings), false);
|
||
|
|
||
|
if (m_activeNoiseSettingsProfile != null)
|
||
|
{
|
||
|
if (GUI.Button(resetButtonRect, Styles.revert))
|
||
|
{
|
||
|
Undo.RecordObject(noiseSettings, "Noise Settings - Revert");
|
||
|
|
||
|
noiseSettings.Copy(m_activeNoiseSettingsProfile);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (GUI.Button(resetButtonRect, Styles.reset))
|
||
|
{
|
||
|
Undo.RecordObject(noiseSettings, "Noise Settings - Reset");
|
||
|
|
||
|
noiseSettings.Reset();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (profileInUse && GUI.Button(saveButtonRect, Styles.apply))
|
||
|
{
|
||
|
Undo.RecordObject(m_activeNoiseSettingsProfile, "NoiseHeightTool - Apply Settings");
|
||
|
m_activeNoiseSettingsProfile.CopySerialized(noiseSettings);
|
||
|
}
|
||
|
|
||
|
if (GUI.Button(saveAsButtonRect, Styles.saveAs))
|
||
|
{
|
||
|
string path = EditorUtility.SaveFilePanel("Save Noise Settings",
|
||
|
Application.dataPath,
|
||
|
"New Noise Settings.asset",
|
||
|
"asset");
|
||
|
// saving to project's asset folder
|
||
|
if (path.StartsWith(Application.dataPath))
|
||
|
{
|
||
|
// TODO(wyatt): need to check if this works with different locales/languages. folder might not be
|
||
|
// called "Assets" in non-English Editor builds
|
||
|
string assetPath = path.Substring(Application.dataPath.Length - 6);
|
||
|
// settingsProfile = NoiseSettings.CreateAsset(assetPath, noiseSettings);
|
||
|
settingsProfile = NoiseSettingsFactory.CreateAsset(assetPath);
|
||
|
settingsProfile.CopySerialized(noiseSettings);
|
||
|
}
|
||
|
// saving asset somewhere else. why? dunno!
|
||
|
else if (!string.IsNullOrEmpty(path))
|
||
|
{
|
||
|
Debug.LogError("Invalid path specified for creation of new Noise Settings asset. Must be a valid path within the current Unity project's Assets folder/data path.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if the profile in the object field changed
|
||
|
bool changed = settingsProfile != m_activeNoiseSettingsProfile;
|
||
|
|
||
|
if (changed)
|
||
|
{
|
||
|
if (settingsProfile == null)
|
||
|
{
|
||
|
noiseSettings.Copy(noiseSettingsIfNull);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (m_activeNoiseSettingsProfile == null)
|
||
|
{
|
||
|
noiseSettingsIfNull.Copy(noiseSettings);
|
||
|
}
|
||
|
|
||
|
noiseSettings.Copy(settingsProfile);
|
||
|
}
|
||
|
|
||
|
noiseSettingsGUI.Init(noiseSettings);
|
||
|
m_activeNoiseSettingsProfile = settingsProfile;
|
||
|
}
|
||
|
|
||
|
GUILayout.Space(12);
|
||
|
}
|
||
|
|
||
|
// Fill Tiles
|
||
|
private void FillAllTiles(Terrain terrain)
|
||
|
{
|
||
|
Terrain[] terrains = TerrainFillUtility.GetTerrainsInGroup(terrain);
|
||
|
|
||
|
foreach (Terrain tile in terrains)
|
||
|
{
|
||
|
FillTile(tile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void FillTile(Terrain terrain)
|
||
|
{
|
||
|
// NoiseSettings noiseSettings = noiseSettings;
|
||
|
// Material material = GetNoiseMaterial(NoiseLib.GetFractalFromName(noiseSettings.domainSettings.fractalTypeName).GetType());
|
||
|
// int pass = NoiseLib.GetNoiseIndex(noiseSettings.domainSettings.noiseTypeName);
|
||
|
|
||
|
// float previewSize = 1 / 512f;
|
||
|
// float brushSize = 1;
|
||
|
// // brushSize = brushSize * previewSize;
|
||
|
|
||
|
// float brushRotation = 0;
|
||
|
|
||
|
// float brushStrength = commonUI.brushStrength;
|
||
|
// Texture brushTexture = Texture2D.whiteTexture;
|
||
|
|
||
|
// Vector3 brushPosWS = terrain.transform.position + .5f * new Vector3( terrain.terrainData.size.x, 0, terrain.terrainData.size.z );
|
||
|
|
||
|
// // set brush params
|
||
|
// Vector4 brushParams = new Vector4( 0.01f * brushStrength, 0.0f, brushSize, 1 / brushSize );
|
||
|
// Quaternion rotQ = Quaternion.AxisAngle(Vector3.up, 0);
|
||
|
// Matrix4x4 translation = Matrix4x4.Translate( brushPosWS );
|
||
|
// Matrix4x4 rotation = Matrix4x4.Rotate( rotQ );
|
||
|
// Matrix4x4 scale = Matrix4x4.Scale( Vector3.one * brushSize );
|
||
|
// Matrix4x4 b2w = translation * rotation * scale;
|
||
|
|
||
|
// // brush mask and settings
|
||
|
// material.SetTexture( "_BrushTex", brushTexture );
|
||
|
// material.SetVector( "_BrushParams", brushParams );
|
||
|
|
||
|
// // use this value to remap noise values
|
||
|
// material.SetVector( "_WorldHeightRemap", m_toolSettings.worldHeightRemap );
|
||
|
|
||
|
// // assign matrices
|
||
|
// material.SetMatrix( "_b2w", b2w );
|
||
|
// material.SetMatrix( "_b2w_Translation", translation );
|
||
|
// material.SetMatrix( "_b2w_Rotation", rotation );
|
||
|
// material.SetMatrix( "_b2w_Scale", scale );
|
||
|
|
||
|
// // assign inverse matrices
|
||
|
// material.SetMatrix( "_w2b", b2w.inverse );
|
||
|
// material.SetMatrix( "_w2b_Translation", translation.inverse );
|
||
|
// material.SetMatrix( "_w2b_Rotation", rotation.inverse );
|
||
|
// material.SetMatrix( "_w2b_Scale", scale.inverse );
|
||
|
|
||
|
// noiseSettings.SetupMaterial(material);
|
||
|
|
||
|
// BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, Vector2.one * .5f,
|
||
|
// Mathf.Min(terrain.terrainData.size.x, terrain.terrainData.size.z), 0);
|
||
|
// PaintContext ctx = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds(), 1);
|
||
|
|
||
|
// TerrainPaintUtility.SetupTerrainToolMaterialProperties(ctx, brushXform, material);
|
||
|
|
||
|
// Graphics.Blit(ctx.sourceRenderTexture, ctx.destinationRenderTexture, material, pass);
|
||
|
|
||
|
// TerrainFillUtility.EndFillHeightmap(ctx, "Terrain Fill - Noise");
|
||
|
// terrain.ApplyDelayedHeightmapModification();
|
||
|
}
|
||
|
|
||
|
private void ApplyBrushInternal(Terrain terrain, PaintContext ctx, BrushTransform brushXform, Vector3 brushPosWS,
|
||
|
float brushRotation, float brushStrength, float brushSize, Texture brushTexture)
|
||
|
{
|
||
|
var prevRT = RenderTexture.active;
|
||
|
|
||
|
var brushPositionOffset = brushPosWS - m_lastBrushPosition;
|
||
|
m_lastBrushPosition = brushPosWS;
|
||
|
brushPositionOffset.y = 0;
|
||
|
|
||
|
var rotationDelta = brushRotation - m_lastRotation;
|
||
|
m_lastRotation = brushRotation;
|
||
|
|
||
|
//blit steps
|
||
|
//1. blit noise to intermediate RT, this includes all the noise transformations and filters,
|
||
|
//using the appropriate noise material. do this with NoiseUtils.Blit2D?
|
||
|
//2. use that noise texture and mult it with brushmask to paint height on terrain
|
||
|
|
||
|
// TODO(wyatt): remove magic number and tie it into NoiseSettingsGUI preview size somehow
|
||
|
float previewSize = 1 / 512f;
|
||
|
|
||
|
// get proper noise material from current noise settings
|
||
|
NoiseSettings noiseSettings = this.noiseSettings;
|
||
|
Material matNoise = NoiseUtils.GetDefaultBlitMaterial(noiseSettings);
|
||
|
|
||
|
// setup the noise material with values in noise settings
|
||
|
noiseSettings.SetupMaterial(matNoise);
|
||
|
|
||
|
// change pos and scale so they match the noiseSettings preview
|
||
|
bool isWorldSpace = (m_toolSettings.coordSpace == CoordinateSpace.World);
|
||
|
brushSize = isWorldSpace ? brushSize * previewSize : 1;
|
||
|
brushPositionOffset = isWorldSpace ? brushPositionOffset * previewSize : Vector3.zero;
|
||
|
var brushTransform = NoiseFilter.GetBrushTransform(rotationDelta, brushSize);
|
||
|
var scaleMultiplier = new Vector2(
|
||
|
1.0f / (brushSize / brushTransform.GetBrushXYBounds().width),
|
||
|
1.0f / (brushSize / brushTransform.GetBrushXYBounds().height));
|
||
|
|
||
|
// // override noise transform
|
||
|
Quaternion rotQ = Quaternion.AngleAxis(-rotationDelta, Vector3.up);
|
||
|
// accumulate transformation delta
|
||
|
m_noiseToWorld *= Matrix4x4.TRS(brushPositionOffset, rotQ, Vector3.one);
|
||
|
|
||
|
matNoise.SetMatrix(NoiseSettings.ShaderStrings.transform, noiseSettings.trs * m_noiseToWorld * Matrix4x4.Scale(new Vector3(scaleMultiplier.x, 1.0f, scaleMultiplier.y) * brushSize));
|
||
|
|
||
|
var noisePass = NoiseUtils.kNumBlitPasses * NoiseLib.GetNoiseIndex(noiseSettings.domainSettings.noiseTypeName);
|
||
|
|
||
|
// render the noise field to a texture
|
||
|
// TODO(wyatt): Handle the 3D case. Would need to blit to Volume Texture
|
||
|
var rtDesc = ctx.destinationRenderTexture.descriptor;
|
||
|
rtDesc.graphicsFormat = NoiseUtils.singleChannelFormat;
|
||
|
rtDesc.sRGB = false;
|
||
|
var noiseRT = RTUtils.GetTempHandle(rtDesc);
|
||
|
RenderTexture.active = noiseRT; // keep this
|
||
|
Graphics.Blit(noiseRT, matNoise, noisePass);
|
||
|
|
||
|
// then add the result to the heightmap using the noise height tool shader
|
||
|
Material matFinal = paintMaterial;
|
||
|
var brushMask = RTUtils.GetTempHandle(ctx.sourceRenderTexture.width, ctx.sourceRenderTexture.height, 0, FilterUtility.defaultFormat);
|
||
|
Utility.GenerateAndSetFilterRT(commonUI, ctx.sourceRenderTexture, brushMask, matFinal);
|
||
|
TerrainPaintUtility.SetupTerrainToolMaterialProperties(ctx, brushXform, matFinal);
|
||
|
// set brush params
|
||
|
Vector4 brushParams = new Vector4(0.01f * brushStrength, 0.0f, brushSize, 1 / brushSize);
|
||
|
matFinal.SetVector("_BrushParams", brushParams);
|
||
|
matFinal.SetTexture("_BrushTex", brushTexture);
|
||
|
matFinal.SetTexture("_NoiseTex", noiseRT);
|
||
|
matFinal.SetVector("_WorldHeightRemap", m_toolSettings.worldHeightRemap);
|
||
|
Graphics.Blit(ctx.sourceRenderTexture, ctx.destinationRenderTexture, matFinal, 0);
|
||
|
|
||
|
RTUtils.Release(noiseRT);
|
||
|
RTUtils.Release(brushMask);
|
||
|
|
||
|
RenderTexture.active = prevRT;
|
||
|
}
|
||
|
|
||
|
public override void OnSceneGUI(Terrain terrain, IOnSceneGUI editContext)
|
||
|
{
|
||
|
commonUI.OnSceneGUI2D(terrain, editContext);
|
||
|
|
||
|
// only do the rest if user mouse hits valid terrain or they are using the
|
||
|
// brush parameter hotkeys to resize, etc
|
||
|
if (!editContext.hitValidTerrain && !commonUI.isInUse)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// update brush UI group
|
||
|
commonUI.OnSceneGUI(terrain, editContext);
|
||
|
|
||
|
// dont render preview if this isnt a repaint. losing performance if we do
|
||
|
if (Event.current.type != EventType.Repaint)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
using (IBrushRenderPreviewUnderCursor brushPreview =
|
||
|
new BrushRenderPreviewUIGroupUnderCursor(commonUI, "NoiseHeightTool", editContext.brushTexture))
|
||
|
{
|
||
|
float brushSize = commonUI.brushSize;
|
||
|
float brushStrength = Event.current.control ? -commonUI.brushStrength : commonUI.brushStrength;
|
||
|
Vector3 brushPosWS = commonUI.raycastHitUnderCursor.point;
|
||
|
brushPreview.CalculateBrushTransform(out var brushXform);
|
||
|
PaintContext ctx = brushPreview.AcquireHeightmap(false, brushXform.GetBrushXYBounds(), 1);
|
||
|
|
||
|
Material previewMaterial = Utility.GetDefaultPreviewMaterial(commonUI.hasEnabledFilters);
|
||
|
|
||
|
var filter = RTUtils.GetTempHandle(ctx.sourceRenderTexture.width, ctx.sourceRenderTexture.height, 0, FilterUtility.defaultFormat);
|
||
|
Utility.GenerateAndSetFilterRT(commonUI, ctx.sourceRenderTexture, filter, previewMaterial);
|
||
|
|
||
|
var texelCtx = Utility.CollectTexelValidity(ctx.originTerrain, brushXform.GetBrushXYBounds());
|
||
|
Utility.SetupMaterialForPaintingWithTexelValidityContext(ctx, texelCtx, brushXform, previewMaterial);
|
||
|
|
||
|
previewMaterial.SetTexture("_HeightmapOrig", ctx.sourceRenderTexture);
|
||
|
TerrainPaintUtilityEditor.DrawBrushPreview(ctx, TerrainBrushPreviewMode.SourceRenderTexture,
|
||
|
editContext.brushTexture, brushXform, previewMaterial, 0);
|
||
|
|
||
|
ApplyBrushInternal(terrain, ctx, brushXform, brushPosWS, commonUI.brushRotation,
|
||
|
brushStrength, brushSize, editContext.brushTexture);
|
||
|
|
||
|
TerrainPaintUtility.SetupTerrainToolMaterialProperties(ctx, brushXform, previewMaterial);
|
||
|
|
||
|
// restore old render target
|
||
|
RenderTexture.active = ctx.oldRenderTexture;
|
||
|
|
||
|
TerrainPaintUtilityEditor.DrawBrushPreview(ctx, TerrainBrushPreviewMode.DestinationRenderTexture,
|
||
|
editContext.brushTexture, brushXform, previewMaterial, 1);
|
||
|
texelCtx.Cleanup();
|
||
|
|
||
|
TerrainPaintUtility.ReleaseContextResources(ctx);
|
||
|
RTUtils.Release(filter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private Vector3 WSPosFromTerrainUV(Terrain terrain, Vector2 uv)
|
||
|
{
|
||
|
return terrain.transform.position +
|
||
|
terrain.terrainData.size.x * uv.x * Vector3.right +
|
||
|
terrain.terrainData.size.z * uv.y * Vector3.forward;
|
||
|
}
|
||
|
|
||
|
public override bool OnPaint(Terrain terrain, IOnPaint editContext)
|
||
|
{
|
||
|
commonUI.OnPaint(terrain, editContext);
|
||
|
|
||
|
if (commonUI.allowPaint)
|
||
|
{
|
||
|
Vector2 uv = editContext.uv;
|
||
|
|
||
|
if (commonUI.ScatterBrushStamp(ref terrain, ref uv))
|
||
|
{
|
||
|
BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, uv, commonUI.brushSize, commonUI.brushRotation);
|
||
|
PaintContext paintContext = TerrainPaintUtility.BeginPaintHeightmap(terrain, brushXform.GetBrushXYBounds());
|
||
|
float brushStrength = commonUI.brushStrength;
|
||
|
if (Event.current != null && Event.current.control)
|
||
|
{
|
||
|
brushStrength = -commonUI.brushStrength;
|
||
|
}
|
||
|
|
||
|
Vector3 brushPosWS = WSPosFromTerrainUV(terrain, uv);
|
||
|
|
||
|
ApplyBrushInternal(terrain, paintContext, brushXform, brushPosWS, commonUI.brushRotation,
|
||
|
brushStrength, commonUI.brushSize, editContext.brushTexture);
|
||
|
|
||
|
TerrainPaintUtility.EndPaintHeightmap(paintContext, "Terrain Paint - Noise");
|
||
|
}
|
||
|
}
|
||
|
editContext.Repaint(RepaintFlags.UI);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private void NoiseSimulationCB()
|
||
|
{
|
||
|
SceneView.RepaintAll();
|
||
|
}
|
||
|
|
||
|
// Tool Settings I/O
|
||
|
const string kToolSettingsName = "Unity.TerrainTools.NoiseHeightTool";
|
||
|
|
||
|
private void LoadSettings()
|
||
|
{
|
||
|
NoiseToolSettings defaultSettings = new NoiseToolSettings();
|
||
|
defaultSettings.Reset();
|
||
|
|
||
|
string settingsStr = EditorPrefs.GetString(kToolSettingsName, JsonUtility.ToJson(defaultSettings));
|
||
|
m_toolSettings = JsonUtility.FromJson<NoiseToolSettings>(settingsStr);
|
||
|
|
||
|
string assetPath = AssetDatabase.GUIDToAssetPath(m_toolSettings.noiseAssetGUID);
|
||
|
|
||
|
if (!string.IsNullOrEmpty(assetPath))
|
||
|
{
|
||
|
m_activeNoiseSettingsProfile = AssetDatabase.LoadAssetAtPath<NoiseSettings>(assetPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void SaveSettings()
|
||
|
{
|
||
|
if (m_activeNoiseSettingsProfile != null)
|
||
|
{
|
||
|
string assetPath = AssetDatabase.GetAssetPath(m_activeNoiseSettingsProfile);
|
||
|
|
||
|
if (!string.IsNullOrEmpty(assetPath))
|
||
|
{
|
||
|
m_toolSettings.noiseAssetGUID = AssetDatabase.AssetPathToGUID(assetPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string settingsStr = JsonUtility.ToJson(m_toolSettings);
|
||
|
EditorPrefs.SetString(kToolSettingsName, settingsStr);
|
||
|
|
||
|
// save the noise settings
|
||
|
UnityEditorInternal.InternalEditorUtility.SaveToSerializedFileAndForget(new UnityEngine.Object[] { m_noiseSettings }, getNoiseSettingsPath, true);
|
||
|
}
|
||
|
|
||
|
// GUI Content and Styles
|
||
|
static class Styles
|
||
|
{
|
||
|
public static GUILayoutOption dontExpandWidth = GUILayout.ExpandWidth(false);
|
||
|
public static GUIContent simulationLabel = EditorGUIUtility.TrTextContent("Simulation Controls:");
|
||
|
public static GUIContent simulationButton = EditorGUIUtility.TrTextContent("Simulate");
|
||
|
public static GUIContent time = EditorGUIUtility.TrTextContent("Time");
|
||
|
public static GUIContent fillOptions = EditorGUIUtility.TrTextContent("Fill Options");
|
||
|
public static GUIContent fillSelected = EditorGUIUtility.TrTextContent("Fill Selected Tile");
|
||
|
public static GUIContent fillGroup = EditorGUIUtility.TrTextContent("Fill Tiles in Group");
|
||
|
public static GUIContent reset = EditorGUIUtility.TrTextContent("Reset", "Reset the Noise Settings to the default settings");
|
||
|
public static GUIContent revert = EditorGUIUtility.TrTextContent("Revert", "Revert the Noise Settings to the values of the Asset that is saved on disk");
|
||
|
public static GUIContent apply = EditorGUIUtility.TrTextContent("Apply", "Apply the current Noise Settings to the Asset that is saved on disk");
|
||
|
public static GUIContent saveAs = EditorGUIUtility.TrTextContent("Save As", "Open a window allowing you to save the current Noise Settings to a new Asset on disk");
|
||
|
public static GUIContent liveUpdate = EditorGUIUtility.TrTextContent("Live Update:");
|
||
|
public static GUIContent noiseSettingsProfile = EditorGUIUtility.TrTextContent("Noise Settings Asset", "The Noise Settings Asset to use when generating Noise for this tool");
|
||
|
public static GUIContent coordSpace = EditorGUIUtility.TrTextContent("Coordinate Space", "The coordinate space that is used when calculating positions fed into the Noise generation");
|
||
|
public static GUIContent worldSpace = EditorGUIUtility.TrTextContent("World", "World space positions are used to generate Noise");
|
||
|
public static GUIContent brushSpace = EditorGUIUtility.TrTextContent("Brush", "Brush space positions based on Brush UVs are used to generate Noise");
|
||
|
public static GUIContent worldSpaceHeightRange = EditorGUIUtility.TrTextContent("World Height Range");
|
||
|
public static GUIContent noiseToolSettings = EditorGUIUtility.TrTextContent("Noise Height Tool Settings", "Settings for the Noise Height Tool");
|
||
|
}
|
||
|
|
||
|
//Analytics Setup
|
||
|
List<TerrainToolsAnalytics.IBrushParameter> m_AnalyticsData = new List<TerrainToolsAnalytics.IBrushParameter>();
|
||
|
private TerrainToolsAnalytics.IBrushParameter[] UpdateAnalyticParameters()
|
||
|
{
|
||
|
m_AnalyticsData.Clear();
|
||
|
|
||
|
SerializedProperty transformSettings = noiseSettingsGUI.serializedNoise.FindProperty("transformSettings");
|
||
|
SerializedProperty domainSettings = noiseSettingsGUI.serializedNoise.FindProperty("domainSettings");
|
||
|
SerializedProperty fractalTypeName = domainSettings.FindPropertyRelative("fractalTypeName");
|
||
|
SerializedProperty fractalTypeParams = domainSettings.FindPropertyRelative("fractalTypeParams");
|
||
|
|
||
|
//Add Generic, Transform, and Domain type settings to be sent as analytic data
|
||
|
m_AnalyticsData.AddRange(new TerrainToolsAnalytics.IBrushParameter[]{
|
||
|
//Generic Settings
|
||
|
new TerrainToolsAnalytics.BrushParameter<string>{Name = Styles.coordSpace.text,
|
||
|
Value = m_toolSettings.coordSpace.ToString()},
|
||
|
|
||
|
//Transform Settings
|
||
|
new TerrainToolsAnalytics.BrushParameter<Vector3>{Name = "Translation",
|
||
|
Value = transformSettings.FindPropertyRelative("translation").vector3Value},
|
||
|
new TerrainToolsAnalytics.BrushParameter<Vector3>{Name = "Rotation",
|
||
|
Value = transformSettings.FindPropertyRelative("rotation").vector3Value},
|
||
|
new TerrainToolsAnalytics.BrushParameter<Vector3>{Name = "Scale",
|
||
|
Value = transformSettings.FindPropertyRelative("scale").vector3Value},
|
||
|
new TerrainToolsAnalytics.BrushParameter<bool>{Name = NoiseSettingsGUI.Styles.flipScaleX.text,
|
||
|
Value = transformSettings.FindPropertyRelative("flipScaleX").boolValue},
|
||
|
new TerrainToolsAnalytics.BrushParameter<bool>{Name = NoiseSettingsGUI.Styles.flipScaleY.text,
|
||
|
Value = transformSettings.FindPropertyRelative("flipScaleY").boolValue},
|
||
|
new TerrainToolsAnalytics.BrushParameter<bool>{Name = NoiseSettingsGUI.Styles.flipScaleZ.text,
|
||
|
Value = transformSettings.FindPropertyRelative("flipScaleZ").boolValue},
|
||
|
|
||
|
//Domain
|
||
|
new TerrainToolsAnalytics.BrushParameter<string>{Name = NoiseSettingsGUI.Styles.noiseType.text,
|
||
|
Value = domainSettings.FindPropertyRelative("noiseTypeName").stringValue},
|
||
|
new TerrainToolsAnalytics.BrushParameter<string>{Name = NoiseSettingsGUI.Styles.fractalType.text,
|
||
|
Value = domainSettings.FindPropertyRelative("fractalTypeName").stringValue}}
|
||
|
);
|
||
|
|
||
|
//Add fractal specific settings to be sent as analytic data
|
||
|
IFractalType fractalType = NoiseLib.GetFractalTypeInstance(fractalTypeName.stringValue);
|
||
|
switch (domainSettings.FindPropertyRelative("fractalTypeName").stringValue)
|
||
|
{
|
||
|
case "Fbm":
|
||
|
FbmFractalType.FbmFractalInput fbmFractalSettings = (FbmFractalType.FbmFractalInput)fractalType.FromSerializedString(fractalTypeParams.stringValue);
|
||
|
m_AnalyticsData.AddRange(new TerrainToolsAnalytics.IBrushParameter[]{
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.octaves.text, Value = fbmFractalSettings.octaves},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.amplitude.text, Value = fbmFractalSettings.amplitude},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.persistence.text, Value = fbmFractalSettings.persistence},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.frequency.text, Value = fbmFractalSettings.frequency},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.lacunarity.text, Value = fbmFractalSettings.lacunarity},
|
||
|
new TerrainToolsAnalytics.BrushParameter<bool>{Name = FbmFractalType.Styles.domainWarpSettings.text, Value = fbmFractalSettings.warpEnabled},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.warpIterations.text, Value = fbmFractalSettings.warpIterations},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = FbmFractalType.Styles.warpStrength.text, Value = fbmFractalSettings.warpStrength},
|
||
|
new TerrainToolsAnalytics.BrushParameter<Vector4>{Name = FbmFractalType.Styles.warpOffsets.text, Value = fbmFractalSettings.warpOffsets},
|
||
|
});
|
||
|
break;
|
||
|
case "Strata":
|
||
|
StrataFractalType.StrataFractalInput strataFractalSettings = (StrataFractalType.StrataFractalInput)fractalType.FromSerializedString(fractalTypeParams.stringValue);
|
||
|
m_AnalyticsData.AddRange(new TerrainToolsAnalytics.IBrushParameter[]{
|
||
|
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.octaves.text, Value = strataFractalSettings.octaves},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.amplitude.text, Value = strataFractalSettings.amplitude},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.persistence.text, Value = strataFractalSettings.persistence},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.frequency.text, Value = strataFractalSettings.frequency},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.lacunarity.text, Value = strataFractalSettings.lacunarity},
|
||
|
new TerrainToolsAnalytics.BrushParameter<bool>{Name = StrataFractalType.Styles.domainWarpSettings.text, Value = strataFractalSettings.warpEnabled},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.warpIterations.text, Value = strataFractalSettings.warpIterations},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.warpStrength.text, Value = strataFractalSettings.warpStrength},
|
||
|
new TerrainToolsAnalytics.BrushParameter<Vector4>{Name = StrataFractalType.Styles.warpOffsets.text, Value = strataFractalSettings.warpOffsets},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.strataOffset.text, Value = strataFractalSettings.strataOffset},
|
||
|
new TerrainToolsAnalytics.BrushParameter<float>{Name = StrataFractalType.Styles.strataScale.text, Value = strataFractalSettings.strataScale},
|
||
|
});
|
||
|
break;
|
||
|
case "None":
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return m_AnalyticsData.ToArray();
|
||
|
}
|
||
|
}
|
||
|
}
|