minecraft-renderer 0.1.79 → 0.1.81

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 (205) hide show
  1. package/README.md +66 -26
  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 +66 -66
  6. package/dist/minecraft-renderer.js.meta.json +1 -1
  7. package/dist/threeWorker.js +450 -450
  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 +349 -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/cameraCollisionBlockCache.ts +156 -0
  113. package/src/three/cameraCollisionSolidity.ts +50 -0
  114. package/src/three/cameraShake.ts +15 -6
  115. package/src/three/chunkMeshManager.ts +223 -390
  116. package/src/three/cinimaticScript.ts +26 -15
  117. package/src/three/cubeDrawSpans.ts +27 -37
  118. package/src/three/cubeMultiDraw.ts +9 -22
  119. package/src/three/documentRenderer.ts +15 -18
  120. package/src/three/entities.ts +175 -150
  121. package/src/three/entity/EntityMesh.ts +114 -108
  122. package/src/three/entity/animations.js +3 -3
  123. package/src/three/entity/armorModels.ts +15 -15
  124. package/src/three/entity/gltfAnimationUtils.ts +2 -5
  125. package/src/three/fireworks.ts +34 -32
  126. package/src/three/fireworksRenderer.ts +32 -29
  127. package/src/three/globalBlockBuffer.ts +167 -100
  128. package/src/three/globalLegacyBuffer.ts +248 -264
  129. package/src/three/graphicsBackendBase.ts +19 -25
  130. package/src/three/graphicsBackendOffThread.ts +12 -15
  131. package/src/three/hand.ts +21 -21
  132. package/src/three/handLegacy.ts +3 -13
  133. package/src/three/holdingBlock.ts +116 -79
  134. package/src/three/holdingBlockFactory.ts +1 -4
  135. package/src/three/holdingBlockItemIdentity.test.ts +25 -17
  136. package/src/three/holdingBlockItemIdentity.ts +9 -6
  137. package/src/three/holdingBlockLegacy.ts +113 -132
  138. package/src/three/itemMesh.ts +20 -29
  139. package/src/three/legacyMultiDraw.ts +10 -16
  140. package/src/three/legacySectionCull.ts +8 -14
  141. package/src/three/menuBackground/activeView.ts +1 -4
  142. package/src/three/menuBackground/assetUrl.ts +1 -1
  143. package/src/three/menuBackground/classic.ts +4 -12
  144. package/src/three/menuBackground/gpuPreference.ts +1 -3
  145. package/src/three/menuBackground/index.ts +2 -11
  146. package/src/three/menuBackground/renderer.ts +1 -5
  147. package/src/three/menuBackground/types.ts +1 -4
  148. package/src/three/menuBackground/v2.ts +196 -96
  149. package/src/three/menuBackground/v2Meta.ts +3 -3
  150. package/src/three/menuBackground/worldBlocks.ts +37 -35
  151. package/src/three/modules/blockBreakParticles.ts +59 -28
  152. package/src/three/modules/cameraBobbing.ts +3 -6
  153. package/src/three/modules/index.ts +1 -1
  154. package/src/three/modules/rain.ts +10 -23
  155. package/src/three/modules/sciFiWorldReveal.ts +11 -25
  156. package/src/three/modules/starfield.ts +7 -13
  157. package/src/three/renderSlot.ts +28 -16
  158. package/src/three/rendererModuleSystem.ts +1 -3
  159. package/src/three/sceneOrigin.ts +96 -33
  160. package/src/three/sectionRaycastAabb.ts +24 -110
  161. package/src/three/shaderCubeMesh.ts +6 -17
  162. package/src/three/shaders/cubeBlockShader.ts +85 -96
  163. package/src/three/shaders/legacyBlockShader.ts +21 -43
  164. package/src/three/shaders/textureIndexMapping.ts +89 -95
  165. package/src/three/shaders/tintPalette.ts +156 -170
  166. package/src/three/signTextureCache.ts +9 -12
  167. package/src/three/skyboxRenderer.ts +62 -70
  168. package/src/three/tests/chunkMeshManagerLegacy.test.ts +34 -64
  169. package/src/three/tests/cubeDrawSpans.test.ts +55 -25
  170. package/src/three/tests/globalBlockBuffer.test.ts +217 -0
  171. package/src/three/tests/globalLegacyBuffer.test.ts +300 -41
  172. package/src/three/tests/legacyMultiDraw.test.ts +8 -29
  173. package/src/three/tests/pr3PerFrameCpuCuts.test.ts +106 -29
  174. package/src/three/tests/signTextureCache.test.ts +4 -4
  175. package/src/three/thirdPersonVoxelRaycast.ts +120 -0
  176. package/src/three/threeJsMedia.ts +55 -72
  177. package/src/three/threeJsParticles.ts +32 -41
  178. package/src/three/threeJsSound.ts +3 -3
  179. package/src/three/threeJsUtils.ts +3 -3
  180. package/src/three/threeWorker.ts +2 -2
  181. package/src/three/waypointSprite.ts +55 -82
  182. package/src/three/waypoints.ts +20 -23
  183. package/src/three/world/cursorBlock.ts +25 -12
  184. package/src/three/world/vr.ts +11 -17
  185. package/src/three/worldGeometryExport.ts +15 -34
  186. package/src/three/worldRendererThree.ts +144 -166
  187. package/src/wasm-mesher/bridge/convertChunk.ts +3 -9
  188. package/src/wasm-mesher/bridge/render-from-wasm.ts +204 -229
  189. package/src/wasm-mesher/bridge/shaderCubeBridge.ts +26 -67
  190. package/src/wasm-mesher/tests/cameraCollisionBlockCache.test.ts +82 -0
  191. package/src/wasm-mesher/tests/cullingRegression.test.ts +307 -0
  192. package/src/wasm-mesher/tests/heightmapParity.test.ts +8 -18
  193. package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +2 -2
  194. package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +1 -93
  195. package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +103 -141
  196. package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +14 -22
  197. package/src/wasm-mesher/tests/thirdPersonVoxelRaycast.test.ts +63 -0
  198. package/src/wasm-mesher/worker/mesherWasm.ts +150 -141
  199. package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +2 -8
  200. package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +1 -3
  201. package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +1 -5
  202. package/src/wasm-mesher/worker/mesherWasmRequestTracker.ts +42 -42
  203. package/src/worldView/worldView.spiral.test.ts +1 -1
  204. package/src/worldView/worldView.ts +34 -39
  205. package/src/worldView/worldViewWorkerBridge.test.ts +4 -4
