using UnityEngine; using System.Collections.Generic; public class MegaTriangle { public int t; public Vector3 a, b, c; public Bounds bounds; public MegaTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 dir, int t) { this.t = t; this.a = a; this.b = b; this.c = c; //Vector3 cross = Vector3.Cross(b - a, c - a); Vector3 min = Vector3.Min(Vector3.Min(a, b), c); Vector3 max = Vector3.Max(Vector3.Max(a, b), c); bounds.SetMinMax(min, max); } public void Barycentric(Vector3 p, out float u, out float v, out float w) { Vector3 v0 = b - a, v1 = c - a, v2 = p - a; float d00 = Vector3.Dot(v0, v0); float d01 = Vector3.Dot(v0, v1); float d11 = Vector3.Dot(v1, v1); float d20 = Vector3.Dot(v2, v0); float d21 = Vector3.Dot(v2, v1); float denom = 1f / (d00 * d11 - d01 * d01); v = (d11 * d20 - d01 * d21) * denom; w = (d00 * d21 - d01 * d20) * denom; u = 1.0f - v - w; } } public class MegaVoxel { public class Voxel_t { public Vector3 position; public List tris; public Voxel_t() { position = Vector3.zero; tris = new List(); } } public static void GetGridIndex(Vector3 p, out int x, out int y, out int z, float unit) { x = (int)((p.x - start.x) / unit); y = (int)((p.y - start.y) / unit); z = (int)((p.z - start.z) / unit); } public static Voxel_t[,,] volume; public static int width; public static int height; public static int depth; static Vector3 start; public static void Voxelize(Vector3[] vertices, int[] indices, Bounds bounds, int resolution, out float unit) { float maxLength = Mathf.Max(bounds.size.x, Mathf.Max(bounds.size.y, bounds.size.z)); unit = maxLength / resolution; float hunit = unit * 0.5f; start = bounds.min - new Vector3(hunit, hunit, hunit); Vector3 end = bounds.max + new Vector3(hunit, hunit, hunit); Vector3 size = end - start; width = Mathf.CeilToInt(size.x / unit); height = Mathf.CeilToInt(size.y / unit); depth = Mathf.CeilToInt(size.z / unit); volume = new Voxel_t[width, height, depth]; Bounds[,,] boxes = new Bounds[width, height, depth]; Vector3 voxelSize = Vector3.one * unit; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { for (int z = 0; z < depth; z++) { Vector3 p = new Vector3(x, y, z) * unit + start; Bounds aabb = new Bounds(p, voxelSize); boxes[x, y, z] = aabb; volume[x, y, z] = new Voxel_t(); } } } Vector3 direction = Vector3.forward; for (int i = 0, n = indices.Length; i < n; i += 3) { MegaTriangle tri = new MegaTriangle(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]], direction, i); Vector3 min = tri.bounds.min - start; Vector3 max = tri.bounds.max - start; int iminX = (int)(min.x / unit), iminY = (int)(min.y / unit), iminZ = (int)(min.z / unit); int imaxX = (int)(max.x / unit), imaxY = (int)(max.y / unit), imaxZ = (int)(max.z / unit); iminX = Mathf.Clamp(iminX, 0, width - 1); iminY = Mathf.Clamp(iminY, 0, height - 1); iminZ = Mathf.Clamp(iminZ, 0, depth - 1); imaxX = Mathf.Clamp(imaxX, 0, width - 1); imaxY = Mathf.Clamp(imaxY, 0, height - 1); imaxZ = Mathf.Clamp(imaxZ, 0, depth - 1); for (int x = iminX; x <= imaxX; x++) { for (int y = iminY; y <= imaxY; y++) { for (int z = iminZ; z <= imaxZ; z++) { if (Intersects(tri, boxes[x, y, z])) { Voxel_t voxel = volume[x, y, z]; voxel.position = boxes[x, y, z].center; voxel.tris.Add(tri); volume[x, y, z] = voxel; } } } } } } public static bool Intersects(MegaTriangle tri, Bounds aabb) { float p0, p1, p2, r; Vector3 center = aabb.center, extents = aabb.max - center; Vector3 v0 = tri.a - center, v1 = tri.b - center, v2 = tri.c - center; Vector3 f0 = v1 - v0, f1 = v2 - v1, f2 = v0 - v2; Vector3 a00 = new Vector3(0, -f0.z, f0.y), a01 = new Vector3(0, -f1.z, f1.y), a02 = new Vector3(0, -f2.z, f2.y), a10 = new Vector3(f0.z, 0, -f0.x), a11 = new Vector3(f1.z, 0, -f1.x), a12 = new Vector3(f2.z, 0, -f2.x), a20 = new Vector3(-f0.y, f0.x, 0), a21 = new Vector3(-f1.y, f1.x, 0), a22 = new Vector3(-f2.y, f2.x, 0); // Test axis a00 p0 = Vector3.Dot(v0, a00); p1 = Vector3.Dot(v1, a00); p2 = Vector3.Dot(v2, a00); r = extents.y * Mathf.Abs(f0.z) + extents.z * Mathf.Abs(f0.y); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a01 p0 = Vector3.Dot(v0, a01); p1 = Vector3.Dot(v1, a01); p2 = Vector3.Dot(v2, a01); r = extents.y * Mathf.Abs(f1.z) + extents.z * Mathf.Abs(f1.y); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a02 p0 = Vector3.Dot(v0, a02); p1 = Vector3.Dot(v1, a02); p2 = Vector3.Dot(v2, a02); r = extents.y * Mathf.Abs(f2.z) + extents.z * Mathf.Abs(f2.y); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a10 p0 = Vector3.Dot(v0, a10); p1 = Vector3.Dot(v1, a10); p2 = Vector3.Dot(v2, a10); r = extents.x * Mathf.Abs(f0.z) + extents.z * Mathf.Abs(f0.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a11 p0 = Vector3.Dot(v0, a11); p1 = Vector3.Dot(v1, a11); p2 = Vector3.Dot(v2, a11); r = extents.x * Mathf.Abs(f1.z) + extents.z * Mathf.Abs(f1.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a12 p0 = Vector3.Dot(v0, a12); p1 = Vector3.Dot(v1, a12); p2 = Vector3.Dot(v2, a12); r = extents.x * Mathf.Abs(f2.z) + extents.z * Mathf.Abs(f2.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a20 p0 = Vector3.Dot(v0, a20); p1 = Vector3.Dot(v1, a20); p2 = Vector3.Dot(v2, a20); r = extents.x * Mathf.Abs(f0.y) + extents.y * Mathf.Abs(f0.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a21 p0 = Vector3.Dot(v0, a21); p1 = Vector3.Dot(v1, a21); p2 = Vector3.Dot(v2, a21); r = extents.x * Mathf.Abs(f1.y) + extents.y * Mathf.Abs(f1.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; // Test axis a22 p0 = Vector3.Dot(v0, a22); p1 = Vector3.Dot(v1, a22); p2 = Vector3.Dot(v2, a22); r = extents.x * Mathf.Abs(f2.y) + extents.y * Mathf.Abs(f2.x); if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r) return false; if (Mathf.Max(v0.x, v1.x, v2.x) < -extents.x || Mathf.Min(v0.x, v1.x, v2.x) > extents.x) return false; if (Mathf.Max(v0.y, v1.y, v2.y) < -extents.y || Mathf.Min(v0.y, v1.y, v2.y) > extents.y) return false; if (Mathf.Max(v0.z, v1.z, v2.z) < -extents.z || Mathf.Min(v0.z, v1.z, v2.z) > extents.z) return false; Vector3 normal = Vector3.Cross(f1, f0).normalized; Plane pl = new Plane(normal, Vector3.Dot(normal, tri.a)); return Intersects(pl, aabb); } public static bool Intersects(Plane pl, Bounds aabb) { Vector3 center = aabb.center; Vector3 extents = aabb.max - center; float r = extents.x * Mathf.Abs(pl.normal.x) + extents.y * Mathf.Abs(pl.normal.y) + extents.z * Mathf.Abs(pl.normal.z); float s = Vector3.Dot(pl.normal, center) - pl.distance; return Mathf.Abs(s) <= r; } } [System.Serializable] public class MegaBindInf { public float dist; public int face; public int i0; public int i1; public int i2; public Vector3 bary; public float weight; public float area; } [System.Serializable] public class MegaBindVert { public float weight; public List verts = new List(); } public struct MegaCloseFace { public int face; public float dist; } [ExecuteInEditMode] public class MegaWrap : MonoBehaviour { public float gap = 0.0f; public float shrink = 1.0f; public List neededVerts = new List(); public Vector3[] skinnedVerts; public Mesh mesh = null; public Vector3 offset = Vector3.zero; public bool targetIsSkin = false; public bool sourceIsSkin = false; public int nomapcount = 0; public Matrix4x4[] bindposes; public BoneWeight[] boneweights; public Transform[] bones; public float size = 0.01f; public int vertindex = 0; public Vector3[] freeverts; // position for any vert with no attachments public Vector3[] startverts; public Vector3[] verts; public MegaBindVert[] bindverts; public MegaModifyObject target; public float maxdist = 0.25f; public int maxpoints = 4; public bool WrapEnabled = true; public MegaNormalMethod NormalMethod = MegaNormalMethod.Unity; #if UNITY_5 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 public bool UseBakedMesh = true; #endif [ContextMenu("Help")] public void Help() { Application.OpenURL("http://www.west-racing.com/mf/?page_id=3709"); } Vector4 Plane(Vector3 v1, Vector3 v2, Vector3 v3) { Vector3 normal = Vector4.zero; normal.x = (v2.y - v1.y) * (v3.z - v1.z) - (v2.z - v1.z) * (v3.y - v1.y); normal.y = (v2.z - v1.z) * (v3.x - v1.x) - (v2.x - v1.x) * (v3.z - v1.z); normal.z = (v2.x - v1.x) * (v3.y - v1.y) - (v2.y - v1.y) * (v3.x - v1.x); normal = normal.normalized; return new Vector4(normal.x, normal.y, normal.z, -Vector3.Dot(v2, normal)); } float PlaneDist(Vector3 p, Vector4 plane) { Vector3 n = plane; return Vector3.Dot(n, p) + plane.w; } public float GetDistance(Vector3 p, Vector3 p0, Vector3 p1, Vector3 p2) { return MegaNearestPointTest.DistPoint3Triangle3Dbl(p, p0, p1, p2); } public float GetPlaneDistance(Vector3 p, Vector3 p0, Vector3 p1, Vector3 p2) { Vector4 pl = Plane(p0, p1, p2); return PlaneDist(p, pl); } public Vector3 MyBary(Vector3 p, Vector3 p0, Vector3 p1, Vector3 p2) { Vector3 bary = Vector3.zero; Vector3 normal = FaceNormal(p0, p1, p2); float areaABC = Vector3.Dot(normal, Vector3.Cross((p1 - p0), (p2 - p0))); float areaPBC = Vector3.Dot(normal, Vector3.Cross((p1 - p), (p2 - p))); float areaPCA = Vector3.Dot(normal, Vector3.Cross((p2 - p), (p0 - p))); bary.x = areaPBC / areaABC; // alpha bary.y = areaPCA / areaABC; // beta bary.z = 1.0f - bary.x - bary.y; // gamma return bary; } public Vector3 MyBary1(Vector3 p, Vector3 a, Vector3 b, Vector3 c) { Vector3 v0 = b - a, v1 = c - a, v2 = p - a; float d00 = Vector3.Dot(v0, v0); float d01 = Vector3.Dot(v0, v1); float d11 = Vector3.Dot(v1, v1); float d20 = Vector3.Dot(v2, v0); float d21 = Vector3.Dot(v2, v1); float denom = d00 * d11 - d01 * d01; float w = (d11 * d20 - d01 * d21) / denom; float v = (d00 * d21 - d01 * d20) / denom; float u = 1.0f - v - w; return new Vector3(u, v, w); } public Vector3 CalcBary(Vector3 p, Vector3 p0, Vector3 p1, Vector3 p2) { return MyBary(p, p0, p1, p2); } public float CalcArea(Vector3 p0, Vector3 p1, Vector3 p2) { Vector3 e1 = p1 - p0; Vector3 e2 = p2 - p0; Vector3 e3 = Vector3.Cross(e1, e2); return 0.5f * e3.magnitude; } Vector3 e11 = Vector3.zero; Vector3 e22 = Vector3.zero; Vector3 cr = Vector3.zero; public Vector3 FaceNormal(Vector3 p0, Vector3 p1, Vector3 p2) { //Vector3 e1 = p1 - p0; //Vector3 e2 = p2 - p0; e11.x = p1.x - p0.x; e11.y = p1.y - p0.y; e11.z = p1.z - p0.z; e22.x = p2.x - p0.x; e22.y = p2.y - p0.y; e22.z = p2.z - p0.z; //Vector3 e2 = p2 - p0; cr.x = e11.y * e22.z - e22.y * e11.z; cr.y = -(e11.x * e22.z - e22.x * e11.z); // * -1; cr.z = e11.x * e22.y - e22.x * e11.y; return cr; //Vector3.Cross(e11, e22); } static void CopyBlendShapes(Mesh mesh1, Mesh clonemesh) { #if UNITY_5_3 || UNITY_5_4 || UNITY_5_5 || UNITY_5_6 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 int bcount = mesh1.blendShapeCount; //GetBlendShapeFrameCount(); Vector3[] deltaverts = new Vector3[mesh1.vertexCount]; Vector3[] deltanorms = new Vector3[mesh1.vertexCount]; Vector3[] deltatans = new Vector3[mesh1.vertexCount]; for ( int j = 0; j < bcount; j++ ) { int frames = mesh1.GetBlendShapeFrameCount(j); string bname = mesh1.GetBlendShapeName(j); for ( int f = 0; f < frames; f++ ) { mesh1.GetBlendShapeFrameVertices(j, f, deltaverts, deltanorms, deltatans); float weight = mesh1.GetBlendShapeFrameWeight(j, f); clonemesh.AddBlendShapeFrame(bname, weight, deltaverts, deltanorms, deltatans); } } #endif } public Mesh CloneMesh(Mesh m) { Mesh clonemesh = new Mesh(); clonemesh.vertices = m.vertices; #if UNITY_5_0 || UNITY_5_1 || UNITY_5 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 clonemesh.uv2 = m.uv2; clonemesh.uv3 = m.uv3; clonemesh.uv4 = m.uv4; #else clonemesh.uv1 = m.uv1; clonemesh.uv2 = m.uv2; #endif clonemesh.uv = m.uv; clonemesh.normals = m.normals; clonemesh.tangents = m.tangents; clonemesh.colors = m.colors; clonemesh.subMeshCount = m.subMeshCount; for ( int s = 0; s < m.subMeshCount; s++ ) clonemesh.SetTriangles(m.GetTriangles(s), s); CopyBlendShapes(m, clonemesh); clonemesh.boneWeights = m.boneWeights; clonemesh.bindposes = m.bindposes; clonemesh.name = m.name; // + "_copy"; clonemesh.RecalculateBounds(); return clonemesh; } [ContextMenu("Reset Mesh")] public void ResetMesh() { if ( mesh ) { mesh.vertices = startverts; mesh.RecalculateBounds(); RecalcNormals(); //mesh.RecalculateNormals(); } target = null; bindverts = null; } public void SetMesh() { MeshFilter mf = GetComponent(); Mesh srcmesh = null; if ( mf != null ) srcmesh = mf.sharedMesh; else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer)); if ( smesh != null ) srcmesh = smesh.sharedMesh; } if ( srcmesh != null ) { mesh = CloneMesh(srcmesh); if ( mf ) mf.sharedMesh = mesh; else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer)); smesh.sharedMesh = mesh; } } } public void Attach() { var modobj = target; var mod = this; mod.targetIsSkin = false; mod.sourceIsSkin = false; if (mod.mesh && mod.startverts != null) mod.mesh.vertices = mod.startverts; if (modobj == null) { mod.bindverts = null; return; } mod.nomapcount = 0; if (mod.mesh) mod.mesh.vertices = mod.startverts; MeshFilter mf = mod.GetComponent(); Mesh srcmesh = null; if (mf != null) { //skinned = false; srcmesh = mf.sharedMesh; } else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)mod.GetComponent(typeof(SkinnedMeshRenderer)); if (smesh != null) { //skinned = true; srcmesh = smesh.sharedMesh; mod.sourceIsSkin = true; } } if (srcmesh == null) { Debug.LogWarning("No Mesh found on the target object, make sure target has a mesh and MegaFiers modifier attached!"); return; } if (mod.mesh == null) mod.mesh = mod.CloneMesh(srcmesh); //mf.mesh); if (mf) mf.mesh = mod.mesh; else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)mod.GetComponent(typeof(SkinnedMeshRenderer)); smesh.sharedMesh = mod.mesh; } if (mod.sourceIsSkin == false) { SkinnedMeshRenderer tmesh = (SkinnedMeshRenderer)modobj.GetComponent(typeof(SkinnedMeshRenderer)); if (tmesh != null) { mod.targetIsSkin = true; if (!mod.sourceIsSkin) { Mesh sm = tmesh.sharedMesh; mod.bindposes = sm.bindposes; mod.boneweights = sm.boneWeights; mod.bones = tmesh.bones; mod.skinnedVerts = sm.vertices; //new Vector3[sm.vertexCount]; } } } if (mod.targetIsSkin) { if (mod.boneweights == null || mod.boneweights.Length == 0) mod.targetIsSkin = false; } mod.neededVerts.Clear(); mod.verts = mod.mesh.vertices; mod.startverts = mod.mesh.vertices; mod.freeverts = new Vector3[mod.startverts.Length]; Vector3[] baseverts = modobj.verts; //basemesh.vertices; int[] basefaces = modobj.tris; //basemesh.triangles; mod.bindverts = new MegaBindVert[mod.verts.Length]; // matrix to get vertex into local space of target Matrix4x4 tm = mod.transform.localToWorldMatrix * modobj.transform.worldToLocalMatrix; List closefaces = new List(); Vector3 p0 = Vector3.zero; Vector3 p1 = Vector3.zero; Vector3 p2 = Vector3.zero; Vector3[] tverts = new Vector3[mod.target.sverts.Length]; for (int i = 0; i < tverts.Length; i++) { if (mod.targetIsSkin && !mod.sourceIsSkin) tverts[i] = modobj.transform.InverseTransformPoint(mod.GetSkinPos(i)); else tverts[i] = baseverts[i]; } //EditorUtility.ClearProgressBar(); float unit = 0.0f; mod.target.mesh.RecalculateBounds(); MegaVoxel.Voxelize(tverts, basefaces, mod.target.mesh.bounds, 16, out unit); //Vector3 min = mod.target.mesh.bounds.min; for (int i = 0; i < mod.verts.Length; i++) { MegaBindVert bv = new MegaBindVert(); mod.bindverts[i] = bv; Vector3 p = tm.MultiplyPoint(mod.verts[i]); p = mod.transform.TransformPoint(mod.verts[i]); p = modobj.transform.InverseTransformPoint(p); mod.freeverts[i] = p; closefaces.Clear(); int gx = 0; int gy = 0; int gz = 0; MegaVoxel.GetGridIndex(p, out gx, out gy, out gz, unit); for (int x = gx - 1; x <= gx + 1; x++) { if (x >= 0 && x < MegaVoxel.width) { for (int y = gy - 1; y <= gy + 1; y++) { if (y >= 0 && y < MegaVoxel.height) { for (int z = gz - 1; z <= gz + 1; z++) { if (z >= 0 && z < MegaVoxel.depth) { List tris = MegaVoxel.volume[x, y, z].tris; for (int t = 0; t < tris.Count; t++) { float dist = mod.GetDistance(p, tris[t].a, tris[t].b, tris[t].c); if (Mathf.Abs(dist) < mod.maxdist) { MegaCloseFace cf = new MegaCloseFace(); cf.dist = Mathf.Abs(dist); cf.face = tris[t].t; bool inserted = false; for (int k = 0; k < closefaces.Count; k++) { if (cf.dist < closefaces[k].dist) { closefaces.Insert(k, cf); inserted = true; break; } } if (!inserted) closefaces.Add(cf); } } } } } } } } float tweight = 0.0f; int maxp = mod.maxpoints; if (maxp == 0) maxp = closefaces.Count; for (int j = 0; j < maxp; j++) { if (j < closefaces.Count) { int t = closefaces[j].face; p0 = tverts[basefaces[t]]; p1 = tverts[basefaces[t + 1]]; p2 = tverts[basefaces[t + 2]]; Vector3 normal = mod.FaceNormal(p0, p1, p2); float dist = closefaces[j].dist; //GetDistance(p, p0, p1, p2); MegaBindInf bi = new MegaBindInf(); bi.dist = mod.GetPlaneDistance(p, p0, p1, p2); //dist; bi.face = t; bi.i0 = basefaces[t]; bi.i1 = basefaces[t + 1]; bi.i2 = basefaces[t + 2]; bi.bary = mod.CalcBary(p, p0, p1, p2); bi.weight = 1.0f / (1.0f + dist); bi.area = normal.magnitude * 0.5f; //CalcArea(baseverts[basefaces[t]], baseverts[basefaces[t + 1]], baseverts[basefaces[t + 2]]); // Could calc once at start tweight += bi.weight; bv.verts.Add(bi); } } if (mod.maxpoints > 0 && mod.maxpoints < bv.verts.Count) bv.verts.RemoveRange(mod.maxpoints, bv.verts.Count - mod.maxpoints); // Only want to calculate skin vertices we use if (!mod.sourceIsSkin && mod.targetIsSkin) { for (int fi = 0; fi < bv.verts.Count; fi++) { if (!mod.neededVerts.Contains(bv.verts[fi].i0)) mod.neededVerts.Add(bv.verts[fi].i0); if (!mod.neededVerts.Contains(bv.verts[fi].i1)) mod.neededVerts.Add(bv.verts[fi].i1); if (!mod.neededVerts.Contains(bv.verts[fi].i2)) mod.neededVerts.Add(bv.verts[fi].i2); } } if (tweight == 0.0f) { mod.nomapcount++; break; } bv.weight = tweight; } } public void Attach(MegaModifyObject modobj) { targetIsSkin = false; sourceIsSkin = false; if ( mesh && startverts != null ) mesh.vertices = startverts; if ( modobj == null ) { bindverts = null; return; } nomapcount = 0; if ( mesh ) mesh.vertices = startverts; MeshFilter mf = GetComponent(); Mesh srcmesh = null; if ( mf != null ) { //skinned = false; srcmesh = mf.mesh; } else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer)); if ( smesh != null ) { //skinned = true; srcmesh = smesh.sharedMesh; sourceIsSkin = true; } } if ( srcmesh == null ) { Debug.LogWarning("No Mesh found on the target object, make sure target has a mesh and MegaFiers modifier attached!"); return; } if ( mesh == null ) mesh = CloneMesh(srcmesh); //mf.mesh); if ( mf ) mf.mesh = mesh; else { SkinnedMeshRenderer smesh = (SkinnedMeshRenderer)GetComponent(typeof(SkinnedMeshRenderer)); smesh.sharedMesh = mesh; } if ( sourceIsSkin == false ) { SkinnedMeshRenderer tmesh = (SkinnedMeshRenderer)modobj.GetComponent(typeof(SkinnedMeshRenderer)); if ( tmesh != null ) { targetIsSkin = true; if ( !sourceIsSkin ) { Mesh sm = tmesh.sharedMesh; bindposes = sm.bindposes; boneweights = sm.boneWeights; bones = tmesh.bones; skinnedVerts = sm.vertices; //new Vector3[sm.vertexCount]; } } } if ( targetIsSkin ) { if ( boneweights == null || boneweights.Length == 0 ) targetIsSkin = false; } neededVerts.Clear(); verts = mesh.vertices; startverts = mesh.vertices; freeverts = new Vector3[startverts.Length]; Vector3[] baseverts = modobj.verts; //basemesh.vertices; int[] basefaces = modobj.tris; //basemesh.triangles; bindverts = new MegaBindVert[verts.Length]; // matrix to get vertex into local space of target Matrix4x4 tm = transform.localToWorldMatrix * modobj.transform.worldToLocalMatrix; List closefaces = new List(); Vector3 p0 = Vector3.zero; Vector3 p1 = Vector3.zero; Vector3 p2 = Vector3.zero; for ( int i = 0; i < verts.Length; i++ ) { MegaBindVert bv = new MegaBindVert(); bindverts[i] = bv; Vector3 p = tm.MultiplyPoint(verts[i]); p = transform.TransformPoint(verts[i]); p = modobj.transform.InverseTransformPoint(p); freeverts[i] = p; closefaces.Clear(); for ( int t = 0; t < basefaces.Length; t += 3 ) { if ( targetIsSkin && !sourceIsSkin ) { p0 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t])); p1 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t + 1])); p2 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t + 2])); } else { p0 = baseverts[basefaces[t]]; p1 = baseverts[basefaces[t + 1]]; p2 = baseverts[basefaces[t + 2]]; } float dist = GetDistance(p, p0, p1, p2); if ( Mathf.Abs(dist) < maxdist ) { MegaCloseFace cf = new MegaCloseFace(); cf.dist = Mathf.Abs(dist); cf.face = t; bool inserted = false; for ( int k = 0; k < closefaces.Count; k++ ) { if ( cf.dist < closefaces[k].dist ) { closefaces.Insert(k, cf); inserted = true; break; } } if ( !inserted ) closefaces.Add(cf); } } float tweight = 0.0f; int maxp = maxpoints; if ( maxp == 0 ) maxp = closefaces.Count; for ( int j = 0; j < maxp; j++ ) { if ( j < closefaces.Count ) { int t = closefaces[j].face; if ( targetIsSkin && !sourceIsSkin ) { p0 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t])); p1 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t + 1])); p2 = modobj.transform.InverseTransformPoint(GetSkinPos(basefaces[t + 2])); } else { p0 = baseverts[basefaces[t]]; p1 = baseverts[basefaces[t + 1]]; p2 = baseverts[basefaces[t + 2]]; } Vector3 normal = FaceNormal(p0, p1, p2); float dist = closefaces[j].dist; //GetDistance(p, p0, p1, p2); MegaBindInf bi = new MegaBindInf(); bi.dist = GetPlaneDistance(p, p0, p1, p2); //dist; bi.face = t; bi.i0 = basefaces[t]; bi.i1 = basefaces[t + 1]; bi.i2 = basefaces[t + 2]; bi.bary = CalcBary(p, p0, p1, p2); bi.weight = 1.0f / (1.0f + dist); bi.area = normal.magnitude * 0.5f; //CalcArea(baseverts[basefaces[t]], baseverts[basefaces[t + 1]], baseverts[basefaces[t + 2]]); // Could calc once at start tweight += bi.weight; bv.verts.Add(bi); } } if ( maxpoints > 0 && maxpoints < bv.verts.Count ) bv.verts.RemoveRange(maxpoints, bv.verts.Count - maxpoints); // Only want to calculate skin vertices we use if ( !sourceIsSkin && targetIsSkin ) { for ( int fi = 0; fi < bv.verts.Count; fi++ ) { if ( !neededVerts.Contains(bv.verts[fi].i0) ) neededVerts.Add(bv.verts[fi].i0); if ( !neededVerts.Contains(bv.verts[fi].i1) ) neededVerts.Add(bv.verts[fi].i1); if ( !neededVerts.Contains(bv.verts[fi].i2) ) neededVerts.Add(bv.verts[fi].i2); } } if ( tweight == 0.0f ) nomapcount++; bv.weight = tweight; } } void LateUpdate() { DoUpdate(); } public Vector3 GetSkinPos(int i) { Vector3 pos = target.sverts[i]; Vector3 bpos = bindposes[boneweights[i].boneIndex0].MultiplyPoint(pos); Vector3 p = bones[boneweights[i].boneIndex0].TransformPoint(bpos) * boneweights[i].weight0; bpos = bindposes[boneweights[i].boneIndex1].MultiplyPoint(pos); p += bones[boneweights[i].boneIndex1].TransformPoint(bpos) * boneweights[i].weight1; bpos = bindposes[boneweights[i].boneIndex2].MultiplyPoint(pos); p += bones[boneweights[i].boneIndex2].TransformPoint(bpos) * boneweights[i].weight2; bpos = bindposes[boneweights[i].boneIndex3].MultiplyPoint(pos); p += bones[boneweights[i].boneIndex3].TransformPoint(bpos) * boneweights[i].weight3; return p; } Vector3 gcp = Vector3.zero; public Vector3 GetCoordMine(Vector3 A, Vector3 B, Vector3 C, Vector3 bary) { //Vector3 p = Vector3.zero; gcp.x = (bary.x * A.x) + (bary.y * B.x) + (bary.z * C.x); gcp.y = (bary.x * A.y) + (bary.y * B.y) + (bary.z * C.y); gcp.z = (bary.x * A.z) + (bary.y * B.z) + (bary.z * C.z); return gcp; } SkinnedMeshRenderer tmesh; #if UNITY_5 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 Mesh bakedmesh = null; #endif void DoUpdate() { if ( WrapEnabled == false || target == null || bindverts == null ) //|| bindposes == null ) return; if ( mesh == null ) SetMesh(); if ( mesh == null ) return; if ( targetIsSkin && neededVerts != null && neededVerts.Count > 0 ) //|| (targetIsSkin && boneweights == null) ) { if ( boneweights == null || tmesh == null ) { tmesh = (SkinnedMeshRenderer)target.GetComponent(typeof(SkinnedMeshRenderer)); if ( tmesh != null ) { if ( !sourceIsSkin ) { Mesh sm = tmesh.sharedMesh; bindposes = sm.bindposes; bones = tmesh.bones; boneweights = sm.boneWeights; } } } #if UNITY_5 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 if ( tmesh == null ) tmesh = (SkinnedMeshRenderer)target.GetComponent(typeof(SkinnedMeshRenderer)); if ( UseBakedMesh ) { if ( bakedmesh == null ) bakedmesh = new Mesh(); tmesh.BakeMesh(bakedmesh); skinnedVerts = bakedmesh.vertices; } else { for ( int i = 0; i < neededVerts.Count; i++ ) skinnedVerts[neededVerts[i]] = GetSkinPos(neededVerts[i]); } #else for ( int i = 0; i < neededVerts.Count; i++ ) skinnedVerts[neededVerts[i]] = GetSkinPos(neededVerts[i]); #endif } Matrix4x4 stm = Matrix4x4.identity; Vector3 p = Vector3.zero; if ( targetIsSkin && !sourceIsSkin ) { #if UNITY_5 || UNITY_2017 || UNITY_2018 || UNITY_2019 || UNITY_2020 if ( UseBakedMesh ) stm = transform.worldToLocalMatrix * target.transform.localToWorldMatrix; // * transform.worldToLocalMatrix; else stm = transform.worldToLocalMatrix; // * target.transform.localToWorldMatrix; // * transform.worldToLocalMatrix; #else stm = transform.worldToLocalMatrix; // * target.transform.localToWorldMatrix; // * transform.worldToLocalMatrix; #endif for ( int i = 0; i < bindverts.Length; i++ ) { if ( bindverts[i].verts.Count > 0 ) { p = Vector3.zero; float oow = 1.0f / bindverts[i].weight; int cnt = bindverts[i].verts.Count; for ( int j = 0; j < cnt; j++ ) { MegaBindInf bi = bindverts[i].verts[j]; Vector3 p0 = skinnedVerts[bi.i0]; Vector3 p1 = skinnedVerts[bi.i1]; Vector3 p2 = skinnedVerts[bi.i2]; Vector3 cp = GetCoordMine(p0, p1, p2, bi.bary); Vector3 norm = FaceNormal(p0, p1, p2); float sq = 1.0f / Mathf.Sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z); float d = (bi.dist * shrink) + gap; //cp += d * norm.x; cp.x += d * norm.x * sq; cp.y += d * norm.y * sq; cp.z += d * norm.z * sq; float bw = bi.weight * oow; if ( j == 0 ) { p.x = cp.x * bw; p.y = cp.y * bw; p.z = cp.z * bw; } else { p.x += cp.x * bw; p.y += cp.y * bw; p.z += cp.z * bw; } //cp += ((bi.dist * shrink) + gap) * norm.normalized; //p += cp * (bi.weight / bindverts[i].weight); } Vector3 pp = stm.MultiplyPoint3x4(p); verts[i].x = pp.x + offset.x; verts[i].y = pp.y + offset.y; verts[i].z = pp.z + offset.z; //verts[i] = transform.InverseTransformPoint(p) + offset; } } } else { stm = transform.worldToLocalMatrix * target.transform.localToWorldMatrix; // * transform.worldToLocalMatrix; //Matrix4x4 tm = target.transform.localToWorldMatrix; for ( int i = 0; i < bindverts.Length; i++ ) { if ( bindverts[i].verts.Count > 0 ) { p = Vector3.zero; float oow = 1.0f / bindverts[i].weight; for ( int j = 0; j < bindverts[i].verts.Count; j++ ) { MegaBindInf bi = bindverts[i].verts[j]; Vector3 p0 = target.sverts[bi.i0]; Vector3 p1 = target.sverts[bi.i1]; Vector3 p2 = target.sverts[bi.i2]; Vector3 cp = GetCoordMine(p0, p1, p2, bi.bary); Vector3 norm = FaceNormal(p0, p1, p2); float sq = 1.0f / Mathf.Sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z); float d = (bi.dist * shrink) + gap; //cp += d * norm.x; cp.x += d * norm.x * sq; cp.y += d * norm.y * sq; cp.z += d * norm.z * sq; float bw = bi.weight * oow; if ( j == 0 ) { p.x = cp.x * bw; p.y = cp.y * bw; p.z = cp.z * bw; } else { p.x += cp.x * bw; p.y += cp.y * bw; p.z += cp.z * bw; } //cp += ((bi.dist * shrink) + gap) * norm.normalized; //p += cp * (bi.weight / bindverts[i].weight); } } else p = freeverts[i]; //startverts[i]; Vector3 pp = stm.MultiplyPoint3x4(p); verts[i].x = pp.x + offset.x; verts[i].y = pp.y + offset.y; verts[i].z = pp.z + offset.z; //p = target.transform.TransformPoint(p); //verts[i] = transform.InverseTransformPoint(p) + offset; } } mesh.vertices = verts; RecalcNormals(); mesh.RecalculateBounds(); } public MegaNormMap[] mapping; public int[] tris; public Vector3[] facenorms; public Vector3[] norms; int[] FindFacesUsing(Vector3 p, Vector3 n) { List faces = new List(); Vector3 v = Vector3.zero; for ( int i = 0; i < tris.Length; i += 3 ) { v = verts[tris[i]]; if ( v.x == p.x && v.y == p.y && v.z == p.z ) { if ( n.Equals(norms[tris[i]]) ) faces.Add(i / 3); } else { v = verts[tris[i + 1]]; if ( v.x == p.x && v.y == p.y && v.z == p.z ) { if ( n.Equals(norms[tris[i + 1]]) ) faces.Add(i / 3); } else { v = verts[tris[i + 2]]; if ( v.x == p.x && v.y == p.y && v.z == p.z ) { if ( n.Equals(norms[tris[i + 2]]) ) faces.Add(i / 3); } } } } return faces.ToArray(); } // Should call this from inspector when we change to mega public void BuildNormalMapping(Mesh mesh, bool force) { if ( mapping == null || mapping.Length == 0 || force ) { // so for each normal we have a vertex, so find all faces that share that vertex tris = mesh.triangles; norms = mesh.normals; facenorms = new Vector3[tris.Length / 3]; mapping = new MegaNormMap[verts.Length]; for ( int i = 0; i < verts.Length; i++ ) { mapping[i] = new MegaNormMap(); mapping[i].faces = FindFacesUsing(verts[i], norms[i]); } } } public void RecalcNormals() { if ( NormalMethod == MegaNormalMethod.Unity ) //|| mapping == null ) mesh.RecalculateNormals(); else { if ( mapping == null ) BuildNormalMapping(mesh, false); RecalcNormals(mesh, verts); } } public void RecalcNormals(Mesh ms, Vector3[] _verts) { int index = 0; Vector3 v30 = Vector3.zero; Vector3 v31 = Vector3.zero; Vector3 v32 = Vector3.zero; Vector3 va = Vector3.zero; Vector3 vb = Vector3.zero; for ( int f = 0; f < tris.Length; f += 3 ) { v30 = _verts[tris[f]]; v31 = _verts[tris[f + 1]]; v32 = _verts[tris[f + 2]]; va.x = v31.x - v30.x; va.y = v31.y - v30.y; va.z = v31.z - v30.z; vb.x = v32.x - v31.x; vb.y = v32.y - v31.y; vb.z = v32.z - v31.z; v30.x = va.y * vb.z - va.z * vb.y; v30.y = va.z * vb.x - va.x * vb.z; v30.z = va.x * vb.y - va.y * vb.x; // Uncomment this if you dont want normals weighted by poly size //float l = v30.x * v30.x + v30.y * v30.y + v30.z * v30.z; //l = 1.0f / Mathf.Sqrt(l); //v30.x *= l; //v30.y *= l; //v30.z *= l; facenorms[index++] = v30; } for ( int n = 0; n < norms.Length; n++ ) { if ( mapping[n].faces.Length > 0 ) { Vector3 norm = facenorms[mapping[n].faces[0]]; for ( int i = 1; i < mapping[n].faces.Length; i++ ) { v30 = facenorms[mapping[n].faces[i]]; norm.x += v30.x; norm.y += v30.y; norm.z += v30.z; } float l = norm.x * norm.x + norm.y * norm.y + norm.z * norm.z; l = 1.0f / Mathf.Sqrt(l); norm.x *= l; norm.y *= l; norm.z *= l; norms[n] = norm; } else norms[n] = Vector3.up; } ms.normals = norms; } }