Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

DecalProjectorEditor.cs 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor.IMGUI.Controls;
  4. using UnityEditor.ShortcutManagement;
  5. using UnityEditorInternal;
  6. using UnityEngine;
  7. using UnityEngine.Rendering.Universal;
  8. using static UnityEditorInternal.EditMode;
  9. namespace UnityEditor.Rendering.Universal
  10. {
  11. [CustomEditor(typeof(DecalProjector))]
  12. [CanEditMultipleObjects]
  13. partial class DecalProjectorEditor : Editor
  14. {
  15. const float k_Limit = 100000f;
  16. const float k_LimitInv = 1f / k_Limit;
  17. static Color fullColor
  18. {
  19. get
  20. {
  21. Color c = s_LastColor;
  22. c.a = 1f;
  23. return c;
  24. }
  25. }
  26. static Color s_LastColor;
  27. static void UpdateColorsInHandlesIfRequired()
  28. {
  29. Color c = DecalPreferences.decalGizmoColor;
  30. if (c != s_LastColor)
  31. {
  32. if (s_BoxHandle != null && !s_BoxHandle.Equals(null))
  33. s_BoxHandle = null;
  34. if (s_uvHandles != null && !s_uvHandles.Equals(null))
  35. s_uvHandles.baseColor = c;
  36. s_LastColor = c;
  37. }
  38. }
  39. MaterialEditor m_MaterialEditor = null;
  40. SerializedProperty m_MaterialProperty;
  41. SerializedProperty m_DrawDistanceProperty;
  42. SerializedProperty m_FadeScaleProperty;
  43. SerializedProperty m_StartAngleFadeProperty;
  44. SerializedProperty m_EndAngleFadeProperty;
  45. SerializedProperty m_UVScaleProperty;
  46. SerializedProperty m_UVBiasProperty;
  47. SerializedProperty m_ScaleMode;
  48. SerializedProperty m_Size;
  49. SerializedProperty[] m_SizeValues;
  50. SerializedProperty m_Offset;
  51. SerializedProperty[] m_OffsetValues;
  52. SerializedProperty m_FadeFactor;
  53. SerializedProperty m_RenderingLayerMask;
  54. int layerMask => (target as Component).gameObject.layer;
  55. bool layerMaskHasMultipleValues
  56. {
  57. get
  58. {
  59. if (targets.Length < 2)
  60. return false;
  61. int layerMask = (targets[0] as Component).gameObject.layer;
  62. for (int index = 1; index < targets.Length; ++index)
  63. {
  64. if ((targets[index] as Component).gameObject.layer != layerMask)
  65. return true;
  66. }
  67. return false;
  68. }
  69. }
  70. static HierarchicalBox s_BoxHandle;
  71. static HierarchicalBox boxHandle
  72. {
  73. get
  74. {
  75. if (s_BoxHandle == null || s_BoxHandle.Equals(null))
  76. {
  77. Color c = fullColor;
  78. s_BoxHandle = new HierarchicalBox(s_LastColor, new[] { c, c, c, c, c, c });
  79. s_BoxHandle.SetBaseColor(s_LastColor);
  80. s_BoxHandle.monoHandle = false;
  81. }
  82. return s_BoxHandle;
  83. }
  84. }
  85. static DisplacableRectHandles s_uvHandles;
  86. static DisplacableRectHandles uvHandles
  87. {
  88. get
  89. {
  90. if (s_uvHandles == null || s_uvHandles.Equals(null))
  91. s_uvHandles = new DisplacableRectHandles(s_LastColor);
  92. return s_uvHandles;
  93. }
  94. }
  95. static readonly BoxBoundsHandle s_AreaLightHandle =
  96. new BoxBoundsHandle { axes = PrimitiveBoundsHandle.Axes.X | PrimitiveBoundsHandle.Axes.Y };
  97. const SceneViewEditMode k_EditShapeWithoutPreservingUV = (SceneViewEditMode)90;
  98. const SceneViewEditMode k_EditShapePreservingUV = (SceneViewEditMode)91;
  99. const SceneViewEditMode k_EditUVAndPivot = (SceneViewEditMode)92;
  100. static readonly SceneViewEditMode[] k_EditVolumeModes = new SceneViewEditMode[]
  101. {
  102. k_EditShapeWithoutPreservingUV,
  103. k_EditShapePreservingUV,
  104. k_EditUVAndPivot,
  105. };
  106. static Func<Vector3, Quaternion, Vector3> s_DrawPivotHandle;
  107. static GUIContent[] k_EditVolumeLabels = null;
  108. static GUIContent[] editVolumeLabels => k_EditVolumeLabels ?? (k_EditVolumeLabels = new GUIContent[]
  109. {
  110. EditorGUIUtility.TrIconContent("d_ScaleTool", k_EditShapeWithoutPreservingUVTooltip),
  111. EditorGUIUtility.TrIconContent("d_RectTool", k_EditShapePreservingUVTooltip),
  112. EditorGUIUtility.TrIconContent("d_MoveTool", k_EditUVTooltip),
  113. });
  114. static List<DecalProjectorEditor> s_Instances = new List<DecalProjectorEditor>();
  115. static DecalProjectorEditor FindEditorFromSelection()
  116. {
  117. GameObject[] selection = Selection.gameObjects;
  118. DecalProjector[] selectionTargets = Selection.GetFiltered<DecalProjector>(SelectionMode.Unfiltered);
  119. foreach (DecalProjectorEditor editor in s_Instances)
  120. {
  121. if (selectionTargets.Length != editor.targets.Length)
  122. continue;
  123. bool allOk = true;
  124. foreach (DecalProjector selectionTarget in selectionTargets)
  125. {
  126. if (!Array.Find(editor.targets, t => t == selectionTarget))
  127. {
  128. allOk = false;
  129. break;
  130. }
  131. }
  132. if (!allOk)
  133. continue;
  134. return editor;
  135. }
  136. return null;
  137. }
  138. private void OnEnable()
  139. {
  140. s_Instances.Add(this);
  141. // Create an instance of the MaterialEditor
  142. UpdateMaterialEditor();
  143. // Fetch serialized properties
  144. m_MaterialProperty = serializedObject.FindProperty("m_Material");
  145. m_DrawDistanceProperty = serializedObject.FindProperty("m_DrawDistance");
  146. m_FadeScaleProperty = serializedObject.FindProperty("m_FadeScale");
  147. m_StartAngleFadeProperty = serializedObject.FindProperty("m_StartAngleFade");
  148. m_EndAngleFadeProperty = serializedObject.FindProperty("m_EndAngleFade");
  149. m_UVScaleProperty = serializedObject.FindProperty("m_UVScale");
  150. m_UVBiasProperty = serializedObject.FindProperty("m_UVBias");
  151. m_ScaleMode = serializedObject.FindProperty("m_ScaleMode");
  152. m_Size = serializedObject.FindProperty("m_Size");
  153. m_SizeValues = new[]
  154. {
  155. m_Size.FindPropertyRelative("x"),
  156. m_Size.FindPropertyRelative("y"),
  157. m_Size.FindPropertyRelative("z"),
  158. };
  159. m_Offset = serializedObject.FindProperty("m_Offset");
  160. m_OffsetValues = new[]
  161. {
  162. m_Offset.FindPropertyRelative("x"),
  163. m_Offset.FindPropertyRelative("y"),
  164. m_Offset.FindPropertyRelative("z"),
  165. };
  166. m_FadeFactor = serializedObject.FindProperty("m_FadeFactor");
  167. m_RenderingLayerMask = serializedObject.FindProperty("m_DecalLayerMask");
  168. ReinitSavedRatioSizePivotPosition();
  169. }
  170. private void OnDisable()
  171. {
  172. s_Instances.Remove(this);
  173. }
  174. private void OnDestroy() =>
  175. DestroyImmediate(m_MaterialEditor);
  176. public bool HasFrameBounds()
  177. {
  178. return true;
  179. }
  180. public Bounds OnGetFrameBounds()
  181. {
  182. DecalProjector decalProjector = target as DecalProjector;
  183. return new Bounds(decalProjector.transform.position, boxHandle.size);
  184. }
  185. public void UpdateMaterialEditor()
  186. {
  187. int validMaterialsCount = 0;
  188. for (int index = 0; index < targets.Length; ++index)
  189. {
  190. DecalProjector decalProjector = (targets[index] as DecalProjector);
  191. if ((decalProjector != null) && (decalProjector.material != null))
  192. validMaterialsCount++;
  193. }
  194. // Update material editor with the new material
  195. UnityEngine.Object[] materials = new UnityEngine.Object[validMaterialsCount];
  196. validMaterialsCount = 0;
  197. for (int index = 0; index < targets.Length; ++index)
  198. {
  199. DecalProjector decalProjector = (targets[index] as DecalProjector);
  200. if ((decalProjector != null) && (decalProjector.material != null))
  201. materials[validMaterialsCount++] = (targets[index] as DecalProjector).material;
  202. }
  203. m_MaterialEditor = (MaterialEditor)CreateEditor(materials);
  204. }
  205. void OnSceneGUI()
  206. {
  207. //called on each targets
  208. DrawHandles();
  209. }
  210. void DrawBoxTransformationHandles(DecalProjector decalProjector)
  211. {
  212. Vector3 scale = decalProjector.effectiveScale;
  213. using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, scale)))
  214. {
  215. Vector3 centerStart = decalProjector.pivot;
  216. boxHandle.center = centerStart;
  217. boxHandle.size = decalProjector.size;
  218. Vector3 boundsSizePreviousOS = boxHandle.size;
  219. Vector3 boundsMinPreviousOS = boxHandle.size * -0.5f + boxHandle.center;
  220. EditorGUI.BeginChangeCheck();
  221. boxHandle.DrawHandle();
  222. if (EditorGUI.EndChangeCheck())
  223. {
  224. // Adjust decal transform if handle changed.
  225. Undo.RecordObject(decalProjector, "Decal Projector Change");
  226. bool xChangeIsValid = scale.x != 0f;
  227. bool yChangeIsValid = scale.y != 0f;
  228. bool zChangeIsValid = scale.z != 0f;
  229. // Preserve serialized state for axes with scale 0.
  230. decalProjector.size = new Vector3(
  231. xChangeIsValid ? boxHandle.size.x : decalProjector.size.x,
  232. yChangeIsValid ? boxHandle.size.y : decalProjector.size.y,
  233. zChangeIsValid ? boxHandle.size.z : decalProjector.size.z);
  234. decalProjector.pivot = new Vector3(
  235. xChangeIsValid ? boxHandle.center.x : decalProjector.pivot.x,
  236. yChangeIsValid ? boxHandle.center.y : decalProjector.pivot.y,
  237. zChangeIsValid ? boxHandle.center.z : decalProjector.pivot.z);
  238. Vector3 boundsSizeCurrentOS = boxHandle.size;
  239. Vector3 boundsMinCurrentOS = boxHandle.size * -0.5f + boxHandle.center;
  240. if (editMode == k_EditShapePreservingUV)
  241. {
  242. // Treat decal projector bounds as a crop tool, rather than a scale tool.
  243. // Compute a new uv scale and bias terms to pin decal projection pixels in world space, irrespective of projector bounds.
  244. // Preserve serialized state for axes with scale 0.
  245. Vector2 uvScale = decalProjector.uvScale;
  246. Vector2 uvBias = decalProjector.uvBias;
  247. if (xChangeIsValid)
  248. {
  249. uvScale.x *= Mathf.Max(k_LimitInv, boundsSizeCurrentOS.x) / Mathf.Max(k_LimitInv, boundsSizePreviousOS.x);
  250. uvBias.x += (boundsMinCurrentOS.x - boundsMinPreviousOS.x) / Mathf.Max(k_LimitInv, boundsSizeCurrentOS.x) * uvScale.x;
  251. }
  252. if (yChangeIsValid)
  253. {
  254. uvScale.y *= Mathf.Max(k_LimitInv, boundsSizeCurrentOS.y) / Mathf.Max(k_LimitInv, boundsSizePreviousOS.y);
  255. uvBias.y += (boundsMinCurrentOS.y - boundsMinPreviousOS.y) / Mathf.Max(k_LimitInv, boundsSizeCurrentOS.y) * uvScale.y;
  256. }
  257. decalProjector.uvScale = uvScale;
  258. decalProjector.uvBias = uvBias;
  259. }
  260. if (PrefabUtility.IsPartOfNonAssetPrefabInstance(decalProjector))
  261. {
  262. PrefabUtility.RecordPrefabInstancePropertyModifications(decalProjector);
  263. }
  264. }
  265. }
  266. }
  267. void DrawPivotHandles(DecalProjector decalProjector)
  268. {
  269. Vector3 scale = decalProjector.effectiveScale;
  270. Vector3 scaledPivot = Vector3.Scale(decalProjector.pivot, scale);
  271. Vector3 scaledSize = Vector3.Scale(decalProjector.size, scale);
  272. using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(Vector3.zero, decalProjector.transform.rotation, Vector3.one)))
  273. {
  274. EditorGUI.BeginChangeCheck();
  275. Vector3 newPosition = ProjectedTransform.DrawHandles(decalProjector.transform.position, .5f * scaledSize.z - scaledPivot.z, decalProjector.transform.rotation);
  276. if (EditorGUI.EndChangeCheck())
  277. {
  278. Undo.RecordObjects(new UnityEngine.Object[] { decalProjector, decalProjector.transform }, "Decal Projector Change");
  279. scaledPivot += Quaternion.Inverse(decalProjector.transform.rotation) * (decalProjector.transform.position - newPosition);
  280. decalProjector.pivot = new Vector3(
  281. scale.x != 0f ? scaledPivot.x / scale.x : decalProjector.pivot.x,
  282. scale.y != 0f ? scaledPivot.y / scale.y : decalProjector.pivot.y,
  283. scale.z != 0f ? scaledPivot.z / scale.z : decalProjector.pivot.z);
  284. decalProjector.transform.position = newPosition;
  285. ReinitSavedRatioSizePivotPosition();
  286. }
  287. }
  288. }
  289. void DrawUVHandles(DecalProjector decalProjector)
  290. {
  291. Vector3 scale = decalProjector.effectiveScale;
  292. Vector3 scaledPivot = Vector3.Scale(decalProjector.pivot, scale);
  293. Vector3 scaledSize = Vector3.Scale(decalProjector.size, scale);
  294. using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position + decalProjector.transform.rotation * (scaledPivot - .5f * scaledSize), decalProjector.transform.rotation, scale)))
  295. {
  296. Vector2 uvScale = decalProjector.uvScale;
  297. Vector2 uvBias = decalProjector.uvBias;
  298. Vector2 uvSize = new Vector2(
  299. (uvScale.x > k_Limit || uvScale.x < -k_Limit) ? 0f : decalProjector.size.x / uvScale.x,
  300. (uvScale.y > k_Limit || uvScale.y < -k_Limit) ? 0f : decalProjector.size.y / uvScale.y
  301. );
  302. Vector2 uvCenter = uvSize * .5f - new Vector2(uvBias.x * uvSize.x, uvBias.y * uvSize.y);
  303. uvHandles.center = uvCenter;
  304. uvHandles.size = uvSize;
  305. EditorGUI.BeginChangeCheck();
  306. uvHandles.DrawHandle();
  307. if (EditorGUI.EndChangeCheck())
  308. {
  309. Undo.RecordObject(decalProjector, "Decal Projector Change");
  310. for (int channel = 0; channel < 2; channel++)
  311. {
  312. // Preserve serialized state for axes with the scaled size 0.
  313. if (scaledSize[channel] != 0f)
  314. {
  315. float handleSize = uvHandles.size[channel];
  316. float minusNewUVStart = .5f * handleSize - uvHandles.center[channel];
  317. float decalSize = decalProjector.size[channel];
  318. float limit = k_LimitInv * decalSize;
  319. if (handleSize > limit || handleSize < -limit)
  320. {
  321. uvScale[channel] = decalSize / handleSize;
  322. uvBias[channel] = minusNewUVStart / handleSize;
  323. }
  324. else
  325. {
  326. // TODO: Decide if uvHandles.size should ever have negative value. It can't currently.
  327. uvScale[channel] = k_Limit * Mathf.Sign(handleSize);
  328. uvBias[channel] = k_Limit * minusNewUVStart / decalSize;
  329. }
  330. }
  331. }
  332. decalProjector.uvScale = uvScale;
  333. decalProjector.uvBias = uvBias;
  334. }
  335. }
  336. }
  337. void DrawHandles()
  338. {
  339. DecalProjector decalProjector = target as DecalProjector;
  340. if (editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV)
  341. DrawBoxTransformationHandles(decalProjector);
  342. else if (editMode == k_EditUVAndPivot)
  343. {
  344. DrawPivotHandles(decalProjector);
  345. DrawUVHandles(decalProjector);
  346. }
  347. }
  348. [DrawGizmo(GizmoType.Selected | GizmoType.Active)]
  349. static void DrawGizmosSelected(DecalProjector decalProjector, GizmoType gizmoType)
  350. {
  351. float lod = Gizmos.CalculateLOD(decalProjector.transform.position, decalProjector.size.magnitude * 0.25f);
  352. // skip drawing anything if it will be too small or behind the camera on screen
  353. if (lod < 0.1f)
  354. return;
  355. UpdateColorsInHandlesIfRequired();
  356. const float k_DotLength = 5f;
  357. // Draw them with scale applied to size and pivot instead of the matrix to keep the proportions of the arrow and lines.
  358. using (new Handles.DrawingScope(fullColor, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
  359. {
  360. Vector3 scale = decalProjector.effectiveScale;
  361. Vector3 scaledPivot = Vector3.Scale(decalProjector.pivot, scale);
  362. Vector3 scaledSize = Vector3.Scale(decalProjector.size, scale);
  363. boxHandle.center = scaledPivot;
  364. boxHandle.size = scaledSize;
  365. bool isVolumeEditMode = editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV;
  366. bool isPivotEditMode = editMode == k_EditUVAndPivot;
  367. if (lod > 0.5f)
  368. {
  369. boxHandle.DrawHull(isVolumeEditMode);
  370. }
  371. else
  372. Handles.DrawWireCube(scaledPivot, scaledSize); // simplify the drawing if too small on screen
  373. if (lod == 1.0f) // only draw when big enough on screen to be useable
  374. {
  375. Vector3 pivot = Vector3.zero;
  376. Vector3 projectedPivot = new Vector3(0, 0, scaledPivot.z - .5f * scaledSize.z);
  377. if (isPivotEditMode)
  378. {
  379. Handles.DrawDottedLines(new[] { projectedPivot, pivot }, k_DotLength);
  380. }
  381. else
  382. {
  383. float arrowSize = scaledSize.z * 0.25f;
  384. Handles.ArrowHandleCap(0, projectedPivot, Quaternion.identity, arrowSize, EventType.Repaint);
  385. }
  386. //draw UV and bolder edges
  387. using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position + decalProjector.transform.rotation * new Vector3(scaledPivot.x, scaledPivot.y, scaledPivot.z - .5f * scaledSize.z), decalProjector.transform.rotation, Vector3.one)))
  388. {
  389. Vector2 UVSize = new Vector2(
  390. (decalProjector.uvScale.x > k_Limit || decalProjector.uvScale.x < -k_Limit) ? 0f : scaledSize.x / decalProjector.uvScale.x,
  391. (decalProjector.uvScale.y > k_Limit || decalProjector.uvScale.y < -k_Limit) ? 0f : scaledSize.y / decalProjector.uvScale.y
  392. );
  393. Vector2 UVCenter = UVSize * .5f - new Vector2(decalProjector.uvBias.x * UVSize.x, decalProjector.uvBias.y * UVSize.y) - (Vector2)scaledSize * .5f;
  394. uvHandles.center = UVCenter;
  395. uvHandles.size = UVSize;
  396. uvHandles.DrawRect(dottedLine: true, screenSpaceSize: k_DotLength);
  397. uvHandles.center = default;
  398. uvHandles.size = scaledSize;
  399. uvHandles.DrawRect(dottedLine: false, thickness: 3f);
  400. }
  401. }
  402. }
  403. }
  404. static Func<Bounds> GetBoundsGetter(DecalProjector decalProjector)
  405. {
  406. return () =>
  407. {
  408. var bounds = new Bounds();
  409. var decalTransform = decalProjector.transform;
  410. bounds.Encapsulate(decalTransform.position);
  411. return bounds;
  412. };
  413. }
  414. // Temporarily save ratio between size and pivot position while editing in inspector.
  415. // null or NaN is used to say that there is no saved ratio.
  416. // Aim is to keep proportion while sliding the value to 0 in Inspector and then go back to something else.
  417. // Current solution only works for the life of this editor, but is enough in most cases.
  418. // Which means if you go to there, selection something else and go back on it, pivot position is thus null.
  419. Dictionary<DecalProjector, Vector3> ratioSizePivotPositionSaved = null;
  420. void ReinitSavedRatioSizePivotPosition()
  421. {
  422. ratioSizePivotPositionSaved = null;
  423. }
  424. void UpdateSize(int axe, float newSize)
  425. {
  426. void UpdateSizeOfOneTarget(DecalProjector currentTarget)
  427. {
  428. //lazy init on demand as targets array cannot be accessed from OnSceneGUI so in edit mode.
  429. if (ratioSizePivotPositionSaved == null)
  430. {
  431. ratioSizePivotPositionSaved = new Dictionary<DecalProjector, Vector3>();
  432. foreach (DecalProjector projector in targets)
  433. ratioSizePivotPositionSaved[projector] = new Vector3(float.NaN, float.NaN, float.NaN);
  434. }
  435. // Save old ratio if not registered
  436. // Either or are NaN or no one, check only first
  437. Vector3 saved = ratioSizePivotPositionSaved[currentTarget];
  438. if (float.IsNaN(saved[axe]))
  439. {
  440. float oldSize = currentTarget.m_Size[axe];
  441. saved[axe] = Mathf.Abs(oldSize) <= Mathf.Epsilon ? 0f : currentTarget.m_Offset[axe] / oldSize;
  442. ratioSizePivotPositionSaved[currentTarget] = saved;
  443. }
  444. currentTarget.m_Size[axe] = newSize;
  445. currentTarget.m_Offset[axe] = saved[axe] * newSize;
  446. // refresh DecalProjector to update projection
  447. currentTarget.OnValidate();
  448. }
  449. // Manually register Undo as we work directly on the target
  450. Undo.RecordObjects(targets, "Change DecalProjector Size or Depth");
  451. // Apply any change on target first
  452. serializedObject.ApplyModifiedProperties();
  453. // update each target
  454. foreach (DecalProjector decalProjector in targets)
  455. {
  456. UpdateSizeOfOneTarget(decalProjector);
  457. // Fix for UUM-29105 (Changes made to Decal Project Prefab in the Inspector are not saved)
  458. // This editor doesn't use serializedObject to modify the target objects, explicitly mark the prefab
  459. // asset dirty to ensure the new data is saved.
  460. if (PrefabUtility.IsPartOfPrefabAsset(decalProjector))
  461. EditorUtility.SetDirty(decalProjector);
  462. }
  463. // update again serialize object to register change in targets
  464. serializedObject.Update();
  465. // change was not tracked by SerializeReference so force repaint the scene views and game views
  466. UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
  467. // strange: we need to force it throu serialization to update multiple differente value state (value are right but still detected as different)
  468. if (m_SizeValues[axe].hasMultipleDifferentValues)
  469. m_SizeValues[axe].floatValue = newSize;
  470. }
  471. public override void OnInspectorGUI()
  472. {
  473. serializedObject.Update();
  474. bool materialChanged = false;
  475. bool isDefaultMaterial = false;
  476. bool isValidDecalMaterial = true;
  477. bool isDecalSupported = DecalProjector.isSupported;
  478. if (!isDecalSupported)
  479. EditorUtils.FeatureHelpBox("The current renderer has no Decal Renderer Feature added.", MessageType.Warning);
  480. EditorGUI.BeginChangeCheck();
  481. {
  482. EditorGUILayout.BeginHorizontal();
  483. GUILayout.FlexibleSpace();
  484. DoInspectorToolbar(k_EditVolumeModes, editVolumeLabels, GetBoundsGetter(target as DecalProjector), this);
  485. GUILayout.FlexibleSpace();
  486. EditorGUILayout.EndHorizontal();
  487. EditorGUILayout.Space();
  488. // Info box for tools
  489. GUIStyle style = new GUIStyle(EditorStyles.miniLabel);
  490. style.richText = true;
  491. GUILayout.BeginVertical(EditorStyles.helpBox);
  492. string helpText = k_BaseSceneEditingToolText;
  493. if (EditMode.editMode == k_EditShapeWithoutPreservingUV && EditMode.IsOwner(this))
  494. helpText = k_EditShapeWithoutPreservingUVName;
  495. if (EditMode.editMode == k_EditShapePreservingUV && EditMode.IsOwner(this))
  496. helpText = k_EditShapePreservingUVName;
  497. if (EditMode.editMode == k_EditUVAndPivot && EditMode.IsOwner(this))
  498. helpText = k_EditUVAndPivotName;
  499. GUILayout.Label(helpText, style);
  500. GUILayout.EndVertical();
  501. EditorGUILayout.Space();
  502. EditorGUILayout.PropertyField(m_ScaleMode, k_ScaleMode);
  503. bool negativeScale = false;
  504. foreach (var target in targets)
  505. {
  506. var decalProjector = target as DecalProjector;
  507. float combinedScale = decalProjector.transform.lossyScale.x * decalProjector.transform.lossyScale.y * decalProjector.transform.lossyScale.z;
  508. negativeScale |= combinedScale < 0 && decalProjector.scaleMode == DecalScaleMode.InheritFromHierarchy;
  509. }
  510. if (negativeScale)
  511. {
  512. EditorGUILayout.HelpBox("Does not work with negative odd scaling (When there are odd number of scale components)", MessageType.Warning);
  513. }
  514. var widthRect = EditorGUILayout.GetControlRect();
  515. EditorGUI.BeginProperty(widthRect, k_WidthContent, m_SizeValues[0]);
  516. EditorGUI.BeginChangeCheck();
  517. float newSizeX = EditorGUI.FloatField(widthRect, k_WidthContent, m_SizeValues[0].floatValue);
  518. if (EditorGUI.EndChangeCheck())
  519. UpdateSize(0, Mathf.Max(0, newSizeX));
  520. EditorGUI.EndProperty();
  521. var heightRect = EditorGUILayout.GetControlRect();
  522. EditorGUI.BeginProperty(heightRect, k_HeightContent, m_SizeValues[1]);
  523. EditorGUI.BeginChangeCheck();
  524. float newSizeY = EditorGUI.FloatField(heightRect, k_HeightContent, m_SizeValues[1].floatValue);
  525. if (EditorGUI.EndChangeCheck())
  526. UpdateSize(1, Mathf.Max(0, newSizeY));
  527. EditorGUI.EndProperty();
  528. var projectionRect = EditorGUILayout.GetControlRect();
  529. EditorGUI.BeginProperty(projectionRect, k_ProjectionDepthContent, m_SizeValues[2]);
  530. EditorGUI.BeginChangeCheck();
  531. float newSizeZ = EditorGUI.FloatField(projectionRect, k_ProjectionDepthContent, m_SizeValues[2].floatValue);
  532. if (EditorGUI.EndChangeCheck())
  533. UpdateSize(2, Mathf.Max(0, newSizeZ));
  534. EditorGUI.EndProperty();
  535. EditorGUI.BeginChangeCheck();
  536. EditorGUILayout.PropertyField(m_Offset, k_Offset);
  537. if (EditorGUI.EndChangeCheck())
  538. ReinitSavedRatioSizePivotPosition();
  539. EditorGUILayout.Space();
  540. EditorGUI.BeginChangeCheck();
  541. EditorGUILayout.PropertyField(m_MaterialProperty, k_MaterialContent);
  542. materialChanged = EditorGUI.EndChangeCheck();
  543. EditorUtils.DrawRenderingLayerMask(m_RenderingLayerMask, k_RenderingLayerMaskContent);
  544. foreach (var target in targets)
  545. {
  546. var decalProjector = target as DecalProjector;
  547. var mat = decalProjector.material;
  548. isDefaultMaterial |= decalProjector.material == DecalProjector.defaultMaterial;
  549. isValidDecalMaterial &= decalProjector.IsValid();
  550. }
  551. if (m_MaterialEditor && !isValidDecalMaterial)
  552. {
  553. CoreEditorUtils.DrawFixMeBox("Decal only work with Decal Material. Use default material or create from decal shader graph sub target.", () =>
  554. {
  555. m_MaterialProperty.objectReferenceValue = DecalProjector.defaultMaterial;
  556. materialChanged = true;
  557. });
  558. }
  559. EditorGUI.indentLevel++;
  560. EditorGUILayout.PropertyField(m_UVScaleProperty, k_UVScaleContent);
  561. EditorGUILayout.PropertyField(m_UVBiasProperty, k_UVBiasContent);
  562. EditorGUILayout.PropertyField(m_FadeFactor, k_OpacityContent);
  563. EditorGUI.indentLevel--;
  564. bool angleFadeSupport = false;
  565. foreach (var decalProjector in targets)
  566. {
  567. var mat = (decalProjector as DecalProjector).material;
  568. if (mat == null)
  569. continue;
  570. angleFadeSupport = mat.HasProperty("_DecalAngleFadeSupported");
  571. }
  572. EditorGUI.BeginChangeCheck();
  573. EditorGUILayout.PropertyField(m_DrawDistanceProperty, k_DistanceContent);
  574. if (EditorGUI.EndChangeCheck() && m_DrawDistanceProperty.floatValue < 0f)
  575. m_DrawDistanceProperty.floatValue = 0f;
  576. EditorGUI.indentLevel++;
  577. EditorGUILayout.PropertyField(m_FadeScaleProperty, k_FadeScaleContent);
  578. EditorGUI.indentLevel--;
  579. using (new EditorGUI.DisabledScope(!angleFadeSupport))
  580. {
  581. float angleFadeMinValue = m_StartAngleFadeProperty.floatValue;
  582. float angleFadeMaxValue = m_EndAngleFadeProperty.floatValue;
  583. EditorGUI.BeginChangeCheck();
  584. EditorGUILayout.MinMaxSlider(k_AngleFadeContent, ref angleFadeMinValue, ref angleFadeMaxValue, 0.0f, 180.0f);
  585. if (EditorGUI.EndChangeCheck())
  586. {
  587. m_StartAngleFadeProperty.floatValue = angleFadeMinValue;
  588. m_EndAngleFadeProperty.floatValue = angleFadeMaxValue;
  589. }
  590. }
  591. if (!angleFadeSupport && isValidDecalMaterial)
  592. {
  593. EditorGUILayout.HelpBox($"Decal Angle Fade is not enabled in Shader. In ShaderGraph enable Angle Fade option.", MessageType.Info);
  594. }
  595. EditorGUILayout.Space();
  596. }
  597. if (EditorGUI.EndChangeCheck())
  598. serializedObject.ApplyModifiedProperties();
  599. if (materialChanged)
  600. UpdateMaterialEditor();
  601. if (layerMaskHasMultipleValues || layerMask != (target as Component).gameObject.layer)
  602. {
  603. foreach (var decalProjector in targets)
  604. {
  605. (decalProjector as DecalProjector).OnValidate();
  606. }
  607. }
  608. if (m_MaterialEditor != null)
  609. {
  610. // We need to prevent the user to edit default decal materials
  611. if (isValidDecalMaterial)
  612. {
  613. using (new DecalProjectorScope())
  614. {
  615. using (new EditorGUI.DisabledGroupScope(isDefaultMaterial))
  616. {
  617. // Draw the material's foldout and the material shader field
  618. // Required to call m_MaterialEditor.OnInspectorGUI ();
  619. m_MaterialEditor.DrawHeader();
  620. // Draw the material properties
  621. // Works only if the foldout of m_MaterialEditor.DrawHeader () is open
  622. m_MaterialEditor.OnInspectorGUI();
  623. }
  624. }
  625. }
  626. }
  627. }
  628. [Shortcut("URP/Decal: Handle changing size stretching UV", typeof(SceneView), KeyCode.Keypad1, ShortcutModifiers.Action)]
  629. static void EnterEditModeWithoutPreservingUV(ShortcutArguments args)
  630. {
  631. //If editor is not there, then the selected GameObject does not contains a DecalProjector
  632. DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent<DecalProjector>();
  633. if (activeDecalProjector == null || activeDecalProjector.Equals(null))
  634. return;
  635. ChangeEditMode(k_EditShapeWithoutPreservingUV, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
  636. }
  637. [Shortcut("URP/Decal: Handle changing size cropping UV", typeof(SceneView), KeyCode.Keypad2, ShortcutModifiers.Action)]
  638. static void EnterEditModePreservingUV(ShortcutArguments args)
  639. {
  640. //If editor is not there, then the selected GameObject does not contains a DecalProjector
  641. DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent<DecalProjector>();
  642. if (activeDecalProjector == null || activeDecalProjector.Equals(null))
  643. return;
  644. ChangeEditMode(k_EditShapePreservingUV, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
  645. }
  646. [Shortcut("URP/Decal: Handle changing pivot position and UVs", typeof(SceneView), KeyCode.Keypad3, ShortcutModifiers.Action)]
  647. static void EnterEditModePivotPreservingUV(ShortcutArguments args)
  648. {
  649. //If editor is not there, then the selected GameObject does not contains a DecalProjector
  650. DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent<DecalProjector>();
  651. if (activeDecalProjector == null || activeDecalProjector.Equals(null))
  652. return;
  653. ChangeEditMode(k_EditUVAndPivot, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
  654. }
  655. [Shortcut("URP/Decal: Handle swap between cropping and stretching UV", typeof(SceneView), KeyCode.Keypad4, ShortcutModifiers.Action)]
  656. static void SwappingEditUVMode(ShortcutArguments args)
  657. {
  658. //If editor is not there, then the selected GameObject does not contains a DecalProjector
  659. DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent<DecalProjector>();
  660. if (activeDecalProjector == null || activeDecalProjector.Equals(null))
  661. return;
  662. SceneViewEditMode targetMode = SceneViewEditMode.None;
  663. switch (editMode)
  664. {
  665. case k_EditShapePreservingUV:
  666. case k_EditUVAndPivot:
  667. targetMode = k_EditShapeWithoutPreservingUV;
  668. break;
  669. case k_EditShapeWithoutPreservingUV:
  670. targetMode = k_EditShapePreservingUV;
  671. break;
  672. }
  673. if (targetMode != SceneViewEditMode.None)
  674. ChangeEditMode(targetMode, GetBoundsGetter(activeDecalProjector)(), FindEditorFromSelection());
  675. }
  676. [Shortcut("URP/Decal: Stop Editing", typeof(SceneView), KeyCode.Keypad0, ShortcutModifiers.Action)]
  677. static void ExitEditMode(ShortcutArguments args)
  678. {
  679. //If editor is not there, then the selected GameObject does not contains a DecalProjector
  680. DecalProjector activeDecalProjector = Selection.activeGameObject?.GetComponent<DecalProjector>();
  681. if (activeDecalProjector == null || activeDecalProjector.Equals(null))
  682. return;
  683. QuitEditMode();
  684. }
  685. }
  686. }