Code Reference – AllShape

 

Thor Brigsted

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// Helper class that represents a parameterized vertex
/// </summary>
public class Vector3Param {
	///bernstein polynomial packing 
	public List<List<float>> bernPolyPack;

	///Point after applying s,t,u to p0, should result in original point
	public Vector3 p = Vector3.zero;

	///Origin
	public Vector3 p0 = Vector3.zero;

	///Distances along S/T/U axes
	public float s,t,u;

	public Vector3Param()
	{
		s = 0.0f;
		t = 0.0f;
		u = 0.0f;
	}

	public Vector3Param(Vector3Param v)
	{
		s = v.s;
		t = v.t;
		u = v.u;
		p = v.p;
		p0 = v.p0;
	}

};

/// <summary>
/// Free form deformation class
/// 
/// Based off of the paper 'Free-Form Deformation of Solid Geometric Models'[1] this class
/// creates a system of control points that can deform a mesh as if that mesh was embedded
/// in a flexible parallelpiped.
/// 
/// Confused?  Yeah, who uses the term parallelpiped.  The idea is to create a uniformly spaced
/// grid of control points around some mesh.  Each control point has some effect on the mesh, like
/// bone weighting in animation.  The effect each control point has is directly proportional to the
/// total number of control points in the entire grid, each exerts some control.
/// 
/// [1] - http://pages.cpsc.ucalgary.ca/~blob/papers/others/ffd.pdf
/// </summary>
public class FreeFormDeformer : MonoBehaviour {

	/// <summary>
	/// Allow FixedUpdate to modify the mesh.
	/// </summary>
	public bool AllowMeshUpdate = false;

	/// <summary>
	/// Target to be morphed
	/// </summary>
	Mesh MorphTarget = null;

	/// <summary>
	/// Target to be filtered (assumed to contain a meshfilter and valid mesh)
	/// </summary>
	public MeshFilter MorphTargetFilter = null;

	/// <summary>
	/// Update frequency in seconds
	/// </summary>
	public float UpdateFrequency = 1.0f;

	/// <summary>
	/// Game object to represent a control point.  Can be anything really, I suggest spheres.
	/// </summary>
	public GameObject ControlPointPrefab;

	/// <summary>
	/// Local coordinate system
	/// </summary>
	Vector3 S, T, U;

	/// <summary>
	/// Number of controls for S, T, & U respectively.  (L,M, and N MUST be >= 1)
	/// </summary>
	public int L=1, M=1, N=1;

	/// <summary>
	/// Time elapsed since last update
	/// </summary>
	float elapsedTime = 0.0f;

	/// <summary>
	/// Grid of controls points. Stored as 3D grid for easier because width,height, and depth can randomly vary. 
	/// </summary>
	GameObject[, ,] controlPoints;

	/// <summary>
	/// Original vertices from MorphTarget
	/// </summary>
	Vector3[] originalVertices;

	/// <summary>
	/// Current updated vertices for MorphTarget
	/// </summary>
	Vector3[] transformedVertices;


	/// <summary>
	/// Vertex parameters
	/// 
	/// Each vertex is given a set of parameters that will define
	/// its final position based on a local coordinate system.
	/// </summary>
	List<Vector3Param> vertexParams = new List<Vector3Param>();

	void Start () {
		MorphTarget = MorphTargetFilter.mesh ;
		originalVertices = MorphTarget.vertices;
		transformedVertices = new Vector3[originalVertices.Length];

		Parameterize();
	}

	/// <summary>
	/// Calculate a binomial coefficient using the multiplicative formula
	/// </summary>
	float binomialCoeff(int n, int k){
		float total = 1.0f;
		for(int i = 1; i <= k; i++){
			total *= (n - (k - i)) / (float)i;
		}
		return total;
	}

	/// <summary>
	/// Calculate a bernstein polynomial
	/// </summary>
	float bernsteinPoly(int n, int v, float x)
	{
		return binomialCoeff(n,v) * Mathf.Pow(x, (float)v) * Mathf.Pow((float)(1.0f - x), (float)(n - v));
	}

	/// <summary>
	/// Calculate local coordinates
	/// </summary>
	void calculateSTU(Vector3 max, Vector3 min){
		S = new Vector3(max.x - min.x, 0.0f, 0.0f);
		T = new Vector3(0.0f, max.y - min.y, 0.0f);
		U = new Vector3(0.0f, 0.0f, max.z - min.z);
	}

