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