minecraft-renderer 0.1.47 → 0.1.49
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 +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +3740 -183
- package/dist/minecraft-renderer.js +332 -60
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +705 -433
- package/package.json +1 -1
- package/src/graphicsBackend/config.ts +4 -0
- package/src/graphicsBackend/playerState.ts +1 -0
- package/src/graphicsBackend/rendererOptionsSync.ts +1 -0
- package/src/lib/worldrendererCommon.ts +13 -0
- package/src/mesher-shared/exportedGeometryTypes.ts +5 -1
- package/src/mesher-shared/shared.ts +8 -0
- package/src/playerState/playerState.ts +2 -0
- package/src/three/chunkMeshManager.ts +312 -39
- package/src/three/globalBlockBuffer.ts +292 -0
- package/src/three/menuBackground/config.ts +1 -1
- package/src/three/menuBackground/defaultOptions.ts +27 -19
- package/src/three/modules/sciFiWorldReveal.ts +162 -68
- package/src/three/modules/starfield.ts +9 -1
- package/src/three/sectionRaycastAabb.ts +167 -0
- package/src/three/shaderCubeMesh.ts +93 -0
- package/src/three/shaders/cubeBlockShader.ts +354 -0
- package/src/three/shaders/textureIndexMapping.ts +122 -0
- package/src/three/shaders/tintPalette.ts +198 -0
- package/src/three/worldGeometryExport.ts +53 -25
- package/src/three/worldRendererThree.ts +93 -26
- package/src/wasm-mesher/bridge/render-from-wasm.ts +62 -185
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +396 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +58 -0
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +360 -0
- package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +11 -4
- package/src/wasm-mesher/worker/mesherWasm.ts +17 -2
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { test, expect, beforeEach } from 'vitest'
|
|
3
|
+
import { WORD0, WORD2, WORD3 } from '../../three/shaders/cubeBlockShader'
|
|
4
|
+
import {
|
|
5
|
+
resetShaderCubeResources,
|
|
6
|
+
getShaderCubeResources,
|
|
7
|
+
isShaderCubeBlock,
|
|
8
|
+
tryBuildShaderCubeInstances,
|
|
9
|
+
buildShaderCubesFromWords,
|
|
10
|
+
countVisibleFaces,
|
|
11
|
+
unpackTexIndexFromWord2,
|
|
12
|
+
decodeSectionBaseFromWords,
|
|
13
|
+
packWord3,
|
|
14
|
+
SHADER_CUBES_FORMAT_VERSION,
|
|
15
|
+
SHADER_CUBES_WORDS_PER_FACE,
|
|
16
|
+
} from '../bridge/shaderCubeBridge'
|
|
17
|
+
import { GlobalBlockBuffer } from '../../three/globalBlockBuffer'
|
|
18
|
+
import { createCubeBlockMaterial } from '../../three/shaders/cubeBlockShader'
|
|
19
|
+
import * as THREE from 'three'
|
|
20
|
+
import { renderWasmOutputToGeometry } from '../bridge/render-from-wasm'
|
|
21
|
+
|
|
22
|
+
const VERSION = '1.16.5'
|
|
23
|
+
const STONE = 1
|
|
24
|
+
/** mc-assets blocksAtlases.json → stone */
|
|
25
|
+
const STONE_ATLAS_TILE_INDEX = 552
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
resetShaderCubeResources()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('packWord2: AO diagonal flip sets bit 12', () => {
|
|
32
|
+
const words: number[] = []
|
|
33
|
+
const block = {
|
|
34
|
+
position: [3, 5, 7] as [number, number, number],
|
|
35
|
+
visible_faces: 1 << 0, // up only
|
|
36
|
+
ao_data: [[0, 1, 2, 3]], // 0+3 >= 1+2 → flip
|
|
37
|
+
light_data: [[1, 1, 1, 1]],
|
|
38
|
+
light_combined: [[255, 255, 255, 255]],
|
|
39
|
+
}
|
|
40
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
41
|
+
const model = {
|
|
42
|
+
elements: [{
|
|
43
|
+
faces: {
|
|
44
|
+
up: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
45
|
+
down: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
46
|
+
east: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
47
|
+
west: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
48
|
+
south: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
49
|
+
north: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
50
|
+
},
|
|
51
|
+
}],
|
|
52
|
+
}
|
|
53
|
+
const ok = tryBuildShaderCubeInstances(
|
|
54
|
+
block,
|
|
55
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
56
|
+
model,
|
|
57
|
+
{
|
|
58
|
+
sectionOrigin: { x: 0, y: 0, z: 0 },
|
|
59
|
+
sectionHeight: 16,
|
|
60
|
+
tintPalette,
|
|
61
|
+
textureIndexMapping,
|
|
62
|
+
},
|
|
63
|
+
words,
|
|
64
|
+
)
|
|
65
|
+
expect(ok).toBe(true)
|
|
66
|
+
expect(words.length).toBe(SHADER_CUBES_WORDS_PER_FACE)
|
|
67
|
+
expect(words[2]! & (1 << WORD2.DIAGONAL_FLAG_SHIFT)).not.toBe(0)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('packWord0: section-local lx/ly/lz and face id', () => {
|
|
71
|
+
const words: number[] = []
|
|
72
|
+
const block = {
|
|
73
|
+
position: [10, 17, 4] as [number, number, number],
|
|
74
|
+
visible_faces: 1 << 2, // east
|
|
75
|
+
ao_data: [[3, 3, 3, 3]],
|
|
76
|
+
light_data: [[0.5, 0.5, 0.5, 0.5]],
|
|
77
|
+
light_combined: [[128, 128, 128, 128]],
|
|
78
|
+
}
|
|
79
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
80
|
+
const model = {
|
|
81
|
+
elements: [{
|
|
82
|
+
faces: {
|
|
83
|
+
up: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
84
|
+
down: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
85
|
+
east: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
86
|
+
west: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
87
|
+
south: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
88
|
+
north: { texture: { u: 16, v: 0, su: 16, sv: 16 } },
|
|
89
|
+
},
|
|
90
|
+
}],
|
|
91
|
+
}
|
|
92
|
+
tryBuildShaderCubeInstances(
|
|
93
|
+
block,
|
|
94
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
95
|
+
model,
|
|
96
|
+
{
|
|
97
|
+
sectionOrigin: { x: 0, y: 16, z: 0 },
|
|
98
|
+
sectionHeight: 16,
|
|
99
|
+
tintPalette,
|
|
100
|
+
textureIndexMapping,
|
|
101
|
+
},
|
|
102
|
+
words,
|
|
103
|
+
)
|
|
104
|
+
const w0 = words[0]!
|
|
105
|
+
expect(w0 & 0xf).toBe(10) // lx
|
|
106
|
+
expect((w0 >> WORD0.LY_SHIFT) & 0xf).toBe(1) // ly = 17 - 16
|
|
107
|
+
expect((w0 >> WORD0.LZ_SHIFT) & 0xf).toBe(4)
|
|
108
|
+
expect((w0 >> WORD0.FACE_SHIFT) & 7).toBe(2) // east
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('isShaderCubeBlock: rejects model rotation and sectionHeight !== 16', () => {
|
|
112
|
+
const { textureIndexMapping } = getShaderCubeResources()
|
|
113
|
+
const baseModel = {
|
|
114
|
+
elements: [{
|
|
115
|
+
faces: {
|
|
116
|
+
up: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
117
|
+
down: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
118
|
+
east: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
119
|
+
west: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
120
|
+
south: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
121
|
+
north: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
122
|
+
},
|
|
123
|
+
}],
|
|
124
|
+
}
|
|
125
|
+
expect(isShaderCubeBlock(
|
|
126
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model: baseModel },
|
|
127
|
+
baseModel,
|
|
128
|
+
16,
|
|
129
|
+
textureIndexMapping,
|
|
130
|
+
)).toBe(true)
|
|
131
|
+
expect(isShaderCubeBlock(
|
|
132
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model: baseModel },
|
|
133
|
+
baseModel,
|
|
134
|
+
24,
|
|
135
|
+
textureIndexMapping,
|
|
136
|
+
)).toBe(false)
|
|
137
|
+
expect(isShaderCubeBlock(
|
|
138
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model: { ...baseModel, y: 90 } },
|
|
139
|
+
{ ...baseModel, y: 90 },
|
|
140
|
+
16,
|
|
141
|
+
textureIndexMapping,
|
|
142
|
+
)).toBe(false)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('renderWasmOutputToGeometry: stone emits shaderCubes and skips legacy vertices when enabled', () => {
|
|
146
|
+
const block = {
|
|
147
|
+
position: [0, 0, 0] as [number, number, number],
|
|
148
|
+
block_state_id: STONE,
|
|
149
|
+
visible_faces: (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5),
|
|
150
|
+
ao_data: Array.from({ length: 6 }, () => [3, 3, 3, 3]),
|
|
151
|
+
light_data: Array.from({ length: 6 }, () => [1, 1, 1, 1]),
|
|
152
|
+
light_combined: Array.from({ length: 6 }, () => [255, 255, 255, 255]),
|
|
153
|
+
}
|
|
154
|
+
const out = renderWasmOutputToGeometry(
|
|
155
|
+
{ blocks: [block], block_count: 1, block_iterations: 0 },
|
|
156
|
+
VERSION,
|
|
157
|
+
'0,0,0',
|
|
158
|
+
{ x: 8, y: 8, z: 8 },
|
|
159
|
+
undefined,
|
|
160
|
+
{ shaderCubes: true },
|
|
161
|
+
)
|
|
162
|
+
expect(out.shaderCubes?.count).toBe(6)
|
|
163
|
+
expect(out.shaderCubes?.formatVersion).toBe(SHADER_CUBES_FORMAT_VERSION)
|
|
164
|
+
expect(out.shaderCubes?.words.length).toBe(6 * SHADER_CUBES_WORDS_PER_FACE)
|
|
165
|
+
expect(out.geometry.positions.length).toBe(0)
|
|
166
|
+
expect(out.geometry.indices.length).toBe(0)
|
|
167
|
+
const words = out.shaderCubes!.words
|
|
168
|
+
for (let i = 0; i < words.length; i += SHADER_CUBES_WORDS_PER_FACE) {
|
|
169
|
+
expect(unpackTexIndexFromWord2(words[i + 2]!)).toBe(STONE_ATLAS_TILE_INDEX)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('renderWasmOutputToGeometry: shaderCubes false keeps legacy path for stone', () => {
|
|
174
|
+
const block = {
|
|
175
|
+
position: [0, 0, 0] as [number, number, number],
|
|
176
|
+
block_state_id: STONE,
|
|
177
|
+
visible_faces: 1 << 0,
|
|
178
|
+
ao_data: [[3, 3, 3, 3]],
|
|
179
|
+
light_data: [[1, 1, 1, 1]],
|
|
180
|
+
}
|
|
181
|
+
const out = renderWasmOutputToGeometry(
|
|
182
|
+
{ blocks: [block], block_count: 1, block_iterations: 0 },
|
|
183
|
+
VERSION,
|
|
184
|
+
'0,0,0',
|
|
185
|
+
{ x: 8, y: 8, z: 8 },
|
|
186
|
+
undefined,
|
|
187
|
+
{ shaderCubes: false },
|
|
188
|
+
)
|
|
189
|
+
expect(out.shaderCubes).toBeUndefined()
|
|
190
|
+
expect(out.geometry.positions.length).toBeGreaterThan(0)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('buildShaderCubesFromWords: empty → undefined', () => {
|
|
194
|
+
expect(buildShaderCubesFromWords([])).toBeUndefined()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('countVisibleFaces', () => {
|
|
198
|
+
expect(countVisibleFaces(0b101010)).toBe(3)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
const SIX_FACE_TEXTURES = {
|
|
202
|
+
up: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
203
|
+
down: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
204
|
+
east: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
205
|
+
west: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
206
|
+
south: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
207
|
+
north: { texture: { u: 0, v: 0, su: 16, sv: 16 } },
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function aoCorner0FromWord0(w0: number): number {
|
|
211
|
+
return (w0 >> WORD0.AO_SHIFT) & 3
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
test('south face: AO corners remapped to shader order (elemFaces [0,3,1,2] → shader [2,3,0,1])', () => {
|
|
215
|
+
const words: number[] = []
|
|
216
|
+
const block = {
|
|
217
|
+
position: [0, 0, 0] as [number, number, number],
|
|
218
|
+
visible_faces: 1 << 4, // south
|
|
219
|
+
ao_data: [[0, 3, 1, 2]],
|
|
220
|
+
light_data: [[1, 1, 1, 1]],
|
|
221
|
+
light_combined: [[10, 20, 30, 40]],
|
|
222
|
+
}
|
|
223
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
224
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
225
|
+
tryBuildShaderCubeInstances(
|
|
226
|
+
block,
|
|
227
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
228
|
+
model,
|
|
229
|
+
{ sectionOrigin: { x: 0, y: 0, z: 0 }, sectionHeight: 16, tintPalette, textureIndexMapping },
|
|
230
|
+
words,
|
|
231
|
+
)
|
|
232
|
+
// Shader vi=0 must get elemFaces ao[2]=1, not ao[0]=0
|
|
233
|
+
expect(aoCorner0FromWord0(words[0]!)).toBe(1)
|
|
234
|
+
// Shader vi=1 → ao[3]=2
|
|
235
|
+
expect((words[0]! >> (WORD0.AO_SHIFT + WORD0.AO_BITS_PER_CORNER)) & 3).toBe(2)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('south face: diagonal flip uses remapped AO (differs from raw elemFaces formula)', () => {
|
|
239
|
+
const wordsFlip: number[] = []
|
|
240
|
+
const wordsNoFlip: number[] = []
|
|
241
|
+
const ao = [0, 3, 1, 2] // raw: 0+2 < 3+1 → no flip; remapped [1,2,3,0]: 1+0 >= 2+3 → flip
|
|
242
|
+
const block = {
|
|
243
|
+
position: [0, 0, 0] as [number, number, number],
|
|
244
|
+
visible_faces: 1 << 4,
|
|
245
|
+
ao_data: [ao],
|
|
246
|
+
light_data: [[1, 1, 1, 1]],
|
|
247
|
+
light_combined: [[255, 255, 255, 255]],
|
|
248
|
+
}
|
|
249
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
250
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
251
|
+
const opts = {
|
|
252
|
+
sectionOrigin: { x: 0, y: 0, z: 0 },
|
|
253
|
+
sectionHeight: 16,
|
|
254
|
+
tintPalette,
|
|
255
|
+
textureIndexMapping,
|
|
256
|
+
}
|
|
257
|
+
tryBuildShaderCubeInstances(
|
|
258
|
+
block,
|
|
259
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
260
|
+
model,
|
|
261
|
+
opts,
|
|
262
|
+
wordsFlip,
|
|
263
|
+
)
|
|
264
|
+
expect(wordsFlip[2]! & (1 << WORD2.DIAGONAL_FLAG_SHIFT)).not.toBe(0)
|
|
265
|
+
|
|
266
|
+
// elemFaces [3,0,0,3] → remapped [0,3,3,0]: 0+0 < 3+3 → no diagonal flip
|
|
267
|
+
tryBuildShaderCubeInstances(
|
|
268
|
+
{ ...block, ao_data: [[3, 0, 0, 3]] },
|
|
269
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
270
|
+
model,
|
|
271
|
+
opts,
|
|
272
|
+
wordsNoFlip,
|
|
273
|
+
)
|
|
274
|
+
expect(wordsNoFlip[2]! & (1 << WORD2.DIAGONAL_FLAG_SHIFT)).toBe(0)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test('doAO false: full bright AO/light and no diagonal flip', () => {
|
|
278
|
+
const words: number[] = []
|
|
279
|
+
const block = {
|
|
280
|
+
position: [0, 0, 0] as [number, number, number],
|
|
281
|
+
visible_faces: 1 << 0,
|
|
282
|
+
ao_data: [[0, 0, 0, 0]],
|
|
283
|
+
light_data: [[0, 0, 0, 0]],
|
|
284
|
+
light_combined: [[0, 0, 0, 0]],
|
|
285
|
+
}
|
|
286
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
287
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
288
|
+
tryBuildShaderCubeInstances(
|
|
289
|
+
block,
|
|
290
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
291
|
+
model,
|
|
292
|
+
{
|
|
293
|
+
sectionOrigin: { x: 0, y: 0, z: 0 },
|
|
294
|
+
sectionHeight: 16,
|
|
295
|
+
tintPalette,
|
|
296
|
+
textureIndexMapping,
|
|
297
|
+
doAO: false,
|
|
298
|
+
},
|
|
299
|
+
words,
|
|
300
|
+
)
|
|
301
|
+
expect(aoCorner0FromWord0(words[0]!)).toBe(3)
|
|
302
|
+
for (let i = 0; i < 4; i++) {
|
|
303
|
+
expect((words[1]! >> (i * 8)) & 0xff).toBe(255)
|
|
304
|
+
}
|
|
305
|
+
expect(words[2]! & (1 << WORD2.DIAGONAL_FLAG_SHIFT)).toBe(0)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
test('section base coords round-trip in word2/word3', () => {
|
|
309
|
+
const words: number[] = []
|
|
310
|
+
const block = {
|
|
311
|
+
position: [10, 17, 4] as [number, number, number],
|
|
312
|
+
visible_faces: 1 << 2,
|
|
313
|
+
ao_data: [[3, 3, 3, 3]],
|
|
314
|
+
light_data: [[1, 1, 1, 1]],
|
|
315
|
+
light_combined: [[255, 255, 255, 255]],
|
|
316
|
+
}
|
|
317
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
318
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
319
|
+
const sectionOrigin = { x: 0, y: 16, z: 32 }
|
|
320
|
+
tryBuildShaderCubeInstances(
|
|
321
|
+
block,
|
|
322
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
323
|
+
model,
|
|
324
|
+
{ sectionOrigin, sectionHeight: 16, tintPalette, textureIndexMapping },
|
|
325
|
+
words,
|
|
326
|
+
)
|
|
327
|
+
const base = decodeSectionBaseFromWords(words[2]!, words[3]!)
|
|
328
|
+
expect(base).toEqual(sectionOrigin)
|
|
329
|
+
const sX = (words[3]! & 0xffff) - WORD3.SECTION_BIAS
|
|
330
|
+
const sZ = ((words[3]! >>> 16) & 0xffff) - WORD3.SECTION_BIAS
|
|
331
|
+
const sY = ((words[2]! >>> WORD2.SECTION_Y_SHIFT) & 0x1f) - 4
|
|
332
|
+
expect(sX * 16).toBe(sectionOrigin.x)
|
|
333
|
+
expect(sY * 16).toBe(sectionOrigin.y)
|
|
334
|
+
expect(sZ * 16).toBe(sectionOrigin.z)
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
test('GlobalBlockBuffer: free-list reuses slot with EMPTY sentinel', () => {
|
|
338
|
+
const scene = new THREE.Scene()
|
|
339
|
+
const mat = createCubeBlockMaterial()
|
|
340
|
+
const buffer = new GlobalBlockBuffer(mat, scene)
|
|
341
|
+
|
|
342
|
+
const words = new Uint32Array([
|
|
343
|
+
1, 2, 0, packWord3(0, 0),
|
|
344
|
+
3, 4, 0, packWord3(0, 0),
|
|
345
|
+
])
|
|
346
|
+
buffer.addSection('a', words, 2)
|
|
347
|
+
expect(buffer.mesh.geometry.instanceCount).toBe(2)
|
|
348
|
+
|
|
349
|
+
buffer.removeSection('a')
|
|
350
|
+
const w2Attr = buffer.mesh.geometry.getAttribute('a_w2') as THREE.InstancedBufferAttribute
|
|
351
|
+
expect(w2Attr.array[0]! & (1 << WORD2.EMPTY_SHIFT)).not.toBe(0)
|
|
352
|
+
expect(w2Attr.array[1]! & (1 << WORD2.EMPTY_SHIFT)).not.toBe(0)
|
|
353
|
+
|
|
354
|
+
const wordsB = new Uint32Array([5, 6, 0, packWord3(16, 0)])
|
|
355
|
+
buffer.addSection('b', wordsB, 1)
|
|
356
|
+
expect(buffer.mesh.geometry.getAttribute('a_w0').array[0]).toBe(5)
|
|
357
|
+
|
|
358
|
+
buffer.dispose()
|
|
359
|
+
mat.dispose()
|
|
360
|
+
})
|
|
@@ -77,7 +77,10 @@ test('splitColumnWasmOutputToSections: per-section split is equivalent to manual
|
|
|
77
77
|
{ x: 0, y: 64, z: 0 }, // contains the isolated block at Y=64
|
|
78
78
|
]
|
|
79
79
|
|
|
80
|
-
const split = splitColumnWasmOutputToSections(fullColumn, requested, {
|
|
80
|
+
const split = splitColumnWasmOutputToSections(fullColumn, requested, {
|
|
81
|
+
version: VERSION,
|
|
82
|
+
shaderCubes: false,
|
|
83
|
+
})
|
|
81
84
|
|
|
82
85
|
expect(split.size).toBe(4)
|
|
83
86
|
for (const r of requested) {
|
|
@@ -100,7 +103,8 @@ test('splitColumnWasmOutputToSections: per-section split is equivalent to manual
|
|
|
100
103
|
VERSION,
|
|
101
104
|
`${r.x},${r.y},${r.z}`,
|
|
102
105
|
{ x: r.x + 8, y: r.y + 8, z: r.z + 8 },
|
|
103
|
-
undefined
|
|
106
|
+
undefined,
|
|
107
|
+
{ shaderCubes: false },
|
|
104
108
|
)
|
|
105
109
|
const got = split.get(`${r.x},${r.y},${r.z}`)!.exported
|
|
106
110
|
expect(got.key).toBe(reference.key)
|
|
@@ -143,7 +147,10 @@ test('splitColumnWasmOutputToSections: per-section split is equivalent to manual
|
|
|
143
147
|
|
|
144
148
|
test('splitColumnWasmOutputToSections: empty requested-keys list returns empty map', () => {
|
|
145
149
|
const fullColumn = makeSeamFixture()
|
|
146
|
-
const out = splitColumnWasmOutputToSections(fullColumn, [], {
|
|
150
|
+
const out = splitColumnWasmOutputToSections(fullColumn, [], {
|
|
151
|
+
version: VERSION,
|
|
152
|
+
shaderCubes: false,
|
|
153
|
+
})
|
|
147
154
|
expect(out.size).toBe(0)
|
|
148
155
|
})
|
|
149
156
|
|
|
@@ -154,7 +161,7 @@ test('splitColumnWasmOutputToSections: blocks outside requested sections are dro
|
|
|
154
161
|
const out = splitColumnWasmOutputToSections(
|
|
155
162
|
fullColumn,
|
|
156
163
|
[{ x: 0, y: 32, z: 0 }],
|
|
157
|
-
{ version: VERSION }
|
|
164
|
+
{ version: VERSION, shaderCubes: false },
|
|
158
165
|
)
|
|
159
166
|
const empty = out.get('0,32,0')!
|
|
160
167
|
expect(empty.exported.geometry.positions).toEqual([])
|
|
@@ -1224,7 +1224,12 @@ function processColumnTick() {
|
|
|
1224
1224
|
exportedMap = splitColumnWasmOutputToSections(
|
|
1225
1225
|
wasmResult,
|
|
1226
1226
|
requestedSectionKeys,
|
|
1227
|
-
{
|
|
1227
|
+
{
|
|
1228
|
+
version,
|
|
1229
|
+
world,
|
|
1230
|
+
sectionHeight,
|
|
1231
|
+
shaderCubes: config?.shaderCubeBlocks === true,
|
|
1232
|
+
},
|
|
1228
1233
|
)
|
|
1229
1234
|
|
|
1230
1235
|
// Push heightmap from the WASM column output. With column meshing as
|
|
@@ -1301,7 +1306,9 @@ function processColumnTick() {
|
|
|
1301
1306
|
|
|
1302
1307
|
let geometry: MesherGeometryOutput
|
|
1303
1308
|
let transferable: any[] = []
|
|
1304
|
-
|
|
1309
|
+
const hasLegacyMesh = (exported?.geometry.indices.length ?? 0) > 0
|
|
1310
|
+
const hasShaderCubes = (exported?.shaderCubes?.count ?? 0) > 0
|
|
1311
|
+
if (exported && (hasLegacyMesh || hasShaderCubes)) {
|
|
1305
1312
|
const maxIndex = exported.geometry.indices.length > 0
|
|
1306
1313
|
? Math.max(...exported.geometry.indices)
|
|
1307
1314
|
: 0
|
|
@@ -1339,6 +1346,13 @@ function processColumnTick() {
|
|
|
1339
1346
|
// section's geometry.
|
|
1340
1347
|
blocksCount: sectionBlocksCount,
|
|
1341
1348
|
}
|
|
1349
|
+
if (exported.shaderCubes) {
|
|
1350
|
+
geometry.shaderCubes = {
|
|
1351
|
+
words: new Uint32Array(exported.shaderCubes.words),
|
|
1352
|
+
count: exported.shaderCubes.count,
|
|
1353
|
+
formatVersion: 2,
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1342
1356
|
transferable = [
|
|
1343
1357
|
geometry.positions?.buffer,
|
|
1344
1358
|
geometry.normals?.buffer,
|
|
@@ -1346,6 +1360,7 @@ function processColumnTick() {
|
|
|
1346
1360
|
geometry.uvs?.buffer,
|
|
1347
1361
|
//@ts-ignore
|
|
1348
1362
|
geometry.indices?.buffer,
|
|
1363
|
+
geometry.shaderCubes?.words?.buffer,
|
|
1349
1364
|
].filter(Boolean)
|
|
1350
1365
|
|
|
1351
1366
|
if (exported.geometry.indices.length > 0 && config.computeWireframeEdges) {
|