Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-21 09:11:14 +02:00
commit eeca72985b
14558 changed files with 1508140 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f8a81fa1c7362de42a8fe4abd77cd033
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aeefa0d70cd35e248b8d4844b4624cda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d62788dcc9ea074fa279a45302d60de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e39bcbc612bddc4680b924ebcda2dd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 728c99f7459324d4abbab04f8bfd63b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03d5bda4ee38ce448b105c32ae5d8bb0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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++;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 445f7f5df38d546a58ce30e477cf0111
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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));
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff682cd7f7129418aa8ec97bc03d5c8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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";
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 891a5db22dd82b84195539b740608c98
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a9f68e69bcc45cf43abc834cb9713e41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c316363f4d5d264fb22b6342654e580
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cc9a7c09b49c544384157f418d4758f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ca33c7ea21cf1742b1ce795f9e3cf30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d79cccad46eec343942a7dc28abf817
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c1dad0290b0d40379a1e56a66042be7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 232631f957b49a94b952c7117d9c3d40
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 570413604d89b514bb2574e4f9628f7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2154d320019e72543b03d9a4778af800
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: