minecraft-renderer 0.1.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 (187) hide show
  1. package/README.md +297 -0
  2. package/dist/index.html +83 -0
  3. package/dist/static/image/arrow.6f27b59f.png +0 -0
  4. package/dist/static/image/blocksAtlasLatest.7850afa3.png +0 -0
  5. package/dist/static/image/blocksAtlasLegacy.5c76823d.png +0 -0
  6. package/dist/static/image/itemsAtlasLatest.36036f95.png +0 -0
  7. package/dist/static/image/itemsAtlasLegacy.dcb1b58d.png +0 -0
  8. package/dist/static/image/tipped_arrow.6f27b59f.png +0 -0
  9. package/dist/static/js/365.f05233ab.js +8462 -0
  10. package/dist/static/js/365.f05233ab.js.LICENSE.txt +52 -0
  11. package/dist/static/js/async/738.efa27644.js +1 -0
  12. package/dist/static/js/index.092ec5be.js +56 -0
  13. package/dist/static/js/lib-polyfill.98986ac5.js +1 -0
  14. package/dist/static/js/lib-react.5c9129e0.js +2 -0
  15. package/dist/static/js/lib-react.5c9129e0.js.LICENSE.txt +39 -0
  16. package/package.json +104 -0
  17. package/src/assets/destroy_stage_0.png +0 -0
  18. package/src/assets/destroy_stage_1.png +0 -0
  19. package/src/assets/destroy_stage_2.png +0 -0
  20. package/src/assets/destroy_stage_3.png +0 -0
  21. package/src/assets/destroy_stage_4.png +0 -0
  22. package/src/assets/destroy_stage_5.png +0 -0
  23. package/src/assets/destroy_stage_6.png +0 -0
  24. package/src/assets/destroy_stage_7.png +0 -0
  25. package/src/assets/destroy_stage_8.png +0 -0
  26. package/src/assets/destroy_stage_9.png +0 -0
  27. package/src/examples/README.md +146 -0
  28. package/src/examples/appViewerExample.ts +205 -0
  29. package/src/examples/initialMenuStart.ts +161 -0
  30. package/src/graphicsBackend/appViewer.ts +297 -0
  31. package/src/graphicsBackend/config.ts +119 -0
  32. package/src/graphicsBackend/index.ts +10 -0
  33. package/src/graphicsBackend/playerState.ts +61 -0
  34. package/src/graphicsBackend/types.ts +143 -0
  35. package/src/index.ts +97 -0
  36. package/src/lib/DebugGui.ts +190 -0
  37. package/src/lib/animationController.ts +85 -0
  38. package/src/lib/buildSharedConfig.mjs +1 -0
  39. package/src/lib/cameraBobbing.ts +94 -0
  40. package/src/lib/canvas2DOverlay.example.ts +361 -0
  41. package/src/lib/canvas2DOverlay.quickstart.ts +242 -0
  42. package/src/lib/canvas2DOverlay.ts +381 -0
  43. package/src/lib/cleanupDecorator.ts +29 -0
  44. package/src/lib/createPlayerObject.ts +55 -0
  45. package/src/lib/frameTimingCollector.ts +164 -0
  46. package/src/lib/guiRenderer.ts +283 -0
  47. package/src/lib/items.ts +140 -0
  48. package/src/lib/mesherlogReader.ts +131 -0
  49. package/src/lib/moreBlockDataGenerated.json +714 -0
  50. package/src/lib/preflatMap.json +1741 -0
  51. package/src/lib/simpleUtils.ts +40 -0
  52. package/src/lib/smoothSwitcher.ts +168 -0
  53. package/src/lib/spiral.ts +29 -0
  54. package/src/lib/ui/newStats.ts +120 -0
  55. package/src/lib/utils/proxy.ts +23 -0
  56. package/src/lib/utils/skins.ts +63 -0
  57. package/src/lib/utils.ts +76 -0
  58. package/src/lib/workerProxy.ts +342 -0
  59. package/src/lib/worldrendererCommon.ts +1088 -0
  60. package/src/mesher/mesher.ts +253 -0
  61. package/src/mesher/models.ts +769 -0
  62. package/src/mesher/modelsGeometryCommon.ts +142 -0
  63. package/src/mesher/shared.ts +80 -0
  64. package/src/mesher/standaloneRenderer.ts +270 -0
  65. package/src/mesher/test/a.ts +3 -0
  66. package/src/mesher/test/mesherTester.ts +76 -0
  67. package/src/mesher/test/playground.ts +19 -0
  68. package/src/mesher/test/test-perf.ts +74 -0
  69. package/src/mesher/test/tests.test.ts +56 -0
  70. package/src/mesher/world.ts +294 -0
  71. package/src/mesher/worldConstants.ts +1 -0
  72. package/src/modules/index.ts +11 -0
  73. package/src/modules/starfield.ts +313 -0
  74. package/src/modules/types.ts +110 -0
  75. package/src/playerState/playerState.ts +78 -0
  76. package/src/playerState/types.ts +36 -0
  77. package/src/playground/allEntitiesDebug.ts +170 -0
  78. package/src/playground/baseScene.ts +587 -0
  79. package/src/playground/mobileControls.tsx +268 -0
  80. package/src/playground/playground.html +83 -0
  81. package/src/playground/playground.ts +11 -0
  82. package/src/playground/playgroundUi.tsx +140 -0
  83. package/src/playground/reactUtils.ts +71 -0
  84. package/src/playground/scenes/allEntities.ts +13 -0
  85. package/src/playground/scenes/entities.ts +37 -0
  86. package/src/playground/scenes/floorRandom.ts +33 -0
  87. package/src/playground/scenes/frequentUpdates.ts +148 -0
  88. package/src/playground/scenes/geometryExport.ts +142 -0
  89. package/src/playground/scenes/index.ts +12 -0
  90. package/src/playground/scenes/lightingStarfield.ts +40 -0
  91. package/src/playground/scenes/main.ts +313 -0
  92. package/src/playground/scenes/railsCobweb.ts +14 -0
  93. package/src/playground/scenes/rotationIssue.ts +7 -0
  94. package/src/playground/scenes/slabsOptimization.ts +16 -0
  95. package/src/playground/scenes/transparencyIssue.ts +11 -0
  96. package/src/playground/shared.ts +79 -0
  97. package/src/resourcesManager/index.ts +5 -0
  98. package/src/resourcesManager/resourcesManager.ts +314 -0
  99. package/src/shims/minecraftData.ts +41 -0
  100. package/src/sign-renderer/index.html +21 -0
  101. package/src/sign-renderer/index.ts +216 -0
  102. package/src/sign-renderer/noop.js +1 -0
  103. package/src/sign-renderer/playground.ts +38 -0
  104. package/src/sign-renderer/tests.test.ts +69 -0
  105. package/src/sign-renderer/vite.config.ts +10 -0
  106. package/src/three/appShared.ts +75 -0
  107. package/src/three/bannerRenderer.ts +275 -0
  108. package/src/three/cameraShake.ts +120 -0
  109. package/src/three/cinimaticScript.ts +350 -0
  110. package/src/three/documentRenderer.ts +491 -0
  111. package/src/three/entities.ts +1580 -0
  112. package/src/three/entity/EntityMesh.ts +707 -0
  113. package/src/three/entity/animations.js +171 -0
  114. package/src/three/entity/armorModels.json +204 -0
  115. package/src/three/entity/armorModels.ts +36 -0
  116. package/src/three/entity/entities.json +6230 -0
  117. package/src/three/entity/exportedModels.js +38 -0
  118. package/src/three/entity/externalTextures.json +1 -0
  119. package/src/three/entity/models/allay.obj +325 -0
  120. package/src/three/entity/models/arrow.obj +60 -0
  121. package/src/three/entity/models/axolotl.obj +509 -0
  122. package/src/three/entity/models/blaze.obj +601 -0
  123. package/src/three/entity/models/boat.obj +417 -0
  124. package/src/three/entity/models/camel.obj +1061 -0
  125. package/src/three/entity/models/cat.obj +509 -0
  126. package/src/three/entity/models/chicken.obj +371 -0
  127. package/src/three/entity/models/cod.obj +371 -0
  128. package/src/three/entity/models/creeper.obj +279 -0
  129. package/src/three/entity/models/dolphin.obj +371 -0
  130. package/src/three/entity/models/ender_dragon.obj +2993 -0
  131. package/src/three/entity/models/enderman.obj +325 -0
  132. package/src/three/entity/models/endermite.obj +187 -0
  133. package/src/three/entity/models/fox.obj +463 -0
  134. package/src/three/entity/models/frog.obj +739 -0
  135. package/src/three/entity/models/ghast.obj +463 -0
  136. package/src/three/entity/models/goat.obj +601 -0
  137. package/src/three/entity/models/guardian.obj +1015 -0
  138. package/src/three/entity/models/horse.obj +1061 -0
  139. package/src/three/entity/models/llama.obj +509 -0
  140. package/src/three/entity/models/minecart.obj +233 -0
  141. package/src/three/entity/models/parrot.obj +509 -0
  142. package/src/three/entity/models/piglin.obj +739 -0
  143. package/src/three/entity/models/pillager.obj +371 -0
  144. package/src/three/entity/models/rabbit.obj +555 -0
  145. package/src/three/entity/models/sheep.obj +555 -0
  146. package/src/three/entity/models/shulker.obj +141 -0
  147. package/src/three/entity/models/sniffer.obj +693 -0
  148. package/src/three/entity/models/spider.obj +509 -0
  149. package/src/three/entity/models/tadpole.obj +95 -0
  150. package/src/three/entity/models/turtle.obj +371 -0
  151. package/src/three/entity/models/vex.obj +325 -0
  152. package/src/three/entity/models/villager.obj +509 -0
  153. package/src/three/entity/models/warden.obj +463 -0
  154. package/src/three/entity/models/witch.obj +647 -0
  155. package/src/three/entity/models/wolf.obj +509 -0
  156. package/src/three/entity/models/zombie_villager.obj +463 -0
  157. package/src/three/entity/objModels.js +1 -0
  158. package/src/three/fireworks.ts +661 -0
  159. package/src/three/fireworksRenderer.ts +434 -0
  160. package/src/three/globals.d.ts +7 -0
  161. package/src/three/graphicsBackend.ts +274 -0
  162. package/src/three/graphicsBackendOffThread.ts +107 -0
  163. package/src/three/hand.ts +89 -0
  164. package/src/three/holdingBlock.ts +926 -0
  165. package/src/three/index.ts +20 -0
  166. package/src/three/itemMesh.ts +427 -0
  167. package/src/three/modules.d.ts +14 -0
  168. package/src/three/panorama.ts +308 -0
  169. package/src/three/panoramaShared.ts +1 -0
  170. package/src/three/renderSlot.ts +82 -0
  171. package/src/three/skyboxRenderer.ts +406 -0
  172. package/src/three/starField.ts +13 -0
  173. package/src/three/threeJsMedia.ts +731 -0
  174. package/src/three/threeJsMethods.ts +15 -0
  175. package/src/three/threeJsParticles.ts +160 -0
  176. package/src/three/threeJsSound.ts +95 -0
  177. package/src/three/threeJsUtils.ts +90 -0
  178. package/src/three/waypointSprite.ts +435 -0
  179. package/src/three/waypoints.ts +163 -0
  180. package/src/three/world/cursorBlock.ts +172 -0
  181. package/src/three/world/vr.ts +257 -0
  182. package/src/three/worldGeometryExport.ts +259 -0
  183. package/src/three/worldGeometryHandler.ts +279 -0
  184. package/src/three/worldRendererThree.ts +1381 -0
  185. package/src/worldView/index.ts +6 -0
  186. package/src/worldView/types.ts +66 -0
  187. package/src/worldView/worldView.ts +424 -0
