using System; using System.Collections; using System.Collections.Generic; using UnityEngine; [System.Serializable] public class LiquidVolumeAnimator : MonoBehaviour { // Use this for initialization //just incase we have multiple materials; [HideInInspector] [SerializeField] public Material[] mats; //the level of the liquid on a hyperplane. [Range(0,1)] [SerializeField] public float level = 0.5f; private float finalLevel; //the current bound given a direction public Vector2 minMaxBounds; //the mesh to [HideInInspector] [SerializeField] private MeshFilter mf; [HideInInspector] [SerializeField] private MeshRenderer mr; [HideInInspector] [SerializeField] private Mesh m; //in order to see how its working, physics is kind of hacked in public bool DebugAnchor = false; //debugsize is how large the handles are public float debugSize = 1.0f; //how much mass it has? kind of? public float _anchorLength = 0.5f; //how much the rotation and movement applies to the anchor [Range(0,1)] public float dampening; //it always wants to be rotated to down [HideInInspector] [SerializeField] private Vector3 anchor; //velocity of the direction of 'gravity' [HideInInspector] [SerializeField] private Vector3 anchorVelocity; //direction of velocity after clamp. ALSO used to see if the position has changed and use that as a velocity reference; [HideInInspector] [SerializeField] private Vector3 transformedPoint, prevTransformedPoint; public bool calculateTextureProjection = true; public float TextureSize = 1.0f; public float TextureSizeScalar = 1; public AnimationCurve texCurveSize = AnimationCurve.Linear(0, 1, 1, 1); //for rotation of the texture; Quaternion previous; float totalRotation = 0.0f; [HideInInspector] [SerializeField] private Vector3 TopLeft, TopRight, BottomLeft, BottomRight; public Transform ExposedLiquidT; public Vector3 GravityDirection = Vector3.down; public bool normalizeGravityDirection = true; //vert cache [HideInInspector] [SerializeField] Vector3[] verts; int shader_Key_localHeight; int shader_Key_anchor; int shader_Key_point; int shader_Key_level; float prvLevel = -1; Quaternion prevQ = Quaternion.identity; //for debugging purposes //for caching [HideInInspector] [SerializeField] Vector3 cPos = Vector3.zero; //output values: public Vector3 finalAnchor, finalPoint; [HideInInspector] [SerializeField] string[] shaderNames; void OnDrawGizmosSelected() { float anchorLength = _anchorLength * Mathf.Max(transform.lossyScale.x, transform.lossyScale.y, transform.lossyScale.z); if(DebugAnchor) { cPos = transform.position; //Vector3 prevTransformedPointD = transform.TransformDirection(Vector3.down); Vector3 anchorD = cPos - transform.TransformDirection(Vector3.up) * anchorLength; if (anchor == Vector3.zero) anchor = anchorD; //makes sure something is available (even while not playing). CalculateSquare(anchor); //the location of the anchor (static) Gizmos.DrawSphere(anchorD, 0.25f * transform.lossyScale.magnitude * 0.1f * debugSize); //line between the anchor and the surface Gizmos.DrawLine(cPos, anchorD); Gizmos.color = Color.blue; //the ocation of the anchor (physics) Gizmos.DrawSphere(anchor, 0.25f * transform.lossyScale.magnitude * 0.1f * debugSize); Gizmos.color = Color.red; Gizmos.DrawSphere(cPos - (anchorD - cPos).normalized * finalLevel, 0.1f * transform.lossyScale.magnitude * 0.01f * debugSize); //All four corners of the texture Gizmos.color = Color.yellow; Gizmos.DrawSphere(TopLeft, 0.25f * transform.lossyScale.magnitude * 0.01f * debugSize); Gizmos.DrawSphere(TopRight, 0.25f * transform.lossyScale.magnitude * 0.01f * debugSize); Gizmos.DrawSphere(BottomLeft, 0.25f * transform.lossyScale.magnitude * 0.01f * debugSize); Gizmos.DrawSphere(BottomRight, 0.25f * transform.lossyScale.magnitude * 0.01f * debugSize); Gizmos.color = Color.white; CalculateSquare(anchor); } } void CalculateSquare(Vector3 anch) { if (!calculateTextureProjection) { return; } Vector3 pos1 = cPos - (anch - cPos).normalized * finalLevel; Vector3 nrm = (cPos - (anch - cPos).normalized * finalLevel - anch).normalized; Vector3 beginningRight = Quaternion.Euler(0, totalRotation, 0) * Vector3.right; Vector3 f1 = Vector3.Cross(beginningRight, nrm.normalized).normalized; Vector3 r1 = Vector3.Cross(f1, nrm).normalized; f1 = Vector3.Cross(r1, nrm).normalized; float tSize = TextureSize * texCurveSize.Evaluate(Mathf.Clamp01(level)) * transform.lossyScale.magnitude * 0.001f; TopLeft = r1 * tSize + f1 * tSize + pos1; TopRight = r1 * tSize * -1 + f1 * tSize + pos1; BottomLeft = r1 * tSize + f1 * tSize * -1 + pos1; BottomRight = r1 * tSize * -1 - f1 * tSize + pos1; } void Start () { cPos = transform.position; shader_Key_localHeight = Shader.PropertyToID ("_localHeight"); shader_Key_anchor = Shader.PropertyToID ("_anchor"); shader_Key_point = Shader.PropertyToID ("_point"); shader_Key_level = Shader.PropertyToID ("_level"); //delta prevTransformedPoint = transformedPoint = transform.TransformDirection((normalizeGravityDirection) ? GravityDirection.normalized : GravityDirection); //anchor = cPos - transform.TransformDirection(Vector3.up) * anchorLength; anchor -= ((normalizeGravityDirection) ? GravityDirection.normalized : GravityDirection) * -1 * Time.deltaTime * (1.0f - dampening); anchor.Normalize(); mr = GetComponent(); mats = mr.materials; shaderNames = new string[mats.Length]; for (int i = 0; i < mats.Length; ++i) { mats[i] = Instantiate(mats[i]); shaderNames[i] = mats[i].shader.name; } mf = GetComponent(); m = mf.sharedMesh; verts = new Vector3[m.vertices.Length]; verts = m.vertices; minMaxBounds.x = minMaxBounds.y = verts[0].y; for (int i = 0; i < verts.Length; ++i) { Vector3 wPos = transform.TransformDirection(verts[i]); if (wPos.y > minMaxBounds.y) { minMaxBounds.y = wPos.y; } if (wPos.y < minMaxBounds.x) { minMaxBounds.x = wPos.y; } } minMaxBounds.x -= cPos.y; minMaxBounds.y -= cPos.y; for (int i = 0; i < mats.Length; ++i) { mats[i].SetFloat(shader_Key_localHeight, Mathf.Lerp(minMaxBounds.x,minMaxBounds.y,level)); } mr.materials = mats; } public void AddForce(Vector3 force) { anchorVelocity += force; } // Update is called once per frame void FixedUpdate () { cPos = transform.position; float nLength = _anchorLength * Mathf.Max(transform.lossyScale.x, transform.lossyScale.y, transform.lossyScale.z); //prevTransformedPoint = transform.TransformDirection(Vector3.down); //anchor = transform.position - transform.TransformDirection(Vector3.up) * nLength; transformedPoint = transform.TransformDirection((normalizeGravityDirection) ? GravityDirection.normalized : GravityDirection); ////Now we need to move the anchor down by gravity; //anchorVelocity += Vector3.down * Time.deltaTime * 0.1f; anchor += anchorVelocity * (1.0f - dampening); Vector3 prevPos = anchor; anchor -= ((normalizeGravityDirection) ? GravityDirection.normalized : GravityDirection) * -1 * Time.deltaTime * (1.0f - dampening); //clamp it float d = Vector3.Distance(anchor, cPos); //float difference = (nLength - d) / d; //float d2 = d / nLength; if (d > nLength) { anchor = cPos + (anchor - cPos).normalized * nLength; //connections[i].position = connections[i].position + (cPos - connections[i].position).normalized * difference * 0.5f; } Vector3 addition = (anchor - prevPos) + (transformedPoint - prevTransformedPoint) * -1 * (1.0f / nLength) * Time.deltaTime; //if there is no change dont bother updating. if (addition == Vector3.zero) { if (prvLevel == level && prevQ == transform.rotation) return; } anchorVelocity += addition; //find the difference and add it to the velocity; //Vector3 lastPos = anchor; //anchor += anchorVelocity * Time.deltaTime; //anchor = (anchor - cPos).normalized * anchorLength; //add the direction as velocity; //anchorVelocity += (b - lastPos); Matrix4x4 localToWorld = transform.localToWorldMatrix; minMaxBounds.x = minMaxBounds.y = (transform.TransformPoint(verts[0])).y; for (int i = 0; i < verts.Length; ++i) { Vector3 wPos = localToWorld.MultiplyPoint(verts[i]); //Vector3 wPos = transform.TransformPoint(verts[i]); if (wPos.y > minMaxBounds.y) { minMaxBounds.y = wPos.y; } if (wPos.y < minMaxBounds.x) { minMaxBounds.x = wPos.y; } } minMaxBounds.y -= cPos.y; minMaxBounds.x -= cPos.y; //minMaxBounds.x += cPos.y; //minMaxBounds.y += cPos.y; finalLevel = Mathf.Lerp(minMaxBounds.x, minMaxBounds.y, level); if(level <= Single.Epsilon * 10) { //dont render (turn off renderer) anchor = Vector3.down * nLength + cPos; } finalPoint = (cPos - (anchor - cPos).normalized * finalLevel); for (int i = 0; i < mats.Length; ++i) { mats[i].SetFloat(shader_Key_localHeight, Mathf.Lerp(minMaxBounds.x - Single.Epsilon * 10, minMaxBounds.y + Single.Epsilon * 10, level)); mats[i].SetVector(shader_Key_anchor, transform.InverseTransformPoint(anchor)); mats[i].SetVector(shader_Key_point, transform.InverseTransformPoint(cPos - (anchor - cPos).normalized * finalLevel)); mats[i].SetFloat(shader_Key_level, level - Single.Epsilon);//to make sure its not rendered accidentally by rotations } //lets update the size of the plane (for texture projection); // Vector3 anchorD = cPos - transform.TransformDirection(Vector3.up) * anchorLength; finalAnchor = anchor; CalculateSquare(anchor); //lets find the delta y rotation; (global); //Quaternion rot = Quaternion.RotateTowards(previous, transform.rotation, 360); //make two quaternions static on the xz plane (only rotation about the y) Quaternion q1 = Quaternion.LookRotation(previous * Vector3.right, Vector3.up); //to find the obtuse angle Vector3 vqf = q1 * Vector3.right; //returns the acute angle, very cute indeed. Quaternion q2 = Quaternion.LookRotation(transform.rotation * Vector3.right, Vector3.up); float angle = Quaternion.Angle(q1, q2) * ((Vector3.Dot(vqf,q2 * Vector3.forward) < 0) ? -1 : 1); float ydelta = angle; //to correct weird rotations (from cross product potentially). (approximation errors) if(Mathf.Abs(ydelta) > 0.05f) totalRotation += ydelta; if(totalRotation > 360) { totalRotation -= 360; } else if(totalRotation < 0) { totalRotation += 360; } if (ExposedLiquidT != null) { ExposedLiquidT.position = cPos - (anchor - cPos).normalized * finalLevel; ExposedLiquidT.localScale = Vector3.one *texCurveSize.Evaluate(Mathf.Clamp01(level)) * transform.lossyScale.magnitude * 0.001f * TextureSize * TextureSizeScalar; ExposedLiquidT.up = (finalPoint - finalAnchor).normalized; } prevTransformedPoint = transformedPoint; previous = transform.rotation; //finally, lets set the plane. Vector4 sTopLeft = transform.InverseTransformPoint(TopLeft); Vector4 sTopRight = transform.InverseTransformPoint((TopRight)); Vector4 sBotLeft = transform.InverseTransformPoint((BottomLeft)); Vector4 sBotRight = transform.InverseTransformPoint((BottomRight)); Vector4 sCenter = transform.InverseTransformPoint(cPos - (anchor - cPos).normalized * finalLevel); for (int i = 0; i < mats.Length; ++i) { if (!shaderNames[i].Contains ("_Texture")) continue; mats[i].SetVector("_TL", sTopLeft); mats[i].SetVector("_TR", sTopRight); mats[i].SetVector("_BL", sBotLeft); mats[i].SetVector("_BR", sBotRight); mats[i].SetVector("_CENTER", sCenter); } prvLevel = level; prevQ = transform.rotation; } }