@@ -5,31 +5,38 @@ import { renderSign } from '.'
5
5
  const PrismarineChat = PrismarineChatLoader({ language: {} } as any)
6
6
 
7
7
  const img = new Image()
8
- img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAYAAAB4MH11AAABbElEQVR4AY3BQY6cMBBA0Q+yQZZVi+ndcJVcKGfMgegdvShKVtuokzGSWwwiUd7rfv388Vst0UgMXCobmgsSA5VaQmKgUks0EgNHji8SA9W8GJCQwVNpLhzJ4KFs4B1HEgPVvBiQkMFTaS44tYTEQDXdIkfiHbuyobmguaDPFzIWGrWExEA13SJH4h1uzS/WbPyvroM1v6jWbFRrNv7GfX5EdmXjzTvUEjJ4zjQXjiQGdmXjzTvUEjJ4HF/UEt/kQqW5UEkMzIshY08jg6dRS3yTC5XmgpsXY7pFztQSEgPNJCNv3lGpJVSfTLfImVpCYsB1HdwfxpU1G9eeNF0H94dxZc2G+/yI7MoG3vEv82LI2NNIDLyVDbzjzFE2mnkxZOy5IoNnkpFGc2FXNpp5MWTsOXJ4h1qikrGnkhjYlY1m1icy9lQSA+TCzjvUEpWMPZXEwK5suPvDOFuzcdZ1sOYX1ZqNas3GlTUbzR+jQbEAcs8ZQAAAAABJRU5ErkJggg=='
8
+ img.src =
9
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAMCAYAAAB4MH11AAABbElEQVR4AY3BQY6cMBBA0Q+yQZZVi+ndcJVcKGfMgegdvShKVtuokzGSWwwiUd7rfv388Vst0UgMXCobmgsSA5VaQmKgUks0EgNHji8SA9W8GJCQwVNpLhzJ4KFs4B1HEgPVvBiQkMFTaS44tYTEQDXdIkfiHbuyobmguaDPFzIWGrWExEA13SJH4h1uzS/WbPyvroM1v6jWbFRrNv7GfX5EdmXjzTvUEjJ4zjQXjiQGdmXjzTvUEjJ4HF/UEt/kQqW5UEkMzIshY08jg6dRS3yTC5XmgpsXY7pFztQSEgPNJCNv3lGpJVSfTLfImVpCYsB1HdwfxpU1G9eeNF0H94dxZc2G+/yI7MoG3vEv82LI2NNIDLyVDbzjzFE2mnkxZOy5IoNnkpFGc2FXNpp5MWTsOXJ4h1qikrGnkhjYlY1m1icy9lQSA+TCzjvUEpWMPZXEwK5suPvDOFuzcdZ1sOYX1ZqNas3GlTUbzR+jQbEAcs8ZQAAAAABJRU5ErkJggg=='
9
10
 
10
11
  await new Promise<void>(resolve => {
11
12
  img.onload = () => resolve()
12
13
  })
13
14
 
