minecraft-renderer 0.1.54 → 0.1.56

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 (34) hide show
  1. package/dist/mesher.js +54 -54
  2. package/dist/mesher.js.map +3 -3
  3. package/dist/mesherWasm.js +46 -46
  4. package/dist/minecraft-renderer.js +61 -61
  5. package/dist/minecraft-renderer.js.meta.json +1 -1
  6. package/dist/threeWorker.js +972 -972
  7. package/package.json +1 -1
  8. package/src/lib/buildWorkerMcDataIndexes.test.ts +48 -0
  9. package/src/lib/buildWorkerMcDataIndexes.ts +121 -0
  10. package/src/lib/items.ts +4 -3
  11. package/src/lib/utils.ts +1 -1
  12. package/src/lib/workerMessageSanitize.test.ts +14 -0
  13. package/src/lib/workerMessageSanitize.ts +48 -0
  14. package/src/lib/workerProxy.restore.test.ts +29 -0
  15. package/src/lib/workerProxy.ts +110 -36
  16. package/src/lib/worldrendererCommon.ts +25 -0
  17. package/src/resourcesManager/resourcesManager.ts +97 -11
  18. package/src/resourcesManager/resourcesManager.worker.test.ts +61 -0
  19. package/src/three/appShared.ts +1 -1
  20. package/src/three/chunkMeshManager.ts +4 -3
  21. package/src/three/entities.ts +27 -19
  22. package/src/three/entity/EntityMesh.ts +4 -10
  23. package/src/three/graphicsBackendBase.ts +25 -15
  24. package/src/three/graphicsBackendOffThread.ts +27 -3
  25. package/src/three/hand.ts +3 -3
  26. package/src/three/itemMesh.ts +20 -7
  27. package/src/three/menuBackground/assetUrl.ts +15 -0
  28. package/src/three/menuBackground/classic.ts +11 -5
  29. package/src/three/menuBackground/futuristic.ts +3 -0
  30. package/src/three/threeJsMedia.ts +31 -4
  31. package/src/three/threeJsUtils.ts +11 -0
  32. package/src/three/threeWorker.ts +12 -11
  33. package/src/three/worldRendererThree.ts +7 -0
  34. package/src/worldView/worldView.ts +2 -1
@@ -1,11 +1,12 @@
1
1
  //@ts-nocheck
2
- import { join } from 'path'
3
2
  import * as THREE from 'three'
4
3
  import { EntityMesh } from '../entity/EntityMesh'
4
+ import { loadImageFromUrl } from '../../lib/utils'
5
5
  import type { DocumentRenderer } from '../documentRenderer'
6
- import { loadThreeJsTextureFromUrl, loadThreeJsTextureFromUrlSync } from '../threeJsUtils'
6
+ import { loadThreeJsTextureFromBitmap } from '../threeJsUtils'
7
7
  import type { MenuBackgroundView } from './activeView'
8
8
  import { resizeMenuBackgroundCamera } from './activeView'
9
+ import { menuBackgroundAssetUrl } from './assetUrl'
9
10
 
10
11
  const date = new Date()
11
12
  const isChristmas = date.getMonth() === 11 && date.getDate() >= 24 && date.getDate() <= 26
