You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
9.5 KiB
C#

3 years ago
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Polybrush;
using UnityEditor.SettingsManagement;
namespace UnityEditor.Polybrush
{
/// <summary>
/// Base class for brush modes that move vertices around. Implements an overlay preview.
/// </summary>
internal abstract class BrushModeSculpt : BrushModeMesh
{
class EditableObjectData
{
public List<Vector3> BrushNormalOnBeginApply = new List<Vector3>();
public Vector3[] CachedNormals;
public HashSet<int> NonManifoldIndices;
public EditableObject CacheTarget;
public List<Material> CacheMaterials;
public OverlayRenderer TempComponent;
}
internal static class Styles
{
/// <summary>
/// List built based on PolyDirection enum.
/// Make sure to properly convert values between this list and PolyDirection.
/// </summary>
internal static string[] s_BrushDirectionList = new string[]
{
"Brush Normal",
"Vertex Normal",
"Global Y Axis",
"Global X Axis",
"Global Z Axis"
};
internal static GUIContent gcDirection = new GUIContent("Direction", "How vertices are moved when the brush is applied. You can explicitly set an axis, or use the vertex normal.");
internal static GUIContent gcIgnoreOpenEdges = new GUIContent("Ignore Open Edges", "When on, edges that are not connected on both sides will be ignored by brush strokes.");
internal static GUIContent gcBrushNormalIsSticky = new GUIContent("Brush Normal is Sticky", "If enabled, vertices will be moved only on the direction of the brush normal at the time of first application.");
}
[UserSetting]
internal static Pref<float> s_VertexBillboardSize = new Pref<float>("Brush.VertexBillboardSize", 2f, SettingsScope.Project);
[UserSettingBlock("General Settings")]
static void HandleBrushPreferences(string searchContext)
{
s_VertexBillboardSize.value = SettingsGUILayout.SettingsSlider(new GUIContent("Vertex Render Size", "The size at which selected vertices will be rendered."), s_VertexBillboardSize, 0f, 10f, searchContext);
}
Dictionary<EditableObject,EditableObjectData> m_EditableObjectsData = new Dictionary<EditableObject, EditableObjectData>();
protected bool m_LikelyToSupportVertexSculpt = true;
internal override string UndoMessage { get { return "Sculpt Vertices"; } }
protected override string ModeSettingsHeader { get { return "Sculpt Settings"; } }
internal override void OnEnable()
{
base.OnEnable();
foreach (GameObject go in Selection.gameObjects)
{
m_LikelyToSupportVertexSculpt = CheckForVertexScluptSupport(go);
if (m_LikelyToSupportVertexSculpt)
break;
}
}
protected List<Vector3> BrushNormalsOnBeginApply(EditableObject target)
{
return m_EditableObjectsData[target].BrushNormalOnBeginApply;
}
protected bool ContainsIndexInNonManifoldIndices(EditableObject target, int index)
{
return m_EditableObjectsData[target].NonManifoldIndices.Contains(index);
}
/// <summary>
/// Check if the sculpt is supported for the current selection. No support for skin meshes
/// </summary>
/// <param name="go"></param>
/// <returns></returns>
bool CheckForVertexScluptSupport(GameObject go)
{
return !go.GetComponentInChildren<SkinnedMeshRenderer>();
}
internal override void OnBrushEnter(EditableObject target, BrushSettings settings)
{
base.OnBrushEnter(target, settings);
EditableObjectData data;
if(!m_EditableObjectsData.TryGetValue(target, out data))
{
data = new EditableObjectData();
m_EditableObjectsData.Add(target, data);
}
data.NonManifoldIndices = PolyMeshUtility.GetNonManifoldIndices(target.editMesh);
RefreshVertexSculptSupport(target);
}
// Called when the mouse exits hovering an editable object.
internal override void OnBrushExit(EditableObject target)
{
base.OnBrushExit(target);
if(m_EditableObjectsData.ContainsKey(target))
{
DestroyImmediate(m_EditableObjectsData[target].TempComponent);
m_EditableObjectsData.Remove(target);
}
}
/// <summary>
/// Will ensure that the check is not done everytime (inspired from BrushModeTexture script)
/// </summary>
/// <param name="target"></param>
void RefreshVertexSculptSupport(EditableObject target)
{
EditableObjectData data;
if(m_EditableObjectsData.TryGetValue(target, out data))
{
bool refresh = (data.CacheTarget != null && !data.CacheTarget.Equals(target.gameObjectAttached)) || data.CacheTarget == null;
if (data.CacheTarget != null && data.CacheTarget.Equals(target.gameObjectAttached))
refresh = data.CacheMaterials != target.gameObjectAttached.GetMaterials();
if (refresh)
{
data.CacheTarget = target;
data.CacheMaterials = target.gameObjectAttached.GetMaterials();
m_LikelyToSupportVertexSculpt = CheckForVertexScluptSupport(target.gameObjectAttached);
}
}
}
internal override void DrawGUI(BrushSettings settings)
{
base.DrawGUI(settings);
if (!m_LikelyToSupportVertexSculpt)
EditorGUILayout.HelpBox("Sculpting on skin meshes is not supported.", MessageType.Warning);
}
/// <summary>
/// Cache the brush normals and the mesh normals
/// </summary>
/// <param name="target">Target object to cache the brush normals and mesh normals from</param>
protected void CacheBrushNormals(BrushTarget target)
{
EditableObjectData data;
if(m_EditableObjectsData.TryGetValue(target.editableObject, out data))
{
data.BrushNormalOnBeginApply.Clear();
for(int i = 0; i < target.raycastHits.Count; i++)
data.BrushNormalOnBeginApply.Add(target.raycastHits[i].normal);
PolyMesh mesh = target.editableObject.editMesh;
data.CachedNormals = new Vector3[mesh.vertexCount];
if(mesh.normals != null && mesh.normals.Length == mesh.vertexCount)
{
System.Array.Copy(mesh.normals, 0, data.CachedNormals, 0, mesh.vertexCount);
target.editableObject.modifiedChannels |= MeshChannel.Normal;
}
}
}
internal override void OnBrushBeginApply(BrushTarget target, BrushSettings settings)
{
CacheBrushNormals(target);
base.OnBrushBeginApply(target, settings);
}
internal override void OnBrushFinishApply(BrushTarget target, BrushSettings settings)
{
base.OnBrushFinishApply(target, settings);
if(target!= null && m_EditableObjectsData.ContainsKey(target.editableObject))
m_EditableObjectsData[target.editableObject].BrushNormalOnBeginApply.Clear();
}
protected override void CreateTempComponent(EditableObject target)
{
if(target == null)
return;
RefreshVertexSculptSupport(target);
if (!m_LikelyToSupportVertexSculpt)
return;
OverlayRenderer ren = target.gameObjectAttached.AddComponent<OverlayRenderer>();
ren.hideFlags = HideFlags.DontSave
| HideFlags.NotEditable
| HideFlags.HideInInspector
| HideFlags.HideInHierarchy;
ren.SetMesh(target.editMesh);
ren.fullColor = s_FullStrengthColor;
ren.gradient = s_BrushGradientColor;
ren.vertexBillboardSize = s_VertexBillboardSize;
EditableObjectData data;
if(!m_EditableObjectsData.TryGetValue(target, out data))
{
data = new EditableObjectData();
m_EditableObjectsData.Add(target, data);
}
data.TempComponent = ren;
}
protected override void UpdateTempComponent(BrushTarget target, BrushSettings settings)
{
if(!Util.IsValid(target))
return;
EditableObjectData data;
if(m_EditableObjectsData.TryGetValue(target.editableObject, out data))
{
if(data.TempComponent != null)
((OverlayRenderer)data.TempComponent).SetWeights(target.GetAllWeights(), settings.strength);
}
}
protected void UpdateWireframe(BrushTarget target, BrushSettings settings)
{
if(!Util.IsValid(target))
return;
if(m_EditableObjectsData.TryGetValue(target.editableObject, out EditableObjectData data))
{
if(data.TempComponent != null)
data.TempComponent.OnVerticesMoved(target.editableObject.editMesh);
//Might be costly to do that on every wireframe update
if(ProBuilderBridge.ProBuilderExists() && target.editableObject.isProBuilderObject)
ProBuilderBridge.RefreshEditor(false);
}
}
}
}