minecraft-renderer 0.1.39 → 0.1.41
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/dist/mesher.js +8 -8
- package/dist/mesher.js.map +4 -4
- package/dist/mesherWasm.js +94 -94
- package/dist/minecraft-renderer.js +57 -57
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +66 -66
- package/package.json +3 -4
- package/src/bundler/bundlePrepare.ts +56 -0
- package/src/graphicsBackend/appViewer.ts +10 -0
- package/src/graphicsBackend/config.ts +5 -1
- package/src/graphicsBackend/preloadWorkers.ts +187 -0
- package/src/lib/worldrendererCommon.ts +26 -2
- package/src/{mesher → mesher-legacy}/mesher.ts +14 -4
- package/src/{mesher → mesher-legacy}/test/mesherTester.ts +2 -2
- package/src/{mesher → mesher-legacy}/test/run/test-js.ts +1 -1
- package/src/{mesher → mesher-legacy}/test/test-perf.ts +1 -1
- package/src/{mesher → mesher-legacy}/test/tests.test.ts +1 -1
- package/src/{mesher → mesher-shared}/shared.ts +2 -0
- package/src/playground/allEntitiesDebug.ts +1 -1
- package/src/three/chunkMeshManager.ts +1 -1
- package/src/three/entities.ts +19 -6
- package/src/three/entity/EntityMesh.ts +123 -140
- package/src/three/graphicsBackendBase.ts +13 -0
- package/src/three/holdingBlock.ts +1 -1
- package/src/three/holdingBlockLegacy.ts +1 -1
- package/src/three/modules/sciFiWorldReveal.ts +1 -1
- package/src/three/worldRendererThree.ts +2 -2
- package/src/wasm-mesher/README.md +90 -0
- package/src/{wasm-lib → wasm-mesher/bridge}/convertChunk.ts +2 -2
- package/src/{wasm-lib → wasm-mesher/bridge}/render-from-wasm.ts +4 -4
- package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +210 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher.js +881 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +24 -0
- package/src/{mesher/test → wasm-mesher/tests}/heightmapParity.test.ts +4 -4
- package/src/{mesher/test → wasm-mesher/tests}/mesherWasmConversionCache.test.ts +2 -2
- package/src/{mesher/test → wasm-mesher/tests}/splitColumnWasmOutput.test.ts +1 -1
- package/src/wasm-mesher/worker/mesherWasm.ts +1247 -0
- package/src/{mesher → wasm-mesher/worker}/mesherWasmConversionCache.ts +1 -1
- package/src/worldView/types.ts +90 -0
- package/src/mesher/mesherWasm.ts +0 -696
- package/wasm/wasm_mesher.d.ts +0 -46
- package/wasm/wasm_mesher.js +0 -443
- package/wasm/wasm_mesher_bg.wasm +0 -0
- package/wasm/wasm_mesher_bg.wasm.d.ts +0 -9
- /package/src/{mesher → mesher-legacy}/test/a.ts +0 -0
- /package/src/{mesher → mesher-legacy}/test/playground.ts +0 -0
- /package/src/{mesher → mesher-legacy}/test/run/chunk.ts +0 -0
- /package/src/{mesher → mesher-legacy}/test/snapshotUtils.ts +0 -0
- /package/src/{mesher → mesher-shared}/blockEntityMetadata.ts +0 -0
- /package/src/{mesher → mesher-shared}/computeHeightmap.ts +0 -0
- /package/src/{mesher → mesher-shared}/models.ts +0 -0
- /package/src/{mesher → mesher-shared}/modelsGeometryCommon.ts +0 -0
- /package/src/{mesher → mesher-shared}/standaloneRenderer.ts +0 -0
- /package/src/{mesher → mesher-shared}/world.ts +0 -0
- /package/src/{mesher → mesher-shared}/worldConstants.ts +0 -0
- /package/src/{mesher → wasm-mesher/worker}/mesherWasmRequestTracker.ts +0 -0
|
@@ -57,21 +57,33 @@ interface JsonModel {
|
|
|
57
57
|
bones: JsonBone[]
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
export type CustomModelMetadata = {
|
|
61
|
+
scale?: number
|
|
62
|
+
offset?: { x?: number, y?: number, z?: number }
|
|
63
|
+
texture?: string
|
|
64
|
+
textures?: Record<string, string>
|
|
65
|
+
animation?: string
|
|
66
|
+
animationLoop?: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type CustomModelPart = {
|
|
70
|
+
modelPath: string | ArrayBuffer
|
|
71
|
+
modelType: 'obj' | 'bedrock' | 'gltf'
|
|
72
|
+
metadata?: CustomModelMetadata
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One part, or several (same idea as `models[]` on the custom-model-overlay). */
|
|
76
|
+
export type EntityCustomModel = CustomModelPart | { parts: CustomModelPart[] }
|
|
77
|
+
|
|
60
78
|
interface EntityOverrides {
|
|
61
79
|
textures?: Record<string, string>
|
|
62
80
|
rotation?: Record<string, { x?: number; y?: number; z?: number }>
|
|
63
|
-
customModel?:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
texture?: string
|
|
70
|
-
textures?: Record<string, string>
|
|
71
|
-
animation?: string
|
|
72
|
-
animationLoop?: boolean
|
|
73
|
-
}
|
|
74
|
-
}
|
|
81
|
+
customModel?: EntityCustomModel
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeCustomModelParts (custom: EntityCustomModel): CustomModelPart[] {
|
|
85
|
+
if ('parts' in custom && Array.isArray(custom.parts)) return custom.parts
|
|
86
|
+
return [custom as CustomModelPart]
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
const elemFaces: Record<string, ElemFace> = {
|
|
@@ -443,7 +455,7 @@ interface EntityGeometry {
|
|
|
443
455
|
export type EntityModelType = 'obj' | 'bedrock' | 'gltf'
|
|
444
456
|
|
|
445
457
|
export type EntityDebugFlags = {
|
|
446
|
-
type?: 'obj' | 'bedrock' | 'special'
|
|
458
|
+
type?: 'obj' | 'bedrock' | 'gltf' | 'special'
|
|
447
459
|
tempMap?: string
|
|
448
460
|
textureMap?: boolean
|
|
449
461
|
errors?: string[]
|
|
@@ -453,9 +465,7 @@ export type EntityDebugFlags = {
|
|
|
453
465
|
export class EntityMesh {
|
|
454
466
|
mesh!: THREE.Object3D
|
|
455
467
|
animations?: THREE.AnimationClip[]
|
|
456
|
-
private
|
|
457
|
-
private initialAnimation?: string
|
|
458
|
-
private initialLoop?: boolean
|
|
468
|
+
private animationControllers: Array<ReturnType<typeof createAnimatedObject>> = []
|
|
459
469
|
|
|
460
470
|
constructor(
|
|
461
471
|
version: string,
|
|
@@ -471,145 +481,117 @@ export class EntityMesh {
|
|
|
471
481
|
debugFlags.tempMap = mappedValue
|
|
472
482
|
}
|
|
473
483
|
|
|
474
|
-
// Handle custom model override
|
|
484
|
+
// Handle custom model override (single or multiple parts)
|
|
475
485
|
if (overrides.customModel) {
|
|
476
|
-
|
|
486
|
+
const parts = normalizeCustomModelParts(overrides.customModel)
|
|
477
487
|
this.mesh = new THREE.Object3D()
|
|
478
488
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
489
|
+
for (let i = 0; i < parts.length; i++) {
|
|
490
|
+
const { modelPath, modelType, metadata } = parts[i]
|
|
491
|
+
const partRoot = new THREE.Object3D()
|
|
492
|
+
partRoot.name = `custom_part_${i}`
|
|
493
|
+
|
|
494
|
+
switch (modelType) {
|
|
495
|
+
case 'gltf': {
|
|
496
|
+
const loader = new GLTFLoader()
|
|
497
|
+
void loader.parseAsync(modelPath, '').then(gltf => {
|
|
498
|
+
partRoot.add(gltf.scene)
|
|
499
|
+
if (metadata?.scale) {
|
|
500
|
+
const s = metadata.scale
|
|
501
|
+
partRoot.scale.set(s, s, s)
|
|
502
|
+
}
|
|
503
|
+
if (metadata?.offset) {
|
|
504
|
+
const { x = 0, y = 0, z = 0 } = metadata.offset
|
|
505
|
+
partRoot.position.set(x, y, z)
|
|
506
|
+
}
|
|
507
|
+
if (metadata?.texture) {
|
|
508
|
+
const texture = new THREE.TextureLoader().load(metadata.texture)
|
|
509
|
+
texture.minFilter = THREE.NearestFilter
|
|
510
|
+
texture.magFilter = THREE.NearestFilter
|
|
511
|
+
partRoot.traverse((child) => {
|
|
512
|
+
if (child instanceof THREE.Mesh) {
|
|
513
|
+
child.material = new THREE.MeshBasicMaterial({
|
|
514
|
+
map: texture,
|
|
515
|
+
transparent: true,
|
|
516
|
+
alphaTest: 0.1
|
|
517
|
+
})
|
|
518
|
+
}
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
if (gltf.animations?.length) {
|
|
522
|
+
this.animations = [...(this.animations ?? []), ...gltf.animations]
|
|
523
|
+
const controller = createAnimatedObject(partRoot, gltf.animations)
|
|
524
|
+
this.animationControllers.push(controller)
|
|
525
|
+
const animationName = metadata?.animation
|
|
526
|
+
const loop = metadata?.animationLoop ?? true
|
|
527
|
+
if (animationName) {
|
|
528
|
+
controller.playAnimation(animationName, loop)
|
|
529
|
+
} else {
|
|
530
|
+
controller.playAnimation(gltf.animations[0].name, loop)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}).catch(err => {
|
|
534
|
+
console.error('Failed to load GLTF model:', err)
|
|
535
|
+
})
|
|
536
|
+
break
|
|
537
|
+
}
|
|
538
|
+
case 'obj': {
|
|
539
|
+
const objLoader = new OBJLoader()
|
|
540
|
+
const obj = objLoader.parse(modelPath as string)
|
|
491
541
|
if (metadata?.scale) {
|
|
492
542
|
const { scale } = metadata
|
|
493
|
-
|
|
543
|
+
obj.scale.set(scale, scale, scale)
|
|
494
544
|
}
|
|
495
545
|
if (metadata?.offset) {
|
|
496
546
|
const { x = 0, y = 0, z = 0 } = metadata.offset
|
|
497
|
-
|
|
547
|
+
obj.position.set(x, y, z)
|
|
498
548
|
}
|
|
499
|
-
|
|
500
|
-
// Apply texture if provided
|
|
501
549
|
if (metadata?.texture) {
|
|
502
550
|
const texture = new THREE.TextureLoader().load(metadata.texture)
|
|
503
551
|
texture.minFilter = THREE.NearestFilter
|
|
504
552
|
texture.magFilter = THREE.NearestFilter
|
|
505
|
-
|
|
553
|
+
const material = new THREE.MeshBasicMaterial({
|
|
554
|
+
map: texture,
|
|
555
|
+
transparent: true,
|
|
556
|
+
alphaTest: 0.1
|
|
557
|
+
})
|
|
558
|
+
obj.traverse((child) => {
|
|
506
559
|
if (child instanceof THREE.Mesh) {
|
|
507
|
-
child.material =
|
|
508
|
-
map: texture,
|
|
509
|
-
transparent: true,
|
|
510
|
-
alphaTest: 0.1
|
|
511
|
-
})
|
|
560
|
+
child.material = material
|
|
512
561
|
}
|
|
513
562
|
})
|
|
514
563
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (gltf.animations && gltf.animations.length > 0) {
|
|
518
|
-
this.animations = gltf.animations
|
|
519
|
-
const animationName = metadata?.animation
|
|
520
|
-
const loop = metadata?.animationLoop ?? true
|
|
521
|
-
|
|
522
|
-
// Store initial animation settings for later use
|
|
523
|
-
this.initialAnimation = animationName
|
|
524
|
-
this.initialLoop = loop
|
|
525
|
-
|
|
526
|
-
// Create animation controller with onBeforeRender support
|
|
527
|
-
this.animationController = createAnimatedObject(this.mesh, gltf.animations)
|
|
528
|
-
|
|
529
|
-
if (animationName) {
|
|
530
|
-
// Play animation from config
|
|
531
|
-
this.playAnimation(animationName, loop)
|
|
532
|
-
} else {
|
|
533
|
-
// Play first animation
|
|
534
|
-
const clip = gltf.animations[0]
|
|
535
|
-
this.animationController.playAnimation(clip.name, loop)
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}).catch(err => {
|
|
539
|
-
console.error('Failed to load GLTF model:', err)
|
|
540
|
-
})
|
|
541
|
-
|
|
542
|
-
// debugFlags.type = 'gltf'
|
|
543
|
-
return
|
|
544
|
-
}
|
|
545
|
-
case 'obj': {
|
|
546
|
-
const objLoader = new OBJLoader()
|
|
547
|
-
const obj = objLoader.parse(modelPath as string)
|
|
548
|
-
|
|
549
|
-
// Apply metadata overrides if available
|
|
550
|
-
if (metadata?.scale) {
|
|
551
|
-
const { scale } = metadata
|
|
552
|
-
obj.scale.set(scale, scale, scale)
|
|
553
|
-
}
|
|
554
|
-
if (metadata?.offset) {
|
|
555
|
-
const { x = 0, y = 0, z = 0 } = metadata.offset
|
|
556
|
-
obj.position.set(x, y, z)
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Apply texture if provided
|
|
560
|
-
if (metadata?.texture) {
|
|
561
|
-
const texture = new THREE.TextureLoader().load(metadata.texture)
|
|
562
|
-
texture.minFilter = THREE.NearestFilter
|
|
563
|
-
texture.magFilter = THREE.NearestFilter
|
|
564
|
-
const material = new THREE.MeshBasicMaterial({
|
|
565
|
-
map: texture,
|
|
566
|
-
transparent: true,
|
|
567
|
-
alphaTest: 0.1
|
|
568
|
-
})
|
|
569
|
-
obj.traverse((child) => {
|
|
570
|
-
if (child instanceof THREE.Mesh) {
|
|
571
|
-
child.material = material
|
|
572
|
-
}
|
|
573
|
-
})
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
this.mesh = obj
|
|
577
|
-
debugFlags.type = 'obj'
|
|
578
|
-
return
|
|
579
|
-
}
|
|
580
|
-
case 'bedrock': {
|
|
581
|
-
// Parse bedrock model JSON
|
|
582
|
-
const modelData = JSON.parse(modelPath as string)
|
|
583
|
-
this.mesh = new THREE.Object3D()
|
|
584
|
-
|
|
585
|
-
// Apply metadata overrides
|
|
586
|
-
if (metadata?.scale) {
|
|
587
|
-
this.mesh.scale.set(metadata.scale, metadata.scale, metadata.scale)
|
|
564
|
+
partRoot.add(obj)
|
|
565
|
+
break
|
|
588
566
|
}
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
567
|
+
case 'bedrock': {
|
|
568
|
+
const modelData = JSON.parse(modelPath as string)
|
|
569
|
+
if (metadata?.scale) {
|
|
570
|
+
partRoot.scale.set(metadata.scale, metadata.scale, metadata.scale)
|
|
571
|
+
}
|
|
572
|
+
if (metadata?.offset) {
|
|
573
|
+
const { x = 0, y = 0, z = 0 } = metadata.offset
|
|
574
|
+
partRoot.position.set(x, y, z)
|
|
575
|
+
}
|
|
576
|
+
for (const [name, jsonModel] of Object.entries(modelData.geometry)) {
|
|
577
|
+
const texture = metadata?.textures?.[name] ?? modelData.textures?.[name]
|
|
578
|
+
if (!texture) continue
|
|
579
|
+
const mesh = getMesh(worldRenderer,
|
|
580
|
+
texture.endsWith('.png') || texture.startsWith('data:image/') || texture.startsWith('block:')
|
|
581
|
+
? texture : texture + '.png',
|
|
582
|
+
jsonModel as JsonModel,
|
|
583
|
+
overrides,
|
|
584
|
+
debugFlags)
|
|
585
|
+
mesh.name = `geometry_${name}`
|
|
586
|
+
partRoot.add(mesh)
|
|
587
|
+
}
|
|
588
|
+
break
|
|
607
589
|
}
|
|
608
|
-
debugFlags.type = 'bedrock'
|
|
609
|
-
return
|
|
610
590
|
}
|
|
611
|
-
|
|
591
|
+
this.mesh.add(partRoot)
|
|
612
592
|
}
|
|
593
|
+
debugFlags.type = parts.length === 1 ? parts[0].modelType : 'special'
|
|
594
|
+
return
|
|
613
595
|
}
|
|
614
596
|
|
|
615
597
|
if (externalModels[type]) {
|
|
@@ -696,15 +678,16 @@ export class EntityMesh {
|
|
|
696
678
|
}
|
|
697
679
|
|
|
698
680
|
playAnimation(name: string, loop = false) {
|
|
699
|
-
if (!this.
|
|
681
|
+
if (!this.animationControllers.length) {
|
|
700
682
|
console.warn('No animation controller available')
|
|
701
683
|
return
|
|
702
684
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
685
|
+
let ok = false
|
|
686
|
+
for (const c of this.animationControllers) {
|
|
687
|
+
if (c.playAnimation(name, loop)) ok = true
|
|
688
|
+
}
|
|
689
|
+
if (!ok) {
|
|
690
|
+
console.warn(`Animation "${name}" not found on any custom model part`)
|
|
708
691
|
}
|
|
709
692
|
}
|
|
710
693
|
|
|
@@ -16,6 +16,7 @@ import { WorldRendererThree } from './worldRendererThree'
|
|
|
16
16
|
import { DocumentRenderer, isWebWorker, ThreeRendererMainData } from './documentRenderer'
|
|
17
17
|
import { PanoramaRenderer } from './panorama'
|
|
18
18
|
import { WorldViewWorker } from '../worldView'
|
|
19
|
+
import type { FeedChunkPacketPayload } from '../worldView/types'
|
|
19
20
|
|
|
20
21
|
// Disable Three.js color management for compatibility
|
|
21
22
|
THREE.ColorManagement.enabled = false
|
|
@@ -80,6 +81,18 @@ export const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
|
|
|
80
81
|
// Import dynamically to avoid circular dependencies
|
|
81
82
|
const { applyWorldGeometryExport } = await import('./worldGeometryExport')
|
|
82
83
|
return applyWorldGeometryExport(worldRenderer, exportData)
|
|
84
|
+
},
|
|
85
|
+
feedChunkPacket(payload: FeedChunkPacketPayload) {
|
|
86
|
+
// Forward parsed/raw map_chunk + update_light packets from the
|
|
87
|
+
// web-client to all WASM mesher workers. The fan-out below uses
|
|
88
|
+
// structured clone (one Uint8Array can only be transferred to a
|
|
89
|
+
// single recipient); useWorkerProxy still gives zero-copy transfer
|
|
90
|
+
// from main into the off-thread renderer worker for free.
|
|
91
|
+
const { kind, ...rest } = payload
|
|
92
|
+
const message = { type: kind, ...rest }
|
|
93
|
+
for (const worker of worldRenderer.workers) {
|
|
94
|
+
worker.postMessage(message)
|
|
95
|
+
}
|
|
83
96
|
}
|
|
84
97
|
}
|
|
85
98
|
}
|
|
@@ -12,7 +12,7 @@ import { disposeObject } from './threeJsUtils'
|
|
|
12
12
|
import type { IHoldingBlock } from './holdingBlockTypes'
|
|
13
13
|
import { HandItemBlock, MovementState } from '../playerState/types'
|
|
14
14
|
import { PlayerStateRenderer } from '../playerState/playerState'
|
|
15
|
-
import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
15
|
+
import { getThreeBlockModelGroup } from '../mesher-shared/standaloneRenderer'
|
|
16
16
|
import { IndexedData } from 'minecraft-data'
|
|
17
17
|
import { WorldRendererConfig } from '../graphicsBackend'
|
|
18
18
|
import { computeCameraBob, type CameraBobInput } from '../lib/cameraBobbing'
|
|
@@ -12,7 +12,7 @@ import { WorldRendererThree } from './worldRendererThree'
|
|
|
12
12
|
import { disposeObject } from './threeJsUtils'
|
|
13
13
|
import { HandItemBlock, MovementState } from '../playerState/types'
|
|
14
14
|
import { PlayerStateRenderer } from '../playerState/playerState'
|
|
15
|
-
import { getThreeBlockModelGroup } from '../mesher/standaloneRenderer'
|
|
15
|
+
import { getThreeBlockModelGroup } from '../mesher-shared/standaloneRenderer'
|
|
16
16
|
import { IndexedData } from 'minecraft-data'
|
|
17
17
|
import { WorldRendererConfig } from '../graphicsBackend'
|
|
18
18
|
import { IHoldingBlock } from './holdingBlockTypes'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import type { WorldRendererThree } from '../worldRendererThree'
|
|
4
4
|
import type { RendererModuleController, RendererModuleManifest } from '../rendererModuleSystem'
|
|
5
|
-
import type { MesherGeometryOutput } from '../../mesher/shared'
|
|
5
|
+
import type { MesherGeometryOutput } from '../../mesher-shared/shared'
|
|
6
6
|
|
|
7
7
|
const SCI_FI_CYAN = new THREE.Color(13 / 255, 234 / 255, 238 / 255)
|
|
8
8
|
const CHUNKS_THRESHOLD = 9
|
|
@@ -10,9 +10,9 @@ import { DisplayWorldOptions, GraphicsInitOptions } from '../graphicsBackend/typ
|
|
|
10
10
|
import { chunkPos, sectionPos } from '../lib/simpleUtils'
|
|
11
11
|
import { WorldRendererCommon } from '../lib/worldrendererCommon'
|
|
12
12
|
import { addNewStat } from '../lib/ui/newStats'
|
|
13
|
-
import { MesherGeometryOutput } from '../mesher/shared'
|
|
13
|
+
import { MesherGeometryOutput } from '../mesher-shared/shared'
|
|
14
14
|
import { ItemSpecificContextProperties } from '../playerState/types'
|
|
15
|
-
import { setBlockPosition } from '../mesher/standaloneRenderer'
|
|
15
|
+
import { setBlockPosition } from '../mesher-shared/standaloneRenderer'
|
|
16
16
|
import { getMyHand } from './hand'
|
|
17
17
|
import { createHoldingBlock } from './holdingBlockFactory'
|
|
18
18
|
import type { IHoldingBlock } from './holdingBlockTypes'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# src/wasm-mesher (JS side of the WASM mesher)
|
|
2
|
+
|
|
3
|
+
This directory is the JS-side counterpart of the Rust crate at
|
|
4
|
+
`/wasm-mesher/`. It contains everything that runs in the browser/Node:
|
|
5
|
+
the worker, the JS↔Rust bridge, the wasm-pack runtime artefacts, and
|
|
6
|
+
the WASM-only unit tests.
|
|
7
|
+
|
|
8
|
+
The Rust source itself stays at the repo root (`/wasm-mesher/`); only
|
|
9
|
+
its compiled output is mirrored here.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/wasm-mesher/
|
|
15
|
+
├── runtime-build/ ← wasm-pack output (--target web)
|
|
16
|
+
│ ├── wasm_mesher.js ← JS shim generated by wasm-bindgen
|
|
17
|
+
│ ├── wasm_mesher_bg.wasm ← compiled WASM module
|
|
18
|
+
│ └── *.d.ts ← typings
|
|
19
|
+
├── bridge/ ← thin JS layer between Rust output and the renderer
|
|
20
|
+
│ ├── convertChunk.ts ← legacy slow path: prismarine-chunk → typed arrays
|
|
21
|
+
│ └── render-from-wasm.ts ← geometry → Three.js + section split + heightmap
|
|
22
|
+
├── worker/ ← runs inside the dedicated mesher Web Worker
|
|
23
|
+
│ ├── mesherWasm.ts ← worker entry; routes by version (1.16/17/18+)
|
|
24
|
+
│ ├── mesherWasmConversionCache.ts
|
|
25
|
+
│ └── mesherWasmRequestTracker.ts
|
|
26
|
+
├── tests/ ← WASM-only vitest tests (run by `pnpm unit-test`)
|
|
27
|
+
│ ├── heightmapParity.test.ts
|
|
28
|
+
│ ├── splitColumnWasmOutput.test.ts
|
|
29
|
+
│ └── mesherWasmConversionCache.test.ts
|
|
30
|
+
└── README.md ← you are here
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Other related directories:
|
|
34
|
+
|
|
35
|
+
- **`/wasm-mesher/`** (repo root) — the Rust crate (`src/*.rs`,
|
|
36
|
+
`Cargo.toml`, `build.sh`) and standalone TS test harnesses
|
|
37
|
+
(`tests/test-chunk.ts`, `tests/test-section-boundary.ts`).
|
|
38
|
+
See `wasm-mesher/README.md` for the public Rust API.
|
|
39
|
+
- **`src/mesher-shared/`** — modules used by **both** this WASM path and
|
|
40
|
+
the legacy JS mesher (`models`, `world`, `shared`, `worldConstants`,
|
|
41
|
+
`computeHeightmap`, …).
|
|
42
|
+
- **`src/mesher-legacy/`** — the original JS mesher, kept until the WASM
|
|
43
|
+
path proves itself in production. Will be removed in a follow-up PR.
|
|
44
|
+
|
|
45
|
+
## Building
|
|
46
|
+
|
|
47
|
+
The worker bundle is built by `scripts/buildMesherWorker.mjs`:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm build:mesher # esbuild → dist/mesherWasm.js (web-client contract)
|
|
51
|
+
pnpm watch:mesher # incremental rebuild on change
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The output filename `dist/mesherWasm.js` is **load-bearing** — the
|
|
55
|
+
web-client imports the worker by that path. `entryNames: '[name]'` in
|
|
56
|
+
the build config keeps the output flat regardless of source-tree depth.
|
|
57
|
+
|
|
58
|
+
The WASM module itself (`runtime-build/wasm_mesher_bg.wasm`) is rebuilt
|
|
59
|
+
by:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pnpm build:wasm # → runs wasm-mesher/build.sh web (release)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This requires `wasm-pack` on PATH; see `wasm-mesher/README.md` for setup.
|
|
66
|
+
|
|
67
|
+
## Testing
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm unit-test --run # all vitest tests, including this dir's tests/
|
|
71
|
+
pnpm test:wasm # snapshot + boundary + heightmap (cjs harnesses)
|
|
72
|
+
pnpm test:wasm:boundary # boundary + heightmap only
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The vitest tests in `tests/` exercise the worker logic against real WASM:
|
|
76
|
+
|
|
77
|
+
- `heightmapParity.test.ts` — JS `computeHeightmap` vs Rust output.
|
|
78
|
+
- `splitColumnWasmOutput.test.ts` — column-split correctness.
|
|
79
|
+
- `mesherWasmConversionCache.test.ts` — cache eviction and column reuse.
|
|
80
|
+
|
|
81
|
+
## Notes
|
|
82
|
+
|
|
83
|
+
- `bridge/convertChunk.ts` is the **slow path** (prismarine-chunk → typed
|
|
84
|
+
arrays in JS). For 1.18+ chunks the worker prefers the fast path:
|
|
85
|
+
`parseChunkDump118FullColumnAll` (a single Rust call) consumed via
|
|
86
|
+
`worker/mesherWasm.ts`. See `docs/issues/issue-15-wasm/history.md` for
|
|
87
|
+
the rationale and benchmarks.
|
|
88
|
+
- Routing of raw `map_chunk` packets (1.16 / 1.17 / 1.18+) lives in
|
|
89
|
+
`worker/mesherWasm.ts`; per-version Rust parsers are in
|
|
90
|
+
`wasm-mesher/src/parser_*.rs`.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Vec3 } from 'vec3'
|
|
3
3
|
import MinecraftData from 'minecraft-data'
|
|
4
4
|
import PrismarineBlockLoader from 'prismarine-block'
|
|
5
|
-
import moreBlockDataGeneratedJson from '
|
|
5
|
+
import moreBlockDataGeneratedJson from '../../lib/moreBlockDataGenerated.json'
|
|
6
6
|
|
|
7
7
|
type BlockMeta = {
|
|
8
8
|
invisibleBlocks: Uint16Array
|
|
@@ -54,7 +54,7 @@ const isLikelyFullCubeBlockName = (name: string) => {
|
|
|
54
54
|
return true
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const getBlockMeta = (version: string): BlockMeta => {
|
|
57
|
+
export const getBlockMeta = (version: string): BlockMeta => {
|
|
58
58
|
const cached = metaCache.get(version)
|
|
59
59
|
if (cached) return cached
|
|
60
60
|
|
|
@@ -9,10 +9,10 @@ import blockStatesModels from 'mc-assets/dist/blockStatesModels.json'
|
|
|
9
9
|
import MinecraftData from 'minecraft-data'
|
|
10
10
|
import PrismarineBlockLoader from 'prismarine-block'
|
|
11
11
|
import { Vec3 } from 'vec3'
|
|
12
|
-
import { elemFaces, buildRotationMatrix, matmul3, matmulmat3, vecadd3, vecsub3 } from '
|
|
13
|
-
import type { ExportedWorldGeometry, ExportedSection } from '
|
|
14
|
-
import type { MesherGeometryOutput } from '
|
|
15
|
-
import type { World } from '
|
|
12
|
+
import { elemFaces, buildRotationMatrix, matmul3, matmulmat3, vecadd3, vecsub3 } from '../../mesher-shared/modelsGeometryCommon'
|
|
13
|
+
import type { ExportedWorldGeometry, ExportedSection } from '../../three/worldGeometryExport'
|
|
14
|
+
import type { MesherGeometryOutput } from '../../mesher-shared/shared'
|
|
15
|
+
import type { World } from '../../mesher-shared/world'
|
|
16
16
|
|
|
17
17
|
// Handle both default and named export
|
|
18
18
|
const worldBlockProvider = (worldBlockProviderModule as any).default || worldBlockProviderModule
|