Initial Commit

This commit is contained in:
2025-07-04 20:33:06 +03:00
commit 8f09347ae0
9219 changed files with 2447903 additions and 0 deletions
@@ -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++;
}
}