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
@@ -1,12 +1,8 @@
1
1
  //@ts-nocheck
2
2
  import { expect, test, vi } from 'vitest'
3
- import {
4
- createLegacyMultiDrawScratch,
5
- detectLegacyMultiDrawCaps,
6
- drawLegacySpans,
7
- } from '../legacyMultiDraw'
3
+ import { createLegacyMultiDrawScratch, detectLegacyMultiDrawCaps, drawLegacySpans } from '../legacyMultiDraw'
8
4
 
9
- function makeMockGl (opts?: { multiDraw?: boolean }): WebGL2RenderingContext {
5
+ function makeMockGl(opts?: { multiDraw?: boolean }): WebGL2RenderingContext {
10
6
  const multiDrawElementsWEBGL = vi.fn()
11
7
  const drawElements = vi.fn()
12
8
  return {
@@ -18,7 +14,7 @@ function makeMockGl (opts?: { multiDraw?: boolean }): WebGL2RenderingContext {
18
14
  }
19
15
  return null
20
16
  },
21
- drawElements,
17
+ drawElements
22
18
  } as unknown as WebGL2RenderingContext
23
19
  }
24
20
 
@@ -42,7 +38,7 @@ test('drawLegacySpans: tier A uses multiDrawElementsWEBGL with byte offsets', ()
42
38
  const scratch = createLegacyMultiDrawScratch()
43
39
  const spans = [
44
40
  { indexStart: 12, indexCount: 6 },
45
- { indexStart: 30, indexCount: 12 },
41
+ { indexStart: 30, indexCount: 12 }
46
42
  ]
47
43
 
48
44
  drawLegacySpans(gl, caps, spans, scratch)
@@ -54,13 +50,7 @@ test('drawLegacySpans: tier A uses multiDrawElementsWEBGL with byte offsets', ()
54
50
 
55
51
  const ext = caps.ext as { multiDrawElementsWEBGL: ReturnType<typeof vi.fn> }
56
52
  expect(ext.multiDrawElementsWEBGL).toHaveBeenCalledTimes(1)
57
- expect(ext.multiDrawElementsWEBGL).toHaveBeenCalledWith(
58
- gl.TRIANGLES,
59
- scratch.counts, 0,
60
- gl.UNSIGNED_INT,
61
- scratch.offsets, 0,
62
- 2,
63
- )
53
+ expect(ext.multiDrawElementsWEBGL).toHaveBeenCalledWith(gl.TRIANGLES, scratch.counts, 0, gl.UNSIGNED_INT, scratch.offsets, 0, 2)
64
54
  })
65
55
 