@@ -75,7 +76,9 @@ export class ClassicMenuBackground implements MenuBackgroundView {
75
76
 
76
77
  for (const file of panoramaFiles) {
77
78
  const load = async () => {
78
- const { texture } = loadThreeJsTextureFromUrlSync(join('background', isChristmas ? 'christmas' : '', file))
79
+ const url = menuBackgroundAssetUrl('background', isChristmas ? 'christmas' : '', file)
80
+ const bitmap = await loadImageFromUrl(url)
81
+ const texture = loadThreeJsTextureFromBitmap(bitmap)
79
82
 
80
83
  texture.matrixAutoUpdate = false
81
84
  texture.matrix.set(-1, 0, 1, 0, 1, 0, 0, 0, 1)
@@ -96,7 +99,9 @@ export class ClassicMenuBackground implements MenuBackgroundView {
96
99
  panorMaterials.push(material)
97
100
  }
98
101
 
99
- void load()
102
+ void load().catch(err => {
103
+ console.warn('[ClassicMenuBackground] Failed to load panorama face:', file, err)
104
+ })
100
105
  }
101
106
 
102
107
  const panoramaBox = new THREE.Mesh(panorGeo, panorMaterials)
@@ -137,7 +142,8 @@ export class ClassicMenuBackground implements MenuBackgroundView {
137
142
 
138
143
  /** Debug helper: flat cubemap face in front of the camera. */
139
144
  async debugImageInFrontOfCamera() {
140
- const image = await loadThreeJsTextureFromUrl(join('background', 'panorama_0.webp'))
145
+ const bitmap = await loadImageFromUrl(menuBackgroundAssetUrl('background', 'panorama_0.webp'))
146
+ const image = loadThreeJsTextureFromBitmap(bitmap)
141
147
  const mesh = new THREE.Mesh(
142
148
  new THREE.PlaneGeometry(1000, 1000),
143
149
  new THREE.MeshBasicMaterial({ map: image })
@@ -632,6 +632,9 @@ export class FuturisticMenuBackground implements MenuBackgroundView {
632
632
  const resourcesManager = this.resourcesManager ?? new ResourcesManager()
633
633
  const needsAssetUpdate = !resourcesManager.currentResources?.blocksAtlasImage
634
634
  if (needsAssetUpdate) {
635
+ if (typeof document === 'undefined') {
636
+ throw new Error('Menu atlas missing in worker; pass resourcesManager from main thread')
637
+ }
635
638
  resourcesManager.currentConfig = {
636
639
  ...resourcesManager.currentConfig,
637
640
  version: MENU_BACKGROUND_MC_VERSION,
@@ -2,6 +2,8 @@
2
2
  import * as THREE from 'three'
3
3
  import { WorldRendererThree } from './worldRendererThree'
4
4
  import { ThreeJsSound } from './threeJsSound'
5
+ import { isWebWorker } from './documentRenderer'
6
+ import { loadThreeJsTextureFromUrlSync } from './threeJsUtils'
5
7
 
6
8
  type ControlModeConfig = {
7
9
  mouseButton: 'both' | 'left' | 'right'
@@ -187,7 +189,11 @@ export class ThreeJsMedia {
187
189
 
188
190
  let video: HTMLVideoElement | undefined
189
191
  let positionalAudio: THREE.PositionalAudio | undefined
190
- if (!isImage) {
192
+ const workerVideoUnsupported = isWebWorker && !isImage
193
+ if (workerVideoUnsupported) {
194
+ console.warn(`[addMedia] Video "${id}" skipped in off-thread renderer (no HTMLVideoElement)`)
195
+ }
196
+ if (!isImage && !workerVideoUnsupported) {
191
197
  video = document.createElement('video')
192
198
  video.src = props.src.endsWith('.gif') ? props.src.replace('.gif', '.mp4') : props.src
193
199
  video.loop = props.loop ?? true
@@ -268,14 +274,35 @@ export class ThreeJsMedia {
268
274
  alphaTest: 0.1
269
275
  })
270
276
 
271
- const texture = video
272
- ? new THREE.VideoTexture(video)
273
- : new THREE.TextureLoader().load(props.src, () => {
277
+ let texture: THREE.Texture
278
+ if (video) {
279
+ texture = new THREE.VideoTexture(video)
280
+ } else if (workerVideoUnsupported) {
281
+ texture = this.createErrorTexture(
282
+ props.size.width,
283
+ props.size.height,
284
+ props.background,
285
+ 'Video unavailable (multi-thread)',
286
+ )
287
+ } else if (isWebWorker) {
288
+ const loaded = loadThreeJsTextureFromUrlSync(props.src)
289
+ texture = loaded.texture
290
+ texture.minFilter = THREE.NearestFilter
291
+ texture.magFilter = THREE.NearestFilter
292
+ void loaded.promise.then(() => {
293
+ if (this.customMedia.get(id)?.texture === texture) {
294
+ material.map = texture
295
+ material.needsUpdate = true
296
+ }
297
+ }).catch(() => handleError())
298
+ } else {
299
+ texture = new THREE.TextureLoader().load(props.src, () => {
274
300
  if (this.customMedia.get(id)?.texture === texture) {
275
301
  material.map = texture
276
302
  material.needsUpdate = true
277
303
  }
278
304
  }, undefined, () => handleError()) // todo cache
305
+ }
279
306
  texture.minFilter = THREE.NearestFilter
280
307
  texture.magFilter = THREE.NearestFilter
281
308
  // texture.format = THREE.RGBAFormat
@@ -69,6 +69,17 @@ export const loadThreeJsTextureFromBitmap = (image: ImageBitmap) => {
69
69
  return texture
70
70
  }
71
71
 
72
+ /** Worker-safe sync handle; image loads via fetch + OffscreenCanvas (not TextureLoader). */
73
+ export function loadNearestFilterTexture (imageUrl: string): THREE.Texture {
74
+ const { texture, promise } = loadThreeJsTextureFromUrlSync(imageUrl)
75
+ texture.magFilter = THREE.NearestFilter
76
+ texture.minFilter = THREE.NearestFilter
77
+ void promise.catch((err) => {
78
+ console.error('[texture] failed to load', imageUrl, err)
79
+ })
80
+ return texture
81
+ }
82
+
72
83
  export async function loadTexture (texture: string, cb: (texture: THREE.Texture) => void, onLoad?: () => void): Promise<void> {
73
84
  const cached = textureCache[texture]
74
85
  if (!cached) {
@@ -1,30 +1,31 @@
1
1
  //@ts-nocheck
2
- // Three.js Worker Entry Point
3
- // This worker handles three.js rendering in an offscreen canvas
2
+ import { augmentWorkerMcData } from '../lib/buildWorkerMcDataIndexes'
4
3
 
5
4
  globalThis.structuredClone ??= (value) => JSON.parse(JSON.stringify(value))
6
5
 
7
- // Handle mcData messages - needed for esbuild plugin to access globalThis.mcData
8
- // Use addEventListener to coexist with worker proxy's message handler
6
+ const applyWorkerMcData = (raw: Record<string, unknown>) => {
7
+ augmentWorkerMcData(raw)
8
+ const globalVar: any = globalThis
9
+ globalVar.mcData = raw
10
+ globalVar.loadedData = raw
11
+ // eslint-disable-next-line no-restricted-globals
12
+ self.postMessage({ type: 'mcDataApplied' })
13
+ }
14
+
9
15
  // eslint-disable-next-line no-restricted-globals
10
16
  self.addEventListener('message', (event: MessageEvent) => {
11
17
  const data = event.data
12
- const globalVar: any = globalThis
13
-
14
18
  if (data.type === 'mcData') {
15
- globalVar.mcData = data.mcData
16
- globalVar.loadedData = data.mcData
19
+ applyWorkerMcData(data.mcData)
17
20
  console.log('data loaded')
18
21
  return
19
22
  }
20
23
 
21
- // Handle array of messages (batch mode)
22
24
  if (Array.isArray(data)) {
23
25
  // eslint-disable-next-line unicorn/no-array-for-each
24
26
  data.forEach((msg) => {
25
27
  if (msg.type === 'mcData') {
26
- globalVar.mcData = msg.mcData
27
- globalVar.loadedData = msg.mcData
28
+ applyWorkerMcData(msg.mcData)
28
29
  }
29
30
  })
30
31
  }
@@ -1510,6 +1510,13 @@ export class WorldRendererThree extends WorldRendererCommon {
1510
1510
  this.chunkMeshManager.onChunkRemovedFromGate(`${x},${z}`)
1511
1511
  }
1512
1512
 
1513
+ updateViewerPosition(pos: Vec3) {
1514
+ super.updateViewerPosition(pos)
1515
+ if (this.chunkMeshManager.pendingNearReveal.size > 0) {
1516
+ this.chunkMeshManager.tryRevealPending()
1517
+ }
1518
+ }
1519
+
1513
1520
  protected onViewerChunkPositionChanged(): void {
1514
1521
  this.chunkMeshManager.tryRevealPending()
1515
1522
  }
@@ -11,6 +11,7 @@ import { Vec3 } from 'vec3'
11
11
  import TypedEmitter from 'typed-emitter'
12
12
  import type { WorldViewEvents, ChunkPosKey, WorldSizeParams } from './types'
13
13
  import { generateSpiralMatrix } from '../lib/spiral'
14
+ import { sanitizeWorkerEventArgs } from '../lib/workerMessageSanitize'
14
15
 
15
16
  /**
16
17
  * Helper to calculate chunk position from absolute position.
@@ -131,7 +132,7 @@ export class WorldView extends (EventEmitter as new () => TypedEmitter<WorldView
131
132
  class: WorldViewWorker.restorerName,
132
133
  type: 'event',
133
134
  eventName,
134
- args,
135
+ args: sanitizeWorkerEventArgs(args),
135
136
  })
136
137
  }) as any
137
138
  }