Menu

VR_Hand C

Davide Enea Roberto Bobby Riva

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

public class VR_Hand : MonoBehaviour {
public Animator animator;

public enum WhichHands { Left, Right };
public WhichHands whichHand;

private GameObject hand;
private Transform shoulder;
public Transform handTarget;
private Transform cHandTarget;
private Vector3 dPalm;

public float thumbInput;
public float indexInput;
public float middleInput;
public float ringInput;
public float littleInput;

private Thumb thumb;
private Finger indexFinger;
private Finger middleFinger;
private Finger ringFinger;
private Finger littleFinger;

private Digit[] digits;

private GameObject grabbedObject = null;
private Rigidbody grabbedRigidbody = null;
private GrabbedRigidbody grabbedRBfunc;

private Collider collidedObject;

private Vector3 palmOffset;
private GameObject go;

private enum LeftHandBones {
    ThumbProximal = 24,
    ThumbIntermediate = 25,
    ThumbDistal = 26,
    IndexProximal = 27,
    IndexIntermediate = 28,
    IndexDistal = 29,
    MiddleProximal = 30,
    MiddleIntermediate = 31,
    MiddleDistal = 32,
    RingProximal = 33,
    RingIntermediate = 34,
    RingDistal = 35, 
    LittleProximal = 36,
    LittleIntermediate = 37,
    LittleDistal = 38
};
private enum RightHandBones {
    ThumbProximal = 39,
    ThumbIntermediate = 40,
    ThumbDistal = 41,
    IndexProximal = 42,
    IndexIntermediate = 43,
    IndexDistal = 44,
    MiddleProximal = 45,
    MiddleIntermediate = 46,
    MiddleDistal = 47,
    RingProximal = 48,
    RingIntermediate = 49,
    RingDistal = 50, 
    LittleProximal = 51,
    LittleIntermediate = 52,
    LittleDistal = 53
};

void Start () {
    hand = this.gameObject;
    thumb = new Thumb();
    indexFinger = new Finger();
    middleFinger = new Finger();
    ringFinger = new Finger();
    littleFinger = new Finger();

    if (whichHand == WhichHands.Left) {
        thumb.transform = animator.GetBoneTransform((HumanBodyBones) LeftHandBones.ThumbIntermediate);
        indexFinger.transform = animator.GetBoneTransform((HumanBodyBones) LeftHandBones.IndexProximal);
        middleFinger.transform = animator.GetBoneTransform((HumanBodyBones) LeftHandBones.MiddleProximal);
        ringFinger.transform = animator.GetBoneTransform((HumanBodyBones) LeftHandBones.RingProximal);
        littleFinger.transform = animator.GetBoneTransform((HumanBodyBones) LeftHandBones.LittleProximal);

        shoulder = animator.GetBoneTransform(HumanBodyBones.LeftUpperArm);

    } else {
        thumb.transform = animator.GetBoneTransform((HumanBodyBones) RightHandBones.ThumbIntermediate);
        indexFinger.transform = animator.GetBoneTransform((HumanBodyBones) RightHandBones.IndexProximal);
        middleFinger.transform = animator.GetBoneTransform((HumanBodyBones) RightHandBones.MiddleProximal);
        ringFinger.transform = animator.GetBoneTransform((HumanBodyBones) RightHandBones.RingProximal);
        littleFinger.transform = animator.GetBoneTransform((HumanBodyBones) RightHandBones.LittleProximal);

        shoulder = animator.GetBoneTransform(HumanBodyBones.RightUpperArm);
    }

    GameObject x = new GameObject("Hand Palm");
    x.transform.position = hand.transform.position;
    x.transform.LookAt(indexFinger.transform.transform, Vector3.up);
    Vector3 handRightAxis = hand.transform.InverseTransformDirection(x.transform.right);

    palmOffset = (indexFinger.transform.transform.position - hand.transform.position) * 0.85f;
    palmOffset = hand.transform.InverseTransformDirection(palmOffset);
    palmOffset += new Vector3(0, 0, -0.03f);

    x.transform.parent = hand.transform;

    if (handTarget.childCount > 0) {
        cHandTarget = handTarget.GetChild(0); 
    } else {
        GameObject cHandTargetGO = new GameObject();
        cHandTargetGO.name = "Corrected Target";
        cHandTarget = cHandTargetGO.transform;
        cHandTarget.transform.parent = handTarget;
        cHandTarget.transform.localPosition = Vector3.zero;
        cHandTarget.transform.localEulerAngles = Vector3.zero;
        cHandTarget.transform.localScale = new Vector3(1, 1, 1);
    }

    digits = new Digit[5];
    digits[0] = thumb;
    digits[1] = indexFinger;
    digits[2] = middleFinger;
    digits[3] = ringFinger;
    digits[4] = littleFinger;

    thumb.Init (hand, handRightAxis, whichHand);
    for (int i = 1; i < digits.Length; i++)
        digits[i].Init(hand, handRightAxis, whichHand);

    MakeTrigger(handTarget.gameObject);
}

void MakeTrigger(GameObject targetGO) {
    targetGO.layer = 10;

    SphereCollider sc = targetGO.AddComponent<SphereCollider>();
    sc.isTrigger = true;
    sc.radius = 0.1f / targetGO.transform.localScale.z; 
    sc.center = new Vector3(0, 0, 0.1f / targetGO.transform.localScale.z);

    Rigidbody rb = targetGO.AddComponent<Rigidbody>();
    rb.isKinematic = true;

    VR_HandTarget ht = targetGO.AddComponent<VR_HandTarget>();
    ht.handScript = this;
}

void Update() {
    thumb.Update (thumbInput);
    digits[1].Update(indexInput);
    digits[2].Update(middleInput);
    digits[3].Update(ringInput);
    digits[4].Update(littleInput);

    if (collidedObject != null) 
        ProjectHand(collidedObject);

    Vector3 palmOffsetWorld = hand.transform.TransformDirection(palmOffset);
    if (grabbedRigidbody != null && grabbedRBfunc) {
        grabbedRBfunc.GrabbedUpdate(hand.transform.position + palmOffsetWorld, go);
    }
}

bool ProjectHand(Collider collidedObject) {
    Vector3 direction = handTarget.position - shoulder.position;
    RaycastHit[] hits;
    RaycastHit hit;
    bool hitFound = false;
    float minDistance = 100;

    Vector3 orgPosition = transform.position;
    transform.position = shoulder.position;

    cHandTarget.position = handTarget.position;

    hits = GetComponent<Rigidbody>().SweepTestAll(direction, direction.magnitude);
    for (int i = 0; i < hits.Length; i++) {
        if (hits[i].rigidbody == null) { 
            hitFound = true;
            if (hits[i].distance < minDistance)
                minDistance = hits[i].distance;
        }
    }

    if (hitFound) {
        Vector3 contactPoint = transform.position + (minDistance * direction.normalized);
        cHandTarget.position = contactPoint;
        transform.position = orgPosition;
        return true;
    } else {
        cHandTarget.position = handTarget.position;
        transform.position = orgPosition;
        return false;
    }
}

public void OnTargetTriggerEnter(Collider other) {
    if (other.gameObject.isStatic == true)
        collidedObject = other;
}


public void OnTargetTriggerExit(Collider other) {
    if (other = collidedObject) {
        if (ProjectHand(other) == false) {
            cHandTarget.position = handTarget.position;
            collidedObject = null;
        }
    }
}

void FixedUpdate() {
    if (grabbedObject != null) {
        bool fingersGrabbing = false;
        for (int i = 1; i < digits.Length; i++) {
            Finger finger = (Finger) digits[i];
            if (finger.hasGrabbed())
                fingersGrabbing = true;
        }

        if (!fingersGrabbing) {
            GrabbedRigidbody grb = grabbedObject.GetComponent<GrabbedRigidbody>();
            grabbedRBfunc = grb;
            grb.Unkinematize();
            Destroy(grb);
            Destroy(go);

            grabbedObject = null;
            grabbedRigidbody = null;
        }
    }

}

void OnCollisionStay (Collision otherCollider) {
    Transform thisTransform, otherTransform;

    if (grabbedObject == null) {
        bool fingersCollided = false;
        bool thumbCollided = false;

        int ncontacts = otherCollider.contacts.Length;
        for (int i = 0; i < ncontacts; i++ ) {
            thisTransform = otherCollider.contacts[i].thisCollider.transform;
            otherTransform = otherCollider.contacts[i].otherCollider.transform;
            if (thisTransform == thumb.transform || otherTransform == thumb.transform)
                thumbCollided = true;
            for (int j = 1; j < digits.Length; j++) {
                Finger finger = (Finger) digits[j];
                if (thisTransform == finger.transform || otherTransform == finger.transform) {
                    fingersCollided = true;
                }
            }
        }

        bool grabbed = false;

        if (fingersCollided && thumbCollided) {
            for (int j = 1; j < digits.Length; j++) {
                Finger finger = (Finger) digits[j];
                if (finger.input > 0 && !finger.hasGrabbed()) {
                    finger.Grab();

                    if (!grabbed) {
                        grabbedObject = otherCollider.gameObject;
                        grabbedRigidbody = otherCollider.collider.attachedRigidbody;
                        Grab (grabbedObject, grabbedRigidbody);
                        grabbed = true;
                    }
                }
            }
        }
    }
}

void Grab(GameObject obj, Rigidbody rb) {
    Vector3 palmOffsetWorld = hand.transform.TransformDirection(palmOffset);

    GrabbedRigidbody grb = obj.AddComponent<GrabbedRigidbody>();
    grabbedRBfunc = grb;

    go = new GameObject();
    go.transform.parent = hand.transform;
    go.transform.localPosition = Enscale(palmOffset, go.transform.localScale);
    if (whichHand == WhichHands.Right)
        go.transform.localRotation = Quaternion.Euler(20,270,0); 
    else
        go.transform.localRotation = Quaternion.Euler(-20,270,180); /

    grb.Kinematize(hand.transform, whichHand == WhichHands.Right, hand.transform.position + palmOffsetWorld, go);


}

private Vector3 Enscale(Vector3 position, Vector3 scale) {
    return new Vector3(
        position.x * scale.x,
        position.y * scale.y,
        position.z * scale.z);
}

public abstract class Digit {
    public abstract void Init(GameObject hand, Vector3 handRightAxis, WhichHands whichHand);
    public abstract void Update(float inputValue);
}

[System.Serializable]
public class Finger : Digit {
    public Transform transform;
    public float input;
    private float val;
    private Vector3 axis;
    private float grabAmount = 0;
    private int nPhalanges;
    private Phalanx[] phalanges;