	/// <summary>
	/// Calculate the trivariate bernstein polynomial as described by [1]
	/// 
	/// My method adapts [1] slightly by precalculating the BP coefficients and storing
	/// them in Vector3Param.  When it comes time to extract a world coordinate, 
	/// it's just a matter of summing up multiplications through each polynomial from eq (2).
	/// </summary>
	/// <links>
	///  [1] - Method based on: http://pages.cpsc.ucalgary.ca/~blob/papers/others/ffd.pdf
	/// </links>
	/// <param name="p0">Origin of our coordinate system (where STU meet)</param>
	void calculateTrivariateBernsteinPolynomial(Vector3 p0){
		Vector3 TcU = Vector3.Cross(T, U);
		Vector3 ScU = Vector3.Cross(S, U);
		Vector3 ScT = Vector3.Cross(S, T);

		float TcUdS = Vector3.Dot(TcU, S);
		float ScUdT = Vector3.Dot(ScU, T);
		float ScTdU = Vector3.Dot(ScT, U);

		for (int v = 0; v < originalVertices.Length; v++)
		{
			Vector3 diff = originalVertices[v] - p0;

			Vector3Param tmp = new Vector3Param();
			tmp.s = Vector3.Dot(TcU, diff / TcUdS);
			tmp.t = Vector3.Dot(ScU, diff / ScUdT);
			tmp.u = Vector3.Dot(ScT, diff / ScTdU);
			tmp.p = p0 + (tmp.s * S) + (tmp.t * T) + (tmp.u * U);
			tmp.p0 = p0;
			tmp.bernPolyPack = new List<List<float>>();

			{	// Reserve room for each bernstein polynomial pack.
				tmp.bernPolyPack.Add(new List<float>(L));	//outer bernstein poly
				tmp.bernPolyPack.Add(new List<float>(M));	//middle bernstein poly
				tmp.bernPolyPack.Add(new List<float>(N));	//inner bernstein poly
			}

			{	// Pre-calculate bernstein polynomial expansion.  It only needs to be done once per parameterization
				for (int i = 0; i <= L; i++)
				{
					for (int j = 0; j <= M; j++)
					{
						for (int k = 0; k <= N; k++)
						{
							tmp.bernPolyPack[2].Add(bernsteinPoly(N, k, tmp.u));
						}
						tmp.bernPolyPack[1].Add(bernsteinPoly(M, j, tmp.t));
					}
					tmp.bernPolyPack[0].Add(bernsteinPoly(L, i, tmp.s));
				}
			}
			vertexParams.Add(tmp);
			if (Vector3.Distance(tmp.p, originalVertices[v]) > 0.001f)
			{
				//Debug.Log("Warning, mismatched parameterization");
			}
		}
	}

	/// <summary>
	/// Parameterize MorphTarget's vertices
	/// </summary>
	void Parameterize(){
		Vector3 min = new Vector3(Mathf.Infinity,Mathf.Infinity,Mathf.Infinity);
		Vector3 max = new Vector3(-Mathf.Infinity,-Mathf.Infinity,-Mathf.Infinity);
		foreach(Vector3 v in originalVertices){
			max = Vector3.Max(v,max);
			min = Vector3.Min(v,min);
		}
		calculateSTU(max, min);
		calculateTrivariateBernsteinPolynomial(min);
		createControlPoints(min);
	}


	/// <summary>
	/// Create grid of control points.
	/// </summary>
	void createControlPoints(Vector3 origin){
		controlPoints = new GameObject[L + 1, M + 1, N + 1];
		for(int i = 0; i <= L; i++){
			for(int j = 0; j <= M; j++){
				for(int k = 0; k <= N; k++){
					controlPoints[i, j, k] = createControlPoint(origin, i, j, k);
				}
			}
		}
	}

	/// <summary>
	/// Create a single control point.
	/// </summary>
	GameObject createControlPoint(Vector3 p0, int i, int j, int k)
	{
		Vector3 position = p0 + (i / (float)L * S) + (j / (float)M * T) + (k / (float)N * U);
		return (GameObject)Instantiate(ControlPointPrefab, position, Quaternion.identity);
	}

	/// <summary>
	/// Convert parameterized vertex in to a world coordinate
	/// </summary>
	Vector3 getWorldVector3(Vector3Param r){
		int l = L;
		int m = M;
		int n = N;

		Vector3 tS = Vector3.zero;
		for(int i = 0; i <= l; i++){
			
			Vector3 tM = Vector3.zero;
			for(int j = 0; j <= m; j++){

				Vector3 tK = Vector3.zero;
				for(int k = 0; k <= n; k++){
					tK += r.bernPolyPack[2][k] * controlPoints[i,j,k].transform.position;
				}
				tM += r.bernPolyPack[1][j] * tK;
			}
			tS += r.bernPolyPack[0][i] * tM;
		}
		return tS;
	}


