minecraft-renderer 0.1.78 → 0.1.80
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 +29 -25
- package/dist/mesher.js +36 -36
- package/dist/mesher.js.map +4 -4
- package/dist/mesherWasm.js +217 -217
- package/dist/minecraft-renderer.js +32 -32
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +415 -415
- package/package.json +5 -2
- package/src/bundler/bundlePrepare.ts +3 -8
- package/src/graphicsBackend/appViewer.ts +10 -7
- package/src/graphicsBackend/config.ts +1 -5
- package/src/graphicsBackend/playerState.ts +35 -34
- package/src/graphicsBackend/preloadWorkers.ts +43 -41
- package/src/graphicsBackend/rendererDefaultOptions.ts +68 -70
- package/src/graphicsBackend/rendererOptionsSync.ts +24 -50
- package/src/graphicsBackend/types.ts +2 -2
- package/src/index.ts +6 -27
- package/src/lib/DebugGui.ts +34 -31
- package/src/lib/animationController.ts +9 -9
- package/src/lib/bakeLegacyLight.ts +1 -6
- package/src/lib/bindAbortableListener.test.ts +32 -12
- package/src/lib/bindAbortableListener.ts +14 -6
- package/src/lib/blockEntityLightRegistry.ts +11 -20
- package/src/lib/blockEntityLighting.test.ts +1 -6
- package/src/lib/blockEntityLighting.ts +5 -5
- package/src/lib/buildSharedConfig.mjs +4 -4
- package/src/lib/buildWorkerMcDataIndexes.test.ts +4 -4
- package/src/lib/buildWorkerMcDataIndexes.ts +11 -19
- package/src/lib/cameraBobbing.ts +1 -1
- package/src/lib/canvas2DOverlay.example.ts +13 -33
- package/src/lib/canvas2DOverlay.quickstart.ts +24 -54
- package/src/lib/canvas2DOverlay.ts +28 -47
- package/src/lib/cleanupDecorator.ts +2 -2
- package/src/lib/createPlayerObject.test.ts +2 -7
- package/src/lib/createPlayerObject.ts +18 -25
- package/src/lib/frameTimingCollector.ts +2 -5
- package/src/lib/guiRenderer.ts +57 -42
- package/src/lib/items.ts +15 -9
- package/src/lib/mesherlogReader.ts +5 -6
- package/src/lib/simpleUtils.ts +5 -5
- package/src/lib/skyLight.ts +3 -3
- package/src/lib/smoothSwitcher.ts +11 -18
- package/src/lib/ui/newStats.ts +10 -12
- package/src/lib/utils/proxy.ts +7 -1
- package/src/lib/utils/skins.ts +4 -4
- package/src/lib/utils.ts +1 -1
- package/src/lib/workerMessageSanitize.ts +5 -12
- package/src/lib/workerProxy.ts +52 -42
- package/src/lib/workerSyncOps.test.ts +58 -41
- package/src/lib/worldrendererCommon.reconfigure.test.ts +24 -21
- package/src/lib/worldrendererCommon.removeColumn.test.ts +11 -13
- package/src/lib/worldrendererCommon.ts +338 -211
- package/src/mesher-legacy/mesher.ts +9 -4
- package/src/mesher-legacy/test/a.ts +1 -1
- package/src/mesher-legacy/test/mesherTester.ts +2 -2
- package/src/mesher-legacy/test/playground.ts +1 -1
- package/src/mesher-legacy/test/run/test-js.ts +1 -1
- package/src/mesher-legacy/test/snapshotUtils.ts +3 -8
- package/src/mesher-legacy/test/test-perf.ts +1 -1
- package/src/mesher-legacy/test/tests.test.ts +2 -2
- package/src/mesher-shared/blockEntityMetadata.test.ts +7 -5
- package/src/mesher-shared/blockEntityMetadata.ts +28 -19
- package/src/mesher-shared/blockPropertiesForMeshing.ts +2 -2
- package/src/mesher-shared/computeHeightmap.ts +2 -4
- package/src/mesher-shared/exportedGeometryTypes.ts +3 -3
- package/src/mesher-shared/faceOcclusion.ts +324 -0
- package/src/mesher-shared/models.ts +80 -131
- package/src/mesher-shared/modelsGeometryCommon.ts +30 -15
- package/src/mesher-shared/shared.ts +46 -46
- package/src/mesher-shared/standaloneRenderer.ts +40 -44
- package/src/mesher-shared/tests/faceOcclusion.test.ts +179 -0
- package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +12 -8
- package/src/mesher-shared/vertexShading.ts +2 -11
- package/src/mesher-shared/world.ts +19 -19
- package/src/mesher-shared/worldLighting.test.ts +2 -2
- package/src/performanceMonitor/PerformanceMonitor.ts +4 -13
- package/src/performanceMonitor/formatPerformanceFactorsDebug.ts +2 -2
- package/src/performanceMonitor/index.ts +1 -5
- package/src/performanceMonitor/types.ts +1 -1
- package/src/playerState/playerState.ts +55 -50
- package/src/playerState/types.ts +2 -8
- package/src/playground/allEntitiesDebug.ts +19 -16
- package/src/playground/baseScene.ts +29 -32
- package/src/playground/mobileControls.tsx +133 -129
- package/src/playground/playground.html +76 -73
- package/src/playground/playground.ts +14 -1
- package/src/playground/playgroundUi.tsx +100 -74
- package/src/playground/reactUtils.ts +3 -7
- package/src/playground/scenes/entities.ts +10 -7
- package/src/playground/scenes/frequentUpdates.ts +5 -5
- package/src/playground/scenes/geometryExport.ts +6 -20
- package/src/playground/scenes/highPosEntityUpdates.ts +2 -10
- package/src/playground/scenes/index.ts +1 -1
- package/src/playground/scenes/lightingStarfield.ts +18 -12
- package/src/playground/scenes/main.ts +15 -11
- package/src/playground/scenes/partialBlockCulling.ts +31 -0
- package/src/playground/scenes/railsCobweb.ts +1 -1
- package/src/playground/scenes/rotationIssue.ts +1 -1
- package/src/playground/scenes/slabsOptimization.ts +1 -2
- package/src/playground/scenes/transparencyIssue.ts +1 -1
- package/src/playground/shared.ts +2 -2
- package/src/resourcesManager/resourcesManager.ts +29 -54
- package/src/resourcesManager/resourcesManager.worker.test.ts +6 -10
- package/src/shims/minecraftData.ts +70 -24
- package/src/sign-renderer/index.html +10 -12
- package/src/sign-renderer/index.ts +29 -29
- package/src/sign-renderer/playground.ts +22 -15
- package/src/sign-renderer/tests.test.ts +16 -13
- package/src/sign-renderer/vite.config.ts +2 -2
- package/src/three/appShared.ts +46 -25
- package/src/three/bannerRenderer.ts +33 -61
- package/src/three/cameraShake.ts +15 -6
- package/src/three/chunkMeshManager.ts +329 -301
- package/src/three/cinimaticScript.ts +26 -15
- package/src/three/cubeDrawSpans.ts +27 -37
- package/src/three/cubeMultiDraw.ts +9 -22
- package/src/three/documentRenderer.ts +15 -18
- package/src/three/entities.ts +175 -150
- package/src/three/entity/EntityMesh.ts +114 -108
- package/src/three/entity/animations.js +3 -3
- package/src/three/entity/armorModels.ts +15 -15
- package/src/three/entity/gltfAnimationUtils.ts +2 -5
- package/src/three/fireworks.ts +34 -32
- package/src/three/fireworksRenderer.ts +32 -29
- package/src/three/globalBlockBuffer.ts +174 -99
- package/src/three/globalLegacyBuffer.ts +465 -136
- package/src/three/graphicsBackendBase.ts +19 -25
- package/src/three/graphicsBackendOffThread.ts +12 -15
- package/src/three/hand.ts +21 -21
- package/src/three/handLegacy.ts +3 -13
- package/src/three/holdingBlock.ts +116 -79
- package/src/three/holdingBlockFactory.ts +1 -4
- package/src/three/holdingBlockItemIdentity.test.ts +25 -17
- package/src/three/holdingBlockItemIdentity.ts +9 -6
- package/src/three/holdingBlockLegacy.ts +113 -132
- package/src/three/itemMesh.ts +20 -29
- package/src/three/legacyMultiDraw.ts +93 -0
- package/src/three/legacySectionCull.ts +8 -14
- package/src/three/menuBackground/activeView.ts +1 -4
- package/src/three/menuBackground/assetUrl.ts +1 -1
- package/src/three/menuBackground/classic.ts +4 -12
- package/src/three/menuBackground/gpuPreference.ts +1 -3
- package/src/three/menuBackground/index.ts +2 -11
- package/src/three/menuBackground/renderer.ts +1 -5
- package/src/three/menuBackground/types.ts +1 -4
- package/src/three/menuBackground/v2.ts +196 -96
- package/src/three/menuBackground/v2Meta.ts +3 -3
- package/src/three/menuBackground/worldBlocks.ts +37 -35
- package/src/three/modules/blockBreakParticles.ts +59 -28
- package/src/three/modules/cameraBobbing.ts +3 -6
- package/src/three/modules/index.ts +1 -1
- package/src/three/modules/rain.ts +10 -23
- package/src/three/modules/sciFiWorldReveal.ts +11 -25
- package/src/three/modules/starfield.ts +7 -13
- package/src/three/renderSlot.ts +28 -16
- package/src/three/rendererModuleSystem.ts +1 -3
- package/src/three/sceneOrigin.ts +96 -33
- package/src/three/sectionRaycastAabb.ts +30 -18
- package/src/three/shaderCubeMesh.ts +6 -17
- package/src/three/shaders/cubeBlockShader.ts +85 -96
- package/src/three/shaders/legacyBlockShader.ts +21 -43
- package/src/three/shaders/textureIndexMapping.ts +89 -95
- package/src/three/shaders/tintPalette.ts +156 -170
- package/src/three/signTextureCache.ts +9 -12
- package/src/three/skyboxRenderer.ts +62 -70
- package/src/three/tests/chunkMeshManagerLegacy.test.ts +133 -25
- package/src/three/tests/cubeDrawSpans.test.ts +55 -25
- package/src/three/tests/globalBlockBuffer.test.ts +217 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +460 -58
- package/src/three/tests/legacyMultiDraw.test.ts +88 -0
- package/src/three/tests/pr3PerFrameCpuCuts.test.ts +370 -0
- package/src/three/tests/signTextureCache.test.ts +4 -4
- package/src/three/threeJsMedia.ts +55 -72
- package/src/three/threeJsParticles.ts +32 -41
- package/src/three/threeJsSound.ts +3 -3
- package/src/three/threeJsUtils.ts +3 -3
- package/src/three/threeWorker.ts +2 -2
- package/src/three/waypointSprite.ts +55 -82
- package/src/three/waypoints.ts +20 -23
- package/src/three/world/cursorBlock.ts +25 -12
- package/src/three/world/vr.ts +11 -17
- package/src/three/worldGeometryExport.ts +15 -34
- package/src/three/worldRendererThree.ts +131 -133
- package/src/wasm-mesher/bridge/convertChunk.ts +3 -9
- package/src/wasm-mesher/bridge/render-from-wasm.ts +204 -229
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +26 -67
- package/src/wasm-mesher/tests/cullingRegression.test.ts +307 -0
- package/src/wasm-mesher/tests/heightmapParity.test.ts +8 -18
- package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +2 -2
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +17 -4
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +103 -141
- package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +14 -22
- package/src/wasm-mesher/worker/mesherWasm.ts +150 -141
- package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +2 -8
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +1 -3
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +1 -5
- package/src/wasm-mesher/worker/mesherWasmRequestTracker.ts +42 -42
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +34 -39
- package/src/worldView/worldViewWorkerBridge.test.ts +4 -4
|
@@ -5,14 +5,16 @@ import { getSectionGeometry, setBlockStatesData as setMesherData, computeWirefra
|
|
|
5
5
|
import { BlockStateModelInfo } from '../mesher-shared/shared'
|
|
6
6
|
import { handleGetHeightmap, EMPTY_COLUMN_HEIGHTMAP_SENTINEL } from '../mesher-shared/computeHeightmap'
|
|
7
7
|
|
|
8
|
-
globalThis.structuredClone ??=
|
|
8
|
+
globalThis.structuredClone ??= value => JSON.parse(JSON.stringify(value))
|
|
9
9
|
|
|
10
10
|
if (globalThis.module && module.require) {
|
|
11
11
|
// If we are in a node environement, we need to fake some env variables
|
|
12
12
|
const r = module.require
|
|
13
13
|
const { parentPort } = r('worker_threads')
|
|
14
14
|
global.self = parentPort
|
|
15
|
-
global.postMessage = (value, transferList) => {
|
|
15
|
+
global.postMessage = (value, transferList) => {
|
|
16
|
+
parentPort.postMessage(value, transferList)
|
|
17
|
+
}
|
|
16
18
|
global.performance = r('perf_hooks').performance
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -44,7 +46,10 @@ const postMessage = (data, transferList = []) => {
|
|
|
44
46
|
|
|
45
47
|
function drainQueue(from, to) {
|
|
46
48
|
const messages = queuedMessages.slice(from, to)
|
|
47
|
-
global.postMessage(
|
|
49
|
+
global.postMessage(
|
|
50
|
+
messages.map(m => m.data),
|
|
51
|
+
messages.flatMap(m => m.transferList) as unknown as string
|
|
52
|
+
)
|
|
48
53
|
queuedMessages = queuedMessages.slice(to)
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -175,7 +180,7 @@ const handleMessage = data => {
|
|
|
175
180
|
type: 'mc-web-pong',
|
|
176
181
|
workerIndex: replyWorkerIndex,
|
|
177
182
|
t: data.t,
|
|
178
|
-
recvAt: typeof performance !== 'undefined' ? performance.now() : undefined
|
|
183
|
+
recvAt: typeof performance !== 'undefined' ? performance.now() : undefined
|
|
179
184
|
})
|
|
180
185
|
break
|
|
181
186
|
}
|
|
@@ -47,7 +47,7 @@ export const setup = (version, initialBlocks: Array<[number[], string]>, options
|
|
|
47
47
|
reload()
|
|
48
48
|
|
|
49
49
|
const getLights = () => {
|
|
50
|
-
return Object.fromEntries(getGeometry().faces.map(({ face, light }) =>
|
|
50
|
+
return Object.fromEntries(getGeometry().faces.map(({ face, light }) => [face, (light ?? 0) * 15 - 2]))
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const setLight = (x: number, y: number, z: number, val = 0) => {
|
|
@@ -78,5 +78,5 @@ const addPositions = [
|
|
|
78
78
|
[[0, 1, 0], 'stone'],
|
|
79
79
|
[[0, -1, 0], 'stone'],
|
|
80
80
|
[[0, 0, 1], 'stone'],
|
|
81
|
-
[[0, 0, -1], 'stone']
|
|
81
|
+
[[0, 0, -1], 'stone']
|
|
82
82
|
]
|
|
@@ -8,7 +8,7 @@ import fs from 'fs'
|
|
|
8
8
|
import { join } from 'path'
|
|
9
9
|
|
|
10
10
|
const chunk = getChunk()
|
|
11
|
-
const { getGeometry } = setup(VERSION, [], { chunkOverride: chunk, noDebugTiles: true
|
|
11
|
+
const { getGeometry } = setup(VERSION, [], { chunkOverride: chunk, noDebugTiles: true })
|
|
12
12
|
|
|
13
13
|
getGeometry()
|
|
14
14
|
console.time('getGeometry')
|
|
@@ -59,15 +59,10 @@ export function deepEqual(obj1: any, obj2: any): boolean {
|
|
|
59
59
|
* @param baseDir - Base directory for relative paths (defaults to __dirname, ignored if snapshotPath is absolute)
|
|
60
60
|
* @returns true if snapshot matches or was created, throws error if mismatch
|
|
61
61
|
*/
|
|
62
|
-
export function compareOrWriteSnapshot(
|
|
63
|
-
output: any,
|
|
64
|
-
snapshotPath: string,
|
|
65
|
-
baseDir?: string
|
|
66
|
-
): boolean {
|
|
62
|
+
export function compareOrWriteSnapshot(output: any, snapshotPath: string, baseDir?: string): boolean {
|
|
67
63
|
// If path is absolute, use it directly; otherwise join with baseDir or __dirname
|
|
68
|
-
const fullPath =
|
|
69
|
-
? snapshotPath
|
|
70
|
-
: baseDir ? join(baseDir, snapshotPath) : join(__dirname, snapshotPath)
|
|
64
|
+
const fullPath =
|
|
65
|
+
snapshotPath.startsWith('/') || snapshotPath.includes(':') ? snapshotPath : baseDir ? join(baseDir, snapshotPath) : join(__dirname, snapshotPath)
|
|
71
66
|
const serialized = serializeOutput(output)
|
|
72
67
|
|
|
73
68
|
if (!existsSync(fullPath)) {
|
|
@@ -22,7 +22,7 @@ const fillers = {
|
|
|
22
22
|
for (let z = 0; z < 16; z++) {
|
|
23
23
|
for (let y = -64; y < 320; y++) {
|
|
24
24
|
// Create a 3D checkerboard pattern where each block is surrounded by air
|
|
25
|
-
const isEvalPoint =
|
|
25
|
+
const isEvalPoint = x % 3 === 0 && z % 3 === 0 && y % 3 === 0
|
|
26
26
|
// chunk.setBlockStateId(new Vec3(x, y, z), isEvalPoint ? 1 : 0)
|
|
27
27
|
chunk.setBlockType(new Vec3(x, y, z), isEvalPoint ? 1 : 0)
|
|
28
28
|
}
|
|
@@ -22,8 +22,8 @@ test('Known blocks are not rendered', () => {
|
|
|
22
22
|
|
|
23
23
|
let time = 0
|
|
24
24
|
let times = 0
|
|
25
|
-
const missingBlocks = {}/* as {[number, number]} */
|
|
26
|
-
const erroredBlocks = {}/* as {[number, number]} */
|
|
25
|
+
const missingBlocks = {} /* as {[number, number]} */
|
|
26
|
+
const erroredBlocks = {} /* as {[number, number]} */
|
|
27
27
|
for (const block of mcData.blocksArray) {
|
|
28
28
|
if (ignoreAsExpected.has(block.name)) continue
|
|
29
29
|
// if (block.maxStateId! - block.minStateId! > 100) continue
|
|
@@ -8,26 +8,28 @@ describe('collectBlockEntityMetadata', () => {
|
|
|
8
8
|
const target = { signs: {}, heads: {}, banners: {} }
|
|
9
9
|
const block = {
|
|
10
10
|
name: 'pink_banner',
|
|
11
|
-
getProperties: () => ({ rotation: 0 })
|
|
11
|
+
getProperties: () => ({ rotation: 0 })
|
|
12
12
|
}
|
|
13
13
|
collectBlockEntityMetadata(
|
|
14
14
|
block,
|
|
15
|
-
1,
|
|
15
|
+
1,
|
|
16
|
+
2,
|
|
17
|
+
3,
|
|
16
18
|
target,
|
|
17
19
|
{},
|
|
18
20
|
{
|
|
19
21
|
getChannelLightNorm: (pos: Vec3) => {
|
|
20
22
|
expect(pos).toBeInstanceOf(Vec3)
|
|
21
23
|
return { block: 0.4, sky: 0.8 }
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
24
26
|
)
|
|
25
27
|
expect(target.banners['1,2,3']).toEqual({
|
|
26
28
|
isWall: false,
|
|
27
29
|
blockName: 'pink_banner',
|
|
28
30
|
rotation: 0,
|
|
29
31
|
blockLightNorm: 0.4,
|
|
30
|
-
skyLightNorm: 0.8
|
|
32
|
+
skyLightNorm: 0.8
|
|
31
33
|
})
|
|
32
34
|
})
|
|
33
35
|
})
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
//@ts-nocheck
|
|
2
2
|
import { Vec3 } from 'vec3'
|
|
3
3
|
|
|
4
|
-
export interface SignMeta {
|
|
5
|
-
|
|
4
|
+
export interface SignMeta {
|
|
5
|
+
isWall: boolean
|
|
6
|
+
isHanging: boolean
|
|
7
|
+
rotation: number
|
|
8
|
+
}
|
|
9
|
+
export interface HeadMeta {
|
|
10
|
+
isWall: boolean
|
|
11
|
+
rotation: number
|
|
12
|
+
}
|
|
6
13
|
export interface BannerMeta {
|
|
7
14
|
isWall: boolean
|
|
8
15
|
blockName: string
|
|
@@ -24,24 +31,26 @@ export interface BlockEntityMetadataOptions {
|
|
|
24
31
|
type BlockLike = { name: string; getProperties(): any }
|
|
25
32
|
|
|
26
33
|
type LightSampler = {
|
|
27
|
-
getChannelLightNorm(pos: Vec3): { block: number
|
|
34
|
+
getChannelLightNorm(pos: Vec3): { block: number; sky: number }
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
export function collectBlockEntityMetadata(
|
|
31
38
|
block: BlockLike,
|
|
32
|
-
x: number,
|
|
39
|
+
x: number,
|
|
40
|
+
y: number,
|
|
41
|
+
z: number,
|
|
33
42
|
target: BlockEntityMetadataTarget,
|
|
34
43
|
options: BlockEntityMetadataOptions,
|
|
35
|
-
world?: LightSampler
|
|
44
|
+
world?: LightSampler
|
|
36
45
|
): void {
|
|
37
46
|
if ((block.name.includes('_sign') || block.name === 'sign') && !options.disableBlockEntityTextures) {
|
|
38
47
|
const key = `${x},${y},${z}`
|
|
39
48
|
const props: any = block.getProperties()
|
|
40
49
|
const facingRotationMap = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
north: 2,
|
|
51
|
+
south: 0,
|
|
52
|
+
west: 1,
|
|
53
|
+
east: 3
|
|
45
54
|
}
|
|
46
55
|
const isWall = block.name.endsWith('wall_sign') || block.name.endsWith('wall_hanging_sign')
|
|
47
56
|
const isHanging = block.name.endsWith('hanging_sign')
|
|
@@ -54,10 +63,10 @@ export function collectBlockEntityMetadata(
|
|
|
54
63
|
const key = `${x},${y},${z}`
|
|
55
64
|
const props: any = block.getProperties()
|
|
56
65
|
const facingRotationMap = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
north: 0,
|
|
67
|
+
south: 2,
|
|
68
|
+
west: 3,
|
|
69
|
+
east: 1
|
|
61
70
|
}
|
|
62
71
|
const isWall = block.name === 'player_wall_head'
|
|
63
72
|
target.heads[key] = {
|
|
@@ -68,19 +77,19 @@ export function collectBlockEntityMetadata(
|
|
|
68
77
|
const key = `${x},${y},${z}`
|
|
69
78
|
const props: any = block.getProperties()
|
|
70
79
|
const facingRotationMap = {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
north: 2,
|
|
81
|
+
south: 0,
|
|
82
|
+
west: 1,
|
|
83
|
+
east: 3
|
|
75
84
|
}
|
|
76
85
|
const isWall = block.name.endsWith('_wall_banner')
|
|
77
86
|
const light = world?.getChannelLightNorm(new Vec3(x, y, z)) ?? { block: 0, sky: 1 }
|
|
78
87
|
target.banners[key] = {
|
|
79
88
|
isWall,
|
|
80
89
|
blockName: block.name, // Pass block name for base color extraction
|
|
81
|
-
rotation: isWall ? facingRotationMap[props.facing] :
|
|
90
|
+
rotation: isWall ? facingRotationMap[props.facing] : props.rotation === undefined ? 0 : +props.rotation,
|
|
82
91
|
blockLightNorm: light.block,
|
|
83
|
-
skyLightNorm: light.sky
|
|
92
|
+
skyLightNorm: light.sky
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
}
|
|
@@ -16,7 +16,7 @@ export function resolveBlockPropertiesForMeshing(
|
|
|
16
16
|
blockProvider: WorldBlockProvider,
|
|
17
17
|
blockStateId: number,
|
|
18
18
|
PrismarineBlockCtor: { fromStateId: (id: number, biome: number) => Block }
|
|
19
|
-
): { name: string
|
|
19
|
+
): { name: string; properties: Record<string, unknown> } {
|
|
20
20
|
if (world?.preflat) {
|
|
21
21
|
const block = world.getBlock(cursor, blockProvider, {})
|
|
22
22
|
if (block) {
|
|
@@ -45,7 +45,7 @@ export function preflatBlockCalculation(block: Block, world: World, position: Ve
|
|
|
45
45
|
const props = {}
|
|
46
46
|
let changed = false
|
|
47
47
|
for (const [i, neighbor] of neighbors.entries()) {
|
|
48
|
-
const isConnectedToSolid = isSolidConnection ?
|
|
48
|
+
const isConnectedToSolid = isSolidConnection ? neighbor && !neighbor.transparent : false
|
|
49
49
|
if (isConnectedToSolid || neighbor?.name === block.name) {
|
|
50
50
|
props[['south', 'north', 'east', 'west'][i]] = 'true'
|
|
51
51
|
changed = true
|
|
@@ -43,9 +43,7 @@ export function computeHeightmap(world: World, chunkX: number, chunkZ: number):
|
|
|
43
43
|
// when we hit worldMinY with the column still entirely invisible/empty.
|
|
44
44
|
// Only the former is a real surface; the latter is the empty-column
|
|
45
45
|
// case and must use the sentinel to match Rust's encoding.
|
|
46
|
-
heightmap[index] = block && !INVISIBLE_BLOCKS.has(block.name)
|
|
47
|
-
? blockPos.y
|
|
48
|
-
: EMPTY_COLUMN_HEIGHTMAP_SENTINEL
|
|
46
|
+
heightmap[index] = block && !INVISIBLE_BLOCKS.has(block.name) ? blockPos.y : EMPTY_COLUMN_HEIGHTMAP_SENTINEL
|
|
49
47
|
}
|
|
50
48
|
}
|
|
51
49
|
return heightmap
|
|
@@ -59,7 +57,7 @@ export function computeHeightmap(world: World, chunkX: number, chunkZ: number):
|
|
|
59
57
|
* Test fixtures (see `wasm-mesher/test-section-boundary.ts`) invoke this helper
|
|
60
58
|
* directly to exercise the real handler path end-to-end.
|
|
61
59
|
*/
|
|
62
|
-
export function handleGetHeightmap(world: World, x: number, z: number): { key: string
|
|
60
|
+
export function handleGetHeightmap(world: World, x: number, z: number): { key: string; heightmap: Int16Array } {
|
|
63
61
|
const heightmap = computeHeightmap(world, x, z)
|
|
64
62
|
const key = `${Math.floor(x / 16)},${Math.floor(z / 16)}`
|
|
65
63
|
return { key, heightmap }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
export interface ExportedSection {
|
|
5
5
|
key: string
|
|
6
|
-
position: { x: number
|
|
6
|
+
position: { x: number; y: number; z: number }
|
|
7
7
|
geometry: {
|
|
8
8
|
positions: number[]
|
|
9
9
|
normals: number[]
|
|
@@ -33,8 +33,8 @@ export interface ExportedWorldGeometry {
|
|
|
33
33
|
version: string
|
|
34
34
|
exportedAt: string
|
|
35
35
|
camera: {
|
|
36
|
-
position: { x: number
|
|
37
|
-
rotation: { pitch: number
|
|
36
|
+
position: { x: number; y: number; z: number }
|
|
37
|
+
rotation: { pitch: number; yaw: number }
|
|
38
38
|
}
|
|
39
39
|
sections: ExportedSection[]
|
|
40
40
|
textureAtlasDataUrl?: string
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import type { WorldBlockProvider } from 'mc-assets/dist/worldBlockProvider'
|
|
3
|
+
import PrismarineBlockLoader from 'prismarine-block'
|
|
4
|
+
import moreBlockDataGeneratedJson from '../lib/moreBlockDataGenerated.json'
|
|
5
|
+
import { buildRotationMatrix, elemFaces, matmul3, matmulmat3, vecsub3 } from './modelsGeometryCommon'
|
|
6
|
+
import type { BlockElement } from './modelsGeometryCommon'
|
|
7
|
+
import type { BlockModelPartsResolved } from './world'
|
|
8
|
+
|
|
9
|
+
export type CardinalDir = [number, number, number]
|
|
10
|
+
|
|
11
|
+
type FaceName = keyof typeof elemFaces
|
|
12
|
+
|
|
13
|
+
type BlockStateInfo = {
|
|
14
|
+
stateId: number
|
|
15
|
+
name: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const BLOCK_CENTER: [number, number, number] = [8, 8, 8]
|
|
19
|
+
const SNAP_EPS = 1e-5
|
|
20
|
+
|
|
21
|
+
function snapBlockUnit(n: number): number {
|
|
22
|
+
const rounded = Math.round(n)
|
|
23
|
+
return Math.abs(n - rounded) < SNAP_EPS ? rounded : n
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DIR_KEYS: CardinalDir[] = [
|
|
27
|
+
[0, 1, 0],
|
|
28
|
+
[0, -1, 0],
|
|
29
|
+
[1, 0, 0],
|
|
30
|
+
[-1, 0, 0],
|
|
31
|
+
[0, 0, 1],
|
|
32
|
+
[0, 0, -1]
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
const faceProjections: Record<
|
|
36
|
+
FaceName,
|
|
37
|
+
{
|
|
38
|
+
touches: (element: BlockElement) => boolean
|
|
39
|
+
rect: (element: BlockElement) => [number, number, number, number]
|
|
40
|
+
}
|
|
41
|
+
> = {
|
|
42
|
+
up: {
|
|
43
|
+
touches: e => e.to[1] === 16,
|
|
44
|
+
rect: e => [e.from[0], e.from[2], e.to[0], e.to[2]]
|
|
45
|
+
},
|
|
46
|
+
down: {
|
|
47
|
+
touches: e => e.from[1] === 0,
|
|
48
|
+
rect: e => [e.from[0], e.from[2], e.to[0], e.to[2]]
|
|
49
|
+
},
|
|
50
|
+
east: {
|
|
51
|
+
touches: e => e.to[0] === 16,
|
|
52
|
+
rect: e => [e.from[2], e.from[1], e.to[2], e.to[1]]
|
|
53
|
+
},
|
|
54
|
+
west: {
|
|
55
|
+
touches: e => e.from[0] === 0,
|
|
56
|
+
rect: e => [e.from[2], e.from[1], e.to[2], e.to[1]]
|
|
57
|
+
},
|
|
58
|
+
south: {
|
|
59
|
+
touches: e => e.to[2] === 16,
|
|
60
|
+
rect: e => [e.from[0], e.from[1], e.to[0], e.to[1]]
|
|
61
|
+
},
|
|
62
|
+
north: {
|
|
63
|
+
touches: e => e.from[2] === 0,
|
|
64
|
+
rect: e => [e.from[0], e.from[1], e.to[0], e.to[1]]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const shapeCache = new Map<string, Uint16Array[]>()
|
|
69
|
+
const blockLoaderCache = new Map<string, ReturnType<typeof PrismarineBlockLoader>>()
|
|
70
|
+
const noOcclusionsSet = new Set(Object.keys(moreBlockDataGeneratedJson.noOcclusions))
|
|
71
|
+
|
|
72
|
+
export function resetFaceOcclusionCache() {
|
|
73
|
+
shapeCache.clear()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function oppositeDir(dir: CardinalDir): CardinalDir {
|
|
77
|
+
return [-dir[0], -dir[1], -dir[2]]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function roundCardinalDir(dir: [number, number, number]): CardinalDir {
|
|
81
|
+
return [Math.round(dir[0]), Math.round(dir[1]), Math.round(dir[2])]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function buildModelGlobalMatrix(model: { x?: number; y?: number; z?: number }): number[][] | null {
|
|
85
|
+
let globalMatrix = null as number[][] | null
|
|
86
|
+
for (const axis of ['x', 'y', 'z'] as const) {
|
|
87
|
+
if (axis in model) {
|
|
88
|
+
globalMatrix = globalMatrix ? matmulmat3(globalMatrix, buildRotationMatrix(axis, -(model[axis] ?? 0))) : buildRotationMatrix(axis, -(model[axis] ?? 0))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return globalMatrix
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isAxisAligned90Rotation(model: { x?: number; y?: number; z?: number }): boolean {
|
|
95
|
+
for (const axis of ['x', 'y', 'z'] as const) {
|
|
96
|
+
if (!(axis in model)) continue
|
|
97
|
+
const deg = model[axis] ?? 0
|
|
98
|
+
if (deg !== 0 && deg % 90 !== 0) return false
|
|
99
|
+
}
|
|
100
|
+
return true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function dirKey(dir: CardinalDir): string {
|
|
104
|
+
return `${dir[0]},${dir[1]},${dir[2]}`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function dirIndex(dir: CardinalDir): number {
|
|
108
|
+
const key = dirKey(dir)
|
|
109
|
+
const idx = DIR_KEYS.findIndex(d => dirKey(d) === key)
|
|
110
|
+
return idx >= 0 ? idx : 0
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function emptyShape(): Uint16Array {
|
|
114
|
+
return new Uint16Array(16)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function orRectIntoShape(u0: number, v0: number, u1: number, v1: number, shape: Uint16Array) {
|
|
118
|
+
const minU = Math.max(0, Math.floor(Math.min(u0, u1)))
|
|
119
|
+
const maxU = Math.min(15, Math.ceil(Math.max(u0, u1)) - 1)
|
|
120
|
+
const minV = Math.max(0, Math.floor(Math.min(v0, v1)))
|
|
121
|
+
const maxV = Math.min(15, Math.ceil(Math.max(v0, v1)) - 1)
|
|
122
|
+
for (let row = minV; row <= maxV; row++) {
|
|
123
|
+
for (let col = minU; col <= maxU; col++) {
|
|
124
|
+
shape[row]! |= 1 << col
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function rectIsSubsetOfShape(u0: number, v0: number, u1: number, v1: number, shape: Uint16Array): boolean {
|
|
130
|
+
const minU = Math.max(0, Math.floor(Math.min(u0, u1)))
|
|
131
|
+
const maxU = Math.min(15, Math.ceil(Math.max(u0, u1)) - 1)
|
|
132
|
+
const minV = Math.max(0, Math.floor(Math.min(v0, v1)))
|
|
133
|
+
const maxV = Math.min(15, Math.ceil(Math.max(v0, v1)) - 1)
|
|
134
|
+
for (let row = minV; row <= maxV; row++) {
|
|
135
|
+
for (let col = minU; col <= maxU; col++) {
|
|
136
|
+
if ((shape[row]! & (1 << col)) === 0) return false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function rotatePointAboutCenter(globalMatrix: number[][] | null, point: [number, number, number]): [number, number, number] {
|
|
143
|
+
if (!globalMatrix) return point
|
|
144
|
+
const centered = vecsub3(point, BLOCK_CENTER)
|
|
145
|
+
const rotated = matmul3(globalMatrix, centered)
|
|
146
|
+
return [snapBlockUnit(rotated[0] + 8), snapBlockUnit(rotated[1] + 8), snapBlockUnit(rotated[2] + 8)]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function rectToFaceCorners3D(faceName: FaceName, rect: [number, number, number, number]): [number, number, number][] {
|
|
150
|
+
const [u0, v0, u1, v1] = rect
|
|
151
|
+
switch (faceName) {
|
|
152
|
+
case 'up':
|
|
153
|
+
return [
|
|
154
|
+
[u0, 16, v0],
|
|
155
|
+
[u1, 16, v0],
|
|
156
|
+
[u0, 16, v1],
|
|
157
|
+
[u1, 16, v1]
|
|
158
|
+
]
|
|
159
|
+
case 'down':
|
|
160
|
+
return [
|
|
161
|
+
[u0, 0, v0],
|
|
162
|
+
[u1, 0, v0],
|
|
163
|
+
[u0, 0, v1],
|
|
164
|
+
[u1, 0, v1]
|
|
165
|
+
]
|
|
166
|
+
case 'east':
|
|
167
|
+
return [
|
|
168
|
+
[16, v0, u0],
|
|
169
|
+
[16, v0, u1],
|
|
170
|
+
[16, v1, u0],
|
|
171
|
+
[16, v1, u1]
|
|
172
|
+
]
|
|
173
|
+
case 'west':
|
|
174
|
+
return [
|
|
175
|
+
[0, v0, u0],
|
|
176
|
+
[0, v0, u1],
|
|
177
|
+
[0, v1, u0],
|
|
178
|
+
[0, v1, u1]
|
|
179
|
+
]
|
|
180
|
+
case 'south':
|
|
181
|
+
return [
|
|
182
|
+
[u0, v0, 16],
|
|
183
|
+
[u1, v0, 16],
|
|
184
|
+
[u0, v1, 16],
|
|
185
|
+
[u1, v1, 16]
|
|
186
|
+
]
|
|
187
|
+
case 'north':
|
|
188
|
+
return [
|
|
189
|
+
[u0, v0, 0],
|
|
190
|
+
[u1, v0, 0],
|
|
191
|
+
[u0, v1, 0],
|
|
192
|
+
[u1, v1, 0]
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function boundingRectOnWorldPlane(corners: [number, number, number][], worldDir: CardinalDir): [number, number, number, number] {
|
|
198
|
+
const dk = dirKey(worldDir)
|
|
199
|
+
let minU = 16
|
|
200
|
+
let minV = 16
|
|
201
|
+
let maxU = 0
|
|
202
|
+
let maxV = 0
|
|
203
|
+
for (const [x, y, z] of corners) {
|
|
204
|
+
let u: number
|
|
205
|
+
let v: number
|
|
206
|
+
if (dk === '0,1,0' || dk === '0,-1,0') {
|
|
207
|
+
u = x
|
|
208
|
+
v = z
|
|
209
|
+
} else if (dk === '1,0,0' || dk === '-1,0,0') {
|
|
210
|
+
u = z
|
|
211
|
+
v = y
|
|
212
|
+
} else {
|
|
213
|
+
u = x
|
|
214
|
+
v = y
|
|
215
|
+
}
|
|
216
|
+
minU = Math.min(minU, u)
|
|
217
|
+
maxU = Math.max(maxU, u)
|
|
218
|
+
minV = Math.min(minV, v)
|
|
219
|
+
maxV = Math.max(maxV, v)
|
|
220
|
+
}
|
|
221
|
+
return [minU, minV, maxU, maxV]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function worldFaceRect(
|
|
225
|
+
element: BlockElement,
|
|
226
|
+
faceName: FaceName,
|
|
227
|
+
worldFaceDir: CardinalDir,
|
|
228
|
+
globalMatrix: number[][] | null
|
|
229
|
+
): [number, number, number, number] | null {
|
|
230
|
+
const proj = faceProjections[faceName]
|
|
231
|
+
if (!proj || !proj.touches(element)) return null
|
|
232
|
+
const rect = proj.rect(element)
|
|
233
|
+
const corners = rectToFaceCorners3D(faceName, rect).map(c => rotatePointAboutCenter(globalMatrix, c))
|
|
234
|
+
return boundingRectOnWorldPlane(corners, worldFaceDir)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function blockRendersSolid(block: { name: string; transparent?: boolean }): boolean {
|
|
238
|
+
if (block.transparent) {
|
|
239
|
+
if (/glass|ice/.test(block.name)) return false
|
|
240
|
+
if (block.name.includes('leaves')) return false
|
|
241
|
+
}
|
|
242
|
+
if (block.name === 'water' || block.name === 'lava') return false
|
|
243
|
+
if (noOcclusionsSet.has(block.name)) return false
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function getBlockFromStateId(version: string, stateId: number) {
|
|
248
|
+
let Block = blockLoaderCache.get(version)
|
|
249
|
+
if (!Block) {
|
|
250
|
+
Block = PrismarineBlockLoader(version)
|
|
251
|
+
blockLoaderCache.set(version, Block)
|
|
252
|
+
}
|
|
253
|
+
return Block.fromStateId(stateId, 1)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function getAllShapesForState(version: string, stateId: number, blockProvider: WorldBlockProvider): Uint16Array[] {
|
|
257
|
+
const cacheKey = `${version}:${stateId}`
|
|
258
|
+
const cached = shapeCache.get(cacheKey)
|
|
259
|
+
if (cached) return cached
|
|
260
|
+
|
|
261
|
+
const shapes = DIR_KEYS.map(() => emptyShape())
|
|
262
|
+
const blockObj = getBlockFromStateId(version, stateId)
|
|
263
|
+
if (!blockObj || !blockRendersSolid(blockObj)) {
|
|
264
|
+
shapeCache.set(cacheKey, shapes)
|
|
265
|
+
return shapes
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const models = blockProvider.getAllResolvedModels0_1({ name: blockObj.name, properties: blockObj.getProperties() }, false) as BlockModelPartsResolved
|
|
269
|
+
|
|
270
|
+
for (const modelVars of models ?? []) {
|
|
271
|
+
const model = modelVars[0]
|
|
272
|
+
if (!model) continue
|
|
273
|
+
if (!isAxisAligned90Rotation(model)) continue
|
|
274
|
+
|
|
275
|
+
const globalMatrix = buildModelGlobalMatrix(model)
|
|
276
|
+
|
|
277
|
+
for (const element of model.elements ?? []) {
|
|
278
|
+
for (const faceName of Object.keys(element.faces) as FaceName[]) {
|
|
279
|
+
const proj = faceProjections[faceName]
|
|
280
|
+
if (!proj || !proj.touches(element)) continue
|
|
281
|
+
|
|
282
|
+
const localDir = elemFaces[faceName].dir as CardinalDir
|
|
283
|
+
const worldDir = roundCardinalDir(matmul3(globalMatrix, localDir))
|
|
284
|
+
const rect = proj.rect(element)
|
|
285
|
+
const corners = rectToFaceCorners3D(faceName, rect).map(c => rotatePointAboutCenter(globalMatrix, c))
|
|
286
|
+
const [u0, v0, u1, v1] = boundingRectOnWorldPlane(corners, worldDir)
|
|
287
|
+
orRectIntoShape(u0, v0, u1, v1, shapes[dirIndex(worldDir)]!)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
shapeCache.set(cacheKey, shapes)
|
|
293
|
+
return shapes
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function getOcclusionShape(version: string, stateId: number, worldDir: CardinalDir, blockProvider: WorldBlockProvider): Uint16Array {
|
|
297
|
+
return getAllShapesForState(version, stateId, blockProvider)[dirIndex(worldDir)]!
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function faceIsCulled(
|
|
301
|
+
version: string,
|
|
302
|
+
currentElement: BlockElement,
|
|
303
|
+
faceName: string,
|
|
304
|
+
neighborStateId: number,
|
|
305
|
+
currentBlock: BlockStateInfo,
|
|
306
|
+
blockProvider: WorldBlockProvider,
|
|
307
|
+
worldFaceDir: CardinalDir,
|
|
308
|
+
globalMatrix: number[][] | null
|
|
309
|
+
): boolean {
|
|
310
|
+
const face = currentElement.faces[faceName]
|
|
311
|
+
if (!face?.cullface) return false
|
|
312
|
+
|
|
313
|
+
if (neighborStateId === currentBlock.stateId && /glass|ice/.test(currentBlock.name)) {
|
|
314
|
+
return true
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!neighborStateId) return false
|
|
318
|
+
|
|
319
|
+
const faceRect = worldFaceRect(currentElement, faceName as FaceName, worldFaceDir, globalMatrix)
|
|
320
|
+
if (!faceRect) return false
|
|
321
|
+
|
|
322
|
+
const neighborShape = getOcclusionShape(version, neighborStateId, oppositeDir(worldFaceDir), blockProvider)
|
|
323
|
+
return rectIsSubsetOfShape(faceRect[0], faceRect[1], faceRect[2], faceRect[3], neighborShape)
|
|
324
|
+
}
|