mudra-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/CLAUDE.md +35 -0
  3. package/bin/install.js +25 -0
  4. package/package.json +28 -0
  5. package/skills/mudra-master/SKILL.md +202 -0
  6. package/skills/mudra-master/mudra-preview/SKILL.md +151 -0
  7. package/skills/mudra-master/mudra-preview/assets/ar-menu.html +382 -0
  8. package/skills/mudra-master/mudra-preview/assets/document-scroller.html +468 -0
  9. package/skills/mudra-master/mudra-preview/assets/drum-machine.html +788 -0
  10. package/skills/mudra-master/mudra-preview/assets/emg-visualizer.html +390 -0
  11. package/skills/mudra-master/mudra-preview/assets/generative-art.html +408 -0
  12. package/skills/mudra-master/mudra-preview/assets/gesture-assistant.html +342 -0
  13. package/skills/mudra-master/mudra-preview/assets/gesture-speech.html +444 -0
  14. package/skills/mudra-master/mudra-preview/assets/hands-free-desktop.html +376 -0
  15. package/skills/mudra-master/mudra-preview/assets/model-rotator.html +365 -0
  16. package/skills/mudra-master/mudra-preview/assets/mudra-duel.html +3200 -0
  17. package/skills/mudra-master/mudra-preview/assets/mudra-monitor.html +1451 -0
  18. package/skills/mudra-master/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  19. package/skills/mudra-master/mudra-preview/assets/music-sequencer.html +455 -0
  20. package/skills/mudra-master/mudra-preview/assets/neural-pong.html +380 -0
  21. package/skills/mudra-master/mudra-preview/assets/neural-snake.html +364 -0
  22. package/skills/mudra-master/mudra-preview/assets/presentation-controller.html +345 -0
  23. package/skills/mudra-master/mudra-preview/assets/pressure-painter.html +285 -0
  24. package/skills/mudra-master/mudra-preview/assets/runner.html +1812 -0
  25. package/skills/mudra-master/mudra-preview/assets/smart-home.html +461 -0
  26. package/skills/mudra-master/mudra-preview/assets/space-invaders.html +1154 -0
  27. package/skills/mudra-master/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  28. package/skills/mudra-master/mudra-preview/references/agent_protocol.json +646 -0
  29. package/skills/mudra-master/mudra-preview/references/promt.md +16829 -0
  30. package/skills/mudra-master/mudra-xr/SKILL.md +245 -0
  31. package/skills/mudra-master/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  32. package/skills/mudra-master/mudra-xr/assets/demos/aisimulator.html +792 -0
  33. package/skills/mudra-master/mudra-xr/assets/demos/balloonpop.html +422 -0
  34. package/skills/mudra-master/mudra-xr/assets/demos/ballpit.html +284 -0
  35. package/skills/mudra-master/mudra-xr/assets/demos/drone.html +41 -0
  36. package/skills/mudra-master/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  37. package/skills/mudra-master/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  38. package/skills/mudra-master/mudra-xr/assets/demos/math3d.html +188 -0
  39. package/skills/mudra-master/mudra-xr/assets/demos/measure.html +247 -0
  40. package/skills/mudra-master/mudra-xr/assets/demos/occlusion.html +251 -0
  41. package/skills/mudra-master/mudra-xr/assets/demos/rain.html +498 -0
  42. package/skills/mudra-master/mudra-xr/assets/demos/screenwiper.html +432 -0
  43. package/skills/mudra-master/mudra-xr/assets/demos/splash.html +519 -0
  44. package/skills/mudra-master/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  45. package/skills/mudra-master/mudra-xr/assets/demos/xremoji.html +833 -0
  46. package/skills/mudra-master/mudra-xr/assets/demos/xrpoet.html +151 -0
  47. package/skills/mudra-master/mudra-xr/assets/samples/depthmap.html +266 -0
  48. package/skills/mudra-master/mudra-xr/assets/samples/depthmesh.html +100 -0
  49. package/skills/mudra-master/mudra-xr/assets/samples/game_rps.html +1099 -0
  50. package/skills/mudra-master/mudra-xr/assets/samples/gestures_custom.html +467 -0
  51. package/skills/mudra-master/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  52. package/skills/mudra-master/mudra-xr/assets/samples/lighting.html +163 -0
  53. package/skills/mudra-master/mudra-xr/assets/samples/mesh_detection.html +46 -0
  54. package/skills/mudra-master/mudra-xr/assets/samples/modelviewer.html +183 -0
  55. package/skills/mudra-master/mudra-xr/assets/samples/paint.html +133 -0
  56. package/skills/mudra-master/mudra-xr/assets/samples/planar-vst.html +45 -0
  57. package/skills/mudra-master/mudra-xr/assets/samples/reticle.html +95 -0
  58. package/skills/mudra-master/mudra-xr/assets/samples/skybox_agent.html +384 -0
  59. package/skills/mudra-master/mudra-xr/assets/samples/sound.html +390 -0
  60. package/skills/mudra-master/mudra-xr/assets/samples/ui.html +496 -0
  61. package/skills/mudra-master/mudra-xr/assets/samples/virtual-screens.html +808 -0
  62. package/skills/mudra-master/mudra-xr/assets/templates/0_basic.html +64 -0
  63. package/skills/mudra-master/mudra-xr/assets/templates/1_ui.html +107 -0
  64. package/skills/mudra-master/mudra-xr/assets/templates/2_hands.html +183 -0
  65. package/skills/mudra-master/mudra-xr/assets/templates/3_depth.html +89 -0
  66. package/skills/mudra-master/mudra-xr/assets/templates/4_stereo.html +84 -0
  67. package/skills/mudra-master/mudra-xr/assets/templates/5_camera.html +117 -0
  68. package/skills/mudra-master/mudra-xr/assets/templates/6_ai.html +154 -0
  69. package/skills/mudra-master/mudra-xr/assets/templates/7_ai_live.html +253 -0
  70. package/skills/mudra-master/mudra-xr/assets/templates/8_objects.html +95 -0
  71. package/skills/mudra-master/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  72. package/skills/mudra-master/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  73. package/skills/mudra-master/mudra-xr/assets/templates/meshes.html +47 -0
  74. package/skills/mudra-master/mudra-xr/assets/templates/planes.html +47 -0
  75. package/skills/mudra-master/mudra-xr/assets/templates/uikit.html +187 -0
  76. package/skills/mudra-master/mudra-xr/references/agent_protocol.json +878 -0
  77. package/skills/mudra-master/mudra-xr/references/promt.md +1516 -0
  78. package/skills/mudra-preview/SKILL.md +151 -0
  79. package/skills/mudra-preview/assets/ar-menu.html +382 -0
  80. package/skills/mudra-preview/assets/document-scroller.html +468 -0
  81. package/skills/mudra-preview/assets/drum-machine.html +788 -0
  82. package/skills/mudra-preview/assets/emg-visualizer.html +390 -0
  83. package/skills/mudra-preview/assets/generative-art.html +408 -0
  84. package/skills/mudra-preview/assets/gesture-assistant.html +342 -0
  85. package/skills/mudra-preview/assets/gesture-speech.html +444 -0
  86. package/skills/mudra-preview/assets/hands-free-desktop.html +376 -0
  87. package/skills/mudra-preview/assets/model-rotator.html +365 -0
  88. package/skills/mudra-preview/assets/mudra-duel.html +3200 -0
  89. package/skills/mudra-preview/assets/mudra-monitor.html +1451 -0
  90. package/skills/mudra-preview/assets/mudra-ultimate-template.html +891 -0
  91. package/skills/mudra-preview/assets/music-sequencer.html +455 -0
  92. package/skills/mudra-preview/assets/neural-pong.html +380 -0
  93. package/skills/mudra-preview/assets/neural-snake.html +364 -0
  94. package/skills/mudra-preview/assets/presentation-controller.html +345 -0
  95. package/skills/mudra-preview/assets/pressure-painter.html +285 -0
  96. package/skills/mudra-preview/assets/runner.html +1812 -0
  97. package/skills/mudra-preview/assets/smart-home.html +461 -0
  98. package/skills/mudra-preview/assets/space-invaders.html +1154 -0
  99. package/skills/mudra-preview/assets/waterful-ring-toss.html +1624 -0
  100. package/skills/mudra-preview/references/agent_protocol.json +646 -0
  101. package/skills/mudra-preview/references/promt.md +16829 -0
  102. package/skills/mudra-xr/SKILL.md +245 -0
  103. package/skills/mudra-xr/assets/demos/3dgs-walkthrough.html +242 -0
  104. package/skills/mudra-xr/assets/demos/aisimulator.html +792 -0
  105. package/skills/mudra-xr/assets/demos/balloonpop.html +422 -0
  106. package/skills/mudra-xr/assets/demos/ballpit.html +284 -0
  107. package/skills/mudra-xr/assets/demos/drone.html +41 -0
  108. package/skills/mudra-xr/assets/demos/gemini-icebreakers.html +338 -0
  109. package/skills/mudra-xr/assets/demos/gemini-xrobject.html +302 -0
  110. package/skills/mudra-xr/assets/demos/math3d.html +188 -0
  111. package/skills/mudra-xr/assets/demos/measure.html +247 -0
  112. package/skills/mudra-xr/assets/demos/occlusion.html +251 -0
  113. package/skills/mudra-xr/assets/demos/rain.html +498 -0
  114. package/skills/mudra-xr/assets/demos/screenwiper.html +432 -0
  115. package/skills/mudra-xr/assets/demos/splash.html +519 -0
  116. package/skills/mudra-xr/assets/demos/webcam_gestures.html +253 -0
  117. package/skills/mudra-xr/assets/demos/xremoji.html +833 -0
  118. package/skills/mudra-xr/assets/demos/xrpoet.html +151 -0
  119. package/skills/mudra-xr/assets/samples/depthmap.html +266 -0
  120. package/skills/mudra-xr/assets/samples/depthmesh.html +100 -0
  121. package/skills/mudra-xr/assets/samples/game_rps.html +1099 -0
  122. package/skills/mudra-xr/assets/samples/gestures_custom.html +467 -0
  123. package/skills/mudra-xr/assets/samples/gestures_heuristic.html +256 -0
  124. package/skills/mudra-xr/assets/samples/lighting.html +163 -0
  125. package/skills/mudra-xr/assets/samples/mesh_detection.html +46 -0
  126. package/skills/mudra-xr/assets/samples/modelviewer.html +183 -0
  127. package/skills/mudra-xr/assets/samples/paint.html +133 -0
  128. package/skills/mudra-xr/assets/samples/planar-vst.html +45 -0
  129. package/skills/mudra-xr/assets/samples/reticle.html +95 -0
  130. package/skills/mudra-xr/assets/samples/skybox_agent.html +384 -0
  131. package/skills/mudra-xr/assets/samples/sound.html +390 -0
  132. package/skills/mudra-xr/assets/samples/ui.html +496 -0
  133. package/skills/mudra-xr/assets/samples/virtual-screens.html +808 -0
  134. package/skills/mudra-xr/assets/templates/0_basic.html +64 -0
  135. package/skills/mudra-xr/assets/templates/1_ui.html +107 -0
  136. package/skills/mudra-xr/assets/templates/2_hands.html +183 -0
  137. package/skills/mudra-xr/assets/templates/3_depth.html +89 -0
  138. package/skills/mudra-xr/assets/templates/4_stereo.html +84 -0
  139. package/skills/mudra-xr/assets/templates/5_camera.html +117 -0
  140. package/skills/mudra-xr/assets/templates/6_ai.html +154 -0
  141. package/skills/mudra-xr/assets/templates/7_ai_live.html +253 -0
  142. package/skills/mudra-xr/assets/templates/8_objects.html +95 -0
  143. package/skills/mudra-xr/assets/templates/9_xr-toggle.html +90 -0
  144. package/skills/mudra-xr/assets/templates/heuristic_hand_gestures.html +102 -0
  145. package/skills/mudra-xr/assets/templates/meshes.html +47 -0
  146. package/skills/mudra-xr/assets/templates/planes.html +47 -0
  147. package/skills/mudra-xr/assets/templates/uikit.html +187 -0
  148. package/skills/mudra-xr/references/agent_protocol.json +878 -0
  149. package/skills/mudra-xr/references/promt.md +1516 -0
