Skip to content

Instantly share code, notes, and snippets.

@Madgvox
Created January 13, 2018 17:45
Show Gist options
  • Save Madgvox/d7192152c5411842003b3dcfc3f76d54 to your computer and use it in GitHub Desktop.
Save Madgvox/d7192152c5411842003b3dcfc3f76d54 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections.Generic;
using System;
[ExecuteInEditMode]
[RequireComponent( typeof( MeshFilter ), typeof( MeshRenderer ) )]
public class StrandRenderer : MonoBehaviour {
public StrandRenderer child;
[HideInInspector]
public MeshFilter filter;
[HideInInspector]
public MeshRenderer renderer;
public Vector3[] points;
public float width;
public float marginWidth;
public bool overrideEndDirections;
public Vector3 entryDirection;
public Vector3 exitDirection;
public Gradient colorGradient;
public Color startColor {
get {
return colorGradient.Evaluate( 0 );
}
set {
var keys = colorGradient.colorKeys;
keys[ 0 ].color = value;
colorGradient.colorKeys = keys;
}
}
public Color endColor {
get {
return colorGradient.Evaluate( 1 );
}
set {
var keys = colorGradient.colorKeys;
keys[ keys.Length - 1 ].color = value;
colorGradient.colorKeys = keys;
}
}
public int sortingOrder {
get {
return renderer.sortingOrder;
}
set {
renderer.sortingOrder = value;
}
}
public int sortingLayerID {
get {
return renderer.sortingLayerID;
}
set {
renderer.sortingLayerID = value;
}
}
public string sortingLayerName {
get {
return renderer.sortingLayerName;
}
set {
renderer.sortingLayerName = value;
}
}
[SerializeField]
[HideInInspector]
bool isChild;
int[] triangles;
Vector3[] vertices;
Vector2[] uv;
Color[] colors;
Mesh mesh;
void Awake () {
mesh = new Mesh();
mesh.name = "StrandRenderer-Mesh";
if( filter != null ) filter.mesh = mesh;
UpdateLine();
if( child != null ) {
child.isChild = true;
}
}
public void UpdateLine () {
if( isChild ) return;
if( mesh == null ) return;
var pointCount = points.Length;
var hasChild = child != null && child.width > 0;
if( pointCount < 2 || Mathf.Approximately( width, 0 ) ) {
return;
}
var vertCount = pointCount * 2;
if( vertices == null || vertCount != vertices.Length ) {
vertices = new Vector3[ vertCount ];
triangles = new int[ ( vertCount - 2 ) * 3 ];
uv = new Vector2[ vertCount ];
colors = new Color[ vertCount ];
if( hasChild ) {
child.vertices = new Vector3[ vertCount ];
child.triangles = new int[ ( vertCount - 2 ) * 3 ];
child.uv = new Vector2[ vertCount ];
child.colors = new Color[ vertCount ];
}
}
var ratio = hasChild ? child.width / width : 0;
var vertIndex = 0;
var triangleIndex = 0;
var sqrLength = 0f;
Vector3 prevTanDir = Vector3.zero;
for( int i = 0; i < pointCount; i++ ) {
var pt = points[ i ];
var hasNextPoint = i < pointCount - 1;
var hasPrevPoint = i > 0;
var nextPoint = hasNextPoint ? points[ i + 1 ] : Vector3.zero;
var prevPoint = hasPrevPoint ? points[ i - 1 ] : Vector3.zero;
Vector3 tangentDir;
if( !hasPrevPoint ) {
if( overrideEndDirections ) {
tangentDir = entryDirection.normalized;
} else {
tangentDir = ( nextPoint - pt ).normalized;
}
prevTanDir = tangentDir;
} else if( !hasNextPoint ) {
if( overrideEndDirections ) {
tangentDir = exitDirection.normalized;
} else {
tangentDir = ( pt - prevPoint ).normalized;
}
prevTanDir = tangentDir;
} else {
var nextTanDir = ( nextPoint - pt ).normalized;
tangentDir = ( prevTanDir + nextTanDir ).normalized;
}
var angle = Vector3.Angle( tangentDir, prevTanDir ) * Mathf.Deg2Rad;
var w = ( 1 / Mathf.Cos( angle ) ) * width * 0.5f;
var left = Vector3.Cross( tangentDir, Vector3.back ).normalized;
var right = Vector3.Cross( tangentDir, Vector3.forward ).normalized;
vertices[ vertIndex ] = pt + left * w;
vertices[ vertIndex + 1 ] = pt + right * w;
if( hasChild ) {
child.vertices[ vertIndex ] = pt + left * ( w * ratio );
child.vertices[ vertIndex + 1 ] = pt + right * ( w * ratio );
}
if( hasNextPoint ) {
triangles[ triangleIndex ] = vertIndex;
triangles[ triangleIndex + 1 ] = vertIndex + 3;
triangles[ triangleIndex + 2 ] = vertIndex + 1;
triangles[ triangleIndex + 3 ] = vertIndex + 2;
triangles[ triangleIndex + 4 ] = vertIndex + 3;
triangles[ triangleIndex + 5 ] = vertIndex;
if( hasChild ) {
child.triangles[ triangleIndex ] = vertIndex;
child.triangles[ triangleIndex + 1 ] = vertIndex + 3;
child.triangles[ triangleIndex + 2 ] = vertIndex + 1;
child.triangles[ triangleIndex + 3 ] = vertIndex + 2;
child.triangles[ triangleIndex + 4 ] = vertIndex + 3;
child.triangles[ triangleIndex + 5 ] = vertIndex;
}
sqrLength += ( nextPoint - pt ).sqrMagnitude;
}
vertIndex += 2;
triangleIndex += 6;
prevTanDir = tangentDir;
}
vertIndex = 0;
var len = 0f;
for( int i = 0; i < pointCount; i++ ) {
var pt = points[ i ];
// set color/uv p based on distance traveled
var p = len / sqrLength;
var left = new Vector2( p, 1 );
var right = new Vector2( p, 0 );
uv[ vertIndex ] = left;
uv[ vertIndex + 1 ] = right;
var color = colorGradient.Evaluate( p );
colors[ vertIndex ] = color;
colors[ vertIndex + 1 ] = color;
if( hasChild ) {
var childColor = child.colorGradient.Evaluate( p );
child.uv[ vertIndex ] = left;
child.uv[ vertIndex + 1 ] = right;
child.colors[ vertIndex ] = childColor;
child.colors[ vertIndex + 1 ] = childColor;
}
if( i < pointCount - 1 ) {
len += ( points[ i + 1 ] - pt ).sqrMagnitude;
}
vertIndex += 2;
}
UpdateMesh();
if( hasChild ) {
child.UpdateMesh();
}
}
void UpdateMesh () {
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.colors = colors;
mesh.uv = uv;
}
void OnValidate () {
if( child != null ) child.isChild = true;
UpdateLine();
}
void Reset () {
filter = GetComponent<MeshFilter>();
renderer = GetComponent<MeshRenderer>();
if( mesh == null ) mesh = new Mesh();
if( filter != null ) filter.mesh = mesh;
isChild = false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment