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().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() != null) { if (m_isReadingTerrainPaintHeight) { Terrain component = m_cursorHitInfo.transform.gameObject.GetComponent(); 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() != null) { m_terrain.GetComponent().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(); 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(); 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; } } }