Unity Udexreal开发插件包
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.7 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UDE_HAND_INTERACTION
{
[RequireComponent(typeof(InteractableObject))]
public class InteractableObjectOutline : MonoBehaviour
{
private Renderer[] renderers;
private MeshFilter[] meshFilters;
private Material MaskOutlineMat;
private Material FillOutlineMat;
[SerializeField]
private Color outlineColor = new Color(1, 0, 0, 1);
[SerializeField, Range(0f, 10f)]
private float outlineWidth = 6f;
[SerializeField, HideInInspector]
private List<Mesh> bakeKeys = new List<Mesh>();
[SerializeField, HideInInspector]
private List<List<Vector3>> bakeValues = new List<List<Vector3>>();
public List<Renderer> ObjectRenderers { get; private set; } = new List<Renderer>();
public List<MeshFilter> ObjectMeshFilters { get; private set; } = new List<MeshFilter>();
private static HashSet<Mesh> registeredMeshes = new HashSet<Mesh>();
bool[] TwoHandEnable = new bool[2] { false, false };
void Awake()
{
FindObjectRenders(transform);
renderers = ObjectRenderers.ToArray();
meshFilters = ObjectMeshFilters.ToArray();
MaskOutlineMat = Instantiate(Resources.Load<Material>(@"Mask"));
FillOutlineMat = Instantiate(Resources.Load<Material>(@"Fill"));
MaskOutlineMat.name = "Mask(TempClone)";
FillOutlineMat.name = "Fill(TempClone)";
foreach (var meshFilter in meshFilters)
{
if (!registeredMeshes.Add(meshFilter.sharedMesh)) continue;
var index = bakeKeys.IndexOf(meshFilter.sharedMesh);
var smoothNormals = (index >= 0) ? bakeValues[index] : SmoothNormals(meshFilter.sharedMesh);
meshFilter.sharedMesh.SetUVs(3, smoothNormals);
var renderer = meshFilter.GetComponent<Renderer>();
if (renderer != null)
{
CombineSubmeshes(meshFilter.sharedMesh, renderer.sharedMaterials);
}
}
FillOutlineMat.SetColor("_OutlineColor", outlineColor);
FillOutlineMat.SetFloat("_OutlineWidth", outlineWidth);
enabled = false;
}
public void EnableWithHand(int hand, bool enable)
{
TwoHandEnable[hand] = enable;
enabled = TwoHandEnable[0] || TwoHandEnable[1];
}
void OnEnable()
{
foreach (var renderer in renderers)
{
var materials = renderer.sharedMaterials.ToList();
materials.Add(MaskOutlineMat);
materials.Add(FillOutlineMat);
renderer.materials = materials.ToArray();
}
}
void OnDisable()
{
foreach (var renderer in renderers)
{
var materials = renderer.sharedMaterials.ToList();
materials.Remove(MaskOutlineMat);
materials.Remove(FillOutlineMat);
renderer.materials = materials.ToArray();
}
}
void OnValidate()
{
if (bakeKeys.Count != bakeValues.Count)
{
bakeKeys.Clear();
bakeValues.Clear();
}
if (bakeKeys.Count == 0)
{
var bakedMeshes = new HashSet<Mesh>();
foreach (var meshFilter in GetComponentsInChildren<MeshFilter>())
{
if (!bakedMeshes.Add(meshFilter.sharedMesh)) continue;
var smoothNormals = SmoothNormals(meshFilter.sharedMesh);
bakeKeys.Add(meshFilter.sharedMesh);
bakeValues.Add(smoothNormals);
}
}
}
void Update()
{
FillOutlineMat.SetColor("_OutlineColor", outlineColor);
FillOutlineMat.SetFloat("_OutlineWidth", outlineWidth);
}
void OnDestroy()
{
Destroy(MaskOutlineMat);
Destroy(FillOutlineMat);
}
void FindObjectRenders(Transform trans)
{
if (trans.TryGetComponent<Renderer>(out var render))
{
ObjectRenderers.Add(render);
}
if (trans.TryGetComponent<MeshFilter>(out var filter))
{
ObjectMeshFilters.Add(filter);
}
for (int i = 0; i < trans.childCount; ++i)
{
FindObjectRenders(trans.GetChild(i));
}
}
List<Vector3> SmoothNormals(Mesh mesh)
{
var groups = mesh.vertices.Select((vertex, index) => new KeyValuePair<Vector3, int>(vertex, index)).GroupBy(pair => pair.Key);
var smoothNormals = new List<Vector3>(mesh.normals);
foreach (var group in groups)
{
if (group.Count() == 1) continue;
var smoothNormal = Vector3.zero;
foreach (var pair in group)
{
smoothNormal += smoothNormals[pair.Value];
}
smoothNormal.Normalize();
foreach (var pair in group)
{
smoothNormals[pair.Value] = smoothNormal;
}
}
return smoothNormals;
}
void CombineSubmeshes(Mesh mesh, Material[] materials)
{
if (mesh.subMeshCount == 1 || mesh.subMeshCount > materials.Length) return;
mesh.subMeshCount++;
mesh.SetTriangles(mesh.triangles, mesh.subMeshCount - 1);
}
}
}