	void UpdateMesh(){
		elapsedTime = 0.0f;

		int idx = 0;
		foreach(Vector3Param vp in vertexParams){
			Vector3 p = getWorldVector3(vp);
			transformedVertices[idx++] = p;
		}

		MorphTarget.vertices = transformedVertices;
		MorphTarget.RecalculateBounds();
		MorphTarget.RecalculateNormals();
		MorphTarget.Optimize();
	}

	void FixedUpdate()
	{
		elapsedTime += Time.fixedDeltaTime;
		if (AllowMeshUpdate)
		{
			if (elapsedTime >= UpdateFrequency) UpdateMesh();
		}
	}
	// Update is called once per frame
	void Update () {
	
	}
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRTK;
using VRTK.GrabAttachMechanics;
using ArgosTweenSpacePuppy;

public class Tetra_MAJOR : MonoBehaviour
{
    public enum State
    {
        FREE,
        ATTACHED,
    }

    public bool bTurn_Off_Collider = false;

    public State state;

    public enum SpokeState
    {
        OPEN,
        OCCUPIED,
    }

    public class Spoke_Tracker
    {
        public SpokeState spokeState = SpokeState.OPEN;
        public GameObject go_occupying = null;
        public float touchTimer = 0;
        public GameObject genSpoke;
    }

    private int id = 0;

    public GameObject[] vertsHT = new GameObject[4]; 

    public Spoke_Tracker[] spoke_Tracker = new Spoke_Tracker[4];
    [Range(0, 10f)]
    public float Radius_of_Action = 7f;

    [Range(0, 1.61803f)]
    public float tDurr = 0.7f;

    private bool bForce_update = false;

    private float tAccum = 0f;

    private float tween_Val = 0f;

    private float touchTimer = 0;

    private Spoke_Tracker attachedSpoke;
    private GameObject attachedTetra_go;

    private Vector3 start_Pos;
    private Vector3 end_Pos;
    private Quaternion qStart;
    private Quaternion qEnd;

    private HMD_Ctrl_Tracking hmd_Ctrl_Tracking;
    private VU_UI_MANAGER VU_UI;
    private ALL_Shape_Instancer all_shape_instancer;
    private bool bTouchGrabEnabled = true;
    private bool bGrabbed = false;

    private VRTK_InteractableObject vrtk_interact;

    public bool bInteractive = true;

    void Start()
    {
        hmd_Ctrl_Tracking = HMD_Ctrl_Tracking.Instance;
        VU_UI = VU_UI_MANAGER.Instance;

        all_shape_instancer = VU_UI.GetComponent<ALL_Shape_Instancer>();

        vrtk_interact = GetComponent<VRTK_InteractableObject>();

        vrtk_interact.InteractableObjectGrabbed   += new InteractableObjectEventHandler(DoObjectGrabbed);
        vrtk_interact.InteractableObjectUngrabbed += new InteractableObjectEventHandler(DoObjectUnGrabbed);

        Vector3[] face_Center = new Vector3[4];

        face_Center[0] = (vertsHT[0].transform.localPosition + vertsHT[1].transform.localPosition + vertsHT[2].transform.localPosition) / 3f;
        face_Center[1] = (vertsHT[1].transform.localPosition + vertsHT[2].transform.localPosition + vertsHT[3].transform.localPosition) / 3f;
        face_Center[2] = (vertsHT[0].transform.localPosition + vertsHT[2].transform.localPosition + vertsHT[3].transform.localPosition) / 3f;
        face_Center[3] = (vertsHT[0].transform.localPosition + vertsHT[1].transform.localPosition + vertsHT[3].transform.localPosition) / 3f;

        float avg_FC = (face_Center[0].magnitude + face_Center[1].magnitude + face_Center[2].magnitude + face_Center[3].magnitude) / 4f;

        Vector3 axis = new Vector3();
        Quaternion q;

        for (int i = 0; i < 4; i++)
        {
            spoke_Tracker[i] = new Spoke_Tracker();
            spoke_Tracker[i].spokeState = SpokeState.OPEN;
            spoke_Tracker[i].genSpoke = new GameObject();

            spoke_Tracker[i].genSpoke.name = "genSpoke_" + i.ToString();

            spoke_Tracker[i].genSpoke.transform.parent = transform;

            spoke_Tracker[i].genSpoke.transform.localPosition = face_Center[i].normalized * 2f*avg_FC;

            axis = vertsHT[i].transform.localPosition - face_Center[i];

            q = Quaternion.AngleAxis(180f, axis);

            spoke_Tracker[i].genSpoke.transform.localRotation = q;
        }


        //if (bInteractive)
        //{
        //    for (int i = 0; i < tet.Length; i++)
        //    {
        //        tet[i].id = i;
        //        tetras.Add(tet[i]);
        //    }
        //}


    }

