ExtrudeManager.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /*
  2. Copyright (c) 2020 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2020.
  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 System.Linq;
  14. using UnityEngine;
  15. namespace PluginMaster
  16. {
  17. #region DATA & SETTINGS
  18. [System.Serializable]
  19. public class ExtrudeSettings : SelectionToolBaseBasic, IToolSettings, IPaintToolSettings
  20. {
  21. [SerializeField] private Space _space = Space.World;
  22. [SerializeField] private Vector3 _spacing = Vector3.zero;
  23. public enum SpacingType { BOX_SIZE, CUSTOM }
  24. [SerializeField] private SpacingType _spacingType = SpacingType.CUSTOM;
  25. [SerializeField] private Vector3 _multiplier = Vector3.one;
  26. public enum RotationAccordingTo { FRIST_SELECTED, LAST_SELECTED }
  27. [SerializeField] private RotationAccordingTo _rotationAccordingTo = RotationAccordingTo.FRIST_SELECTED;
  28. [SerializeField] private bool _sameParentAsSource = true;
  29. [SerializeField] private Vector3 _eulerOffset = Vector3.zero;
  30. [SerializeField] private bool _addRandomRotation = false;
  31. [SerializeField] private float _rotationFactor = 90;
  32. [SerializeField] private bool _rotateInMultiples = false;
  33. [SerializeField]
  34. private RandomUtils.Range3 _randomEulerOffset = new RandomUtils.Range3(Vector3.zero, Vector3.zero);
  35. public Space space
  36. {
  37. get => _space;
  38. set
  39. {
  40. if (_space == value) return;
  41. _space = value;
  42. DataChanged();
  43. }
  44. }
  45. public Vector3 multiplier
  46. {
  47. get => _multiplier;
  48. set
  49. {
  50. if (_multiplier == value) return;
  51. _multiplier = value;
  52. DataChanged();
  53. }
  54. }
  55. public RotationAccordingTo rotationAccordingTo
  56. {
  57. get => _rotationAccordingTo;
  58. set
  59. {
  60. if (_rotationAccordingTo == value) return;
  61. _rotationAccordingTo = value;
  62. DataChanged();
  63. }
  64. }
  65. public Vector3 spacing
  66. {
  67. get => _spacing;
  68. set
  69. {
  70. if (_spacing == value) return;
  71. _spacing = value;
  72. DataChanged();
  73. }
  74. }
  75. public SpacingType spacingType
  76. {
  77. get => _spacingType;
  78. set
  79. {
  80. if (_spacingType == value) return;
  81. _spacingType = value;
  82. DataChanged();
  83. }
  84. }
  85. public ExtrudeSettings Clone()
  86. {
  87. var clone = new ExtrudeSettings();
  88. clone.Copy(this);
  89. return clone;
  90. }
  91. public Vector3 eulerOffset
  92. {
  93. get => _eulerOffset;
  94. set
  95. {
  96. if (_eulerOffset == value) return;
  97. _eulerOffset = value;
  98. _randomEulerOffset.v1 = _randomEulerOffset.v2 = Vector3.zero;
  99. }
  100. }
  101. public bool addRandomRotation
  102. {
  103. get => _addRandomRotation;
  104. set
  105. {
  106. if (_addRandomRotation == value) return;
  107. _addRandomRotation = value;
  108. }
  109. }
  110. public float rotationFactor
  111. {
  112. get => _rotationFactor;
  113. set
  114. {
  115. value = Mathf.Max(value, 0f);
  116. if (_rotationFactor == value) return;
  117. _rotationFactor = value;
  118. }
  119. }
  120. public bool rotateInMultiples
  121. {
  122. get => _rotateInMultiples;
  123. set
  124. {
  125. if (_rotateInMultiples == value) return;
  126. _rotateInMultiples = value;
  127. }
  128. }
  129. public RandomUtils.Range3 randomEulerOffset
  130. {
  131. get => _randomEulerOffset;
  132. set
  133. {
  134. if (_randomEulerOffset == value) return;
  135. _randomEulerOffset = value;
  136. _eulerOffset = Vector3.zero;
  137. }
  138. }
  139. public override void Copy(IToolSettings other)
  140. {
  141. var otherExtrudeSettings = other as ExtrudeSettings;
  142. if (otherExtrudeSettings == null) return;
  143. base.Copy(other);
  144. _paintTool.Copy(otherExtrudeSettings._paintTool);
  145. _sameParentAsSource = otherExtrudeSettings._sameParentAsSource;
  146. _space = otherExtrudeSettings._space;
  147. _multiplier = otherExtrudeSettings._multiplier;
  148. _rotationAccordingTo = otherExtrudeSettings._rotationAccordingTo;
  149. _spacing = otherExtrudeSettings._spacing;
  150. _spacingType = otherExtrudeSettings._spacingType;
  151. _eulerOffset = otherExtrudeSettings._eulerOffset;
  152. _addRandomRotation = otherExtrudeSettings._addRandomRotation;
  153. _rotationFactor = otherExtrudeSettings._rotationFactor;
  154. _rotateInMultiples = otherExtrudeSettings._rotateInMultiples;
  155. _randomEulerOffset = otherExtrudeSettings._randomEulerOffset;
  156. }
  157. public bool sameParentAsSource
  158. {
  159. get => _sameParentAsSource;
  160. set
  161. {
  162. if (_sameParentAsSource == value) return;
  163. _sameParentAsSource = value;
  164. DataChanged();
  165. }
  166. }
  167. #region PAINT TOOL
  168. [SerializeField] private PaintToolSettings _paintTool = new PaintToolSettings();
  169. public Transform parent { get => _paintTool.parent; set => _paintTool.parent = value; }
  170. public bool overwritePrefabLayer
  171. { get => _paintTool.overwritePrefabLayer; set => _paintTool.overwritePrefabLayer = value; }
  172. public int layer { get => _paintTool.layer; set => _paintTool.layer = value; }
  173. public bool autoCreateParent { get => _paintTool.autoCreateParent; set => _paintTool.autoCreateParent = value; }
  174. public bool setSurfaceAsParent { get => _paintTool.setSurfaceAsParent; set => _paintTool.setSurfaceAsParent = value; }
  175. public bool createSubparentPerPalette
  176. {
  177. get => _paintTool.createSubparentPerPalette;
  178. set => _paintTool.createSubparentPerPalette = value;
  179. }
  180. public bool createSubparentPerTool
  181. {
  182. get => _paintTool.createSubparentPerTool;
  183. set => _paintTool.createSubparentPerTool = value;
  184. }
  185. public bool createSubparentPerBrush
  186. {
  187. get => _paintTool.createSubparentPerBrush;
  188. set => _paintTool.createSubparentPerBrush = value;
  189. }
  190. public bool createSubparentPerPrefab
  191. {
  192. get => _paintTool.createSubparentPerPrefab;
  193. set => _paintTool.createSubparentPerPrefab = value;
  194. }
  195. public bool overwriteBrushProperties
  196. { get => _paintTool.overwriteBrushProperties; set => _paintTool.overwriteBrushProperties = value; }
  197. public BrushSettings brushSettings => _paintTool.brushSettings;
  198. #endregion
  199. }
  200. [System.Serializable]
  201. public class ExtrudeManager : ToolManagerBase<ExtrudeSettings> { }
  202. #endregion
  203. #region PWBIO
  204. public static partial class PWBIO
  205. {
  206. private static Vector3 _extrudeHandlePosition;
  207. private static Vector3Int _extrudeDirection;
  208. private static Vector3 _initialExtrudePosition;
  209. private static Vector3 _selectionSize;
  210. private static Vector3 _deltaSnapped;
  211. private static Vector3 _extrudeSpacing;
  212. private static int _extrudegPreviewObjectCount = 0;
  213. public static void ResetExtrudeState(bool askIfWantToSave = true)
  214. {
  215. if (askIfWantToSave && _extrudegPreviewObjectCount > 0) DisplaySaveDialog(CreateExtrudedObjects);
  216. _extrudegPreviewObjectCount = 0;
  217. ClearExtrudeAngles();
  218. }
  219. public static void ClearExtrudeAngles() => _extrudeAngles.Clear();
  220. private static void ExtrudeDuringSceneGUI(UnityEditor.SceneView sceneView)
  221. {
  222. if (ExtrudeManager.settings.createTempColliders)
  223. PWBCore.CreateTempCollidersWithinFrustum(sceneView.camera);
  224. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
  225. {
  226. ResetUnityCurrentTool();
  227. ResetExtrudeState(false);
  228. ToolManager.DeselectTool();
  229. return;
  230. }
  231. if (SelectionManager.topLevelSelection.Length == 0) return;
  232. ExtrudeInput();
  233. if (SelectionManager.topLevelSelection.Length == 0) return;
  234. var settings = ExtrudeManager.settings;
  235. if (UnityEditor.Tools.current != UnityEditor.Tool.View && UnityEditor.Tools.current != UnityEditor.Tool.None)
  236. {
  237. SaveUnityCurrentTool();
  238. UnityEditor.Tools.current = UnityEditor.Tool.None;
  239. }
  240. var anchor = settings.rotationAccordingTo == ExtrudeSettings.RotationAccordingTo.FRIST_SELECTED
  241. ? SelectionManager.topLevelSelection.First().transform
  242. : SelectionManager.topLevelSelection.Last().transform;
  243. var handlePosition = UnityEditor.Handles.PositionHandle(_extrudeHandlePosition,
  244. settings.space == Space.World ? Quaternion.identity : anchor.rotation);
  245. var handleDelta = handlePosition - _extrudeHandlePosition;
  246. _extrudeHandlePosition = handlePosition;
  247. var delta = _extrudeHandlePosition - _initialExtrudePosition;
  248. if (settings.space == Space.Self)
  249. {
  250. handleDelta = anchor.InverseTransformVector(handleDelta);
  251. delta = anchor.InverseTransformVector(delta);
  252. }
  253. if (delta.sqrMagnitude > 0.01)
  254. {
  255. var direction = Vector3Int.one;
  256. var absDelta = new Vector3(Mathf.Abs(handleDelta.x),
  257. Mathf.Abs(handleDelta.y), Mathf.Abs(handleDelta.z));
  258. direction.x = (absDelta.x <= absDelta.y || absDelta.x <= absDelta.z) ? 0 : (int)Mathf.Sign(delta.x);
  259. direction.y = (absDelta.y <= absDelta.x || absDelta.y <= absDelta.z) ? 0 : (int)Mathf.Sign(delta.y);
  260. direction.z = (absDelta.z <= absDelta.x || absDelta.z <= absDelta.y) ? 0 : (int)Mathf.Sign(delta.z);
  261. var directionChanged = direction != Vector3Int.zero && _extrudeDirection != direction;
  262. if (handleDelta != Vector3.zero && directionChanged && _extrudeDirection != Vector3.zero
  263. && _extrudeDirection != (direction * -1)) CreateExtrudedObjects(anchor);
  264. if (directionChanged) _extrudeDirection = direction;
  265. _extrudeSpacing = _selectionSize + (settings.spacingType == ExtrudeSettings.SpacingType.BOX_SIZE
  266. ? Vector3.Scale(_selectionSize, settings.multiplier - Vector3.one)
  267. : settings.spacing);
  268. _deltaSnapped = new Vector3(
  269. Mathf.Floor((Mathf.Abs(delta.x) + _selectionSize.x / 2f) / _extrudeSpacing.x)
  270. * _extrudeSpacing.x * Mathf.Sign(delta.x),
  271. Mathf.Floor((Mathf.Abs(delta.y) + _selectionSize.y / 2f) / _extrudeSpacing.y)
  272. * _extrudeSpacing.y * Mathf.Sign(delta.y),
  273. Mathf.Floor((Mathf.Abs(delta.z) + _selectionSize.z / 2f) / _extrudeSpacing.z)
  274. * _extrudeSpacing.z * Mathf.Sign(delta.z));
  275. if (_deltaSnapped != Vector3.zero) PreviewExtrudedObjects(sceneView.camera, anchor);
  276. }
  277. }
  278. private static Vector3 GetExtrudeStep(Transform anchor)
  279. {
  280. var step = Vector3.Scale(_extrudeSpacing, _extrudeDirection);
  281. if (ExtrudeManager.settings.space == Space.Self)
  282. {
  283. if (anchor.lossyScale.x != 0) step.x /= anchor.lossyScale.x;
  284. if (anchor.lossyScale.y != 0) step.y /= anchor.lossyScale.y;
  285. if (anchor.lossyScale.z != 0) step.z /= anchor.lossyScale.z;
  286. }
  287. return step;
  288. }
  289. private static System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<Vector3>>
  290. _extrudeAngles = new System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<Vector3>>();
  291. private static System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<Pose>>
  292. _extrudePoses = new System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<Pose>>();
  293. private static void PreviewExtrudedObjects(Camera camera, Transform anchor)
  294. {
  295. var step = GetExtrudeStep(anchor);
  296. var settings = ExtrudeManager.settings;
  297. _extrudegPreviewObjectCount = 0;
  298. _extrudePoses.Clear();
  299. foreach (var obj in SelectionManager.topLevelSelection)
  300. {
  301. var objPose = new Pose(obj.transform.position, obj.transform.rotation);
  302. var delta = step;
  303. var objId = obj.GetInstanceID();
  304. _extrudePoses.Add(objId, new System.Collections.Generic.List<Pose>());
  305. System.Collections.Generic.List<Vector3> rotationList = null;
  306. if (_extrudeAngles.ContainsKey(objId))
  307. {
  308. rotationList = _extrudeAngles[objId];
  309. }
  310. else
  311. {
  312. rotationList = new System.Collections.Generic.List<Vector3>();
  313. _extrudeAngles.Add(objId, rotationList);
  314. }
  315. int rotationIdx = 0;
  316. do
  317. {
  318. var deltaPos = settings.space == Space.World ? delta : anchor.TransformVector(delta);
  319. var localToWorld = Matrix4x4.Translate(deltaPos);
  320. var additonalAngle = Vector3.zero;
  321. if (settings.space == Space.World)
  322. {
  323. if (rotationIdx < rotationList.Count - 1)
  324. {
  325. additonalAngle = rotationList[rotationIdx];
  326. }
  327. else
  328. {
  329. if (settings.addRandomRotation)
  330. {
  331. var randomAngle = settings.randomEulerOffset.randomVector;
  332. if (settings.rotateInMultiples)
  333. {
  334. randomAngle = new Vector3(
  335. Mathf.Round(randomAngle.x / settings.rotationFactor) * settings.rotationFactor,
  336. Mathf.Round(randomAngle.y / settings.rotationFactor) * settings.rotationFactor,
  337. Mathf.Round(randomAngle.z / settings.rotationFactor) * settings.rotationFactor);
  338. }
  339. additonalAngle += randomAngle;
  340. }
  341. else additonalAngle += settings.eulerOffset;
  342. rotationList.Add(additonalAngle);
  343. }
  344. if (additonalAngle != Vector3.zero)
  345. {
  346. var aditionalRotation = Quaternion.Euler(additonalAngle);
  347. Vector3 additionalRotationAxis;
  348. float additionalRotationAngle;
  349. aditionalRotation.ToAngleAxis(out additionalRotationAngle, out additionalRotationAxis);
  350. obj.transform.rotation = objPose.rotation;
  351. obj.transform.position = objPose.position;
  352. var center = BoundsUtils.GetBoundsRecursive(obj.transform).center;
  353. obj.transform.RotateAround(center, additionalRotationAxis, additionalRotationAngle);
  354. }
  355. }
  356. var surfaceDelta = Vector3.zero;
  357. if (settings.embedInSurface)
  358. {
  359. var bottomVertices = BoundsUtils.GetBottomVertices(obj.transform);
  360. var height = BoundsUtils.GetMagnitude(obj.transform) * 3;
  361. Vector3 position = anchor.position + deltaPos;
  362. var rotation = anchor.rotation;
  363. var TRS = Matrix4x4.TRS(position, rotation, obj.transform.lossyScale);
  364. var surfceDistance = settings.embedAtPivotHeight
  365. ? GetPivotDistanceToSurfaceSigned(position, height, true, true)
  366. : GetBottomDistanceToSurfaceSigned(bottomVertices, TRS, height, true, true);
  367. surfceDistance -= settings.surfaceDistance;
  368. position += new Vector3(0f, -surfceDistance, 0f);
  369. deltaPos += new Vector3(0f, -surfceDistance, 0f);
  370. surfaceDelta = new Vector3(0f, -surfceDistance, 0f);
  371. localToWorld = Matrix4x4.Translate(deltaPos);
  372. }
  373. ++_extrudegPreviewObjectCount;
  374. PreviewBrushItem(obj, localToWorld, obj.layer, camera, false, false, false, false);
  375. var posePosition = obj.transform.position + surfaceDelta;
  376. posePosition += settings.space == Space.World ? delta : obj.transform.rotation * delta;
  377. _extrudePoses[objId].Add(new Pose(posePosition, obj.transform.rotation));
  378. delta += step;
  379. ++rotationIdx;
  380. } while (Mathf.Abs(delta.x) <= Mathf.Abs(_deltaSnapped.x)
  381. && Mathf.Abs(delta.y) <= Mathf.Abs(_deltaSnapped.y)
  382. && Mathf.Abs(delta.z) <= Mathf.Abs(_deltaSnapped.z));
  383. obj.transform.rotation = objPose.rotation;
  384. obj.transform.position = objPose.position;
  385. }
  386. }
  387. private static void CreateExtrudedObjects(Transform anchor)
  388. {
  389. _extrudegPreviewObjectCount = 0;
  390. if (SelectionManager.topLevelSelection.Length == 0 || _extrudeDirection == Vector3Int.zero
  391. || _deltaSnapped == Vector3.zero) return;
  392. var newSelection = new System.Collections.Generic.List<GameObject>();
  393. _initialExtrudePosition += Vector3.Scale(_extrudeDirection, _deltaSnapped);
  394. _extrudeHandlePosition = _initialExtrudePosition;
  395. var step = GetExtrudeStep(anchor);
  396. foreach (var obj in SelectionManager.topLevelSelection)
  397. {
  398. GameObject extruded = null;
  399. var parent = GetParent(ExtrudeManager.settings, obj.name, true, null);
  400. if (ExtrudeManager.settings.sameParentAsSource) parent = obj.transform.parent;
  401. foreach(var pose in _extrudePoses[obj.GetInstanceID()])
  402. {
  403. extruded = UnityEditor.PrefabUtility.IsOutermostPrefabInstanceRoot(obj)
  404. ? (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(
  405. UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(obj))
  406. : GameObject.Instantiate(obj);
  407. extruded.transform.position = pose.position;
  408. extruded.transform.rotation = pose.rotation;
  409. extruded.transform.localScale = obj.transform.lossyScale;
  410. if (ExtrudeManager.settings.overwritePrefabLayer)
  411. extruded.layer = ExtrudeManager.settings.layer;
  412. const string COMMAND_NAME = "Extrude";
  413. UnityEditor.Undo.RegisterCreatedObjectUndo(extruded, COMMAND_NAME);
  414. UnityEditor.Undo.SetTransformParent(extruded.transform, parent, COMMAND_NAME);
  415. }
  416. newSelection.Add(extruded);
  417. }
  418. UnityEditor.Selection.objects = newSelection.ToArray();
  419. }
  420. private static void CreateExtrudedObjects()
  421. {
  422. var anchor = ExtrudeManager.settings.rotationAccordingTo == ExtrudeSettings.RotationAccordingTo.FRIST_SELECTED
  423. ? SelectionManager.topLevelSelection.First().transform
  424. : SelectionManager.topLevelSelection.Last().transform;
  425. CreateExtrudedObjects(anchor);
  426. }
  427. private static void ExtrudeInput()
  428. {
  429. if (SelectionManager.topLevelSelection.First() == null || SelectionManager.topLevelSelection.Last() == null)
  430. SelectionManager.UpdateSelection();
  431. if (SelectionManager.topLevelSelection.Length == 0) return;
  432. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
  433. CreateExtrudedObjects();
  434. }
  435. }
  436. #endregion
  437. }