internal abstract void RegisterUndo(BrushTarget brushTarget);
internal override void OnBrushSettingsChanged(BrushTarget target, BrushSettings settings) { base.OnBrushSettingsChanged(target, settings); RebuildColorTargets(brushColor, settings.strength); }
// Called whenever the brush is moved. Note that @target may have a null editableObject. internal override void OnBrushMove(BrushTarget target, BrushSettings settings) { base.OnBrushMove(target, settings); }
void OnBrushEnter(BrushTarget target, BrushSettings settings) { mode.OnBrushEnter(target.editableObject, settings); }
/// <summary> /// Draw gizmos taking into account handling of normal by smooth brush mode. /// </summary> /// <param name="target">Current target Object</param> ///// <param name="settings">Current brush settings</param> internal override void DrawGizmos(BrushTarget target, BrushSettings settings) { UpdateBrushGizmosColor(); Vector3 normal = s_SmoothDirection.value.ToVector3(); int rayCount = target.raycastHits.Count; PolyMesh mesh = target.editableObject.editMesh; int vertexCount = mesh.vertexCount; Vector3[] normals = (s_SmoothDirection == PolyDirection.BrushNormal) ? mesh.normals : null; // don't use target.GetAllWeights because brush normal needs // to know which ray to use for normal for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } if (s_SmoothDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector && brushNormalOnBeginApply.Count > ri) { normal = brushNormalOnBeginApply[ri]; } else { // get the highest weighted vertex to use its direction computation float highestWeight = .0001f; int highestIndex = -1; for (int i = 0; i < commonVertexCount; i++) { int index = commonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && nonManifoldIndices.Contains(index))) { continue; } if (hit.weights[index] > highestWeight) { highestIndex = index; } } if (highestIndex != -1) { normal = Math.WeightedAverage(normals, neighborLookup[highestIndex], hit.weights).normalized; } else { normal = hit.normal; } } } else if (s_SmoothDirection == PolyDirection.VertexNormal) { normal = hit.normal; } PolyHandles.DrawBrush(hit.position, normal, settings, target.localToWorldMatrix, innerColor, outerColor); } }
/// <summary> /// Calculates the per-vertex weight for each raycast hit and fills in brush target weights. /// </summary> /// <param name="target"></param> /// <param name="settings"></param> /// <param name="tool"></param> /// <param name="bMode"></param> internal static void CalculateWeightedVertices(BrushTarget target, BrushSettings settings, BrushTool tool = BrushTool.None, BrushMode bMode = null) { //null checks if (target == null || settings == null) { return; } if (target.editableObject == null) { return; } bool uniformScale = PolyMath.VectorIsUniform(target.transform.lossyScale); float scale = uniformScale ? 1f / target.transform.lossyScale.x : 1f; PolyMesh mesh = target.editableObject.visualMesh; if (tool == BrushTool.Texture && mesh.subMeshCount > 1) { var mode = bMode as BrushModeTexture; int[] submeshIndices = mesh.subMeshes[mode.currentMeshACIndex].indexes; //List<List<int>> common = PolyMeshUtility.GetCommonVertices(mesh); Transform transform = target.transform; int vertexCount = mesh.vertexCount; Vector3[] vertices = mesh.vertices; if (!uniformScale) { Vector3[] world = new Vector3[vertexCount]; for (int i = 0; i < vertexCount; i++) { world[i] = transform.TransformPoint(vertices[i]); } vertices = world; } AnimationCurve curve = settings.falloffCurve; float radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f); Vector3 hitPosition = Vector3.zero; PolyRaycastHit hit; for (int n = 0; n < target.raycastHits.Count; n++) { hit = target.raycastHits[n]; hit.SetVertexCount(vertexCount); for (int i = 0; i < vertexCount; i++) { hit.weights[i] = 0f; } hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position); for (int i = 0; i < submeshIndices.Length; i++) { int currentIndex = submeshIndices[i]; float dist = (hitPosition - vertices[currentIndex]).magnitude; float delta = radius - dist; if (delta >= 0) { float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp(delta / falloff_mag, 0f, 1f)), 0f, 1f); hit.weights[currentIndex] = weight; } } } } else { List <List <int> > common = PolyMeshUtility.GetCommonVertices(mesh); Transform transform = target.transform; int vertexCount = mesh.vertexCount; Vector3[] vertices = mesh.vertices; if (!uniformScale) { Vector3[] world = new Vector3[vertexCount]; for (int i = 0; i < vertexCount; i++) { world[i] = transform.TransformPoint(vertices[i]); } vertices = world; } AnimationCurve curve = settings.falloffCurve; float radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f); Vector3 hitPosition = Vector3.zero; PolyRaycastHit hit; for (int n = 0; n < target.raycastHits.Count; n++) { hit = target.raycastHits[n]; hit.SetVertexCount(vertexCount); hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position); for (int i = 0; i < common.Count; i++) { int commonArrayCount = common[i].Count; float sqrDist = (hitPosition - vertices[common[i][0]]).sqrMagnitude; if (sqrDist > radius * radius) { for (int j = 0; j < commonArrayCount; j++) { hit.weights[common[i][j]] = 0f; } } else { float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp((radius - Mathf.Sqrt(sqrDist)) / falloff_mag, 0f, 1f)), 0f, 1f); for (int j = 0; j < commonArrayCount; j++) { hit.weights[common[i][j]] = weight; } } } } } target.GetAllWeights(true); }
/// <summary> /// Calculates the per-vertex weight for each raycast hit and fills in brush target weights. /// </summary> /// <param name="target"></param> /// <param name="settings"></param> /// <param name="tool"></param> /// <param name="bMode"></param> internal static void CalculateWeightedVertices(BrushTarget target, BrushSettings settings, BrushTool tool = BrushTool.None, BrushMode bMode = null) { if (target == null || settings == null) { return; } if (target.editableObject == null) { return; } bool uniformScale = Math.VectorIsUniform(target.transform.lossyScale); float scale = uniformScale ? 1f / target.transform.lossyScale.x : 1f; PolyMesh mesh = target.editableObject.visualMesh; Transform transform = target.transform; int vertexCount = mesh.vertexCount; Vector3[] vertices = mesh.vertices; if (!uniformScale) { // As we only increase size only when it's needed, always make sure to // use the vertexCount variable in loop statements and not the buffer length. if (s_WorldBuffer.Length < vertexCount) { System.Array.Resize <Vector3>(ref s_WorldBuffer, vertexCount); } for (int i = 0; i < vertexCount; i++) { s_WorldBuffer[i] = transform.TransformPoint(vertices[i]); } vertices = s_WorldBuffer; } AnimationCurve curve = settings.falloffCurve; float radius = settings.radius * scale, falloff_mag = Mathf.Max((radius - radius * settings.falloff), 0.00001f); Vector3 hitPosition = Vector3.zero; PolyRaycastHit hit; if (tool == BrushTool.Texture && mesh.subMeshCount > 1) { var mode = bMode as BrushModeTexture; int[] submeshIndices = mesh.subMeshes[mode.currentMeshACIndex].indexes; for (int n = 0; n < target.raycastHits.Count; n++) { hit = target.raycastHits[n]; hit.SetVertexCount(vertexCount); for (int i = 0; i < vertexCount; i++) { hit.weights[i] = 0f; } hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position); for (int i = 0; i < submeshIndices.Length; i++) { int currentIndex = submeshIndices[i]; float dist = (hitPosition - vertices[currentIndex]).magnitude; float delta = radius - dist; if (delta >= 0) { float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp(delta / falloff_mag, 0f, 1f)), 0f, 1f); hit.weights[currentIndex] = weight; } } } } else { int[][] common = PolyMeshUtility.GetCommonVertices(mesh); Vector3 buf = Vector3.zero; for (int n = 0; n < target.raycastHits.Count; n++) { hit = target.raycastHits[n]; hit.SetVertexCount(vertexCount); hitPosition = uniformScale ? hit.position : transform.TransformPoint(hit.position); for (int i = 0; i < common.Length; i++) { int[] commonItem = common[i]; int commonArrayCount = commonItem.Length; Math.Subtract(vertices[commonItem[0]], hitPosition, ref buf); float sqrDist = buf.sqrMagnitude; if (sqrDist > radius * radius) { for (int j = 0; j < commonArrayCount; j++) { hit.weights[commonItem[j]] = 0f; } } else { float weight = Mathf.Clamp(curve.Evaluate(1f - Mathf.Clamp((radius - Mathf.Sqrt(sqrDist)) / falloff_mag, 0f, 1f)), 0f, 1f); for (int j = 0; j < commonArrayCount; j++) { hit.weights[commonItem[j]] = weight; } } } } } target.GetAllWeights(true); }
/// <summary> /// Handle Undo locally since it doesn't follow the same pattern as mesh modifications. /// </summary> /// <param name="brushTarget"></param> internal override void RegisterUndo(BrushTarget brushTarget) { }
internal override void OnBrushSettingsChanged(BrushTarget target, BrushSettings settings) { base.OnBrushSettingsChanged(target, settings); m_MeshVertexColors.RebuildColorTargets(m_BrushColor, settings.strength, mask); }
// Called whenever the brush is moved. Note that @target may have a null editableObject. internal override void OnBrushMove(BrushTarget target, BrushSettings settings) { base.OnBrushMove(target, settings); if (!Util.IsValid(target)) { return; } bool invert = settings.isUserHoldingControl; PolyMesh mesh = target.editableObject.editMesh; int vertexCount = mesh.vertexCount; float[] weights = target.GetAllWeights(); switch (paintMode) { case PaintMode.Flood: for (int i = 0; i < vertexCount; i++) { m_MeshVertexColors.Colors[i] = invert? s_WhiteColor : m_MeshVertexColors.TargetColors[i]; } break; case PaintMode.Fill: System.Array.Copy(m_MeshVertexColors.OriginalColors, m_MeshVertexColors.Colors, vertexCount); int[] indices = target.editableObject.editMesh.GetTriangles(); int index = 0; foreach (PolyRaycastHit hit in target.raycastHits) { if (hit.triangle > -1) { index = hit.triangle * 3; m_MeshVertexColors.Colors[indices[index + 0]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 0]]; m_MeshVertexColors.Colors[indices[index + 1]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 1]]; m_MeshVertexColors.Colors[indices[index + 2]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 2]]; m_FillModeEdges[0].x = indices[index + 0]; m_FillModeEdges[0].y = indices[index + 1]; m_FillModeEdges[1].x = indices[index + 1]; m_FillModeEdges[1].y = indices[index + 2]; m_FillModeEdges[2].x = indices[index + 2]; m_FillModeEdges[2].y = indices[index + 0]; for (int i = 0; i < 3; i++) { if (m_TriangleLookup.TryGetValue(m_FillModeEdges[i], out m_FillModeAdjacentTriangles)) { for (int n = 0; n < m_FillModeAdjacentTriangles.Count; n++) { index = m_FillModeAdjacentTriangles[n] * 3; m_MeshVertexColors.Colors[indices[index + 0]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 0]]; m_MeshVertexColors.Colors[indices[index + 1]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 1]]; m_MeshVertexColors.Colors[indices[index + 2]] = invert ? s_WhiteColor : m_MeshVertexColors.TargetColors[indices[index + 2]]; } } } } } break; default: { for (int i = 0; i < vertexCount; i++) { m_MeshVertexColors.Colors[i] = Util.Lerp(m_MeshVertexColors.OriginalColors[i], invert ? m_MeshVertexColors.EraseColors[i] : m_MeshVertexColors.TargetColors[i], mask, weights[i]); } break; } } target.editableObject.editMesh.colors = m_MeshVertexColors.Colors; target.editableObject.ApplyMeshAttributes(MeshChannel.Color); }
/// <summary> /// Calculate the weights for this ray. /// </summary> /// <param name="mouseRay">The ray used to calculate weights</param> /// <param name="target">The object on which to calculate the weights</param> /// <returns>true if mouseRay hits the target, false otherwise</returns> bool DoMeshRaycast(Ray mouseRay, BrushTarget target, MirrorSettings mirrorSettings) { m_SecondaryBrushTargets.Clear(); if (!Util.IsValid(target)) { return(false); } target.ClearRaycasts(); EditableObject editable = target.editableObject; s_Rays.Clear(); s_Rays.Add(mouseRay); if (mirrorSettings.Axes != BrushMirror.None) { for (int i = 0; i < 3; i++) { if (((uint)mirrorSettings.Axes & (1u << i)) < 1) { continue; } int len = s_Rays.Count; for (int n = 0; n < len; n++) { Vector3 flipVec = ((BrushMirror)(1u << i)).ToVector3(); if (mirrorSettings.Space == MirrorCoordinateSpace.World) { Vector3 cen = editable.gameObjectAttached.GetComponent <Renderer>().bounds.center; s_Rays.Add(new Ray(Vector3.Scale(s_Rays[n].origin - cen, flipVec) + cen, Vector3.Scale(s_Rays[n].direction, flipVec))); } else { Transform t = SceneView.lastActiveSceneView.camera.transform; Vector3 o = t.InverseTransformPoint(s_Rays[n].origin); Vector3 d = t.InverseTransformDirection(s_Rays[n].direction); s_Rays.Add(new Ray(t.TransformPoint(Vector3.Scale(o, flipVec)), t.TransformDirection(Vector3.Scale(d, flipVec)))); } } } } bool hitMesh = false; int[] triangles = editable.editMesh.GetTriangles(); foreach (Ray ray in s_Rays) { PolyRaycastHit hit; if (PolySceneUtility.WorldRaycast(ray, editable.transform, editable.visualMesh.vertices, triangles, out hit)) { target.raycastHits.Add(hit); hitMesh = true; } } PolySceneUtility.CalculateWeightedVertices(target, brushSettings, tool, mode); if (hitMesh) { Transform[] trs = Selection.GetTransforms(SelectionMode.Unfiltered); var hits = target.raycastHits; foreach (var selectedTransform in trs) { bool isValid = false; if (selectedTransform != editable.transform) { BrushTarget secondaryTarget = GetOrCreateBrushTarget(selectedTransform.gameObject); isValid = Util.IsValid(secondaryTarget); if (isValid) { m_SecondaryBrushTargets.Add(secondaryTarget); secondaryTarget.ClearRaycasts(); foreach (var hit in hits) { PolyRaycastHit secondaryHit = new PolyRaycastHit(hit.distance, secondaryTarget.transform.InverseTransformPoint(editable.transform.TransformPoint(hit.position)), hit.normal, -1); secondaryTarget.raycastHits.Add(secondaryHit); } } PolySceneUtility.CalculateWeightedVertices(secondaryTarget, brushSettings, tool, mode); } } } return(hitMesh); }
/// <summary> /// Update the current brush object and weights with the current mouse position. /// </summary> /// <param name="mousePosition">current mouse position (from Event)</param> /// <param name="isDrag">optional, is dragging the mouse cursor</param> /// <param name="overridenGO">optional, provides an already selected gameobject (used in unit tests only)</param> /// <param name="overridenRay"> optional, provides a ray already created (used in unit tests only)</param> internal void UpdateBrush(Vector2 mousePosition, bool isUserHoldingControl = false, bool isUserHoldingShift = false, bool isDrag = false, GameObject overridenGO = null, Ray?overridenRay = null) { MirrorSettings mirrorSettings = m_BrushMirrorEditor.settings; // Must check HandleUtility.PickGameObject only during MouseMoveEvents or errors will rain. GameObject go = null; brushTarget = null; GameObject cur = null; #if UNITY_2021_1_OR_NEWER int materialIndex; cur = HandleUtility.PickGameObject(mousePosition, false, null, Selection.gameObjects, out materialIndex); if (cur != null) { brushTarget = GetOrCreateBrushTarget(cur); } if (brushTarget != null) { go = cur; } #else int max = 0; // safeguard against unforeseen while loop errors crashing unity do { int tmp; // overloaded PickGameObject ignores array of GameObjects, this is used // when there are non-selected gameObjects between the mouse and selected // gameObjects. cur = overridenGO; if (cur == null) { m_IgnoreDrag.RemoveAll(x => x == null); cur = HandleUtility.PickGameObject(mousePosition, m_IgnoreDrag.ToArray(), out tmp); } if (cur != null) { if (!PolyEditorUtility.InSelection(cur)) { if (!m_IgnoreDrag.Contains(cur)) { m_IgnoreDrag.Add(cur); } } else { brushTarget = GetOrCreateBrushTarget(cur); if (brushTarget != null) { go = cur; } else { m_IgnoreDrag.Add(cur); } } } } while(go == null && cur != null && max++ < 128); #endif bool mouseHoverTargetChanged = false; Ray mouseRay = overridenRay != null ? (Ray)overridenRay : HandleUtility.GUIPointToWorldRay(mousePosition); // if the mouse hover picked up a valid editable, raycast against that. otherwise // raycast all meshes in selection if (go == null) { foreach (var kvp in m_Hovering) { BrushTarget t = kvp.Value; if (Util.IsValid(t) && DoMeshRaycast(mouseRay, t, mirrorSettings)) { brushTarget = t; go = t.gameObject; break; } } } else { if (!DoMeshRaycast(mouseRay, brushTarget, mirrorSettings)) { brushTarget = null; return; } } // if m_Hovering off another gameobject, call OnBrushExit on that last one and mark the // target as having been changed if (go != m_LastHoveredGameObject) { if (m_LastHoveredGameObject) { OnBrushExit(m_LastHoveredGameObject); } if (m_ApplyingBrush) { mode.OnBrushFinishApply(brushTarget, brushSettings); } mouseHoverTargetChanged = true; m_LastHoveredGameObject = go; foreach (var secondaryTarget in m_LastSecondaryBrushTargets) { if (!m_SecondaryBrushTargets.Contains(secondaryTarget)) { OnBrushExit(secondaryTarget.gameObject); if (m_ApplyingBrush) { mode.OnBrushFinishApply(brushTarget, brushSettings); } } } } if (brushTarget == null) { SceneView.RepaintAll(); DoRepaint(); m_LastSecondaryBrushTargets.Clear(); m_SecondaryBrushTargets.Clear(); return; } brushSettings.isUserHoldingControl = isUserHoldingControl; brushSettings.isUserHoldingShift = isUserHoldingShift; if (mouseHoverTargetChanged) { foreach (var secondaryTarget in m_SecondaryBrushTargets) { if (!m_LastSecondaryBrushTargets.Contains(secondaryTarget)) { OnBrushEnter(secondaryTarget, brushSettings); if (m_ApplyingBrush) { mode.OnBrushBeginApply(secondaryTarget, brushSettings); } } } //The active brushtarget is the last one to notify the brush OnBrushEnter(brushTarget, brushSettings); // brush is in use, adding a new object to the undo if (m_ApplyingBrush) { if (!m_UndoQueue.Contains(go)) { int curGroup = Undo.GetCurrentGroup(); brushTarget.editableObject.isDirty = true; OnBrushBeginApply(brushTarget, brushSettings); Undo.CollapseUndoOperations(curGroup); } else { mode.OnBrushBeginApply(brushTarget, brushSettings); } } } m_LastSecondaryBrushTargets.Clear(); m_LastSecondaryBrushTargets.AddRange(m_SecondaryBrushTargets); OnBrushMove(); SceneView.RepaintAll(); DoRepaint(); }
void OnGUI() { Profiler.BeginSample("Polybrush GUI"); Event e = Event.current; GUILayout.Space(8); DoContextMenu(); DrawToolbar(); CheckForEscapeKey(e); // Call current mode GUI if (mode != null) { m_Scroll = EditorGUILayout.BeginScrollView(m_Scroll); DrawBrushSettings(); m_BrushMirrorEditor.OnGUI(); if (tool != BrushTool.None) { DrawActiveToolmodeSettings(); } EditorGUILayout.Space(); if (EditorGUI.EndChangeCheck()) { mode.OnBrushSettingsChanged(brushTarget, brushSettings); } EditorGUILayout.EndScrollView(); } #if POLYBRUSH_DEBUG GUILayout.Label("DEBUG", EditorStyles.boldLabel); GUILayout.Label("target: " + (Util.IsValid(brushTarget) ? brushTarget.editableObject.gameObjectAttached.name : "null")); GUILayout.Label("vertex: " + (Util.IsValid(brushTarget) ? brushTarget.editableObject.vertexCount : 0)); GUILayout.Label("applying: " + m_ApplyingBrush); GUILayout.Label("lockBrushToFirst: " + s_LockBrushToFirst); GUILayout.Label("lastHoveredGameObject: " + m_LastHoveredGameObject); GUILayout.Space(6); foreach (var kvp in m_Hovering) { BrushTarget t = kvp.Value; EditableObject dbg_editable = t.editableObject; GUILayout.Label("Vertex Streams: " + dbg_editable.usingVertexStreams); GUILayout.Label("Original: " + (dbg_editable.originalMesh == null ? "null" : dbg_editable.originalMesh.name)); GUILayout.Label("Active: " + (dbg_editable.editMesh == null ? "null" : dbg_editable.editMesh.name)); GUILayout.Label("Graphics: " + (dbg_editable.graphicsMesh == null ? "null" : dbg_editable.graphicsMesh.name)); } #endif if (m_WantsRepaint) { m_WantsRepaint = false; Repaint(); } Profiler.EndSample(); }
// Called whenever the brush is moved. Note that @target may have a null editableObject. internal override void OnBrushMove(BrushTarget target, BrushSettings settings) { base.OnBrushMove(target, settings); if (!Util.IsValid(target) || !m_LikelySupportsTextureBlending) { return; } bool invert = settings.isUserHoldingControl; float[] weights; if (paintMode == PaintMode.Brush) { weights = target.GetAllWeights(); } else if (paintMode == PaintMode.Flood) { weights = new float[m_VertexCount]; for (int i = 0; i < m_VertexCount; i++) { weights[i] = 1f; } } else { weights = new float[m_VertexCount]; int[] indices = target.editableObject.editMesh.GetTriangles(); int index = 0; float weightTarget = 1f; foreach (PolyRaycastHit hit in target.raycastHits) { if (hit.triangle > -1) { index = hit.triangle * 3; m_FillModeEdges[0].x = indices[index + 0]; m_FillModeEdges[0].y = indices[index + 1]; m_FillModeEdges[1].x = indices[index + 1]; m_FillModeEdges[1].y = indices[index + 2]; m_FillModeEdges[2].x = indices[index + 2]; m_FillModeEdges[2].y = indices[index + 0]; for (int i = 0; i < 3; i++) { if (m_TriangleLookup.TryGetValue(m_FillModeEdges[i], out m_FillModeAdjacentTriangles)) { for (int n = 0; n < m_FillModeAdjacentTriangles.Count; n++) { index = m_FillModeAdjacentTriangles[n] * 3; weights[indices[index]] = weightTarget; weights[indices[index + 1]] = weightTarget; weights[indices[index + 2]] = weightTarget; } } } } } } if (m_SelectedAttributeIndex < 0 || m_SelectedAttributeIndex >= meshAttributes.Length) { SetBrushColorWithAttributeIndex(0); } int mask = meshAttributes[m_SelectedAttributeIndex].mask; splat_current.LerpWeights(splat_cache, invert ? splat_erase : splat_target, mask, weights); splat_current.Apply(target.editableObject.editMesh); target.editableObject.ApplyMeshAttributes(); }
/// <summary> /// Called whenever the brush is moved. Note that @target may have a null editableObject. /// </summary> /// <param name="target">Current target of the brush</param> /// <param name="settings">Current brush settings</param> internal virtual void OnBrushMove(BrushTarget target, BrushSettings settings) { UpdateTempComponent(target, settings); }
internal override void DrawGizmos(BrushTarget target, BrushSettings settings) { PolyMesh mesh = target.editableObject.editMesh; if (Util.IsValid(target) && paintMode == PaintMode.Fill) { Vector3[] vertices = mesh.vertices; int[] indices = mesh.GetTriangles(); PolyHandles.PushMatrix(); PolyHandles.PushHandleColor(); Handles.matrix = target.transform.localToWorldMatrix; int index = 0; foreach (PolyRaycastHit hit in target.raycastHits) { if (hit.triangle > -1) { Handles.color = Color.green; index = hit.triangle * 3; Handles.DrawLine(vertices[indices[index + 0]] + hit.normal * .1f, vertices[indices[index + 1]] + hit.normal * .1f); Handles.DrawLine(vertices[indices[index + 1]] + hit.normal * .1f, vertices[indices[index + 2]] + hit.normal * .1f); Handles.DrawLine(vertices[indices[index + 2]] + hit.normal * .1f, vertices[indices[index + 0]] + hit.normal * .1f); m_FillModeEdges[0].x = indices[index + 0]; m_FillModeEdges[0].y = indices[index + 1]; m_FillModeEdges[1].x = indices[index + 1]; m_FillModeEdges[1].y = indices[index + 2]; m_FillModeEdges[2].x = indices[index + 2]; m_FillModeEdges[2].y = indices[index + 0]; for (int i = 0; i < 3; i++) { if (m_TriangleLookup.TryGetValue(m_FillModeEdges[i], out m_FillModeAdjacentTriangles)) { for (int n = 0; n < m_FillModeAdjacentTriangles.Count; n++) { index = m_FillModeAdjacentTriangles[n] * 3; Handles.DrawLine(vertices[indices[index + 0]] + hit.normal * .1f, vertices[indices[index + 1]] + hit.normal * .1f); Handles.DrawLine(vertices[indices[index + 1]] + hit.normal * .1f, vertices[indices[index + 2]] + hit.normal * .1f); Handles.DrawLine(vertices[indices[index + 2]] + hit.normal * .1f, vertices[indices[index + 0]] + hit.normal * .1f); } } } } } PolyHandles.PopHandleColor(); PolyHandles.PopMatrix(); } else { base.DrawGizmos(target, settings); } }
/// <summary> /// Called when the mouse begins a drag across a valid target. /// </summary> /// <param name="target"> The object the mouse is dragging on</param> /// <param name="settings">Current brush settings</param> internal virtual void OnBrushBeginApply(BrushTarget target, BrushSettings settings) { }
void PlaceGameObject(PolyRaycastHit hit, PrefabAndSettings prefabAndSettings, BrushTarget target, BrushSettings settings) { if (prefabAndSettings == null) { return; } GameObject prefab = prefabAndSettings.gameObject; var worldPosition = target.transform.TransformPoint(hit.position); var worldNormal = target.transform.TransformDirection(hit.normal); Ray ray = RandomRay(worldPosition, worldNormal, settings.radius, settings.falloff, settings.falloffCurve); ray.origin = target.transform.InverseTransformPoint(ray.origin); ray.direction = target.transform.InverseTransformDirection(ray.direction); PolyRaycastHit rand_hit; Vector3[] vertices = target.editableObject.editMesh.vertices; int[] triangles = target.editableObject.editMesh.GetTriangles(); if (PolySceneUtility.MeshRaycast(ray, vertices, triangles, out rand_hit)) { PlacementSettings placementSettings = prefabAndSettings.settings; Vector3 scaleSetting = prefab.transform.localScale; if (placementSettings.uniformBool) { float uniformScale = Random.Range(placementSettings.uniformScale.x, placementSettings.uniformScale.y); scaleSetting *= uniformScale; } else { if (placementSettings.xScaleBool) { scaleSetting.x = Random.Range(placementSettings.scaleRangeMin.x, placementSettings.scaleRangeMax.x); } if (placementSettings.yScaleBool) { scaleSetting.y = Random.Range(placementSettings.scaleRangeMin.y, placementSettings.scaleRangeMax.y); } if (placementSettings.zScaleBool) { scaleSetting.z = Random.Range(placementSettings.scaleRangeMin.z, placementSettings.scaleRangeMax.z); } } Vector3 rotationSetting = Vector3.zero; if (placementSettings.xRotationBool) { rotationSetting.x = Random.Range(placementSettings.rotationRangeMin.x, placementSettings.rotationRangeMax.x); } if (placementSettings.yRotationBool) { rotationSetting.y = Random.Range(placementSettings.rotationRangeMin.y, placementSettings.rotationRangeMax.y); } if (placementSettings.xRotationBool) { rotationSetting.z = Random.Range(placementSettings.rotationRangeMin.z, placementSettings.rotationRangeMax.z); } Quaternion rotation = SceneView.lastActiveSceneView.in2DMode ? Quaternion.FromToRotation(-Vector3.forward, target.transform.TransformDirection(rand_hit.normal)): Quaternion.FromToRotation(Vector3.up, target.transform.TransformDirection(rand_hit.normal)); GameObject inst = (GameObject)PrefabUtility.InstantiatePrefab(prefab); inst.transform.position = target.transform.TransformPoint(rand_hit.position); inst.transform.rotation = rotation; inst.transform.localScale = scaleSetting; float pivotOffset = s_UsePivotForPlacement ? 0f : GetPivotOffset(inst); inst.name = FormatInstanceName(prefab); inst.transform.position = inst.transform.position - (inst.transform.up * pivotOffset); inst.transform.rotation = inst.transform.rotation * Quaternion.Euler(rotationSetting); if (SceneView.lastActiveSceneView.in2DMode) { inst.transform.position += Vector3.back * s_2DDepthOffset; } if (s_AvoidOverlappingGameObjects && TestIntersection(inst)) { Object.DestroyImmediate(inst); return; } if (s_ParentObjectWithSurface) { inst.transform.SetParent(target.transform); } m_PrefabsInstances.Add(inst); Undo.RegisterCreatedObjectUndo(inst, UndoMessage); } }
/// <summary> /// Called every time the brush should apply itself to a valid target. Default is on mouse move. /// </summary> /// <param name="target">Object on which to apply the brush</param> /// <param name="settings">Current brush settings</param> internal abstract void OnBrushApply(BrushTarget target, BrushSettings settings);
internal override void OnBrushApply(BrushTarget target, BrushSettings settings) { if (!likelyToSupportVertexSculpt) { return; } int rayCount = target.raycastHits.Count; Vector3 v, t, avg, dirVec = s_SmoothDirection.value.ToVector3(); Plane plane = new Plane(Vector3.up, Vector3.zero); PolyMesh mesh = target.editableObject.editMesh; Vector3[] normals = (s_SmoothDirection == PolyDirection.BrushNormal) ? mesh.normals : null; int vertexCount = mesh.vertexCount; // don't use target.GetAllWeights because brush normal needs // to know which ray to use for normal for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } for (int i = 0; i < commonVertexCount; i++) { int index = commonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && nonManifoldIndices.Contains(index))) { continue; } v = vertices[index]; if (s_SmoothDirection == PolyDirection.VertexNormal) { avg = Math.Average(vertices, neighborLookup[index]); } else { avg = Math.WeightedAverage(vertices, neighborLookup[index], hit.weights); if (s_SmoothDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector) { dirVec = brushNormalOnBeginApply[ri].normalized; } else { dirVec = Math.WeightedAverage(normals, neighborLookup[index], hit.weights).normalized; } } plane.SetNormalAndPosition(dirVec, avg); avg = v - dirVec * plane.GetDistanceToPoint(v); } t = Vector3.Lerp(v, avg, hit.weights[index]); int[] indices = commonVertices[i]; Vector3 pos = v + (t - v) * settings.strength * SMOOTH_STRENGTH_MODIFIER; for (int n = 0; n < indices.Length; n++) { vertices[indices[n]] = pos; } } } mesh.vertices = vertices; if (tempComponent != null) { tempComponent.OnVerticesMoved(mesh); } base.OnBrushApply(target, settings); }
/// <summary> /// Called when a brush application has finished. Use this to clean up temporary resources or apply /// deferred actions to a mesh (rebuild UV2, tangents, whatever). /// </summary> /// <param name="target">The Object the brush was being applied on</param> /// <param name="settings">Current brush settings</param> internal virtual void OnBrushFinishApply(BrushTarget target, BrushSettings settings) { DestroyTempComponent(); }
internal override void OnBrushSettingsChanged(BrushTarget target, BrushSettings settings) { base.OnBrushSettingsChanged(target, settings); }
/// <summary> /// Draw scene gizmos. Base implementation draws the brush preview. /// </summary> /// <param name="target">Current target Object</param> /// <param name="settings">Current brush settings</param> internal virtual void DrawGizmos(BrushTarget target, BrushSettings settings) { UpdateBrushGizmosColor(); foreach (PolyRaycastHit hit in target.raycastHits) { PolyHandles.DrawBrush(hit.position, hit.normal, settings, target.localToWorldMatrix, innerColor, outerColor); } #if Z_DEBUG #if Z_DRAW_WEIGHTS || DRAW_PER_VERTEX_ATTRIBUTES float[] w = target.GetAllWeights(); #endif #if Z_DRAW_WEIGHTS Mesh m = target.mesh; Vector3[] v = m.vertices; GUIContent content = new GUIContent("", ""); Handles.BeginGUI(); for (int i = 0; i < v.Length; i++) { if (w[i] < .0001f) { continue; } content.text = w[i].ToString("F2"); GUI.Label(HandleUtility.WorldPointToSizedRect(target.transform.TransformPoint(v[i]), content, EditorStyles.label), content); } Handles.EndGUI(); #endif #if DRAW_PER_VERTEX_ATTRIBUTES Mesh m = target.editableObject.editMesh; Color32[] colors = m.colors; Vector4[] tangents = m.tangents; List <Vector4> uv0 = m.uv0; List <Vector4> uv1 = m.uv1; List <Vector4> uv2 = m.uv2; List <Vector4> uv3 = m.uv3; int vertexCount = m.vertexCount; Vector3[] verts = m.vertices; GUIContent gc = new GUIContent(""); List <List <int> > common = MeshUtility.GetCommonVertices(m); System.Text.StringBuilder sb = new System.Text.StringBuilder(); Handles.BeginGUI(); foreach (List <int> l in common) { if (w[l[0]] < .001) { continue; } Vector3 v = target.transform.TransformPoint(verts[l[0]]); if (colors != null) { sb.AppendLine("color: " + colors[l[0]].ToString("F2")); } if (tangents != null) { sb.AppendLine("tangent: " + tangents[l[0]].ToString("F2")); } if (uv0 != null && uv0.Count == vertexCount) { sb.AppendLine("uv0: " + uv0[l[0]].ToString("F2")); } if (uv1 != null && uv1.Count == vertexCount) { sb.AppendLine("uv1: " + uv1[l[0]].ToString("F2")); } if (uv2 != null && uv2.Count == vertexCount) { sb.AppendLine("uv2: " + uv2[l[0]].ToString("F2")); } if (uv3 != null && uv3.Count == vertexCount) { sb.AppendLine("uv3: " + uv3[l[0]].ToString("F2")); } gc.text = sb.ToString(); sb.Remove(0, sb.Length); // @todo .NET 4.0 GUI.Label(HandleUtility.WorldPointToSizedRect(v, gc, EditorStyles.label), gc); } Handles.EndGUI(); #endif #endif }
internal override void OnBrushBeginApply(BrushTarget target, BrushSettings settings) { base.OnBrushBeginApply(target, settings); m_PrefabsInstances = PolySceneUtility.FindInstancesInScene(prefabPalette.prefabs.Select(x => x.gameObject), FormatInstanceName).ToList(); }
internal override void OnBrushBeginApply(BrushTarget brushTarget, BrushSettings brushSettings) { base.OnBrushBeginApply(brushTarget, brushSettings); }