    public override void Init(GameObject hand, Vector3 axis_in, WhichHands whichHand) {
        if (transform != null) {
            axis = axis_in;

            phalanges = new Phalanx[3];
            phalanges[0] = new Phalanx(transform);
            if (phalanges[0].transform.childCount == 1) {
                phalanges[1] = new Phalanx(phalanges[0].transform.GetChild(0).transform);
                if (phalanges[1].transform.childCount == 1) {
                    phalanges[2] = new Phalanx(phalanges[1].transform.GetChild(0).transform);
                    nPhalanges = 3;
                } else {
                    phalanges[2] = null;
                    nPhalanges = 2;
                }
            } else {
                phalanges[1] = null;
                nPhalanges = 1;
            }
        }
    }

    private float grabSpeed = 0.1f;
    public override void Update(float inputValue) {
        if (transform != null) {
            input = inputValue;
            if (grabAmount > 0)
                val = grabAmount;
            else {
                float d = input - val;
                if (d > grabSpeed)
                    val += grabSpeed;
                else if (d < -grabSpeed)
                    val -= grabSpeed;
                else
                    val += d;
            }
            Bend();

            if (grabAmount > 0) {
                if (input < grabAmount) {

                    grabAmount = 0f;
                }
            }
        }
    }

    private void Bend() {
        switch (nPhalanges) {
        case 3:
            phalanges[0].Bend(val * 45, axis);
            phalanges[1].Bend(val * 90, axis);
            phalanges[2].Bend(val * 90, axis);
            break;
        case 2:
            phalanges[0].Bend(val * 90, axis);
            phalanges[1].Bend(val * 90, axis);
            break;
        case 1:
            phalanges[0].Bend(val * 135, axis);
            break;
        }
    }

