506 lines
14 KiB
C#
506 lines
14 KiB
C#
using LE_LevelEditor.Commands;
|
|
using LE_LevelEditor.Core;
|
|
using LE_LevelEditor.Events;
|
|
using TT_TerrainTools;
|
|
using UndoRedo;
|
|
using UnityEngine;
|
|
|
|
namespace LE_LevelEditor.UI
|
|
{
|
|
public class LE_GUI3dTerrain : LE_GUI3dBase
|
|
{
|
|
public int TERRAIN_LAYER = 28;
|
|
|
|
private Terrain m_terrain;
|
|
|
|
private LE_TerrainManager m_terrainMgr;
|
|
|
|
private TerrainData m_defaultTerrainDataPrefab;
|
|
|
|
private Projector m_brushProjector;
|
|
|
|
private Texture2D m_brushAlphaTexture;
|
|
|
|
private int m_selectedSplatPrototype;
|
|
|
|
private float m_amount = 0.1f;
|
|
|
|
private float m_size = 0.1f;
|
|
|
|
private float m_targetRelativeValue = 0.5f;
|
|
|
|
private float m_targetTextureStrengthValue = 0.5f;
|
|
|
|
private bool m_isReadingTerrainPaintHeight;
|
|
|
|
private int m_isTerrainPaintHeightReadInFrame = -1;
|
|
|
|
private bool m_isDirectedSmooth;
|
|
|
|
private float m_directedSmoothAngle;
|
|
|
|
private LE_ETerrainEditMode m_editMode;
|
|
|
|
private LE_GenCmdTerrain m_genCmdTerrain;
|
|
|
|
private Vector3 m_lastCursorActiveScreenCoords = -1f * Vector3.one;
|
|
|
|
public Terrain TerrainInstance
|
|
{
|
|
get
|
|
{
|
|
return m_terrain;
|
|
}
|
|
}
|
|
|
|
public LE_TerrainManager TerrainManager
|
|
{
|
|
get
|
|
{
|
|
return m_terrainMgr;
|
|
}
|
|
}
|
|
|
|
public TerrainData DefaultTerrainDataPrefab
|
|
{
|
|
get
|
|
{
|
|
return m_defaultTerrainDataPrefab;
|
|
}
|
|
set
|
|
{
|
|
m_defaultTerrainDataPrefab = value;
|
|
}
|
|
}
|
|
|
|
public Projector BrushProjector
|
|
{
|
|
get
|
|
{
|
|
return m_brushProjector;
|
|
}
|
|
set
|
|
{
|
|
m_brushProjector = value;
|
|
m_brushProjector.material = Object.Instantiate(m_brushProjector.material);
|
|
m_brushProjector.ignoreLayers = ~(1 << TERRAIN_LAYER);
|
|
}
|
|
}
|
|
|
|
public Texture2D BrushAlphaTexture
|
|
{
|
|
get
|
|
{
|
|
return m_brushAlphaTexture;
|
|
}
|
|
set
|
|
{
|
|
m_brushAlphaTexture = value;
|
|
m_brushProjector.material.mainTexture = m_brushAlphaTexture;
|
|
}
|
|
}
|
|
|
|
public int SelectedSplatPrototype
|
|
{
|
|
get
|
|
{
|
|
return m_selectedSplatPrototype;
|
|
}
|
|
set
|
|
{
|
|
m_selectedSplatPrototype = value;
|
|
}
|
|
}
|
|
|
|
public float Amount
|
|
{
|
|
get
|
|
{
|
|
return m_amount;
|
|
}
|
|
set
|
|
{
|
|
m_amount = value;
|
|
}
|
|
}
|
|
|
|
public float Size
|
|
{
|
|
get
|
|
{
|
|
return m_size;
|
|
}
|
|
set
|
|
{
|
|
m_size = value;
|
|
if (m_terrainMgr != null)
|
|
{
|
|
float num = 1f / (float)Mathf.Min(m_terrainMgr.TerrainData.heightmapWidth - 1, m_terrainMgr.TerrainData.heightmapHeight - 1);
|
|
m_size = Mathf.Floor(m_size / num) * num;
|
|
}
|
|
}
|
|
}
|
|
|
|
public float TargetRelativeValue
|
|
{
|
|
get
|
|
{
|
|
return m_targetRelativeValue;
|
|
}
|
|
set
|
|
{
|
|
m_targetRelativeValue = value;
|
|
}
|
|
}
|
|
|
|
public float TargetTextureStrengthValue
|
|
{
|
|
get
|
|
{
|
|
return m_targetTextureStrengthValue;
|
|
}
|
|
set
|
|
{
|
|
m_targetTextureStrengthValue = value;
|
|
}
|
|
}
|
|
|
|
public bool IsReadingTerrainPaintHeight
|
|
{
|
|
get
|
|
{
|
|
return m_isReadingTerrainPaintHeight;
|
|
}
|
|
set
|
|
{
|
|
m_isReadingTerrainPaintHeight = value;
|
|
m_isTerrainPaintHeightReadInFrame = -1;
|
|
}
|
|
}
|
|
|
|
public bool IsDirectedSmooth
|
|
{
|
|
get
|
|
{
|
|
return m_isDirectedSmooth;
|
|
}
|
|
set
|
|
{
|
|
m_isDirectedSmooth = value;
|
|
}
|
|
}
|
|
|
|
public float DirectedSmoothAngle
|
|
{
|
|
get
|
|
{
|
|
return m_directedSmoothAngle;
|
|
}
|
|
set
|
|
{
|
|
m_directedSmoothAngle = value;
|
|
if (m_brushProjector.transform.childCount > 0)
|
|
{
|
|
m_brushProjector.transform.GetChild(0).transform.localRotation = Quaternion.Euler(Vector3.back * m_directedSmoothAngle);
|
|
}
|
|
}
|
|
}
|
|
|
|
public LE_ETerrainEditMode EditMode
|
|
{
|
|
get
|
|
{
|
|
return m_editMode;
|
|
}
|
|
set
|
|
{
|
|
m_editMode = value;
|
|
if (m_editMode != LE_ETerrainEditMode.CHANGE_HEIGHT_TO_TARGET_VALUE)
|
|
{
|
|
m_isReadingTerrainPaintHeight = false;
|
|
m_isTerrainPaintHeightReadInFrame = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override LE_EEditMode ActiveEditMode
|
|
{
|
|
get
|
|
{
|
|
return LE_EEditMode.TERRAIN;
|
|
}
|
|
}
|
|
|
|
public void SetTerrain(Terrain p_terrain)
|
|
{
|
|
if (m_terrainMgr != null)
|
|
{
|
|
Debug.LogError("LE_GUI3dTerrain: SetTerrain: a terrain manager was already set and will be overwritten! Use 'RemoveTerrainManager' to reset the instance.");
|
|
}
|
|
m_terrain = p_terrain;
|
|
m_terrainMgr = new LE_TerrainManager(p_terrain.terrainData);
|
|
if (LE_EventInterface.OnChangeLevelData != null)
|
|
{
|
|
LE_EventInterface.OnChangeLevelData(m_terrain.gameObject, new LE_LevelDataChangedEvent(LE_ELevelDataChangeType.TERRAIN_SELECTION));
|
|
}
|
|
}
|
|
|
|
public void RemoveTerrainManager()
|
|
{
|
|
if (m_terrain != null && LE_EventInterface.OnChangeLevelData != null)
|
|
{
|
|
LE_EventInterface.OnChangeLevelData(m_terrain.gameObject, new LE_LevelDataChangedEvent(LE_ELevelDataChangeType.TERRAIN_SELECTION));
|
|
}
|
|
m_terrain = null;
|
|
m_terrainMgr = null;
|
|
}
|
|
|
|
public override void SetCursorPosition(Vector3 p_cursorScreenCoords)
|
|
{
|
|
m_cursorScreenCoords = p_cursorScreenCoords;
|
|
m_cursorRay = Camera.main.ScreenPointToRay(p_cursorScreenCoords);
|
|
if (m_terrain != null)
|
|
{
|
|
SetIsCursorOverSomething(m_terrain.GetComponent<Collider>().Raycast(m_cursorRay, out m_cursorHitInfo, float.MaxValue));
|
|
}
|
|
else
|
|
{
|
|
SetIsCursorOverSomething(false);
|
|
}
|
|
}
|
|
|
|
public override void SetIsCursorAction(bool p_isCursorAction)
|
|
{
|
|
if (m_isTerrainPaintHeightReadInFrame != -1 && !p_isCursorAction && m_isTerrainPaintHeightReadInFrame + 1 < Time.frameCount)
|
|
{
|
|
m_isReadingTerrainPaintHeight = false;
|
|
m_isTerrainPaintHeightReadInFrame = -1;
|
|
}
|
|
if (base.IsCursorOverSomething && base.IsInteractable && p_isCursorAction && m_lastCursorActiveScreenCoords != m_cursorScreenCoords)
|
|
{
|
|
m_lastCursorActiveScreenCoords = m_cursorScreenCoords;
|
|
if (m_cursorHitInfo.transform.gameObject.GetComponent<Terrain>() != null)
|
|
{
|
|
if (m_isReadingTerrainPaintHeight)
|
|
{
|
|
Terrain component = m_cursorHitInfo.transform.gameObject.GetComponent<Terrain>();
|
|
float num = component.SampleHeight(m_cursorHitInfo.point);
|
|
m_targetRelativeValue = Mathf.Clamp01(num / component.terrainData.size.y);
|
|
m_isTerrainPaintHeightReadInFrame = Time.frameCount;
|
|
}
|
|
else
|
|
{
|
|
if (m_genCmdTerrain == null)
|
|
{
|
|
LE_GenCmdTerrain.Mode p_cmdMode = ((m_editMode == LE_ETerrainEditMode.DRAW_TEXTURE) ? LE_GenCmdTerrain.Mode.ALPHAMAPS_CMD : LE_GenCmdTerrain.Mode.HEIGHTS_CMD);
|
|
m_genCmdTerrain = new LE_GenCmdTerrain(this, m_terrainMgr, p_cmdMode);
|
|
}
|
|
switch (m_editMode)
|
|
{
|
|
case LE_ETerrainEditMode.CHANGE_HEIGHT:
|
|
m_genCmdTerrain.ChangeHeight(Mathf.Sign(Amount) * Mathf.Max(0.002f, Amount * Amount) * Time.deltaTime * 2f, BrushAlphaTexture, Size, GetRelativeLocalLocation(m_cursorHitInfo));
|
|
break;
|
|
case LE_ETerrainEditMode.CHANGE_HEIGHT_TO_TARGET_VALUE:
|
|
m_genCmdTerrain.ChangeHeight(Mathf.Max(0.002f, Amount * Amount) * Time.deltaTime * 2f, TargetRelativeValue, BrushAlphaTexture, Size, GetRelativeLocalLocation(m_cursorHitInfo));
|
|
break;
|
|
case LE_ETerrainEditMode.SMOOTH_HEIGHT:
|
|
{
|
|
int p_neighbourCount = 3 + 2 * Mathf.RoundToInt(Mathf.Abs(Amount) * 3f);
|
|
m_genCmdTerrain.SmoothHeight(Time.deltaTime * 2f, p_neighbourCount, BrushAlphaTexture, Size, GetRelativeLocalLocation(m_cursorHitInfo), m_isDirectedSmooth, m_directedSmoothAngle);
|
|
break;
|
|
}
|
|
case LE_ETerrainEditMode.DRAW_TEXTURE:
|
|
m_genCmdTerrain.PaintTexture(SelectedSplatPrototype, Mathf.Abs(Amount) * Time.deltaTime * 8f, TargetTextureStrengthValue, BrushAlphaTexture, Size, GetRelativeLocalLocation(m_cursorHitInfo));
|
|
break;
|
|
default:
|
|
Debug.LogError("LE_GUI3dTerrain: unknown EditMode!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_lastCursorActiveScreenCoords = -1f * Vector3.one;
|
|
}
|
|
if (m_genCmdTerrain != null && m_genCmdTerrain.LastEditedFrame + 1 < Time.frameCount)
|
|
{
|
|
UR_ICommand cmd = m_genCmdTerrain.GetCmd();
|
|
if (cmd != null)
|
|
{
|
|
UR_CommandMgr.Instance.Add(cmd, true);
|
|
}
|
|
m_genCmdTerrain = null;
|
|
}
|
|
}
|
|
|
|
public void HideCursor()
|
|
{
|
|
BrushProjector.orthographicSize = 0f;
|
|
if (BrushProjector.transform.childCount > 0)
|
|
{
|
|
BrushProjector.transform.GetChild(0).gameObject.SetActive(false);
|
|
}
|
|
SetIsCursorOverSomething(false);
|
|
}
|
|
|
|
public void ResetToDefaultOrDestroyTerrain()
|
|
{
|
|
if (m_terrain != null)
|
|
{
|
|
if (m_defaultTerrainDataPrefab != null)
|
|
{
|
|
TerrainData defaultTerrainDataDeepCopy = GetDefaultTerrainDataDeepCopy();
|
|
RecycleTerrain(defaultTerrainDataDeepCopy, false);
|
|
m_terrain.gameObject.layer = TERRAIN_LAYER;
|
|
m_terrain.transform.position = new Vector3((0f - defaultTerrainDataDeepCopy.size.x) * 0.5f, m_terrain.transform.position.y, (0f - defaultTerrainDataDeepCopy.size.z) * 0.5f);
|
|
}
|
|
else
|
|
{
|
|
Object.Destroy(m_terrain.gameObject);
|
|
RemoveTerrainManager();
|
|
SetIsCursorOverSomething(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RecycleTerrain(TerrainData p_data, bool p_isDefaultTerrainDataApplied)
|
|
{
|
|
if (m_terrain != null)
|
|
{
|
|
if (p_isDefaultTerrainDataApplied && m_defaultTerrainDataPrefab != null)
|
|
{
|
|
ApplyDefaultTerrainData(p_data);
|
|
if (p_data.treePrototypes.Length > 0)
|
|
{
|
|
p_data.SetHeights(0, 0, p_data.GetHeights(0, 0, 0, 0));
|
|
}
|
|
}
|
|
m_terrain.enabled = false;
|
|
Object.Destroy(m_terrain.terrainData);
|
|
m_terrain.terrainData = p_data;
|
|
if (m_terrain.GetComponent<TerrainCollider>() != null)
|
|
{
|
|
m_terrain.GetComponent<TerrainCollider>().terrainData = p_data;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("LE_GUI3dTerrain: RecycleTerrain: the CustomDefaultTerrain assigned to LE_ConfigTerrain must have a collider!");
|
|
}
|
|
m_terrain.Flush();
|
|
m_terrain.enabled = true;
|
|
TT_Terrain9Patch component = m_terrain.GetComponent<TT_Terrain9Patch>();
|
|
if (component != null)
|
|
{
|
|
component.CrashCheck();
|
|
}
|
|
Terrain terrain = m_terrain;
|
|
RemoveTerrainManager();
|
|
SetTerrain(terrain);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("LE_GUI3dTerrain: RecycleTerrain: there is not terrain that can be recycled. Call this function only if TerrainInstance is not null!");
|
|
}
|
|
}
|
|
|
|
public TerrainData GetDefaultTerrainDataDeepCopy()
|
|
{
|
|
if (m_defaultTerrainDataPrefab != null)
|
|
{
|
|
TerrainData terrainData = new TerrainData();
|
|
terrainData.name = m_defaultTerrainDataPrefab.name + "(DeepCopy)";
|
|
terrainData.size = m_defaultTerrainDataPrefab.size;
|
|
ApplyDefaultTerrainData(terrainData);
|
|
terrainData.splatPrototypes = m_defaultTerrainDataPrefab.splatPrototypes;
|
|
terrainData.alphamapResolution = m_defaultTerrainDataPrefab.alphamapResolution;
|
|
terrainData.heightmapResolution = m_defaultTerrainDataPrefab.heightmapResolution;
|
|
terrainData.SetAlphamaps(0, 0, m_defaultTerrainDataPrefab.GetAlphamaps(0, 0, m_defaultTerrainDataPrefab.alphamapWidth, m_defaultTerrainDataPrefab.alphamapHeight));
|
|
terrainData.SetHeights(0, 0, m_defaultTerrainDataPrefab.GetHeights(0, 0, terrainData.heightmapWidth, terrainData.heightmapHeight));
|
|
terrainData.size = m_defaultTerrainDataPrefab.size;
|
|
return terrainData;
|
|
}
|
|
Debug.LogError("LE_GUI3dTerrain: GetDefaultTerrainDataDeepCopy: there is no default terrain assigned! Check that DefaultTerrainDataPrefab is not null before calling this function!");
|
|
return null;
|
|
}
|
|
|
|
private void ApplyDefaultTerrainData(TerrainData p_terrainData)
|
|
{
|
|
p_terrainData.hideFlags = m_defaultTerrainDataPrefab.hideFlags;
|
|
p_terrainData.detailPrototypes = m_defaultTerrainDataPrefab.detailPrototypes;
|
|
p_terrainData.treePrototypes = m_defaultTerrainDataPrefab.treePrototypes;
|
|
p_terrainData.treeInstances = m_defaultTerrainDataPrefab.treeInstances;
|
|
p_terrainData.wavingGrassAmount = m_defaultTerrainDataPrefab.wavingGrassAmount;
|
|
p_terrainData.wavingGrassSpeed = m_defaultTerrainDataPrefab.wavingGrassSpeed;
|
|
p_terrainData.wavingGrassStrength = m_defaultTerrainDataPrefab.wavingGrassStrength;
|
|
p_terrainData.wavingGrassTint = m_defaultTerrainDataPrefab.wavingGrassTint;
|
|
p_terrainData.baseMapResolution = m_defaultTerrainDataPrefab.baseMapResolution;
|
|
p_terrainData.SetDetailResolution(m_defaultTerrainDataPrefab.detailResolution, 8);
|
|
if (m_defaultTerrainDataPrefab.detailResolution > 0)
|
|
{
|
|
int[] supportedLayers = m_defaultTerrainDataPrefab.GetSupportedLayers(0, 0, m_defaultTerrainDataPrefab.detailWidth, m_defaultTerrainDataPrefab.detailHeight);
|
|
for (int i = 0; i < supportedLayers.Length; i++)
|
|
{
|
|
p_terrainData.SetDetailLayer(0, 0, supportedLayers[i], m_defaultTerrainDataPrefab.GetDetailLayer(0, 0, m_defaultTerrainDataPrefab.detailWidth, m_defaultTerrainDataPrefab.detailHeight, supportedLayers[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (m_brushProjector == null)
|
|
{
|
|
Debug.LogError("LE_GUI3dTerrain: m_brushProjector was not initialized!");
|
|
}
|
|
if (m_brushAlphaTexture == null)
|
|
{
|
|
Debug.LogError("LE_GUI3dTerrain: m_brushAlphaTexture was not initialized!");
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateBrushProjector(base.IsCursorOverSomething, m_cursorHitInfo);
|
|
}
|
|
|
|
private void UpdateBrushProjector(bool p_isHit, RaycastHit p_hitInfo)
|
|
{
|
|
if (p_isHit && m_terrainMgr != null)
|
|
{
|
|
Vector3 size = m_terrainMgr.TerrainData.size;
|
|
Vector2 relativeLocalLocation = GetRelativeLocalLocation(p_hitInfo);
|
|
BrushProjector.transform.position = new Vector3(p_hitInfo.transform.position.x + relativeLocalLocation.y * size.x, p_hitInfo.point.y, p_hitInfo.transform.position.z + relativeLocalLocation.x * size.z);
|
|
BrushProjector.orthographicSize = m_terrainMgr.TerrainData.size.z * m_size * 0.5f;
|
|
BrushProjector.aspectRatio = m_terrainMgr.TerrainData.size.x / m_terrainMgr.TerrainData.size.z;
|
|
if (BrushProjector.transform.childCount > 0)
|
|
{
|
|
BrushProjector.transform.GetChild(0).gameObject.SetActive(m_editMode == LE_ETerrainEditMode.SMOOTH_HEIGHT && IsDirectedSmooth);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BrushProjector.orthographicSize = 0f;
|
|
if (BrushProjector.transform.childCount > 0)
|
|
{
|
|
BrushProjector.transform.GetChild(0).gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private Vector2 GetRelativeLocalLocation(RaycastHit hit)
|
|
{
|
|
Terrain component = hit.transform.gameObject.GetComponent<Terrain>();
|
|
Vector3 size = component.terrainData.size;
|
|
Vector3 vector = hit.transform.InverseTransformPoint(hit.point);
|
|
Vector2 zero = Vector2.zero;
|
|
zero.x = vector.z / size.z;
|
|
zero.y = vector.x / size.x;
|
|
float num = 1f / (float)(component.terrainData.heightmapWidth - 1);
|
|
float num2 = 1f / (float)(component.terrainData.heightmapHeight - 1);
|
|
zero.x = Mathf.Round(zero.x / num) * num;
|
|
zero.y = Mathf.Round(zero.y / num2) * num2;
|
|
return zero;
|
|
}
|
|
}
|
|
}
|