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,422 @@
1
+ <!doctype html>
2
+ <!-- reference: demos/balloonpop -->
3
+ <html lang="en">
4
+ <head>
5
+ <title>XR Blocks: Balloon Pop (v5.1 - Stable Fallback)</title>
6
+
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, user-scalable=no"
10
+ />
11
+ <link type="text/css" rel="stylesheet" href="https://xrblocks.github.io/css/xr.css" />
12
+ <script>
13
+ window.litDisableBundleWarning = true;
14
+ </script>
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js",
19
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.180.0/examples/jsm/",
20
+ "troika-three-text": "https://cdn.jsdelivr.net/gh/protectwise/troika@028b81cf308f0f22e5aa8e78196be56ec1997af5/packages/troika-three-text/src/index.js",
21
+ "troika-three-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-three-utils/src/index.js",
22
+ "troika-worker-utils": "https://cdn.jsdelivr.net/gh/protectwise/troika@v0.52.4/packages/troika-worker-utils/src/index.js",
23
+ "bidi-js": "https://esm.sh/bidi-js@%5E1.0.2?target=es2022",
24
+ "webgl-sdf-generator": "https://esm.sh/webgl-sdf-generator@1.1.1/es2022/webgl-sdf-generator.mjs",
25
+ "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js",
26
+ "lit/": "https://esm.run/lit@3/",
27
+ "@dimforge/rapier3d-simd-compat": "https://cdn.skypack.dev/@dimforge/rapier3d-simd-compat@0.17.0",
28
+ "xrblocks": "https://cdn.jsdelivr.net/gh/google/xrblocks@build/xrblocks.js",
29
+ "xrblocks/addons/": "https://cdn.jsdelivr.net/gh/google/xrblocks@build/addons/"
30
+ }
31
+ }
32
+ </script>
33
+ </head>
34
+
35
+ <body>
36
+ <script type="module">
37
+ import * as xb from 'xrblocks';
38
+ import RAPIER from '@dimforge/rapier3d-simd-compat';
39
+ import 'xrblocks/addons/simulator/SimulatorAddons.js';
40
+ import * as THREE from 'three';
41
+
42
+ // --- audio.js inlined ---
43
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
44
+
45
+ function playPopSound() {
46
+ if (audioContext.state === 'suspended') audioContext.resume();
47
+ const noiseSource = audioContext.createBufferSource();
48
+ const bandpass = audioContext.createBiquadFilter();
49
+ const gainNode = audioContext.createGain();
50
+ const now = audioContext.currentTime;
51
+ const sampleRate = audioContext.sampleRate;
52
+ const bufferSize = sampleRate * 0.15;
53
+ const noiseBuffer = audioContext.createBuffer(1, bufferSize, sampleRate);
54
+ const output = noiseBuffer.getChannelData(0);
55
+ for (let i = 0; i < bufferSize; i++) output[i] = Math.random() * 2 - 1;
56
+ noiseSource.buffer = noiseBuffer;
57
+ bandpass.type = 'bandpass';
58
+ bandpass.frequency.setValueAtTime(3000, now);
59
+ bandpass.Q.setValueAtTime(1.2, now);
60
+ gainNode.gain.setValueAtTime(0, now);
61
+ gainNode.gain.linearRampToValueAtTime(0.8, now + 0.002);
62
+ gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.12);
63
+ noiseSource.connect(bandpass);
64
+ bandpass.connect(gainNode);
65
+ gainNode.connect(audioContext.destination);
66
+ noiseSource.start(0);
67
+ }
68
+
69
+ function playWhooshSound() {
70
+ if (audioContext.state === 'suspended') audioContext.resume();
71
+ const oscillator = audioContext.createOscillator();
72
+ const gainNode = audioContext.createGain();
73
+ const now = audioContext.currentTime;
74
+ oscillator.type = 'sawtooth';
75
+ oscillator.frequency.setValueAtTime(800, now);
76
+ oscillator.frequency.exponentialRampToValueAtTime(100, now + 0.1);
77
+ gainNode.gain.setValueAtTime(0.3, now);
78
+ gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.1);
79
+ oscillator.connect(gainNode);
80
+ gainNode.connect(audioContext.destination);
81
+ oscillator.start(0);
82
+ oscillator.stop(now + 0.1);
83
+ }
84
+
85
+ // --- BalloonPop.js inlined ---
86
+ const DART_SPEED = 15.0;
87
+ const DART_GRAVITY_SCALE = 0.1;
88
+ const MENU_WIDTH = 0.85;
89
+ const PARTICLE_COUNT = 30;
90
+ const PARTICLE_LIFE = 1.0;
91
+ const BOUNDARY_RADIUS = 7.62;
92
+ const BOUNDARY_IMPULSE = 0.02;
93
+
94
+ const GROUP_WORLD = (0x0001 << 16) | (0x0002 | 0x0004);
95
+ const GROUP_BALLOON = (0x0002 << 16) | (0x0001 | 0x0002 | 0x0004);
96
+ const GROUP_DART = (0x0004 << 16) | (0x0001 | 0x0002);
97
+
98
+ function createStepperControl(game, grid, labelText, valueRef, min, max, step, isCount) {
99
+ const H_BUTTON = 0.15;
100
+ const H_VALUE_LABEL = 0.12;
101
+ const menuHeight = game.menuPanel.height;
102
+ const getW = (h) => h / menuHeight;
103
+
104
+ grid.addRow({weight: getW(H_BUTTON)}).addTextButton({
105
+ text: '+', fontColor: '#ffffff', backgroundColor: '#4285f4', fontSize: 0.7, width: 0.3, weight: 1.0,
106
+ }).onTriggered = () => {
107
+ const maxVal = isCount ? max : 0.1;
108
+ game[valueRef] = Math.min(maxVal, game[valueRef] + step);
109
+ game.renderMenu();
110
+ };
111
+
112
+ const displayValue = isCount ? game[valueRef] : Math.round(game[valueRef] * 100);
113
+ const valueText = grid.addRow({weight: getW(H_VALUE_LABEL)}).addText({
114
+ text: `${labelText}: ${displayValue}`, fontColor: '#ffffff', fontSize: 0.12, textAlign: 'center',
115
+ });
116
+
117
+ if (isCount) game.countValueText = valueText;
118
+ else game.speedValueText = valueText;
119
+
120
+ grid.addRow({weight: getW(H_BUTTON)}).addTextButton({
121
+ text: '-', fontColor: '#ffffff', backgroundColor: '#4285f4', fontSize: 0.7, width: 0.3, weight: 1.0,
122
+ }).onTriggered = () => {
123
+ game[valueRef] = Math.max(min, game[valueRef] - step);
124
+ game.renderMenu();
125
+ };
126
+ grid.addRow({weight: getW(0.01)});
127
+ }
128
+
129
+ class BalloonGame extends xb.Script {
130
+ constructor() {
131
+ super();
132
+ this.balloons = new Map();
133
+ this.darts = new Map();
134
+ this.particles = [];
135
+ this.balloonCount = 10;
136
+ this.balloonSpeed = 0.03;
137
+ this.balloonsPopped = 0;
138
+ this.activeDart = null;
139
+ this.menuPanel = null;
140
+ this.isMenuExpanded = true;
141
+ this.physics = null;
142
+ this.physicsWorld = null;
143
+ this.RAPIER = null;
144
+ this.balloonModel = null;
145
+ this.dartModel = null;
146
+ this.particleGeometry = null;
147
+ this.particleMaterial = null;
148
+ this.raycaster = new THREE.Raycaster();
149
+ this.menuPos = new THREE.Vector3(0.6, 1.3, -1.0);
150
+ this.menuRot = new THREE.Euler(0, -0.4, 0);
151
+ }
152
+
153
+ async init() {
154
+ setTimeout(() => {
155
+ const options = xb.core.registry.get(xb.Options);
156
+ if (options && options.depth) {
157
+ options.depth.enabled = true;
158
+ if (options.depth.depthMesh) {
159
+ options.depth.depthMesh.enabled = true;
160
+ options.depth.depthMesh.physicsEnabled = true;
161
+ options.depth.depthMesh.collisionGroups = GROUP_WORLD;
162
+ }
163
+ }
164
+ }, 1000);
165
+
166
+ this.add(new THREE.HemisphereLight(0xffffff, 0xbbbbff, 3));
167
+ const dirLight = new THREE.DirectionalLight(0xffffff, 2);
168
+ dirLight.position.set(1, 2, 1);
169
+ this.add(dirLight);
170
+
171
+ this.createPrefabs();
172
+ this.renderMenu();
173
+ }
174
+
175
+ createPrefabs() {
176
+ this.dartModel = new THREE.Group();
177
+ const needleMat = new THREE.MeshStandardMaterial({color: 0xaaaaaa, metalness: 1.0, roughness: 0.1});
178
+ const silverMat = new THREE.MeshStandardMaterial({color: 0xcccccc, metalness: 0.8, roughness: 0.3});
179
+ const redMat = new THREE.MeshStandardMaterial({color: 0xaa0000, roughness: 0.5});
180
+ const finMat = new THREE.MeshStandardMaterial({color: 0xcc0000, roughness: 0.6});
181
+ const needle = new THREE.Mesh(new THREE.ConeGeometry(0.002, 0.04, 6), needleMat);
182
+ needle.position.y = 0.17;
183
+ const tipHolder = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.012, 0.03, 8), silverMat);
184
+ tipHolder.position.y = 0.135;
185
+ const body = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 0.18, 8), redMat);
186
+ body.position.y = 0.03;
187
+ const createFin = (rotationY) => {
188
+ const fin = new THREE.Mesh(new THREE.BoxGeometry(0.07, 0.05, 0.002, 1, 1, 1), finMat);
189
+ fin.position.set(0, -0.05, 0);
190
+ fin.rotation.set(Math.PI, rotationY, 0);
191
+ return fin;
192
+ };
193
+ this.dartModel.add(needle, tipHolder, body);
194
+ this.dartModel.add(createFin(0));
195
+ this.dartModel.add(createFin(Math.PI / 2));
196
+ this.dartModel.add(createFin(Math.PI));
197
+ this.dartModel.add(createFin(Math.PI * 1.5));
198
+
199
+ this.balloonModel = new THREE.Group();
200
+ const balloonGeo = new THREE.SphereGeometry(0.5, 32, 32);
201
+ const pos = balloonGeo.attributes.position;
202
+ const v = new THREE.Vector3();
203
+ for (let i = 0; i < pos.count; i++) {
204
+ v.fromBufferAttribute(pos, i);
205
+ if (v.y < 0) { const t = 1.0 - Math.abs(v.y) * 0.35; v.x *= t; v.z *= t; }
206
+ pos.setXYZ(i, v.x, v.y, v.z);
207
+ }
208
+ balloonGeo.computeVertexNormals();
209
+ const balloonMat = new THREE.MeshStandardMaterial({color: 0xffffff, roughness: 0.2, metalness: 0.1, transparent: true, opacity: 0.85, side: THREE.FrontSide});
210
+ this.balloonModel.add(new THREE.Mesh(balloonGeo, balloonMat));
211
+ const knotGeo = new THREE.CylinderGeometry(0.03, 0.01, 0.12, 16);
212
+ knotGeo.translate(0, -0.54, 0);
213
+ this.balloonModel.add(new THREE.Mesh(knotGeo, balloonMat));
214
+ const ringGeo = new THREE.TorusGeometry(0.035, 0.015, 8, 24);
215
+ ringGeo.rotateX(Math.PI / 2);
216
+ ringGeo.translate(0, -0.6, 0);
217
+ this.balloonModel.add(new THREE.Mesh(ringGeo, balloonMat));
218
+
219
+ this.particleGeometry = new THREE.PlaneGeometry(0.08, 0.08);
220
+ this.particleMaterial = new THREE.MeshBasicMaterial({color: 0xffffff, transparent: true, opacity: 1.0, side: THREE.DoubleSide, blending: THREE.AdditiveBlending});
221
+ }
222
+
223
+ initPhysics(physics) {
224
+ this.physics = physics;
225
+ this.physicsWorld = physics.blendedWorld;
226
+ this.RAPIER = physics.RAPIER;
227
+ this.spawnBalloons();
228
+ this.renderMenu();
229
+ }
230
+
231
+ renderMenu() {
232
+ if (this.menuPanel) {
233
+ this.menuPos.copy(this.menuPanel.position);
234
+ this.menuRot.copy(this.menuPanel.rotation);
235
+ this.remove(this.menuPanel);
236
+ }
237
+ const H_TOGGLE = 0.1, H_SCORE = 0.15, H_RESET = 0.15, H_SPACE = 0.03, H_BUTTON = 0.15, H_VALUE_LABEL = 0.12;
238
+ const headerHeight = H_TOGGLE + H_SCORE + H_RESET + H_SPACE;
239
+ const expandedControlsHeight = (H_BUTTON + H_VALUE_LABEL + H_BUTTON + H_SPACE) * 2;
240
+ const menuHeight = this.isMenuExpanded ? headerHeight + expandedControlsHeight : headerHeight;
241
+ const getW = (h) => h / menuHeight;
242
+
243
+ this.menuPanel = new xb.SpatialPanel({width: MENU_WIDTH, height: menuHeight, backgroundColor: '#2b2b2baa', showEdge: true, edgeColor: 'white', edgeWidth: 0.001});
244
+ this.menuPanel.position.copy(this.menuPos);
245
+ this.menuPanel.rotation.copy(this.menuRot);
246
+ this.add(this.menuPanel);
247
+ const grid = this.menuPanel.addGrid();
248
+ grid.addRow({weight: getW(H_TOGGLE)}).addTextButton({text: this.isMenuExpanded ? '\u25B2' : '\u25BC', fontColor: '#ffffff', backgroundColor: '#444444', fontSize: 0.7, weight: 1.0}).onTriggered = () => this.toggleMenu();
249
+ this.scoreText = grid.addRow({weight: getW(H_SCORE)}).addText({text: `${this.balloonsPopped} / ${this.balloonCount}`, fontColor: '#ffffff', fontSize: 0.15, textAlign: 'center'});
250
+ grid.addRow({weight: getW(H_RESET)}).addTextButton({text: '\u21BB', fontColor: '#ffffff', backgroundColor: '#d93025', fontSize: 0.7, weight: 1.0}).onTriggered = () => this.resetGame();
251
+ grid.addRow({weight: getW(H_SPACE)});
252
+ if (this.isMenuExpanded) {
253
+ createStepperControl(this, grid, 'Balloons', 'balloonCount', 5, 30, 1, true);
254
+ createStepperControl(this, grid, 'Speed', 'balloonSpeed', 0.0, 0.1, 0.01, false);
255
+ }
256
+ }
257
+
258
+ toggleMenu() { this.isMenuExpanded = !this.isMenuExpanded; this.renderMenu(); }
259
+ updateScoreDisplay() { if (this.scoreText) this.scoreText.text = `${this.balloonsPopped} / ${this.balloonCount}`; }
260
+ resetGame() { this.spawnBalloons(); this.renderMenu(); }
261
+
262
+ spawnBalloons() {
263
+ if (!this.physicsWorld || !this.RAPIER || !this.balloonModel) return;
264
+ this.clearBalloons();
265
+ this.balloonsPopped = 0;
266
+ this.updateScoreDisplay();
267
+ const color = new THREE.Color();
268
+ for (let i = 0; i < this.balloonCount; i++) {
269
+ const grp = this.balloonModel.clone();
270
+ const x = (Math.random() - 0.5) * 4, y = 1.5 + Math.random() * 1, z = -2 - Math.random() * 2;
271
+ grp.position.set(x, y, z);
272
+ const s = 0.7 + Math.random() * 0.6;
273
+ grp.scale.set(s, s, s);
274
+ color.setHSL(Math.random(), 0.95, 0.6);
275
+ grp.traverse((c) => { if (c.isMesh) { c.material = c.material.clone(); c.material.color.copy(color); } });
276
+ const rb = this.physicsWorld.createRigidBody(this.RAPIER.RigidBodyDesc.dynamic().setTranslation(x, y, z).setGravityScale(-0.05 * s).setLinearDamping(0.5).setAngularDamping(0.5));
277
+ const col = this.physicsWorld.createCollider(this.RAPIER.ColliderDesc.ball(0.5 * s).setActiveEvents(this.RAPIER.ActiveEvents.COLLISION_EVENTS).setRestitution(0.85).setDensity(0.1).setCollisionGroups(GROUP_BALLOON), rb);
278
+ this.balloons.set(col.handle, {mesh: grp, rigidBody: rb, collider: col, color: color.clone()});
279
+ this.add(grp);
280
+ }
281
+ }
282
+
283
+ clearBalloons() {
284
+ if (!this.physicsWorld) return;
285
+ for (const [h, b] of this.balloons.entries()) { this.remove(b.mesh); this.physicsWorld.removeCollider(b.collider, false); this.physicsWorld.removeRigidBody(b.rigidBody); }
286
+ this.balloons.clear();
287
+ }
288
+
289
+ spawnExplosion(position, color) {
290
+ for (let i = 0; i < PARTICLE_COUNT; i++) {
291
+ const mat = this.particleMaterial.clone();
292
+ mat.color.copy(color);
293
+ const mesh = new THREE.Mesh(this.particleGeometry, mat);
294
+ mesh.position.copy(position);
295
+ this.add(mesh);
296
+ this.particles.push({mesh, velocity: new THREE.Vector3((Math.random() - 0.5) * 4.0, (Math.random() - 0.5) * 4.0, (Math.random() - 0.5) * 4.0), life: PARTICLE_LIFE});
297
+ }
298
+ }
299
+
300
+ onSelectStart(event) {
301
+ if (this.menuPanel && this.menuPanel.parent) {
302
+ const ctrl = event.target, pos = new THREE.Vector3(), quat = new THREE.Quaternion();
303
+ ctrl.getWorldPosition(pos);
304
+ ctrl.getWorldQuaternion(quat);
305
+ this.raycaster.set(pos, new THREE.Vector3(0, 0, -1).applyQuaternion(quat));
306
+ if (this.raycaster.intersectObject(this.menuPanel, true).length > 0) return;
307
+ }
308
+ if (this.activeDart) return;
309
+ this.activeDart = this.dartModel.clone();
310
+ this.activeDart.position.set(0, -0.05, -0.15);
311
+ this.activeDart.rotation.set(-Math.PI / 2, 0, 0);
312
+ event.target.add(this.activeDart);
313
+ }
314
+
315
+ onSelectEnd(event) {
316
+ const ctrl = event.target;
317
+ if (!this.activeDart || !this.physicsWorld) return;
318
+ const dart = this.activeDart;
319
+ this.activeDart = null;
320
+ const wPos = new THREE.Vector3(), wQuat = new THREE.Quaternion();
321
+ dart.getWorldPosition(wPos);
322
+ dart.getWorldQuaternion(wQuat);
323
+ ctrl.remove(dart);
324
+ dart.position.copy(wPos);
325
+ dart.quaternion.copy(wQuat);
326
+ this.add(dart);
327
+ playWhooshSound();
328
+ const rb = this.physicsWorld.createRigidBody(this.RAPIER.RigidBodyDesc.dynamic().setTranslation(wPos.x, wPos.y, wPos.z).setRotation(wQuat).setGravityScale(DART_GRAVITY_SCALE).setCcdEnabled(true));
329
+ const col = this.physicsWorld.createCollider(this.RAPIER.ColliderDesc.capsule(0.1, 0.015).setActiveEvents(this.RAPIER.ActiveEvents.COLLISION_EVENTS).setSensor(true).setCollisionGroups(GROUP_DART), rb);
330
+ rb.setLinvel(new THREE.Vector3(0, 1, 0).applyQuaternion(wQuat).multiplyScalar(DART_SPEED), true);
331
+ this.darts.set(col.handle, {mesh: dart, rigidBody: rb, collider: col});
332
+ }
333
+
334
+ update(time, delta) {
335
+ if (this.physicsWorld) {
336
+ for (const [h, b] of this.balloons.entries()) { b.mesh.position.copy(b.rigidBody.translation()); b.mesh.quaternion.copy(b.rigidBody.rotation()); }
337
+ for (const [h, d] of this.darts.entries()) {
338
+ d.mesh.position.copy(d.rigidBody.translation());
339
+ d.mesh.quaternion.copy(d.rigidBody.rotation());
340
+ if (d.mesh.position.y < -5 || Math.abs(d.mesh.position.z) > 30) this.removeDart(h);
341
+ }
342
+ }
343
+ const dt = delta || 1 / 60;
344
+ for (let i = this.particles.length - 1; i >= 0; i--) {
345
+ const p = this.particles[i];
346
+ p.life -= dt;
347
+ if (p.life <= 0) { this.remove(p.mesh); this.particles.splice(i, 1); }
348
+ else { p.mesh.position.addScaledVector(p.velocity, dt); p.mesh.material.opacity = p.life / PARTICLE_LIFE; if (xb.core.camera) p.mesh.lookAt(xb.core.camera.position); }
349
+ }
350
+ }
351
+
352
+ physicsStep() {
353
+ if (!this.physics || !this.physicsWorld || !xb.core.camera) return;
354
+ const camPos = xb.core.camera.position;
355
+ const speedFactor = this.balloonSpeed / 10;
356
+ for (const [h, b] of this.balloons.entries()) {
357
+ const s = speedFactor;
358
+ b.rigidBody.addForce({x: (Math.random() - 0.5) * s, y: (Math.random() - 0.5) * (s * 0.5), z: (Math.random() - 0.5) * s}, true);
359
+ const p = b.rigidBody.translation();
360
+ const dx = p.x - camPos.x, dz = p.z - camPos.z;
361
+ const dist = Math.sqrt(dx * dx + dz * dz);
362
+ if (dist > BOUNDARY_RADIUS) b.rigidBody.applyImpulse({x: (-dx / dist) * BOUNDARY_IMPULSE, y: 0, z: (-dz / dist) * BOUNDARY_IMPULSE}, true);
363
+ if (p.y > 5.0) b.rigidBody.applyImpulse({x: 0, y: -BOUNDARY_IMPULSE, z: 0}, true);
364
+ if (p.y < 0.5) b.rigidBody.applyImpulse({x: 0, y: BOUNDARY_IMPULSE, z: 0}, true);
365
+ }
366
+ this.physics.eventQueue.drainCollisionEvents((h1, h2, s) => {
367
+ if (!s) return;
368
+ if (this.darts.has(h1) && this.balloons.has(h2)) { this.popBalloon(h2); this.removeDart(h1); }
369
+ else if (this.darts.has(h2) && this.balloons.has(h1)) { this.popBalloon(h1); this.removeDart(h2); }
370
+ });
371
+ }
372
+
373
+ popBalloon(h) {
374
+ const b = this.balloons.get(h);
375
+ if (b) { this.spawnExplosion(b.mesh.position.clone(), b.color); playPopSound(); }
376
+ this.removeBalloon(h);
377
+ this.balloonsPopped++;
378
+ this.updateScoreDisplay();
379
+ }
380
+ removeBalloon(h) {
381
+ const b = this.balloons.get(h);
382
+ if (!b) return;
383
+ this.remove(b.mesh);
384
+ this.physicsWorld.removeCollider(b.collider, false);
385
+ this.physicsWorld.removeRigidBody(b.rigidBody);
386
+ this.balloons.delete(h);
387
+ }
388
+ removeDart(h) {
389
+ const d = this.darts.get(h);
390
+ if (!d) return;
391
+ this.remove(d.mesh);
392
+ this.physicsWorld.removeCollider(d.collider, false);
393
+ this.physicsWorld.removeRigidBody(d.rigidBody);
394
+ this.darts.delete(h);
395
+ }
396
+ }
397
+
398
+ document.addEventListener('DOMContentLoaded', () => {
399
+ const o = new xb.Options();
400
+ o.simulator.instructions.enabled = false;
401
+ o.enableUI();
402
+ o.physics.RAPIER = RAPIER;
403
+ o.physics.useEventQueue = true;
404
+ o.physics.worldStep = true;
405
+ o.hands.enabled = true;
406
+ o.simulator.defaultMode = xb.SimulatorMode.POSE;
407
+
408
+ o.depth.enabled = false;
409
+ if (o.depth.depthMesh) {
410
+ o.depth.depthMesh.enabled = true;
411
+ o.depth.depthMesh.physicsEnabled = true;
412
+ o.depth.depthMesh.collisionGroups = GROUP_WORLD;
413
+ o.depth.depthMesh.colliderUpdateFps = 5;
414
+ }
415
+
416
+ xb.add(new BalloonGame());
417
+ xb.init(o);
418
+ });
419
+
420
+ </script>
421
+ </body>
422
+ </html>