minecraft-renderer 0.1.18 → 0.1.20

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-renderer",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "The most Modular Minecraft world renderer with Three.js WebGL backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "dist",
11
11
  "src",
12
+ "public/wasm_mesher_bg.wasm",
12
13
  "logo.webp"
13
14
  ],
14
15
  "browserslist": {
@@ -108,7 +109,7 @@
108
109
  "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
109
110
  },
110
111
  "scripts": {
111
- "build": "pnpm unit-test && pnpm build:publish",
112
+ "build": "pnpm unit-test --run && pnpm build:publish",
112
113
  "build:publish": "run-s build:lib:minify build:mesher build:threeworker",
113
114
  "build:lib": "node scripts/buildLib.mjs",
114
115
  "build:lib:minify": "node scripts/buildLib.mjs --minify",
Binary file
@@ -24,6 +24,7 @@ export const defaultWorldRendererConfig = {
24
24
  futuristicReveal: false,
25
25
 
26
26
  // Performance settings
27
+ wasmMesher: false,
27
28
  mesherWorkers: 1,
28
29
  addChunksBatchWaitTime: 200,
29
30
  _experimentalSmoothChunkLoading: true,
@@ -45,18 +46,18 @@ export const defaultWorldRendererConfig = {
45
46
  showHand: false,
46
47
  viewBobbing: false,
47
48
  renderEars: true,
48
- highlightBlockColor: 'blue',
49
+ highlightBlockColor: 'blue' as 'blue' | 'classic' | undefined,
49
50
 
50
51
  // Player models
51
52
  fetchPlayerSkins: true,
52
- skinTexturesProxy: undefined,
53
+ skinTexturesProxy: undefined as undefined | string,
53
54
 
54
55
  // VR settings
55
56
  vrSupport: true,
56
57
  vrPageGameRendering: true,
57
58
 
58
59
  // World settings
59
- clipWorldBelowY: undefined,
60
+ clipWorldBelowY: undefined as undefined | number,
60
61
  isPlayground: false,
61
62
  instantCameraUpdate: false
62
63
  }
@@ -9,7 +9,7 @@ import { proxy } from 'valtio'
9
9
  import type { ResourcesManagerTransferred } from '../resourcesManager/resourcesManager'
10
10
  import { dynamicMcDataFiles } from './buildSharedConfig.mjs'
11
11
  import { DisplayWorldOptions, GraphicsInitOptions, RendererReactiveState, SoundSystem } from '../graphicsBackend/types'
12
- import { HighestBlockInfo, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig, MesherMainEvent } from '../mesher/shared'
12
+ import { HighestBlockInfo, CustomBlockModels, BlockStateModelInfo, getBlockAssetsCacheKey, MesherConfig, MesherMainEvent, IS_FULL_WORLD_SECTION, SECTION_HEIGHT } from '../mesher/shared'
13
13
  import { chunkPos } from './simpleUtils'
14
14
  import { addNewStat, removeAllStats, updatePanesVisibility, updateStatText } from './ui/newStats'
15
15
  import { getPlayerStateUtils } from '../graphicsBackend/playerState'
@@ -244,7 +244,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
244
244
  }
245
245
 
246
246
  async getBlockInfo(blockPos: { x: number, y: number, z: number }, stateId: number) {
247
- const chunkKey = `${Math.floor(blockPos.x / 16) * 16},${Math.floor(blockPos.z / 16) * 16}`
247
+ const CHUNK_SIZE = 16
248
+ const chunkKey = `${Math.floor(blockPos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(blockPos.z / CHUNK_SIZE) * CHUNK_SIZE}`
248
249
  const customBlockName = this.protocolCustomBlocks.get(chunkKey)?.[`${blockPos.x},${blockPos.y},${blockPos.z}`]
249
250
  const cacheKey = getBlockAssetsCacheKey(stateId, customBlockName)
250
251
  const modelInfo = this.blockStateModelInfo.get(cacheKey)
@@ -256,7 +257,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
256
257
 
257
258
  initWorkers(numWorkers = this.worldRendererConfig.mesherWorkers) {
258
259
  // init workers
259
- for (let i = 0; i < numWorkers + 1; i++) {
260
+ for (let i = 0; i < numWorkers + 0; i++) {
260
261
  const worker = initMesherWorker((data) => {
261
262
  if (Array.isArray(data)) {
262
263
  this.messageQueue.push(...data)
@@ -264,7 +265,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
264
265
  this.messageQueue.push(data)
265
266
  }
266
267
  void this.processMessageQueue('worker')
267
- })
268
+ }, this.worldRendererConfig.wasmMesher ? 'mesherWasm.js' : 'mesher.js')
268
269
  this.workers.push(worker)
269
270
  }
270
271
  }
@@ -367,16 +368,26 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
367
368
  const chunkKey = `${chunkCoords[0]},${chunkCoords[2]}`
368
369
  if (this.loadedChunks[chunkKey]) { // ensure chunk data was added, not a neighbor chunk update
369
370
  let loaded = true
370
- for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += 16) {
371
- if (!this.finishedSections[`${chunkCoords[0]},${y},${chunkCoords[2]}`]) {
371
+ const sectionHeight = this.getSectionHeight()
372
+ if (IS_FULL_WORLD_SECTION) {
373
+ // Only one section per chunk when full world section
374
+ const sectionY = this.worldMinYRender
375
+ if (!this.finishedSections[`${chunkCoords[0]},${sectionY},${chunkCoords[2]}`]) {
372
376
  loaded = false
373
- break
377
+ }
378
+ } else {
379
+ for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
380
+ if (!this.finishedSections[`${chunkCoords[0]},${y},${chunkCoords[2]}`]) {
381
+ loaded = false
382
+ break
383
+ }
374
384
  }
375
385
  }
376
386
  if (loaded) {
377
387
  // CHUNK FINISHED
378
388
  this.finishedChunks[chunkKey] = true
379
- this.reactiveState.world.chunksLoaded.add(`${Math.floor(chunkCoords[0] / 16)},${Math.floor(chunkCoords[2] / 16)}`)
389
+ const CHUNK_SIZE = 16
390
+ this.reactiveState.world.chunksLoaded.add(`${Math.floor(chunkCoords[0] / CHUNK_SIZE)},${Math.floor(chunkCoords[2] / CHUNK_SIZE)}`)
380
391
  this.renderUpdateEmitter.emit(`chunkFinished`, `${chunkCoords[0]},${chunkCoords[2]}`)
381
392
  this.checkAllFinished()
382
393
  // merge highest blocks by sections into highest blocks by chunks
@@ -479,8 +490,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
479
490
 
480
491
  getDistance(posAbsolute: Vec3) {
481
492
  const [botX, botZ] = chunkPos(this.viewerChunkPosition!)
482
- const dx = Math.abs(botX - Math.floor(posAbsolute.x / 16))
483
- const dz = Math.abs(botZ - Math.floor(posAbsolute.z / 16))
493
+ const CHUNK_SIZE = 16
494
+ const dx = Math.abs(botX - Math.floor(posAbsolute.x / CHUNK_SIZE))
495
+ const dz = Math.abs(botZ - Math.floor(posAbsolute.z / CHUNK_SIZE))
484
496
  return [dx, dz] as [number, number]
485
497
  }
486
498
 
@@ -567,8 +579,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
567
579
  console.log('textures loaded')
568
580
  }
569
581
 
582
+ getSectionHeight() {
583
+ if (IS_FULL_WORLD_SECTION) {
584
+ return this.worldSizeParams.worldHeight
585
+ }
586
+ return SECTION_HEIGHT
587
+ }
588
+
570
589
  get worldMinYRender() {
571
- return Math.floor(Math.max(this.worldSizeParams.minY, this.worldRendererConfig.clipWorldBelowY ?? -Infinity) / 16) * 16
590
+ const sectionHeight = this.getSectionHeight()
591
+ return Math.floor(Math.max(this.worldSizeParams.minY, this.worldRendererConfig.clipWorldBelowY ?? -Infinity) / sectionHeight) * sectionHeight
572
592
  }
573
593
 
574
594
  updateChunksStats() {
@@ -609,14 +629,29 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
609
629
  // })
610
630
  this.logWorkerWork(() => `-> chunk ${JSON.stringify({ x, z, chunkLength: chunk.length, customBlockModelsLength: customBlockModels ? Object.keys(customBlockModels).length : 0 })}`)
611
631
  this.mesherLogReader?.chunkReceived(x, z, chunk.length)
612
- for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += 16) {
613
- const loc = new Vec3(x, y, z)
632
+ const sectionHeight = this.getSectionHeight()
633
+ const CHUNK_SIZE = 16
634
+
635
+ if (IS_FULL_WORLD_SECTION) {
636
+ // Only one section per chunk when full world section
637
+ const loc = new Vec3(x, this.worldMinYRender, z)
614
638
  this.setSectionDirty(loc)
615
639
  if (this.neighborChunkUpdates && (!isLightUpdate || this.worldRendererConfig.smoothLighting)) {
616
- this.setSectionDirty(loc.offset(-16, 0, 0))
617
- this.setSectionDirty(loc.offset(16, 0, 0))
618
- this.setSectionDirty(loc.offset(0, 0, -16))
619
- this.setSectionDirty(loc.offset(0, 0, 16))
640
+ this.setSectionDirty(loc.offset(-CHUNK_SIZE, 0, 0))
641
+ this.setSectionDirty(loc.offset(CHUNK_SIZE, 0, 0))
642
+ this.setSectionDirty(loc.offset(0, 0, -CHUNK_SIZE))
643
+ this.setSectionDirty(loc.offset(0, 0, CHUNK_SIZE))
644
+ }
645
+ } else {
646
+ for (let y = this.worldMinYRender; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
647
+ const loc = new Vec3(x, y, z)
648
+ this.setSectionDirty(loc)
649
+ if (this.neighborChunkUpdates && (!isLightUpdate || this.worldRendererConfig.smoothLighting)) {
650
+ this.setSectionDirty(loc.offset(-CHUNK_SIZE, 0, 0))
651
+ this.setSectionDirty(loc.offset(CHUNK_SIZE, 0, 0))
652
+ this.setSectionDirty(loc.offset(0, 0, -CHUNK_SIZE))
653
+ this.setSectionDirty(loc.offset(0, 0, CHUNK_SIZE))
654
+ }
620
655
  }
621
656
  }
622
657
  }
@@ -640,9 +675,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
640
675
  this.allLoadedIn = undefined
641
676
  this.initialChunkLoadWasStartedIn = undefined
642
677
  }
643
- for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += 16) {
644
- this.setSectionDirty(new Vec3(x, y, z), false)
645
- delete this.finishedSections[`${x},${y},${z}`]
678
+ const sectionHeight = this.getSectionHeight()
679
+ if (IS_FULL_WORLD_SECTION) {
680
+ const sectionY = this.worldMinYRender
681
+ this.setSectionDirty(new Vec3(x, sectionY, z), false)
682
+ delete this.finishedSections[`${x},${sectionY},${z}`]
683
+ } else {
684
+ for (let y = this.worldSizeParams.minY; y < this.worldSizeParams.worldHeight; y += sectionHeight) {
685
+ this.setSectionDirty(new Vec3(x, y, z), false)
686
+ delete this.finishedSections[`${x},${y},${z}`]
687
+ }
646
688
  }
647
689
  this.highestBlocksByChunks.delete(`${x},${z}`)
648
690
 
@@ -657,8 +699,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
657
699
 
658
700
  setBlockStateId(pos: Vec3, stateId: number | undefined, needAoRecalculation = true) {
659
701
  const set = async () => {
660
- const sectionX = Math.floor(pos.x / 16) * 16
661
- const sectionZ = Math.floor(pos.z / 16) * 16
702
+ const CHUNK_SIZE = 16
703
+ const sectionX = Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE
704
+ const sectionZ = Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE
662
705
  if (this.queuedChunks.has(`${sectionX},${sectionZ}`)) {
663
706
  await new Promise<void>(resolve => {
664
707
  this.queuedFunctions.push(() => {
@@ -792,7 +835,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
792
835
  }
793
836
 
794
837
  setBlockStateIdInner(pos: Vec3, stateId: number | undefined, needAoRecalculation = true) {
795
- const chunkKey = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.z / 16) * 16}`
838
+ const CHUNK_SIZE = 16
839
+ const chunkKey = `${Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE}`
796
840
  const blockPosKey = `${pos.x},${pos.y},${pos.z}`
797
841
  const customBlockModels = this.protocolCustomBlocks.get(chunkKey) || {}
798
842
 
@@ -807,34 +851,36 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
807
851
  this.logWorkerWork(`-> blockUpdate ${JSON.stringify({ pos, stateId, customBlockModels })}`)
808
852
  this.setSectionDirty(pos, true, true)
809
853
  if (this.neighborChunkUpdates) {
810
- if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, 0), true, true)
811
- if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 0), true, true)
812
- if ((pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 0), true, true)
813
- if ((pos.y & 15) === 15) this.setSectionDirty(pos.offset(0, 16, 0), true, true)
814
- if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -16), true, true)
815
- if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, 16), true, true)
854
+ const CHUNK_SIZE = 16
855
+ const sectionHeight = this.getSectionHeight()
856
+ if ((pos.x & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, 0), true, true)
857
+ if ((pos.x & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, 0), true, true)
858
+ if ((pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, 0), true, true)
859
+ if ((pos.y & (sectionHeight - 1)) === (sectionHeight - 1)) this.setSectionDirty(pos.offset(0, sectionHeight, 0), true, true)
860
+ if ((pos.z & 15) === 0) this.setSectionDirty(pos.offset(0, 0, -CHUNK_SIZE), true, true)
861
+ if ((pos.z & 15) === 15) this.setSectionDirty(pos.offset(0, 0, CHUNK_SIZE), true, true)
816
862
 
817
863
  if (needAoRecalculation) {
818
864
  // top view neighbors
819
- if ((pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-16, 0, -16), true, true)
820
- if ((pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(16, 0, -16), true, true)
821
- if ((pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-16, 0, 16), true, true)
822
- if ((pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(16, 0, 16), true, true)
865
+ if ((pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, -CHUNK_SIZE), true, true)
866
+ if ((pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, -CHUNK_SIZE), true, true)
867
+ if ((pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-CHUNK_SIZE, 0, CHUNK_SIZE), true, true)
868
+ if ((pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, 0, CHUNK_SIZE), true, true)
823
869
 
824
870
  // side view neighbors (but ignore updates above)
825
871
  // z view neighbors
826
- if ((pos.x & 15) === 0 && (pos.y & 15) === 0) this.setSectionDirty(pos.offset(-16, -16, 0), true, true)
827
- if ((pos.x & 15) === 15 && (pos.y & 15) === 0) this.setSectionDirty(pos.offset(16, -16, 0), true, true)
872
+ if ((pos.x & 15) === 0 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, 0), true, true)
873
+ if ((pos.x & 15) === 15 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, 0), true, true)
828
874
 
829
875
  // x view neighbors
830
- if ((pos.z & 15) === 0 && (pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, -16), true, true)
831
- if ((pos.z & 15) === 15 && (pos.y & 15) === 0) this.setSectionDirty(pos.offset(0, -16, 16), true, true)
876
+ if ((pos.z & 15) === 0 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, -CHUNK_SIZE), true, true)
877
+ if ((pos.z & 15) === 15 && (pos.y & (sectionHeight - 1)) === 0) this.setSectionDirty(pos.offset(0, -sectionHeight, CHUNK_SIZE), true, true)
832
878
 
833
879
  // x & z neighbors
834
- if ((pos.y & 15) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-16, -16, -16), true, true)
835
- if ((pos.y & 15) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(16, -16, -16), true, true)
836
- if ((pos.y & 15) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-16, -16, 16), true, true)
837
- if ((pos.y & 15) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(16, -16, 16), true, true)
880
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
881
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 0) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, -CHUNK_SIZE), true, true)
882
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 0 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(-CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
883
+ if ((pos.y & (sectionHeight - 1)) === 0 && (pos.x & 15) === 15 && (pos.z & 15) === 15) this.setSectionDirty(pos.offset(CHUNK_SIZE, -sectionHeight, CHUNK_SIZE), true, true)
838
884
  }
839
885
  }
840
886
  }
@@ -845,14 +891,16 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
845
891
  toWorkerMessagesQueue = {} as { [workerIndex: string]: any[] }
846
892
 
847
893
  getWorkerNumber(pos: Vec3, updateAction = false) {
894
+ const CHUNK_SIZE = 16
895
+ const sectionHeight = this.getSectionHeight()
848
896
  if (updateAction) {
849
- const key = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.y / 16) * 16},${Math.floor(pos.z / 16) * 16}`
897
+ const key = `${Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(pos.y / sectionHeight) * sectionHeight},${Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE}`
850
898
  const cantUseChangeWorker = this.sectionsWaiting.get(key) && !this.finishedSections[key]
851
899
  if (!cantUseChangeWorker) return 0
852
900
  }
853
901
 
854
- const hash = mod(Math.floor(pos.x / 16) + Math.floor(pos.y / 16) + Math.floor(pos.z / 16), this.workers.length - 1)
855
- return hash + 1
902
+ const hash = mod(Math.floor(pos.x / CHUNK_SIZE) + Math.floor(pos.y / sectionHeight) + Math.floor(pos.z / CHUNK_SIZE), this.workers.length)
903
+ return hash + 0
856
904
  }
857
905
 
858
906
  async debugGetWorkerCustomBlockModel(pos: Vec3) {
@@ -881,7 +929,9 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
881
929
  const distance = this.getDistance(pos)
882
930
  // todo shouldnt we check loadedChunks instead?
883
931
  if (!this.workers.length || distance[0] > this.viewDistance || distance[1] > this.viewDistance) return
884
- const key = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.y / 16) * 16},${Math.floor(pos.z / 16) * 16}`
932
+ const CHUNK_SIZE = 16
933
+ const sectionHeight = this.getSectionHeight()
934
+ const key = `${Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(pos.y / sectionHeight) * sectionHeight},${Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE}`
885
935
  // if (this.sectionsOutstanding.has(key)) return
886
936
  this.renderUpdateEmitter.emit('dirty', pos, value)
887
937
  // Dispatch sections to workers based on position
@@ -951,7 +1001,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
951
1001
 
952
1002
  async waitForChunkToLoad(pos: Vec3) {
953
1003
  return new Promise<void>((resolve, reject) => {
954
- const key = `${Math.floor(pos.x / 16) * 16},${Math.floor(pos.z / 16) * 16}`
1004
+ const CHUNK_SIZE = 16
1005
+ const key = `${Math.floor(pos.x / CHUNK_SIZE) * CHUNK_SIZE},${Math.floor(pos.z / CHUNK_SIZE) * CHUNK_SIZE}`
955
1006
  if (this.loadedChunks[key]) {
956
1007
  resolve()
957
1008
  return
@@ -995,9 +1046,8 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
995
1046
  }
996
1047
  }
997
1048
 
998
- export const initMesherWorker = (onGotMessage: (data: any) => void) => {
1049
+ export const initMesherWorker = (onGotMessage: (data: any) => void, workerName = 'mesher.js') => {
999
1050
  // Node environment needs an absolute path, but browser needs the url of the file
1000
- const workerName = 'mesher.js'
1001
1051
 
1002
1052
  let worker: any
1003
1053
  if (process.env.SINGLE_FILE_BUILD) {