    private void DoObjectGrabbed(object sender, InteractableObjectEventArgs e)
    {
        bGrabbed = true;

        touchTimer = 1f;

        //if (state == State.ATTACHED)
        //{
        //    //octa_Major.Detach(id);
        //    state = State.FREE;
        //}
    }

    private void DoObjectUnGrabbed(object sender, InteractableObjectEventArgs e)
    {
        bGrabbed = false;
    }

    public void Detach(int id)
    {
        //for (int i = 0; i < spoke_Tracker.Length; i++)
        //{
        //    if (spoke_Tracker[i].go_occupying != null)
        //    {
        //        if (spoke_Tracker[i].go_occupying.GetComponent<Tetra_MAJOR>().id == id)
        //        {
        //            spoke_Tracker[i].touchTimer = 2f;
        //            spoke_Tracker[i].go_occupying = null;
        //            print("OPEN HAPPENED");
        //        }
        //    }
        //}
    }

    void Update()
    {
        touchTimer -= Time.deltaTime;
        if (bGrabbed)
        {
            float dist;
            foreach (GameObject g in all_shape_instancer.getList())
            {
                Spoke_Tracker[] sp = g.GetComponent<Tetra_MAJOR>().spoke_Tracker;

                if (g != gameObject)
                {                
                    for (int i = 0; i < 4; i++)
                    {
                        dist = (sp[i].genSpoke.transform.position - transform.position).magnitude;
   
                        if (dist < Radius_of_Action && sp[i].spokeState == SpokeState.OPEN && touchTimer < 0)
                        {
                            print("Within Range of idx: " + i.ToString());

                            //disconnect
                            GetComponent<VRTK_FixedJointGrabAttach>().StopGrab(false);

                            //sp[i].spokeState = SpokeState.OCCUPIED;
                            sp[i].go_occupying = this.gameObject;
                            //t.state = Tetra_MAJOR.State.ATTACHED;

                            attachedSpoke = sp[i];
                            attachedTetra_go = gameObject;
                            start_Pos = transform.position;
                            end_Pos = sp[i].genSpoke.transform.position;
                            qStart = transform.rotation;
                            qEnd = sp[i].genSpoke.transform.rotation;
                            tAccum = 0;

                            StartCoroutine(Tween_Move_to_Spoke());
                        }
                    }
                }
            }
            //Switch off Touch Grab if in the VU
            //if (!bTouchGrabEnabled && hmd_Ctrl_Tracking.Get_Current_USER_Location() != HMD_Ctrl_Tracking.USER_LOCATION.IN_THE_VU)
            //{
            //    Enable_Touch_Grab();
            //    bTouchGrabEnabled = true;
            //}
            //else if (bTouchGrabEnabled && hmd_Ctrl_Tracking.Get_Current_USER_Location() == HMD_Ctrl_Tracking.USER_LOCATION.IN_THE_VU)
            //{
            //    Disable_Touch_Grab();
            //    bTouchGrabEnabled = false;
            //}
        }
    }

    private void Enable_Touch_Grab()
    {
        //GetComponent<SphereCollider>().enabled = true;
        //foreach (Tetra_MAJOR t in tetras)
        //{
        //    t.GetComponent<SphereCollider>().enabled = true;
        //}
    }

    private void Disable_Touch_Grab()
    {
        //GetComponent<SphereCollider>().enabled = false;
        //foreach (GameObject g in all_shape_instancer.getList())
        //{
        //    g.GetComponent<SphereCollider>().enabled = false;
        //}
    }

    private IEnumerator Tween_Move_to_Spoke()
    {
        int i = 0; //Sanity check for infinite loops
        float linProg = tAccum / tDurr;

        while (i < 180 && (linProg < 1))
        {
            bForce_update = true;
            tAccum += Time.deltaTime;
            linProg = tAccum / tDurr;
            Mathf.Clamp(tAccum, 0, tDurr);

            tween_Val = EaseMethods.QuintEaseOut(tAccum, 0, 1, tDurr);
            Move_To_Spoke(tween_Val);

            yield return true;
            i++;
        }

        tAccum = tDurr;
        tween_Val = 1;

        Move_To_Spoke(tween_Val);
        bForce_update = false;
        StopCoroutine("Tween_State");
    }

    private void Move_To_Spoke(float val)
    {
        transform.position = Vector3.Lerp(start_Pos, end_Pos, val);
        transform.rotation = Quaternion.Slerp(qStart, qEnd, val);
    }
}