Initialer Upload neues Unity-Projekt
This commit is contained in:
@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class AugmentedObject : MonoBehaviour
|
||||
{
|
||||
public OVRInput.Controller controllerHand = OVRInput.Controller.None;
|
||||
public Transform shadow;
|
||||
bool groundShadow = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (GetComponent<GrabObject>())
|
||||
{
|
||||
GetComponent<GrabObject>().GrabbedObjectDelegate += Grab;
|
||||
GetComponent<GrabObject>().ReleasedObjectDelegate += Release;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (controllerHand != OVRInput.Controller.None)
|
||||
{
|
||||
if (OVRInput.GetUp(OVRInput.Button.One, controllerHand))
|
||||
{
|
||||
ToggleShadowType();
|
||||
}
|
||||
}
|
||||
|
||||
if (shadow)
|
||||
{
|
||||
if (groundShadow)
|
||||
{
|
||||
shadow.transform.position = new Vector3(transform.position.x, 0, transform.position.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow.transform.localPosition = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
controllerHand = grabHand;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
controllerHand = OVRInput.Controller.None;
|
||||
}
|
||||
|
||||
void ToggleShadowType()
|
||||
{
|
||||
groundShadow = !groundShadow;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8a81fa1c7362de42a8fe4abd77cd033
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,95 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class BrushController : MonoBehaviour
|
||||
{
|
||||
public PassthroughBrush brush;
|
||||
public MeshRenderer backgroundSphere;
|
||||
|
||||
IEnumerator grabRoutine;
|
||||
IEnumerator releaseRoutine;
|
||||
|
||||
void Start()
|
||||
{
|
||||
brush.controllerHand = OVRInput.Controller.None;
|
||||
|
||||
if (!brush.lineContainer)
|
||||
{
|
||||
brush.lineContainer = new GameObject("LineContainer");
|
||||
}
|
||||
|
||||
// the material on the background sphere ignores z-write, so it can overwrite other opaque objects in the scene
|
||||
// also renders after transparent objects
|
||||
backgroundSphere.material.renderQueue = 3998;
|
||||
// the selective Passthrough shader renders at 4000 and higher, to render after other transparent objects
|
||||
// (white ring and info text render after)
|
||||
backgroundSphere.transform.parent = null;
|
||||
backgroundSphere.enabled = false;
|
||||
|
||||
if (GetComponent<GrabObject>())
|
||||
{
|
||||
GetComponent<GrabObject>().GrabbedObjectDelegate += Grab;
|
||||
GetComponent<GrabObject>().ReleasedObjectDelegate += Release;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
backgroundSphere.transform.position = Camera.main.transform.position;
|
||||
}
|
||||
|
||||
public void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
brush.controllerHand = grabHand;
|
||||
brush.lineContainer.SetActive(true);
|
||||
backgroundSphere.enabled = true;
|
||||
|
||||
if (grabRoutine != null) StopCoroutine(grabRoutine);
|
||||
if (releaseRoutine != null) StopCoroutine(releaseRoutine);
|
||||
grabRoutine = FadeSphere(Color.grey, 0.25f);
|
||||
StartCoroutine(grabRoutine);
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
brush.controllerHand = OVRInput.Controller.None;
|
||||
brush.lineContainer.SetActive(false);
|
||||
|
||||
if (grabRoutine != null) StopCoroutine(grabRoutine);
|
||||
if (releaseRoutine != null) StopCoroutine(releaseRoutine);
|
||||
releaseRoutine = FadeSphere(new Color(0.5f, 0.5f, 0.5f, 0.0f), 0.25f, true);
|
||||
StartCoroutine(releaseRoutine);
|
||||
}
|
||||
|
||||
IEnumerator FadeCameraClearColor(Color newColor, float fadeTime)
|
||||
{
|
||||
float timer = 0.0f;
|
||||
Color currentColor = Camera.main.backgroundColor;
|
||||
while (timer <= fadeTime)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float normTimer = Mathf.Clamp01(timer / fadeTime);
|
||||
Camera.main.backgroundColor = Color.Lerp(currentColor, newColor, normTimer);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator FadeSphere(Color newColor, float fadeTime, bool disableOnFinish = false)
|
||||
{
|
||||
float timer = 0.0f;
|
||||
Color currentColor = backgroundSphere.material.GetColor("_Color");
|
||||
while (timer <= fadeTime)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float normTimer = Mathf.Clamp01(timer / fadeTime);
|
||||
backgroundSphere.material.SetColor("_Color", Color.Lerp(currentColor, newColor, normTimer));
|
||||
if (disableOnFinish && timer >= fadeTime)
|
||||
{
|
||||
backgroundSphere.enabled = false;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aeefa0d70cd35e248b8d4844b4624cda
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,19 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class EnableUnpremultipliedAlpha : MonoBehaviour
|
||||
{
|
||||
void Start()
|
||||
{
|
||||
// Since the alpha values for Selective Passthrough are written to the framebuffers after the color pass, we
|
||||
// need to ensure that the color values get multiplied by the alpha value during compositing. By default, this is
|
||||
// not the case, as framebuffers typically contain premultiplied color values. This step is only needed when
|
||||
// Selective Passthrough is non-binary (i.e. alpha values are neither 0 nor 1), and it doesn't work if the
|
||||
// framebuffer contains semi-transparent pixels even without Selective Passthrough, as those will have
|
||||
// premultiplied colors.
|
||||
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_ANDROID
|
||||
OVRManager.eyeFovPremultipliedAlphaModeEnabled = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d62788dcc9ea074fa279a45302d60de
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class Flashlight : MonoBehaviour
|
||||
{
|
||||
public GameObject lightVolume;
|
||||
public Light spotlight;
|
||||
public GameObject bulbGlow;
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
// ensure all the light volume quads are camera-facing
|
||||
for (int i = 0; i < lightVolume.transform.childCount; i++)
|
||||
{
|
||||
lightVolume.transform.GetChild(i).rotation = Quaternion.LookRotation(
|
||||
(lightVolume.transform.GetChild(i).position - Camera.main.transform.position).normalized);
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleFlashlight()
|
||||
{
|
||||
lightVolume.SetActive(!lightVolume.activeSelf);
|
||||
spotlight.enabled = !spotlight.enabled;
|
||||
bulbGlow.SetActive(lightVolume.activeSelf);
|
||||
}
|
||||
|
||||
public void EnableFlashlight(bool doEnable)
|
||||
{
|
||||
lightVolume.SetActive(doEnable);
|
||||
spotlight.enabled = doEnable;
|
||||
bulbGlow.SetActive(doEnable);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e39bcbc612bddc4680b924ebcda2dd9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,178 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class FlashlightController : MonoBehaviour
|
||||
{
|
||||
public Light sceneLight;
|
||||
public Transform flashlightRoot;
|
||||
Vector3 localPosition = Vector3.zero;
|
||||
Quaternion localRotation = Quaternion.identity;
|
||||
public TextMesh infoText;
|
||||
GrabObject externalController = null;
|
||||
|
||||
OVRSkeleton[] skeletons;
|
||||
OVRHand[] hands;
|
||||
int handIndex = -1;
|
||||
bool pinching = false;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
localRotation = flashlightRoot.localRotation;
|
||||
localPosition = flashlightRoot.localPosition;
|
||||
skeletons = new OVRSkeleton[2];
|
||||
hands = new OVRHand[2];
|
||||
|
||||
externalController = GetComponent<GrabObject>();
|
||||
if (externalController)
|
||||
{
|
||||
externalController.GrabbedObjectDelegate += Grab;
|
||||
externalController.ReleasedObjectDelegate += Release;
|
||||
}
|
||||
|
||||
if (GetComponent<Flashlight>())
|
||||
{
|
||||
GetComponent<Flashlight>().EnableFlashlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!externalController)
|
||||
{
|
||||
FindHands();
|
||||
bool usingControllers =
|
||||
(OVRInput.GetActiveController() == OVRInput.Controller.RTouch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.LTouch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.Touch);
|
||||
|
||||
if (!usingControllers)
|
||||
{
|
||||
if (handIndex >= 0)
|
||||
{
|
||||
AlignWithHand(hands[handIndex], skeletons[handIndex]);
|
||||
}
|
||||
|
||||
if (infoText) infoText.text = "Pinch to toggle flashlight";
|
||||
}
|
||||
else
|
||||
{
|
||||
AlignWithController(OVRInput.Controller.RTouch);
|
||||
if (OVRInput.GetUp(OVRInput.RawButton.A))
|
||||
{
|
||||
if (GetComponent<Flashlight>()) GetComponent<Flashlight>().ToggleFlashlight();
|
||||
}
|
||||
|
||||
if (infoText) infoText.text = "Press A to toggle flashlight";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindHands()
|
||||
{
|
||||
if (skeletons[0] == null || skeletons[1] == null)
|
||||
{
|
||||
OVRSkeleton[] foundSkeletons = FindObjectsOfType<OVRSkeleton>();
|
||||
if (foundSkeletons[0])
|
||||
{
|
||||
skeletons[0] = foundSkeletons[0];
|
||||
hands[0] = skeletons[0].GetComponent<OVRHand>();
|
||||
handIndex = 0;
|
||||
}
|
||||
|
||||
if (foundSkeletons[1])
|
||||
{
|
||||
skeletons[1] = foundSkeletons[1];
|
||||
hands[1] = skeletons[1].GetComponent<OVRHand>();
|
||||
handIndex = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handIndex == 0)
|
||||
{
|
||||
if (hands[1].GetFingerIsPinching(OVRHand.HandFinger.Index))
|
||||
{
|
||||
handIndex = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hands[0].GetFingerIsPinching(OVRHand.HandFinger.Index))
|
||||
{
|
||||
handIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AlignWithHand(OVRHand hand, OVRSkeleton skeleton)
|
||||
{
|
||||
if (pinching)
|
||||
{
|
||||
if (hand.GetFingerPinchStrength(OVRHand.HandFinger.Index) < 0.8f)
|
||||
{
|
||||
pinching = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hand.GetFingerIsPinching(OVRHand.HandFinger.Index))
|
||||
{
|
||||
if (GetComponent<Flashlight>()) GetComponent<Flashlight>().ToggleFlashlight();
|
||||
pinching = true;
|
||||
}
|
||||
}
|
||||
|
||||
flashlightRoot.position = skeleton.Bones[6].Transform.position;
|
||||
flashlightRoot.rotation =
|
||||
Quaternion.LookRotation(skeleton.Bones[6].Transform.position - skeleton.Bones[0].Transform.position);
|
||||
}
|
||||
|
||||
void AlignWithController(OVRInput.Controller controller)
|
||||
{
|
||||
transform.position = OVRInput.GetLocalControllerPosition(controller);
|
||||
transform.rotation = OVRInput.GetLocalControllerRotation(controller);
|
||||
|
||||
flashlightRoot.localRotation = localRotation;
|
||||
flashlightRoot.localPosition = localPosition;
|
||||
}
|
||||
|
||||
public void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
if (GetComponent<Flashlight>())
|
||||
{
|
||||
GetComponent<Flashlight>().EnableFlashlight(true);
|
||||
}
|
||||
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeLighting(new Color(0, 0, 0, 0.95f), 0.0f, 0.25f));
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (GetComponent<Flashlight>())
|
||||
{
|
||||
GetComponent<Flashlight>().EnableFlashlight(false);
|
||||
}
|
||||
|
||||
StopAllCoroutines();
|
||||
StartCoroutine(FadeLighting(Color.clear, 1.0f, 0.25f));
|
||||
}
|
||||
|
||||
IEnumerator FadeLighting(Color newColor, float sceneLightIntensity, float fadeTime)
|
||||
{
|
||||
float timer = 0.0f;
|
||||
Color currentColor = Camera.main.backgroundColor;
|
||||
float currentLight = sceneLight ? sceneLight.intensity : 0;
|
||||
while (timer <= fadeTime)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float normTimer = Mathf.Clamp01(timer / fadeTime);
|
||||
Camera.main.backgroundColor = Color.Lerp(currentColor, newColor, normTimer);
|
||||
if (sceneLight) sceneLight.intensity = Mathf.Lerp(currentLight, sceneLightIntensity, normTimer);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 728c99f7459324d4abbab04f8bfd63b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,59 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class GrabObject : MonoBehaviour
|
||||
{
|
||||
[TextArea]
|
||||
public string ObjectName;
|
||||
|
||||
[TextArea]
|
||||
public string ObjectInstructions;
|
||||
|
||||
public enum ManipulationType
|
||||
{
|
||||
Default,
|
||||
ForcedHand,
|
||||
DollyHand,
|
||||
DollyAttached,
|
||||
HorizontalScaled,
|
||||
VerticalScaled,
|
||||
Menu
|
||||
};
|
||||
|
||||
public ManipulationType objectManipulationType = ManipulationType.Default;
|
||||
public bool showLaserWhileGrabbed = false;
|
||||
|
||||
[HideInInspector]
|
||||
public Quaternion grabbedRotation = Quaternion.identity;
|
||||
|
||||
// only handle grab/release
|
||||
// other button input is handled by another script on the object
|
||||
public delegate void GrabbedObject(OVRInput.Controller grabHand);
|
||||
|
||||
public GrabbedObject GrabbedObjectDelegate;
|
||||
|
||||
public delegate void ReleasedObject();
|
||||
|
||||
public ReleasedObject ReleasedObjectDelegate;
|
||||
|
||||
public delegate void SetCursorPosition(Vector3 cursorPosition);
|
||||
|
||||
public SetCursorPosition CursorPositionDelegate;
|
||||
|
||||
public void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
grabbedRotation = transform.rotation;
|
||||
GrabbedObjectDelegate?.Invoke(grabHand);
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
ReleasedObjectDelegate?.Invoke();
|
||||
}
|
||||
|
||||
public void CursorPos(Vector3 cursorPos)
|
||||
{
|
||||
CursorPositionDelegate?.Invoke(cursorPos);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03d5bda4ee38ce448b105c32ae5d8bb0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,387 @@
|
||||
using UnityEngine;
|
||||
|
||||
/*
|
||||
* This script creates a custom mesh, specifically for hand masking in the Passthrough SDK:
|
||||
*
|
||||
* 1. Created with 2D screen space in mind, since it's 2D triangles facing the camera.
|
||||
* Not advised to use this mesh in any other way.
|
||||
* 2. The look of it should be coupled with maskMaterial, which defines the falloff of the fade and
|
||||
* also how it blends Passthrough.
|
||||
* 3. The mesh has UV.x of 0 where the hand is fully opaque, and UV.x of 1 where faded out.
|
||||
* This way, the gradient curve can be tuned in a user-friendly way (instead of in shader).
|
||||
* There may be an optimization of avoiding a texture read, if the fade is all in shader (remapping UV.x)
|
||||
* 4. Requires knowledge of the hand bones and indices
|
||||
*/
|
||||
|
||||
public class HandMeshMask : MonoBehaviour
|
||||
{
|
||||
public OVRSkeleton referenceHand;
|
||||
public Material maskMaterial;
|
||||
|
||||
// these feel like good defaults
|
||||
[Tooltip("The segments around the tip of a finger")]
|
||||
public int radialDivisions = 9;
|
||||
|
||||
[Tooltip("The fade range (finger width is 2x this)")]
|
||||
public float borderSize = 0.2f;
|
||||
|
||||
[Tooltip("Along the fingers, each knuckle scales down by this amount. " +
|
||||
"Default is zero for uniform width along entire finger.")]
|
||||
public float fingerTaper = 0.13f;
|
||||
|
||||
[Tooltip("Shorten the last bone of each finger; need this to account for bone structure " +
|
||||
"(end bone is at finger tip instead of center). Default is 1.")]
|
||||
public float fingerTipLength = 0.8f;
|
||||
|
||||
[Tooltip("Move the base of the 4 main fingers towards the tips, to avoid a visible mesh crack " +
|
||||
"between finger webbing. Default is 0.")]
|
||||
public float webOffset = 0.25f;
|
||||
|
||||
// retrieved by OVRHands at runtime
|
||||
float handScale = 1.0f;
|
||||
|
||||
// mesh information
|
||||
GameObject maskMeshObject;
|
||||
Mesh maskMesh;
|
||||
Vector3[] handVertices;
|
||||
Vector2[] handUVs;
|
||||
Color32[] handColors;
|
||||
int[] handTriangles;
|
||||
int vertCounter = 0;
|
||||
int triCounter = 0;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// this object must be at the origin for the vertex positions to work
|
||||
transform.position = Vector3.zero;
|
||||
|
||||
maskMesh = new Mesh();
|
||||
maskMeshObject = new GameObject("MeshMask");
|
||||
maskMeshObject.transform.parent = this.transform;
|
||||
maskMeshObject.transform.localPosition = Vector3.zero;
|
||||
maskMeshObject.AddComponent<MeshFilter>();
|
||||
maskMeshObject.AddComponent<MeshRenderer>();
|
||||
maskMeshObject.GetComponent<MeshFilter>().mesh = maskMesh;
|
||||
maskMeshObject.GetComponent<MeshRenderer>().material = maskMaterial;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// must have a minimum amount for math to work out
|
||||
radialDivisions = Mathf.Max(2, radialDivisions);
|
||||
|
||||
if (referenceHand)
|
||||
{
|
||||
// make sure all math accounts for hand scale
|
||||
handScale = referenceHand.GetComponent<OVRHand>().HandScale;
|
||||
CreateHandMesh();
|
||||
}
|
||||
|
||||
bool handsActive = (
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.Hands ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.LHand ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.RHand);
|
||||
maskMeshObject.SetActive(handsActive);
|
||||
}
|
||||
|
||||
// do not edit any numbers below unless you like pain
|
||||
// (it's EASY to break mesh creation and HARD to debug it)
|
||||
// if you do, you need to become intimately aware of the OVRSkeleton.Bone numbers
|
||||
void CreateHandMesh()
|
||||
{
|
||||
int knuckleVerts = 8 + (radialDivisions - 2) * 2;
|
||||
int knuckleCount = 25; // five fingers, three knuckles per finger, then 10 more as palm borders
|
||||
|
||||
int palmVerts = 12;
|
||||
int palmTriIndices = 66; // 22 triangles, each triangle has three verts
|
||||
|
||||
handVertices = new Vector3[knuckleVerts * knuckleCount + palmVerts];
|
||||
handUVs = new Vector2[handVertices.Length];
|
||||
handColors = new Color32[handVertices.Length];
|
||||
handTriangles = new int[handVertices.Length * 3 + palmTriIndices];
|
||||
// these counters are incremented during mesh construction, at each step, to ensure a valid mesh
|
||||
vertCounter = 0;
|
||||
triCounter = 0;
|
||||
|
||||
// make knuckle meshes for each finger
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int pinkyOffset = i < 4 ? 0 : 1; // pinky bone numbering is slightly different than other fingers
|
||||
int baseId = 3 + i * 3 + pinkyOffset;
|
||||
int tipId = 19 + i;
|
||||
float k0taper = 1.0f;
|
||||
float k1taper = 1.0f - fingerTaper;
|
||||
float k2taper = 1.0f - (fingerTaper * 2);
|
||||
float k3taper = 1.0f - (fingerTaper * 3);
|
||||
// thumb also gets a bit wider thickness at the base
|
||||
if (i == 0)
|
||||
{
|
||||
k0taper *= 1.2f;
|
||||
k1taper *= 1.1f;
|
||||
}
|
||||
|
||||
AddKnuckleMesh(knuckleVerts, k0taper, k1taper, referenceHand.Bones[baseId].Transform.position,
|
||||
referenceHand.Bones[baseId + 1].Transform.position);
|
||||
AddKnuckleMesh(knuckleVerts, k1taper, k2taper, referenceHand.Bones[baseId + 1].Transform.position,
|
||||
referenceHand.Bones[baseId + 2].Transform.position);
|
||||
|
||||
// for the tip of the finger, the mask needs to be a bit different:
|
||||
// the final joint of the skeleton's finger is at the tip, but
|
||||
// we need the final joint to be somewhat inside the finger tip, so the radial mask matches
|
||||
Vector3 lastKnucklePos = referenceHand.Bones[baseId + 2].Transform.position;
|
||||
Vector3 tipPos = referenceHand.Bones[tipId].Transform.position;
|
||||
Vector3 offsetTipPos = (tipPos - lastKnucklePos) * fingerTipLength + lastKnucklePos;
|
||||
AddKnuckleMesh(knuckleVerts, k2taper, k3taper, lastKnucklePos, offsetTipPos);
|
||||
}
|
||||
|
||||
// add palm mesh, which is very different than fingers
|
||||
// it uses the same concept and parameters ("fade out from center")
|
||||
AddPalmMesh(knuckleVerts);
|
||||
|
||||
// final step: combine all the arrays to make the mesh object
|
||||
maskMesh.Clear();
|
||||
maskMesh.name = "HandMeshMask";
|
||||
maskMesh.vertices = handVertices;
|
||||
maskMesh.uv = handUVs;
|
||||
maskMesh.colors32 = handColors;
|
||||
maskMesh.triangles = handTriangles;
|
||||
}
|
||||
|
||||
// a "knuckle" is a camera-facing mesh from two bones
|
||||
// the first two verts are at the bones, the rest border them like a 2D capsule
|
||||
void AddKnuckleMesh(int knuckleVerts, float point1scale, float point2scale, Vector3 point1, Vector3 point2)
|
||||
{
|
||||
int baseVertId = vertCounter;
|
||||
|
||||
Vector3 camPos = Camera.main.transform.position;
|
||||
Vector3 fwdVec = (point1 + point2) * 0.5f - camPos; // use the center of the two points
|
||||
Vector3 upVec = point2 - point1;
|
||||
Vector3.OrthoNormalize(ref fwdVec, ref upVec);
|
||||
Vector3 rightVec = Vector3.Cross(upVec, fwdVec);
|
||||
|
||||
AddVertex(point2, Vector2.zero, Color.black);
|
||||
AddVertex(point1, Vector2.zero, Color.black);
|
||||
|
||||
int fanVerts = radialDivisions + 1;
|
||||
|
||||
// rotate this vector counter clockwise, making verts along the way
|
||||
Vector3 windingVec = rightVec;
|
||||
|
||||
for (int i = 0; i < fanVerts * 2; i++)
|
||||
{
|
||||
int basePoint = (i / fanVerts) + baseVertId;
|
||||
Vector3 vertPos = handVertices[basePoint] + windingVec * borderSize * handScale *
|
||||
(basePoint != baseVertId ? point1scale : point2scale);
|
||||
AddVertex(vertPos, new Vector2(1, 0), Color.black);
|
||||
if (i != radialDivisions) // after making the first fan, don't wind for one vert
|
||||
{
|
||||
windingVec = Quaternion.AngleAxis(180.0f / radialDivisions, fwdVec) * windingVec;
|
||||
}
|
||||
}
|
||||
|
||||
// after vertices have been made, assign their indices to create triangles
|
||||
handTriangles[triCounter++] = baseVertId + 0;
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
handTriangles[triCounter++] = baseVertId + radialDivisions + 3;
|
||||
handTriangles[triCounter++] = baseVertId + 0;
|
||||
handTriangles[triCounter++] = baseVertId + radialDivisions + 3;
|
||||
handTriangles[triCounter++] = baseVertId + radialDivisions + 2;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 2;
|
||||
handTriangles[triCounter++] = baseVertId + knuckleVerts - 1;
|
||||
handTriangles[triCounter++] = baseVertId + 0;
|
||||
handTriangles[triCounter++] = baseVertId + 0;
|
||||
handTriangles[triCounter++] = baseVertId + knuckleVerts - 1;
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
|
||||
for (int i = 0; i < radialDivisions; i++)
|
||||
{
|
||||
handTriangles[triCounter++] = baseVertId + 2 + i;
|
||||
handTriangles[triCounter++] = baseVertId + 0;
|
||||
handTriangles[triCounter++] = baseVertId + 3 + i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < radialDivisions; i++)
|
||||
{
|
||||
handTriangles[triCounter++] = baseVertId + fanVerts + 2 + i;
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
handTriangles[triCounter++] = baseVertId + fanVerts + 3 + i;
|
||||
}
|
||||
}
|
||||
|
||||
// make the palm section, append it to the mesh
|
||||
// study the bone indices and locations in OVRSkeleton.Bones to understand this
|
||||
void AddPalmMesh(int knuckleVerts)
|
||||
{
|
||||
int baseVertId = vertCounter;
|
||||
// make a few vertices that aren't bone positions
|
||||
|
||||
// vertex between middle and ring fingers
|
||||
Vector3 customVert1 = (referenceHand.Bones[9].Transform.position + referenceHand.Bones[12].Transform.position) *
|
||||
0.5f;
|
||||
// vertex at "saddle" between thumb and index
|
||||
Vector3 customVert2 = (referenceHand.Bones[4].Transform.position + referenceHand.Bones[6].Transform.position) *
|
||||
0.5f;
|
||||
customVert2 = (customVert2 - referenceHand.Bones[15].Transform.position) * 0.9f +
|
||||
referenceHand.Bones[15].Transform.position;
|
||||
// vertex further up thumb, between bones 4 and 5
|
||||
Vector3 thumbPos = (referenceHand.Bones[5].Transform.position - referenceHand.Bones[4].Transform.position) *
|
||||
webOffset;
|
||||
thumbPos += referenceHand.Bones[4].Transform.position;
|
||||
// at knuckles - move the mesh down the fingers to avoid the ugly mesh gap at finger-webs
|
||||
Vector3 indexPos = (referenceHand.Bones[7].Transform.position - referenceHand.Bones[6].Transform.position) *
|
||||
webOffset;
|
||||
indexPos += referenceHand.Bones[6].Transform.position;
|
||||
Vector3 pinkyPos = (referenceHand.Bones[17].Transform.position - referenceHand.Bones[16].Transform.position) *
|
||||
webOffset;
|
||||
pinkyPos += referenceHand.Bones[16].Transform.position;
|
||||
Vector3 middlePos = (referenceHand.Bones[10].Transform.position - referenceHand.Bones[9].Transform.position) +
|
||||
(referenceHand.Bones[13].Transform.position - referenceHand.Bones[12].Transform.position);
|
||||
middlePos *= 0.5f * webOffset;
|
||||
middlePos += customVert1;
|
||||
|
||||
// first, make solid low-poly palm
|
||||
AddVertex(referenceHand.Bones[0].Transform.position, Vector2.zero, Color.black); // baseVertId + 0
|
||||
AddVertex(referenceHand.Bones[3].Transform.position, Vector2.zero, Color.black); // +1
|
||||
AddVertex(referenceHand.Bones[4].Transform.position, Vector2.zero, Color.black); // +2
|
||||
AddVertex(thumbPos, Vector2.zero, Color.black); // +3
|
||||
AddVertex(customVert2, Vector2.zero, Color.black); // +4
|
||||
AddVertex(referenceHand.Bones[6].Transform.position, Vector2.zero, Color.black); // +5
|
||||
AddVertex(customVert1, Vector2.zero, Color.black); // +6
|
||||
AddVertex(referenceHand.Bones[15].Transform.position, Vector2.zero, Color.black); // +7
|
||||
AddVertex(referenceHand.Bones[16].Transform.position, Vector2.zero, Color.black); // +8
|
||||
AddVertex(indexPos, Vector2.zero, Color.black); // +9
|
||||
AddVertex(middlePos, Vector2.zero, Color.black); // +10
|
||||
AddVertex(pinkyPos, Vector2.zero, Color.black); // +11
|
||||
|
||||
// then, assign triangles
|
||||
// unfortunately there's no elegant way to do this
|
||||
// this is literally making a low-poly mesh in code (YUCK)
|
||||
|
||||
// palm side (on left hand)
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
handTriangles[triCounter++] = baseVertId + 2;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 2;
|
||||
handTriangles[triCounter++] = baseVertId + 3;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
handTriangles[triCounter++] = baseVertId + 7;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
handTriangles[triCounter++] = baseVertId + 9;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 9;
|
||||
handTriangles[triCounter++] = baseVertId + 10;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 10;
|
||||
handTriangles[triCounter++] = baseVertId + 11;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 11;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
|
||||
// back side - to make triangulation easier, it's the same as palm side but reversing the last two verts
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 1;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
handTriangles[triCounter++] = baseVertId + 2;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 2;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
handTriangles[triCounter++] = baseVertId + 3;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
handTriangles[triCounter++] = baseVertId + 4;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId;
|
||||
handTriangles[triCounter++] = baseVertId + 7;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 9;
|
||||
handTriangles[triCounter++] = baseVertId + 5;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 10;
|
||||
handTriangles[triCounter++] = baseVertId + 9;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 11;
|
||||
handTriangles[triCounter++] = baseVertId + 10;
|
||||
|
||||
handTriangles[triCounter++] = baseVertId + 6;
|
||||
handTriangles[triCounter++] = baseVertId + 8;
|
||||
handTriangles[triCounter++] = baseVertId + 11;
|
||||
|
||||
// then, make camera-facing "fins" for the outer fade
|
||||
// fortunately these can be knuckle capsules
|
||||
// as an optimization, they could just be fins on silhouette edges
|
||||
|
||||
// add fading border to palm mesh
|
||||
// between thumb and index
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f, 1.0f, customVert2, referenceHand.Bones[6].Transform.position);
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f - fingerTaper, 1.0f, thumbPos, customVert2);
|
||||
|
||||
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f, 1.0f, indexPos, middlePos);
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f, 1.0f, middlePos, pinkyPos);
|
||||
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f, 1.0f, referenceHand.Bones[6].Transform.position, customVert1);
|
||||
AddKnuckleMesh(knuckleVerts, 1.0f, 1.0f, customVert1, referenceHand.Bones[16].Transform.position);
|
||||
|
||||
AddKnuckleMesh(knuckleVerts, 1.2f, 1.0f, referenceHand.Bones[15].Transform.position,
|
||||
referenceHand.Bones[16].Transform.position);
|
||||
AddKnuckleMesh(knuckleVerts, 1.3f, 1.2f, referenceHand.Bones[0].Transform.position,
|
||||
referenceHand.Bones[15].Transform.position);
|
||||
AddKnuckleMesh(knuckleVerts, 1.3f, 1.2f, referenceHand.Bones[0].Transform.position,
|
||||
referenceHand.Bones[3].Transform.position);
|
||||
AddKnuckleMesh(knuckleVerts, 1.3f, 1.0f, referenceHand.Bones[0].Transform.position,
|
||||
referenceHand.Bones[6].Transform.position);
|
||||
}
|
||||
|
||||
void AddVertex(Vector3 position, Vector2 uv, Color color)
|
||||
{
|
||||
handVertices[vertCounter] = position;
|
||||
// UV.x will be the distance from center
|
||||
// this way, the shader could decide how best to remap: either with a gradient texture, or directly on UV.x
|
||||
handUVs[vertCounter] = uv;
|
||||
// using vertex alpha could also be an option for fading, if the UVs are needed elsewhere
|
||||
handColors[vertCounter] = color;
|
||||
vertCounter++;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 445f7f5df38d546a58ce30e477cf0111
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,172 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// low-effort way to get a UI
|
||||
public class HandMeshUI : MonoBehaviour
|
||||
{
|
||||
public SphereCollider[] knobs;
|
||||
public TextMesh[] readouts;
|
||||
|
||||
int rightHeldKnob = -1;
|
||||
int leftHeldKnob = -1;
|
||||
|
||||
public OVRSkeleton leftHand;
|
||||
public OVRSkeleton rightHand;
|
||||
|
||||
public HandMeshMask leftMask;
|
||||
public HandMeshMask rightMask;
|
||||
|
||||
void Start()
|
||||
{
|
||||
SetSliderValue(0, rightMask.radialDivisions, false);
|
||||
SetSliderValue(1, rightMask.borderSize, false);
|
||||
SetSliderValue(2, rightMask.fingerTaper, false);
|
||||
SetSliderValue(3, rightMask.fingerTipLength, false);
|
||||
SetSliderValue(4, rightMask.webOffset, false);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
CheckForHands();
|
||||
|
||||
Vector3 RfingerPos = rightHand.Bones[20].Transform.position;
|
||||
Vector3 LfingerPos = leftHand.Bones[20].Transform.position;
|
||||
if (rightHeldKnob >= 0)
|
||||
{
|
||||
Vector3 localCursorPos = knobs[rightHeldKnob].transform.parent.InverseTransformPoint(RfingerPos);
|
||||
SetSliderValue(rightHeldKnob, Mathf.Clamp01(localCursorPos.x * 10), true);
|
||||
if (localCursorPos.z < -0.02f)
|
||||
{
|
||||
rightHeldKnob = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < knobs.Length; i++)
|
||||
{
|
||||
if (Vector3.Distance(RfingerPos, knobs[i].transform.position) <= 0.02f && leftHeldKnob != i)
|
||||
{
|
||||
rightHeldKnob = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leftHeldKnob >= 0)
|
||||
{
|
||||
Vector3 localCursorPos = knobs[leftHeldKnob].transform.parent.InverseTransformPoint(LfingerPos);
|
||||
SetSliderValue(leftHeldKnob, Mathf.Clamp01(localCursorPos.x * 10), true);
|
||||
if (localCursorPos.z < -0.02f)
|
||||
{
|
||||
leftHeldKnob = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < knobs.Length; i++)
|
||||
{
|
||||
if (Vector3.Distance(LfingerPos, knobs[i].transform.position) <= 0.02f && rightHeldKnob != i)
|
||||
{
|
||||
leftHeldKnob = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetSliderValue(int sliderID, float value, bool isNormalized)
|
||||
{
|
||||
float sliderStart = 0.0f;
|
||||
float sliderEnd = 1.0f;
|
||||
float sliderScale = 0.1f;
|
||||
string displayString = "";
|
||||
switch (sliderID)
|
||||
{
|
||||
case 0:
|
||||
sliderStart = 2;
|
||||
sliderEnd = 16;
|
||||
displayString = "{0, 0:0}";
|
||||
break;
|
||||
case 1:
|
||||
sliderStart = 0;
|
||||
sliderEnd = 0.05f;
|
||||
displayString = "{0, 0:0.000}";
|
||||
break;
|
||||
case 2:
|
||||
sliderStart = 0;
|
||||
sliderEnd = 0.3333f;
|
||||
displayString = "{0, 0:0.00}";
|
||||
break;
|
||||
case 3:
|
||||
sliderStart = 0.5f;
|
||||
sliderEnd = 1.5f;
|
||||
displayString = "{0, 0:0.00}";
|
||||
break;
|
||||
case 4:
|
||||
sliderStart = 0.0f;
|
||||
sliderEnd = 1.0f;
|
||||
displayString = "{0, 0:0.00}";
|
||||
break;
|
||||
}
|
||||
|
||||
float absoluteValue = isNormalized ? value * (sliderEnd - sliderStart) + sliderStart : value;
|
||||
float normalizedValue = isNormalized ? value : (value - sliderStart) / (sliderEnd - sliderStart);
|
||||
knobs[sliderID].transform.localPosition = Vector3.right * normalizedValue * sliderScale;
|
||||
readouts[sliderID].text = string.Format(displayString, absoluteValue);
|
||||
|
||||
// for both hands, set the properties
|
||||
switch (sliderID)
|
||||
{
|
||||
case 0:
|
||||
rightMask.radialDivisions = (int)absoluteValue;
|
||||
leftMask.radialDivisions = (int)absoluteValue;
|
||||
break;
|
||||
case 1:
|
||||
rightMask.borderSize = absoluteValue;
|
||||
leftMask.borderSize = absoluteValue;
|
||||
break;
|
||||
case 2:
|
||||
rightMask.fingerTaper = absoluteValue;
|
||||
leftMask.fingerTaper = absoluteValue;
|
||||
break;
|
||||
case 3:
|
||||
rightMask.fingerTipLength = absoluteValue;
|
||||
leftMask.fingerTipLength = absoluteValue;
|
||||
break;
|
||||
case 4:
|
||||
rightMask.webOffset = absoluteValue;
|
||||
leftMask.webOffset = absoluteValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckForHands()
|
||||
{
|
||||
bool handsActive = (
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.Hands ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.LHand ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.RHand);
|
||||
|
||||
if (transform.GetChild(0).gameObject.activeSelf)
|
||||
{
|
||||
if (!handsActive)
|
||||
{
|
||||
transform.GetChild(0).gameObject.SetActive(false);
|
||||
leftHeldKnob = -1;
|
||||
rightHeldKnob = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handsActive)
|
||||
{
|
||||
transform.GetChild(0).gameObject.SetActive(true);
|
||||
transform.position = (rightHand.Bones[20].Transform.position + rightHand.Bones[20].Transform.position) *
|
||||
0.5f;
|
||||
transform.position += (transform.position - Camera.main.transform.position).normalized * 0.1f;
|
||||
transform.rotation = Quaternion.LookRotation(new Vector3(Camera.main.transform.forward.x, 0,
|
||||
Camera.main.transform.forward.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff682cd7f7129418aa8ec97bc03d5c8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,380 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// grabs any object that has a collider
|
||||
// adding a GrabObject script to the object offers more functionality
|
||||
public class ObjectManipulator : MonoBehaviour
|
||||
{
|
||||
OVRInput.Controller controller = OVRInput.Controller.RTouch;
|
||||
GameObject hoverObject = null;
|
||||
|
||||
GameObject grabObject = null;
|
||||
|
||||
// all-purpose timer to use for blending after object is grabbed/released
|
||||
float grabTime = 0.0f;
|
||||
|
||||
// the grabbed object's transform relative to the controller
|
||||
Vector3 localGrabOffset = Vector3.zero;
|
||||
|
||||
Quaternion localGrabRotation = Quaternion.identity;
|
||||
|
||||
// the camera and grabbing hand's world position when grabbing
|
||||
Vector3 camGrabPosition = Vector3.zero;
|
||||
Quaternion camGrabRotation = Quaternion.identity;
|
||||
Vector3 handGrabPosition = Vector3.zero;
|
||||
Quaternion handGrabRotation = Quaternion.identity;
|
||||
Vector3 cursorPosition = Vector3.zero;
|
||||
float rotationOffset = 0.0f;
|
||||
public LineRenderer laser;
|
||||
public Transform objectInfo;
|
||||
public TextMesh objectNameLabel;
|
||||
public TextMesh objectInstructionsLabel;
|
||||
public Image objectInfoBG;
|
||||
|
||||
// align these in front of the user's view when starting
|
||||
public GameObject demoObjects;
|
||||
|
||||
// only used in this script for fading in from black
|
||||
public OVRPassthroughLayer passthrough;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (passthrough)
|
||||
{
|
||||
passthrough.colorMapEditorBrightness = -1;
|
||||
passthrough.colorMapEditorContrast = -1;
|
||||
}
|
||||
|
||||
StartCoroutine(StartDemo());
|
||||
// render these UI elements after the passthrough "hole punch" shader and the brush ring
|
||||
if (objectNameLabel) objectNameLabel.font.material.renderQueue = 4600;
|
||||
if (objectInstructionsLabel) objectInstructionsLabel.font.material.renderQueue = 4600;
|
||||
if (objectInfoBG) objectInfoBG.materialForRendering.renderQueue = 4599;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Vector3 controllerPos = OVRInput.GetLocalControllerPosition(controller);
|
||||
Quaternion controllerRot = OVRInput.GetLocalControllerRotation(controller);
|
||||
|
||||
FindHoverObject(controllerPos, controllerRot);
|
||||
|
||||
if (hoverObject)
|
||||
{
|
||||
if (OVRInput.GetDown(OVRInput.Button.PrimaryHandTrigger, controller))
|
||||
{
|
||||
// grabbing
|
||||
grabObject = hoverObject;
|
||||
GrabHoverObject(grabObject, controllerPos, controllerRot);
|
||||
}
|
||||
}
|
||||
|
||||
if (grabObject)
|
||||
{
|
||||
grabTime += Time.deltaTime * 5;
|
||||
grabTime = Mathf.Clamp01(grabTime);
|
||||
ManipulateObject(grabObject, controllerPos, controllerRot);
|
||||
|
||||
if (!OVRInput.Get(OVRInput.Button.PrimaryHandTrigger, controller))
|
||||
{
|
||||
ReleaseObject();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grabTime -= Time.deltaTime * 5;
|
||||
grabTime = Mathf.Clamp01(grabTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GrabHoverObject(GameObject grbObj, Vector3 controllerPos, Quaternion controllerRot)
|
||||
{
|
||||
localGrabOffset = Quaternion.Inverse(controllerRot) * (grabObject.transform.position - controllerPos);
|
||||
localGrabRotation = Quaternion.Inverse(controllerRot) * grabObject.transform.rotation;
|
||||
rotationOffset = 0.0f;
|
||||
if (grabObject.GetComponent<GrabObject>())
|
||||
{
|
||||
grabObject.GetComponent<GrabObject>().Grab(controller);
|
||||
grabObject.GetComponent<GrabObject>().grabbedRotation = grabObject.transform.rotation;
|
||||
AssignInstructions(grabObject.GetComponent<GrabObject>());
|
||||
}
|
||||
|
||||
handGrabPosition = controllerPos;
|
||||
handGrabRotation = controllerRot;
|
||||
camGrabPosition = Camera.main.transform.position;
|
||||
camGrabRotation = Camera.main.transform.rotation;
|
||||
}
|
||||
|
||||
void ReleaseObject()
|
||||
{
|
||||
if (grabObject.GetComponent<GrabObject>())
|
||||
{
|
||||
if (grabObject.GetComponent<GrabObject>().objectManipulationType == GrabObject.ManipulationType.Menu)
|
||||
{
|
||||
// restore the menu it to its first resting place
|
||||
grabObject.transform.position = handGrabPosition + handGrabRotation * localGrabOffset;
|
||||
grabObject.transform.rotation = handGrabRotation * localGrabRotation;
|
||||
}
|
||||
|
||||
grabObject.GetComponent<GrabObject>().Release();
|
||||
}
|
||||
|
||||
grabObject = null;
|
||||
}
|
||||
|
||||
// wait for systems to get situated, then spawn the objects in front of them
|
||||
IEnumerator StartDemo()
|
||||
{
|
||||
demoObjects.SetActive(false);
|
||||
// fade from black
|
||||
float timer = 0.0f;
|
||||
float fadeTime = 1.0f;
|
||||
while (timer <= fadeTime)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float normTimer = Mathf.Clamp01(timer / fadeTime);
|
||||
if (passthrough)
|
||||
{
|
||||
passthrough.colorMapEditorBrightness = Mathf.Lerp(-1.0f, 0.0f, normTimer);
|
||||
passthrough.colorMapEditorContrast = Mathf.Lerp(-1.0f, 0.0f, normTimer);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
//yield return new WaitForSeconds(1.0f);
|
||||
demoObjects.SetActive(true);
|
||||
Vector3 objFwd = new Vector3(Camera.main.transform.forward.x, 0, Camera.main.transform.forward.z).normalized;
|
||||
demoObjects.transform.position = Camera.main.transform.position + objFwd;
|
||||
demoObjects.transform.rotation = Quaternion.LookRotation(objFwd);
|
||||
}
|
||||
|
||||
void FindHoverObject(Vector3 controllerPos, Quaternion controllerRot)
|
||||
{
|
||||
RaycastHit[] objectsHit = Physics.RaycastAll(controllerPos, controllerRot * Vector3.forward);
|
||||
float closestObject = Mathf.Infinity;
|
||||
float rayDistance = 2.0f;
|
||||
bool showLaser = true;
|
||||
Vector3 labelPosition = Vector3.zero;
|
||||
foreach (RaycastHit hit in objectsHit)
|
||||
{
|
||||
float thisHitDistance = Vector3.Distance(hit.point, controllerPos);
|
||||
if (thisHitDistance < closestObject)
|
||||
{
|
||||
hoverObject = hit.collider.gameObject;
|
||||
closestObject = thisHitDistance;
|
||||
rayDistance = grabObject ? thisHitDistance : thisHitDistance - 0.1f;
|
||||
labelPosition = hit.point;
|
||||
}
|
||||
}
|
||||
|
||||
if (objectsHit.Length == 0)
|
||||
{
|
||||
hoverObject = null;
|
||||
}
|
||||
|
||||
// if intersecting with an object, grab it
|
||||
Collider[] hitColliders = Physics.OverlapSphere(controllerPos, 0.05f);
|
||||
foreach (var hitCollider in hitColliders)
|
||||
{
|
||||
// use the last object, if there are multiple hits.
|
||||
// If objects overlap, this would require improvements.
|
||||
hoverObject = hitCollider.gameObject;
|
||||
showLaser = false;
|
||||
labelPosition = hitCollider.ClosestPoint(controllerPos);
|
||||
labelPosition += (Camera.main.transform.position - labelPosition).normalized * 0.05f;
|
||||
}
|
||||
|
||||
if (objectInfo && objectInstructionsLabel)
|
||||
{
|
||||
bool showObjectInfo = hoverObject || grabObject;
|
||||
objectInfo.gameObject.SetActive(showObjectInfo);
|
||||
Vector3 toObj = labelPosition - Camera.main.transform.position;
|
||||
if (hoverObject && !grabObject)
|
||||
{
|
||||
Vector3 targetPos = labelPosition - toObj.normalized * 0.05f;
|
||||
objectInfo.position = Vector3.Lerp(targetPos, objectInfo.position, grabTime);
|
||||
objectInfo.rotation = Quaternion.LookRotation(toObj);
|
||||
//objectInstructionsLabel.gameObject.SetActive(false);
|
||||
objectInfo.localScale = Vector3.one * toObj.magnitude * 2.0f;
|
||||
if (hoverObject.GetComponent<GrabObject>())
|
||||
{
|
||||
AssignInstructions(hoverObject.GetComponent<GrabObject>());
|
||||
}
|
||||
}
|
||||
else if (grabObject)
|
||||
{
|
||||
Vector3 targetPos = controllerPos + (Camera.main.transform.position - controllerPos).normalized * 0.1f;
|
||||
objectInfo.position = Vector3.Lerp(objectInfo.position, targetPos, grabTime);
|
||||
;
|
||||
objectInfo.rotation = Quaternion.LookRotation(objectInfo.position - Camera.main.transform.position);
|
||||
//objectInstructionsLabel.gameObject.SetActive(true);
|
||||
objectInfo.localScale = Vector3.one;
|
||||
if (grabObject.GetComponent<GrabObject>())
|
||||
showLaser = grabObject.GetComponent<GrabObject>().showLaserWhileGrabbed;
|
||||
}
|
||||
}
|
||||
|
||||
// show/hide laser pointer
|
||||
if (laser)
|
||||
{
|
||||
laser.positionCount = 2;
|
||||
Vector3 pos1 = controllerPos + controllerRot * (Vector3.forward * 0.05f);
|
||||
cursorPosition = controllerPos + controllerRot * (Vector3.forward * rayDistance);
|
||||
laser.SetPosition(0, pos1);
|
||||
laser.SetPosition(1, cursorPosition);
|
||||
laser.enabled = (showLaser);
|
||||
if (grabObject && grabObject.GetComponent<GrabObject>())
|
||||
{
|
||||
grabObject.GetComponent<GrabObject>().CursorPos(cursorPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the heavy lifting code for moving objects
|
||||
void ManipulateObject(GameObject obj, Vector3 controllerPos, Quaternion controllerRot)
|
||||
{
|
||||
bool useDefaultManipulation = true;
|
||||
Vector2 thumbstick = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, controller);
|
||||
|
||||
if (obj.GetComponent<GrabObject>())
|
||||
{
|
||||
useDefaultManipulation = false;
|
||||
switch (obj.GetComponent<GrabObject>().objectManipulationType)
|
||||
{
|
||||
case GrabObject.ManipulationType.Default:
|
||||
useDefaultManipulation = true;
|
||||
break;
|
||||
case GrabObject.ManipulationType.ForcedHand:
|
||||
obj.transform.position = Vector3.Lerp(obj.transform.position, controllerPos, grabTime);
|
||||
obj.transform.rotation = Quaternion.Lerp(obj.transform.rotation, controllerRot, grabTime);
|
||||
break;
|
||||
case GrabObject.ManipulationType.DollyHand:
|
||||
float targetDist = localGrabOffset.z + thumbstick.y * 0.01f;
|
||||
targetDist = Mathf.Clamp(targetDist, 0.1f, 2.0f);
|
||||
localGrabOffset = Vector3.forward * targetDist;
|
||||
obj.transform.position = Vector3.Lerp(obj.transform.position,
|
||||
controllerPos + controllerRot * localGrabOffset, grabTime);
|
||||
obj.transform.rotation = Quaternion.Lerp(obj.transform.rotation, controllerRot, grabTime);
|
||||
break;
|
||||
case GrabObject.ManipulationType.DollyAttached:
|
||||
obj.transform.position = controllerPos + controllerRot * localGrabOffset;
|
||||
obj.transform.rotation = controllerRot * localGrabRotation;
|
||||
ClampGrabOffset(ref localGrabOffset, thumbstick.y);
|
||||
break;
|
||||
case GrabObject.ManipulationType.HorizontalScaled:
|
||||
obj.transform.position = controllerPos + controllerRot * localGrabOffset;
|
||||
if (!OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger, controller))
|
||||
{
|
||||
obj.transform.localScale = ClampScale(obj.transform.localScale, thumbstick);
|
||||
}
|
||||
else
|
||||
{
|
||||
rotationOffset -= thumbstick.x;
|
||||
ClampGrabOffset(ref localGrabOffset, thumbstick.y);
|
||||
}
|
||||
|
||||
Vector3 newFwd = obj.GetComponent<GrabObject>().grabbedRotation * Vector3.forward;
|
||||
newFwd = new Vector3(newFwd.x, 0, newFwd.z);
|
||||
obj.transform.rotation = Quaternion.Euler(0, rotationOffset, 0) * Quaternion.LookRotation(newFwd);
|
||||
break;
|
||||
case GrabObject.ManipulationType.VerticalScaled:
|
||||
obj.transform.position = controllerPos + controllerRot * localGrabOffset;
|
||||
if (!OVRInput.Get(OVRInput.Button.PrimaryIndexTrigger, controller))
|
||||
{
|
||||
obj.transform.localScale = ClampScale(obj.transform.localScale, thumbstick);
|
||||
}
|
||||
else
|
||||
{
|
||||
rotationOffset -= thumbstick.x;
|
||||
ClampGrabOffset(ref localGrabOffset, thumbstick.y);
|
||||
}
|
||||
|
||||
Vector3 newUp = obj.GetComponent<GrabObject>().grabbedRotation * Vector3.up;
|
||||
newUp = new Vector3(newUp.x, 0, newUp.z);
|
||||
obj.transform.rotation =
|
||||
Quaternion.LookRotation(Vector3.up, Quaternion.Euler(0, rotationOffset, 0) * newUp);
|
||||
break;
|
||||
case GrabObject.ManipulationType.Menu:
|
||||
Vector3 targetPos = handGrabPosition + (handGrabRotation * Vector3.forward * 0.4f);
|
||||
Quaternion targetRotation = Quaternion.LookRotation(targetPos - camGrabPosition);
|
||||
obj.transform.position = Vector3.Lerp(obj.transform.position, targetPos, grabTime);
|
||||
obj.transform.rotation = Quaternion.Lerp(obj.transform.rotation, targetRotation, grabTime);
|
||||
break;
|
||||
default:
|
||||
useDefaultManipulation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (useDefaultManipulation)
|
||||
{
|
||||
obj.transform.position = controllerPos + controllerRot * localGrabOffset;
|
||||
obj.transform.Rotate(Vector3.up * -thumbstick.x);
|
||||
ClampGrabOffset(ref localGrabOffset, thumbstick.y);
|
||||
}
|
||||
}
|
||||
|
||||
void ClampGrabOffset(ref Vector3 localOffset, float thumbY)
|
||||
{
|
||||
Vector3 projectedGrabOffset = localOffset + Vector3.forward * thumbY * 0.01f;
|
||||
if (projectedGrabOffset.z > 0.1f)
|
||||
{
|
||||
localOffset = projectedGrabOffset;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 ClampScale(Vector3 localScale, Vector2 thumb)
|
||||
{
|
||||
float newXscale = localScale.x + thumb.x * 0.01f;
|
||||
if (newXscale <= 0.1f) newXscale = 0.1f;
|
||||
float newZscale = localScale.z + thumb.y * 0.01f;
|
||||
if (newZscale <= 0.1f) newZscale = 0.1f;
|
||||
return new Vector3(newXscale, 0.0f, newZscale);
|
||||
}
|
||||
|
||||
void CheckForDominantHand()
|
||||
{
|
||||
// don't switch if hovering or grabbing
|
||||
if (hoverObject || grabObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller == OVRInput.Controller.RTouch)
|
||||
{
|
||||
if (OVRInput.Get(OVRInput.RawButton.LHandTrigger))
|
||||
{
|
||||
controller = OVRInput.Controller.LTouch;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OVRInput.Get(OVRInput.RawButton.RHandTrigger))
|
||||
{
|
||||
controller = OVRInput.Controller.RTouch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssignInstructions(GrabObject targetObject)
|
||||
{
|
||||
if (objectNameLabel)
|
||||
{
|
||||
objectNameLabel.text = targetObject.ObjectName;
|
||||
}
|
||||
|
||||
if (objectInstructionsLabel)
|
||||
{
|
||||
if (grabObject)
|
||||
{
|
||||
objectInstructionsLabel.text = targetObject.ObjectInstructions;
|
||||
}
|
||||
else
|
||||
{
|
||||
objectInstructionsLabel.text = "Grip Trigger to grab";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 891a5db22dd82b84195539b740608c98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class OverlayPassthrough : MonoBehaviour
|
||||
{
|
||||
OVRPassthroughLayer passthroughLayer;
|
||||
|
||||
void Start()
|
||||
{
|
||||
GameObject ovrCameraRig = GameObject.Find("OVRCameraRig");
|
||||
if (ovrCameraRig == null)
|
||||
{
|
||||
Debug.LogError("Scene does not contain an OVRCameraRig");
|
||||
return;
|
||||
}
|
||||
|
||||
passthroughLayer = ovrCameraRig.GetComponent<OVRPassthroughLayer>();
|
||||
if (passthroughLayer == null)
|
||||
{
|
||||
Debug.LogError("OVRCameraRig does not contain an OVRPassthroughLayer component");
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (OVRInput.GetDown(OVRInput.Button.One, OVRInput.Controller.RTouch))
|
||||
{
|
||||
passthroughLayer.hidden = !passthroughLayer.hidden;
|
||||
}
|
||||
|
||||
float thumbstickX = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick, OVRInput.Controller.RTouch).x;
|
||||
passthroughLayer.textureOpacity = thumbstickX * 0.5f + 0.5f;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f68e69bcc45cf43abc834cb9713e41
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,110 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PassthroughBrush : MonoBehaviour
|
||||
{
|
||||
public OVRInput.Controller controllerHand = OVRInput.Controller.None;
|
||||
public GameObject lineSegmentPrefab;
|
||||
public GameObject lineContainer;
|
||||
public bool forceActive = true;
|
||||
LineRenderer currentLineSegment = null;
|
||||
List<Vector3> inkPositions = new List<Vector3>();
|
||||
float minInkDist = 0.01f;
|
||||
float strokeWidth = 0.1f;
|
||||
float strokeLength = 0.0f;
|
||||
|
||||
public enum BrushState
|
||||
{
|
||||
Idle,
|
||||
Inking
|
||||
};
|
||||
|
||||
BrushState brushStatus = BrushState.Idle;
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
brushStatus = BrushState.Idle;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
// face camera
|
||||
transform.rotation = Quaternion.LookRotation(transform.position - Camera.main.transform.position);
|
||||
|
||||
if (!(controllerHand == OVRInput.Controller.LTouch || controllerHand == OVRInput.Controller.RTouch))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 tipPosition = transform.position;
|
||||
switch (brushStatus)
|
||||
{
|
||||
case BrushState.Idle:
|
||||
if (OVRInput.GetUp(OVRInput.Button.One, controllerHand))
|
||||
{
|
||||
UndoInkLine();
|
||||
}
|
||||
|
||||
if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, controllerHand))
|
||||
{
|
||||
StartLine(tipPosition);
|
||||
brushStatus = BrushState.Inking;
|
||||
}
|
||||
|
||||
break;
|
||||
case BrushState.Inking:
|
||||
// every frame, draw line
|
||||
UpdateLine(tipPosition);
|
||||
if (OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, controllerHand))
|
||||
{
|
||||
brushStatus = BrushState.Idle;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StartLine(Vector3 inkPos)
|
||||
{
|
||||
GameObject newLine = Instantiate(lineSegmentPrefab, inkPos, Quaternion.identity);
|
||||
currentLineSegment = newLine.GetComponent<LineRenderer>();
|
||||
currentLineSegment.positionCount = 1;
|
||||
currentLineSegment.SetPosition(0, inkPos);
|
||||
strokeWidth = currentLineSegment.startWidth;
|
||||
strokeLength = 0.0f;
|
||||
inkPositions.Clear();
|
||||
inkPositions.Add(inkPos);
|
||||
newLine.transform.parent = lineContainer.transform;
|
||||
}
|
||||
|
||||
void UpdateLine(Vector3 inkPos)
|
||||
{
|
||||
float segmentLength = (inkPos - inkPositions[inkPositions.Count - 1]).magnitude;
|
||||
if (segmentLength >= minInkDist)
|
||||
{
|
||||
inkPositions.Add(inkPos);
|
||||
currentLineSegment.positionCount = inkPositions.Count;
|
||||
currentLineSegment.SetPositions(inkPositions.ToArray());
|
||||
strokeLength += segmentLength;
|
||||
// passing the line length to the shader ensures that the tail/end fades are consistent width
|
||||
currentLineSegment.material.SetFloat("_LineLength", strokeLength / strokeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearLines()
|
||||
{
|
||||
for (int i = 0; i < lineContainer.transform.childCount; i++)
|
||||
{
|
||||
Destroy(lineContainer.transform.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void UndoInkLine()
|
||||
{
|
||||
if (lineContainer.transform.childCount >= 1)
|
||||
{
|
||||
Destroy(lineContainer.transform.GetChild(lineContainer.transform.childCount - 1).gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c316363f4d5d264fb22b6342654e580
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,40 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PassthroughController : MonoBehaviour
|
||||
{
|
||||
OVRPassthroughLayer passthroughLayer;
|
||||
|
||||
void Start()
|
||||
{
|
||||
GameObject ovrCameraRig = GameObject.Find("OVRCameraRig");
|
||||
if (ovrCameraRig == null)
|
||||
{
|
||||
Debug.LogError("Scene does not contain an OVRCameraRig");
|
||||
return;
|
||||
}
|
||||
|
||||
passthroughLayer = ovrCameraRig.GetComponent<OVRPassthroughLayer>();
|
||||
if (passthroughLayer == null)
|
||||
{
|
||||
Debug.LogError("OVRCameraRig does not contain an OVRPassthroughLayer component");
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
float colorHSV = Time.time * 0.1f;
|
||||
colorHSV %= 1; // make sure value is normalized (0-1) so Color.HSVToRGB functions correctly
|
||||
Color edgeColor = Color.HSVToRGB(colorHSV, 1, 1);
|
||||
passthroughLayer.edgeColor = edgeColor;
|
||||
|
||||
float contrastRange = Mathf.Sin(Time.time); // returns a value -1...1, ideal range for contrast
|
||||
passthroughLayer.SetColorMapControls(contrastRange);
|
||||
|
||||
transform.position = Camera.main.transform.position;
|
||||
transform.rotation =
|
||||
Quaternion.LookRotation(new Vector3(Camera.main.transform.forward.x, 0.0f, Camera.main.transform.forward.z)
|
||||
.normalized);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cc9a7c09b49c544384157f418d4758f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,56 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PassthroughProjectionSurface : MonoBehaviour
|
||||
{
|
||||
private OVRPassthroughLayer passthroughLayer;
|
||||
public MeshFilter projectionObject;
|
||||
MeshRenderer quadOutline;
|
||||
|
||||
void Start()
|
||||
{
|
||||
GameObject ovrCameraRig = GameObject.Find("OVRCameraRig");
|
||||
if (ovrCameraRig == null)
|
||||
{
|
||||
Debug.LogError("Scene does not contain an OVRCameraRig");
|
||||
return;
|
||||
}
|
||||
|
||||
passthroughLayer = ovrCameraRig.GetComponent<OVRPassthroughLayer>();
|
||||
if (passthroughLayer == null)
|
||||
{
|
||||
Debug.LogError("OVRCameraRig does not contain an OVRPassthroughLayer component");
|
||||
}
|
||||
|
||||
passthroughLayer.AddSurfaceGeometry(projectionObject.gameObject, true);
|
||||
|
||||
// The MeshRenderer component renders the quad as a blue outline
|
||||
// we only use this when Passthrough isn't visible
|
||||
quadOutline = projectionObject.GetComponent<MeshRenderer>();
|
||||
quadOutline.enabled = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Hide object when A button is held, show it again when button is released, move it while held.
|
||||
if (OVRInput.GetDown(OVRInput.Button.One))
|
||||
{
|
||||
passthroughLayer.RemoveSurfaceGeometry(projectionObject.gameObject);
|
||||
quadOutline.enabled = true;
|
||||
}
|
||||
|
||||
if (OVRInput.Get(OVRInput.Button.One))
|
||||
{
|
||||
OVRInput.Controller controllingHand = OVRInput.Controller.RTouch;
|
||||
transform.position = OVRInput.GetLocalControllerPosition(controllingHand);
|
||||
transform.rotation = OVRInput.GetLocalControllerRotation(controllingHand);
|
||||
}
|
||||
|
||||
if (OVRInput.GetUp(OVRInput.Button.One))
|
||||
{
|
||||
passthroughLayer.AddSurfaceGeometry(projectionObject.gameObject);
|
||||
quadOutline.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ca33c7ea21cf1742b1ce795f9e3cf30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,275 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class PassthroughStyler : MonoBehaviour
|
||||
{
|
||||
private const float FadeDuration = 0.2f;
|
||||
|
||||
[SerializeField]
|
||||
private OVRInput.Controller _controllerHand = OVRInput.Controller.None;
|
||||
|
||||
[SerializeField]
|
||||
private OVRPassthroughLayer _passthroughLayer;
|
||||
|
||||
[SerializeField]
|
||||
private RectTransform _colorWheel;
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D _colorTexture;
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D _colorLutTexture;
|
||||
|
||||
[SerializeField]
|
||||
private CanvasGroup _mainCanvas;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] _compactObjects;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject[] _objectsToHideForColorPassthrough;
|
||||
|
||||
private Vector3 _cursorPosition = Vector3.zero;
|
||||
private bool _settingColor = false;
|
||||
private Color _savedColor = Color.white;
|
||||
private float _savedBrightness = 0.0f;
|
||||
private float _savedContrast = 0.0f;
|
||||
private float _savedSaturation = 0.0f;
|
||||
|
||||
private OVRPassthroughLayer.ColorMapEditorType _currentStyle =
|
||||
OVRPassthroughLayer.ColorMapEditorType.ColorAdjustment;
|
||||
|
||||
private float _savedBlend = 1;
|
||||
private OVRPassthroughColorLut _passthroughColorLut;
|
||||
private IEnumerator _fade;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (TryGetComponent<GrabObject>(out var grabOject))
|
||||
{
|
||||
grabOject.GrabbedObjectDelegate += Grab;
|
||||
grabOject.ReleasedObjectDelegate += Release;
|
||||
grabOject.CursorPositionDelegate += Cursor;
|
||||
}
|
||||
|
||||
_savedColor = new Color(1, 1, 1, 0);
|
||||
ShowFullMenu(false);
|
||||
_mainCanvas.interactable = false;
|
||||
_passthroughColorLut = new OVRPassthroughColorLut(_colorLutTexture);
|
||||
|
||||
if (!OVRManager.GetPassthroughCapabilities().SupportsColorPassthrough)
|
||||
{
|
||||
if (_objectsToHideForColorPassthrough != null)
|
||||
{
|
||||
for (int i = 0; i < _objectsToHideForColorPassthrough.Length; i++)
|
||||
{
|
||||
_objectsToHideForColorPassthrough[i].SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_controllerHand == OVRInput.Controller.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_settingColor)
|
||||
{
|
||||
GetColorFromWheel();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void OnBrightnessChanged(float newValue)
|
||||
{
|
||||
_savedBrightness = newValue;
|
||||
UpdateBrighnessContrastSaturation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void OnContrastChanged(float newValue)
|
||||
{
|
||||
_savedContrast = newValue;
|
||||
UpdateBrighnessContrastSaturation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void OnSaturationChanged(float newValue)
|
||||
{
|
||||
_savedSaturation = newValue;
|
||||
UpdateBrighnessContrastSaturation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void OnAlphaChanged(float newValue)
|
||||
{
|
||||
_savedColor = new Color(_savedColor.r, _savedColor.g, _savedColor.b, newValue);
|
||||
_passthroughLayer.edgeColor = _savedColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void OnBlendChange(float newValue)
|
||||
{
|
||||
_savedBlend = newValue;
|
||||
_passthroughLayer.SetColorLut(_passthroughColorLut, _savedBlend);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void DoColorDrag(bool doDrag)
|
||||
{
|
||||
_settingColor = doDrag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void SetPassthroughStyleToColorAdjustment(bool isOn)
|
||||
{
|
||||
if (isOn)
|
||||
{
|
||||
SetPassthroughStyle(OVRPassthroughLayer.ColorMapEditorType.ColorAdjustment);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from editor
|
||||
/// </summary>
|
||||
public void SetPassthroughStyleToColorLut(bool isOn)
|
||||
{
|
||||
if (isOn)
|
||||
{
|
||||
SetPassthroughStyle(OVRPassthroughLayer.ColorMapEditorType.ColorLut);
|
||||
}
|
||||
}
|
||||
|
||||
private void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
_controllerHand = grabHand;
|
||||
ShowFullMenu(true);
|
||||
if (_mainCanvas) _mainCanvas.interactable = true;
|
||||
|
||||
if (_fade != null) StopCoroutine(_fade);
|
||||
_fade = FadeToCurrentStyle(FadeDuration);
|
||||
StartCoroutine(_fade);
|
||||
}
|
||||
|
||||
private void Release()
|
||||
{
|
||||
_controllerHand = OVRInput.Controller.None;
|
||||
ShowFullMenu(false);
|
||||
if (_mainCanvas) _mainCanvas.interactable = false;
|
||||
|
||||
if (_fade != null) StopCoroutine(_fade);
|
||||
_fade = FadeToDefaultPassthrough(FadeDuration);
|
||||
StartCoroutine(_fade);
|
||||
}
|
||||
|
||||
private IEnumerator FadeToCurrentStyle(float fadeTime)
|
||||
{
|
||||
_passthroughLayer.edgeRenderingEnabled = true;
|
||||
yield return FadeTo(1, fadeTime);
|
||||
}
|
||||
|
||||
private IEnumerator FadeToDefaultPassthrough(float fadeTime)
|
||||
{
|
||||
yield return FadeTo(0, fadeTime);
|
||||
_passthroughLayer.edgeRenderingEnabled = false;
|
||||
}
|
||||
|
||||
private IEnumerator FadeTo(float styleValueMultiplier, float duration)
|
||||
{
|
||||
float timer = 0.0f;
|
||||
float brightness = _passthroughLayer.colorMapEditorBrightness;
|
||||
float contrast = _passthroughLayer.colorMapEditorContrast;
|
||||
float saturation = _passthroughLayer.colorMapEditorSaturation;
|
||||
Color edgeCol = _passthroughLayer.edgeColor;
|
||||
float blend = _savedBlend;
|
||||
while (timer <= duration)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
float normTimer = Mathf.Clamp01(timer / duration);
|
||||
if (_currentStyle == OVRPassthroughLayer.ColorMapEditorType.ColorLut)
|
||||
{
|
||||
_passthroughLayer.SetColorLut(_passthroughColorLut,
|
||||
Mathf.Lerp(blend, _savedBlend * styleValueMultiplier, normTimer));
|
||||
}
|
||||
else
|
||||
{
|
||||
_passthroughLayer.SetBrightnessContrastSaturation(
|
||||
Mathf.Lerp(brightness, _savedBrightness * styleValueMultiplier, normTimer),
|
||||
Mathf.Lerp(contrast, _savedContrast * styleValueMultiplier, normTimer),
|
||||
Mathf.Lerp(saturation, _savedSaturation * styleValueMultiplier, normTimer));
|
||||
}
|
||||
|
||||
_passthroughLayer.edgeColor = Color.Lerp(edgeCol,
|
||||
new Color(_savedColor.r, _savedColor.g, _savedColor.b, _savedColor.a * styleValueMultiplier),
|
||||
normTimer);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBrighnessContrastSaturation()
|
||||
{
|
||||
_passthroughLayer.SetBrightnessContrastSaturation(_savedBrightness, _savedContrast, _savedSaturation);
|
||||
}
|
||||
|
||||
private void ShowFullMenu(bool doShow)
|
||||
{
|
||||
foreach (GameObject go in _compactObjects)
|
||||
{
|
||||
go.SetActive(doShow);
|
||||
}
|
||||
}
|
||||
|
||||
private void Cursor(Vector3 cP)
|
||||
{
|
||||
_cursorPosition = cP;
|
||||
}
|
||||
|
||||
private void GetColorFromWheel()
|
||||
{
|
||||
// convert cursor world position to UV
|
||||
var localPos = _colorWheel.transform.InverseTransformPoint(_cursorPosition);
|
||||
var toImg = new Vector2(localPos.x / _colorWheel.sizeDelta.x + 0.5f,
|
||||
localPos.y / _colorWheel.sizeDelta.y + 0.5f);
|
||||
Debug.Log("Sanctuary: " + toImg.x.ToString() + ", " + toImg.y.ToString());
|
||||
Color sampledColor = Color.black;
|
||||
if (toImg.x < 1.0 && toImg.x > 0.0f && toImg.y < 1.0 && toImg.y > 0.0f)
|
||||
{
|
||||
int Upos = Mathf.RoundToInt(toImg.x * _colorTexture.width);
|
||||
int Vpos = Mathf.RoundToInt(toImg.y * _colorTexture.height);
|
||||
sampledColor = _colorTexture.GetPixel(Upos, Vpos);
|
||||
}
|
||||
|
||||
_savedColor = new Color(sampledColor.r, sampledColor.g, sampledColor.b, _savedColor.a);
|
||||
_passthroughLayer.edgeColor = _savedColor;
|
||||
}
|
||||
|
||||
private void SetPassthroughStyle(OVRPassthroughLayer.ColorMapEditorType passthroughStyle)
|
||||
{
|
||||
_currentStyle = passthroughStyle;
|
||||
if (_currentStyle == OVRPassthroughLayer.ColorMapEditorType.ColorLut)
|
||||
{
|
||||
_passthroughLayer.SetColorLut(_passthroughColorLut, _savedBlend);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBrighnessContrastSaturation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d79cccad46eec343942a7dc28abf817
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,15 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class PassthroughSurface : MonoBehaviour
|
||||
{
|
||||
public OVRPassthroughLayer passthroughLayer;
|
||||
public MeshFilter projectionObject;
|
||||
|
||||
void Start()
|
||||
{
|
||||
Destroy(projectionObject.GetComponent<MeshRenderer>());
|
||||
passthroughLayer.AddSurfaceGeometry(projectionObject.gameObject, true);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c1dad0290b0d40379a1e56a66042be7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,33 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SPPquad : MonoBehaviour
|
||||
{
|
||||
OVRPassthroughLayer passthroughLayer;
|
||||
public MeshFilter projectionObject;
|
||||
OVRInput.Controller controllerHand;
|
||||
|
||||
void Start()
|
||||
{
|
||||
passthroughLayer = GetComponent<OVRPassthroughLayer>();
|
||||
passthroughLayer.AddSurfaceGeometry(projectionObject.gameObject, false);
|
||||
if (GetComponent<GrabObject>())
|
||||
{
|
||||
GetComponent<GrabObject>().GrabbedObjectDelegate += Grab;
|
||||
GetComponent<GrabObject>().ReleasedObjectDelegate += Release;
|
||||
}
|
||||
}
|
||||
|
||||
public void Grab(OVRInput.Controller grabHand)
|
||||
{
|
||||
passthroughLayer.RemoveSurfaceGeometry(projectionObject.gameObject);
|
||||
controllerHand = grabHand;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
controllerHand = OVRInput.Controller.None;
|
||||
passthroughLayer.AddSurfaceGeometry(projectionObject.gameObject, false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 232631f957b49a94b952c7117d9c3d40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class SceneSampler : MonoBehaviour
|
||||
{
|
||||
int currentSceneIndex = 0;
|
||||
public GameObject displayText;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
DontDestroyOnLoad(this.gameObject);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
bool controllersActive = OVRInput.GetActiveController() == OVRInput.Controller.Touch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.LTouch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.RTouch;
|
||||
|
||||
displayText.SetActive(controllersActive);
|
||||
|
||||
if (OVRInput.GetUp(OVRInput.Button.Start))
|
||||
{
|
||||
currentSceneIndex++;
|
||||
if (currentSceneIndex >= SceneManager.sceneCountInBuildSettings) currentSceneIndex = 0;
|
||||
SceneManager.LoadScene(currentSceneIndex);
|
||||
}
|
||||
|
||||
Vector3 menuPosition = OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch) + Vector3.up * 0.09f;
|
||||
displayText.transform.position = menuPosition;
|
||||
displayText.transform.rotation = Quaternion.LookRotation(menuPosition - Camera.main.transform.position);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 570413604d89b514bb2574e4f9628f7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class SelectivePassthroughExperience : MonoBehaviour
|
||||
{
|
||||
public GameObject leftMaskObject;
|
||||
public GameObject rightMaskObject;
|
||||
|
||||
void Update()
|
||||
{
|
||||
Camera.main.depthTextureMode = DepthTextureMode.Depth;
|
||||
|
||||
bool controllersActive = (OVRInput.GetActiveController() == OVRInput.Controller.LTouch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.RTouch ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.Touch);
|
||||
|
||||
leftMaskObject.SetActive(controllersActive);
|
||||
rightMaskObject.SetActive(controllersActive);
|
||||
|
||||
// controller masks are giant circles attached to controllers
|
||||
if (controllersActive)
|
||||
{
|
||||
Vector3 Lpos = OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch) +
|
||||
OVRInput.GetLocalControllerRotation(OVRInput.Controller.LTouch) * Vector3.forward * 0.1f;
|
||||
Vector3 Rpos = OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch) +
|
||||
OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch) * Vector3.forward * 0.1f;
|
||||
|
||||
leftMaskObject.transform.position = Lpos;
|
||||
rightMaskObject.transform.position = Rpos;
|
||||
}
|
||||
// hand masks are an inflated hands shader, with alpha fading at wrists and edges
|
||||
else if (OVRInput.GetActiveController() == OVRInput.Controller.LHand ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.RHand ||
|
||||
OVRInput.GetActiveController() == OVRInput.Controller.Hands)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2154d320019e72543b03d9a4778af800
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user