minecraft-renderer 0.1.79 → 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.
Files changed (200) hide show
  1. package/README.md +29 -25
  2. package/dist/mesher.js +36 -36
  3. package/dist/mesher.js.map +4 -4
  4. package/dist/mesherWasm.js +217 -217
  5. package/dist/minecraft-renderer.js +27 -27
  6. package/dist/minecraft-renderer.js.meta.json +1 -1
  7. package/dist/threeWorker.js +133 -133
  8. package/package.json +5 -2
  9. package/src/bundler/bundlePrepare.ts +3 -8
  10. package/src/graphicsBackend/appViewer.ts +10 -7
  11. package/src/graphicsBackend/config.ts +1 -5
  12. package/src/graphicsBackend/playerState.ts +35 -34
  13. package/src/graphicsBackend/preloadWorkers.ts +43 -41
  14. package/src/graphicsBackend/rendererDefaultOptions.ts +68 -70
  15. package/src/graphicsBackend/rendererOptionsSync.ts +24 -50
  16. package/src/graphicsBackend/types.ts +2 -2
  17. package/src/index.ts +6 -27
  18. package/src/lib/DebugGui.ts +34 -31
  19. package/src/lib/animationController.ts +9 -9
  20. package/src/lib/bakeLegacyLight.ts +1 -6
  21. package/src/lib/bindAbortableListener.test.ts +32 -12
  22. package/src/lib/bindAbortableListener.ts +14 -6
  23. package/src/lib/blockEntityLightRegistry.ts +11 -20
  24. package/src/lib/blockEntityLighting.test.ts +1 -6
  25. package/src/lib/blockEntityLighting.ts +5 -5
  26. package/src/lib/buildSharedConfig.mjs +4 -4
  27. package/src/lib/buildWorkerMcDataIndexes.test.ts +4 -4
  28. package/src/lib/buildWorkerMcDataIndexes.ts +11 -19
  29. package/src/lib/cameraBobbing.ts +1 -1
  30. package/src/lib/canvas2DOverlay.example.ts +13 -33
  31. package/src/lib/canvas2DOverlay.quickstart.ts +24 -54
  32. package/src/lib/canvas2DOverlay.ts +28 -47
  33. package/src/lib/cleanupDecorator.ts +2 -2
  34. package/src/lib/createPlayerObject.test.ts +2 -7
  35. package/src/lib/createPlayerObject.ts +18 -25
  36. package/src/lib/frameTimingCollector.ts +2 -5
  37. package/src/lib/guiRenderer.ts +57 -42
  38. package/src/lib/items.ts +15 -9
  39. package/src/lib/mesherlogReader.ts +5 -6
  40. package/src/lib/simpleUtils.ts +5 -5
  41. package/src/lib/skyLight.ts +3 -3
  42. package/src/lib/smoothSwitcher.ts +11 -18
  43. package/src/lib/ui/newStats.ts +10 -12
  44. package/src/lib/utils/proxy.ts +7 -1
  45. package/src/lib/utils/skins.ts +4 -4
  46. package/src/lib/utils.ts +1 -1
  47. package/src/lib/workerMessageSanitize.ts +5 -12
  48. package/src/lib/workerProxy.ts +52 -42
  49. package/src/lib/workerSyncOps.test.ts +58 -41
  50. package/src/lib/worldrendererCommon.reconfigure.test.ts +24 -21
  51. package/src/lib/worldrendererCommon.removeColumn.test.ts +11 -13
  52. package/src/lib/worldrendererCommon.ts +338 -211
  53. package/src/mesher-legacy/mesher.ts +9 -4
  54. package/src/mesher-legacy/test/a.ts +1 -1
  55. package/src/mesher-legacy/test/mesherTester.ts +2 -2
  56. package/src/mesher-legacy/test/playground.ts +1 -1
  57. package/src/mesher-legacy/test/run/test-js.ts +1 -1
  58. package/src/mesher-legacy/test/snapshotUtils.ts +3 -8
  59. package/src/mesher-legacy/test/test-perf.ts +1 -1
  60. package/src/mesher-legacy/test/tests.test.ts +2 -2
  61. package/src/mesher-shared/blockEntityMetadata.test.ts +7 -5
  62. package/src/mesher-shared/blockEntityMetadata.ts +28 -19
  63. package/src/mesher-shared/blockPropertiesForMeshing.ts +2 -2
  64. package/src/mesher-shared/computeHeightmap.ts +2 -4
  65. package/src/mesher-shared/exportedGeometryTypes.ts +3 -3
  66. package/src/mesher-shared/faceOcclusion.ts +324 -0
  67. package/src/mesher-shared/models.ts +80 -131
  68. package/src/mesher-shared/modelsGeometryCommon.ts +30 -15
  69. package/src/mesher-shared/shared.ts +46 -46
  70. package/src/mesher-shared/standaloneRenderer.ts +40 -44
  71. package/src/mesher-shared/tests/faceOcclusion.test.ts +179 -0
  72. package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +12 -8
  73. package/src/mesher-shared/vertexShading.ts +2 -11
  74. package/src/mesher-shared/world.ts +19 -19
  75. package/src/mesher-shared/worldLighting.test.ts +2 -2
  76. package/src/performanceMonitor/PerformanceMonitor.ts +4 -13
  77. package/src/performanceMonitor/formatPerformanceFactorsDebug.ts +2 -2
  78. package/src/performanceMonitor/index.ts +1 -5
  79. package/src/performanceMonitor/types.ts +1 -1
  80. package/src/playerState/playerState.ts +55 -50
  81. package/src/playerState/types.ts +2 -8
  82. package/src/playground/allEntitiesDebug.ts +19 -16
  83. package/src/playground/baseScene.ts +29 -32
  84. package/src/playground/mobileControls.tsx +133 -129
  85. package/src/playground/playground.html +76 -73
  86. package/src/playground/playground.ts +14 -1
  87. package/src/playground/playgroundUi.tsx +100 -74
  88. package/src/playground/reactUtils.ts +3 -7
  89. package/src/playground/scenes/entities.ts +10 -7
  90. package/src/playground/scenes/frequentUpdates.ts +5 -5
  91. package/src/playground/scenes/geometryExport.ts +6 -20
  92. package/src/playground/scenes/highPosEntityUpdates.ts +2 -10
  93. package/src/playground/scenes/index.ts +1 -1
  94. package/src/playground/scenes/lightingStarfield.ts +18 -12
  95. package/src/playground/scenes/main.ts +15 -11
  96. package/src/playground/scenes/partialBlockCulling.ts +31 -0
  97. package/src/playground/scenes/railsCobweb.ts +1 -1
  98. package/src/playground/scenes/rotationIssue.ts +1 -1
  99. package/src/playground/scenes/slabsOptimization.ts +1 -2
  100. package/src/playground/scenes/transparencyIssue.ts +1 -1
  101. package/src/playground/shared.ts +2 -2
  102. package/src/resourcesManager/resourcesManager.ts +29 -54
  103. package/src/resourcesManager/resourcesManager.worker.test.ts +6 -10
  104. package/src/shims/minecraftData.ts +70 -24
  105. package/src/sign-renderer/index.html +10 -12
  106. package/src/sign-renderer/index.ts +29 -29
  107. package/src/sign-renderer/playground.ts +22 -15
  108. package/src/sign-renderer/tests.test.ts +16 -13
  109. package/src/sign-renderer/vite.config.ts +2 -2
  110. package/src/three/appShared.ts +46 -25
  111. package/src/three/bannerRenderer.ts +33 -61
  112. package/src/three/cameraShake.ts +15 -6
  113. package/src/three/chunkMeshManager.ts +239 -282
  114. package/src/three/cinimaticScript.ts +26 -15
  115. package/src/three/cubeDrawSpans.ts +27 -37
  116. package/src/three/cubeMultiDraw.ts +9 -22
  117. package/src/three/documentRenderer.ts +15 -18
  118. package/src/three/entities.ts +175 -150
  119. package/src/three/entity/EntityMesh.ts +114 -108
  120. package/src/three/entity/animations.js +3 -3
  121. package/src/three/entity/armorModels.ts +15 -15
  122. package/src/three/entity/gltfAnimationUtils.ts +2 -5
  123. package/src/three/fireworks.ts +34 -32
  124. package/src/three/fireworksRenderer.ts +32 -29
  125. package/src/three/globalBlockBuffer.ts +167 -100
  126. package/src/three/globalLegacyBuffer.ts +257 -166
  127. package/src/three/graphicsBackendBase.ts +19 -25
  128. package/src/three/graphicsBackendOffThread.ts +12 -15
  129. package/src/three/hand.ts +21 -21
  130. package/src/three/handLegacy.ts +3 -13
  131. package/src/three/holdingBlock.ts +116 -79
  132. package/src/three/holdingBlockFactory.ts +1 -4
  133. package/src/three/holdingBlockItemIdentity.test.ts +25 -17
  134. package/src/three/holdingBlockItemIdentity.ts +9 -6
  135. package/src/three/holdingBlockLegacy.ts +113 -132
  136. package/src/three/itemMesh.ts +20 -29
  137. package/src/three/legacyMultiDraw.ts +10 -16
  138. package/src/three/legacySectionCull.ts +8 -14
  139. package/src/three/menuBackground/activeView.ts +1 -4
  140. package/src/three/menuBackground/assetUrl.ts +1 -1
  141. package/src/three/menuBackground/classic.ts +4 -12
  142. package/src/three/menuBackground/gpuPreference.ts +1 -3
  143. package/src/three/menuBackground/index.ts +2 -11
  144. package/src/three/menuBackground/renderer.ts +1 -5
  145. package/src/three/menuBackground/types.ts +1 -4
  146. package/src/three/menuBackground/v2.ts +196 -96
  147. package/src/three/menuBackground/v2Meta.ts +3 -3
  148. package/src/three/menuBackground/worldBlocks.ts +37 -35
  149. package/src/three/modules/blockBreakParticles.ts +59 -28
  150. package/src/three/modules/cameraBobbing.ts +3 -6
  151. package/src/three/modules/index.ts +1 -1
  152. package/src/three/modules/rain.ts +10 -23
  153. package/src/three/modules/sciFiWorldReveal.ts +11 -25
  154. package/src/three/modules/starfield.ts +7 -13
  155. package/src/three/renderSlot.ts +28 -16
  156. package/src/three/rendererModuleSystem.ts +1 -3
  157. package/src/three/sceneOrigin.ts +96 -33
  158. package/src/three/sectionRaycastAabb.ts +30 -18
  159. package/src/three/shaderCubeMesh.ts +6 -17
  160. package/src/three/shaders/cubeBlockShader.ts +85 -96
  161. package/src/three/shaders/legacyBlockShader.ts +21 -43
  162. package/src/three/shaders/textureIndexMapping.ts +89 -95
  163. package/src/three/shaders/tintPalette.ts +156 -170
  164. package/src/three/signTextureCache.ts +9 -12
  165. package/src/three/skyboxRenderer.ts +62 -70
  166. package/src/three/tests/chunkMeshManagerLegacy.test.ts +34 -34
  167. package/src/three/tests/cubeDrawSpans.test.ts +55 -25
  168. package/src/three/tests/globalBlockBuffer.test.ts +217 -0
  169. package/src/three/tests/globalLegacyBuffer.test.ts +300 -41
  170. package/src/three/tests/legacyMultiDraw.test.ts +8 -29
  171. package/src/three/tests/pr3PerFrameCpuCuts.test.ts +106 -29
  172. package/src/three/tests/signTextureCache.test.ts +4 -4
  173. package/src/three/threeJsMedia.ts +55 -72
  174. package/src/three/threeJsParticles.ts +32 -41
  175. package/src/three/threeJsSound.ts +3 -3
  176. package/src/three/threeJsUtils.ts +3 -3
  177. package/src/three/threeWorker.ts +2 -2
  178. package/src/three/waypointSprite.ts +55 -82
  179. package/src/three/waypoints.ts +20 -23
  180. package/src/three/world/cursorBlock.ts +25 -12
  181. package/src/three/world/vr.ts +11 -17
  182. package/src/three/worldGeometryExport.ts +15 -34
  183. package/src/three/worldRendererThree.ts +117 -129
  184. package/src/wasm-mesher/bridge/convertChunk.ts +3 -9
  185. package/src/wasm-mesher/bridge/render-from-wasm.ts +204 -229
  186. package/src/wasm-mesher/bridge/shaderCubeBridge.ts +26 -67
  187. package/src/wasm-mesher/tests/cullingRegression.test.ts +307 -0
  188. package/src/wasm-mesher/tests/heightmapParity.test.ts +8 -18
  189. package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +2 -2
  190. package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +17 -4
  191. package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +103 -141
  192. package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +14 -22
  193. package/src/wasm-mesher/worker/mesherWasm.ts +150 -141
  194. package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +2 -8
  195. package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +1 -3
  196. package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +1 -5
  197. package/src/wasm-mesher/worker/mesherWasmRequestTracker.ts +42 -42
  198. package/src/worldView/worldView.spiral.test.ts +1 -1
  199. package/src/worldView/worldView.ts +34 -39
  200. 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 ??= (value) => JSON.parse(JSON.stringify(value))
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) => { parentPort.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(messages.map(m => m.data), messages.flatMap(m => m.transferList) as unknown as string)
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
  }