14
15
  const blockEntity = {
15
- 'GlowingText': 0,
16
- 'Color': 'black',
17
- 'Text4': '{"text":""}',
18
- 'Text3': '{"text":""}',
19
- 'Text2': '{"text":""}',
20
- 'Text1': '{"extra":[{"color":"dark_green","text":"Minecraft "},{"text":"Tools"}],"text":""}'
16
+ GlowingText: 0,
17
+ Color: 'black',
18
+ Text4: '{"text":""}',
19
+ Text3: '{"text":""}',
20
+ Text2: '{"text":""}',
21
+ Text1: '{"extra":[{"color":"dark_green","text":"Minecraft "},{"text":"Tools"}],"text":""}'
21
22
  } as const
22
23
 
23
24
  await document.fonts.load('1em mojangles')
24
25
 
25
- const canvas = renderSign(blockEntity, false, PrismarineChat, (ctx) => {
26
- ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height)
27
- }, (width, height) => {
28
- const canvas = document.createElement('canvas')
29
- canvas.width = width
30
- canvas.height = height
31
- return canvas as unknown as OffscreenCanvas
32
- }) as unknown as HTMLCanvasElement
26
+ const canvas = renderSign(
27
+ blockEntity,
28
+ false,
29
+ PrismarineChat,
30
+ ctx => {
31
+ ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height)
32
+ },
33
+ (width, height) => {
34
+ const canvas = document.createElement('canvas')
35
+ canvas.width = width
36
+ canvas.height = height
37
+ return canvas as unknown as OffscreenCanvas
38
+ }
39
+ ) as unknown as HTMLCanvasElement
33
40
 
34
41
  if (canvas) {
35
42
  canvas.style.imageRendering = 'pixelated'
@@ -14,14 +14,16 @@ globalThis.document = {
14
14
  fillText(text, x, y) {
15
15
  ctxTexts.push({ text, x, y })
16
16
  },
17
- measureText() { return 0 }
17
+ measureText() {
18
+ return 0
19
+ }
18
20
  }
19
21
  }
20
22
  }
21
23
  }
22
24
  } as any
23
25
 