@@ -0,0 +1,268 @@
1
+ import { CSSProperties, PointerEvent, useEffect, useRef } from 'react'
2
+ import { proxy, ref, useSnapshot } from 'valtio'
3
+
4
+ export type ButtonName = 'jump' | 'sneak'
5
+
6
+ export const joystickPointer = proxy({
7
+ pointer: null as { x: number, y: number, pointerId: number } | null,
8
+ joystickInner: null as HTMLDivElement | null,
9
+ })
10
+
11
+ export const cameraPointer = proxy({
12
+ pointer: null as { x: number, y: number, pointerId: number } | null,
13
+ })
14
+
15
+ export const handleMovementStickDelta = (e?: { clientX: number, clientY: number }) => {
16
+ const max = 32
17
+ let x = 0
18
+ let y = 0
19
+
20
+ if (e && joystickPointer.pointer) {
21
+ x = e.clientX - joystickPointer.pointer.x
22
+ y = e.clientY - joystickPointer.pointer.y
23
+ x = Math.min(Math.max(x, -max), max)
24
+ y = Math.min(Math.max(y, -max), max)
25
+ }
26
+
27
+ if (joystickPointer.joystickInner) {
28
+ joystickPointer.joystickInner.style.transform = `translate(${x}px, ${y}px)`
29
+ }
30
+
31
+ const vector = {
32
+ x: x / max,
33
+ y: 0,
34
+ z: y / max,
35
+ }
36
+
37
+ console.log('Movement vector:', vector)
38
+ }
39
+
40
+ export const handleCameraRotation = (deltaX: number, deltaY: number) => {
41
+ console.log('Camera rotation:', { deltaX, deltaY })
42
+ }
43
+
44
+ export const MobileControls = () => {
45
+ const usingTouch = navigator.maxTouchPoints > 0
46
+ const joystickOuter = useRef<HTMLDivElement>(null)
47
+ const joystickInner = useRef<HTMLDivElement>(null)
48
+ const { pointer: movementPointer } = useSnapshot(joystickPointer)
49
+ const { pointer: cameraPointerState } = useSnapshot(cameraPointer)
50
+
51
+ const joystickSize = 80
52
+ const Z_INDEX_INTERACTIBLE = 8
53
+
54
+ const buttonProps = (name: ButtonName) => {
55
+ const holdDown = {
56
+ jump() {
57
+ console.log('Jump button pressed')
58
+ },
59
+ sneak() {
60
+ console.log('Sneak button pressed')
61
+ }
62
+ }
63
+
64
+ const holdUp = {
65
+ jump() {
66
+ console.log('Jump button released')
67
+ },
68
+ sneak() {
69
+ console.log('Sneak button released')
70
+ }
71
+ }
72
+
73
+ type PType = PointerEvent<HTMLDivElement>
74
+
75
+ const pointerup = (e: PType) => {
76
+ const elem = e.currentTarget as HTMLElement
77
+ elem.releasePointerCapture(e.pointerId)
78
+ holdUp[name]()
79
+ elem.style.background = 'rgba(0, 0, 0, 0.5)'
80
+ }
81
+
82
+ const buttonPositions = {
83
+ jump: [85, 60],
84
+ sneak: [85, 75],
85
+ }
86
+
87
+ const buttonIcons = {
88
+ jump: '↑',
89
+ sneak: '↓',
90
+ }
91
+
92
+ return {
93
+ style: {
94
+ position: 'fixed',
95
+ left: `${buttonPositions[name][0]}%`,
96
+ top: `${buttonPositions[name][1]}%`,
97
+ borderRadius: '50%',
98
+ width: '50px',
99
+ height: '50px',
100
+ background: 'rgba(0, 0, 0, 0.5)',
101
+ display: 'flex',
102
+ justifyContent: 'center',
103
+ alignItems: 'center',
104
+ transition: 'background 0.1s',
105
+ zIndex: Z_INDEX_INTERACTIBLE,
106
+ color: 'white',
107
+ fontSize: '16px',
108
+ userSelect: 'none',
109
+ transform: 'translate(-50%, -50%)',
110
+ border: '2px solid rgba(255, 255, 255, 0.3)',
111
+ } satisfies CSSProperties,
112
+ onPointerDown(e: PType) {
113
+ const elem = e.currentTarget as HTMLElement
114
+ elem.setPointerCapture(e.pointerId)
115
+ holdDown[name]()
116
+ elem.style.background = 'rgba(0, 0, 0, 0.8)'
117
+ },
118
+ onPointerUp: pointerup,
119
+ onLostPointerCapture: pointerup,
120
+ children: buttonIcons[name],
121
+ }
122
+ }
123
+
124
+ useEffect(() => {
125
+ if (joystickInner.current) {
126
+ joystickPointer.joystickInner = ref(joystickInner.current)
127
+ }
128
+ }, [])
129
+
130
+ if (!usingTouch) return null
131
+
132
+ return <div>
133
+ {/* Movement Joystick */}
134
+ <div
135
+ className='movement_joystick_outer'
136
+ ref={joystickOuter}
137
+ style={{
138
+ display: movementPointer ? 'flex' : 'none',
139
+ borderRadius: '50%',
140
+ width: joystickSize,
141
+ height: joystickSize,
142
+ border: '2px solid rgba(0, 0, 0, 0.5)',
143
+ backgroundColor: 'rgba(255, 255, 255, 0.3)',
144
+ position: 'fixed',
145
+ justifyContent: 'center',
146
+ alignItems: 'center',
147
+ transform: 'translate(-50%, -50%)',
148
+ zIndex: Z_INDEX_INTERACTIBLE,
149
+ ...(movementPointer ? {
150
+ left: `${movementPointer.x / window.innerWidth * 100}%`,
151
+ top: `${movementPointer.y / window.innerHeight * 100}%`
152
+ } : {}),
153
+ }}
154
+ >
155
+ <div
156
+ className='movement_joystick_inner'
157
+ style={{
158
+ borderRadius: '50%',
159
+ width: joystickSize * 0.35,
160
+ height: joystickSize * 0.35,
161
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
162
+ position: 'absolute',
163
+ }}
164
+ ref={joystickInner}
165
+ />
166
+ </div>
167
+
168
+ {/* Up/Down Action Buttons */}
169
+ <div {...buttonProps('jump')} />
170
+ <div {...buttonProps('sneak')} />
171
+
172
+ {/* Movement touch area (left half of screen) */}
173
+ <div
174
+ style={{
175
+ position: 'fixed',
176
+ left: '0',
177
+ top: '0',
178
+ width: '50%',
179
+ height: '100%',
180
+ zIndex: Z_INDEX_INTERACTIBLE - 1,
181
+ }}
182
+ onPointerDown={(e) => {
183
+ if (joystickPointer.pointer) return
184
+
185
+ joystickPointer.pointer = {
186
+ x: e.clientX,
187
+ y: e.clientY,
188
+ pointerId: e.pointerId,
189
+ }
190
+
191
+ const elem = e.currentTarget as HTMLElement
192
+ elem.setPointerCapture(e.pointerId)
193
+ }}
194
+ onPointerMove={(e) => {
195
+ if (!joystickPointer.pointer || e.pointerId !== joystickPointer.pointer.pointerId) return
196
+
197
+ handleMovementStickDelta({
198
+ clientX: e.clientX,
199
+ clientY: e.clientY,
200
+ })
201
+ }}
202
+ onPointerUp={(e) => {
203
+ if (!joystickPointer.pointer || e.pointerId !== joystickPointer.pointer.pointerId) return
204
+
205
+ joystickPointer.pointer = null
206
+ handleMovementStickDelta() // Reset position
207
+
208
+ const elem = e.currentTarget as HTMLElement
209
+ elem.releasePointerCapture(e.pointerId)
210
+ }}
211
+ onLostPointerCapture={(e) => {
212
+ if (!joystickPointer.pointer || e.pointerId !== joystickPointer.pointer.pointerId) return
213
+
214
+ joystickPointer.pointer = null
215
+ handleMovementStickDelta() // Reset position
216
+ }}
217
+ />
218
+
219
+ {/* Camera rotation touch area (right half of screen, excluding buttons) */}
220
+ <div
221
+ style={{
222
+ position: 'fixed',
223
+ right: '0',
224
+ top: '0',
225
+ width: '50%',
226
+ height: '100%',
227
+ zIndex: Z_INDEX_INTERACTIBLE - 2,
228
+ }}
229
+ onPointerDown={(e) => {
230
+ if (cameraPointer.pointer) return
231
+
232
+ cameraPointer.pointer = {
233
+ x: e.clientX,
234
+ y: e.clientY,
235
+ pointerId: e.pointerId,
236
+ }
237
+
238
+ const elem = e.currentTarget as HTMLElement
239
+ elem.setPointerCapture(e.pointerId)
240
+ }}
241
+ onPointerMove={(e) => {
242
+ if (!cameraPointer.pointer || e.pointerId !== cameraPointer.pointer.pointerId) return
243
+
244
+ const deltaX = e.clientX - cameraPointer.pointer.x
245
+ const deltaY = e.clientY - cameraPointer.pointer.y
246
+
247
+ handleCameraRotation(deltaX, deltaY)
248
+
249
+ // Update pointer position for continuous rotation
250
+ cameraPointer.pointer.x = e.clientX
251
+ cameraPointer.pointer.y = e.clientY
252
+ }}
253
+ onPointerUp={(e) => {
254
+ if (!cameraPointer.pointer || e.pointerId !== cameraPointer.pointer.pointerId) return
255
+
256
+ cameraPointer.pointer = null
257
+
258
+ const elem = e.currentTarget as HTMLElement
259
+ elem.releasePointerCapture(e.pointerId)
260
+ }}
261
+ onLostPointerCapture={(e) => {
262
+ if (!cameraPointer.pointer || e.pointerId !== cameraPointer.pointer.pointerId) return
263
+
264
+ cameraPointer.pointer = null
265
+ }}
266
+ />
267
+ </div>
268
+ }
@@ -0,0 +1,83 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Minecraft Renderer Playground</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ html, body {
15
+ width: 100%;
16
+ height: 100%;
17
+ overflow: hidden;
18
+ background: #1a1a1a;
19
+ }
20
+
21
+ #viewer-canvas {
22
+ position: fixed;
23
+ top: 0;
24
+ left: 0;
25
+ width: 100%;
26
+ height: 100%;
27
+ }
28
+
29
+ .lil-gui {
30
+ position: fixed !important;
31
+ top: 10px;
32
+ right: 10px;
33
+ z-index: 1000;
34
+ }
35
+
36
+ .loading-overlay {
37
+ position: fixed;
38
+ top: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ height: 100%;
42
+ background: #1a1a1a;
43
+ display: flex;
44
+ flex-direction: column;
45
+ justify-content: center;
46
+ align-items: center;
47
+ z-index: 10000;
48
+ color: #fff;
49
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
50
+ }
51
+
52
+ .loading-overlay.hidden {
53
+ display: none;
54
+ }
55
+
56
+ .loading-spinner {
57
+ width: 50px;
58
+ height: 50px;
59
+ border: 3px solid #333;
60
+ border-top-color: #4ade80;
61
+ border-radius: 50%;
62
+ animation: spin 1s linear infinite;
63
+ }
64
+
65
+ @keyframes spin {
66
+ to { transform: rotate(360deg); }
67
+ }
68
+
69
+ .loading-text {
70
+ margin-top: 16px;
71
+ font-size: 14px;
72
+ color: #888;
73
+ }
74
+ </style>
75
+ </head>
76
+ <body>
77
+ <div id="react-root"></div>
78
+ <div class="loading-overlay" id="loading">
79
+ <div class="loading-spinner"></div>
80
+ <div class="loading-text">Loading Minecraft Renderer...</div>
81
+ </div>
82
+ </body>
83
+ </html>
@@ -0,0 +1,11 @@
1
+ import { BasePlaygroundScene } from './baseScene'
2
+ import { playgroundGlobalUiState } from './playgroundUi'
3
+ import * as scenes from './scenes'
4
+
5
+ const qsScene = new URLSearchParams(window.location.search).get('scene')
6
+ const Scene: typeof BasePlaygroundScene = qsScene ? scenes[qsScene] : scenes.main
7
+ playgroundGlobalUiState.scenes = ['main', 'railsCobweb', 'floorRandom', 'lightingStarfield', 'transparencyIssue', 'entities', 'frequentUpdates', 'slabsOptimization', 'allEntities', 'geometryExport']
8
+ playgroundGlobalUiState.selected = qsScene ?? 'main'
9
+
10
+ const scene = new Scene()
11
+ globalThis.scene = scene
@@ -0,0 +1,140 @@
1
+ import { isMobile } from '@/lib/simpleUtils'
2
+ import useLongPress from '@/playground/reactUtils'
3
+ import { renderToDom } from '@zardoy/react-util'
4
+ import { useEffect, useState } from 'react'
5
+ import { proxy, useSnapshot } from 'valtio'
6
+ import { Vec3 } from 'vec3'
7
+ import { MobileControls } from './mobileControls'
8
+
9
+ export const playgroundGlobalUiState = proxy({
10
+ scenes: [] as string[],
11
+ selected: '',
12
+ selectorOpened: false,
13
+ actions: {} as Record<string, () => void>,
14
+ })
15
+
16
+
17
+ renderToDom(<Playground />, {
18
+ strictMode: false,
19
+ selector: '#react-root',
20
+ })
21
+
22
+ function Playground () {
23
+ useEffect(() => {
24
+ const style = document.createElement('style')
25
+ style.innerHTML = /* css */ `
26
+ .lil-gui {
27
+ top: 60px !important;
28
+ right: 0 !important;
29
+ }
30
+ `
31
+ document.body.appendChild(style)
32
+
33
+ // Hide the loading overlay once the playground UI is ready
34
+ const loadingOverlay = document.getElementById('loading')
35
+ if (loadingOverlay) {
36
+ loadingOverlay.classList.add('hidden')
37
+ }
38
+
39
+ return () => {
40
+ style.remove()
41
+ }
42
+ }, [])
43
+
44
+ return <div style={{
45
+ fontFamily: 'monospace',
46
+ color: 'white',
47
+ }}>
48
+ <Controls />
49
+ <SceneSelector />
50
+ <ActionsSelector />
51
+ </div>
52
+ }
53
+
54
+ function SceneSelector () {
55
+ const mobile = isMobile()
56
+ const { scenes, selected } = useSnapshot(playgroundGlobalUiState)
57
+ const [hidden, setHidden] = useState(false)
58
+ const longPressEvents = useLongPress(() => {
59
+ playgroundGlobalUiState.selectorOpened = true
60
+ }, () => { })
61
+
62
+ if (hidden) return null
63
+
64
+ return <div
65
+ style={{
66
+ position: 'fixed',
67
+ top: 0,
68
+ left: 0,
69
+ display: 'flex',
70
+ alignItems: 'flex-start',
71
+ }} {...longPressEvents}>
72
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
73
+ {scenes.map(scene => <div
74
+ key={scene}
75
+ style={{
76
+ padding: mobile ? '5px' : '2px 5px',
77
+ cursor: 'pointer',
78
+ userSelect: 'none',
79
+ background: scene === selected ? 'rgba(0, 0, 0, 0.5)' : 'rgba(0, 0, 0, 0.6)',
80
+ fontWeight: scene === selected ? 'bold' : 'normal',
81
+ }}
82
+ onClick={() => {
83
+ const qs = new URLSearchParams(window.location.search)
84
+ qs.set('scene', scene)
85
+ location.search = qs.toString()
86
+ }}
87
+ >{scene}</div>)}
88
+ </div>
89
+ <div
90
+ style={{
91
+ padding: mobile ? '5px' : '2px 5px',
92
+ cursor: 'pointer',
93
+ userSelect: 'none',
94
+ background: 'rgba(0, 0, 0, 0.6)',
95
+ fontSize: '14px',
96
+ lineHeight: '1',
97
+ }}
98
+ onClick={() => setHidden(true)}
99
+ >×</div>
100
+ </div>
101
+ }
102
+
103
+ const ActionsSelector = () => {
104
+ const { actions, selectorOpened } = useSnapshot(playgroundGlobalUiState)
105
+
106
+ if (!selectorOpened) return null
107
+ return <div style={{
108
+ position: 'fixed',
109
+ inset: 0,
110
+ background: 'rgba(0, 0, 0, 0.5)',
111
+ zIndex: 10,
112
+ display: 'flex',
113
+ flexDirection: 'column',
114
+ justifyContent: 'center',
115
+ alignItems: 'center',
116
+ gap: 5,
117
+ fontSize: 24,
118
+ }}>{Object.entries({
119
+ ...actions,
120
+ 'Close' () {
121
+ playgroundGlobalUiState.selectorOpened = false
122
+ }
123
+ }).map(([name, action]) => <div
124
+ key={name}
125
+ style={{
126
+ padding: '2px 5px',
127
+ cursor: 'pointer',
128
+ userSelect: 'none',
129
+ background: 'rgba(0, 0, 0, 0.5)',
130
+ }}
131
+ onClick={() => {
132
+ action()
133
+ playgroundGlobalUiState.selectorOpened = false
134
+ }}
135
+ >{name}</div>)}</div>
136
+ }
137
+
138
+ const Controls = () => {
139
+ return <MobileControls />
140
+ }
@@ -0,0 +1,71 @@
1
+ import { useCallback, useRef, useState } from 'react'
2
+
3
+ interface LongPressOptions {
4
+ shouldPreventDefault?: boolean;
5
+ delay?: number;
6
+ }
7
+
8
+ const useLongPress = (
9
+ onLongPress: () => void,
10
+ onClick: () => void,
11
+ { shouldPreventDefault = false, delay = 300 }: LongPressOptions = {}
12
+ ) => {
13
+ const [longPressTriggered, setLongPressTriggered] = useState(false)
14
+ const timeout = useRef<number | undefined>(null)
15
+ const target = useRef<EventTarget | null>(null)
16
+
17
+ const start = useCallback(
18
+ (event: React.MouseEvent | React.TouchEvent) => {
19
+ if (shouldPreventDefault && event.target) {
20
+ event.target.addEventListener('touchend', preventDefault, {
21
+ passive: false
22
+ })
23
+ target.current = event.target
24
+ }
25
+ timeout.current = window.setTimeout(() => {
26
+ onLongPress()
27
+ setLongPressTriggered(true)
28
+ }, delay)
29
+ },
30
+ [onLongPress, delay, shouldPreventDefault]
31
+ )
32
+
33
+ const clear = useCallback(
34
+ (event: React.MouseEvent | React.TouchEvent) => {
35
+ if (timeout.current) {
36
+ clearTimeout(timeout.current)
37
+ timeout.current = undefined
38
+ }
39
+
40
+ setLongPressTriggered(false)
41
+
42
+ if (shouldPreventDefault && target.current) {
43
+ target.current.removeEventListener('touchend', preventDefault)
44
+ target.current = null
45
+ }
46
+ },
47
+ [shouldPreventDefault, onClick, longPressTriggered]
48
+ )
49
+
50
+ return {
51
+ onMouseDown: (e: React.MouseEvent) => start(e),
52
+ onTouchStart: (e: React.TouchEvent) => start(e),
53
+ onMouseUp: (e: React.MouseEvent) => clear(e),
54
+ onMouseLeave: (e: React.MouseEvent) => clear(e),
55
+ onTouchEnd: (e: React.TouchEvent) => clear(e),
56
+ onClick(e: React.MouseEvent) {
57
+ onClick()
58
+ }
59
+ }
60
+ }
61
+
62
+ const preventDefault = (event: Event) => {
63
+ if (!('touches' in event)) return
64
+
65
+ const touchEvent = event as TouchEvent
66
+ if (touchEvent.touches.length < 2 && event.preventDefault) {
67
+ event.preventDefault()
68
+ }
69
+ }
70
+
71
+ export default useLongPress
@@ -0,0 +1,13 @@
1
+ import { BasePlaygroundScene } from '../baseScene'
2
+ import { EntityDebugFlags, EntityMesh, rendererSpecialHandled } from '../../viewer/three/entity/EntityMesh'
3
+ import { displayEntitiesDebugList } from '../allEntitiesDebug'
4
+
5
+ export default class AllEntities extends BasePlaygroundScene {
6
+ continuousRender = false
7
+ enableCameraControls = false
8
+
9
+ async initData () {
10
+ await super.initData()
11
+ displayEntitiesDebugList(this.version)
12
+ }
13
+ }
@@ -0,0 +1,37 @@
1
+ //@ts-nocheck
2
+ import * as THREE from 'three'
3
+ import { Vec3 } from 'vec3'
4
+ import { BasePlaygroundScene } from '../baseScene'
5
+ import { WorldRendererThree } from '../../viewer/three/worldrendererThree'
6
+
7
+ export default class extends BasePlaygroundScene {
8
+ continuousRender = true
9
+
10
+ override initGui (): void {
11
+ this.params = {
12
+ starfield: false,
13
+ entity: 'player',
14
+ count: 4
15
+ }
16
+ }
17
+
18
+ override renderFinish (): void {
19
+ if (this.params.starfield) {
20
+ ;(viewer.world as WorldRendererThree).scene.background = new THREE.Color(0x00_00_00)
21
+ ;(viewer.world as WorldRendererThree).starField.enabled = true
22
+ ;(viewer.world as WorldRendererThree).starField.addToScene()
23
+ }
24
+
25
+ for (let i = 0; i < this.params.count; i++) {
26
+ for (let j = 0; j < this.params.count; j++) {
27
+ for (let k = 0; k < this.params.count; k++) {
28
+ viewer.entities.update({
29
+ id: i * 1000 + j * 100 + k,
30
+ name: this.params.entity,
31
+ pos: this.targetPos.offset(i, j, k)
32
+ } as any, {})
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,33 @@
1
+ import { BasePlaygroundScene } from '../baseScene'
2
+
3
+ export default class RailsCobwebScene extends BasePlaygroundScene {
4
+ viewDistance = 5
5
+ continuousRender = true
6
+
7
+ override initGui (): void {
8
+ this.params = {
9
+ squareSize: 50
10
+ }
11
+
12
+ super.initGui()
13
+ }
14
+
15
+ setupWorld () {
16
+ const squareSize = this.params.squareSize ?? 30
17
+ const maxSquareSize = this.viewDistance * 16 * 2
18
+ if (squareSize > maxSquareSize) throw new Error(`Square size too big, max is ${maxSquareSize}`)
19
+ // const fullBlocks = loadedData.blocksArray.map(x => x.name)
20
+ const fullBlocks = loadedData.blocksArray.filter(block => {
21
+ const b = this.Block.fromStateId(block.defaultState, 0)
22
+ if (b.shapes?.length !== 1) return false
23
+ const shape = b.shapes[0]
24
+ return shape[0] === 0 && shape[1] === 0 && shape[2] === 0 && shape[3] === 1 && shape[4] === 1 && shape[5] === 1
25
+ })
26
+ for (let x = -squareSize; x <= squareSize; x++) {
27
+ for (let z = -squareSize; z <= squareSize; z++) {
28
+ const i = Math.abs(x + z) * squareSize
29
+ worldView!.world.setBlock(this.targetPos.offset(x, 0, z), this.Block.fromStateId(fullBlocks[i % fullBlocks.length].defaultState, 0))
30
+ }
31
+ }
32
+ }
33
+ }