minecraft-renderer 0.1.67 → 0.1.68
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.map +1 -1
- package/dist/mesherWasm.js +198 -198
- package/dist/minecraft-renderer.js +22 -21
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +19 -18
- package/package.json +1 -1
- package/src/mesher-shared/exportedGeometryTypes.ts +1 -1
- package/src/mesher-shared/shared.ts +1 -1
- package/src/three/shaders/cubeBlockShader.ts +13 -7
- package/src/wasm-mesher/bridge/render-from-wasm.ts +0 -2
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +31 -9
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +43 -28
- package/src/wasm-mesher/worker/mesherWasm.ts +1 -1
package/package.json
CHANGED
|
@@ -148,8 +148,9 @@ void main() {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// --- Position: section base (multiples of 16) + face quad + block-local 0..15 ---
|
|
151
|
-
|
|
152
|
-
int
|
|
151
|
+
// Must mirror WORD2/WORD3 constants in TS (GLSL cannot import them).
|
|
152
|
+
int sX = int((a_w3 & 0xFFFFu) | (((a_w2 >> 19u) & 0x3Fu) << 16u)) - 2097152;
|
|
153
|
+
int sZ = int(((a_w3 >> 16u) & 0xFFFFu) | (((a_w2 >> 25u) & 0x3Fu) << 16u)) - 2097152;
|
|
153
154
|
int sY = int((a_w2 >> 13u) & 0x1Fu) - 4;
|
|
154
155
|
vec3 sectionBase = vec3(float(sX * 16), float(sY * 16), float(sZ * 16));
|
|
155
156
|
vec3 facePos = BASE[faceId] + u * DU[faceId] + v * DV[faceId];
|
|
@@ -343,12 +344,17 @@ export const WORD2 = {
|
|
|
343
344
|
SECTION_Y_SHIFT: 13,
|
|
344
345
|
SECTION_Y_BITS: 5,
|
|
345
346
|
EMPTY_SHIFT: 18,
|
|
346
|
-
|
|
347
|
+
SECTION_X_HI_SHIFT: 19,
|
|
348
|
+
SECTION_Z_HI_SHIFT: 25,
|
|
349
|
+
SECTION_HI_BITS: 6,
|
|
350
|
+
SPARE_BITS: 1,
|
|
347
351
|
} as const
|
|
348
352
|
|
|
349
|
-
/** Section base X/Z
|
|
353
|
+
/** Section base X/Z: low 16 bits in a_w3, high 6 in a_w2 (22-bit biased section index). */
|
|
350
354
|
export const WORD3 = {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
355
|
+
SECTION_BITS: 22,
|
|
356
|
+
SECTION_MASK: (1 << 22) - 1,
|
|
357
|
+
LO_BITS: 16,
|
|
358
|
+
HI_BITS: 6,
|
|
359
|
+
SECTION_BIAS: 2097152,
|
|
354
360
|
} as const
|
|
@@ -524,8 +524,6 @@ export function renderWasmOutputToGeometry(
|
|
|
524
524
|
const [bx, by, bz] = block.position
|
|
525
525
|
const blockStateId = block.block_state_id
|
|
526
526
|
|
|
527
|
-
log(`[WASM] Processing block at (${bx}, ${by}, ${bz}), stateId=${blockStateId}, visible_faces=0b${block.visible_faces.toString(2).padStart(6, '0')}`)
|
|
528
|
-
|
|
529
527
|
const prismBlock = PrismarineBlock.fromStateId(blockStateId, 1)
|
|
530
528
|
|
|
531
529
|
let biome: string | undefined
|
|
@@ -10,7 +10,7 @@ import { WORD0, WORD1, WORD2, WORD3 } from '../../three/shaders/cubeBlockShader'
|
|
|
10
10
|
import { TextureIndexMapping, type TextureEntry } from '../../three/shaders/textureIndexMapping'
|
|
11
11
|
import { TintPalette } from '../../three/shaders/tintPalette'
|
|
12
12
|
|
|
13
|
-
export const SHADER_CUBES_FORMAT_VERSION =
|
|
13
|
+
export const SHADER_CUBES_FORMAT_VERSION = 3 as const
|
|
14
14
|
export const SHADER_CUBES_WORDS_PER_FACE = 4 as const
|
|
15
15
|
|
|
16
16
|
export type ShaderCubesOutput = {
|
|
@@ -238,26 +238,40 @@ function packWord1(lightCombined: number[]): number {
|
|
|
238
238
|
return w >>> 0
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
function biasedSectionIndex(sectionBaseCoord: number): number {
|
|
242
|
+
return (Math.floor(sectionBaseCoord / 16) + WORD3.SECTION_BIAS) & WORD3.SECTION_MASK
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function packWord2(
|
|
246
|
+
texIndex: number,
|
|
247
|
+
aoDiagonalFlip: boolean,
|
|
248
|
+
sectionBaseX: number,
|
|
249
|
+
sectionBaseY: number,
|
|
250
|
+
sectionBaseZ: number,
|
|
251
|
+
): number {
|
|
242
252
|
let w = texIndex & ((1 << WORD2.TEX_INDEX_BITS) - 1)
|
|
243
253
|
if (aoDiagonalFlip) {
|
|
244
254
|
w |= 1 << WORD2.DIAGONAL_FLAG_SHIFT
|
|
245
255
|
}
|
|
246
256
|
const sectionY = ((Math.floor(sectionBaseY / 16) + 4) & 0x1f) << WORD2.SECTION_Y_SHIFT
|
|
247
257
|
w |= sectionY
|
|
258
|
+
const sx = biasedSectionIndex(sectionBaseX)
|
|
259
|
+
const sz = biasedSectionIndex(sectionBaseZ)
|
|
260
|
+
w |= ((sx >>> 16) & 0x3f) << WORD2.SECTION_X_HI_SHIFT
|
|
261
|
+
w |= ((sz >>> 16) & 0x3f) << WORD2.SECTION_Z_HI_SHIFT
|
|
248
262
|
return w >>> 0
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
export function packWord3(sectionBaseX: number, sectionBaseZ: number): number {
|
|
252
|
-
const sx = (
|
|
253
|
-
const sz = (
|
|
254
|
-
return (sx | (sz << 16)) >>> 0
|
|
266
|
+
const sx = biasedSectionIndex(sectionBaseX)
|
|
267
|
+
const sz = biasedSectionIndex(sectionBaseZ)
|
|
268
|
+
return ((sx & 0xffff) | ((sz & 0xffff) << 16)) >>> 0
|
|
255
269
|
}
|
|
256
270
|
|
|
257
271
|
/** Decode section base block coords from packed words (round-trip helper for tests). */
|
|
258
272
|
export function decodeSectionBaseFromWords(word2: number, word3: number): { x: number, y: number, z: number } {
|
|
259
|
-
const sX = (word3 & 0xffff) - WORD3.SECTION_BIAS
|
|
260
|
-
const sZ = ((word3 >>> 16) & 0xffff) - WORD3.SECTION_BIAS
|
|
273
|
+
const sX = ((word3 & 0xffff) | (((word2 >>> WORD2.SECTION_X_HI_SHIFT) & 0x3f) << 16)) - WORD3.SECTION_BIAS
|
|
274
|
+
const sZ = (((word3 >>> 16) & 0xffff) | (((word2 >>> WORD2.SECTION_Z_HI_SHIFT) & 0x3f) << 16)) - WORD3.SECTION_BIAS
|
|
261
275
|
const sY = ((word2 >>> WORD2.SECTION_Y_SHIFT) & ((1 << WORD2.SECTION_Y_BITS) - 1)) - 4
|
|
262
276
|
return { x: sX * 16, y: sY * 16, z: sZ * 16 }
|
|
263
277
|
}
|
|
@@ -323,7 +337,14 @@ export function tryBuildShaderCubeInstances(
|
|
|
323
337
|
opts: BuildShaderCubeInstancesOpts,
|
|
324
338
|
words: number[],
|
|
325
339
|
): boolean {
|
|
326
|
-
const {
|
|
340
|
+
const {
|
|
341
|
+
sectionOrigin,
|
|
342
|
+
sectionHeight,
|
|
343
|
+
biome,
|
|
344
|
+
tintPalette,
|
|
345
|
+
textureIndexMapping,
|
|
346
|
+
doAO = true,
|
|
347
|
+
} = opts
|
|
327
348
|
|
|
328
349
|
if (!isShaderCubeBlock(cached, model, sectionHeight, textureIndexMapping)) {
|
|
329
350
|
return false
|
|
@@ -381,9 +402,10 @@ export function tryBuildShaderCubeInstances(
|
|
|
381
402
|
words.push(
|
|
382
403
|
packWord0(lx, ly, lz, faceIdx, tintIndex, ao),
|
|
383
404
|
packWord1(lightCombined),
|
|
384
|
-
packWord2(texIndex, aoDiagonalFlip, sectionOrigin.y),
|
|
405
|
+
packWord2(texIndex, aoDiagonalFlip, sectionOrigin.x, sectionOrigin.y, sectionOrigin.z),
|
|
385
406
|
packWord3(sectionOrigin.x, sectionOrigin.z),
|
|
386
407
|
)
|
|
408
|
+
|
|
387
409
|
}
|
|
388
410
|
|
|
389
411
|
return true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import { test, expect, beforeEach } from 'vitest'
|
|
3
|
-
import { WORD0, WORD2
|
|
3
|
+
import { WORD0, WORD2 } from '../../three/shaders/cubeBlockShader'
|
|
4
4
|
import {
|
|
5
5
|
resetShaderCubeResources,
|
|
6
6
|
getShaderCubeResources,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
countVisibleFaces,
|
|
11
11
|
unpackTexIndexFromWord2,
|
|
12
12
|
decodeSectionBaseFromWords,
|
|
13
|
+
packWord2Empty,
|
|
13
14
|
packWord3,
|
|
14
15
|
SHADER_CUBES_FORMAT_VERSION,
|
|
15
16
|
SHADER_CUBES_WORDS_PER_FACE,
|
|
@@ -305,33 +306,47 @@ test('doAO false: full bright AO/light and no diagonal flip', () => {
|
|
|
305
306
|
expect(words[2]! & (1 << WORD2.DIAGONAL_FLAG_SHIFT)).toBe(0)
|
|
306
307
|
})
|
|
307
308
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
309
|
+
const SECTION_ORIGIN_ROUND_TRIP_CASES: Array<{ x: number, y: number, z: number }> = [
|
|
310
|
+
{ x: 0, y: 16, z: 32 },
|
|
311
|
+
{ x: 0, y: 0, z: 0 },
|
|
312
|
+
{ x: 524288, y: 0, z: 524288 },
|
|
313
|
+
{ x: 1000000, y: 64, z: 1000000 },
|
|
314
|
+
{ x: 33000000, y: 0, z: 33000000 },
|
|
315
|
+
{ x: -524288, y: 0, z: -524288 },
|
|
316
|
+
{ x: -1000000, y: 0, z: -1000000 },
|
|
317
|
+
{ x: 1000000, y: 0, z: -1000000 },
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
test.each(SECTION_ORIGIN_ROUND_TRIP_CASES)(
|
|
321
|
+
'section base coords round-trip in word2/word3 at origin (%#)',
|
|
322
|
+
(sectionOrigin) => {
|
|
323
|
+
const words: number[] = []
|
|
324
|
+
const block = {
|
|
325
|
+
position: [10, 17, 4] as [number, number, number],
|
|
326
|
+
visible_faces: 1 << 2,
|
|
327
|
+
ao_data: [[3, 3, 3, 3]],
|
|
328
|
+
light_data: [[1, 1, 1, 1]],
|
|
329
|
+
light_combined: [[255, 255, 255, 255]],
|
|
330
|
+
}
|
|
331
|
+
const { textureIndexMapping, tintPalette } = getShaderCubeResources()
|
|
332
|
+
const model = { elements: [{ faces: SIX_FACE_TEXTURES }] }
|
|
333
|
+
tryBuildShaderCubeInstances(
|
|
334
|
+
block,
|
|
335
|
+
{ blockName: 'stone', blockProps: {}, isCube: true, model },
|
|
336
|
+
model,
|
|
337
|
+
{ sectionOrigin, sectionHeight: 16, tintPalette, textureIndexMapping },
|
|
338
|
+
words,
|
|
339
|
+
)
|
|
340
|
+
const base = decodeSectionBaseFromWords(words[2]!, words[3]!)
|
|
341
|
+
expect(base).toEqual(sectionOrigin)
|
|
342
|
+
},
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
test('packWord2Empty: bit 18 set regardless of high X/Z bits in word2', () => {
|
|
346
|
+
const empty = packWord2Empty()
|
|
347
|
+
expect(empty & (1 << WORD2.EMPTY_SHIFT)).not.toBe(0)
|
|
348
|
+
const withHighBits = empty | (0x3f << WORD2.SECTION_X_HI_SHIFT) | (0x3f << WORD2.SECTION_Z_HI_SHIFT)
|
|
349
|
+
expect(withHighBits & (1 << WORD2.EMPTY_SHIFT)).not.toBe(0)
|
|
335
350
|
})
|
|
336
351
|
|
|
337
352
|
test('GlobalBlockBuffer: free-list reuses slot with EMPTY sentinel', () => {
|