ReplacerManager.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. Copyright (c) 2021 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2021.
  5. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  6. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  7. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  8. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  9. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  10. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  11. THE SOFTWARE.
  12. */
  13. using UnityEngine;
  14. using System.Linq;
  15. namespace PluginMaster
  16. {
  17. #region DATA & SETTINGS
  18. [System.Serializable]
  19. public class ReplacerSettings : CircleToolBase, ISelectionBrushTool, IModifierTool, IPaintToolSettings
  20. {
  21. [SerializeField] private bool _keepTargetSize = false;
  22. [SerializeField] private bool _maintainProportions = false;
  23. public enum PositionMode { CENTER, PIVOT, ON_SURFACE }
  24. [SerializeField] private PositionMode _positionMode = PositionMode.CENTER;
  25. [SerializeField] private bool _sameParentasTarget = true;
  26. public bool keepTargetSize
  27. {
  28. get => _keepTargetSize;
  29. set
  30. {
  31. if (_keepTargetSize == value) return;
  32. _keepTargetSize = value;
  33. DataChanged();
  34. }
  35. }
  36. public bool maintainProportions
  37. {
  38. get => _maintainProportions;
  39. set
  40. {
  41. if (_maintainProportions == value) return;
  42. _maintainProportions = value;
  43. DataChanged();
  44. }
  45. }
  46. public PositionMode positionMode
  47. {
  48. get => _positionMode;
  49. set
  50. {
  51. if (_positionMode == value) return;
  52. _positionMode = value;
  53. DataChanged();
  54. }
  55. }
  56. public bool sameParentAsTarget
  57. {
  58. get => _sameParentasTarget;
  59. set
  60. {
  61. if (_sameParentasTarget == value) return;
  62. _sameParentasTarget = value;
  63. DataChanged();
  64. }
  65. }
  66. #region MODIFIER TOOL
  67. [SerializeField] private ModifierToolSettings _modifierTool = new ModifierToolSettings();
  68. public ReplacerSettings() => _modifierTool.OnDataChanged += DataChanged;
  69. public ModifierToolSettings.Command command { get => _modifierTool.command; set => _modifierTool.command = value; }
  70. public bool modifyAllButSelected
  71. {
  72. get => _modifierTool.modifyAllButSelected;
  73. set => _modifierTool.modifyAllButSelected = value;
  74. }
  75. public bool onlyTheClosest
  76. {
  77. get => _modifierTool.onlyTheClosest;
  78. set => _modifierTool.onlyTheClosest = value;
  79. }
  80. public bool outermostPrefabFilter
  81. {
  82. get => _modifierTool.outermostPrefabFilter;
  83. set => _modifierTool.outermostPrefabFilter = value;
  84. }
  85. #endregion
  86. #region PAINT TOOL
  87. [SerializeField] private PaintToolSettings _paintTool = new PaintToolSettings();
  88. public Transform parent { get => _paintTool.parent; set => _paintTool.parent = value; }
  89. public bool overwritePrefabLayer
  90. {
  91. get => _paintTool.overwritePrefabLayer;
  92. set => _paintTool.overwritePrefabLayer = value;
  93. }
  94. public int layer { get => _paintTool.layer; set => _paintTool.layer = value; }
  95. public bool autoCreateParent { get => _paintTool.autoCreateParent; set => _paintTool.autoCreateParent = value; }
  96. public bool setSurfaceAsParent { get => _paintTool.setSurfaceAsParent; set => _paintTool.setSurfaceAsParent = value; }
  97. public bool createSubparentPerPalette
  98. {
  99. get => _paintTool.createSubparentPerPalette;
  100. set => _paintTool.createSubparentPerPalette = value;
  101. }
  102. public bool createSubparentPerTool
  103. {
  104. get => _paintTool.createSubparentPerTool;
  105. set => _paintTool.createSubparentPerTool = value;
  106. }
  107. public bool createSubparentPerBrush
  108. {
  109. get => _paintTool.createSubparentPerBrush;
  110. set => _paintTool.createSubparentPerBrush = value;
  111. }
  112. public bool createSubparentPerPrefab
  113. {
  114. get => _paintTool.createSubparentPerPrefab;
  115. set => _paintTool.createSubparentPerPrefab = value;
  116. }
  117. public bool overwriteBrushProperties
  118. {
  119. get => _paintTool.overwriteBrushProperties;
  120. set => _paintTool.overwriteBrushProperties = value;
  121. }
  122. public BrushSettings brushSettings => _paintTool.brushSettings;
  123. #endregion
  124. public override void Copy(IToolSettings other)
  125. {
  126. var otherReplacerSettings = other as ReplacerSettings;
  127. if (otherReplacerSettings == null) return;
  128. base.Copy(other);
  129. _modifierTool.Copy(otherReplacerSettings);
  130. _paintTool.Copy(otherReplacerSettings._paintTool);
  131. _keepTargetSize = otherReplacerSettings._keepTargetSize;
  132. _maintainProportions = otherReplacerSettings._maintainProportions;
  133. _positionMode = otherReplacerSettings._positionMode;
  134. _sameParentasTarget = otherReplacerSettings._sameParentasTarget;
  135. }
  136. public override void DataChanged()
  137. {
  138. base.DataChanged();
  139. BrushstrokeManager.ClearReplacerDictionary();
  140. BrushstrokeManager.UpdateBrushstroke();
  141. }
  142. }
  143. [System.Serializable]
  144. public class ReplacerManager : ToolManagerBase<ReplacerSettings> { }
  145. #endregion
  146. #region PWBIO
  147. public static partial class PWBIO
  148. {
  149. private class ReplacerPaintStrokeItem : PaintStrokeItem
  150. {
  151. public Transform target = null;
  152. public ReplacerPaintStrokeItem(Transform target, GameObject prefab, Vector3 position, Quaternion rotation,
  153. Vector3 scale, int layer, Transform parent, Transform surface, bool flipX, bool flipY,
  154. int index = -1) : base(prefab, position, rotation, scale, layer, parent, surface, flipX, flipY, index)
  155. {
  156. this.target = target;
  157. }
  158. }
  159. private static System.Collections.Generic.List<Renderer> _replaceRenderers
  160. = new System.Collections.Generic.List<Renderer>();
  161. private static bool _replaceAllSelected = false;
  162. private static void ReplacerMouseEvents()
  163. {
  164. var settings = ReplacerManager.settings;
  165. if (Event.current.button == 0 && !Event.current.alt
  166. && (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag))
  167. {
  168. Replace();
  169. Event.current.Use();
  170. }
  171. if (Event.current.button == 1)
  172. {
  173. if (Event.current.type == EventType.MouseDown && (Event.current.control || Event.current.shift))
  174. {
  175. _pinned = true;
  176. _pinMouse = Event.current.mousePosition;
  177. Event.current.Use();
  178. }
  179. else if (Event.current.type == EventType.MouseUp) _pinned = false;
  180. }
  181. }
  182. private static void ReplacerDuringSceneGUI(UnityEditor.SceneView sceneView)
  183. {
  184. if (PaletteManager.selectedBrushIdx < 0) return;
  185. if (_replaceAllSelected)
  186. {
  187. var targets = SelectionManager.topLevelSelection;
  188. BrushstrokeManager.UpdateReplacerBrushstroke(clearDictionary: true, targets);
  189. _paintStroke.Clear();
  190. _replaceAllSelected = false;
  191. ReplacePreview(sceneView.camera, ReplacerManager.settings, targets);
  192. Replace();
  193. return;
  194. }
  195. ReplacerMouseEvents();
  196. var mousePos = Event.current.mousePosition;
  197. if (_pinned) mousePos = _pinMouse;
  198. var mouseRay = UnityEditor.HandleUtility.GUIPointToWorldRay(mousePos);
  199. var center = mouseRay.GetPoint(_lastHitDistance);
  200. if (MouseRaycast(mouseRay, out RaycastHit mouseHit, out GameObject collider,
  201. float.MaxValue, -1, true, true))
  202. {
  203. _lastHitDistance = mouseHit.distance;
  204. center = mouseHit.point;
  205. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  206. }
  207. DrawCircleTool(center, sceneView.camera, new Color(0f, 0f, 0, 1f), ReplacerManager.settings.radius);
  208. DrawPreview(center, mouseRay, sceneView.camera);
  209. }
  210. public static void ResetReplacer()
  211. {
  212. foreach (var renderer in _replaceRenderers)
  213. {
  214. if (renderer == null) continue;
  215. renderer.enabled = true;
  216. }
  217. _replaceRenderers.Clear();
  218. _paintStroke.Clear();
  219. BrushstrokeManager.ClearReplacerDictionary();
  220. }
  221. private static Transform _replaceSurface = null;
  222. private static void ReplacePreview(Camera camera, ReplacerSettings settings, GameObject[] targets)
  223. {
  224. BrushstrokeManager.UpdateReplacerBrushstroke(false, targets);
  225. var brushstroke = BrushstrokeManager.brushstroke;
  226. foreach (var strokeItem in brushstroke)
  227. {
  228. var target = BrushstrokeManager.GetReplacerTargetFromStrokeItem(strokeItem);
  229. if (target == null) continue;
  230. var prefab = strokeItem.settings.prefab;
  231. var itemPosition = strokeItem.tangentPosition;
  232. var itemRotation = Quaternion.Euler(strokeItem.additionalAngle);
  233. var scaleMult = strokeItem.scaleMultiplier;
  234. var itemScale = Vector3.Scale(prefab.transform.localScale, scaleMult);
  235. var layer = settings.overwritePrefabLayer ? settings.layer : target.gameObject.layer;
  236. Transform parentTransform = target.parent;
  237. if (!settings.sameParentAsTarget)
  238. parentTransform = GetParent(settings, prefab.name, create: false, _replaceSurface);
  239. _paintStroke.Add(new ReplacerPaintStrokeItem(target, prefab, itemPosition,
  240. itemRotation * prefab.transform.rotation,
  241. itemScale, layer, parentTransform, null, false, false));
  242. var rootToWorld = Matrix4x4.TRS(itemPosition, itemRotation, scaleMult)
  243. * Matrix4x4.Translate(-prefab.transform.position);
  244. PreviewBrushItem(prefab, rootToWorld, layer, camera, false, false, strokeItem.flipX, strokeItem.flipY);
  245. }
  246. }
  247. private static GameObject[] Replace()
  248. {
  249. const string COMMAND_NAME = "Replace";
  250. foreach (var renderer in _replaceRenderers) renderer.enabled = true;
  251. _replaceRenderers.Clear();
  252. var settings = ReplacerManager.settings;
  253. var newObjects = new System.Collections.Generic.HashSet<GameObject>();
  254. foreach (ReplacerPaintStrokeItem item in _paintStroke)
  255. {
  256. if (item.prefab == null) continue;
  257. var target = item.target.gameObject;
  258. if (target == null) continue;
  259. if (settings.outermostPrefabFilter)
  260. {
  261. var nearestRoot = UnityEditor.PrefabUtility.GetNearestPrefabInstanceRoot(target);
  262. if (nearestRoot != null) target = nearestRoot;
  263. }
  264. else
  265. {
  266. var parent = target.transform.parent.gameObject;
  267. if (parent != null)
  268. {
  269. GameObject outermost = null;
  270. do
  271. {
  272. outermost = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(target);
  273. if (outermost == null) break;
  274. if (outermost == target) break;
  275. UnityEditor.PrefabUtility.UnpackPrefabInstance(outermost,
  276. UnityEditor.PrefabUnpackMode.OutermostRoot, UnityEditor.InteractionMode.UserAction);
  277. } while (outermost != parent);
  278. }
  279. }
  280. var type = UnityEditor.PrefabUtility.GetPrefabAssetType(item.prefab);
  281. GameObject obj = type == UnityEditor.PrefabAssetType.NotAPrefab ? GameObject.Instantiate(item.prefab)
  282. : (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab
  283. (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(item.prefab)
  284. ? item.prefab : UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(item.prefab));
  285. if (settings.overwritePrefabLayer) obj.layer = settings.layer;
  286. obj.transform.SetPositionAndRotation(item.position, item.rotation);
  287. obj.transform.localScale = item.scale;
  288. var root = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(obj);
  289. PWBCore.AddTempCollider(obj);
  290. AddPaintedObject(obj);
  291. newObjects.Add(obj);
  292. if (!LineManager.instance.ReplaceObject(target, obj))
  293. if (!ShapeManager.instance.ReplaceObject(target, obj))
  294. TilingManager.instance.ReplaceObject(target, obj);
  295. var tempColliders = PWBCore.GetTempColliders(target);
  296. if (tempColliders != null)
  297. foreach (var tempCollider in tempColliders) UnityEditor.Undo.DestroyObjectImmediate(tempCollider);
  298. UnityEditor.Undo.DestroyObjectImmediate(target);
  299. UnityEditor.Undo.RegisterCreatedObjectUndo(obj, COMMAND_NAME);
  300. Transform parentTransform = item.parent;
  301. if (settings.sameParentAsTarget)
  302. {
  303. if (root != null) UnityEditor.Undo.SetTransformParent(root.transform, parentTransform, COMMAND_NAME);
  304. else UnityEditor.Undo.SetTransformParent(obj.transform, parentTransform, COMMAND_NAME);
  305. }
  306. else
  307. {
  308. parentTransform = GetParent(settings, item.prefab.name, create: true, _replaceSurface);
  309. UnityEditor.Undo.SetTransformParent(obj.transform, parentTransform, COMMAND_NAME);
  310. }
  311. }
  312. _paintStroke.Clear();
  313. BrushstrokeManager.ClearReplacerDictionary();
  314. return newObjects.ToArray();
  315. }
  316. public static void ReplaceAllSelected() => _replaceAllSelected = true;
  317. private static GameObject[] GetReplacerTargets(Vector3 center, Ray mouseRay, Camera camera)
  318. {
  319. var targetsSet = new System.Collections.Generic.HashSet<GameObject>();
  320. GetCircleToolTargets(mouseRay, camera, ReplacerManager.settings, ReplacerManager.settings.radius, targetsSet);
  321. var toReplace = targetsSet.ToArray();
  322. _paintStroke.Clear();
  323. var result = new System.Collections.Generic.HashSet<GameObject>();
  324. for (int i = 0; i < toReplace.Length; ++i)
  325. {
  326. var obj = toReplace[i];
  327. var isChild = false;
  328. foreach (var listed in toReplace)
  329. {
  330. if (obj.transform.IsChildOf(listed.transform) && listed != obj)
  331. {
  332. isChild = true;
  333. break;
  334. }
  335. }
  336. if (isChild) continue;
  337. result.Add(obj);
  338. }
  339. return result.ToArray();
  340. }
  341. private static void DrawPreview(Vector3 center, Ray mouseRay, Camera camera)
  342. {
  343. foreach (var renderer in _replaceRenderers)
  344. {
  345. if (renderer == null) continue;
  346. renderer.enabled = true;
  347. }
  348. var targets = GetReplacerTargets(center, mouseRay, camera);
  349. _replaceRenderers.Clear();
  350. foreach ( var obj in targets)
  351. _replaceRenderers.AddRange(obj.GetComponentsInChildren<Renderer>().Where(r => r.enabled == true));
  352. ReplacePreview(camera, ReplacerManager.settings, targets);
  353. foreach (var renderer in _replaceRenderers) renderer.enabled = false;
  354. }
  355. }
  356. #endregion
  357. }