summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Editor/RecastMeshObjEditor.cs
blob: b911dc29a7accec4bb776b97c37e5324ceaf2fa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using UnityEngine;
using UnityEditor;

namespace Pathfinding {
	[CustomEditor(typeof(RecastMeshObj))]
	[CanEditMultipleObjects]
	public class RecastMeshObjEditor : EditorBase {
		protected override void Inspector () {
			var modeProp = FindProperty("mode");
			var areaProp = FindProperty("surfaceID");
			var geometrySource = FindProperty("geometrySource");
			var includeInScan = FindProperty("includeInScan");
			var script = target as RecastMeshObj;

			if (areaProp.intValue < 0) {
				areaProp.intValue = 0;
			}

			PropertyField(includeInScan);
			if (!includeInScan.hasMultipleDifferentValues && script.includeInScan == RecastMeshObj.ScanInclusion.AlwaysExclude) {
				EditorGUILayout.HelpBox("This object will be completely ignored by the graph. Even if it would otherwise be included due to its layer or tag.", MessageType.None);
				return;
			}

			PropertyField(geometrySource, "Geometry Source");
			if (!geometrySource.hasMultipleDifferentValues) {
				var geometrySourceValue = (RecastMeshObj.GeometrySource)geometrySource.intValue;
				script.ResolveMeshSource(out var filter, out var coll, out var coll2D);
				switch (geometrySourceValue) {
				case RecastMeshObj.GeometrySource.Auto:
					if (filter != null) {
						EditorGUILayout.HelpBox("Using the attached MeshFilter as a source", MessageType.None);
						if (script.GetComponent<MeshRenderer>() == null) {
							EditorGUILayout.HelpBox("When a MeshFilter is used as a geometry source, a MeshRenderer must also be attached", MessageType.Error);
						}
					} else if (coll != null) {
						EditorGUILayout.HelpBox("Using the attached collider as a source", MessageType.None);
					} else if (coll2D != null) {
						EditorGUILayout.HelpBox("Using the attached 2D collider as a source", MessageType.None);
					} else {
						EditorGUILayout.HelpBox("No MeshFilter or MeshCollider found on this GameObject", MessageType.Error);
					}
					break;
				case RecastMeshObj.GeometrySource.MeshFilter:
					if (filter == null) {
						EditorGUILayout.HelpBox("No MeshFilter found on this GameObject", MessageType.Error);
					} else if (script.GetComponent<MeshRenderer>() == null) {
						EditorGUILayout.HelpBox("When a MeshFilter is used as a geometry source, a MeshRenderer must also be attached", MessageType.Error);
					}
					break;
				case RecastMeshObj.GeometrySource.Collider:
					if (coll == null && coll2D == null) {
						EditorGUILayout.HelpBox("No collider found on this GameObject", MessageType.Error);
					}
					break;
				}
			}

			PropertyField(modeProp, "Surface Type");
			// Note: uses intValue instead of enumValueIndex because the enum does not start from 0.
			var mode = (RecastMeshObj.Mode)modeProp.intValue;
			if (!modeProp.hasMultipleDifferentValues) {
				switch (mode) {
				case RecastMeshObj.Mode.UnwalkableSurface:
					EditorGUILayout.HelpBox("All surfaces on this mesh will be made unwalkable", MessageType.None);
					break;
				case RecastMeshObj.Mode.WalkableSurface:
					EditorGUILayout.HelpBox("All surfaces on this mesh will be walkable", MessageType.None);
					break;
				case RecastMeshObj.Mode.WalkableSurfaceWithSeam:
					EditorGUILayout.HelpBox("All surfaces on this mesh will be walkable and a " +
						"seam will be created between the surfaces on this mesh and the surfaces on other meshes (with a different surface id)", MessageType.None);
					EditorGUI.indentLevel++;
					PropertyField(areaProp, "Surface ID");
					if (!areaProp.hasMultipleDifferentValues && areaProp.intValue < 0) {
						areaProp.intValue = 0;
					}
					EditorGUI.indentLevel--;
					break;
				case RecastMeshObj.Mode.WalkableSurfaceWithTag:
					EditorGUILayout.HelpBox("All surfaces on this mesh will be walkable and the given tag will be applied to them", MessageType.None);
					EditorGUI.indentLevel++;

					EditorGUI.showMixedValue = areaProp.hasMultipleDifferentValues;
					EditorGUI.BeginChangeCheck();
					var newTag = Util.EditorGUILayoutHelper.TagField(new GUIContent("Tag Value"), areaProp.intValue, AstarPathEditor.EditTags);
					if (EditorGUI.EndChangeCheck()) {
						areaProp.intValue = newTag;
					}
					if (!areaProp.hasMultipleDifferentValues && (areaProp.intValue < 0 || areaProp.intValue > GraphNode.MaxTagIndex)) {
						areaProp.intValue = Mathf.Clamp(areaProp.intValue, 0, GraphNode.MaxTagIndex);
					}

					EditorGUI.indentLevel--;
					break;
				}
			}

			var dynamicProp = FindProperty("dynamic");
			PropertyField(dynamicProp, "Dynamic", "Setting this value to false will give better scanning performance, but you will not be able to move the object during runtime");
			if (!dynamicProp.hasMultipleDifferentValues && !dynamicProp.boolValue) {
				EditorGUILayout.HelpBox("This object must not be moved during runtime since 'dynamic' is set to false", MessageType.Info);
			}

			bool solidAlwaysEnabled = true;
			bool solidRelevant = false;
			for (int i = 0; i < targets.Length; i++) {
				script.ResolveMeshSource(out var meshSource, out var collider, out var collider2D);
				bool usesConvexCollider = collider != null && (collider is BoxCollider || collider is SphereCollider || collider is CapsuleCollider || (collider is MeshCollider mc && mc.convex));
				solidAlwaysEnabled &= usesConvexCollider;

				// If the object only has a 2D collider, the solid field doesn't affect anything
				solidRelevant |= meshSource != null || collider != null;
			}

			if (solidRelevant) {
				if (solidAlwaysEnabled) {
					// Forced solid
					EditorGUI.BeginDisabledGroup(true);
					EditorGUILayout.Toggle("Solid", true);
					EditorGUILayout.HelpBox("Convex colliders are always treated as solid", MessageType.Info);
					EditorGUI.EndDisabledGroup();
				} else {
					PropertyField("solid");
				}
			}
		}
	}
}