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.
- package/README.md +29 -25
- package/dist/mesher.js +36 -36
- package/dist/mesher.js.map +4 -4
- package/dist/mesherWasm.js +217 -217
- package/dist/minecraft-renderer.js +27 -27
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +133 -133
- package/package.json +5 -2
- package/src/bundler/bundlePrepare.ts +3 -8
- package/src/graphicsBackend/appViewer.ts +10 -7
- package/src/graphicsBackend/config.ts +1 -5
- package/src/graphicsBackend/playerState.ts +35 -34
- package/src/graphicsBackend/preloadWorkers.ts +43 -41
- package/src/graphicsBackend/rendererDefaultOptions.ts +68 -70
- package/src/graphicsBackend/rendererOptionsSync.ts +24 -50
- package/src/graphicsBackend/types.ts +2 -2
- package/src/index.ts +6 -27
- package/src/lib/DebugGui.ts +34 -31
- package/src/lib/animationController.ts +9 -9
- package/src/lib/bakeLegacyLight.ts +1 -6
- package/src/lib/bindAbortableListener.test.ts +32 -12
- package/src/lib/bindAbortableListener.ts +14 -6
- package/src/lib/blockEntityLightRegistry.ts +11 -20
- package/src/lib/blockEntityLighting.test.ts +1 -6
- package/src/lib/blockEntityLighting.ts +5 -5
- package/src/lib/buildSharedConfig.mjs +4 -4
- package/src/lib/buildWorkerMcDataIndexes.test.ts +4 -4
- package/src/lib/buildWorkerMcDataIndexes.ts +11 -19
- package/src/lib/cameraBobbing.ts +1 -1
- package/src/lib/canvas2DOverlay.example.ts +13 -33
- package/src/lib/canvas2DOverlay.quickstart.ts +24 -54
- package/src/lib/canvas2DOverlay.ts +28 -47
- package/src/lib/cleanupDecorator.ts +2 -2
- package/src/lib/createPlayerObject.test.ts +2 -7
- package/src/lib/createPlayerObject.ts +18 -25
- package/src/lib/frameTimingCollector.ts +2 -5
- package/src/lib/guiRenderer.ts +57 -42
- package/src/lib/items.ts +15 -9
- package/src/lib/mesherlogReader.ts +5 -6
- package/src/lib/simpleUtils.ts +5 -5
- package/src/lib/skyLight.ts +3 -3
- package/src/lib/smoothSwitcher.ts +11 -18
- package/src/lib/ui/newStats.ts +10 -12
- package/src/lib/utils/proxy.ts +7 -1
- package/src/lib/utils/skins.ts +4 -4
- package/src/lib/utils.ts +1 -1
- package/src/lib/workerMessageSanitize.ts +5 -12
- package/src/lib/workerProxy.ts +52 -42
- package/src/lib/workerSyncOps.test.ts +58 -41
- package/src/lib/worldrendererCommon.reconfigure.test.ts +24 -21
- package/src/lib/worldrendererCommon.removeColumn.test.ts +11 -13
- package/src/lib/worldrendererCommon.ts +338 -211
- package/src/mesher-legacy/mesher.ts +9 -4
- package/src/mesher-legacy/test/a.ts +1 -1
- package/src/mesher-legacy/test/mesherTester.ts +2 -2
- package/src/mesher-legacy/test/playground.ts +1 -1
- package/src/mesher-legacy/test/run/test-js.ts +1 -1
- package/src/mesher-legacy/test/snapshotUtils.ts +3 -8
- package/src/mesher-legacy/test/test-perf.ts +1 -1
- package/src/mesher-legacy/test/tests.test.ts +2 -2
- package/src/mesher-shared/blockEntityMetadata.test.ts +7 -5
- package/src/mesher-shared/blockEntityMetadata.ts +28 -19
- package/src/mesher-shared/blockPropertiesForMeshing.ts +2 -2
- package/src/mesher-shared/computeHeightmap.ts +2 -4
- package/src/mesher-shared/exportedGeometryTypes.ts +3 -3
- package/src/mesher-shared/faceOcclusion.ts +324 -0
- package/src/mesher-shared/models.ts +80 -131
- package/src/mesher-shared/modelsGeometryCommon.ts +30 -15
- package/src/mesher-shared/shared.ts +46 -46
- package/src/mesher-shared/standaloneRenderer.ts +40 -44
- package/src/mesher-shared/tests/faceOcclusion.test.ts +179 -0
- package/src/mesher-shared/tests/liquidQuadInvariant.test.ts +12 -8
- package/src/mesher-shared/vertexShading.ts +2 -11
- package/src/mesher-shared/world.ts +19 -19
- package/src/mesher-shared/worldLighting.test.ts +2 -2
- package/src/performanceMonitor/PerformanceMonitor.ts +4 -13
- package/src/performanceMonitor/formatPerformanceFactorsDebug.ts +2 -2
- package/src/performanceMonitor/index.ts +1 -5
- package/src/performanceMonitor/types.ts +1 -1
- package/src/playerState/playerState.ts +55 -50
- package/src/playerState/types.ts +2 -8
- package/src/playground/allEntitiesDebug.ts +19 -16
- package/src/playground/baseScene.ts +29 -32
- package/src/playground/mobileControls.tsx +133 -129
- package/src/playground/playground.html +76 -73
- package/src/playground/playground.ts +14 -1
- package/src/playground/playgroundUi.tsx +100 -74
- package/src/playground/reactUtils.ts +3 -7
- package/src/playground/scenes/entities.ts +10 -7
- package/src/playground/scenes/frequentUpdates.ts +5 -5
- package/src/playground/scenes/geometryExport.ts +6 -20
- package/src/playground/scenes/highPosEntityUpdates.ts +2 -10
- package/src/playground/scenes/index.ts +1 -1
- package/src/playground/scenes/lightingStarfield.ts +18 -12
- package/src/playground/scenes/main.ts +15 -11
- package/src/playground/scenes/partialBlockCulling.ts +31 -0
- package/src/playground/scenes/railsCobweb.ts +1 -1
- package/src/playground/scenes/rotationIssue.ts +1 -1
- package/src/playground/scenes/slabsOptimization.ts +1 -2
- package/src/playground/scenes/transparencyIssue.ts +1 -1
- package/src/playground/shared.ts +2 -2
- package/src/resourcesManager/resourcesManager.ts +29 -54
- package/src/resourcesManager/resourcesManager.worker.test.ts +6 -10
- package/src/shims/minecraftData.ts +70 -24
- package/src/sign-renderer/index.html +10 -12
- package/src/sign-renderer/index.ts +29 -29
- package/src/sign-renderer/playground.ts +22 -15
- package/src/sign-renderer/tests.test.ts +16 -13
- package/src/sign-renderer/vite.config.ts +2 -2
- package/src/three/appShared.ts +46 -25
- package/src/three/bannerRenderer.ts +33 -61
- package/src/three/cameraShake.ts +15 -6
- package/src/three/chunkMeshManager.ts +239 -282
- package/src/three/cinimaticScript.ts +26 -15
- package/src/three/cubeDrawSpans.ts +27 -37
- package/src/three/cubeMultiDraw.ts +9 -22
- package/src/three/documentRenderer.ts +15 -18
- package/src/three/entities.ts +175 -150
- package/src/three/entity/EntityMesh.ts +114 -108
- package/src/three/entity/animations.js +3 -3
- package/src/three/entity/armorModels.ts +15 -15
- package/src/three/entity/gltfAnimationUtils.ts +2 -5
- package/src/three/fireworks.ts +34 -32
- package/src/three/fireworksRenderer.ts +32 -29
- package/src/three/globalBlockBuffer.ts +167 -100
- package/src/three/globalLegacyBuffer.ts +257 -166
- package/src/three/graphicsBackendBase.ts +19 -25
- package/src/three/graphicsBackendOffThread.ts +12 -15
- package/src/three/hand.ts +21 -21
- package/src/three/handLegacy.ts +3 -13
- package/src/three/holdingBlock.ts +116 -79
- package/src/three/holdingBlockFactory.ts +1 -4
- package/src/three/holdingBlockItemIdentity.test.ts +25 -17
- package/src/three/holdingBlockItemIdentity.ts +9 -6
- package/src/three/holdingBlockLegacy.ts +113 -132
- package/src/three/itemMesh.ts +20 -29
- package/src/three/legacyMultiDraw.ts +10 -16
- package/src/three/legacySectionCull.ts +8 -14
- package/src/three/menuBackground/activeView.ts +1 -4
- package/src/three/menuBackground/assetUrl.ts +1 -1
- package/src/three/menuBackground/classic.ts +4 -12
- package/src/three/menuBackground/gpuPreference.ts +1 -3
- package/src/three/menuBackground/index.ts +2 -11
- package/src/three/menuBackground/renderer.ts +1 -5
- package/src/three/menuBackground/types.ts +1 -4
- package/src/three/menuBackground/v2.ts +196 -96
- package/src/three/menuBackground/v2Meta.ts +3 -3
- package/src/three/menuBackground/worldBlocks.ts +37 -35
- package/src/three/modules/blockBreakParticles.ts +59 -28
- package/src/three/modules/cameraBobbing.ts +3 -6
- package/src/three/modules/index.ts +1 -1
- package/src/three/modules/rain.ts +10 -23
- package/src/three/modules/sciFiWorldReveal.ts +11 -25
- package/src/three/modules/starfield.ts +7 -13
- package/src/three/renderSlot.ts +28 -16
- package/src/three/rendererModuleSystem.ts +1 -3
- package/src/three/sceneOrigin.ts +96 -33
- package/src/three/sectionRaycastAabb.ts +30 -18
- package/src/three/shaderCubeMesh.ts +6 -17
- package/src/three/shaders/cubeBlockShader.ts +85 -96
- package/src/three/shaders/legacyBlockShader.ts +21 -43
- package/src/three/shaders/textureIndexMapping.ts +89 -95
- package/src/three/shaders/tintPalette.ts +156 -170
- package/src/three/signTextureCache.ts +9 -12
- package/src/three/skyboxRenderer.ts +62 -70
- package/src/three/tests/chunkMeshManagerLegacy.test.ts +34 -34
- package/src/three/tests/cubeDrawSpans.test.ts +55 -25
- package/src/three/tests/globalBlockBuffer.test.ts +217 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +300 -41
- package/src/three/tests/legacyMultiDraw.test.ts +8 -29
- package/src/three/tests/pr3PerFrameCpuCuts.test.ts +106 -29
- package/src/three/tests/signTextureCache.test.ts +4 -4
- package/src/three/threeJsMedia.ts +55 -72
- package/src/three/threeJsParticles.ts +32 -41
- package/src/three/threeJsSound.ts +3 -3
- package/src/three/threeJsUtils.ts +3 -3
- package/src/three/threeWorker.ts +2 -2
- package/src/three/waypointSprite.ts +55 -82
- package/src/three/waypoints.ts +20 -23
- package/src/three/world/cursorBlock.ts +25 -12
- package/src/three/world/vr.ts +11 -17
- package/src/three/worldGeometryExport.ts +15 -34
- package/src/three/worldRendererThree.ts +117 -129
- package/src/wasm-mesher/bridge/convertChunk.ts +3 -9
- package/src/wasm-mesher/bridge/render-from-wasm.ts +204 -229
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +26 -67
- package/src/wasm-mesher/tests/cullingRegression.test.ts +307 -0
- package/src/wasm-mesher/tests/heightmapParity.test.ts +8 -18
- package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +2 -2
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +17 -4
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +103 -141
- package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +14 -22
- package/src/wasm-mesher/worker/mesherWasm.ts +150 -141
- package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +2 -8
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +1 -3
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +1 -5
- package/src/wasm-mesher/worker/mesherWasmRequestTracker.ts +42 -42
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +34 -39
- package/src/worldView/worldViewWorkerBridge.test.ts +4 -4
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
drawCubeSpans,
|
|
8
8
|
logMultiDrawTierOnce,
|
|
9
9
|
type CubeMultiDrawScratch,
|
|
10
|
-
type MultiDrawCaps
|
|
10
|
+
type MultiDrawCaps
|
|
11
11
|
} from './cubeMultiDraw'
|
|
12
12
|
import { VERTICES_PER_FACE, computeSectionOriginRel } from './shaders/cubeBlockShader'
|
|
13
13
|
import { computeCameraRelativeUniforms, type RenderOrigin } from './shaders/legacyBlockShader'
|
|
@@ -19,7 +19,7 @@ type WebGLRendererInternals = THREE.WebGLRenderer & {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
type TierCAttrBinding = { loc: number
|
|
22
|
+
type TierCAttrBinding = { loc: number; buffer: WebGLBuffer }
|
|
23
23
|
type TierCAttrState = {
|
|
24
24
|
w0: TierCAttrBinding
|
|
25
25
|
w1: TierCAttrBinding
|
|
@@ -29,16 +29,17 @@ type TierCAttrState = {
|
|
|
29
29
|
|
|
30
30
|
// Linear growth (NOT doubling) to keep iOS allocation spikes bounded to one increment.
|
|
31
31
|
// Reference: prismarine-web-client PR #90 (webgl) and #120 (webgpu) both grow by +1M faces.
|
|
32
|
-
const INITIAL_CAPACITY_FACES = 512_000
|
|
33
|
-
const GROWTH_INCREMENT_FACES = 1_000_000
|
|
34
|
-
const MAX_UPLOAD_FACES_PER_FRAME = 15_000
|
|
32
|
+
const INITIAL_CAPACITY_FACES = 512_000 // ~8 MB up front (4 words × 4 B), well under 1M
|
|
33
|
+
const GROWTH_INCREMENT_FACES = 1_000_000 // +16 MB per growth step instead of doubling
|
|
34
|
+
const MAX_UPLOAD_FACES_PER_FRAME = 15_000 // face-indexed budget (chunksStorage uses 10k blocks)
|
|
35
35
|
const FRAGMENTATION_THRESHOLD = 0.25
|
|
36
36
|
const EMPTY_W2 = packWord2Empty()
|
|
37
37
|
|
|
38
38
|
/** CPU bytes per instanced cube face (a_w0..a_w3). */
|
|
39
39
|
export const SHADER_CUBE_BYTES_PER_FACE = 16
|
|
40
40
|
|
|
41
|
-
type PendingMove = { key: string
|
|
41
|
+
type PendingMove = { key: string; oldStart: number; newStart: number; count: number }
|
|
42
|
+
type PendingReplace = { oldStart: number; oldCount: number }
|
|
42
43
|
|
|
43
44
|
export type GlobalBlockBufferShaderData = {
|
|
44
45
|
words: Uint32Array
|
|
@@ -57,11 +58,13 @@ export class GlobalBlockBuffer {
|
|
|
57
58
|
private w1: Uint32Array
|
|
58
59
|
private w2: Uint32Array
|
|
59
60
|
private w3: Uint32Array
|
|
60
|
-
private readonly sectionSlots = new Map<string, { start: number
|
|
61
|
-
private freeList: Array<{ start: number
|
|
61
|
+
private readonly sectionSlots = new Map<string, { start: number; count: number }>()
|
|
62
|
+
private freeList: Array<{ start: number; count: number }> = []
|
|
62
63
|
private highWatermark = 0
|
|
63
|
-
private pendingRanges: Array<{ start: number
|
|
64
|
+
private pendingRanges: Array<{ start: number; end: number }> = []
|
|
64
65
|
private pendingMove: PendingMove | null = null
|
|
66
|
+
private readonly pendingReplace = new Map<string, PendingReplace>()
|
|
67
|
+
private uploadEpoch = 0
|
|
65
68
|
private visibleSpans: CubeDrawSpan[] = []
|
|
66
69
|
private readonly _drawScratch: CubeMultiDrawScratch = createCubeMultiDrawScratch()
|
|
67
70
|
private multiDrawCaps: MultiDrawCaps | null = null
|
|
@@ -71,10 +74,7 @@ export class GlobalBlockBuffer {
|
|
|
71
74
|
private debugOverlay = false
|
|
72
75
|
private layoutVersion = 0
|
|
73
76
|
|
|
74
|
-
constructor
|
|
75
|
-
material: THREE.ShaderMaterial,
|
|
76
|
-
scene: THREE.Object3D,
|
|
77
|
-
) {
|
|
77
|
+
constructor(material: THREE.ShaderMaterial, scene: THREE.Object3D) {
|
|
78
78
|
this.capacityFaces = INITIAL_CAPACITY_FACES
|
|
79
79
|
this.w0 = new Uint32Array(this.capacityFaces)
|
|
80
80
|
this.w1 = new Uint32Array(this.capacityFaces)
|
|
@@ -119,14 +119,12 @@ export class GlobalBlockBuffer {
|
|
|
119
119
|
this.multiDrawCaps,
|
|
120
120
|
this.visibleSpans,
|
|
121
121
|
this._drawScratch,
|
|
122
|
-
this.multiDrawCaps.tier === 'C'
|
|
123
|
-
? (g, spans) => this.drawTierCSpans(g, spans, renderer, shaderMaterial)
|
|
124
|
-
: undefined,
|
|
122
|
+
this.multiDrawCaps.tier === 'C' ? (g, spans) => this.drawTierCSpans(g, spans, renderer, shaderMaterial) : undefined
|
|
125
123
|
)
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
|
|
129
|
-
setDebugOverlay
|
|
127
|
+
setDebugOverlay(enabled: boolean): void {
|
|
130
128
|
this.debugOverlay = enabled
|
|
131
129
|
}
|
|
132
130
|
|
|
@@ -135,69 +133,117 @@ export class GlobalBlockBuffer {
|
|
|
135
133
|
* setDrawRange(0,0) skips bindingStates.setup in r0.184 — use 6 verts + instanceCount=0
|
|
136
134
|
* so program/VAO stay bound while renderInstances no-ops at primcount===0.
|
|
137
135
|
*/
|
|
138
|
-
suppressThreeDraw
|
|
136
|
+
suppressThreeDraw(): void {
|
|
139
137
|
const geometry = this.mesh.geometry
|
|
140
138
|
geometry.setDrawRange(0, VERTICES_PER_FACE)
|
|
141
139
|
geometry.instanceCount = 0
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
setVisibleSpans
|
|
142
|
+
setVisibleSpans(spans: CubeDrawSpan[]): void {
|
|
145
143
|
this.visibleSpans = spans
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
getVisibleSpans
|
|
146
|
+
getVisibleSpans(): readonly CubeDrawSpan[] {
|
|
149
147
|
return this.visibleSpans
|
|
150
148
|
}
|
|
151
149
|
|
|
152
|
-
forEachSectionSlot
|
|
150
|
+
forEachSectionSlot(cb: (key: string, slot: { start: number; count: number }) => void): void {
|
|
153
151
|
for (const [key, slot] of this.sectionSlots) {
|
|
154
152
|
cb(key, slot)
|
|
155
153
|
}
|
|
156
154
|
}
|
|
157
155
|
|
|
158
|
-
getSectionDrawStart
|
|
156
|
+
getSectionDrawStart(sectionKey: string): number | undefined {
|
|
159
157
|
const slot = this.sectionSlots.get(sectionKey)
|
|
160
158
|
if (!slot) return undefined
|
|
161
159
|
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
160
|
+
const replace = this.pendingReplace.get(sectionKey)
|
|
161
|
+
if (replace) return replace.oldStart
|
|
162
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
162
163
|
return slot.start
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
|
|
166
|
+
getSectionDrawCount(sectionKey: string): number | undefined {
|
|
167
|
+
const slot = this.sectionSlots.get(sectionKey)
|
|
168
|
+
if (!slot) return undefined
|
|
169
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
170
|
+
const replace = this.pendingReplace.get(sectionKey)
|
|
171
|
+
if (replace) return replace.oldCount
|
|
172
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
173
|
+
return slot.count
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getUploadEpoch(): number {
|
|
177
|
+
return this.uploadEpoch
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
hasPendingReplace(): boolean {
|
|
181
|
+
return this.pendingReplace.size > 0
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
canUseFullDrawShortcut(): boolean {
|
|
185
|
+
return this.pendingRanges.length === 0 && this.interiorFreeFaces() === 0 && this.pendingMove === null && this.pendingReplace.size === 0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
isRangeFullyUploaded(start: number, end: number): boolean {
|
|
189
|
+
return this.rangeFullyUploaded(start, end)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getPendingDirtyRanges(): ReadonlyArray<{ start: number; end: number }> {
|
|
193
|
+
return this.pendingRanges
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getHighWatermark(): number {
|
|
166
197
|
return this.highWatermark
|
|
167
198
|
}
|
|
168
199
|
|
|
169
|
-
getCapacityFaces
|
|
200
|
+
getCapacityFaces(): number {
|
|
170
201
|
return this.capacityFaces
|
|
171
202
|
}
|
|
172
203
|
|
|
173
|
-
getSectionCount
|
|
204
|
+
getSectionCount(): number {
|
|
174
205
|
return this.sectionSlots.size
|
|
175
206
|
}
|
|
176
207
|
|
|
177
|
-
getMemoryBytes
|
|
208
|
+
getMemoryBytes(): number {
|
|
178
209
|
return this.capacityFaces * SHADER_CUBE_BYTES_PER_FACE
|
|
179
210
|
}
|
|
180
211
|
|
|
181
|
-
getUsedMemoryBytes
|
|
212
|
+
getUsedMemoryBytes(): number {
|
|
182
213
|
return this.highWatermark * SHADER_CUBE_BYTES_PER_FACE
|
|
183
214
|
}
|
|
184
215
|
|
|
185
|
-
hasPendingUploads
|
|
216
|
+
hasPendingUploads(): boolean {
|
|
186
217
|
return this.pendingRanges.length > 0
|
|
187
218
|
}
|
|
188
219
|
|
|
189
|
-
getPendingMove
|
|
220
|
+
getPendingMove(): PendingMove | null {
|
|
190
221
|
return this.pendingMove
|
|
191
222
|
}
|
|
192
223
|
|
|
193
|
-
addSection
|
|
224
|
+
addSection(sectionKey: string, words: Uint32Array, faceCount: number): void {
|
|
194
225
|
if (faceCount <= 0) {
|
|
195
226
|
this.removeSection(sectionKey)
|
|
196
227
|
return
|
|
197
228
|
}
|
|
198
229
|
|
|
199
|
-
|
|
200
|
-
|
|
230
|
+
const isRemesh = this.sectionSlots.has(sectionKey)
|
|
231
|
+
let previousSlot: { start: number; count: number } | undefined
|
|
232
|
+
if (isRemesh) {
|
|
233
|
+
const currentSlot = this.sectionSlots.get(sectionKey)!
|
|
234
|
+
const inflightReplace = this.pendingReplace.get(sectionKey)
|
|
235
|
+
const inflightMove = this.pendingMove?.key === sectionKey ? this.pendingMove : undefined
|
|
236
|
+
if (inflightReplace) {
|
|
237
|
+
this.zeroAndFreeSlot(currentSlot.start, currentSlot.count)
|
|
238
|
+
previousSlot = { start: inflightReplace.oldStart, count: inflightReplace.oldCount }
|
|
239
|
+
this.pendingReplace.delete(sectionKey)
|
|
240
|
+
} else if (inflightMove) {
|
|
241
|
+
this.zeroAndFreeSlot(currentSlot.start, currentSlot.count)
|
|
242
|
+
previousSlot = { start: inflightMove.oldStart, count: inflightMove.count }
|
|
243
|
+
this.pendingMove = null
|
|
244
|
+
} else {
|
|
245
|
+
previousSlot = currentSlot
|
|
246
|
+
}
|
|
201
247
|
}
|
|
202
248
|
|
|
203
249
|
if (faceCount > this.capacityFaces) {
|
|
@@ -224,30 +270,33 @@ export class GlobalBlockBuffer {
|
|
|
224
270
|
}
|
|
225
271
|
|
|
226
272
|
this.sectionSlots.set(sectionKey, slot)
|
|
273
|
+
if (isRemesh && previousSlot) {
|
|
274
|
+
this.pendingReplace.set(sectionKey, { oldStart: previousSlot.start, oldCount: previousSlot.count })
|
|
275
|
+
}
|
|
227
276
|
this.markDirty(slot.start, slot.start + faceCount - 1)
|
|
228
277
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
229
278
|
this.layoutVersion++
|
|
230
279
|
}
|
|
231
280
|
|
|
232
|
-
getLayoutVersion
|
|
281
|
+
getLayoutVersion(): number {
|
|
233
282
|
return this.layoutVersion
|
|
234
283
|
}
|
|
235
284
|
|
|
236
|
-
hasSection
|
|
285
|
+
hasSection(sectionKey: string): boolean {
|
|
237
286
|
return this.sectionSlots.has(sectionKey)
|
|
238
287
|
}
|
|
239
288
|
|
|
240
|
-
getSectionSlot
|
|
289
|
+
getSectionSlot(sectionKey: string): { start: number; count: number } | undefined {
|
|
241
290
|
return this.sectionSlots.get(sectionKey)
|
|
242
291
|
}
|
|
243
292
|
|
|
244
293
|
/** Fetch fresh each raycast — growCapacity reallocates the backing array. */
|
|
245
|
-
getW0
|
|
294
|
+
getW0(): Uint32Array {
|
|
246
295
|
return this.w0
|
|
247
296
|
}
|
|
248
297
|
|
|
249
298
|
/** Copy live GPU words and remove the section (sci-fi reveal hide / restore). */
|
|
250
|
-
takeSectionData
|
|
299
|
+
takeSectionData(sectionKey: string): GlobalBlockBufferShaderData | undefined {
|
|
251
300
|
const slot = this.sectionSlots.get(sectionKey)
|
|
252
301
|
if (!slot) return undefined
|
|
253
302
|
|
|
@@ -265,20 +314,19 @@ export class GlobalBlockBuffer {
|
|
|
265
314
|
return { words, count: slot.count }
|
|
266
315
|
}
|
|
267
316
|
|
|
268
|
-
removeSection
|
|
317
|
+
removeSection(sectionKey: string): void {
|
|
269
318
|
const slot = this.sectionSlots.get(sectionKey)
|
|
270
319
|
if (!slot) return
|
|
271
320
|
|
|
321
|
+
if (this.pendingReplace.has(sectionKey)) {
|
|
322
|
+
const pr = this.pendingReplace.get(sectionKey)!
|
|
323
|
+
this.zeroAndFreeSlot(pr.oldStart, pr.oldCount)
|
|
324
|
+
this.pendingReplace.delete(sectionKey)
|
|
325
|
+
}
|
|
326
|
+
|
|
272
327
|
if (this.pendingMove?.key === sectionKey) {
|
|
273
328
|
const { oldStart, count } = this.pendingMove
|
|
274
|
-
|
|
275
|
-
this.w0[i] = 0
|
|
276
|
-
this.w1[i] = 0
|
|
277
|
-
this.w2[i] = EMPTY_W2
|
|
278
|
-
this.w3[i] = 0
|
|
279
|
-
}
|
|
280
|
-
this.markDirty(oldStart, oldStart + count - 1)
|
|
281
|
-
this.insertFreeSlot({ start: oldStart, count })
|
|
329
|
+
this.zeroAndFreeSlot(oldStart, count)
|
|
282
330
|
this.pendingMove = null
|
|
283
331
|
}
|
|
284
332
|
|
|
@@ -298,7 +346,7 @@ export class GlobalBlockBuffer {
|
|
|
298
346
|
}
|
|
299
347
|
|
|
300
348
|
/** One interior-hole move per frame when fragmentation exceeds threshold; deferred shrink. */
|
|
301
|
-
compactStep
|
|
349
|
+
compactStep(): void {
|
|
302
350
|
if (this.pendingMove) {
|
|
303
351
|
const { newStart, count } = this.pendingMove
|
|
304
352
|
if (this.rangeFullyUploaded(newStart, newStart + count - 1)) {
|
|
@@ -307,6 +355,14 @@ export class GlobalBlockBuffer {
|
|
|
307
355
|
return
|
|
308
356
|
}
|
|
309
357
|
|
|
358
|
+
for (const key of [...this.pendingReplace.keys()]) {
|
|
359
|
+
const slot = this.sectionSlots.get(key)
|
|
360
|
+
if (!slot) continue
|
|
361
|
+
if (this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) {
|
|
362
|
+
this.finalizePendingReplace(key)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
310
366
|
if (this.highWatermark === 0) return
|
|
311
367
|
const interiorFree = this.interiorFreeFaces()
|
|
312
368
|
if (interiorFree / this.highWatermark <= FRAGMENTATION_THRESHOLD) return
|
|
@@ -328,7 +384,7 @@ export class GlobalBlockBuffer {
|
|
|
328
384
|
this.layoutVersion++
|
|
329
385
|
}
|
|
330
386
|
|
|
331
|
-
uploadDirtyRange
|
|
387
|
+
uploadDirtyRange(): void {
|
|
332
388
|
const r = this.pendingRanges[0]
|
|
333
389
|
if (!r) return
|
|
334
390
|
|
|
@@ -343,11 +399,15 @@ export class GlobalBlockBuffer {
|
|
|
343
399
|
attr.needsUpdate = true
|
|
344
400
|
}
|
|
345
401
|
|
|
346
|
-
if (offset + count > r.end)
|
|
347
|
-
|
|
402
|
+
if (offset + count > r.end) {
|
|
403
|
+
this.pendingRanges.shift()
|
|
404
|
+
} else {
|
|
405
|
+
r.start = offset + count
|
|
406
|
+
}
|
|
407
|
+
this.uploadEpoch++
|
|
348
408
|
}
|
|
349
409
|
|
|
350
|
-
setCameraOrigin
|
|
410
|
+
setCameraOrigin(renderOrigin: RenderOrigin, x: number, y: number, z: number): void {
|
|
351
411
|
const { originDelta, cameraOriginFrac } = computeCameraRelativeUniforms(renderOrigin, x, y, z)
|
|
352
412
|
const sectionOriginRel = computeSectionOriginRel(renderOrigin)
|
|
353
413
|
const u = this.mesh.material.uniforms.u_originDelta
|
|
@@ -364,12 +424,14 @@ export class GlobalBlockBuffer {
|
|
|
364
424
|
}
|
|
365
425
|
}
|
|
366
426
|
|
|
367
|
-
reset
|
|
427
|
+
reset(): void {
|
|
368
428
|
this.sectionSlots.clear()
|
|
369
429
|
this.freeList.length = 0
|
|
370
430
|
this.highWatermark = 0
|
|
371
431
|
this.pendingRanges.length = 0
|
|
372
432
|
this.pendingMove = null
|
|
433
|
+
this.pendingReplace.clear()
|
|
434
|
+
this.uploadEpoch = 0
|
|
373
435
|
this.visibleSpans = []
|
|
374
436
|
this.w0.fill(0)
|
|
375
437
|
this.w1.fill(0)
|
|
@@ -379,14 +441,14 @@ export class GlobalBlockBuffer {
|
|
|
379
441
|
this.invalidateTierCVao()
|
|
380
442
|
}
|
|
381
443
|
|
|
382
|
-
dispose
|
|
444
|
+
dispose(): void {
|
|
383
445
|
this.invalidateTierCVao()
|
|
384
446
|
this.mesh.parent?.remove(this.mesh)
|
|
385
447
|
this.mesh.geometry.dispose()
|
|
386
448
|
this.reset()
|
|
387
449
|
}
|
|
388
450
|
|
|
389
|
-
private invalidateTierCVao
|
|
451
|
+
private invalidateTierCVao(): void {
|
|
390
452
|
if (this.tierCVao && this.tierCGl) {
|
|
391
453
|
this.tierCGl.deleteVertexArray(this.tierCVao)
|
|
392
454
|
}
|
|
@@ -394,12 +456,7 @@ export class GlobalBlockBuffer {
|
|
|
394
456
|
this.tierCAttrs = null
|
|
395
457
|
}
|
|
396
458
|
|
|
397
|
-
private drawTierCSpans
|
|
398
|
-
gl: WebGL2RenderingContext,
|
|
399
|
-
spans: readonly CubeDrawSpan[],
|
|
400
|
-
renderer: THREE.WebGLRenderer,
|
|
401
|
-
material: THREE.ShaderMaterial,
|
|
402
|
-
): void {
|
|
459
|
+
private drawTierCSpans(gl: WebGL2RenderingContext, spans: readonly CubeDrawSpan[], renderer: THREE.WebGLRenderer, material: THREE.ShaderMaterial): void {
|
|
403
460
|
this.ensureTierCVao(gl, renderer, material)
|
|
404
461
|
if (!this.tierCVao || !this.tierCAttrs) return
|
|
405
462
|
|
|
@@ -420,11 +477,7 @@ export class GlobalBlockBuffer {
|
|
|
420
477
|
gl.bindVertexArray(null)
|
|
421
478
|
}
|
|
422
479
|
|
|
423
|
-
private ensureTierCVao
|
|
424
|
-
gl: WebGL2RenderingContext,
|
|
425
|
-
renderer: THREE.WebGLRenderer,
|
|
426
|
-
material: THREE.ShaderMaterial,
|
|
427
|
-
): void {
|
|
480
|
+
private ensureTierCVao(gl: WebGL2RenderingContext, renderer: THREE.WebGLRenderer, material: THREE.ShaderMaterial): void {
|
|
428
481
|
this.tierCGl = gl
|
|
429
482
|
const internals = renderer as WebGLRendererInternals
|
|
430
483
|
const materialProps = internals.properties.get(material) as { currentProgram: { program: WebGLProgram } }
|
|
@@ -474,19 +527,19 @@ export class GlobalBlockBuffer {
|
|
|
474
527
|
w0: { loc: w0Loc, buffer: w0Buf },
|
|
475
528
|
w1: { loc: w1Loc, buffer: w1Buf },
|
|
476
529
|
w2: { loc: w2Loc, buffer: w2Buf },
|
|
477
|
-
w3: { loc: w3Loc, buffer: w3Buf }
|
|
530
|
+
w3: { loc: w3Loc, buffer: w3Buf }
|
|
478
531
|
}
|
|
479
532
|
}
|
|
480
533
|
|
|
481
|
-
private markDirty
|
|
534
|
+
private markDirty(start: number, end: number): void {
|
|
482
535
|
this.pendingRanges.push({ start, end })
|
|
483
536
|
this.pendingRanges.sort((a, b) => a.start - b.start)
|
|
484
537
|
this.mergePendingRanges()
|
|
485
538
|
}
|
|
486
539
|
|
|
487
|
-
private mergePendingRanges
|
|
540
|
+
private mergePendingRanges(): void {
|
|
488
541
|
if (this.pendingRanges.length < 2) return
|
|
489
|
-
const merged: Array<{ start: number
|
|
542
|
+
const merged: Array<{ start: number; end: number }> = []
|
|
490
543
|
let cur = this.pendingRanges[0]!
|
|
491
544
|
for (let i = 1; i < this.pendingRanges.length; i++) {
|
|
492
545
|
const next = this.pendingRanges[i]!
|
|
@@ -501,7 +554,7 @@ export class GlobalBlockBuffer {
|
|
|
501
554
|
this.pendingRanges = merged
|
|
502
555
|
}
|
|
503
556
|
|
|
504
|
-
private takeFreeSlot
|
|
557
|
+
private takeFreeSlot(count: number): { start: number; count: number } | undefined {
|
|
505
558
|
for (let i = 0; i < this.freeList.length; i++) {
|
|
506
559
|
const slot = this.freeList[i]!
|
|
507
560
|
if (slot.count >= count) {
|
|
@@ -515,15 +568,15 @@ export class GlobalBlockBuffer {
|
|
|
515
568
|
return undefined
|
|
516
569
|
}
|
|
517
570
|
|
|
518
|
-
private insertFreeSlot
|
|
571
|
+
private insertFreeSlot(slot: { start: number; count: number }): void {
|
|
519
572
|
this.freeList.push(slot)
|
|
520
573
|
this.freeList.sort((a, b) => a.start - b.start)
|
|
521
574
|
this.mergeFreeList()
|
|
522
575
|
}
|
|
523
576
|
|
|
524
|
-
private mergeFreeList
|
|
577
|
+
private mergeFreeList(): void {
|
|
525
578
|
if (this.freeList.length < 2) return
|
|
526
|
-
const merged: Array<{ start: number
|
|
579
|
+
const merged: Array<{ start: number; count: number }> = []
|
|
527
580
|
let cur = this.freeList[0]!
|
|
528
581
|
for (let i = 1; i < this.freeList.length; i++) {
|
|
529
582
|
const next = this.freeList[i]!
|
|
@@ -538,7 +591,7 @@ export class GlobalBlockBuffer {
|
|
|
538
591
|
this.freeList = merged
|
|
539
592
|
}
|
|
540
593
|
|
|
541
|
-
private interiorFreeFaces
|
|
594
|
+
private interiorFreeFaces(): number {
|
|
542
595
|
let total = 0
|
|
543
596
|
for (const slot of this.freeList) {
|
|
544
597
|
if (slot.start < this.highWatermark) total += slot.count
|
|
@@ -546,8 +599,8 @@ export class GlobalBlockBuffer {
|
|
|
546
599
|
return total
|
|
547
600
|
}
|
|
548
601
|
|
|
549
|
-
private findMovableSection
|
|
550
|
-
const sections: Array<{ key: string
|
|
602
|
+
private findMovableSection(maxCount: number): { key: string; start: number; count: number } | undefined {
|
|
603
|
+
const sections: Array<{ key: string; start: number; count: number }> = []
|
|
551
604
|
for (const [key, slot] of this.sectionSlots) {
|
|
552
605
|
sections.push({ key, start: slot.start, count: slot.count })
|
|
553
606
|
}
|
|
@@ -555,7 +608,7 @@ export class GlobalBlockBuffer {
|
|
|
555
608
|
|
|
556
609
|
sections.sort((a, b) => {
|
|
557
610
|
if (b.start !== a.start) return b.start - a.start
|
|
558
|
-
return
|
|
611
|
+
return b.start + b.count - (a.start + a.count)
|
|
559
612
|
})
|
|
560
613
|
|
|
561
614
|
const tailmost = sections[0]!
|
|
@@ -563,9 +616,7 @@ export class GlobalBlockBuffer {
|
|
|
563
616
|
return tailmost
|
|
564
617
|
}
|
|
565
618
|
|
|
566
|
-
const candidates = sections
|
|
567
|
-
.filter(s => s.count <= maxCount)
|
|
568
|
-
.sort((a, b) => b.count - a.count)
|
|
619
|
+
const candidates = sections.filter(s => s.count <= maxCount).sort((a, b) => b.count - a.count)
|
|
569
620
|
|
|
570
621
|
for (const s of candidates) {
|
|
571
622
|
if (this.findLowestInteriorHole(s.start, s.count)) return s
|
|
@@ -573,10 +624,7 @@ export class GlobalBlockBuffer {
|
|
|
573
624
|
return undefined
|
|
574
625
|
}
|
|
575
626
|
|
|
576
|
-
private findLowestInteriorHole
|
|
577
|
-
sectionStart: number,
|
|
578
|
-
count: number,
|
|
579
|
-
): { start: number, count: number, index: number } | undefined {
|
|
627
|
+
private findLowestInteriorHole(sectionStart: number, count: number): { start: number; count: number; index: number } | undefined {
|
|
580
628
|
for (let i = 0; i < this.freeList.length; i++) {
|
|
581
629
|
const slot = this.freeList[i]!
|
|
582
630
|
if (slot.start < sectionStart && slot.count >= count) {
|
|
@@ -586,7 +634,7 @@ export class GlobalBlockBuffer {
|
|
|
586
634
|
return undefined
|
|
587
635
|
}
|
|
588
636
|
|
|
589
|
-
private reserveFreeSlotAt
|
|
637
|
+
private reserveFreeSlotAt(index: number, count: number): { start: number; count: number } {
|
|
590
638
|
const slot = this.freeList[index]!
|
|
591
639
|
this.freeList.splice(index, 1)
|
|
592
640
|
if (slot.count === count) return { start: slot.start, count }
|
|
@@ -595,41 +643,57 @@ export class GlobalBlockBuffer {
|
|
|
595
643
|
return used
|
|
596
644
|
}
|
|
597
645
|
|
|
598
|
-
private copySectionRange
|
|
646
|
+
private copySectionRange(oldStart: number, newStart: number, count: number): void {
|
|
599
647
|
this.w0.copyWithin(newStart, oldStart, oldStart + count)
|
|
600
648
|
this.w1.copyWithin(newStart, oldStart, oldStart + count)
|
|
601
649
|
this.w2.copyWithin(newStart, oldStart, oldStart + count)
|
|
602
650
|
this.w3.copyWithin(newStart, oldStart, oldStart + count)
|
|
603
651
|
}
|
|
604
652
|
|
|
605
|
-
private rangeFullyUploaded
|
|
653
|
+
private rangeFullyUploaded(start: number, end: number): boolean {
|
|
606
654
|
for (const r of this.pendingRanges) {
|
|
607
655
|
if (r.start <= end && r.end >= start) return false
|
|
608
656
|
}
|
|
609
657
|
return true
|
|
610
658
|
}
|
|
611
659
|
|
|
612
|
-
private
|
|
613
|
-
|
|
614
|
-
if (!move) return
|
|
615
|
-
|
|
616
|
-
const { oldStart, count } = move
|
|
617
|
-
for (let i = oldStart; i < oldStart + count; i++) {
|
|
660
|
+
private zeroAndFreeSlot(start: number, count: number): void {
|
|
661
|
+
for (let i = start; i < start + count; i++) {
|
|
618
662
|
this.w0[i] = 0
|
|
619
663
|
this.w1[i] = 0
|
|
620
664
|
this.w2[i] = EMPTY_W2
|
|
621
665
|
this.w3[i] = 0
|
|
622
666
|
}
|
|
623
|
-
this.
|
|
667
|
+
this.markDirty(start, start + count - 1)
|
|
668
|
+
this.insertFreeSlot({ start, count })
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private finalizePendingReplace(key: string): void {
|
|
672
|
+
const pr = this.pendingReplace.get(key)
|
|
673
|
+
if (!pr) return
|
|
674
|
+
|
|
675
|
+
this.zeroAndFreeSlot(pr.oldStart, pr.oldCount)
|
|
676
|
+
this.pendingReplace.delete(key)
|
|
677
|
+
this.shrinkHighWatermark()
|
|
678
|
+
this.mesh.geometry.instanceCount = this.highWatermark
|
|
679
|
+
this.layoutVersion++
|
|
680
|
+
this.uploadEpoch++
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
private finalizePendingMove(): void {
|
|
684
|
+
const move = this.pendingMove
|
|
685
|
+
if (!move) return
|
|
686
|
+
|
|
687
|
+
const { oldStart, count } = move
|
|
688
|
+
this.zeroAndFreeSlot(oldStart, count)
|
|
624
689
|
this.shrinkHighWatermark()
|
|
625
|
-
if (oldStart < this.highWatermark) {
|
|
626
|
-
this.markDirty(oldStart, oldStart + count - 1)
|
|
627
|
-
}
|
|
628
690
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
629
691
|
this.pendingMove = null
|
|
692
|
+
this.layoutVersion++
|
|
693
|
+
this.uploadEpoch++
|
|
630
694
|
}
|
|
631
695
|
|
|
632
|
-
private shrinkHighWatermark
|
|
696
|
+
private shrinkHighWatermark(): void {
|
|
633
697
|
while (this.highWatermark > 0) {
|
|
634
698
|
const tail = this.highWatermark - 1
|
|
635
699
|
const free = this.freeList.find(s => s.start <= tail && s.start + s.count > tail)
|
|
@@ -640,9 +704,12 @@ export class GlobalBlockBuffer {
|
|
|
640
704
|
}
|
|
641
705
|
}
|
|
642
706
|
|
|
643
|
-
private growCapacity
|
|
707
|
+
private growCapacity(minFaces: number): void {
|
|
644
708
|
// Moved CPU data at newStart survives nw*.set(); pendingRanges cleared below anyway.
|
|
645
709
|
if (this.pendingMove) this.finalizePendingMove()
|
|
710
|
+
for (const key of [...this.pendingReplace.keys()]) {
|
|
711
|
+
this.finalizePendingReplace(key)
|
|
712
|
+
}
|
|
646
713
|
|
|
647
714
|
console.warn('[globalBlockBuffer] growing faces', this.capacityFaces, '->', '(need', minFaces, ')')
|
|
648
715
|
let newCap = this.capacityFaces
|
|
@@ -671,7 +738,7 @@ export class GlobalBlockBuffer {
|
|
|
671
738
|
if (prev) {
|
|
672
739
|
geometry.deleteAttribute(name)
|
|
673
740
|
if ('dispose' in prev && typeof (prev as { dispose?: () => void }).dispose === 'function') {
|
|
674
|
-
(prev as { dispose: () => void }).dispose()
|
|
741
|
+
;(prev as { dispose: () => void }).dispose()
|
|
675
742
|
}
|
|
676
743
|
}
|
|
677
744
|
const attr = new THREE.InstancedBufferAttribute(arr, 1)
|