您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
302 行
7.9 KiB
302 行
7.9 KiB
using UnityEngine;
|
|
using Unity.DemoTeam.Attributes;
|
|
|
|
namespace Unity.DemoTeam.DigitalHuman
|
|
{
|
|
[ExecuteAlways]
|
|
[RequireComponent(typeof(Renderer))]
|
|
public class TeethRenderer : MonoBehaviour
|
|
{
|
|
private Renderer rnd;
|
|
private Material rndMat;
|
|
private MaterialPropertyBlock rndProps;
|
|
|
|
private const uint vertexFixedBit6 = 1 << 5;
|
|
private const uint vertexFixedMask = vertexFixedBit6;
|
|
private const int vertexLimit = 32;// should match limit in LitTeeth.shader
|
|
private Vector4[] vertexData = new Vector4[0];
|
|
|
|
[Range(0.0f, 1.0f)] public float litPotentialMin = 0.0f;
|
|
[Range(0.0f, 1.0f)] public float litPotentialMax = 1.0f;
|
|
[Min(1.0f)] public float litPotentialFalloff = 4.0f;
|
|
|
|
public Attenuation mode;
|
|
public enum Attenuation
|
|
{
|
|
None,
|
|
Linear,
|
|
SkyPolygon,
|
|
}
|
|
|
|
[EditableIf("mode", Attenuation.Linear)] public Transform linearBack;
|
|
[EditableIf("mode", Attenuation.Linear)] public Transform linearFront;
|
|
|
|
[EditableIf("mode", Attenuation.SkyPolygon)] public Transform skyPolygonContainer;
|
|
[EditableIf("mode", Attenuation.SkyPolygon)] public Transform skyPolygonDebugSphere;
|
|
|
|
public bool showDebugWireframe;
|
|
|
|
void Awake()
|
|
{
|
|
rnd = GetComponent<Renderer>();
|
|
#if UNITY_EDITOR
|
|
rndMat = rnd.sharedMaterial;
|
|
#else
|
|
rndMat = rnd.material;
|
|
#endif
|
|
rndProps = new MaterialPropertyBlock();
|
|
}
|
|
|
|
void PrepareKeyword(string keyword, bool enabled)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (rndMat != rnd.sharedMaterial)
|
|
rndMat = rnd.sharedMaterial;
|
|
#endif
|
|
|
|
if (rndMat.IsKeywordEnabled(keyword) != enabled)
|
|
{
|
|
if (enabled)
|
|
rndMat.EnableKeyword(keyword);
|
|
else
|
|
rndMat.DisableKeyword(keyword);
|
|
}
|
|
}
|
|
|
|
void PrepareVertexData(int vertexCount)
|
|
{
|
|
if (vertexData.Length != vertexCount)
|
|
vertexData = new Vector4[vertexCount];
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
var outputAttn = mode;
|
|
var outputSize = 0;
|
|
|
|
switch (outputAttn)
|
|
{
|
|
case Attenuation.None:
|
|
break;
|
|
|
|
case Attenuation.Linear:
|
|
if (linearFront == null || linearBack == null)
|
|
outputAttn = Attenuation.None;
|
|
else
|
|
outputSize = 6;
|
|
break;
|
|
|
|
case Attenuation.SkyPolygon:
|
|
if (skyPolygonContainer == null || skyPolygonContainer.childCount < 3)
|
|
outputAttn = Attenuation.None;
|
|
else
|
|
outputSize = Mathf.Min(vertexLimit, skyPolygonContainer.childCount);
|
|
break;
|
|
}
|
|
|
|
var outputFixedBit = outputSize > 0 ? (1u << (outputSize - 1)) : 0u;
|
|
var outputFixedSize = (vertexFixedMask & outputFixedBit) != 0;
|
|
|
|
PrepareKeyword("TEETH_ATTN_NONE", outputAttn == Attenuation.None);
|
|
PrepareKeyword("TEETH_ATTN_LINEAR", outputAttn == Attenuation.Linear);
|
|
PrepareKeyword("TEETH_ATTN_SKYPOLYGON", outputAttn == Attenuation.SkyPolygon);
|
|
|
|
PrepareKeyword("TEETH_DATA_FIXED_6", outputFixedSize && outputSize == 6);
|
|
PrepareKeyword("TEETH_DATA_VARIABLE_32", !outputFixedSize);
|
|
|
|
if (outputFixedSize)
|
|
PrepareVertexData(outputSize);
|
|
else
|
|
PrepareVertexData(vertexLimit);
|
|
|
|
switch (outputAttn)
|
|
{
|
|
case Attenuation.Linear:
|
|
vertexData[0] = linearFront.position;
|
|
vertexData[1] = linearBack.position;
|
|
break;
|
|
|
|
case Attenuation.SkyPolygon:
|
|
for (int i = 0; i != outputSize; i++)
|
|
{
|
|
vertexData[i] = skyPolygonContainer.GetChild(i).position;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (rndProps == null)
|
|
rndProps = new MaterialPropertyBlock();
|
|
|
|
rnd.GetPropertyBlock(rndProps);
|
|
{
|
|
rndProps.Clear();// TODO would be nice if SetVectorArray didn't truncate ...
|
|
rndProps.SetVector("_TeethParams", new Vector4(litPotentialMin, litPotentialMax, litPotentialFalloff));
|
|
rndProps.SetVectorArray("_TeethVertexData", vertexData);
|
|
rndProps.SetInt("_TeethVertexCount", outputSize);
|
|
}
|
|
rnd.SetPropertyBlock(rndProps);
|
|
|
|
//DebugSpherical();
|
|
}
|
|
|
|
void OnDrawGizmos()
|
|
{
|
|
if (!showDebugWireframe)
|
|
return;
|
|
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.matrix = Matrix4x4.identity;
|
|
|
|
switch (mode)
|
|
{
|
|
case Attenuation.Linear:
|
|
if (linearFront != null || linearBack != null)
|
|
{
|
|
int vertexCount = 2;
|
|
if (vertexCount > vertexData.Length)
|
|
break;
|
|
|
|
Gizmos.DrawLine(vertexData[0], vertexData[1]);
|
|
}
|
|
break;
|
|
|
|
case Attenuation.SkyPolygon:
|
|
if (skyPolygonContainer != null)
|
|
{
|
|
int vertexCount = skyPolygonContainer.childCount;
|
|
if (vertexCount > vertexLimit)
|
|
vertexCount = vertexLimit;
|
|
if (vertexCount > vertexData.Length)
|
|
vertexCount = vertexData.Length;
|
|
if (vertexCount > 0)
|
|
{
|
|
for (int i = 1; i != vertexCount; i++)
|
|
{
|
|
Gizmos.DrawLine(vertexData[i - 1], vertexData[i]);
|
|
}
|
|
Gizmos.DrawLine(vertexData[vertexCount - 1], vertexData[0]);
|
|
}
|
|
|
|
if (skyPolygonDebugSphere != null)
|
|
{
|
|
var origin = skyPolygonDebugSphere.position;
|
|
var radius = skyPolygonDebugSphere.localScale.x * 0.5f;
|
|
|
|
for (int i = 0; i != vertexCount; i++)
|
|
{
|
|
Vector3 v0 = vertexData[i];
|
|
Vector3 v1 = vertexData[(i + 1) % vertexCount];
|
|
Vector3 A = radius * Vector3.Normalize(v0 - origin);
|
|
Vector3 B = radius * Vector3.Normalize(v1 - origin);
|
|
|
|
Gizmos_DrawArc(origin, A, B);
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Gizmos_DrawArc(Vector3 O, Vector3 A, Vector3 B, int segments = 100)
|
|
{
|
|
var arcRot = Quaternion.FromToRotation(A, B);
|
|
var preRot = Quaternion.identity;
|
|
var rcpSeg = 1.0f / segments;
|
|
for (int i = 0; i != segments; i++)
|
|
{
|
|
var preDir = preRot * A;
|
|
var curRot = preRot = Quaternion.Slerp(Quaternion.identity, arcRot, (i + 1) * rcpSeg);
|
|
var curDir = curRot * A;
|
|
Gizmos.DrawLine(O + preDir, O + curDir);
|
|
}
|
|
}
|
|
|
|
//----
|
|
/*
|
|
const int NUM_VERTS = 6;
|
|
const float NUM_VERTS_FLT = 6.0f;
|
|
const int LAST_VERT = NUM_VERTS - 1;
|
|
const float PI = 3.14159265359f;
|
|
|
|
static Vector3 normalize(Vector3 v) { return Vector3.Normalize(v); }
|
|
static Vector3 cross(Vector3 a, Vector3 b) { return -Vector3.Cross(a, b); }
|
|
static float dot(Vector3 a, Vector3 b) { return Vector3.Dot(a, b); }
|
|
static float sign(float s) { return Mathf.Sign(s); }
|
|
static float acos(float s) { return Mathf.Acos(s); }
|
|
|
|
void SphericalPoly_CalcInteriorAngles(Vector3[] P, float[] A)
|
|
{
|
|
Vector3[] N = new Vector3[NUM_VERTS];
|
|
|
|
// calc plane normals
|
|
// where N[i] = normal of incident plane
|
|
// eg. N[i+0] = cross(C, A);
|
|
// N[i+1] = cross(A, B);
|
|
{
|
|
N[0] = normalize(cross(P[LAST_VERT], P[0]));
|
|
for (int i = 1; i != NUM_VERTS; i++)
|
|
{
|
|
N[i] = normalize(cross(P[i - 1], P[i]));
|
|
}
|
|
}
|
|
|
|
// calc interior angles
|
|
{
|
|
string As = " A = [ ";
|
|
string Ds = " dot = [ ";
|
|
for (int i = 0; i != LAST_VERT; i++)
|
|
{
|
|
A[i] = PI - sign(dot(N[i], P[i + 1])) * acos(dot(N[i], N[i + 1]));
|
|
{
|
|
As += A[i] + ", ";
|
|
Ds += dot(N[i], P[i + 1]) + ", ";
|
|
}
|
|
}
|
|
A[LAST_VERT] = PI - sign(dot(N[LAST_VERT], P[0])) * acos(dot(N[LAST_VERT], N[0]));
|
|
{
|
|
As += A[LAST_VERT] + " ]";
|
|
Ds += sign(dot(N[LAST_VERT], P[0])) + " ]";
|
|
}
|
|
Debug.Log(As + "\n" + Ds);
|
|
}
|
|
}
|
|
|
|
void SphericalPoly_CalcAreaFromInteriorAngles(float[] A, out float area)
|
|
{
|
|
float E = 0.0f;
|
|
for (int i = 0; i != NUM_VERTS; i++)
|
|
{
|
|
E += A[i];
|
|
}
|
|
area = E - (NUM_VERTS_FLT - 2.0f) * PI;
|
|
}
|
|
|
|
void SphericalPoly_CalcAreaFromProjectedPositions(Vector3[] P, out float area)
|
|
{
|
|
float[] A = new float[NUM_VERTS];
|
|
SphericalPoly_CalcInteriorAngles(P, A);
|
|
SphericalPoly_CalcAreaFromInteriorAngles(A, out area);
|
|
}
|
|
|
|
void DebugSpherical()
|
|
{
|
|
if (skyPolygonDebugSphere == null)
|
|
return;
|
|
|
|
Vector3[] P = new Vector3[NUM_VERTS];
|
|
for (int i = 0; i != NUM_VERTS; i++)
|
|
{
|
|
Vector3 vertPos = vertices[i];
|
|
//P[i] = normalize(mul(input.worldToTangent, _TeethSkyPolygon[i].xyz - positionWS));
|
|
P[i] = normalize(vertPos - skyPolygonDebugSphere.position);
|
|
}
|
|
|
|
float skyIncident;
|
|
float maxIncident = 2.0f * Mathf.PI;// hemisphere
|
|
SphericalPoly_CalcAreaFromProjectedPositions(P, out skyIncident);
|
|
|
|
Debug.Log("skyIncident = " + skyIncident + ", maxIncident = " + maxIncident);
|
|
}*/
|
|
}
|
|
}
|