    public bool hasGrabbed() {
        return (grabAmount > 0);
    }

    public void Grab() {
        grabAmount = val;
    }
}

[System.Serializable]
public class Thumb : Digit {
    public Transform transform;
    public float input;
    private float val, grabAmount = 0;
    private Vector3 axis1, axis2;
    private Quaternion startRotation;

    public override void Init(GameObject hand, Vector3 handRightAxis, WhichHands whichHand) {
        if (transform != null) {
            axis1 = transform.InverseTransformDirection(hand.transform.up);
            if (whichHand == WhichHands.Left)
                axis2 = transform.InverseTransformDirection(-hand.transform.forward);
            else
                axis2 = transform.InverseTransformDirection(hand.transform.forward);
            startRotation = transform.localRotation;
            startRotation *= Quaternion.AngleAxis(45, -axis1);
        }
    }

    private float grabSpeed = 0.1f;
    public override void Update(float inputValue) {
        if (transform != null) {
            input = inputValue;
            if (grabAmount > 0)
                val = grabAmount;
            else {
                float d = input - val;
                if (d > grabSpeed)
                    val += grabSpeed;
                else if (d < -grabSpeed)
                    val -= grabSpeed;
                else
                    val += d;
            }
            transform.localRotation = startRotation * Quaternion.AngleAxis(val * 45, axis1);
            transform.localRotation *= Quaternion.AngleAxis(val * 30, axis2);
        }
    }
}

[System.Serializable]
public class Phalanx {
    public Transform transform;
    public Quaternion startRotation;

    public Phalanx(Transform newTransform) {
        transform = newTransform;
        startRotation = transform.localRotation;
    }

    public void Bend(float bendFactor, Vector3 axis) {
        transform.localRotation = startRotation * Quaternion.AngleAxis(bendFactor, axis);
    }
}

}