minecraft-renderer 0.1.72 → 0.1.74
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/README.md +1 -1
- package/dist/mesher.js +81 -81
- package/dist/mesher.js.map +3 -3
- package/dist/mesherWasm.js +1183 -943
- package/dist/minecraft-renderer.js +253 -80
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +1735 -1002
- package/package.json +3 -3
- package/src/graphicsBackend/config.ts +4 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +2 -7
- package/src/graphicsBackend/rendererOptionsSync.ts +1 -1
- package/src/graphicsBackend/types.ts +1 -0
- package/src/lib/bakeLegacyLight.ts +17 -0
- package/src/lib/bindAbortableListener.ts +1 -1
- package/src/lib/blockEntityLightRegistry.test.ts +18 -0
- package/src/lib/blockEntityLightRegistry.ts +75 -0
- package/src/lib/blockEntityLighting.test.ts +30 -0
- package/src/lib/blockEntityLighting.ts +53 -0
- package/src/lib/createPlayerObject.ts +1 -1
- package/src/lib/worldrendererCommon.reconfigure.test.ts +4 -1
- package/src/lib/worldrendererCommon.removeColumn.test.ts +8 -4
- package/src/lib/worldrendererCommon.ts +15 -7
- package/src/mesher-shared/blockEntityMetadata.test.ts +33 -0
- package/src/mesher-shared/blockEntityMetadata.ts +19 -3
- package/src/mesher-shared/exportedGeometryTypes.ts +11 -0
- package/src/mesher-shared/models.ts +161 -92
- package/src/mesher-shared/shared.ts +15 -4
- package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +40 -0
- package/src/mesher-shared/world.ts +12 -0
- package/src/mesher-shared/worldLighting.test.ts +54 -0
- package/src/playground/baseScene.ts +1 -1
- package/src/three/bannerRenderer.ts +14 -4
- package/src/three/chunkMeshManager.ts +663 -69
- package/src/three/cubeDrawSpans.ts +74 -0
- package/src/three/cubeMultiDraw.ts +119 -0
- package/src/three/documentRenderer.ts +0 -2
- package/src/three/entities.ts +7 -7
- package/src/three/entity/EntityMesh.ts +7 -5
- package/src/three/entity/gltfAnimationUtils.ts +5 -3
- package/src/three/globalBlockBuffer.ts +208 -12
- package/src/three/globalLegacyBuffer.ts +701 -0
- package/src/three/graphicsBackendBase.ts +9 -5
- package/src/three/itemMesh.ts +6 -3
- package/src/three/legacySectionCull.ts +85 -0
- package/src/three/modules/rain.ts +22 -21
- package/src/three/modules/sciFiWorldReveal.ts +347 -703
- package/src/three/modules/starfield.ts +19 -6
- package/src/three/sectionRaycastAabb.ts +25 -0
- package/src/three/shaders/cubeBlockShader.ts +80 -17
- package/src/three/shaders/legacyBlockShader.ts +292 -0
- package/src/three/skyboxRenderer.ts +1 -1
- package/src/three/tests/chunkMeshManagerLegacy.test.ts +286 -0
- package/src/three/tests/cubeDrawSpans.test.ts +73 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +360 -0
- package/src/three/tests/legacySectionCull.test.ts +80 -0
- package/src/three/tests/signTextureCache.test.ts +83 -0
- package/src/three/threeJsMedia.ts +2 -2
- package/src/three/waypointSprite.ts +2 -2
- package/src/three/world/cursorBlock.ts +1 -0
- package/src/three/world/vr.ts +2 -2
- package/src/three/worldGeometryExport.ts +83 -26
- package/src/three/worldRendererThree.ts +100 -30
- package/src/wasm-mesher/bridge/render-from-wasm.ts +214 -72
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +18 -6
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +20 -0
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +80 -12
- package/src/wasm-mesher/worker/mesherWasm.ts +70 -14
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +11 -0
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +15 -0
|
@@ -16,12 +16,19 @@ import {
|
|
|
16
16
|
SHADER_CUBES_WORDS_PER_FACE,
|
|
17
17
|
} from '../bridge/shaderCubeBridge'
|
|
18
18
|
import { GlobalBlockBuffer } from '../../three/globalBlockBuffer'
|
|
19
|
-
import {
|
|
19
|
+
import { buildVisibleCubeSpans } from '../../three/cubeDrawSpans'
|
|
20
|
+
import { createCubeBlockMaterial, computeSectionOriginRel } from '../../three/shaders/cubeBlockShader'
|
|
20
21
|
import * as THREE from 'three'
|
|
21
22
|
import { renderWasmOutputToGeometry } from '../bridge/render-from-wasm'
|
|
22
23
|
|
|
23
24
|
const VERSION = '1.16.5'
|
|
24
25
|
const STONE = 1
|
|
26
|
+
|
|
27
|
+
function requireShaderCubeResources() {
|
|
28
|
+
const resources = getShaderCubeResources()
|
|
29
|
+
if (!resources) throw new Error('shader cube resources unavailable in test')
|
|
30
|
+
return resources
|
|
31
|
+
}
|
|
25
32
|
/** mc-assets blocksAtlases.json → stone */
|
|
26
33
|
const STONE_ATLAS_TILE_INDEX = 552
|
|
27
34
|
|
|
@@ -38,7 +45,7 @@ test('packWord2: AO diagonal flip sets bit 12', () => {
|
|
|
38
45
|
light_data: [[1, 1, 1, 1]],
|
|
39
46
|
light_combined: [[255, 255, 255, 255]],
|
|
40
47
|
}
|
|
41
|
-
const { textureIndexMapping, tintPalette } =
|
|
48
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
42
49
|
const model = {
|
|
43
50
|
elements: [{
|
|
44
51
|
faces: {
|
|
@@ -77,7 +84,7 @@ test('packWord0: section-local lx/ly/lz and face id', () => {
|
|
|
77
84
|
light_data: [[0.5, 0.5, 0.5, 0.5]],
|
|
78
85
|
light_combined: [[128, 128, 128, 128]],
|
|
79
86
|
}
|
|
80
|
-
const { textureIndexMapping, tintPalette } =
|
|
87
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
81
88
|
const model = {
|
|
82
89
|
elements: [{
|
|
83
90
|
faces: {
|
|
@@ -110,7 +117,7 @@ test('packWord0: section-local lx/ly/lz and face id', () => {
|
|
|
110
117
|
})
|
|
111
118
|
|
|
112
119
|
test('isShaderCubeBlock: rejects model rotation and sectionHeight !== 16', () => {
|
|
113
|
-
const { textureIndexMapping } =
|
|
120
|
+
const { textureIndexMapping } = requireShaderCubeResources()
|
|
114
121
|
const baseModel = {
|
|
115
122
|
elements: [{
|
|
116
123
|
faces: {
|
|
@@ -221,7 +228,7 @@ test('south face: AO corners remapped to shader order (elemFaces [0,3,1,2] → s
|
|
|
221
228
|
light_data: [[1, 1, 1, 1]],
|
|
222
229
|
light_combined: [[10, 20, 30, 40]],
|
|
223
230
|
}
|
|
224
|
-
const { textureIndexMapping, tintPalette } =
|
|
231
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
225
232
|
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
226
233
|
tryBuildShaderCubeInstances(
|
|
227
234
|
block,
|
|
@@ -247,7 +254,7 @@ test('south face: diagonal flip uses remapped AO (differs from raw elemFaces for
|
|
|
247
254
|
light_data: [[1, 1, 1, 1]],
|
|
248
255
|
light_combined: [[255, 255, 255, 255]],
|
|
249
256
|
}
|
|
250
|
-
const { textureIndexMapping, tintPalette } =
|
|
257
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
251
258
|
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
252
259
|
const opts = {
|
|
253
260
|
sectionOrigin: { x: 0, y: 0, z: 0 },
|
|
@@ -284,7 +291,7 @@ test('doAO false: full bright AO/light and no diagonal flip', () => {
|
|
|
284
291
|
light_data: [[0, 0, 0, 0]],
|
|
285
292
|
light_combined: [[0, 0, 0, 0]],
|
|
286
293
|
}
|
|
287
|
-
const { textureIndexMapping, tintPalette } =
|
|
294
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
288
295
|
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
289
296
|
tryBuildShaderCubeInstances(
|
|
290
297
|
block,
|
|
@@ -328,7 +335,7 @@ test.each(SECTION_ORIGIN_ROUND_TRIP_CASES)(
|
|
|
328
335
|
light_data: [[1, 1, 1, 1]],
|
|
329
336
|
light_combined: [[255, 255, 255, 255]],
|
|
330
337
|
}
|
|
331
|
-
const { textureIndexMapping, tintPalette } =
|
|
338
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
332
339
|
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
333
340
|
tryBuildShaderCubeInstances(
|
|
334
341
|
block,
|
|
@@ -349,6 +356,38 @@ test('packWord2Empty: bit 18 set regardless of high X/Z bits in word2', () => {
|
|
|
349
356
|
expect(withHighBits & (1 << WORD2.EMPTY_SHIFT)).not.toBe(0)
|
|
350
357
|
})
|
|
351
358
|
|
|
359
|
+
test('section index relative decode past 2^20: exact integer subtract', () => {
|
|
360
|
+
const sectionBlockX = 21_050_000
|
|
361
|
+
const renderOrigin = { x: 21_000_000, y: 0, z: 0 }
|
|
362
|
+
const words: number[] = []
|
|
363
|
+
const block = {
|
|
364
|
+
position: [0, 0, 0] as [number, number, number],
|
|
365
|
+
visible_faces: 1 << 2,
|
|
366
|
+
ao_data: [[3, 3, 3, 3]],
|
|
367
|
+
light_data: [[1, 1, 1, 1]],
|
|
368
|
+
light_combined: [[255, 255, 255, 255]],
|
|
369
|
+
}
|
|
370
|
+
const { textureIndexMapping, tintPalette } = requireShaderCubeResources()
|
|
371
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
372
|
+
tryBuildShaderCubeInstances(
|
|
373
|
+
block,
|
|
374
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
375
|
+
model,
|
|
376
|
+
{
|
|
377
|
+
sectionOrigin: { x: sectionBlockX, y: 0, z: 0 },
|
|
378
|
+
sectionHeight: 16,
|
|
379
|
+
tintPalette,
|
|
380
|
+
textureIndexMapping,
|
|
381
|
+
},
|
|
382
|
+
words,
|
|
383
|
+
)
|
|
384
|
+
const base = decodeSectionBaseFromWords(words[2]!, words[3]!)
|
|
385
|
+
const sX = base.x / 16
|
|
386
|
+
const sectionOriginRel = computeSectionOriginRel(renderOrigin)
|
|
387
|
+
const sXr = sX - sectionOriginRel.x
|
|
388
|
+
expect(sXr * 16).toBe(sectionBlockX - renderOrigin.x)
|
|
389
|
+
})
|
|
390
|
+
|
|
352
391
|
test('GlobalBlockBuffer: free-list reuses slot with EMPTY sentinel', () => {
|
|
353
392
|
const scene = new THREE.Scene()
|
|
354
393
|
const mat = createCubeBlockMaterial()
|
|
@@ -661,6 +700,35 @@ test('GlobalBlockBuffer: takeSectionData reads relocated section slot', () => {
|
|
|
661
700
|
mat.dispose()
|
|
662
701
|
})
|
|
663
702
|
|
|
703
|
+
test('GlobalBlockBuffer: pendingMove draw start uses oldStart for visible spans', () => {
|
|
704
|
+
const scene = new THREE.Scene()
|
|
705
|
+
const mat = createCubeBlockMaterial()
|
|
706
|
+
const buffer = new GlobalBlockBuffer(mat, scene)
|
|
707
|
+
|
|
708
|
+
buffer.addSection('a', makeSectionWords([10]), 1)
|
|
709
|
+
buffer.addSection('b', makeSectionWords([20]), 1)
|
|
710
|
+
buffer.addSection('c', makeSectionWords([30]), 1)
|
|
711
|
+
buffer.removeSection('b')
|
|
712
|
+
drainAllUploads(buffer)
|
|
713
|
+
|
|
714
|
+
buffer.compactStep()
|
|
715
|
+
const move = buffer.getPendingMove()
|
|
716
|
+
expect(move?.key).toBe('c')
|
|
717
|
+
|
|
718
|
+
const slotStart = buffer.getSectionSlot('c')!.start
|
|
719
|
+
expect(buffer.getSectionDrawStart('c')).toBe(move!.oldStart)
|
|
720
|
+
expect(buffer.getSectionDrawStart('c')).not.toBe(slotStart)
|
|
721
|
+
|
|
722
|
+
const spans = buildVisibleCubeSpans(
|
|
723
|
+
[{ start: buffer.getSectionDrawStart('c')!, count: 1 }],
|
|
724
|
+
buffer.getHighWatermark(),
|
|
725
|
+
)
|
|
726
|
+
expect(spans[0]?.start).toBe(move!.oldStart)
|
|
727
|
+
|
|
728
|
+
buffer.dispose()
|
|
729
|
+
mat.dispose()
|
|
730
|
+
})
|
|
731
|
+
|
|
664
732
|
test('GlobalBlockBuffer: uploadDirtyRange budgets large dirty span across frames', () => {
|
|
665
733
|
const scene = new THREE.Scene()
|
|
666
734
|
const mat = createCubeBlockMaterial()
|
|
@@ -679,12 +747,12 @@ test('GlobalBlockBuffer: uploadDirtyRange budgets large dirty span across frames
|
|
|
679
747
|
|
|
680
748
|
const w0Attr = buffer.mesh.geometry.getAttribute('a_w0') as THREE.InstancedBufferAttribute
|
|
681
749
|
buffer.uploadDirtyRange()
|
|
682
|
-
expect(w0Attr.
|
|
683
|
-
expect(w0Attr.
|
|
750
|
+
expect(w0Attr.updateRanges[0].start).toBe(0)
|
|
751
|
+
expect(w0Attr.updateRanges[0].count).toBe(15_000)
|
|
684
752
|
|
|
685
753
|
buffer.uploadDirtyRange()
|
|
686
|
-
expect(w0Attr.
|
|
687
|
-
expect(w0Attr.
|
|
754
|
+
expect(w0Attr.updateRanges[0].start).toBe(15_000)
|
|
755
|
+
expect(w0Attr.updateRanges[0].count).toBe(5_000)
|
|
688
756
|
|
|
689
757
|
buffer.uploadDirtyRange()
|
|
690
758
|
expect((buffer as unknown as { pendingRanges: unknown[] }).pendingRanges).toHaveLength(0)
|
|
@@ -8,6 +8,7 @@ import { worldColumnKey, World } from '../../mesher-shared/world'
|
|
|
8
8
|
import { handleGetHeightmap, EMPTY_COLUMN_HEIGHTMAP_SENTINEL } from '../../mesher-shared/computeHeightmap'
|
|
9
9
|
import { collectBlockEntityMetadata, type SignMeta, type HeadMeta, type BannerMeta } from '../../mesher-shared/blockEntityMetadata'
|
|
10
10
|
import { SectionRequestTracker } from './mesherWasmRequestTracker'
|
|
11
|
+
import { sectionYsForLightColumnDirty } from './mesherWasmLightDirty'
|
|
11
12
|
import {
|
|
12
13
|
CONVERSION_CACHE_LIMIT,
|
|
13
14
|
clearConversionCache,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
|
|
19
20
|
let wasm: typeof import('../runtime-build/wasm_mesher.js') | null = null
|
|
20
21
|
let wasmInitialized = false
|
|
22
|
+
let wasmReady = false // true ONLY after wasm.default() instantiates the module; gates light-packet parsing
|
|
21
23
|
|
|
22
24
|
// Pending raw `update_light` packets that arrived before WASM finished
|
|
23
25
|
// loading. Parsed and drained once `initWasm` resolves. Without this queue
|
|
@@ -33,7 +35,7 @@ const pendingUpdateLightV17: Array<{ rawPacket: Uint8Array, numSections: number
|
|
|
33
35
|
const pendingUpdateLightV16: Array<{ rawPacket: Uint8Array }> = []
|
|
34
36
|
|
|
35
37
|
function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): void {
|
|
36
|
-
if (!
|
|
38
|
+
if (!wasmReady) {
|
|
37
39
|
pendingUpdateLightV17.push({ rawPacket, numSections })
|
|
38
40
|
return
|
|
39
41
|
}
|
|
@@ -41,11 +43,13 @@ function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): voi
|
|
|
41
43
|
const parsed: any = (wasm as any).parseUpdateLightV17(rawPacket, numSections)
|
|
42
44
|
const x = (parsed.x as number) * 16
|
|
43
45
|
const z = (parsed.z as number) * 16
|
|
46
|
+
const skyLight = parsed.skyLight as Uint8Array
|
|
44
47
|
updateLightV17Cache.set(rawCacheKey(x, z), {
|
|
45
|
-
skyLight
|
|
48
|
+
skyLight,
|
|
46
49
|
blockLight: parsed.blockLight as Uint8Array,
|
|
47
50
|
})
|
|
48
51
|
invalidateConversion(x, z)
|
|
52
|
+
dirtyColumnSectionsForLightUpdate(x, z)
|
|
49
53
|
} catch (err) {
|
|
50
54
|
console.warn('[WASM Mesher] parseUpdateLightV17 failed:', err)
|
|
51
55
|
}
|
|
@@ -57,7 +61,7 @@ function processUpdateLightV17 (rawPacket: Uint8Array, numSections: number): voi
|
|
|
57
61
|
// chunk cache has the entry, and crossing the streams could mismatch a
|
|
58
62
|
// stale 1.17 column with 1.16 light or vice versa during version switches.
|
|
59
63
|
function processUpdateLightV16 (rawPacket: Uint8Array): void {
|
|
60
|
-
if (!
|
|
64
|
+
if (!wasmReady) {
|
|
61
65
|
pendingUpdateLightV16.push({ rawPacket })
|
|
62
66
|
return
|
|
63
67
|
}
|
|
@@ -70,6 +74,7 @@ function processUpdateLightV16 (rawPacket: Uint8Array): void {
|
|
|
70
74
|
blockLight: parsed.blockLight as Uint8Array,
|
|
71
75
|
})
|
|
72
76
|
invalidateConversion(x, z)
|
|
77
|
+
dirtyColumnSectionsForLightUpdate(x, z)
|
|
73
78
|
} catch (err) {
|
|
74
79
|
console.warn('[WASM Mesher] parseUpdateLightV17 (v16) failed:', err)
|
|
75
80
|
}
|
|
@@ -81,6 +86,7 @@ async function initWasm() {
|
|
|
81
86
|
wasmInitialized = true
|
|
82
87
|
wasm = await import('../runtime-build/wasm_mesher.js')
|
|
83
88
|
await wasm.default('/wasm_mesher_bg.wasm') as any
|
|
89
|
+
wasmReady = true // instance is now usable; drained packets below will pass the guard
|
|
84
90
|
|
|
85
91
|
if (pendingUpdateLightV17.length > 0) {
|
|
86
92
|
console.log('[WASM Mesher] draining', pendingUpdateLightV17.length, 'pending update_light v17 packets')
|
|
@@ -94,7 +100,10 @@ async function initWasm() {
|
|
|
94
100
|
for (const item of queue) processUpdateLightV16(item.rawPacket)
|
|
95
101
|
}
|
|
96
102
|
} catch (err) {
|
|
97
|
-
console.error(
|
|
103
|
+
console.error(
|
|
104
|
+
'[WASM Mesher] Failed to initialize WASM mesher — block lighting may stay at full brightness:',
|
|
105
|
+
err,
|
|
106
|
+
)
|
|
98
107
|
wasmInitialized = true // Don't try to initialize again
|
|
99
108
|
// Don't throw - allow worker to continue without WASM (will fail on first use)
|
|
100
109
|
}
|
|
@@ -195,6 +204,15 @@ function setSectionDirty(pos: Vec3, value = true) {
|
|
|
195
204
|
}
|
|
196
205
|
}
|
|
197
206
|
|
|
207
|
+
/** Re-mesh every section in a column after `update_light` updates the light cache. */
|
|
208
|
+
function dirtyColumnSectionsForLightUpdate (x: number, z: number) {
|
|
209
|
+
const worldMinY = config?.worldMinY ?? 0
|
|
210
|
+
const worldMaxY = config?.worldMaxY ?? 256
|
|
211
|
+
for (const y of sectionYsForLightColumnDirty(worldMinY, worldMaxY, SECTION_HEIGHT)) {
|
|
212
|
+
setSectionDirty(new Vec3(x, y, z))
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
198
216
|
const softCleanup = () => {
|
|
199
217
|
world = new World(world.config.version)
|
|
200
218
|
globalThis.world = world
|
|
@@ -340,7 +358,7 @@ const convertParsedV17ToWasm = (
|
|
|
340
358
|
} else {
|
|
341
359
|
blockLight = new Uint8Array(totalBlocks)
|
|
342
360
|
skyLight = new Uint8Array(totalBlocks)
|
|
343
|
-
skyLight.fill(15)
|
|
361
|
+
skyLight.fill(config?.skyLight ?? 15)
|
|
344
362
|
}
|
|
345
363
|
const biomesArray: Uint8Array = parsed.biomes
|
|
346
364
|
let blockCount = 0
|
|
@@ -406,7 +424,7 @@ const convertParsedV16ToWasm = (
|
|
|
406
424
|
} else {
|
|
407
425
|
blockLight = new Uint8Array(totalBlocks)
|
|
408
426
|
skyLight = new Uint8Array(totalBlocks)
|
|
409
|
-
skyLight.fill(15)
|
|
427
|
+
skyLight.fill(config?.skyLight ?? 15)
|
|
410
428
|
}
|
|
411
429
|
const biomesArray: Uint8Array = parsed.biomes
|
|
412
430
|
let blockCount = 0
|
|
@@ -465,7 +483,7 @@ const meshColumnFromRawV18Plus = (
|
|
|
465
483
|
meta.occludingBlocks,
|
|
466
484
|
config?.enableLighting !== false,
|
|
467
485
|
config?.smoothLighting !== false,
|
|
468
|
-
config?.skyLight
|
|
486
|
+
config?.skyLight ?? 15
|
|
469
487
|
)
|
|
470
488
|
} catch (err) {
|
|
471
489
|
console.warn('[WASM Mesher] generateGeometryFromMapChunkV18Plus failed, falling back:', err)
|
|
@@ -511,7 +529,7 @@ const meshColumnFromParsedV16V17 = (
|
|
|
511
529
|
meta.occludingBlocks,
|
|
512
530
|
config?.enableLighting !== false,
|
|
513
531
|
config?.smoothLighting !== false,
|
|
514
|
-
config?.skyLight
|
|
532
|
+
config?.skyLight ?? 15
|
|
515
533
|
)
|
|
516
534
|
} catch (err) {
|
|
517
535
|
console.warn('[WASM Mesher] generateGeometryFromParsedV16V17 failed, falling back:', err)
|
|
@@ -572,7 +590,7 @@ const meshMultiColumnsFromRawV18Plus = (
|
|
|
572
590
|
meta.occludingBlocks,
|
|
573
591
|
config?.enableLighting !== false,
|
|
574
592
|
config?.smoothLighting !== false,
|
|
575
|
-
config?.skyLight
|
|
593
|
+
config?.skyLight ?? 15
|
|
576
594
|
)
|
|
577
595
|
} catch (err) {
|
|
578
596
|
console.warn('[WASM Mesher] generateGeometryFromMapChunkV18PlusMulti failed:', err)
|
|
@@ -672,7 +690,7 @@ const meshMultiColumnsFromParsedV16V17 = (
|
|
|
672
690
|
meta.occludingBlocks,
|
|
673
691
|
config?.enableLighting !== false,
|
|
674
692
|
config?.smoothLighting !== false,
|
|
675
|
-
config?.skyLight
|
|
693
|
+
config?.skyLight ?? 15
|
|
676
694
|
)
|
|
677
695
|
} catch (err) {
|
|
678
696
|
console.warn('[WASM Mesher] generateGeometryFromParsedV16V17Multi failed:', err)
|
|
@@ -976,6 +994,8 @@ function makeEmptyColumnGeometry(sx: number, sy: number, sz: number, sectionHeig
|
|
|
976
994
|
positions: new Float32Array(0),
|
|
977
995
|
normals: new Float32Array(0),
|
|
978
996
|
colors: new Float32Array(0),
|
|
997
|
+
skyLights: new Float32Array(0),
|
|
998
|
+
blockLights: new Float32Array(0),
|
|
979
999
|
uvs: new Float32Array(0),
|
|
980
1000
|
indices: new Uint32Array(0),
|
|
981
1001
|
indicesCount: 0,
|
|
@@ -1033,6 +1053,7 @@ function processColumnTick() {
|
|
|
1033
1053
|
let preCacheHits = 0
|
|
1034
1054
|
let preCacheMisses = 0
|
|
1035
1055
|
let hadError = false
|
|
1056
|
+
let columnMeshPath = 'none'
|
|
1036
1057
|
// Outer-scope timestamps so we can finalize `processTime` and
|
|
1037
1058
|
// `postPhase` AFTER the per-section emit loop runs (the loop builds
|
|
1038
1059
|
// typed arrays, walks block-entity metadata, and calls postMessage —
|
|
@@ -1052,6 +1073,7 @@ function processColumnTick() {
|
|
|
1052
1073
|
let wasmResult: any
|
|
1053
1074
|
let t1 = 0
|
|
1054
1075
|
let usedFusedPath = false
|
|
1076
|
+
columnMeshPath = 'none'
|
|
1055
1077
|
|
|
1056
1078
|
const meta = getBlockMeta(version)
|
|
1057
1079
|
|
|
@@ -1067,6 +1089,7 @@ function processColumnTick() {
|
|
|
1067
1089
|
|
|
1068
1090
|
if (rawEntry) {
|
|
1069
1091
|
wasmResult = meshColumnFromRawV18Plus(rawEntry, x, z, worldMinY, worldMaxY, meta)
|
|
1092
|
+
if (wasmResult) columnMeshPath = 'v18_fused'
|
|
1070
1093
|
} else if (v17Entry) {
|
|
1071
1094
|
const v17Light = updateLightV17Cache.get(rawCacheKey(x, z))
|
|
1072
1095
|
wasmResult = meshColumnFromParsedV16V17(
|
|
@@ -1075,6 +1098,7 @@ function processColumnTick() {
|
|
|
1075
1098
|
v17Light?.skyLight ?? null, v17Light?.blockLight ?? null,
|
|
1076
1099
|
x, z, worldMinY, worldMaxY, meta
|
|
1077
1100
|
)
|
|
1101
|
+
if (wasmResult) columnMeshPath = 'v17_fused'
|
|
1078
1102
|
} else if (v16Entry) {
|
|
1079
1103
|
const v16Light = updateLightV16Cache.get(rawCacheKey(x, z))
|
|
1080
1104
|
const bitMapLoHi = new Uint32Array([v16Entry.bitMap >>> 0, 0])
|
|
@@ -1084,6 +1108,7 @@ function processColumnTick() {
|
|
|
1084
1108
|
v16Light?.skyLight ?? null, v16Light?.blockLight ?? null,
|
|
1085
1109
|
x, z, worldMinY, worldMaxY, meta
|
|
1086
1110
|
)
|
|
1111
|
+
if (wasmResult) columnMeshPath = 'v16_fused'
|
|
1087
1112
|
}
|
|
1088
1113
|
|
|
1089
1114
|
if (wasmResult) {
|
|
@@ -1104,6 +1129,7 @@ function processColumnTick() {
|
|
|
1104
1129
|
wasmResult = meshMultiColumnsFromRawV18Plus(chunksToUse, x, z, worldMinY, worldMaxY, meta)
|
|
1105
1130
|
?? meshMultiColumnsFromParsedV16V17(chunksToUse, x, z, worldMinY, worldMaxY, meta)
|
|
1106
1131
|
if (wasmResult) {
|
|
1132
|
+
columnMeshPath = 'multi_fused'
|
|
1107
1133
|
usedFusedPath = true
|
|
1108
1134
|
t1 = performance.now()
|
|
1109
1135
|
wasmPhase = t1 - t0
|
|
@@ -1181,8 +1207,9 @@ function processColumnTick() {
|
|
|
1181
1207
|
invisibleBlocks, transparentBlocks, noAoBlocks, cullIdenticalBlocks, occludingBlocks,
|
|
1182
1208
|
config?.enableLighting !== false,
|
|
1183
1209
|
config?.smoothLighting !== false,
|
|
1184
|
-
config?.skyLight
|
|
1210
|
+
config?.skyLight ?? 15
|
|
1185
1211
|
)
|
|
1212
|
+
columnMeshPath = 'two_step_single'
|
|
1186
1213
|
} else {
|
|
1187
1214
|
const tBuildStart = performance.now()
|
|
1188
1215
|
const perChunkLen = conversions[0].blockStates.length
|
|
@@ -1214,8 +1241,9 @@ function processColumnTick() {
|
|
|
1214
1241
|
invisibleBlocks, transparentBlocks, noAoBlocks, cullIdenticalBlocks, occludingBlocks,
|
|
1215
1242
|
config?.enableLighting !== false,
|
|
1216
1243
|
config?.smoothLighting !== false,
|
|
1217
|
-
config?.skyLight
|
|
1244
|
+
config?.skyLight ?? 15
|
|
1218
1245
|
)
|
|
1246
|
+
columnMeshPath = 'two_step_multi'
|
|
1219
1247
|
}
|
|
1220
1248
|
}
|
|
1221
1249
|
|
|
@@ -1306,14 +1334,16 @@ function processColumnTick() {
|
|
|
1306
1334
|
for (cursor.x = sx; cursor.x < sx + 16; cursor.x++) {
|
|
1307
1335
|
const b = world.getBlock(cursor)
|
|
1308
1336
|
if (!b) continue
|
|
1309
|
-
collectBlockEntityMetadata(b, cursor.x, cursor.y, cursor.z, beTarget, beOpts)
|
|
1337
|
+
collectBlockEntityMetadata(b, cursor.x, cursor.y, cursor.z, beTarget, beOpts, world)
|
|
1310
1338
|
}
|
|
1311
1339
|
}
|
|
1312
1340
|
}
|
|
1313
1341
|
|
|
1314
1342
|
let geometry: MesherGeometryOutput
|
|
1315
1343
|
let transferable: any[] = []
|
|
1316
|
-
const
|
|
1344
|
+
const hasOpaqueMesh = (exported?.geometry.indices.length ?? 0) > 0
|
|
1345
|
+
const hasBlendMesh = (exported?.blendGeometry?.indices.length ?? 0) > 0
|
|
1346
|
+
const hasLegacyMesh = hasOpaqueMesh || hasBlendMesh
|
|
1317
1347
|
const hasShaderCubes = (exported?.shaderCubes?.count ?? 0) > 0
|
|
1318
1348
|
if (exported && (hasLegacyMesh || hasShaderCubes)) {
|
|
1319
1349
|
const maxIndex = exported.geometry.indices.length > 0
|
|
@@ -1335,6 +1365,8 @@ function processColumnTick() {
|
|
|
1335
1365
|
positions: new Float32Array(exported.geometry.positions),
|
|
1336
1366
|
normals: new Float32Array(exported.geometry.normals),
|
|
1337
1367
|
colors: new Float32Array(exported.geometry.colors),
|
|
1368
|
+
skyLights: new Float32Array(exported.geometry.skyLights),
|
|
1369
|
+
blockLights: new Float32Array(exported.geometry.blockLights),
|
|
1338
1370
|
uvs: new Float32Array(exported.geometry.uvs),
|
|
1339
1371
|
indices: using32Array
|
|
1340
1372
|
? new Uint32Array(exported.geometry.indices)
|
|
@@ -1360,13 +1392,37 @@ function processColumnTick() {
|
|
|
1360
1392
|
formatVersion: 3,
|
|
1361
1393
|
}
|
|
1362
1394
|
}
|
|
1395
|
+
if (exported.blendGeometry && hasBlendMesh) {
|
|
1396
|
+
const blendMax = Math.max(...exported.blendGeometry.indices)
|
|
1397
|
+
geometry.blend = {
|
|
1398
|
+
positions: new Float32Array(exported.blendGeometry.positions),
|
|
1399
|
+
normals: new Float32Array(exported.blendGeometry.normals),
|
|
1400
|
+
colors: new Float32Array(exported.blendGeometry.colors),
|
|
1401
|
+
skyLights: new Float32Array(exported.blendGeometry.skyLights),
|
|
1402
|
+
blockLights: new Float32Array(exported.blendGeometry.blockLights),
|
|
1403
|
+
uvs: new Float32Array(exported.blendGeometry.uvs),
|
|
1404
|
+
indices: blendMax > 65535
|
|
1405
|
+
? new Uint32Array(exported.blendGeometry.indices)
|
|
1406
|
+
: new Uint16Array(exported.blendGeometry.indices),
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1363
1409
|
transferable = [
|
|
1364
1410
|
geometry.positions?.buffer,
|
|
1365
1411
|
geometry.normals?.buffer,
|
|
1366
1412
|
geometry.colors?.buffer,
|
|
1413
|
+
geometry.skyLights?.buffer,
|
|
1414
|
+
geometry.blockLights?.buffer,
|
|
1367
1415
|
geometry.uvs?.buffer,
|
|
1368
1416
|
//@ts-ignore
|
|
1369
1417
|
geometry.indices?.buffer,
|
|
1418
|
+
geometry.blend?.positions?.buffer,
|
|
1419
|
+
geometry.blend?.normals?.buffer,
|
|
1420
|
+
geometry.blend?.colors?.buffer,
|
|
1421
|
+
geometry.blend?.skyLights?.buffer,
|
|
1422
|
+
geometry.blend?.blockLights?.buffer,
|
|
1423
|
+
geometry.blend?.uvs?.buffer,
|
|
1424
|
+
//@ts-ignore
|
|
1425
|
+
geometry.blend?.indices?.buffer,
|
|
1370
1426
|
geometry.shaderCubes?.words?.buffer,
|
|
1371
1427
|
].filter(Boolean)
|
|
1372
1428
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { sectionYsForLightColumnDirty } from './mesherWasmLightDirty'
|
|
4
|
+
|
|
5
|
+
describe('sectionYsForLightColumnDirty', () => {
|
|
6
|
+
it('covers every section in a 256-high overworld column', () => {
|
|
7
|
+
expect(sectionYsForLightColumnDirty(0, 256)).toEqual(
|
|
8
|
+
Array.from({ length: 16 }, (_, i) => i * 16),
|
|
9
|
+
)
|
|
10
|
+
})
|
|
11
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { SECTION_HEIGHT } from '../../mesher-shared/shared'
|
|
3
|
+
|
|
4
|
+
/** Section Y values to dirty for a column after `update_light` updates the light cache. */
|
|
5
|
+
export function sectionYsForLightColumnDirty (
|
|
6
|
+
worldMinY: number,
|
|
7
|
+
worldMaxY: number,
|
|
8
|
+
sectionHeight = SECTION_HEIGHT,
|
|
9
|
+
): number[] {
|
|
10
|
+
const ys: number[] = []
|
|
11
|
+
for (let y = worldMinY; y < worldMaxY; y += sectionHeight) {
|
|
12
|
+
ys.push(y)
|
|
13
|
+
}
|
|
14
|
+
return ys
|
|
15
|
+
}
|