24
- const render = (entity) => {
26
+ const render = entity => {
25
27
  ctxTexts = []
26
28
  renderSign(entity, true, PrismarineChat)
27
29
  return ctxTexts.map(({ text, y }) => [y / 64, text])
@@ -29,12 +31,12 @@ const render = (entity) => {
29
31
 
30
32
  test('sign renderer', () => {
31
33
  let blockEntity = {
32
- 'GlowingText': 0,
33
- 'Color': 'black',
34
- 'Text4': '{"text":""}',
35
- 'Text3': '{"text":""}',
36
- 'Text2': '{"text":""}',
37
- 'Text1': '{"extra":[{"color":"dark_green","text":"Minecraft "},{"text":"Tools"}],"text":""}'
34
+ GlowingText: 0,
35
+ Color: 'black',
36
+ Text4: '{"text":""}',
37
+ Text3: '{"text":""}',
38
+ Text2: '{"text":""}',
39
+ Text1: '{"extra":[{"color":"dark_green","text":"Minecraft "},{"text":"Tools"}],"text":""}'
38
40
  } as any
39
41
  expect(render(blockEntity)).toMatchInlineSnapshot(`
40
42
  [
@@ -49,11 +51,12 @@ test('sign renderer', () => {
49
51
  ]
50
52
  `)
51
53
 
52
- blockEntity = { // pre flatenning
53
- 'Text1': 'Welcome to',
54
- 'Text2': '',
55
- 'Text3': 'null',
56
- 'Text4': '"Version 2.1"',
54
+ blockEntity = {
55
+ // pre flatenning
56
+ Text1: 'Welcome to',
57
+ Text2: '',
58
+ Text3: 'null',
59
+ Text4: '"Version 2.1"'
57
60
  } as const
58
61
  expect(render(blockEntity)).toMatchInlineSnapshot(`
59
62
  [
@@ -6,6 +6,6 @@ export default defineConfig({
6
6
  alias: {
7
7
  'prismarine-registry': './noop.js',
8
8
  'prismarine-nbt': './noop.js'
9
- },
10
- },
9
+ }
10
+ }
11
11
  })
@@ -6,37 +6,55 @@ import { GeneralInputItem, getItemModelName } from '../lib/items'
6
6
  import { ResourcesManagerTransferred } from '../resourcesManager'
7
7
  import { renderSlot } from './renderSlot'
8
8
 
9
- export const getItemUv = (item: Record<string, any>, specificProps: ItemSpecificContextProperties, resourcesManager: ResourcesManagerTransferred, playerState: PlayerStateRenderer): {
10
- u: number
11
- v: number
12
- su: number
13
- sv: number
14
- renderInfo?: ReturnType<typeof renderSlot>
15
- // texture: ImageBitmap
16
- modelName: string
17
- } | {
18
- resolvedModel: BlockModel
19
- modelName: string
20
- } => {
9
+ export const getItemUv = (
10
+ item: Record<string, any>,
11
+ specificProps: ItemSpecificContextProperties,
12
+ resourcesManager: ResourcesManagerTransferred,
13
+ playerState: PlayerStateRenderer
14
+ ):
15
+ | {
16
+ u: number
17
+ v: number
18
+ su: number
19
+ sv: number
20
+ renderInfo?: ReturnType<typeof renderSlot>
21
+ // texture: ImageBitmap
22
+ modelName: string
23
+ }
24
+ | {
25
+ resolvedModel: BlockModel
26
+ modelName: string
27
+ } => {
21
28
  const resources = resourcesManager.currentResources
22
29
  if (!resources) throw new Error('Resources not loaded')
23
30
  const idOrName = item.itemId ?? item.blockId ?? item.name
24
31
  const { blockState } = item
25
32
  try {
26
- const name =
27
- blockState
28
- ? globalThis.loadedData.blocksByStateId[blockState]?.name
29
- : typeof idOrName === 'number' ? globalThis.loadedData.items[idOrName]?.name : idOrName
33
+ const name = blockState
34
+ ? globalThis.loadedData.blocksByStateId[blockState]?.name
35
+ : typeof idOrName === 'number'
36
+ ? globalThis.loadedData.items[idOrName]?.name
37
+ : idOrName
30
38
  if (!name) throw new Error(`Item not found: ${idOrName}`)
31
39
 
32
- const model = getItemModelName({
33
- ...item,
34
- name,
35
- } as GeneralInputItem, specificProps, resourcesManager, playerState)
40
+ const model = getItemModelName(
41
+ {
42
+ ...item,
43
+ name
44
+ } as GeneralInputItem,
45
+ specificProps,
46
+ resourcesManager,
47
+ playerState
48
+ )
36
49
 
37
- const renderInfo = renderSlot({
38
- modelName: model,
39
- }, resourcesManager, false, true)
50
+ const renderInfo = renderSlot(
51
+ {
52
+ modelName: model
53
+ },
54
+ resourcesManager,
55
+ false,
56
+ true
57
+ )
40
58
 
41
59
  if (!renderInfo) throw new Error(`Failed to get render info for item ${name}`)
42
60
 
@@ -51,9 +69,12 @@ export const getItemUv = (item: Record<string, any>, specificProps: ItemSpecific
51
69
  if (renderInfo.slice) {
52
70
  // Get slice coordinates from either block or item texture
53
71
  const [x, y, w, h] = renderInfo.slice
54
- const [u, v, su, sv] = [x / img.width, y / img.height, (w / img.width), (h / img.height)]
72
+ const [u, v, su, sv] = [x / img.width, y / img.height, w / img.width, h / img.height]
55
73
  return {
56
- u, v, su, sv,
74
+ u,
75
+ v,
76
+ su,
77
+ sv,
57
78
  renderInfo,
58
79
  // texture: img,
59
80
  modelName: renderInfo.modelName!
@@ -18,22 +18,22 @@ const BANNER_HEIGHT = 40
18
18
 
19
19
  // Map banner block names to base color IDs
20
20
  const BANNER_NAME_TO_COLOR: Record<string, number> = {
21
- 'white_banner': 15,
22
- 'orange_banner': 14,
23
- 'magenta_banner': 13,
24
- 'light_blue_banner': 12,
25
- 'yellow_banner': 11,
26
- 'lime_banner': 10,
27
- 'pink_banner': 9,
28
- 'gray_banner': 8,
29
- 'light_gray_banner': 7,
30
- 'cyan_banner': 6,
31
- 'purple_banner': 5,
32
- 'blue_banner': 4,
33
- 'brown_banner': 3,
34
- 'green_banner': 2,
35
- 'red_banner': 1,
36
- 'black_banner': 0,
21
+ white_banner: 15,
22
+ orange_banner: 14,
23
+ magenta_banner: 13,
24
+ light_blue_banner: 12,
25
+ yellow_banner: 11,
26
+ lime_banner: 10,
27
+ pink_banner: 9,
28
+ gray_banner: 8,
29
+ light_gray_banner: 7,
30
+ cyan_banner: 6,
31
+ purple_banner: 5,
32
+ blue_banner: 4,
33
+ brown_banner: 3,
34
+ green_banner: 2,
35
+ red_banner: 1,
36
+ black_banner: 0
37
37
  }
38
38
 
39
39
  // Basic Minecraft banner colors (DyeColor enum values)
@@ -53,7 +53,7 @@ const BANNER_COLORS: Record<number, string> = {
53
53
  12: '#3ab3da', // light_blue
54
54
  13: '#c74ebd', // magenta
55
55
  14: '#f9801d', // orange
56
- 15: '#f9fffe', // white
56
+ 15: '#f9fffe' // white
57
57
  }
58
58
 
59
59
  // Extract base color from banner block name
@@ -64,15 +64,7 @@ function getBannerBaseColor(blockName: string): number {
64
64
  }
65
65
 
66
66
  // Basic pattern rendering (simplified - just solid colors for now)
67
- const renderPattern = (
68
- ctx: OffscreenCanvasRenderingContext2D,
69
- pattern: string,
70
- color: string,
71
- x: number,
72
- y: number,
73
- width: number,
74
- height: number
75
- ) => {
67
+ const renderPattern = (ctx: OffscreenCanvasRenderingContext2D, pattern: string, color: string, x: number, y: number, width: number, height: number) => {
76
68
  ctx.fillStyle = color
77
69
  // For now, just render basic patterns as solid colors
78
70
  // TODO: Implement actual pattern shapes (stripes, crosses, etc.)
@@ -84,19 +76,19 @@ const renderPattern = (
84
76
  ctx.fillRect(x, y, width / 3, height)
85
77
  break
86
78
  case 'rs': // Right stripe
87
- ctx.fillRect(x + (width * 2 / 3), y, width / 3, height)
79
+ ctx.fillRect(x + (width * 2) / 3, y, width / 3, height)
88
80
  break
89
81
  case 'ts': // Top stripe
90
82
  ctx.fillRect(x, y, width, height / 3)
91
83
  break
92
84
  case 'ms': // Middle stripe
93
- ctx.fillRect(x, y + (height / 3), width, height / 3)
85
+ ctx.fillRect(x, y + height / 3, width, height / 3)
94
86
  break
95
87
  case 'drs': // Down-right stripe
96
88
  ctx.fillRect(x, y, width / 2, height / 2)
97
89
  break
98
90
  case 'dls': // Down-left stripe
99
- ctx.fillRect(x + (width / 2), y, width / 2, height / 2)
91
+ ctx.fillRect(x + width / 2, y, width / 2, height / 2)
100
92
  break
101
93
  case 'ss': // Small stripes
102
94
  for (let i = 0; i < width; i += 2) {
@@ -104,12 +96,12 @@ const renderPattern = (
104
96
  }
105
97
  break
106
98
  case 'cr': // Cross
107
- ctx.fillRect(x, y + (height / 3), width, height / 3)
108
- ctx.fillRect(x + (width / 3), y, width / 3, height)
99
+ ctx.fillRect(x, y + height / 3, width, height / 3)
100
+ ctx.fillRect(x + width / 3, y, width / 3, height)
109
101
  break
110
102
  case 'sc': // Straight cross
111
- ctx.fillRect(x, y + (height / 2) - 1, width, 2)
112
- ctx.fillRect(x + (width / 2) - 1, y, 2, height)
103
+ ctx.fillRect(x, y + height / 2 - 1, width, 2)
104
+ ctx.fillRect(x + width / 2 - 1, y, 2, height)
113
105
  break
114
106
  default:
115
107
  // Default: fill entire area
@@ -118,7 +110,7 @@ const renderPattern = (
118
110
  }
119
111
 
120
112
  // Create a cache key from banner content (base color + patterns)
121
- function createBannerCacheKey(baseColor: number, patterns: Array<{ Color?: number, Pattern?: string }> | undefined): string {
113
+ function createBannerCacheKey(baseColor: number, patterns: Array<{ Color?: number; Pattern?: string }> | undefined): string {
122
114
  if (!patterns || patterns.length === 0) {
123
115
  return `banner_${baseColor}_empty`
124
116
  }
@@ -159,30 +151,17 @@ export const renderBanner = (
159
151
  const color = BANNER_COLORS[colorId] || BANNER_COLORS[0]
160
152
 
161
153
  // Render each pattern on top of previous ones
162
- renderPattern(
163
- ctx,
164
- pattern,
165
- color,
166
- 0,
167
- 0,
168
- BANNER_WIDTH * scale,
169
- BANNER_HEIGHT * scale
170
- )
154
+ renderPattern(ctx, pattern, color, 0, 0, BANNER_WIDTH * scale, BANNER_HEIGHT * scale)
171
155
  }
172
156
  }
173
157
 
174
158
  return canvas
175
159
  }
176
160
 
177
-
178
161
  // Banner texture cache with reference counting
179
- const bannerTextureCache = new Map<string, { texture: THREE.Texture, refCount: number }>()
162
+ const bannerTextureCache = new Map<string, { texture: THREE.Texture; refCount: number }>()
180
163
 
181
- export function getBannerTexture(
182
- worldRenderer: WorldRendererThree,
183
- blockName: string,
184
- blockEntity: any
185
- ): THREE.Texture | undefined {
164
+ export function getBannerTexture(worldRenderer: WorldRendererThree, blockName: string, blockEntity: any): THREE.Texture | undefined {
186
165
  // Extract base color from block name
187
166
  const baseColor = getBannerBaseColor(blockName)
188
167
 
@@ -232,8 +211,8 @@ export function createBannerMesh(
232
211
  texture: THREE.Texture,
233
212
  blockLightNorm = 0,
234
213
  skyLightNorm = 1,
235
- skyLevel = 1,
236
- ): THREE.Group & { bannerTexture?: THREE.Texture, bannerMaterial?: THREE.MeshBasicMaterial } {
214
+ skyLevel = 1
215
+ ): THREE.Group & { bannerTexture?: THREE.Texture; bannerMaterial?: THREE.MeshBasicMaterial } {
237
216
  const bannerWidth = 13.6 / 16
238
217
  const bannerHeight = 28 / 16
239
218
  const clothXOffset = 0
@@ -256,10 +235,7 @@ export function createBannerMesh(
256
235
 
257
236
  const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true })
258
237
  tintBannerMaterial(material, blockLightNorm, skyLightNorm, skyLevel)
259
- const mesh = new THREE.Mesh(
260
- new THREE.PlaneGeometry(bannerWidth, bannerHeight),
261
- material,
262
- )
238
+ const mesh = new THREE.Mesh(new THREE.PlaneGeometry(bannerWidth, bannerHeight), material)
263
239
  mesh.renderOrder = 999
264
240
 
265
241
  const thickness = 0.5 / 16
@@ -274,11 +250,7 @@ export function createBannerMesh(
274
250
  bannerTexture?: THREE.Texture
275
251
  bannerMaterial?: THREE.MeshBasicMaterial
276
252
  }
277
- group.rotation.set(
278
- 0,
279
- -THREE.MathUtils.degToRad(rotation * (isWall ? 90 : 45 / 2)),
280
- 0
281
- )
253
+ group.rotation.set(0, -THREE.MathUtils.degToRad(rotation * (isWall ? 90 : 45 / 2)), 0)
282
254
  group.add(mesh)
283
255
  group.bannerTexture = texture
284
256
  group.bannerMaterial = material
@@ -0,0 +1,156 @@
1
+ //@ts-nocheck
2
+ import Chunks from 'prismarine-chunk'
3
+ import { Vec3 } from 'vec3'
4
+ import { getCameraCollisionSolidityTable, isStateIdSolidForCameraCollision } from './cameraCollisionSolidity'
5
+
6
+ /** 16³ blocks per section; 1 bit per block. */
7
+ const WORDS_PER_SECTION = 128
8
+ export const CAMERA_COLLISION_BYTES_PER_SECTION = WORDS_PER_SECTION * 4
9
+
10
+ type SectionBitset = Uint32Array
11
+
12
+ const sectionKey = (sx: number, sy: number, sz: number) => `${sx},${sy},${sz}`
13
+
14
+ const alignSection = (coord: number) => Math.floor(coord / 16) * 16
15
+
16
+ const localCoord = (coord: number) => ((coord % 16) + 16) % 16
17
+
18
+ const blockBitIndex = (lx: number, ly: number, lz: number) => lx + lz * 16 + ly * 256
19
+
20
+ const getBit = (bits: SectionBitset, idx: number) => {
21
+ const word = idx >>> 5
22
+ return (bits[word]! & (1 << (idx & 31))) !== 0
23
+ }
24
+
25
+ const setBit = (bits: SectionBitset, idx: number, value: boolean) => {
26
+ const word = idx >>> 5
27
+ const mask = 1 << (idx & 31)
28
+ if (value) bits[word]! |= mask
29
+ else bits[word]! &= ~mask
30
+ }
31
+
32
+ const isBitsetEmpty = (bits: SectionBitset) => {
33
+ for (let i = 0; i < WORDS_PER_SECTION; i++) {
34
+ if (bits[i]!) return false
35
+ }
36
+ return true
37
+ }
38
+
39
+ /**
40
+ * Sparse 1-bit-per-block solidity cache for third-person voxel DDA.
41
+ * Allocates a 512-byte section bitset only when it contains ≥1 solid block.
42
+ */
43
+ export class CameraCollisionBlockCache {
44
+ private readonly sections = new Map<string, SectionBitset>()
45
+ private Chunk: ReturnType<typeof Chunks>
46
+ private solidityTable: Uint8Array
47
+ private worldMinY = 0
48
+ private worldMaxY = 256
49
+
50
+ constructor(version: string) {
51
+ this.Chunk = Chunks(version) as ReturnType<typeof Chunks>
52
+ this.solidityTable = getCameraCollisionSolidityTable(version)
53
+ }
54
+
55
+ setVersion(version: string) {
56
+ this.Chunk = Chunks(version) as ReturnType<typeof Chunks>
57
+ this.solidityTable = getCameraCollisionSolidityTable(version)
58
+ this.sections.clear()
59
+ }
60
+
61
+ setWorldBounds(minY: number, worldHeight: number) {
62
+ if (this.worldMinY === minY && this.worldMaxY === worldHeight) return
63
+ this.worldMinY = minY
64
+ this.worldMaxY = worldHeight
65
+ this.sections.clear()
66
+ }
67
+
68
+ clear() {
69
+ this.sections.clear()
70
+ }
71
+
72
+ /** Allocated 16³ section bitsets (512 bytes each). */
73
+ getAllocatedSectionCount(): number {
74
+ return this.sections.size
75
+ }
76
+
77
+ getAllocatedBytes(): number {
78
+ return this.sections.size * CAMERA_COLLISION_BYTES_PER_SECTION
79
+ }
80
+
81
+ removeColumn(chunkX: number, chunkZ: number) {
82
+ const syStart = alignSection(this.worldMinY)
83
+ for (let sy = syStart; sy < this.worldMaxY; sy += 16) {
84
+ this.sections.delete(sectionKey(chunkX, sy, chunkZ))
85
+ }
86
+ }
87
+
88
+ ingestColumn(chunkX: number, chunkZ: number, chunkJson: unknown) {
89
+ const chunk = this.Chunk.fromJson(chunkJson as Parameters<ReturnType<typeof Chunks>['fromJson']>[0]) as unknown as {
90
+ getBlockStateId: (pos: Vec3) => number
91
+ }
92
+ const pos = new Vec3(0, 0, 0)
93
+
94
+ for (let y = this.worldMinY; y < this.worldMaxY; y++) {
95
+ pos.y = y
96
+ const sy = alignSection(y)
97
+ const ly = y - sy
98
+ for (let lz = 0; lz < 16; lz++) {
99
+ pos.z = lz
100
+ const wz = chunkZ + lz
101
+ const sz = alignSection(wz)
102
+ const lzLocal = wz - sz
103
+ for (let lx = 0; lx < 16; lx++) {
104
+ pos.x = lx
105
+ const stateId = chunk.getBlockStateId(pos) || 0
106
+ if (!isStateIdSolidForCameraCollision(stateId, this.solidityTable)) continue
107
+
108
+ const wx = chunkX + lx
109
+ const sx = alignSection(wx)
110
+ const key = sectionKey(sx, sy, sz)
111
+ let bits = this.sections.get(key)
112
+ if (!bits) {
113
+ bits = new Uint32Array(WORDS_PER_SECTION)
114
+ this.sections.set(key, bits)
115
+ }
116
+ setBit(bits, blockBitIndex(wx - sx, ly, lzLocal), true)
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ setBlockStateId(x: number, y: number, z: number, stateId: number) {
123
+ if (y < this.worldMinY || y >= this.worldMaxY) return
124
+
125
+ const sx = alignSection(x)
126
+ const sy = alignSection(y)
127
+ const sz = alignSection(z)
128
+ const key = sectionKey(sx, sy, sz)
129
+ const idx = blockBitIndex(localCoord(x), y - sy, localCoord(z))
130
+ const solid = isStateIdSolidForCameraCollision(stateId, this.solidityTable)
131
+
132
+ if (solid) {
133
+ let bits = this.sections.get(key)
134
+ if (!bits) {
135
+ bits = new Uint32Array(WORDS_PER_SECTION)
136
+ this.sections.set(key, bits)
137
+ }
138
+ setBit(bits, idx, true)
139
+ return
140
+ }
141
+
142
+ const bits = this.sections.get(key)
143
+ if (!bits) return
144
+ setBit(bits, idx, false)
145
+ if (isBitsetEmpty(bits)) this.sections.delete(key)
146
+ }
147
+
148
+ isSolidBlock(x: number, y: number, z: number): boolean {
149
+ if (y < this.worldMinY || y >= this.worldMaxY) return false
150
+
151
+ const bits = this.sections.get(sectionKey(alignSection(x), alignSection(y), alignSection(z)))
152
+ if (!bits) return false
153
+
154
+ return getBit(bits, blockBitIndex(localCoord(x), y - alignSection(y), localCoord(z)))
155
+ }
156
+ }
@@ -0,0 +1,50 @@
1
+ //@ts-nocheck
2
+ import MinecraftData from 'minecraft-data'
3
+ import PrismarineBlockLoader from 'prismarine-block'
4
+ import moreBlockDataGeneratedJson from '../lib/moreBlockDataGenerated.json'
5
+
6
+ const solidityCache = new Map<string, Uint8Array>()
7
+
8
+ const PASS_THROUGH_NAMES = new Set(['water', 'flowing_water', 'lava', 'flowing_lava', 'grass', 'short_grass', 'tall_grass'])
9
+
10
+ /** 1 = blocks third-person camera DDA; 0 = pass-through (air, plants, fluids). */
11
+ export function getCameraCollisionSolidityTable(version: string): Uint8Array {
12
+ const cached = solidityCache.get(version)
13
+ if (cached) return cached
14
+
15
+ const mcData = MinecraftData(version)
16
+ const Block = PrismarineBlockLoader(version)
17
+ const noOcclusionsSet = new Set(Object.keys(moreBlockDataGeneratedJson.noOcclusions))
18
+ const invisibleNames = new Set(mcData.blocksArray.filter(b => moreBlockDataGeneratedJson.invisibleBlocks[b.name]).map(b => b.name))
19
+
20
+ let maxStateId = 0
21
+ for (const idStr of Object.keys((mcData as { blocksByStateId: Record<string, unknown> }).blocksByStateId)) {
22
+ maxStateId = Math.max(maxStateId, Number(idStr))
23
+ }
24
+
25
+ const table = new Uint8Array(maxStateId + 1)
26
+ for (const idStr of Object.keys((mcData as { blocksByStateId: Record<string, unknown> }).blocksByStateId)) {
27
+ const stateId = Number(idStr)
28
+ if (!stateId) continue
29
+
30
+ const block = (
31
+ Block as { fromStateId: (id: number, y: number) => { name: string; transparent: boolean; boundingBox: string; shapes?: number[][] } | null }
32
+ ).fromStateId(stateId, 0)
33
+ if (!block) continue
34
+ if (invisibleNames.has(block.name)) continue
35
+ if (noOcclusionsSet.has(block.name)) continue
36
+ if (PASS_THROUGH_NAMES.has(block.name)) continue
37
+ if (block.boundingBox === 'empty') continue
38
+ if (block.transparent && block.boundingBox !== 'block') continue
39
+ if (!block.shapes || block.shapes.length === 0) continue
40
+
41
+ table[stateId] = 1
42
+ }
43
+
44
+ solidityCache.set(version, table)
45
+ return table
46
+ }
47
+
48
+ export function isStateIdSolidForCameraCollision(stateId: number, solidityTable: Uint8Array): boolean {
49
+ return stateId > 0 && stateId < solidityTable.length && solidityTable[stateId] === 1
50
+ }
@@ -5,9 +5,13 @@ import { WorldRendererThree } from './worldRendererThree'
5
5
 
6
6
  export class CameraShake {
7
7
  private rollAngle = 0
8
- private get damageRollAmount() { return 5 }
9
- private get damageAnimDuration() { return 200 }
10
- private rollAnimation?: { startTime: number, startRoll: number, targetRoll: number, duration: number, returnToZero?: boolean }
8
+ private get damageRollAmount() {
9
+ return 5
10
+ }
11
+ private get damageAnimDuration() {
12
+ return 200
13
+ }
14
+ private rollAnimation?: { startTime: number; startRoll: number; targetRoll: number; duration: number; returnToZero?: boolean }
11
15
  private basePitch = 0
12
16
  private baseYaw = 0
13
17
  private cameraBobInput: CameraBobInput | null = null
@@ -16,7 +20,10 @@ export class CameraShake {
16
20
  this.cameraBobInput = input
17
21
  }
18
22
 
19
- constructor(public worldRenderer: WorldRendererThree, public onRenderCallbacks: Array<(deltaTime: number) => void>) {
23
+ constructor(
24
+ public worldRenderer: WorldRendererThree,
25
+ public onRenderCallbacks: Array<(deltaTime: number) => void>
26
+ ) {
20
27
  onRenderCallbacks.push(() => {
21
28
  this.update()
22
29
  })
@@ -133,10 +140,12 @@ export class CameraShake {
133
140
  const normalizedAngle = ((angle % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2)
134
141
  const tolerance = 0.01 // Tolerance for considering an angle "ideal"
135
142
 
136
- if (Math.abs(normalizedAngle) < tolerance ||
143
+ if (
144
+ Math.abs(normalizedAngle) < tolerance ||
137
145
  Math.abs(normalizedAngle - Math.PI / 2) < tolerance ||
138
146
  Math.abs(normalizedAngle - Math.PI) < tolerance ||
139
- Math.abs(normalizedAngle - 3 * Math.PI / 2) < tolerance) {
147
+ Math.abs(normalizedAngle - (3 * Math.PI) / 2) < tolerance
148
+ ) {
140
149
  return angle + offset
141
150
  }
142
151