@@ -1,4 +1,4 @@
1
1
  //@ts-nocheck
2
- import {MaxRectsPacker } from 'maxrects-packer'
2
+ import { MaxRectsPacker } from 'maxrects-packer'
3
3
 
4
4
  console.log('test', MaxRectsPacker)
@@ -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 }) => ([face, (light ?? 0) * 15 - 2])))
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 @@ const addPositions = [
8
8
  [[0, 1, 0], 'stone'],
9
9
  [[0, -1, 0], 'stone'],
10
10
  [[0, 0, 1], 'stone'],
11
- [[0, 0, -1], 'stone'],
11
+ [[0, 0, -1], 'stone']
12
12
  ] as const
13
13
 
14
14
  const { mesherWorld, getGeometry, pos, mcData } = setup('1.21.1', addPositions as any)
@@ -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 = snapshotPath.startsWith('/') || snapshotPath.includes(':')
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 = (x % 3 === 0) && (z % 3 === 0) && (y % 3 === 0)
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, 2, 3,
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 { isWall: boolean; isHanging: boolean; rotation: number }
5
- export interface HeadMeta { isWall: boolean; rotation: number }
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, sky: 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, y: number, z: 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
- 'north': 2,
42
- 'south': 0,
43
- 'west': 1,
44
- 'east': 3
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
- 'north': 0,
58
- 'south': 2,
59
- 'west': 3,
60
- 'east': 1
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
- 'north': 2,
72
- 'south': 0,
73
- 'west': 1,
74
- 'east': 3
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] : (props.rotation === undefined ? 0 : +props.rotation),
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, properties: Record<string, unknown> } {
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 ? (neighbor && !neighbor.transparent) : false
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, heightmap: Int16Array } {
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, y: number, z: 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, y: number, z: number }
37
- rotation: { pitch: number, yaw: 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
+ }