@@ -0,0 +1,519 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/splash -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>Splash</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+
12
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
13
+ <script>
14
+ window.litDisableBundleWarning = true;
15
+ </script>
16
+ <script type="importmap">
17
+ {
18
+ "imports": {
19
+ "@dimforge/rapier3d-simd-compat": "https://cdn.jsdelivr.net/npm/@dimforge/rapier3d-simd-compat@0.19.2/rapier.mjs",
20
+ "three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
21
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/",
22
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
23
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
24
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
25
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
26
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
27
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
28
+ "lit/": "https://esm.run/lit@3/",
29
+ "xrblocks": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/xrblocks.js",
30
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/npm/xrblocks@0.11.0/build/addons/"
31
+ }
32
+ }
33
+ </script>
34
+ </head>
35
+
36
+ <body>
37
+ <div
38
+ class="background-image"
39
+ style="background-image: url('textures/background.webp')"
40
+ ></div>
41
+ <script type="module">
42
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
43
+
44
+ import RAPIER from '@dimforge/rapier3d-simd-compat';
45
+ import * as THREE from 'three';
46
+ import * as xb from 'xrblocks';
47
+ import {palette} from 'xrblocks/addons/utils/Palette.js';
48
+ import {SimpleDecalGeometry} from 'xrblocks/addons/objects/SimpleDecalGeometry.js';
49
+
50
+ // --- BallShooter inlined (from ballpit) ---
51
+ class BallShooter extends xb.Script {
52
+ constructor({
53
+ numBalls = 100,
54
+ radius = 0.08,
55
+ palette: pal = null,
56
+ liveDuration = 3000,
57
+ deflateDuration = 200,
58
+ distanceThreshold = 0.25,
59
+ distanceFadeout = 0.25,
60
+ }) {
61
+ super();
62
+ this.liveDuration = liveDuration;
63
+ this.deflateDuration = deflateDuration;
64
+ this.distanceThreshold = distanceThreshold;
65
+ this.distanceFadeout = distanceFadeout;
66
+ const geometry = new THREE.IcosahedronGeometry(radius, 3);
67
+ this.spheres = [];
68
+ for (let i = 0; i < numBalls; ++i) {
69
+ const material = new THREE.MeshLambertMaterial({transparent: true});
70
+ const sphere = new THREE.Mesh(geometry, material);
71
+ sphere.castShadow = true;
72
+ sphere.receiveShadow = true;
73
+ this.spheres.push(sphere);
74
+ }
75
+ for (let i = 0; i < this.spheres.length; i++) {
76
+ this.spheres[i].position.set(Math.random() * 2 - 2, Math.random() * 2, Math.random() * 2 - 2);
77
+ if (pal != null) this.spheres[i].material.color.copy(pal.getRandomLiteGColor());
78
+ }
79
+ const now = performance.now();
80
+ this.spawnTimes = [];
81
+ for (let i = 0; i < numBalls; ++i) this.spawnTimes[i] = now;
82
+ this.nextBall = 0;
83
+ this.rigidBodies = [];
84
+ this.colliders = [];
85
+ this.colliderHandleToIndex = new Map();
86
+ this.viewSpacePosition = new THREE.Vector3();
87
+ this.clipSpacePosition = new THREE.Vector3();
88
+ this.projectedPosition = new THREE.Vector3();
89
+ this.clipFromWorld = new THREE.Matrix4();
90
+ }
91
+
92
+ initPhysics(physics) {
93
+ this.setupPhysics({
94
+ RAPIER: physics.RAPIER,
95
+ blendedWorld: physics.blendedWorld,
96
+ colliderActiveEvents: physics.RAPIER.ActiveEvents.CONTACT_FORCE_EVENTS,
97
+ });
98
+ }
99
+
100
+ setupPhysics({RAPIER, blendedWorld, colliderActiveEvents = 0, continuousCollisionDetection = false}) {
101
+ this.RAPIER = RAPIER;
102
+ this.blendedWorld = blendedWorld;
103
+ this.colliderActiveEvents = colliderActiveEvents;
104
+ this.continuousCollisionDetection = continuousCollisionDetection;
105
+ }
106
+
107
+ spawnBallAt(position, velocity = new THREE.Vector3(), now = performance.now()) {
108
+ const ball = this.spheres[this.nextBall];
109
+ ball.position.copy(position);
110
+ ball.scale.setScalar(1.0);
111
+ ball.opacity = 1.0;
112
+ this._createRigidBody(this.nextBall, position, velocity, ball.geometry.parameters.radius);
113
+ this.spawnTimes[this.nextBall] = now;
114
+ this.nextBall = (this.nextBall + 1) % this.spheres.length;
115
+ this.add(ball);
116
+ }
117
+
118
+ _createRigidBody(index, position, velocity, radius) {
119
+ if (this.rigidBodies[index] != null) this.blendedWorld.removeRigidBody(this.rigidBodies[index]);
120
+ const desc = this.RAPIER.RigidBodyDesc.dynamic().setTranslation(position.x, position.y, position.z).setLinvel(velocity.x, velocity.y, velocity.z).setCcdEnabled(this.continuousCollisionDetection);
121
+ const body = this.blendedWorld.createRigidBody(desc);
122
+ const shape = this.RAPIER.ColliderDesc.ball(radius).setActiveEvents(this.colliderActiveEvents);
123
+ const collider = this.blendedWorld.createCollider(shape, body);
124
+ this.colliderHandleToIndex.set(collider.handle, index);
125
+ this.rigidBodies[index] = body;
126
+ this.colliders[index] = collider;
127
+ }
128
+
129
+ physicsStep(now = performance.now()) {
130
+ for (let i = 0; i < this.spheres.length; i++) {
131
+ const sphere = this.spheres[i];
132
+ const body = this.rigidBodies[i];
133
+ let spawnTime = this.spawnTimes[i];
134
+ if (this.isBallActive(i) && body != null) {
135
+ let ballVisibility = 1.0;
136
+ const position = sphere.position.copy(body.translation());
137
+ const viewMatrix = xb.depth?.enabled ? xb.depth.depthViewMatrices[0] : xb.core.camera.matrixWorldInverse;
138
+ const projectionMatrix = xb.depth?.enabled ? xb.depth.depthProjectionMatrices[0] : xb.core.camera.projectionMatrix;
139
+ const viewSpacePosition = this.viewSpacePosition.copy(position).applyMatrix4(viewMatrix);
140
+ const clipSpacePosition = this.clipSpacePosition.copy(viewSpacePosition).applyMatrix4(projectionMatrix);
141
+ const ballIsInView = -1.0 <= clipSpacePosition.x && clipSpacePosition.x <= 1.0 && -1.0 <= clipSpacePosition.y && clipSpacePosition.y <= 1.0;
142
+ if (ballIsInView && xb.depth.enabled) {
143
+ const projectedPosition = xb.depth.getProjectedDepthViewPositionFromWorldPosition(position, this.projectedPosition);
144
+ const distanceBehindDepth = Math.max(projectedPosition.z - viewSpacePosition.z, 0.0);
145
+ if (distanceBehindDepth > this.distanceThreshold) {
146
+ const deflateAmount = Math.max((distanceBehindDepth - this.distanceThreshold) / this.distanceFadeout, 1.0);
147
+ spawnTime = Math.min(spawnTime, now - this.liveDuration - this.deflateDuration * deflateAmount);
148
+ }
149
+ }
150
+ if (now - spawnTime > this.liveDuration) {
151
+ const timeSinceDeflateStarted = now - spawnTime - this.liveDuration;
152
+ const deflateAmount = Math.min(1, timeSinceDeflateStarted / this.deflateDuration);
153
+ ballVisibility = 1.0 - deflateAmount;
154
+ }
155
+ sphere.material.opacity = ballVisibility;
156
+ if (ballVisibility < 0.001) { this.removeBall(i); }
157
+ else { sphere.position.copy(body.translation()); sphere.quaternion.copy(body.rotation()); }
158
+ }
159
+ }
160
+ }
161
+
162
+ getIndexForColliderHandle(handle) { return this.colliderHandleToIndex.get(handle); }
163
+
164
+ removeBall(index) {
165
+ const ball = this.spheres[index];
166
+ ball.material.opacity = 0.0;
167
+ ball.scale.setScalar(0);
168
+ const body = this.rigidBodies[index];
169
+ if (body != null) { this.blendedWorld.removeRigidBody(body); this.rigidBodies[index] = null; this.colliders[index] = null; }
170
+ this.remove(ball);
171
+ }
172
+
173
+ isBallActive(index) { return this.spheres[index].parent == this; }
174
+ }
175
+
176
+ // --- PaintSplash inlined ---
177
+ const ASSETS_PATH = 'https://cdn.jsdelivr.net/gh/xrblocks/assets@main/';
178
+
179
+ const kFadeoutMs = 2000;
180
+ const textureLoader = new THREE.TextureLoader();
181
+ const decalDiffuse = textureLoader.load(
182
+ './paintball_assets/decal-diffuse1.webp'
183
+ );
184
+ decalDiffuse.colorSpace = THREE.SRGBColorSpace;
185
+ const decalNormal = textureLoader.load('./paintball_assets/decal-normal1.webp');
186
+
187
+ let paintshotAudioBuffer;
188
+ const audioLoader = new THREE.AudioLoader();
189
+ audioLoader.load(
190
+ ASSETS_PATH + 'musicLibrary/PaintOneShot1.opus',
191
+ function (buffer) {
192
+ paintshotAudioBuffer = buffer;
193
+ }
194
+ );
195
+
196
+ class PaintSplash extends THREE.Object3D {
197
+ constructor(listener, color) {
198
+ super();
199
+ if (listener != null) {
200
+ this.sound = new THREE.PositionalAudio(listener);
201
+ }
202
+ this.color = color;
203
+ this.enableSound = true;
204
+ this.splashList = [];
205
+ }
206
+
207
+ splatFromIntersection(intersection, scale) {
208
+ const objectRotation = new THREE.Quaternion();
209
+ intersection.object.getWorldQuaternion(objectRotation);
210
+
211
+ const normal = intersection.normal.clone().applyQuaternion(objectRotation);
212
+
213
+ const originalNormal = new THREE.Vector3(0, 0, 1);
214
+ const angle = originalNormal.angleTo(normal);
215
+
216
+ originalNormal.cross(normal).normalize();
217
+
218
+ const randomRotation = new THREE.Quaternion().setFromAxisAngle(
219
+ normal,
220
+ Math.random() * Math.PI * 2
221
+ );
222
+
223
+ const rotateFacingNormal = new THREE.Quaternion()
224
+ .setFromAxisAngle(originalNormal, angle)
225
+ .premultiply(randomRotation);
226
+
227
+ this.splatOnMesh(
228
+ intersection.object,
229
+ intersection.point,
230
+ rotateFacingNormal,
231
+ scale
232
+ );
233
+ }
234
+
235
+ splatOnMesh(mesh, position, orientation, scale) {
236
+ const material = new THREE.MeshPhongMaterial({
237
+ color: this.color,
238
+ specular: 0x555555,
239
+ map: decalDiffuse,
240
+ normalMap: decalNormal,
241
+ normalScale: new THREE.Vector2(1, 1),
242
+ shininess: 30,
243
+ transparent: true,
244
+ depthTest: true,
245
+ polygonOffset: true,
246
+ polygonOffsetFactor: 0,
247
+ alphaTest: 0.5,
248
+ opacity: 1.0,
249
+ side: THREE.FrontSide,
250
+ });
251
+
252
+ const scaleVector3 = new THREE.Vector3(scale, scale, scale);
253
+
254
+ const geometry = new SimpleDecalGeometry(
255
+ mesh,
256
+ position,
257
+ orientation,
258
+ scaleVector3
259
+ );
260
+ geometry.computeVertexNormals();
261
+
262
+ this.decalMesh = new THREE.Mesh(geometry, material);
263
+ this.decalMesh.createdTime = performance.now();
264
+ this.add(this.decalMesh);
265
+
266
+ if (
267
+ this.enableSound &&
268
+ this.sound != null &&
269
+ paintshotAudioBuffer != null
270
+ ) {
271
+ this.sound.setBuffer(paintshotAudioBuffer);
272
+ this.sound.setRefDistance(10);
273
+ this.sound.play();
274
+ }
275
+ }
276
+
277
+ update() {
278
+ const currentTime = performance.now();
279
+
280
+ this.children.forEach((child) => {
281
+ if (child instanceof THREE.Mesh && child.createdTime !== undefined) {
282
+ const timeElapsed = currentTime - child.createdTime;
283
+
284
+ if (timeElapsed > kFadeoutMs) {
285
+ const timeSinceFadeStart = timeElapsed - kFadeoutMs;
286
+
287
+ if (timeSinceFadeStart <= kFadeoutMs) {
288
+ const newOpacity = 1.0 - timeSinceFadeStart / kFadeoutMs;
289
+ child.material.opacity = Math.max(0.0, newOpacity);
290
+ child.material.transparent = true;
291
+ } else {
292
+ this.remove(child);
293
+ }
294
+ }
295
+ }
296
+ });
297
+ }
298
+
299
+ dispose() {
300
+ if (this.decalMesh) {
301
+ this.decalMesh.geometry.dispose();
302
+ this.decalMesh.material.dispose();
303
+ }
304
+ }
305
+ }
306
+
307
+ // --- SplashScript inlined ---
308
+ const kLightX = xb.getUrlParamFloat('lightX', 0);
309
+ const kLightY = xb.getUrlParamFloat('lightY', 500);
310
+ const kLightZ = xb.getUrlParamFloat('lightZ', -10);
311
+ const kBallsPerSecond = xb.getUrlParamFloat('ballsPerSecond', 10);
312
+ const kVelocityScale = xb.getUrlParamInt('velocityScale', 1.0);
313
+ const kBallRadius = xb.getUrlParamFloat('ballRadius', 0.04);
314
+
315
+ class SplashScript extends xb.Script {
316
+ constructor() {
317
+ super();
318
+ this.ballShooter = new BallShooter({
319
+ numBalls: 100,
320
+ radius: kBallRadius,
321
+ palette: palette,
322
+ });
323
+ this.add(this.ballShooter);
324
+ this.lastBallTime = new Map();
325
+
326
+ this.raycaster = new THREE.Raycaster();
327
+ this.paintballs = [];
328
+ this.listener = new THREE.AudioListener();
329
+
330
+ this.pointer = new THREE.Vector2();
331
+ this.velocity = new THREE.Vector3();
332
+ }
333
+
334
+ init() {
335
+ this.addLights();
336
+ xb.core.input.addReticles();
337
+ xb.showReticleOnDepthMesh(true);
338
+ xb.core.camera.add(this.listener);
339
+ }
340
+
341
+ initPhysics(xrPhysics) {
342
+ this.physics = xrPhysics;
343
+ this.ballShooter.setupPhysics({
344
+ RAPIER: xrPhysics.RAPIER,
345
+ blendedWorld: xrPhysics.blendedWorld,
346
+ colliderActiveEvents: xrPhysics.RAPIER.ActiveEvents.CONTACT_FORCE_EVENTS,
347
+ continuousCollisionDetection: true,
348
+ });
349
+ }
350
+
351
+ generateDecalAtIntersection(intersection) {
352
+ const paintball = new PaintSplash(
353
+ this.listener,
354
+ palette.getRandomLiteGColor()
355
+ );
356
+
357
+ const scaleMultiplier = 0.4;
358
+
359
+ if (xb.core.depth.depthData.length > 0) {
360
+ xb.core.depth.depthMesh.updateFullResolutionGeometry(
361
+ xb.core.depth.depthData[0]
362
+ );
363
+ }
364
+ paintball.splatFromIntersection(
365
+ intersection /*scale=*/,
366
+ xb.lerp(scaleMultiplier * 0.3, scaleMultiplier * 0.5, Math.random())
367
+ );
368
+ this.add(paintball);
369
+ this.paintballs.push(paintball);
370
+ }
371
+
372
+ generateDecalFromCollision(position, direction, color) {
373
+ const paintball = new PaintSplash(this.listener, color);
374
+ const orientation = new THREE.Quaternion().setFromUnitVectors(
375
+ new THREE.Vector3(0.0, 0.0, -1.0),
376
+ direction
377
+ );
378
+ const scaleMultiplier = 0.4;
379
+ const scale = xb.lerp(
380
+ scaleMultiplier * 0.3,
381
+ scaleMultiplier * 0.5,
382
+ Math.random()
383
+ );
384
+ if (xb.core.depth.cpuDepthData.length > 0) {
385
+ xb.core.depth.depthMesh.updateFullResolutionGeometry(
386
+ xb.core.depth.cpuDepthData[0]
387
+ );
388
+ } else if (xb.core.depth.gpuDepthData.length > 0) {
389
+ xb.core.depth.depthMesh.updateFullResolutionGeometry(
390
+ xb.core.depth.depthMesh.convertGPUToGPU(xb.core.depth.gpuDepthData[0])
391
+ );
392
+ }
393
+ paintball.splatOnMesh(
394
+ xb.core.depth.depthMesh,
395
+ position,
396
+ orientation,
397
+ scale
398
+ );
399
+ this.add(paintball);
400
+ this.paintballs.push(paintball);
401
+ }
402
+
403
+ controllerUpdate(controller) {
404
+ const now = performance.now();
405
+ if (!this.lastBallTime.has(controller)) {
406
+ this.lastBallTime.set(controller, -99);
407
+ }
408
+ if (
409
+ controller.userData.selected &&
410
+ now - this.lastBallTime.get(controller) >= 1000 / kBallsPerSecond
411
+ ) {
412
+ const newPosition = new THREE.Vector3(0.0, 0.0, -0.1)
413
+ .applyQuaternion(controller.quaternion)
414
+ .add(controller.position);
415
+ this.velocity
416
+ .set(0, 0, -5.0 * kVelocityScale)
417
+ .applyQuaternion(controller.quaternion);
418
+ this.ballShooter.spawnBallAt(newPosition, this.velocity);
419
+ this.lastBallTime.set(controller, now);
420
+ }
421
+ }
422
+
423
+ update() {
424
+ for (let controller of xb.core.input.controllers) {
425
+ this.controllerUpdate(controller);
426
+ }
427
+
428
+ for (let paintball of this.paintballs) {
429
+ paintball.update();
430
+ }
431
+ }
432
+
433
+ physicsStep() {
434
+ const contactPoint = new THREE.Vector3();
435
+ const forceDirection = new THREE.Vector3();
436
+ const ballShooter = this.ballShooter;
437
+ this.physics.eventQueue.drainContactForceEvents((event) => {
438
+ const handle1 = event.collider1();
439
+ const handle2 = event.collider2();
440
+ const depthMeshCollider =
441
+ xb.core.depth.depthMesh.getColliderFromHandle(handle1) ||
442
+ xb.core.depth.depthMesh.getColliderFromHandle(handle2);
443
+ const ballIndex =
444
+ ballShooter.getIndexForColliderHandle(handle1) ||
445
+ ballShooter.getIndexForColliderHandle(handle2);
446
+ let generatedDecal = false;
447
+ if (
448
+ depthMeshCollider &&
449
+ ballIndex != null &&
450
+ ballShooter.isBallActive(ballIndex)
451
+ ) {
452
+ const ball = ballShooter.spheres[ballIndex];
453
+ this.physics.blendedWorld.contactPair(
454
+ depthMeshCollider,
455
+ ballShooter.colliders[ballIndex],
456
+ (manifold, flipped) => {
457
+ if (!generatedDecal && manifold.numSolverContacts() > 0) {
458
+ contactPoint.copy(manifold.solverContactPoint(0));
459
+ forceDirection.copy(event.maxForceDirection());
460
+ this.generateDecalFromCollision(
461
+ contactPoint,
462
+ forceDirection,
463
+ ball.material.color
464
+ );
465
+ generatedDecal = true;
466
+ }
467
+ }
468
+ );
469
+ ballShooter.removeBall(ballIndex);
470
+ }
471
+ });
472
+ ballShooter.physicsStep();
473
+ }
474
+
475
+ addLights() {
476
+ this.add(new THREE.HemisphereLight(0xbbbbbb, 0x888888, 3));
477
+ const light = new THREE.DirectionalLight(0xffffff, 2);
478
+ light.position.set(kLightX, kLightY, kLightZ);
479
+ light.castShadow = true;
480
+ light.shadow.mapSize.width = 2048;
481
+ light.shadow.mapSize.height = 2048;
482
+ this.add(light);
483
+ }
484
+
485
+ onPointerUp(event) {
486
+ this.mouseReticle.setPressed(false);
487
+ }
488
+ }
489
+
490
+ const depthMeshColliderUpdateFps = xb.getUrlParamFloat(
491
+ 'depthMeshColliderUpdateFps',
492
+ 30
493
+ );
494
+ const splashScript = new SplashScript();
495
+
496
+ let options = new xb.Options();
497
+ options.simulator.instructions.enabled = false;
498
+ options.depth = new xb.DepthOptions(xb.xrDepthMeshPhysicsOptions);
499
+ options.depth.depthMesh.colliderUpdateFps = depthMeshColliderUpdateFps;
500
+ options.xrButton = {
501
+ ...options.xrButton,
502
+ startText: '<i id="xrlogo"></i> MAKE A MESS',
503
+ endText: '<i id="xrlogo"></i> MISSION COMPLETE',
504
+ };
505
+ options.physics.RAPIER = RAPIER;
506
+ options.physics.useEventQueue = true;
507
+
508
+ async function start() {
509
+ xb.add(splashScript);
510
+ await xb.init(options);
511
+ }
512
+
513
+ document.addEventListener('DOMContentLoaded', function () {
514
+ start();
515
+ });
516
+
517
+ </script>
518
+ </body>
519
+ </html>