BrushManager.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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 BrushToolSettings : BrushToolBase, IPaintOnSurfaceToolSettings, ISerializationCallbackReceiver
  20. {
  21. [SerializeField] private PaintOnSurfaceToolSettings _paintOnSurfaceToolSettings = new PaintOnSurfaceToolSettings();
  22. [SerializeField] private float _maxHeightFromCenter = 2f;
  23. public enum HeightType { CUSTOM, RADIUS }
  24. [SerializeField] private HeightType _heightType = HeightType.RADIUS;
  25. public enum AvoidOverlappingType
  26. {
  27. DISABLED,
  28. WITH_PALETTE_PREFABS,
  29. WITH_BRUSH_PREFABS,
  30. WITH_SAME_PREFABS,
  31. WITH_ALL_OBJECTS
  32. }
  33. [SerializeField] private AvoidOverlappingType _avoidOverlapping = AvoidOverlappingType.WITH_ALL_OBJECTS;
  34. [SerializeField] private LayerMask _layerFilter = -1;
  35. [SerializeField] private System.Collections.Generic.List<string> _tagFilter = null;
  36. [SerializeField] private RandomUtils.Range _slopeFilter = new RandomUtils.Range(0, 60);
  37. [SerializeField] private string[] _terrainLayerIds = null;
  38. [SerializeField] private bool _showPreview = false;
  39. private TerrainLayer[] _terrainLayerFilter = null;
  40. private bool _updateTerrainFilter = false;
  41. private long id = 0;
  42. public BrushToolSettings() : base()
  43. {
  44. id = System.DateTime.Now.Ticks;
  45. _paintOnSurfaceToolSettings.OnDataChanged += DataChanged;
  46. }
  47. public bool paintOnMeshesWithoutCollider
  48. {
  49. get => _paintOnSurfaceToolSettings.paintOnMeshesWithoutCollider;
  50. set => _paintOnSurfaceToolSettings.paintOnMeshesWithoutCollider = value;
  51. }
  52. public bool paintOnSelectedOnly
  53. {
  54. get => _paintOnSurfaceToolSettings.paintOnSelectedOnly;
  55. set => _paintOnSurfaceToolSettings.paintOnSelectedOnly = value;
  56. }
  57. public bool paintOnPalettePrefabs
  58. {
  59. get => _paintOnSurfaceToolSettings.paintOnPalettePrefabs;
  60. set => _paintOnSurfaceToolSettings.paintOnPalettePrefabs = value;
  61. }
  62. public bool showPreview
  63. {
  64. get => _showPreview;
  65. set
  66. {
  67. if (_showPreview == value) return;
  68. _showPreview = value;
  69. DataChanged();
  70. }
  71. }
  72. public float maxHeightFromCenter
  73. {
  74. get => _maxHeightFromCenter;
  75. set
  76. {
  77. if (_maxHeightFromCenter == value) return;
  78. _maxHeightFromCenter = value;
  79. DataChanged();
  80. }
  81. }
  82. public HeightType heightType
  83. {
  84. get => _heightType;
  85. set
  86. {
  87. if (_heightType == value) return;
  88. _heightType = value;
  89. DataChanged();
  90. }
  91. }
  92. public AvoidOverlappingType avoidOverlapping
  93. {
  94. get => _avoidOverlapping;
  95. set
  96. {
  97. if (_avoidOverlapping == value) return;
  98. _avoidOverlapping = value;
  99. DataChanged();
  100. }
  101. }
  102. public virtual LayerMask layerFilter
  103. {
  104. get => _layerFilter;
  105. set
  106. {
  107. if (_layerFilter == value) return;
  108. _layerFilter = value;
  109. DataChanged();
  110. }
  111. }
  112. public virtual System.Collections.Generic.List<string> tagFilter
  113. {
  114. get
  115. {
  116. if (_tagFilter == null) UpdateTagFilter();
  117. return _tagFilter;
  118. }
  119. set
  120. {
  121. if (_tagFilter == value) return;
  122. _tagFilter = value;
  123. DataChanged();
  124. }
  125. }
  126. public virtual RandomUtils.Range slopeFilter
  127. {
  128. get => _slopeFilter;
  129. set
  130. {
  131. if (_slopeFilter == value) return;
  132. _slopeFilter = value;
  133. DataChanged();
  134. }
  135. }
  136. public TerrainLayer[] terrainLayerFilter
  137. {
  138. get
  139. {
  140. if ((_terrainLayerFilter == null && _terrainLayerIds != null) || _updateTerrainFilter) UpdateTerrainFilter();
  141. return _terrainLayerFilter;
  142. }
  143. set
  144. {
  145. if (Equals(_terrainLayerFilter, value)) return;
  146. if (value == null)
  147. {
  148. _terrainLayerFilter = null;
  149. _terrainLayerIds = null;
  150. return;
  151. }
  152. var layerList = new System.Collections.Generic.List<TerrainLayer>();
  153. var terrainLayerIds = new System.Collections.Generic.List<string>();
  154. foreach (var layer in value)
  155. {
  156. layerList.Add(layer);
  157. if (layer == null) continue;
  158. terrainLayerIds.Add(UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(layer).ToString());
  159. }
  160. _terrainLayerFilter = layerList.ToArray();
  161. _terrainLayerIds = terrainLayerIds.ToArray();
  162. }
  163. }
  164. public override void Copy(IToolSettings other)
  165. {
  166. var otherBrushToolSettings = other as BrushToolSettings;
  167. if (otherBrushToolSettings == null) return;
  168. base.Copy(other);
  169. _paintOnSurfaceToolSettings.Copy(otherBrushToolSettings._paintOnSurfaceToolSettings);
  170. _maxHeightFromCenter = otherBrushToolSettings._maxHeightFromCenter;
  171. _heightType = otherBrushToolSettings._heightType;
  172. _avoidOverlapping = otherBrushToolSettings._avoidOverlapping;
  173. _layerFilter = otherBrushToolSettings._layerFilter;
  174. _tagFilter = otherBrushToolSettings._tagFilter == null ? null
  175. : new System.Collections.Generic.List<string>(otherBrushToolSettings._tagFilter);
  176. _slopeFilter = new RandomUtils.Range(otherBrushToolSettings._slopeFilter);
  177. _terrainLayerFilter = otherBrushToolSettings._terrainLayerFilter == null ? null
  178. : otherBrushToolSettings._terrainLayerFilter.ToArray();
  179. _terrainLayerIds = otherBrushToolSettings._terrainLayerIds == null ? null
  180. : otherBrushToolSettings._terrainLayerIds.ToArray();
  181. }
  182. private void UpdateTagFilter()
  183. {
  184. if (_tagFilter != null) return;
  185. _tagFilter = new System.Collections.Generic.List<string>(UnityEditorInternal.InternalEditorUtility.tags);
  186. }
  187. private void UpdateTerrainFilter()
  188. {
  189. _updateTerrainFilter = false;
  190. if (_terrainLayerIds == null) return;
  191. var terrainLayerList = new System.Collections.Generic.List<TerrainLayer>();
  192. foreach (var globalId in _terrainLayerIds)
  193. {
  194. if (UnityEditor.GlobalObjectId.TryParse(globalId, out UnityEditor.GlobalObjectId id))
  195. {
  196. var layer = UnityEditor.GlobalObjectId.GlobalObjectIdentifierToObjectSlow(id) as TerrainLayer;
  197. if (layer == null) continue;
  198. terrainLayerList.Add(layer);
  199. }
  200. }
  201. _terrainLayerFilter = terrainLayerList.ToArray();
  202. }
  203. public void OnBeforeSerialize()
  204. {
  205. UpdateTagFilter();
  206. UpdateTerrainFilter();
  207. }
  208. public void OnAfterDeserialize()
  209. {
  210. UpdateTagFilter();
  211. _updateTerrainFilter = true;
  212. }
  213. }
  214. [System.Serializable]
  215. public class BrushManager : ToolManagerBase<BrushToolSettings> { }
  216. #endregion
  217. #region PWBIO
  218. public static partial class PWBIO
  219. {
  220. private static float _brushAngle = 0f;
  221. private static bool BrushRaycast(Ray ray, out RaycastHit hit, float maxDistance,
  222. LayerMask layerMask, BrushToolSettings settings, TerrainLayer[] terrainLayers)
  223. {
  224. hit = new RaycastHit();
  225. bool result = false;
  226. var noColliderDistance = float.MaxValue;
  227. if (MouseRaycast(ray, out RaycastHit hitInfo, out GameObject collider, maxDistance,
  228. layerMask, settings.paintOnPalettePrefabs, settings.paintOnMeshesWithoutCollider,
  229. settings.tagFilter.ToArray(), terrainLayers))
  230. {
  231. var nearestRoot = UnityEditor.PrefabUtility.GetNearestPrefabInstanceRoot(collider);
  232. bool isAPaintedObject = false;
  233. while (nearestRoot != null)
  234. {
  235. isAPaintedObject = isAPaintedObject || _paintedObjects.Contains(nearestRoot);
  236. var parent = nearestRoot.transform.parent == null ? null
  237. : nearestRoot.transform.parent.gameObject;
  238. nearestRoot = parent == null ? null : UnityEditor.PrefabUtility.GetNearestPrefabInstanceRoot(parent);
  239. }
  240. bool selectedOnlyFilter = !settings.paintOnSelectedOnly
  241. || SelectionManager.selection.Contains(collider)
  242. || PWBCore.CollidersContains(SelectionManager.selection, collider.name);
  243. bool paletteFilter = !isAPaintedObject || settings.paintOnPalettePrefabs;
  244. var filterResult = selectedOnlyFilter && paletteFilter;
  245. result = filterResult;
  246. if (filterResult && (hitInfo.distance < noColliderDistance))
  247. hit = hitInfo;
  248. }
  249. return result;
  250. }
  251. private static void BrushDuringSceneGUI(UnityEditor.SceneView sceneView)
  252. {
  253. if (BrushManager.settings.paintOnMeshesWithoutCollider)
  254. PWBCore.CreateTempCollidersWithinFrustum(sceneView.camera);
  255. BrushstrokeMouseEvents(BrushManager.settings);
  256. var mousePos = Event.current.mousePosition;
  257. if (_pinned) mousePos = _pinMouse;
  258. var mouseRay = UnityEditor.HandleUtility.GUIPointToWorldRay(mousePos);
  259. bool snappedToVertex = false;
  260. var closestVertexInfo = new RaycastHit();
  261. if (_snapToVertex) snappedToVertex = SnapToVertex(mouseRay, out closestVertexInfo, sceneView.in2DMode);
  262. if (snappedToVertex) mouseRay.origin = closestVertexInfo.point - mouseRay.direction;
  263. var in2DMode = (PaletteManager.selectedBrush != null && PaletteManager.selectedBrush.isAsset2D)
  264. && sceneView.in2DMode;
  265. if (BrushRaycast(mouseRay, out RaycastHit hit, float.MaxValue, -1, BrushManager.settings, null) || in2DMode)
  266. {
  267. if (in2DMode)
  268. {
  269. hit.point = new Vector3(mouseRay.origin.x, mouseRay.origin.y, 0f);
  270. hit.normal = Vector3.back;
  271. }
  272. DrawBrush(sceneView, ref hit, BrushManager.settings.showPreview);
  273. }
  274. else _paintStroke.Clear();
  275. if (Event.current.button == 0 && !Event.current.alt
  276. && (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag))
  277. {
  278. if (!BrushManager.settings.showPreview) DrawBrush(sceneView, ref hit, true);
  279. Paint(BrushManager.settings);
  280. Event.current.Use();
  281. }
  282. //BrushInfoText(sceneView, hit.point);
  283. }
  284. private static void BrushInfoText(UnityEditor.SceneView sceneView, Vector3 hitPoint)
  285. {
  286. if (!PWBCore.staticData.showInfoText) return;
  287. if (_paintStroke.Count == 0) return;
  288. var labelTexts = new string[]
  289. { $"{hitPoint.x.ToString("F2")}, {hitPoint.y.ToString("F2")}, {hitPoint.z.ToString("F2")}" };
  290. InfoText.Draw(sceneView, labelTexts);
  291. }
  292. private static Vector3 GetTangent(Vector3 normal)
  293. {
  294. var rotation = Quaternion.AngleAxis(_brushAngle, Vector3.up);
  295. var tangent = Vector3.Cross(normal, rotation * Vector3.right);
  296. if (tangent.sqrMagnitude < 0.000001) tangent = Vector3.Cross(normal, rotation * Vector3.forward);
  297. tangent.Normalize();
  298. return tangent;
  299. }
  300. private static void DrawBrush(UnityEditor.SceneView sceneView, ref RaycastHit hit, bool preview)
  301. {
  302. var settings = BrushManager.settings;
  303. UpdateStrokeDirection(hit.point);
  304. if (PaletteManager.selectedBrush == null) return;
  305. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  306. hit.point = SnapAndUpdateGridOrigin(hit.point, SnapManager.settings.snappingEnabled,
  307. settings.paintOnPalettePrefabs, settings.paintOnMeshesWithoutCollider, false, Vector3.down);
  308. var tangent = GetTangent(hit.normal);
  309. var bitangent = Vector3.Cross(hit.normal, tangent);
  310. if (settings.brushShape == BrushToolSettings.BrushShape.POINT)
  311. {
  312. DrawCricleIndicator(hit.point, hit.normal, 0.1f, settings.maxHeightFromCenter,
  313. tangent, bitangent, hit.normal, settings.paintOnPalettePrefabs, true,
  314. settings.layerFilter, settings.tagFilter.ToArray());
  315. }
  316. else
  317. {
  318. UnityEditor.Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
  319. UnityEditor.Handles.color = Color.green;
  320. UnityEditor.Handles.DrawAAPolyLine(3, hit.point, hit.point + hit.normal * settings.maxHeightFromCenter);
  321. if (settings.brushShape == BrushToolSettings.BrushShape.CIRCLE)
  322. {
  323. DrawCricleIndicator(hit.point, hit.normal, settings.radius, settings.maxHeightFromCenter, tangent,
  324. bitangent, hit.normal, settings.paintOnPalettePrefabs, true,
  325. settings.layerFilter, settings.tagFilter.ToArray());
  326. }
  327. else if (settings.brushShape == BrushToolSettings.BrushShape.SQUARE)
  328. {
  329. DrawSquareIndicator(hit.point, hit.normal, settings.radius, settings.maxHeightFromCenter, tangent,
  330. bitangent, hit.normal, settings.paintOnPalettePrefabs, true,
  331. settings.layerFilter, settings.tagFilter.ToArray());
  332. }
  333. }
  334. if (preview) BrushstrokePreview(hit.point, hit.normal, tangent, bitangent, sceneView);
  335. }
  336. private static void BrushstrokePreview(Vector3 hitPoint, Vector3 normal,
  337. Vector3 tangent, Vector3 bitangent, UnityEditor.SceneView sceneView)
  338. {
  339. var camera = sceneView.camera;
  340. var settings = BrushManager.settings;
  341. _paintStroke.Clear();
  342. var nearbyObjectsAtDensitySpacing = new System.Collections.Generic.List<GameObject>();
  343. foreach (var strokeItem in BrushstrokeManager.brushstroke)
  344. {
  345. var worldPos = hitPoint + TangentSpaceToWorld(tangent, bitangent,
  346. new Vector2(strokeItem.tangentPosition.x, strokeItem.tangentPosition.y));
  347. var height = settings.heightType == BrushToolSettings.HeightType.CUSTOM
  348. ? settings.maxHeightFromCenter : settings.radius;
  349. var ray = new Ray(worldPos + normal * height, -normal);
  350. var in2DMode = strokeItem.settings.isAsset2D && sceneView.in2DMode;
  351. if (BrushRaycast(ray, out RaycastHit itemHit, height * 2f, settings.layerFilter,
  352. settings, settings.terrainLayerFilter) || in2DMode)
  353. {
  354. if (in2DMode)
  355. {
  356. itemHit.point = new Vector3(worldPos.x, worldPos.y, 0f);
  357. itemHit.normal = Vector3.forward;
  358. }
  359. else
  360. {
  361. var slope = Mathf.Abs(Vector3.Angle(Vector3.up, itemHit.normal));
  362. if (slope > 90f) slope = 180f - slope;
  363. if (slope < settings.slopeFilter.min || slope > settings.slopeFilter.max) continue;
  364. }
  365. var prefab = strokeItem.settings.prefab;
  366. if (prefab == null) continue;
  367. BrushSettings brushSettings = strokeItem.settings;
  368. if (settings.overwriteBrushProperties)
  369. {
  370. brushSettings = settings.brushSettings;
  371. }
  372. var itemRotation = Quaternion.AngleAxis(_brushAngle, Vector3.up);
  373. var itemPosition = itemHit.point;
  374. if (brushSettings.rotateToTheSurface)
  375. {
  376. var itemTangent = GetTangent(itemHit.normal);
  377. itemRotation = Quaternion.LookRotation(itemTangent, itemHit.normal);
  378. itemPosition += itemHit.normal * brushSettings.surfaceDistance;
  379. }
  380. else itemPosition += normal * brushSettings.surfaceDistance;
  381. if (settings.avoidOverlapping != BrushToolSettings.AvoidOverlappingType.DISABLED
  382. && settings.avoidOverlapping != BrushToolSettings.AvoidOverlappingType.WITH_ALL_OBJECTS)
  383. {
  384. var rSqr = settings.minSpacing * settings.minSpacing;
  385. var d = settings.density / 100f;
  386. var densitySpacing = Mathf.Sqrt(rSqr / d) * 0.99999f;
  387. octree.GetNearbyNonAlloc(itemPosition, densitySpacing, nearbyObjectsAtDensitySpacing);
  388. if (nearbyObjectsAtDensitySpacing.Count > 0)
  389. {
  390. var brushObjectsNearby = false;
  391. foreach (var obj in nearbyObjectsAtDensitySpacing)
  392. {
  393. if (settings.avoidOverlapping
  394. == BrushToolSettings.AvoidOverlappingType.WITH_BRUSH_PREFABS
  395. && PaletteManager.selectedBrush.ContainsSceneObject(obj))
  396. {
  397. brushObjectsNearby = true;
  398. break;
  399. }
  400. else if (settings.avoidOverlapping
  401. == BrushToolSettings.AvoidOverlappingType.WITH_PALETTE_PREFABS
  402. && PaletteManager.selectedPalette.ContainsSceneObject(obj))
  403. {
  404. brushObjectsNearby = true;
  405. break;
  406. }
  407. else if (settings.avoidOverlapping
  408. == BrushToolSettings.AvoidOverlappingType.WITH_SAME_PREFABS)
  409. {
  410. var outermostPrefab = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(obj);
  411. if (outermostPrefab == null) continue;
  412. var source = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(outermostPrefab);
  413. if (source == null) continue;
  414. if (prefab == source)
  415. {
  416. brushObjectsNearby = true;
  417. break;
  418. }
  419. }
  420. }
  421. if (brushObjectsNearby) continue;
  422. }
  423. }
  424. if (settings.orientAlongBrushstroke)
  425. {
  426. itemRotation = Quaternion.Euler(settings.additionalOrientationAngle)
  427. * Quaternion.LookRotation(_strokeDirection, itemRotation * Vector3.up);
  428. itemPosition = hitPoint + itemRotation * (itemPosition - hitPoint);
  429. }
  430. itemRotation *= Quaternion.Euler(strokeItem.additionalAngle);
  431. if (brushSettings.alwaysOrientUp)
  432. {
  433. var fw = Quaternion.Euler(strokeItem.additionalAngle) * itemHit.normal;
  434. fw.y = 0;
  435. const float minMag = 1e-6f;
  436. if (Mathf.Abs(fw.x) > minMag || Mathf.Abs(fw.z) > minMag)
  437. itemRotation = Quaternion.LookRotation(fw, Vector3.up);
  438. }
  439. itemPosition += itemRotation * brushSettings.localPositionOffset;
  440. if (brushSettings.embedInSurface && !brushSettings.embedAtPivotHeight)
  441. {
  442. var TRS = Matrix4x4.TRS(itemPosition, itemRotation,
  443. Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier));
  444. var localDirection = Quaternion.Inverse(itemRotation) * -normal;
  445. float magnitudeInDirection;
  446. var furthestVertices = strokeItem.settings.GetFurthestVerticesInDirection(localDirection,
  447. out magnitudeInDirection);
  448. var distanceTosurface = GetDistanceToSurface(furthestVertices, TRS, -normal,
  449. Mathf.Abs(magnitudeInDirection), BrushManager.settings.paintOnPalettePrefabs,
  450. BrushManager.settings.paintOnMeshesWithoutCollider, out Transform surfaceTransform, prefab);
  451. itemPosition -= normal * distanceTosurface;
  452. }
  453. var itemScale = Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier);
  454. if (settings.avoidOverlapping == BrushToolSettings.AvoidOverlappingType.WITH_ALL_OBJECTS)
  455. {
  456. var itemBounds = BoundsUtils.GetBoundsRecursive(prefab.transform, Quaternion.identity);
  457. var pivotToCenter = itemBounds.center - prefab.transform.position;
  458. pivotToCenter = Vector3.Scale(pivotToCenter, strokeItem.scaleMultiplier);
  459. pivotToCenter = itemRotation * pivotToCenter;
  460. var itemCenter = itemPosition + pivotToCenter;
  461. var itemHalfExtends = Vector3.Scale(itemBounds.size / 2, strokeItem.scaleMultiplier);
  462. var overlaped = Physics.OverlapBox(itemCenter, itemHalfExtends,
  463. itemRotation, -1, QueryTriggerInteraction.Ignore)
  464. .Where(c => c != itemHit.collider && IsVisible(c.gameObject)).ToArray();
  465. if (overlaped.Length > 0) continue;
  466. }
  467. Transform surface = null;
  468. GameObject colObj = null;
  469. if (itemHit.collider != null)
  470. colObj = PWBCore.GetGameObjectFromTempCollider(itemHit.collider.gameObject);
  471. if (colObj != null) surface = colObj.transform;
  472. var layer = settings.overwritePrefabLayer ? settings.layer : prefab.layer;
  473. Transform parentTransform = GetParent(settings, prefab.name, false, surface);
  474. _paintStroke.Add(new PaintStrokeItem(prefab, itemPosition,
  475. itemRotation * Quaternion.Euler(prefab.transform.eulerAngles),
  476. itemScale, layer, parentTransform, surface, strokeItem.flipX, strokeItem.flipY));
  477. if (settings.showPreview)
  478. {
  479. var rootToWorld = Matrix4x4.TRS(itemPosition, itemRotation, strokeItem.scaleMultiplier)
  480. * Matrix4x4.Translate(-prefab.transform.position);
  481. PreviewBrushItem(prefab, rootToWorld, layer, camera, false, false, strokeItem.flipX, strokeItem.flipY);
  482. }
  483. }
  484. }
  485. }
  486. }
  487. #endregion
  488. }