66
56
  test('drawLegacySpans: tier B loops drawElements per span', () => {
@@ -71,12 +61,7 @@ test('drawLegacySpans: tier B loops drawElements per span', () => {
71
61
 
72
62
  drawLegacySpans(gl, caps, spans, scratch)
73
63
 
74
- expect((gl as unknown as { drawElements: ReturnType<typeof vi.fn> }).drawElements).toHaveBeenCalledWith(
75
- gl.TRIANGLES,
76
- 6,
77
- gl.UNSIGNED_INT,
78
- 16,
79
- )
64
+ expect((gl as unknown as { drawElements: ReturnType<typeof vi.fn> }).drawElements).toHaveBeenCalledWith(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 16)
80
65
  })
81
66
 
82
67
  test('drawLegacySpans: grows scratch past MAX_OPAQUE_SPANS for uncapped blend sets', () => {
@@ -88,7 +73,7 @@ test('drawLegacySpans: grows scratch past MAX_OPAQUE_SPANS for uncapped blend se
88
73
  const spanCount = 70
89
74
  const spans = Array.from({ length: spanCount }, (_, i) => ({
90
75
  indexStart: i * 6,
91
- indexCount: 6,
76
+ indexCount: 6
92
77
  }))
93
78
 
94
79
  drawLegacySpans(gl, caps, spans, scratch)
@@ -99,11 +84,5 @@ test('drawLegacySpans: grows scratch past MAX_OPAQUE_SPANS for uncapped blend se
99
84
  expect(scratch.offsets[69]).toBe(69 * 6 * 4)
100
85
 
101
86
  const ext = caps.ext as { multiDrawElementsWEBGL: ReturnType<typeof vi.fn> }
102
- expect(ext.multiDrawElementsWEBGL).toHaveBeenCalledWith(
103
- gl.TRIANGLES,
104
- scratch.counts, 0,
105
- gl.UNSIGNED_INT,
106
- scratch.offsets, 0,
107
- spanCount,
108
- )
87
+ expect(ext.multiDrawElementsWEBGL).toHaveBeenCalledWith(gl.TRIANGLES, scratch.counts, 0, gl.UNSIGNED_INT, scratch.offsets, 0, spanCount)
109
88
  })
@@ -3,7 +3,7 @@ import { test, expect, vi, afterEach } from 'vitest'
3
3
  import * as THREE from 'three'
4
4
 
5
5
  vi.mock('../entity/EntityMesh', () => ({
6
- getMesh: vi.fn(),
6
+ getMesh: vi.fn()
7
7
  }))
8
8
 
9
9
  import { ChunkMeshManager } from '../chunkMeshManager'
@@ -11,13 +11,8 @@ import type { GlobalLegacyBuffer } from '../globalLegacyBuffer'
11
11
  import type { WorldRendererThree } from '../worldRendererThree'
12
12
  import type { MesherGeometryOutput } from '../../mesher-shared/shared'
13
13
 
14
- function makeQuadArrays () {
15
- const positions = new Float32Array([
16
- -1, -1, -1,
17
- -1, 1, -1,
18
- -1, 1, 1,
19
- -1, -1, 1,
20
- ])
14
+ function makeQuadArrays() {
15
+ const positions = new Float32Array([-1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1])
21
16
  const colors = new Float32Array(12).fill(1)
22
17
  const skyLights = new Float32Array(4).fill(1)
23
18
  const blockLights = new Float32Array(4).fill(0)
@@ -26,7 +21,7 @@ function makeQuadArrays () {
26
21
  return { positions, colors, skyLights, blockLights, uvs, indices }
27
22
  }
28
23
 
29
- function makeBlendOnlyGeometry (): MesherGeometryOutput {
24
+ function makeBlendOnlyGeometry(): MesherGeometryOutput {
30
25
  const blend = makeQuadArrays()
31
26
  return {
32
27
  sectionYNumber: 0,
@@ -62,12 +57,12 @@ function makeBlendOnlyGeometry (): MesherGeometryOutput {
62
57
  skyLights: blend.skyLights,
63
58
  blockLights: blend.blockLights,
64
59
  uvs: blend.uvs,
65
- indices: blend.indices,
66
- },
60
+ indices: blend.indices
61
+ }
67
62
  }
68
63
  }
69
64
 
70
- function makeOpaqueOnlyGeometry (sx = 8, sy = 8, sz = 8): MesherGeometryOutput {
65
+ function makeOpaqueOnlyGeometry(sx = 8, sy = 8, sz = 8): MesherGeometryOutput {
71
66
  const opaque = makeQuadArrays()
72
67
  return {
73
68
  sectionYNumber: 0,
@@ -95,11 +90,11 @@ function makeOpaqueOnlyGeometry (sx = 8, sy = 8, sz = 8): MesherGeometryOutput {
95
90
  signs: {},
96
91
  banners: {},
97
92
  hadErrors: false,
98
- blocksCount: 1,
93
+ blocksCount: 1
99
94
  }
100
95
  }
101
96
 
102
- function makeMixedGeometry (): MesherGeometryOutput {
97
+ function makeMixedGeometry(): MesherGeometryOutput {
103
98
  const opaque = makeQuadArrays()
104
99
  const blend = makeQuadArrays()
105
100
  return {
@@ -136,12 +131,12 @@ function makeMixedGeometry (): MesherGeometryOutput {
136
131
  skyLights: blend.skyLights,
137
132
  blockLights: blend.blockLights,
138
133
  uvs: blend.uvs,
139
- indices: blend.indices,
140
- },
134
+ indices: blend.indices
135
+ }
141
136
  }
142
137
  }
143
138
 
144
- function createManager (): ChunkMeshManager {
139
+ function createManager(): ChunkMeshManager {
145
140
  const scene = new THREE.Scene()
146
141
  const material = new THREE.MeshBasicMaterial()
147
142
  const worldRenderer = {
@@ -150,25 +145,25 @@ function createManager (): ChunkMeshManager {
150
145
  sceneOrigin: {
151
146
  track: () => {},
152
147
  removeAndUntrack: () => {},
153
- removeAndUntrackAll: () => {},
148
+ removeAndUntrackAll: () => {}
154
149
  },
155
150
  blockEntities: {},
156
- worldRendererConfig: {},
151
+ worldRendererConfig: {}
157
152
  } as unknown as WorldRendererThree
158
153
  return new ChunkMeshManager(worldRenderer, scene, material, 256, 1)
159
154
  }
160
155
 
161
156
  type ManagerInternals = {
162
- legacyCullSections: Map<string, { worldX: number, worldY: number, worldZ: number }>
157
+ legacyCullSections: Map<string, { worldX: number; worldY: number; worldZ: number }>
163
158
  registerLegacyCullSection: (key: string, wx: number, wy: number, wz: number) => void
164
159
  maybeUnregisterLegacyCullSection: (key: string) => void
165
160
  }
166
161
 
167
- function getLegacyCullSections (manager: ChunkMeshManager): Map<string, { worldX: number, worldY: number, worldZ: number }> {
162
+ function getLegacyCullSections(manager: ChunkMeshManager): Map<string, { worldX: number; worldY: number; worldZ: number }> {
168
163
  return (manager as unknown as ManagerInternals).legacyCullSections
169
164
  }
170
165
 
171
- function makeCamera (x: number, y: number, z: number): THREE.PerspectiveCamera {
166
+ function makeCamera(x: number, y: number, z: number): THREE.PerspectiveCamera {
172
167
  const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000)
173
168
  camera.position.set(x, y, z)
174
169
  camera.lookAt(8, 8, 8)
@@ -225,9 +220,14 @@ test('updateSectionCullAndSort: same visible set skips span rebuild on camera mo
225
220
  manager.updateSectionCullAndSort(camera1, 8, 8, 20)
226
221
  expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
227
222
 
223
+ while (blendBuffer.hasPendingUploads()) blendBuffer.uploadDirtyRange()
224
+
225
+ manager.updateSectionCullAndSort(camera1, 8, 8, 20)
226
+ expect(updateDrawSpansSpy).toHaveBeenCalledTimes(2)
227
+
228
228
  const camera2 = makeCamera(8, 8, 18)
229
229
  manager.updateSectionCullAndSort(camera2, 8, 8, 18)
230
- expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
230
+ expect(updateDrawSpansSpy).toHaveBeenCalledTimes(2)
231
231
 
232
232
  manager.cleanupSection(key)
233
233
  manager.dispose()
@@ -246,11 +246,7 @@ test('updateSectionCullAndSort: layoutVersion change forces span rebuild', () =>
246
246
  expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
247
247
 
248
248
  blendBuffer.removeSection(key)
249
- blendBuffer.addSection(
250
- key,
251
- makeBlendOnlyGeometry().blend!,
252
- 8, 8, 8,
253
- )
249
+ blendBuffer.addSection(key, makeBlendOnlyGeometry().blend!, 8, 8, 8)
254
250
  ;(manager as unknown as ManagerInternals).registerLegacyCullSection(key, 8, 8, 8)
255
251
 
256
252
  manager.updateSectionCullAndSort(camera, 8, 8, 20)
@@ -260,10 +256,91 @@ test('updateSectionCullAndSort: layoutVersion change forces span rebuild', () =>
260
256
  manager.dispose()
261
257
  })
262
258
 
263
- function drainLegacyUploads (buffer: GlobalLegacyBuffer): void {
259
+ function drainLegacyUploads(buffer: GlobalLegacyBuffer): void {
264
260
  while (buffer.hasPendingUploads()) buffer.uploadDirtyRange()
265
261
  }
266
262
 
263
+ function simulateRenderCullGate(manager: ChunkMeshManager, camera: THREE.PerspectiveCamera, x: number, y: number, z: number): void {
264
+ for (const buf of [manager.globalLegacyBuffer, manager.globalLegacyBlendBuffer, manager.globalBlockBuffer]) {
265
+ if (!buf) continue
266
+ buf.compactStep()
267
+ if (buf.hasPendingUploads()) buf.uploadDirtyRange()
268
+ }
269
+ manager.markCullDirtyIfBufferStateChanged()
270
+ if (manager.cullDirty) {
271
+ manager.updateSectionCullAndSort(camera, x, y, z)
272
+ manager.clearCullDirty()
273
+ }
274
+ }
275
+
276
+ test('markCullDirtyIfBufferStateChanged: finalize layout bump marks cull dirty', () => {
277
+ const manager = createManager()
278
+ const key = '0,0,0'
279
+ manager.updateSection(key, makeOpaqueOnlyGeometry(8, 8, 8))
280
+
281
+ const opaqueBuf = manager.globalLegacyBuffer!
282
+ drainLegacyUploads(opaqueBuf)
283
+
284
+ const camera = makeCamera(8, 8, 20)
285
+ manager.updateSectionCullAndSort(camera, 8, 8, 20)
286
+ manager.clearCullDirty()
287
+
288
+ opaqueBuf.addSection(
289
+ key,
290
+ {
291
+ positions: makeQuadArrays().positions,
292
+ colors: makeQuadArrays().colors,
293
+ skyLights: makeQuadArrays().skyLights,
294
+ blockLights: makeQuadArrays().blockLights,
295
+ uvs: makeQuadArrays().uvs,
296
+ indices: makeQuadArrays().indices
297
+ },
298
+ 16,
299
+ 8,
300
+ 8
301
+ )
302
+ drainLegacyUploads(opaqueBuf)
303
+ opaqueBuf.compactStep()
304
+ expect(opaqueBuf.hasPendingReplace()).toBe(false)
305
+
306
+ manager.markCullDirtyIfBufferStateChanged()
307
+ expect(manager.cullDirty).toBe(true)
308
+
309
+ manager.cleanupSection(key)
310
+ manager.dispose()
311
+ })
312
+
313
+ test('simulateRenderCullGate: finalize remesh refreshes visible spans without camera move', () => {
314
+ const manager = createManager()
315
+ const key = '0,0,0'
316
+ const camera = makeCamera(8, 8, 20)
317
+
318
+ manager.updateSection(key, makeOpaqueOnlyGeometry(8, 8, 8))
319
+ const opaqueBuf = manager.globalLegacyBuffer!
320
+ drainLegacyUploads(opaqueBuf)
321
+ simulateRenderCullGate(manager, camera, 8, 8, 20)
322
+
323
+ const slotA = opaqueBuf.getSectionSlot(key)!.start
324
+ opaqueBuf.updateDrawSpans([{ key, distSq: 1 }], 'opaque')
325
+ expect(opaqueBuf.getVisibleIndexSpans()[0]!.indexStart).toBe(slotA * 6)
326
+
327
+ manager.updateSection(key, makeOpaqueOnlyGeometry(16, 8, 8))
328
+ for (let i = 0; i < 32 && (manager.hasPendingBufferWork() || opaqueBuf.hasPendingReplace() || opaqueBuf.hasPendingUploads()); i++) {
329
+ simulateRenderCullGate(manager, camera, 8, 8, 20)
330
+ }
331
+
332
+ expect(opaqueBuf.hasPendingReplace()).toBe(false)
333
+ expect(opaqueBuf.hasPendingUploads()).toBe(false)
334
+
335
+ const drawStart = opaqueBuf.getSectionDrawStart(key)!
336
+ const spans = opaqueBuf.getVisibleIndexSpans()
337
+ expect(spans.length).toBeGreaterThan(0)
338
+ expect(spans[0]!.indexStart).toBe(drawStart * 6)
339
+
340
+ manager.cleanupSection(key)
341
+ manager.dispose()
342
+ })
343
+
267
344
  test('updateSectionCullAndSort: defrag finalize forces span rebuild with static camera', () => {
268
345
  const manager = createManager()
269
346
  const keys = ['0,0,0', '1,0,0', '2,0,0'] as const
@@ -4,21 +4,21 @@ import * as THREE from 'three'
4
4
 
5
5
  const renderSignMock = vi.fn()
6
6
  vi.mock('../../sign-renderer', () => ({
7
- renderSign: (...args: unknown[]) => renderSignMock(...args),
7
+ renderSign: (...args: unknown[]) => renderSignMock(...args)
8
8
  }))
9
9
 
10
10
  vi.mock('prismarine-chat', () => ({
11
- default: () => () => ({}),
11
+ default: () => () => ({})
12
12
  }))
13
13
 
14
14
  import { getSignTexture, releaseSignTexture, disposeAllSignTextures } from '../signTextureCache'
15
15
  import type { WorldRendererThree } from '../worldRendererThree'
16
16
 
17
- function createWorldRenderer (): WorldRendererThree {
17
+ function createWorldRenderer(): WorldRendererThree {
18
18
  return { version: '1.20' } as WorldRendererThree
19
19
  }
20
20
 
21
- function stubCanvas () {
21
+ function stubCanvas() {
22
22
  return { width: 64, height: 32 } as HTMLCanvasElement
23
23
  }
24
24
 
@@ -0,0 +1,120 @@
1
+ //@ts-nocheck
2
+ import { raycastAabb, raycastAabbFromInside } from './sectionRaycastAabb'
3
+
4
+ export type VoxelRaycastHit = {
5
+ distance: number
6
+ blockX: number
7
+ blockY: number
8
+ blockZ: number
9
+ }
10
+
11
+ const intBound = (s: number, ds: number): number => {
12
+ if (ds === 0) return Infinity
13
+ if (ds < 0) return intBound(-s, -ds)
14
+ const frac = s - Math.floor(s)
15
+ return (1 - frac) / ds
16
+ }
17
+
18
+ const hitSolidBlock = (
19
+ ox: number,
20
+ oy: number,
21
+ oz: number,
22
+ dx: number,
23
+ dy: number,
24
+ dz: number,
25
+ bx: number,
26
+ by: number,
27
+ bz: number,
28
+ maxDist: number,
29
+ radius: number,
30
+ minCameraDistance: number
31
+ ): number | undefined => {
32
+ const minX = bx - radius
33
+ const minY = by - radius
34
+ const minZ = bz - radius
35
+ const maxX = bx + 1 + radius
36
+ const maxY = by + 1 + radius
37
+ const maxZ = bz + 1 + radius
38
+
39
+ let t = raycastAabb(ox, oy, oz, dx, dy, dz, minX, minY, minZ, maxX, maxY, maxZ, maxDist)
40
+ if (t === undefined) {
41
+ t = raycastAabbFromInside(ox, oy, oz, dx, dy, dz, minX, minY, minZ, maxX, maxY, maxZ, maxDist)
42
+ }
43
+ if (t === undefined) return undefined
44
+ return Math.max(minCameraDistance, t)
45
+ }
46
+
47
+ /**
48
+ * Amanatides–Woo grid traversal against solid block volumes (not rendered mesh faces).
49
+ * `radius` is baked into per-block AABB expansion (swept sphere).
50
+ */
51
+ export function raycastVoxelSolid(
52
+ ox: number,
53
+ oy: number,
54
+ oz: number,
55
+ dx: number,
56
+ dy: number,
57
+ dz: number,
58
+ maxDist: number,
59
+ radius: number,
60
+ minCameraDistance: number,
61
+ isSolid: (x: number, y: number, z: number) => boolean
62
+ ): VoxelRaycastHit | undefined {
63
+ if (maxDist <= 0) return undefined
64
+
65
+ let x = Math.floor(ox)
66
+ let y = Math.floor(oy)
67
+ let z = Math.floor(oz)
68
+
69
+ const stepX = dx > 0 ? 1 : dx < 0 ? -1 : 0
70
+ const stepY = dy > 0 ? 1 : dy < 0 ? -1 : 0
71
+ const stepZ = dz > 0 ? 1 : dz < 0 ? -1 : 0
72
+
73
+ const tDeltaX = stepX !== 0 ? Math.abs(1 / dx) : Infinity
74
+ const tDeltaY = stepY !== 0 ? Math.abs(1 / dy) : Infinity
75
+ const tDeltaZ = stepZ !== 0 ? Math.abs(1 / dz) : Infinity
76
+
77
+ let tMaxX = stepX !== 0 ? intBound(ox, dx) : Infinity
78
+ let tMaxY = stepY !== 0 ? intBound(oy, dy) : Infinity
79
+ let tMaxZ = stepZ !== 0 ? intBound(oz, dz) : Infinity
80
+
81
+ const tryHit = (bx: number, by: number, bz: number): VoxelRaycastHit | undefined => {
82
+ if (!isSolid(bx, by, bz)) return undefined
83
+ const distance = hitSolidBlock(ox, oy, oz, dx, dy, dz, bx, by, bz, maxDist, radius, minCameraDistance)
84
+ if (distance === undefined || distance > maxDist) return undefined
85
+ return { distance, blockX: bx, blockY: by, blockZ: bz }
86
+ }
87
+
88
+ let hit = tryHit(x, y, z)
89
+ if (hit) return hit
90
+
91
+ let t = 0
92
+ while (t <= maxDist) {
93
+ if (tMaxX < tMaxY) {
94
+ if (tMaxX < tMaxZ) {
95
+ x += stepX
96
+ t = tMaxX
97
+ tMaxX += tDeltaX
98
+ } else {
99
+ z += stepZ
100
+ t = tMaxZ
101
+ tMaxZ += tDeltaZ
102
+ }
103
+ } else if (tMaxY < tMaxZ) {
104
+ y += stepY
105
+ t = tMaxY
106
+ tMaxY += tDeltaY
107
+ } else {
108
+ z += stepZ
109
+ t = tMaxZ
110
+ tMaxZ += tDeltaZ
111
+ }
112
+
113
+ if (t > maxDist) break
114
+
115
+ hit = tryHit(x, y, z)
116
+ if (hit) return hit
117
+ }
118
+
119
+ return undefined
120
+ }