diff --git a/src/SparkXr.ts b/src/SparkXr.ts index 5609dfc9..206bfc3c 100644 --- a/src/SparkXr.ts +++ b/src/SparkXr.ts @@ -1,5 +1,19 @@ import * as THREE from "three"; import { SplatMesh } from "./SplatMesh"; +import { + FINGER_TIPS, + HANDS, + Hand, + JOINT_IDS, + JOINT_INDEX, + JOINT_RADIUS, + JOINT_SEGMENTS, + JOINT_SEGMENT_STEPS, + JOINT_TIPS, + JointEnum, + type JointId, + NUM_JOINTS, +} from "./hands"; export interface SparkXrOptions { renderer: THREE.WebGLRenderer; @@ -123,118 +137,6 @@ export const DEFAULT_CONTROLLER_GETSLOW = ( ) => gamepads.rightIsHand ? false : (gamepads.right?.buttons[1]?.pressed ?? false); -export enum JointEnum { - w = "wrist", - t0 = "thumb-metacarpal", - t1 = "thumb-phalanx-proximal", - t2 = "thumb-phalanx-distal", - t3 = "thumb-tip", - i0 = "index-finger-metacarpal", - i1 = "index-finger-phalanx-proximal", - i2 = "index-finger-phalanx-intermediate", - i3 = "index-finger-phalanx-distal", - i4 = "index-finger-tip", - m0 = "middle-finger-metacarpal", - m1 = "middle-finger-phalanx-proximal", - m2 = "middle-finger-phalanx-intermediate", - m3 = "middle-finger-phalanx-distal", - m4 = "middle-finger-tip", - r0 = "ring-finger-metacarpal", - r1 = "ring-finger-phalanx-proximal", - r2 = "ring-finger-phalanx-intermediate", - r3 = "ring-finger-phalanx-distal", - r4 = "ring-finger-tip", - p0 = "pinky-finger-metacarpal", - p1 = "pinky-finger-phalanx-proximal", - p2 = "pinky-finger-phalanx-intermediate", - p3 = "pinky-finger-phalanx-distal", - p4 = "pinky-finger-tip", -} -export type JointId = keyof typeof JointEnum; -export const JOINT_IDS = Object.keys(JointEnum) as JointId[]; -export const NUM_JOINTS = JOINT_IDS.length; - -export const JOINT_INDEX: { [key in JointId]: number } = { - w: 0, - t0: 1, - t1: 2, - t2: 3, - t3: 4, - i0: 5, - i1: 6, - i2: 7, - i3: 8, - i4: 9, - m0: 10, - m1: 11, - m2: 12, - m3: 13, - m4: 14, - r0: 15, - r1: 16, - r2: 17, - r3: 18, - r4: 19, - p0: 20, - p1: 21, - p2: 22, - p3: 23, - p4: 24, -}; - -export const JOINT_RADIUS: { [key in JointId]: number } = { - w: 0.02, - t0: 0.015, - t1: 0.012, - t2: 0.0105, - t3: 0.0085, - i0: 0.022, - i1: 0.012, - i2: 0.0085, - i3: 0.0075, - i4: 0.0065, - m0: 0.021, - m1: 0.012, - m2: 0.008, - m3: 0.0075, - m4: 0.0065, - r0: 0.019, - r1: 0.011, - r2: 0.0075, - r3: 0.007, - r4: 0.006, - p0: 0.012, - p1: 0.01, - p2: 0.007, - p3: 0.0065, - p4: 0.0055, -}; - -export const JOINT_SEGMENTS: JointId[][] = [ - ["w", "t0", "t1", "t2", "t3"], - ["w", "i0", "i1", "i2", "i3", "i4"], - ["w", "m0", "m1", "m2", "m3", "m4"], - ["w", "r0", "r1", "r2", "r3", "r4"], - ["w", "p0", "p1", "p2", "p3", "p4"], -]; - -export const JOINT_SEGMENT_STEPS: number[][] = [ - [8, 10, 8, 6], - [8, 19, 14, 8, 6], - [8, 19, 14, 8, 6], - [8, 19, 14, 8, 6], - [8, 19, 14, 8, 6], -]; - -export const JOINT_TIPS: JointId[] = ["t3", "i4", "m4", "r4", "p4"]; -export const FINGER_TIPS: JointId[] = ["i4", "m4", "r4", "p4"]; - -export enum Hand { - left = "left", - right = "right", -} -export const HANDS = Object.keys(Hand) as Hand[]; - const XR_HEADSET_HINTS = /Quest|OculusBrowser|VisionOS|XRBrowser|Pico|Lynx|MagicLeap/i; @@ -540,17 +442,17 @@ export class SparkXr { return !!this.xr; } - static JointEnum = JointEnum; - static JOINT_IDS = JOINT_IDS; - static NUM_JOINTS = NUM_JOINTS; - static JOINT_INDEX = JOINT_INDEX; - static JOINT_RADIUS = JOINT_RADIUS; - static JOINT_SEGMENTS = JOINT_SEGMENTS; - static JOINT_SEGMENT_STEPS = JOINT_SEGMENT_STEPS; - static JOINT_TIPS = JOINT_TIPS; - static FINGER_TIPS = FINGER_TIPS; - static Hand = Hand; - static HANDS = HANDS; + static readonly JointEnum = JointEnum; + static readonly JOINT_IDS = JOINT_IDS; + static readonly NUM_JOINTS = NUM_JOINTS; + static readonly JOINT_INDEX = JOINT_INDEX; + static readonly JOINT_RADIUS = JOINT_RADIUS; + static readonly JOINT_SEGMENTS = JOINT_SEGMENTS; + static readonly JOINT_SEGMENT_STEPS = JOINT_SEGMENT_STEPS; + static readonly JOINT_TIPS = JOINT_TIPS; + static readonly FINGER_TIPS = FINGER_TIPS; + static readonly Hand = Hand; + static readonly HANDS = HANDS; left() { return this.hands[0]; diff --git a/src/SplatAccumulator.ts b/src/SplatAccumulator.ts index 619e3509..46ac76ee 100644 --- a/src/SplatAccumulator.ts +++ b/src/SplatAccumulator.ts @@ -1,7 +1,7 @@ import * as THREE from "three"; import { FullScreenQuad } from "three/addons/postprocessing/Pass.js"; import { Readback } from "./Readback"; -import { SplatEdit } from "./SplatEdit"; +import { type SplatEdit, isSplatEdit } from "./SplatEdit"; import { type CovSplatGenerator, type GsplatGenerator, @@ -481,7 +481,7 @@ export class SplatAccumulator { const globalEditsSet = new Set(); scene.traverseVisible((node) => { - if (node instanceof SplatEdit) { + if (isSplatEdit(node)) { let ancestor = node.parent; while (ancestor != null && !(ancestor instanceof SplatMesh)) { ancestor = ancestor.parent; diff --git a/src/SplatEdit.ts b/src/SplatEdit.ts index ff0a4784..b8384bbc 100644 --- a/src/SplatEdit.ts +++ b/src/SplatEdit.ts @@ -10,7 +10,6 @@ import { unindent, unindentLines, } from "./dyno"; -import { newArray } from "./utils"; // Spark provides the ability to apply "edits" to Gsplats as part of the standard // SplatMesh pipeline. These edits take the form of a sequence of operations, @@ -127,6 +126,8 @@ export type SplatEditSdfOptions = { }; export class SplatEditSdf extends THREE.Object3D { + readonly isSplatEditSdf = true; + type: SplatEditSdfType; invert: boolean; opacity: number; @@ -146,6 +147,10 @@ export class SplatEditSdf extends THREE.Object3D { } } +export function isSplatEditSdf(obj: THREE.Object3D): obj is SplatEditSdf { + return (obj as SplatEditSdf).isSplatEditSdf; +} + export type SplatEditOptions = { // Name of this edit operation. If you omit it, a default "Edit 1", "Edit 2", ... // is assigned. @@ -167,6 +172,8 @@ export type SplatEditOptions = { }; export class SplatEdit extends THREE.Object3D { + readonly isSplatEdit = true; + // ordering used to apply SplatEdit operations to Gsplats. This is implicitly // increased with each new SplatEdit. Reassigning ordering can be used to // reorder the operations. @@ -222,6 +229,10 @@ export class SplatEdit extends THREE.Object3D { } } +export function isSplatEdit(obj: THREE.Object3D): obj is SplatEdit { + return (obj as SplatEdit).isSplatEdit; +} + // Dyno implementation of RGBA-XYZ SDF editing. // The SDFs are encoded in a texture while the edits are encoded // as a uniform uvec4 array. diff --git a/src/SplatMesh.ts b/src/SplatMesh.ts index bfd42c90..367ebcda 100644 --- a/src/SplatMesh.ts +++ b/src/SplatMesh.ts @@ -9,7 +9,13 @@ import { import { ExtSplats } from "./ExtSplats"; import { PackedSplats } from "./PackedSplats"; import { type RgbaArray, TRgbaArray } from "./RgbaArray"; -import { SplatEdit, SplatEditSdf, SplatEdits } from "./SplatEdit"; +import { + type SplatEdit, + type SplatEditSdf, + SplatEdits, + isSplatEdit, + isSplatEditSdf, +} from "./SplatEdit"; import { type CovSplatModifier, CovSplatTransformer, @@ -946,7 +952,7 @@ export class SplatMesh extends SplatGenerator { if (this.editable && !this.edits) { // If we haven't set any explicit edits, add any child SplatEdits this.traverseVisible((node) => { - if (node instanceof SplatEdit) { + if (isSplatEdit(node)) { edits.push(node); } }); @@ -959,7 +965,7 @@ export class SplatMesh extends SplatGenerator { } const sdfs: SplatEditSdf[] = []; edit.traverseVisible((node) => { - if (node instanceof SplatEditSdf) { + if (isSplatEditSdf(node)) { sdfs.push(node); } }); diff --git a/src/SplatSkinning.ts b/src/SplatSkinning.ts index 079f3910..26781da7 100644 --- a/src/SplatSkinning.ts +++ b/src/SplatSkinning.ts @@ -313,11 +313,11 @@ export class SplatSkinning { this.mesh.needsUpdate = true; } - private static UNIT_SCALE = new THREE.Vector3(1, 1, 1); - private static relQuat = new THREE.Quaternion(); - private static relPos = new THREE.Vector3(); - private static dual = new THREE.Quaternion(); - private static skinMat = new THREE.Matrix4(); + private static readonly UNIT_SCALE = /*@__PURE__*/ new THREE.Vector3(1, 1, 1); + private static readonly relQuat = /*@__PURE__*/ new THREE.Quaternion(); + private static readonly relPos = /*@__PURE__*/ new THREE.Vector3(); + private static readonly dual = /*@__PURE__*/ new THREE.Quaternion(); + private static readonly skinMat = /*@__PURE__*/ new THREE.Matrix4(); } // dyno program definitions for SplatSkinning @@ -326,7 +326,7 @@ export const GsplatSkinning = { type: "GsplatSkinning" } as { type: "GsplatSkinning"; }; -export const defineGsplatSkinning = unindent(` +export const defineGsplatSkinning = /*@__PURE__*/ unindent(` struct GsplatSkinning { int numSplats; int numBones; @@ -335,7 +335,7 @@ export const defineGsplatSkinning = unindent(` }; `); -export const defineApplyGsplatSkinning = unindent(` +export const defineApplyGsplatSkinning = /*@__PURE__*/ unindent(` void applyGsplatSkinning( int numSplats, int numBones, usampler2DArray skinTexture, sampler2D boneTexture, @@ -426,7 +426,7 @@ function applyGsplatSkinning( return dyno.outputs.gsplat; } -export const defineApplyCovSplatDQSkinning = unindent(` +export const defineApplyCovSplatDQSkinning = /*@__PURE__*/ unindent(` void applyCovSplatDQSkinning( int numSplats, int numBones, usampler2DArray skinTexture, sampler2D boneTexture, @@ -492,7 +492,7 @@ export const defineApplyCovSplatDQSkinning = unindent(` } `); -export const defineApplyCovSplatLBSkinning = unindent(` +export const defineApplyCovSplatLBSkinning = /*@__PURE__*/ unindent(` void applyCovSplatLBSkinning( int numSplats, int numBones, usampler2DArray skinTexture, sampler2D boneTexture, diff --git a/src/hands.ts b/src/hands.ts index 6fab4021..40cb1366 100644 --- a/src/hands.ts +++ b/src/hands.ts @@ -14,36 +14,42 @@ const DEFAULT_MOVE_INERTIA = 0.5; const DEFAULT_ROTATE_INERTIA = 0.5; const TOUCH_BIAS = 0.0; -export enum JointEnum { - w = "wrist", - t0 = "thumb-metacarpal", - t1 = "thumb-phalanx-proximal", - t2 = "thumb-phalanx-distal", - t3 = "thumb-tip", - i0 = "index-finger-metacarpal", - i1 = "index-finger-phalanx-proximal", - i2 = "index-finger-phalanx-intermediate", - i3 = "index-finger-phalanx-distal", - i4 = "index-finger-tip", - m0 = "middle-finger-metacarpal", - m1 = "middle-finger-phalanx-proximal", - m2 = "middle-finger-phalanx-intermediate", - m3 = "middle-finger-phalanx-distal", - m4 = "middle-finger-tip", - r0 = "ring-finger-metacarpal", - r1 = "ring-finger-phalanx-proximal", - r2 = "ring-finger-phalanx-intermediate", - r3 = "ring-finger-phalanx-distal", - r4 = "ring-finger-tip", - p0 = "pinky-finger-metacarpal", - p1 = "pinky-finger-phalanx-proximal", - p2 = "pinky-finger-phalanx-intermediate", - p3 = "pinky-finger-phalanx-distal", - p4 = "pinky-finger-tip", -} +export const JointEnum = { + w: "wrist", + t0: "thumb-metacarpal", + t1: "thumb-phalanx-proximal", + t2: "thumb-phalanx-distal", + t3: "thumb-tip", + i0: "index-finger-metacarpal", + i1: "index-finger-phalanx-proximal", + i2: "index-finger-phalanx-intermediate", + i3: "index-finger-phalanx-distal", + i4: "index-finger-tip", + m0: "middle-finger-metacarpal", + m1: "middle-finger-phalanx-proximal", + m2: "middle-finger-phalanx-intermediate", + m3: "middle-finger-phalanx-distal", + m4: "middle-finger-tip", + r0: "ring-finger-metacarpal", + r1: "ring-finger-phalanx-proximal", + r2: "ring-finger-phalanx-intermediate", + r3: "ring-finger-phalanx-distal", + r4: "ring-finger-tip", + p0: "pinky-finger-metacarpal", + p1: "pinky-finger-phalanx-proximal", + p2: "pinky-finger-phalanx-intermediate", + p3: "pinky-finger-phalanx-distal", + p4: "pinky-finger-tip", +} as const; export type JointId = keyof typeof JointEnum; -export const JOINT_IDS = Object.keys(JointEnum) as JointId[]; -export const NUM_JOINTS = JOINT_IDS.length; + +export const JOINT_IDS = /*@__PURE__*/ Object.keys(JointEnum) as JointId[]; +// NOTE: Using .length prevents tree-shaking, hence a runtime computation which +// can be statically analysed and eliminated. +export const NUM_JOINTS = /*@__PURE__*/ JOINT_IDS.reduce( + (acc, _) => acc + 1, + 0, +); export const JOINT_INDEX: { [key in JointId]: number } = { w: 0, @@ -75,9 +81,9 @@ export const JOINT_INDEX: { [key in JointId]: number } = { export const JOINT_RADIUS: { [key in JointId]: number } = { w: 0.02, - t0: 0.02, - t1: 0.014, - t2: 0.0115, + t0: 0.015, + t1: 0.012, + t2: 0.0105, t3: 0.0085, i0: 0.022, i1: 0.012,