Created
June 17, 2018 01:40
-
-
Save findstr/f11242aa59b7eb795b5278f14e0b54f0 to your computer and use it in GitHub Desktop.
Decal(Mesh Projector)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UnityEditor; | |
class MeshBuilder { | |
private readonly List<Vector3> vertices = new List<Vector3>(); | |
private readonly List<Vector3> normals = new List<Vector3>(); | |
private readonly List<Vector2> texCoords = new List<Vector2>(); | |
private readonly List<int> indices = new List<int>(); | |
private int FindVertex(Vector3 vertex) { | |
for (int i = 0; i < vertices.Count; i++) { | |
if (Vector3.Distance( vertices[i], vertex ) < 0.01f) return i; | |
} | |
return -1; | |
} | |
private void AddTexCoord(Vector3 ver) { | |
float u = Mathf.Lerp(0.0f, 1.0f, ver.x + 0.5f); | |
float v = Mathf.Lerp(0.0f, 1.0f, ver.z + 0.5f); | |
texCoords.Add( new Vector2( u, v ) ); | |
} | |
private int AddVertex(Vector3 vertex, Vector3 normal) { | |
int index = FindVertex(vertex); | |
if (index == -1) { | |
vertices.Add(vertex); | |
normals.Add(normal); | |
AddTexCoord(vertex); | |
return vertices.Count - 1; | |
} else { | |
normals[index] = (normals[index] + normal).normalized; | |
return index; | |
} | |
} | |
public void AddPolygon(Vector3[] poly, Vector3 normal) { | |
int ind1 = AddVertex( poly[0], normal); | |
for (int i = 1; i < poly.Length - 1; i++) { | |
int ind2 = AddVertex( poly[i], normal); | |
int ind3 = AddVertex( poly[i + 1], normal); | |
indices.Add( ind1 ); | |
indices.Add( ind2 ); | |
indices.Add( ind3 ); | |
} | |
} | |
public void Push(float distance) { | |
for (int i = 0; i < vertices.Count; i++) { | |
vertices[i] += normals[i] * distance; | |
} | |
} | |
public void ToMesh(Mesh mesh) { | |
mesh.Clear( true ); | |
if (indices.Count == 0) return; | |
mesh.vertices = vertices.ToArray(); | |
mesh.normals = normals.ToArray(); | |
mesh.uv = texCoords.ToArray(); | |
mesh.uv2 = texCoords.ToArray(); | |
mesh.triangles = indices.ToArray(); | |
vertices.Clear(); | |
normals.Clear(); | |
texCoords.Clear(); | |
indices.Clear(); | |
} | |
} | |
static class PolygonClippingUtils { | |
private static readonly Plane right = new Plane( Vector3.right, 0.5f ); | |
private static readonly Plane left = new Plane( Vector3.left, 0.5f ); | |
private static readonly Plane top = new Plane( Vector3.up, 0.5f ); | |
private static readonly Plane bottom = new Plane( Vector3.down, 0.5f ); | |
private static readonly Plane front = new Plane( Vector3.forward, 0.5f ); | |
private static readonly Plane back = new Plane( Vector3.back, 0.5f ); | |
public static Vector3[] Clip(params Vector3[] poly) { | |
poly = Clip( poly, right ).ToArray(); | |
poly = Clip( poly, left ).ToArray(); | |
poly = Clip( poly, top ).ToArray(); | |
poly = Clip( poly, bottom ).ToArray(); | |
poly = Clip( poly, front ).ToArray(); | |
poly = Clip( poly, back ).ToArray(); | |
return poly; | |
} | |
private static IEnumerable<Vector3> Clip(Vector3[] poly, Plane plane) { | |
for (int i = 0; i < poly.Length; i++) { | |
int next = (i + 1) % poly.Length; | |
Vector3 v1 = poly[i]; | |
Vector3 v2 = poly[next]; | |
if (plane.GetSide( v1 )) { | |
yield return v1; | |
} | |
if (plane.GetSide( v1 ) != plane.GetSide( v2 )) { | |
yield return PlaneLineCast( plane, v1, v2 ); | |
} | |
} | |
} | |
private static Vector3 PlaneLineCast(Plane plane, Vector3 a, Vector3 b) { | |
float dis; | |
Ray ray = new Ray( a, b - a ); | |
plane.Raycast( ray, out dis ); | |
return ray.GetPoint( dis ); | |
} | |
} | |
[RequireComponent( typeof( MeshFilter ) )] | |
[RequireComponent( typeof( MeshRenderer ) )] | |
[ExecuteInEditMode] | |
public class Decal : MonoBehaviour { | |
public Sprite sprite; | |
public Material material; | |
private readonly MeshBuilder builder = new MeshBuilder(); | |
private Vector3 radius = new Vector3(1, 1, 1); | |
// Use this for initialization | |
void Start () { | |
} | |
private static bool HasLayer(LayerMask mask, int layer) { | |
return (mask.value & 1 << layer) != 0; | |
} | |
public MeshFilter[] GetAffectObjects() { | |
Bounds bounds = new Bounds(transform.position, radius); | |
return GameObject.FindObjectsOfType<MeshRenderer>() | |
.Where( obj => obj.gameObject.isStatic ) | |
.Where( obj => obj.GetComponent<Decal>() == null ) | |
.Where( obj => bounds.Intersects( obj.bounds ) ) | |
.Select( obj => obj.GetComponent<MeshFilter>() ) | |
.Where( obj => obj != null && obj.sharedMesh != null ) | |
.ToArray(); | |
} | |
private Rect To01(Rect rect, Texture2D texture) { | |
rect.x /= texture.width; | |
rect.y /= texture.height; | |
rect.width /= texture.width; | |
rect.height /= texture.height; | |
return rect; | |
} | |
private void AddTriangle(MeshBuilder builder, Vector3 v1, Vector3 v2, Vector3 v3) { | |
Vector3 normal = Vector3.Cross( v2 - v1, v3 - v1 ).normalized; | |
if (Vector3.Angle( Vector3.forward, -normal ) <= 90.0f) { | |
var poly = PolygonClippingUtils.Clip( v1, v2, v3 ); | |
if (poly.Length > 0) { | |
builder.AddPolygon(poly, normal); | |
} | |
} | |
} | |
private string PV(Vector3 v) { | |
return ":x:" + v.x + ":y:" + v.y + ":z:" + v.z; | |
} | |
private void Build(MeshBuilder builder, MeshFilter filter) { | |
Matrix4x4 tolocal = transform.worldToLocalMatrix * filter.transform.localToWorldMatrix; | |
Mesh mesh = filter.sharedMesh; | |
Vector3[] vertices = mesh.vertices; | |
int[] triangles = mesh.triangles; | |
Debug.Log("Build:" + triangles.Length); | |
for (int i = 0; i < triangles.Length; i += 3) { | |
int i1 = triangles[i]; | |
int i2 = triangles[i + 1]; | |
int i3 = triangles[i + 2]; | |
Vector3 v1l = vertices[i1]; | |
Vector3 v2l = vertices[i2]; | |
Vector3 v3l = vertices[i3]; | |
Vector3 v1 = tolocal.MultiplyPoint(vertices[i1]); | |
Vector3 v2 = tolocal.MultiplyPoint(vertices[i2]); | |
Vector3 v3 = tolocal.MultiplyPoint(vertices[i3]); | |
AddTriangle(builder, v1, v2, v3); | |
} | |
} | |
// Update is called once per frame | |
void Update () { | |
if (transform.hasChanged == false) | |
return ; | |
MeshFilter filter = GetComponent<MeshFilter>(); | |
MeshRenderer renderer = GetComponent<MeshRenderer>(); | |
transform.hasChanged = false; | |
if (filter.sharedMesh != null && !filter.sharedMesh.isReadable) | |
return ; | |
var objects = GetAffectObjects(); | |
foreach (var obj in objects) { | |
Debug.Log("___:" + obj); | |
Build(builder, obj); | |
} | |
if (filter.sharedMesh == null) { | |
filter.sharedMesh = new Mesh(); | |
filter.sharedMesh.name = "DecalGGG"; | |
} | |
builder.Push(0.009f); | |
builder.ToMesh( filter.sharedMesh ); | |
renderer.sharedMaterial = material; | |
return ; | |
} | |
void OnDrawGizmosSelected() { | |
Gizmos.matrix = transform.localToWorldMatrix; | |
Gizmos.color = Color.yellow; | |
Gizmos.DrawWireCube(new Vector3(0, 0, 0), radius); | |
} | |
[DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.NotInSelectionHierarchy)] | |
static void DrawGameObjectName(Transform transform, GizmoType gizmoType) | |
{ | |
/* | |
GUIStyle style = new GUIStyle(); | |
style.normal.textColor = Color.red; | |
Handles.Label(transform.position, transform.gameObject.name, style); | |
*/ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment