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,274 @@
1
+ /**
2
+ * Three.js Graphics Backend - Main entry point for Three.js WebGL rendering.
3
+ *
4
+ * This module provides the GraphicsBackend implementation using Three.js.
5
+ * It creates and manages:
6
+ * - WebGL renderer (via DocumentRenderer)
7
+ * - World renderer (via WorldRendererThree - to be implemented)
8
+ * - Scene management
9
+ * - Camera updates
10
+ */
11
+
12
+ import * as THREE from 'three'
13
+ import { Vec3 } from 'vec3'
14
+ import type { GraphicsBackendLoader, GraphicsBackend, GraphicsInitOptions, DisplayWorldOptions } from '@/graphicsBackend'
15
+ import { createWorkerProxy, restoreTransferred } from '@/lib/workerProxy'
16
+ import { ResourcesManager } from '@/resourcesManager'
17
+ // import { WorldViewWorker } from '@/worldView'
18
+ import { FrameTimingCollector } from '@/lib/frameTimingCollector'
19
+ import { WorldRendererThree } from './worldRendererThree'
20
+ import { DocumentRenderer, isWebWorker, ThreeRendererMainData } from './documentRenderer'
21
+ import { PanoramaRenderer } from './panorama'
22
+
23
+ // Disable Three.js color management for compatibility
24
+ THREE.ColorManagement.enabled = false
25
+ // Enable Three.js in global scope for debugging
26
+ globalThis.THREE = THREE
27
+
28
+ const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
29
+ return {
30
+ updateMap: worldRenderer.entities.updateMap.bind(worldRenderer.entities),
31
+ updateCustomBlock: worldRenderer.updateCustomBlock.bind(worldRenderer),
32
+ getBlockInfo: worldRenderer.getBlockInfo.bind(worldRenderer),
33
+ playEntityAnimation: worldRenderer.entities.playAnimation.bind(worldRenderer.entities),
34
+ damageEntity: worldRenderer.entities.handleDamageEvent.bind(worldRenderer.entities),
35
+ updatePlayerSkin: worldRenderer.entities.updatePlayerSkin.bind(worldRenderer.entities),
36
+ changeHandSwingingState: worldRenderer.changeHandSwingingState.bind(worldRenderer),
37
+ getHighestBlocks: worldRenderer.getHighestBlocks.bind(worldRenderer),
38
+ reloadWorld: worldRenderer.reloadWorld.bind(worldRenderer),
39
+ updateEntityModel: worldRenderer.entities.updateEntityModel.bind(worldRenderer.entities),
40
+ playEntityModelAnimation: worldRenderer.entities.playEntityModelAnimation.bind(worldRenderer.entities),
41
+ addMedia: worldRenderer.media.addMedia.bind(worldRenderer.media),
42
+ destroyMedia: worldRenderer.media.destroyMedia.bind(worldRenderer.media),
43
+ setControlMode: worldRenderer.media.setControlMode.bind(worldRenderer.media),
44
+ setVideoPlaying: worldRenderer.media.setVideoPlaying.bind(worldRenderer.media),
45
+ setVideoSeeking: worldRenderer.media.setVideoSeeking.bind(worldRenderer.media),
46
+ setVideoVolume: worldRenderer.media.setVideoVolume.bind(worldRenderer.media),
47
+ setVideoSpeed: worldRenderer.media.setVideoSpeed.bind(worldRenderer.media),
48
+ handleUserClick: worldRenderer.media.handleUserClick.bind(worldRenderer.media),
49
+ addSectionAnimation(id: string, animation: typeof worldRenderer.sectionsOffsetsAnimations[string]) {
50
+ worldRenderer.sectionsOffsetsAnimations[id] = animation
51
+ },
52
+ removeSectionAnimation(id: string) {
53
+ delete worldRenderer.sectionsOffsetsAnimations[id]
54
+ },
55
+ shakeFromDamage: worldRenderer.cameraShake.shakeFromDamage.bind(worldRenderer.cameraShake),
56
+ onPageInteraction: worldRenderer.media.onPageInteraction.bind(worldRenderer.media),
57
+ downloadMesherLog: worldRenderer.downloadMesherLog.bind(worldRenderer),
58
+ // Fireworks methods
59
+ explodeFirework: worldRenderer.fireworksLegacy.explode.bind(worldRenderer.fireworksLegacy),
60
+ explodeFireworkFacingCamera: worldRenderer.fireworksLegacy.explodeFacingCamera.bind(worldRenderer.fireworksLegacy),
61
+ addWaypoint: worldRenderer.waypoints.addWaypoint.bind(worldRenderer.waypoints),
62
+ removeWaypoint: worldRenderer.waypoints.removeWaypoint.bind(worldRenderer.waypoints),
63
+ // Cinematic script methods
64
+ startCinimaticScript: worldRenderer.cinimaticScript.startScript.bind(worldRenderer.cinimaticScript),
65
+ stopCinimaticScript: worldRenderer.cinimaticScript.stopScript.bind(worldRenderer.cinimaticScript),
66
+ launchFirework: worldRenderer.fireworks.launchFirework.bind(worldRenderer.fireworks),
67
+ // New method for updating skybox
68
+ setSkyboxImage: worldRenderer.skyboxRenderer.setSkyboxImage.bind(worldRenderer.skyboxRenderer),
69
+ async loadGeometryExport(exportData: any) {
70
+ // Import dynamically to avoid circular dependencies
71
+ const { applyWorldGeometryExport } = await import('./worldGeometryExport')
72
+ return applyWorldGeometryExport(worldRenderer, exportData)
73
+ }
74
+ }
75
+ }
76
+
77
+ export type ThreeJsBackendMethods = ReturnType<typeof getBackendMethods>
78
+
79
+ const initOptionsRestorers = [
80
+ ResourcesManager,
81
+ // WorldDataEmitterWorker
82
+ ]
83
+
84
+ /**
85
+ * Creates the base graphics backend with core functionality.
86
+ */
87
+ export const createGraphicsBackendBase = () => {
88
+ // Private state
89
+ let initOptions!: GraphicsInitOptions
90
+ let documentRenderer: DocumentRenderer | null = null
91
+ let panoramaRenderer: PanoramaRenderer | null = null
92
+ let worldRenderer: WorldRendererThree | null = null
93
+ let frameTimingCollector: FrameTimingCollector | null = null
94
+
95
+ const init = (initOptionsArg: GraphicsInitOptions, mainData?: ThreeRendererMainData) => {
96
+ if (isWebWorker) {
97
+ initOptions = restoreTransferred(initOptionsArg, initOptionsRestorers, globalThis as unknown as Worker)
98
+ } else {
99
+ initOptions = initOptionsArg
100
+ }
101
+
102
+ documentRenderer = new DocumentRenderer(initOptions, mainData?.canvas)
103
+ ; (globalThis as any).renderer = documentRenderer.renderer
104
+ ; (globalThis as any).documentRenderer = documentRenderer
105
+ ; (globalThis as any).threeJsBackend = backend
106
+ ; (globalThis as any).resourcesManager = initOptions.resourcesManager
107
+
108
+ callModsMethod('default', backend)
109
+ }
110
+
111
+ const startPanorama = async () => {
112
+ if (!documentRenderer) throw new Error('Document renderer not initialized')
113
+ if (worldRenderer) return
114
+
115
+ if (!panoramaRenderer) {
116
+ panoramaRenderer = new PanoramaRenderer(documentRenderer, initOptions, !!process.env.SINGLE_FILE_BUILD_MODE)
117
+ ; (globalThis as any).panoramaRenderer = panoramaRenderer
118
+
119
+ callModsMethod('panoramaCreated', panoramaRenderer)
120
+ await panoramaRenderer.start()
121
+ callModsMethod('panoramaReady', panoramaRenderer)
122
+ }
123
+ }
124
+
125
+ const startWorld = async (displayOptionsArg: DisplayWorldOptions) => {
126
+ const displayOptions: DisplayWorldOptions = isWebWorker ? restoreTransferred(displayOptionsArg, initOptionsRestorers, globalThis as unknown as Worker) : displayOptionsArg
127
+
128
+ if (!documentRenderer) throw new Error('Document renderer not initialized')
129
+
130
+ documentRenderer.nonReactiveState = displayOptions.nonReactiveState
131
+
132
+ if (panoramaRenderer) {
133
+ panoramaRenderer.dispose()
134
+ panoramaRenderer = null
135
+ }
136
+
137
+ worldRenderer = new WorldRendererThree(documentRenderer.renderer, initOptions, displayOptions)
138
+
139
+ await worldRenderer.worldReadyPromise
140
+
141
+ frameTimingCollector = new FrameTimingCollector(displayOptions.nonReactiveState)
142
+ ; (globalThis as any).frameTimingCollector = frameTimingCollector
143
+
144
+ const originalRender = documentRenderer.render
145
+
146
+ documentRenderer.render = function (sizeChanged: boolean) {
147
+ originalRender.call(this, sizeChanged)
148
+ frameTimingCollector?.markFrameStart()
149
+
150
+ if (!displayOptions.inWorldRenderingConfig.paused) {
151
+ worldRenderer?.render(sizeChanged)
152
+ }
153
+
154
+ frameTimingCollector?.markFrameEnd()
155
+ frameTimingCollector?.markFrameDisplay()
156
+ }
157
+
158
+ documentRenderer.inWorldRenderingConfig = displayOptions.inWorldRenderingConfig
159
+
160
+ ; (globalThis as any).world = worldRenderer
161
+
162
+ callModsMethod('worldReady', worldRenderer)
163
+ }
164
+
165
+ const disconnect = () => {
166
+ if (panoramaRenderer) {
167
+ panoramaRenderer.dispose()
168
+ panoramaRenderer = null
169
+ }
170
+
171
+ if (documentRenderer) {
172
+ documentRenderer.dispose()
173
+ }
174
+
175
+ if (worldRenderer) {
176
+ worldRenderer.destroy()
177
+ worldRenderer = null
178
+ }
179
+ }
180
+
181
+ // Public interface
182
+ const backend: GraphicsBackend = {
183
+ id: 'threejs',
184
+ displayName: `three.js ${THREE.REVISION}`,
185
+ startPanorama,
186
+ startWorld,
187
+ disconnect,
188
+ setRendering(rendering) {
189
+ documentRenderer!.setPaused(!rendering)
190
+ if (worldRenderer) worldRenderer.renderingActive = rendering
191
+ },
192
+ getDebugOverlay: () => ({
193
+ get entitiesString() {
194
+ return worldRenderer?.entities.getDebugString()
195
+ },
196
+ }),
197
+ updateCamera(pos: Vec3 | null, yaw: number, pitch: number) {
198
+ // Mark camera update event for frame timing visualization
199
+ frameTimingCollector?.markCameraUpdate(!pos)
200
+ worldRenderer?.setFirstPersonCamera(pos, yaw, pitch)
201
+ },
202
+ get soundSystem() {
203
+ return worldRenderer?.soundSystem
204
+ },
205
+ get backendMethods() {
206
+ if (!worldRenderer) return undefined
207
+ return getBackendMethods(worldRenderer)
208
+ }
209
+ }
210
+
211
+ return {
212
+ main: {
213
+ init,
214
+ backend
215
+ },
216
+ workerProxy() {
217
+ return createWorkerProxy({
218
+ init(initOptionsArg: GraphicsInitOptions, canvas: OffscreenCanvas) {
219
+ init(initOptionsArg, { canvas })
220
+ },
221
+ updateSizeExternal(width: number, height: number, pixelRatio: number) {
222
+ documentRenderer?.updateSizeExternal(width, height, pixelRatio)
223
+ },
224
+ startPanorama,
225
+ startWorld,
226
+ disconnect,
227
+ setRendering: backend.setRendering,
228
+ updateCamera(pos, yaw, pitch) {
229
+ const posVec = pos ? new Vec3(pos.x, pos.y, pos.z) : null
230
+ frameTimingCollector?.markCameraUpdate(!posVec)
231
+ backend.updateCamera(posVec, yaw, pitch)
232
+ },
233
+ async callBackendMethod<K extends keyof ThreeJsBackendMethods>(
234
+ method: K,
235
+ ...args: Parameters<ThreeJsBackendMethods[K]>
236
+ ): Promise<ReturnType<ThreeJsBackendMethods[K]> extends Promise<infer R> ? R : ReturnType<ThreeJsBackendMethods[K]>> {
237
+ if (!worldRenderer) {
238
+ throw new Error('World renderer not initialized')
239
+ }
240
+
241
+ const methods = getBackendMethods(worldRenderer)
242
+ const target = methods[method]
243
+
244
+ if (!target) {
245
+ throw new Error(`Backend method ${String(method)} is unavailable`)
246
+ }
247
+
248
+ return target(...args)
249
+ }
250
+ })
251
+ }
252
+ }
253
+ }
254
+
255
+ const createGraphicsBackend = (initOptions: GraphicsInitOptions) => {
256
+ const { main } = createGraphicsBackendBase()
257
+ main.init(initOptions)
258
+ return main.backend
259
+ }
260
+
261
+ const callModsMethod = (method: string, ...args: any[]) => {
262
+ for (const mod of Object.values((globalThis.loadedMods ?? {}) as Record<string, any>)) {
263
+ try {
264
+ mod.threeJsBackendModule?.[method]?.(...args)
265
+ } catch (err) {
266
+ const errorMessage = `[mod three.js] Error calling ${method} on ${mod.name}: ${err}`
267
+ throw new Error(errorMessage)
268
+ }
269
+ }
270
+ }
271
+
272
+ createGraphicsBackend.id = 'threejs'
273
+
274
+ export default createGraphicsBackend
@@ -0,0 +1,107 @@
1
+ import * as THREE from 'three'
2
+ import { GraphicsBackend, GraphicsBackendLoader } from '@/graphicsBackend'
3
+ import { useWorkerProxy, deepPrepareForTransfer, findProblemTransfer } from '@/lib/workerProxy'
4
+ import { initMesherWorker, meshersSendMcData } from '@/lib/worldrendererCommon'
5
+ import { dynamicMcDataFiles } from '@/lib/buildSharedConfig.mjs'
6
+ import { addNewStat } from '@/lib/ui/newStats'
7
+ import { createGraphicsBackendBase, type ThreeJsBackendMethods } from './graphicsBackend'
8
+ import { addCanvasForWorker } from './documentRenderer'
9
+
10
+ export const createGraphicsBackendOffThread: GraphicsBackendLoader = async (initOptions) => {
11
+ const worker = initMesherWorker(() => { })
12
+ const promise = new Promise(resolve => {
13
+ worker.onmessage = ({ data }) => {
14
+ if (data.type === 'sideControlTookOver') {
15
+ resolve(data)
16
+ }
17
+ }
18
+ })
19
+ worker.postMessage({ type: 'sideControl', value: 'graphicsBackendThree' })
20
+ await promise
21
+ type WorkerType = ReturnType<ReturnType<typeof createGraphicsBackendBase>['workerProxy']>
22
+
23
+ const proxy = useWorkerProxy<WorkerType>(worker)
24
+ const canvas = addCanvasForWorker()
25
+ canvas.onSizeChanged((w, h) => {
26
+ proxy.updateSizeExternal(w, h, window.devicePixelRatio || 1)
27
+ })
28
+
29
+ const preparedInitOptions = deepPrepareForTransfer(initOptions, worker)
30
+ try {
31
+ proxy.init(preparedInitOptions, canvas.canvas)
32
+ } catch (err) {
33
+ findProblemTransfer(preparedInitOptions)
34
+ throw err
35
+ }
36
+
37
+ const backendMethodsProxy = new Proxy({} as ThreeJsBackendMethods, {
38
+ get(_target, prop) {
39
+ if (typeof prop !== 'string') {
40
+ return undefined
41
+ }
42
+ return async (...args: any[]) => proxy.callBackendMethod(prop as any, ...args)
43
+ }
44
+ })
45
+
46
+ const backend: GraphicsBackend = {
47
+ id: 'threejs',
48
+ displayName: `three.js ${THREE.REVISION}`,
49
+ // startPanorama: proxy.startPanorama,
50
+ async startPanorama() { },
51
+ async startWorld(options) {
52
+ meshersSendMcData([worker], options.version, [...dynamicMcDataFiles, 'items', 'itemsArray', 'entitiesByName', 'blocksByStateId'])
53
+
54
+ options.inWorldRenderingConfig['__syncToWorker'] = true
55
+
56
+ if (options.playerStateReactive) {
57
+ options.playerStateReactive['__syncToWorker'] = true
58
+ }
59
+
60
+ if (options.rendererState) {
61
+ options.rendererState['__syncFromWorker'] = true
62
+ }
63
+ if (options.nonReactiveState) {
64
+ options.nonReactiveState['__syncFromWorker'] = true
65
+ }
66
+ options.nonReactiveState['__syncFromWorkerInterval'] = 200
67
+ const prepared = deepPrepareForTransfer(options, worker)
68
+ try {
69
+ await proxy.startWorld(structuredClone(prepared))
70
+ console.log('startWorld done')
71
+ } catch (err) {
72
+ findProblemTransfer(prepared)
73
+ throw err
74
+ }
75
+ proxy.updateSizeExternal(canvas.size.width, canvas.size.height, window.devicePixelRatio || 1)
76
+
77
+
78
+ const fpsStat = addNewStat('fps')
79
+ setInterval(() => {
80
+ const { fps, avgRenderTime, worstRenderTime } = options.nonReactiveState
81
+ fpsStat.updateText(`FPS: ${fps.toFixed(0)} (${avgRenderTime.toFixed(0)}ms/${worstRenderTime.toFixed(0)}ms)`)
82
+ options.nonReactiveState.fps = 0
83
+ }, 1000)
84
+ },
85
+ disconnect() {
86
+ canvas.destroy()
87
+ proxy.disconnect()
88
+ worker.terminate()
89
+ },
90
+ setRendering(rendering) {
91
+ proxy.setRendering(rendering)
92
+ },
93
+ updateCamera(pos, yaw, pitch) {
94
+ proxy.updateCamera(pos ? { x: pos.x, y: pos.y, z: pos.z } : null, yaw, pitch)
95
+ },
96
+ soundSystem: undefined,
97
+ backendMethods: backendMethodsProxy
98
+ }
99
+
100
+ return backend
101
+ }
102
+ createGraphicsBackendOffThread.id = 'threejs-off-thread'
103
+
104
+ export const isOffthreadRendererSupported = () => {
105
+ // check if toOffscreenCanvas is supported
106
+ return 'OffscreenCanvas' in window && 'transferControlToOffscreen' in HTMLCanvasElement.prototype && !process.env.SINGLE_FILE_BUILD_MODE
107
+ }
@@ -0,0 +1,89 @@
1
+ import * as THREE from 'three'
2
+ import { loadSkinFromUsername, loadSkinImage } from '../lib/utils/skins'
3
+ import { steveTexture } from './entities'
4
+
5
+
6
+ export const getMyHand = async (image?: string, userName?: string) => {
7
+ let newMap: THREE.Texture
8
+ if (!image && !userName) {
9
+ newMap = await steveTexture
10
+ } else {
11
+ if (!image) {
12
+ image = await loadSkinFromUsername(userName!, 'skin')
13
+ }
14
+ if (!image) {
15
+ return
16
+ }
17
+ const { canvas } = await loadSkinImage(image)
18
+ newMap = new THREE.CanvasTexture(canvas)
19
+ }
20
+
21
+ newMap.magFilter = THREE.NearestFilter
22
+ newMap.minFilter = THREE.NearestFilter
23
+ // right arm
24
+ const box = new THREE.BoxGeometry()
25
+ const material = new THREE.MeshStandardMaterial()
26
+ const slim = false
27
+ const mesh = new THREE.Mesh(box, material)
28
+ mesh.scale.x = slim ? 3 : 4
29
+ mesh.scale.y = 12
30
+ mesh.scale.z = 4
31
+ setSkinUVs(box, 40, 16, slim ? 3 : 4, 12, 4)
32
+ material.map = newMap
33
+ material.needsUpdate = true
34
+ const group = new THREE.Group()
35
+ group.add(mesh)
36
+ group.scale.set(0.1, 0.1, 0.1)
37
+ mesh.rotation.z = Math.PI
38
+ return group
39
+ }
40
+
41
+ function setUVs (
42
+ box: THREE.BoxGeometry,
43
+ u: number,
44
+ v: number,
45
+ width: number,
46
+ height: number,
47
+ depth: number,
48
+ textureWidth: number,
49
+ textureHeight: number
50
+ ): void {
51
+ const toFaceVertices = (x1: number, y1: number, x2: number, y2: number) => [
52
+ new THREE.Vector2(x1 / textureWidth, 1 - y2 / textureHeight),
53
+ new THREE.Vector2(x2 / textureWidth, 1 - y2 / textureHeight),
54
+ new THREE.Vector2(x2 / textureWidth, 1 - y1 / textureHeight),
55
+ new THREE.Vector2(x1 / textureWidth, 1 - y1 / textureHeight),
56
+ ]
57
+
58
+ const top = toFaceVertices(u + depth, v, u + width + depth, v + depth)
59
+ const bottom = toFaceVertices(u + width + depth, v, u + width * 2 + depth, v + depth)
60
+ const left = toFaceVertices(u, v + depth, u + depth, v + depth + height)
61
+ const front = toFaceVertices(u + depth, v + depth, u + width + depth, v + depth + height)
62
+ const right = toFaceVertices(u + width + depth, v + depth, u + width + depth * 2, v + height + depth)
63
+ const back = toFaceVertices(u + width + depth * 2, v + depth, u + width * 2 + depth * 2, v + height + depth)
64
+
65
+ const uvAttr = box.attributes.uv as THREE.BufferAttribute
66
+ const uvRight = [right[3], right[2], right[0], right[1]]
67
+ const uvLeft = [left[3], left[2], left[0], left[1]]
68
+ const uvTop = [top[3], top[2], top[0], top[1]]
69
+ const uvBottom = [bottom[0], bottom[1], bottom[3], bottom[2]]
70
+ const uvFront = [front[3], front[2], front[0], front[1]]
71
+ const uvBack = [back[3], back[2], back[0], back[1]]
72
+
73
+ // Create a new array to hold the modified UV data
74
+ const newUVData = [] as number[]
75
+
76
+ // Iterate over the arrays and copy the data to uvData
77
+ for (const uvArray of [uvRight, uvLeft, uvTop, uvBottom, uvFront, uvBack]) {
78
+ for (const uv of uvArray) {
79
+ newUVData.push(uv.x, uv.y)
80
+ }
81
+ }
82
+
83
+ uvAttr.set(new Float32Array(newUVData))
84
+ uvAttr.needsUpdate = true
85
+ }
86
+
87
+ function setSkinUVs (box: THREE.BoxGeometry, u: number, v: number, width: number, height: number, depth: number): void {
88
+ setUVs(box, u, v, width, height, depth, 64, 64)
89
+ }