435 lines
13 KiB
C#
435 lines
13 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using AwesomeTechnologies.Utility;
|
|||
|
using AwesomeTechnologies.Utility.Extentions;
|
|||
|
#if UNITY_EDITOR
|
|||
|
using UnityEditor;
|
|||
|
#endif
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
namespace AwesomeTechnologies
|
|||
|
{
|
|||
|
[System.Serializable]
|
|||
|
public class Node
|
|||
|
{
|
|||
|
public bool Selected;
|
|||
|
public Vector3 Position;
|
|||
|
public bool OverrideWidth;
|
|||
|
public float CustomWidth = 2f;
|
|||
|
public bool Active = true;
|
|||
|
}
|
|||
|
|
|||
|
[ExecuteInEditMode]
|
|||
|
public class VegetationMask : MonoBehaviour
|
|||
|
{
|
|||
|
public bool RemoveGrass = true;
|
|||
|
public bool RemovePlants = true;
|
|||
|
public bool RemoveTrees = true;
|
|||
|
public bool RemoveObjects = true;
|
|||
|
public bool RemoveLargeObjects = true;
|
|||
|
public float AdditionalGrassPerimiter;
|
|||
|
public float AdditionalPlantPerimiter;
|
|||
|
public float AdditionalTreePerimiter;
|
|||
|
public float AdditionalObjectPerimiter;
|
|||
|
public float AdditionalLargeObjectPerimiter;
|
|||
|
|
|||
|
public float AdditionalGrassPerimiterMax;
|
|||
|
public float AdditionalPlantPerimiterMax;
|
|||
|
public float AdditionalTreePerimiterMax;
|
|||
|
public float AdditionalObjectPerimiterMax;
|
|||
|
public float AdditionalLargeObjectPerimiterMax;
|
|||
|
|
|||
|
public float NoiseScaleGrass = 5;
|
|||
|
public float NoiseScalePlant = 5;
|
|||
|
public float NoiseScaleTree = 5;
|
|||
|
public float NoiseScaleObject = 5;
|
|||
|
public float NoiseScaleLargeObject = 5;
|
|||
|
|
|||
|
public string Id;
|
|||
|
|
|||
|
public bool IncludeVegetationType;
|
|||
|
|
|||
|
public List<Node> Nodes = new List<Node>();
|
|||
|
public bool ClosedArea = true;
|
|||
|
public bool ShowArea = true;
|
|||
|
public bool ShowHandles = true;
|
|||
|
public string MaskName = "";
|
|||
|
public LayerMask GroundLayerMask;
|
|||
|
|
|||
|
public List<VegetationTypeSettings> VegetationTypeList = new List<VegetationTypeSettings>();
|
|||
|
|
|||
|
private Vector3 _lastPosition;
|
|||
|
private Quaternion _lastRotation;
|
|||
|
private bool _needInit;
|
|||
|
// ReSharper disable once UnusedMember.Local
|
|||
|
void Start()
|
|||
|
{
|
|||
|
_lastPosition = transform.position;
|
|||
|
_lastRotation = transform.rotation;
|
|||
|
|
|||
|
if (Nodes.Count == 0)
|
|||
|
{
|
|||
|
CreateDefaultNodes();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ReSharper disable once Unity.RedundantEventFunction
|
|||
|
public virtual void Awake()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void AddVegetationTypes(BaseMaskArea maskArea)
|
|||
|
{
|
|||
|
for (int i = 0; i <= VegetationTypeList.Count - 1; i++)
|
|||
|
{
|
|||
|
maskArea.VegetationTypeList.Add(new VegetationTypeSettings(VegetationTypeList[i]));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ReSharper disable once UnusedMember.Local
|
|||
|
void OnEnable()
|
|||
|
{
|
|||
|
if (Id == "") Id = System.Guid.NewGuid().ToString();
|
|||
|
_needInit = true;
|
|||
|
}
|
|||
|
|
|||
|
// ReSharper disable once UnusedMember.Local
|
|||
|
void Update()
|
|||
|
{
|
|||
|
if (!Application.isPlaying)
|
|||
|
{
|
|||
|
if (_lastPosition != transform.position || _lastRotation != transform.rotation)
|
|||
|
{
|
|||
|
PositionNodes();
|
|||
|
_lastPosition = transform.position;
|
|||
|
_lastRotation = transform.rotation;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (_needInit)
|
|||
|
{
|
|||
|
_needInit = false;
|
|||
|
PositionNodes();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Reset()
|
|||
|
{
|
|||
|
if (Id == "") Id = System.Guid.NewGuid().ToString();
|
|||
|
ClearNodes();
|
|||
|
CreateDefaultNodes();
|
|||
|
}
|
|||
|
|
|||
|
void CreateDefaultNodes()
|
|||
|
{
|
|||
|
Bounds tempBounds = new Bounds(new Vector3(0,0,0), new Vector3(6f, 1f, 6f));
|
|||
|
ClearNodes();
|
|||
|
for (int i = 0; i <= 3; i++)
|
|||
|
{
|
|||
|
Node node = new Node();
|
|||
|
switch (i)
|
|||
|
{
|
|||
|
case 0:
|
|||
|
node.Position = new Vector3(tempBounds.extents.x, tempBounds.extents.y, tempBounds.extents.z);
|
|||
|
break;
|
|||
|
case 1:
|
|||
|
node.Position = new Vector3(-tempBounds.extents.x, tempBounds.extents.y, tempBounds.extents.z);
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
node.Position = new Vector3(-tempBounds.extents.x, tempBounds.extents.y, -tempBounds.extents.z);
|
|||
|
break;
|
|||
|
case 3:
|
|||
|
node.Position = new Vector3(tempBounds.extents.x, tempBounds.extents.y, -tempBounds.extents.z);
|
|||
|
break;
|
|||
|
}
|
|||
|
Nodes.Add(node);
|
|||
|
}
|
|||
|
PositionNodes();
|
|||
|
}
|
|||
|
|
|||
|
public void ClearNodes()
|
|||
|
{
|
|||
|
Nodes.Clear();
|
|||
|
}
|
|||
|
|
|||
|
public void PositionNodes()
|
|||
|
{
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
Ray ray = new Ray(transform.TransformPoint(Nodes[i].Position) + new Vector3(0, 2000f, 0), Vector3.down);
|
|||
|
var hits = Physics.RaycastAll(ray).OrderBy(h => h.distance).ToArray();
|
|||
|
for (int j = 0; j <= hits.Length - 1; j++)
|
|||
|
{
|
|||
|
if (hits[j].collider is TerrainCollider || GroundLayerMask.Contains(hits[j].collider.gameObject.layer))
|
|||
|
{
|
|||
|
Nodes[i].Position = transform.InverseTransformPoint(hits[j].point);// + new Vector3(0, 0.5f, 0));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
UpdateVegetationMask();
|
|||
|
}
|
|||
|
|
|||
|
public List<Vector3> GetWorldSpaceNodePositions()
|
|||
|
{
|
|||
|
List<Vector3> worldSpaceNodeList = new List<Vector3>();
|
|||
|
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
worldSpaceNodeList.Add(transform.TransformPoint(Nodes[i].Position));
|
|||
|
}
|
|||
|
return worldSpaceNodeList;
|
|||
|
}
|
|||
|
public virtual void UpdateVegetationMask()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteNode(Node node)
|
|||
|
{
|
|||
|
Nodes.Remove(node);
|
|||
|
}
|
|||
|
|
|||
|
public void AddNodesToEnd(Vector3[] worldPositions)
|
|||
|
{
|
|||
|
for (int i = 0; i <= worldPositions.Length - 1; i++)
|
|||
|
{
|
|||
|
AddNodeToEnd(worldPositions[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void AddNodesToEnd(Vector3[] worldPositions, float[] customWidth, bool[] active)
|
|||
|
{
|
|||
|
for (int i = 0; i <= worldPositions.Length - 1; i++)
|
|||
|
{
|
|||
|
AddNodeToEnd(worldPositions[i], customWidth[i], active[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void AddNodeToEnd(Vector3 worldPosition)
|
|||
|
{
|
|||
|
Node node = new Node {Position = transform.InverseTransformPoint(worldPosition)};
|
|||
|
Nodes.Add(node);
|
|||
|
}
|
|||
|
|
|||
|
public void AddNodeToEnd(Vector3 worldPosition, float customWidth, bool active)
|
|||
|
{
|
|||
|
Node node = new Node
|
|||
|
{
|
|||
|
Position = transform.InverseTransformPoint(worldPosition),
|
|||
|
CustomWidth = customWidth,
|
|||
|
OverrideWidth = true,
|
|||
|
Active = active
|
|||
|
};
|
|||
|
Nodes.Add(node);
|
|||
|
}
|
|||
|
|
|||
|
public void AddNode(Vector3 worldPosition)
|
|||
|
{
|
|||
|
if (Nodes.Count == 0)
|
|||
|
{
|
|||
|
AddNodeToEnd(worldPosition);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Node closestNode = FindClosestNode(worldPosition);
|
|||
|
Node nextNode = GetNextNode(closestNode);
|
|||
|
Node previousNode = GetPreviousNode(closestNode);
|
|||
|
|
|||
|
LineSegment3D nextSegment = new LineSegment3D(transform.TransformPoint(closestNode.Position), transform.TransformPoint(nextNode.Position));
|
|||
|
LineSegment3D previousSegment = new LineSegment3D(transform.TransformPoint(closestNode.Position), transform.TransformPoint(previousNode.Position));
|
|||
|
|
|||
|
float nextSegmentDistance = nextSegment.DistanceTo(worldPosition);
|
|||
|
float previousSegmentDistance = previousSegment.DistanceTo(worldPosition);
|
|||
|
|
|||
|
Node node = new Node {Position = transform.InverseTransformPoint(worldPosition)};
|
|||
|
|
|||
|
int currentNodeIndex = GetNodeIndex(closestNode);
|
|||
|
|
|||
|
if (nextSegmentDistance < previousSegmentDistance)
|
|||
|
{
|
|||
|
if (currentNodeIndex == Nodes.Count - 1)
|
|||
|
{
|
|||
|
Nodes.Add(node);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Nodes.Insert(currentNodeIndex + 1, node);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Nodes.Insert(currentNodeIndex, node);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int GetNodeIndex(Node node)
|
|||
|
{
|
|||
|
int nodeIndex = 0;
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
if (Nodes[i] == node)
|
|||
|
{
|
|||
|
nodeIndex = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return nodeIndex;
|
|||
|
}
|
|||
|
public Node GetNextNode(Node node)
|
|||
|
{
|
|||
|
int nodeIndex = 0;
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
if (Nodes[i] == node)
|
|||
|
{
|
|||
|
nodeIndex = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (nodeIndex == Nodes.Count - 1)
|
|||
|
{
|
|||
|
return Nodes[0];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return Nodes[nodeIndex + 1];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Node GetPreviousNode(Node node)
|
|||
|
{
|
|||
|
int nodeIndex = 0;
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
if (Nodes[i] == node)
|
|||
|
{
|
|||
|
nodeIndex = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (nodeIndex == 0)
|
|||
|
{
|
|||
|
return Nodes[Nodes.Count - 1];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return Nodes[nodeIndex - 1];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public Node FindClosestNode(Vector3 worldPosition)
|
|||
|
{
|
|||
|
Node returnNode = null;
|
|||
|
float smallestDistance = float.MaxValue;
|
|||
|
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
float distance = Vector3.Distance(worldPosition, transform.TransformPoint(Nodes[i].Position));
|
|||
|
if (distance < smallestDistance)
|
|||
|
{
|
|||
|
smallestDistance = distance;
|
|||
|
returnNode = Nodes[i];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return returnNode;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void DrawGizmos()
|
|||
|
{
|
|||
|
#if UNITY_EDITOR
|
|||
|
if (MaskName != "")
|
|||
|
{
|
|||
|
GUIStyle stLabel = new GUIStyle(EditorStyles.whiteLabel);
|
|||
|
Handles.Label(GetMaskCenter(), MaskName, stLabel);
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (ShowArea)
|
|||
|
{
|
|||
|
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
Gizmos.color = new Color(1f, 1f, 0, 1f);
|
|||
|
Camera sceneviewCamera = SceneViewDetector.GetCurrentSceneViewCamera();
|
|||
|
if (!sceneviewCamera) return;
|
|||
|
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
var distance = Vector3.Distance(sceneviewCamera.transform.position, transform.TransformPoint(Nodes[i].Position));
|
|||
|
|
|||
|
if (distance < 200)
|
|||
|
{
|
|||
|
|
|||
|
Gizmos.color = new Color(1f, 1f, 1f, 1f);
|
|||
|
if (Nodes[i].Selected) Gizmos.color = new Color(0, 1f, 0f, 1f);
|
|||
|
|
|||
|
Gizmos.DrawSphere(transform.TransformPoint(Nodes[i].Position), 0.015f * distance);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (Nodes.Count > 1)
|
|||
|
{
|
|||
|
for (int i = 0; i <= Nodes.Count - 1; i++)
|
|||
|
{
|
|||
|
if (i == Nodes.Count - 1)
|
|||
|
{
|
|||
|
if (ClosedArea)
|
|||
|
{
|
|||
|
Gizmos.DrawLine(transform.TransformPoint(Nodes[0].Position), transform.TransformPoint(Nodes[i].Position));
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Gizmos.DrawLine(transform.TransformPoint(Nodes[i].Position), transform.TransformPoint(Nodes[i + 1].Position));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void OnDrawGizmosSelected()
|
|||
|
{
|
|||
|
DrawGizmos();
|
|||
|
}
|
|||
|
|
|||
|
Vector3 GetMaskCenter()
|
|||
|
{
|
|||
|
List<Vector3> worldPositions = GetWorldSpaceNodePositions();
|
|||
|
return (GetMeanVector(worldPositions.ToArray()));
|
|||
|
}
|
|||
|
|
|||
|
private Vector3 GetMeanVector(Vector3[] positions)
|
|||
|
{
|
|||
|
if (positions.Length == 0)
|
|||
|
return Vector3.zero;
|
|||
|
float x = 0f;
|
|||
|
float y = 0f;
|
|||
|
float z = 0f;
|
|||
|
foreach (Vector3 pos in positions)
|
|||
|
{
|
|||
|
x += pos.x;
|
|||
|
y += pos.y;
|
|||
|
z += pos.z;
|
|||
|
}
|
|||
|
return new Vector3(x / positions.Length, y / positions.Length, z / positions.Length);
|
|||
|
}
|
|||
|
|
|||
|
// // ReSharper disable once UnusedMember.Local
|
|||
|
// void Update()
|
|||
|
// {
|
|||
|
// if (_needInit)
|
|||
|
// {
|
|||
|
// _needInit = false;
|
|||
|
// PositionNodes();
|
|||
|
// }
|
|||
|
// }
|
|||
|
}
|
|||
|
}
|