using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class MeshLiquidEmission : MonoBehaviour { // Use this for initialization [System.Serializable] private class BVertex { public Vector3 p; public Vector3 n; public bool b; } [HideInInspector] [SerializeField] private MeshFilter r; public LiquidVolumeAnimator LVA; [HideInInspector] [SerializeField] int[] calculatedTriangles; [HideInInspector] [SerializeField] BVertex[] calculatedVerts; public ParticleSystem system; float particlesToEmit = 0; public float emissionSpeed = 0.0f; private Mesh m; [HideInInspector] [SerializeField] int[] tris; [HideInInspector] [SerializeField] Vector3[] verts, norms; public bool debug = false; public float debugScale = 1.0f; public bool CullNullNormals = false; public Rigidbody Cork; public float volumeOfParticles = 70.0f; public bool emitting = true; public BottleSmash bottleSmash; public float angleSpeedScalar = 1.0f; int CVOB = 0; void Start () { r = GetComponent(); m = r.mesh; calculatedVerts = new BVertex[m.vertexCount * 6]; for(int i = 0; i < calculatedVerts.Length; ++i) { calculatedVerts[i] = new BVertex(); } verts = m.vertices; tris = m.triangles; norms = m.normals; } bool LinePlaneIntersection(Vector3 p0, Vector3 p1, Vector3 planePoint, Vector3 planeNormal, out Vector3 coordinate) { Vector3 line = p1 - p0; coordinate = Vector3.zero; float d = Vector3.Dot(planeNormal.normalized, line); if(Mathf.Abs(d) > float.Epsilon) { Vector3 w = p0 - planePoint; float fac = (Vector3.Dot(planeNormal.normalized, w) * -1) / d; line = line * fac; coordinate = p0 + line; return true; } else { return false; } } void OnDrawGizmos() { if (!debug) return; if(calculatedVerts != null) { for(int i =0; i < calculatedVerts.Length; ++i) { if (!calculatedVerts[i].b) break; Gizmos.DrawSphere(transform.TransformPoint(calculatedVerts[i].p), 0.01f * transform.lossyScale.magnitude * debugScale); } } } // Update is called once per frame void SetDual(int under1, int under2, int above, ref Vector3 dir, ref Vector3 lpos, ref int currentVOB, ref Vector3 tmpV) { calculatedVerts[currentVOB].p = verts[under1]; calculatedVerts[currentVOB].n = norms[under1]; calculatedVerts[currentVOB].b = true; LinePlaneIntersection(verts[under1], verts[above], lpos, (dir * -1), out tmpV); calculatedVerts[currentVOB + 1].p = tmpV; calculatedVerts[currentVOB + 1].n = Vector3.Lerp(norms[under1], norms[above], (tmpV - verts[under1]).magnitude / (verts[under1] - verts[above]).magnitude); calculatedVerts[currentVOB + 1].b = true; calculatedVerts[currentVOB + 2].p = verts[under2]; calculatedVerts[currentVOB + 2].n = norms[under2]; calculatedVerts[currentVOB + 2].b = true; calculatedVerts[currentVOB + 3].p = verts[under2]; calculatedVerts[currentVOB + 3].n = norms[under2]; calculatedVerts[currentVOB + 3].b = true; calculatedVerts[currentVOB + 4].p = tmpV; calculatedVerts[currentVOB + 4].n = calculatedVerts[currentVOB + 1].n; calculatedVerts[currentVOB + 4].b = true; LinePlaneIntersection(verts[under2], verts[above], lpos, (dir * -1), out tmpV); calculatedVerts[currentVOB + 5].p = tmpV; calculatedVerts[currentVOB + 5].n = Vector3.Lerp(norms[under2], norms[above], (tmpV - verts[under2]).magnitude / (verts[under2] - verts[above]).magnitude); calculatedVerts[currentVOB + 5].b = true; } void SetDualInverted(int under1, int above1, int above2, ref Vector3 dir, ref Vector3 lpos, ref int currentVOB, ref Vector3 tmpV) { calculatedVerts[currentVOB].p = verts[under1]; calculatedVerts[currentVOB].n = norms[under1]; calculatedVerts[currentVOB].b = true; LinePlaneIntersection(verts[under1], verts[above1], lpos, (dir * -1), out tmpV); calculatedVerts[currentVOB + 1].p = tmpV; calculatedVerts[currentVOB + 1].n = Vector3.Lerp(norms[under1], norms[above1], (tmpV - verts[under1]).magnitude / (verts[under1] - verts[above1]).magnitude); calculatedVerts[currentVOB + 1].b = true; LinePlaneIntersection(verts[under1], verts[above2], lpos, (dir * -1), out tmpV); calculatedVerts[currentVOB + 2].p = tmpV; calculatedVerts[currentVOB + 2].n = Vector3.Lerp(norms[under1], norms[above2], (tmpV - verts[under1]).magnitude / (verts[under1] - verts[above2]).magnitude); calculatedVerts[currentVOB + 2].b = true; } void CalculateTrianglesToEmitFrom(int[] tris, Vector3[] verts) { Vector3 dir = (LVA.finalAnchor - LVA.finalPoint).normalized; Vector3 lpos = transform.InverseTransformPoint(LVA.finalPoint); dir = transform.InverseTransformDirection(dir).normalized; int currentVOB = 0; Vector3 tmpV = Vector3.zero; //for every triangle, check if ANY vertex is below the level. for (int i = 2; i < tris.Length; i+=3) { int v1i = tris[i - 2], v2i = tris[i - 1], v3i = tris[i]; Vector3 v1 = verts[v1i], v2 = verts[v2i], v3 = verts[v3i]; Vector3 v1d = v1 - lpos; bool v1b = Vector3.Dot(dir, v1d) >= 0; Vector3 v2d = v2 - lpos; bool v2b = Vector3.Dot(dir, v2d) >= 0; Vector3 v3d = v3 - lpos; bool v3b = Vector3.Dot(dir, v3d) >= 0; //all are under if(v3b && v2b && v1b) { //add ALL vertices to buffer calculatedVerts[currentVOB].p = v1; calculatedVerts[currentVOB].n = norms[v1i]; calculatedVerts[currentVOB].b = true; calculatedVerts[currentVOB+1].p = v2; calculatedVerts[currentVOB+1].n = norms[v2i]; calculatedVerts[currentVOB+1].b = true; calculatedVerts[currentVOB+2].p = v3; calculatedVerts[currentVOB+2].n = norms[v2i]; calculatedVerts[currentVOB+2].b = true; currentVOB += 3; continue; } else if(!(v3b || v2b || v1b)) { //none are in, do nothing continue; } else { //one or TWO of them out of 3 are under; bool found = false; if(v1b && v2b) { //subdivide face with projected vertices SetDual(v1i, v2i, v3i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 6; found = true; } if (v1b && v3b) { SetDual(v1i, v3i, v2i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 6; found = true; } if (v2b && v3b) { SetDual(v3i, v2i, v1i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 6; found = true; } if(found) { continue; } if(v1b) { SetDualInverted(v1i, v2i, v3i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 3; continue; } if (v2b) { SetDualInverted(v2i, v1i, v3i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 3; continue; } if (v3b) { SetDualInverted(v3i, v2i, v1i, ref dir, ref lpos, ref currentVOB, ref tmpV); currentVOB += 3; continue; } continue; } } if(currentVOB < calculatedVerts.Length) { calculatedVerts[currentVOB].b = false; } CVOB = currentVOB; } float GetPS_StartSpeed() { switch (system.main.startSpeed.mode) { case ParticleSystemCurveMode.TwoConstants: return UnityEngine.Random.Range(system.main.startSpeed.constantMin, system.main.startSpeed.constantMax); case ParticleSystemCurveMode.Constant: return system.main.startSpeed.constant; case ParticleSystemCurveMode.Curve: return system.main.startSpeed.Evaluate(UnityEngine.Random.value); case ParticleSystemCurveMode.TwoCurves: return system.main.startSpeed.Evaluate(UnityEngine.Random.value); default: return 0.0f; } } bool EmitFromSubmesh() { //randomly select ONE triangle; // div by 3 int index = UnityEngine.Random.Range(0,(CVOB / 3)-1); //multiply out to corresponding id; index *= 3; //find the position to emit from; //pick vert1, lerp to v2 randomly, then v3 randomly. float r1 = UnityEngine.Random.value; float r2 = UnityEngine.Random.value; Vector3 pos = Vector3.Lerp(calculatedVerts[index].p, calculatedVerts[index + 1].p, r1); Vector3 nrm = Vector3.Lerp(calculatedVerts[index].n, calculatedVerts[index + 1].n, r1).normalized; pos = Vector3.Lerp(pos, calculatedVerts[index + 2].p, r2); nrm = Vector3.Lerp(nrm, calculatedVerts[index + 2].n, r2).normalized; //ParticleSystem.Particle p = new ParticleSystem.Particle(); //p.position = pos; //p.angularVelocity3D = ParticleSystem.EmitParams param = new ParticleSystem.EmitParams(); float pouringAngle = 1; if (bottleSmash != null) { Vector3 wNrm = transform.TransformDirection(nrm).normalized; pouringAngle = ((1.0f + Vector3.Dot((LVA.finalAnchor - LVA.finalPoint).normalized, wNrm))/2.0f); param.velocity = wNrm * GetPS_StartSpeed() * pouringAngle * angleSpeedScalar; } else { param.velocity = transform.TransformDirection(nrm).normalized * GetPS_StartSpeed(); } param.position = transform.TransformPoint(pos); if (param.velocity == Vector3.zero && CullNullNormals) return false; particlesToEmit -= 1; //adjust level if (Cork != null) { if (Cork.isKinematic && volumeOfParticles > 0) { // LVA.level = Mathf.Clamp01((volumeOfParticles * LVA.level - 1) / volumeOfParticles); } } else if(volumeOfParticles > 0) { LVA.level = Mathf.Clamp01((volumeOfParticles * LVA.level - 1) / volumeOfParticles); } system.Emit(param, 1); return true; } void Update () { if (!emitting || Cork != null) return; if (LVA.level <= 0) return; CalculateTrianglesToEmitFrom(tris, verts); //triangles are in order particlesToEmit += emissionSpeed * Time.deltaTime; if(calculatedVerts.Length == 0) { particlesToEmit = 0; } else { if(calculatedVerts[0].b == false) particlesToEmit = 0; } int maxFailInASeq = 10; int seq = maxFailInASeq; while(particlesToEmit > 0 && (GetPS_StartSpeed() > 0 || !CullNullNormals)) { if (EmitFromSubmesh()) { seq = maxFailInASeq; } else { seq -= 1; } if (seq <= 0) { break; } } } }