minecraft-renderer 0.1.107 → 0.1.109
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/dist/lightOwnerWorker.js +9 -0
- package/dist/lightOwnerWorker.js.map +7 -0
- package/dist/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +222 -222
- package/dist/minecraft-renderer.js +84 -84
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +517 -517
- package/package.json +1 -1
- package/src/bundler/bundlePrepare.test.ts +25 -0
- package/src/bundler/bundlePrepare.ts +1 -1
- package/src/graphicsBackend/appViewer.ts +15 -0
- package/src/graphicsBackend/config.ts +5 -0
- package/src/graphicsBackend/rendererDefaultOptions.test.ts +51 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +13 -1
- package/src/graphicsBackend/rendererOptionsSync.ts +3 -3
- package/src/lib/blockEntityLightRegistry.ts +4 -0
- package/src/lib/worldrendererCommon.lightOwner.test.ts +916 -0
- package/src/lib/worldrendererCommon.removeColumn.test.ts +16 -0
- package/src/lib/worldrendererCommon.ts +329 -67
- package/src/lighting/cache/rendererLightCache.test.ts +158 -0
- package/src/lighting/cache/rendererLightCache.ts +248 -0
- package/src/lighting/coordinator/clientLightCoordinator.test.ts +102 -0
- package/src/lighting/coordinator/clientLightCoordinator.ts +574 -0
- package/src/lighting/debug/clientLightProbe.test.ts +24 -0
- package/src/lighting/debug/clientLightProbe.ts +20 -0
- package/src/lighting/debug/clientLightTrace.test.ts +271 -0
- package/src/lighting/debug/clientLightTrace.ts +335 -0
- package/src/lighting/entity/entityLightController.test.ts +216 -0
- package/src/lighting/entity/entityLightController.ts +322 -0
- package/src/lighting/owner/clientLightOwner.test.ts +467 -0
- package/src/lighting/owner/clientLightOwner.ts +523 -0
- package/src/lighting/owner/lightOwnerHost.test.ts +78 -0
- package/src/lighting/owner/lightOwnerHost.ts +202 -0
- package/src/lighting/owner/lightOwnerWorker.ts +154 -0
- package/src/lighting/owner/updateLight1171OwnerPath.test.ts +68 -0
- package/src/lighting/owner/updateLightToOwnerEvents.ts +49 -0
- package/src/lighting/tables/lightTables1171.generated.json +10 -0
- package/src/lighting/tables/lightTables1171.test.ts +168 -0
- package/src/lighting/tables/lightTables1171.ts +59 -0
- package/src/lighting/versioning/clientLightVersions.test.ts +241 -0
- package/src/lighting/versioning/clientLightVersions.ts +203 -0
- package/src/lighting/versioning/pendingSectionFlush.test.ts +214 -0
- package/src/lighting/versioning/pendingSectionFlush.ts +123 -0
- package/src/mesher-shared/lightNibblePack.ts +26 -0
- package/src/mesher-shared/mcDataWorkerShim.cjs +9 -7
- package/src/mesher-shared/modelsGeometryCommon.test.ts +6 -1
- package/src/mesher-shared/shared.ts +48 -1
- package/src/three/chunkMeshManager.ts +26 -0
- package/src/three/cloudCover.ts +22 -0
- package/src/three/cloudGeometry.ts +71 -0
- package/src/three/entities.ts +77 -11
- package/src/three/entity/EntityMesh.ts +1 -0
- package/src/three/entity/legWalkAnimation.test.ts +11 -2
- package/src/three/entity/legWalkAnimation.ts +1 -9
- package/src/three/globalBlockBuffer.ts +94 -2
- package/src/three/globalLegacyBuffer.ts +94 -2
- package/src/three/graphicsBackendBase.ts +2 -9
- package/src/three/modules/clouds.ts +98 -75
- package/src/three/tests/cloudGeometry.test.ts +87 -0
- package/src/three/tests/clouds.test.ts +65 -14
- package/src/three/tests/globalBlockBuffer.test.ts +98 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +108 -0
- package/src/three/worldRendererThree.ts +75 -34
- package/src/wasm-mesher/bridge/render-from-wasm.ts +46 -1
- package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +31 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher.js +180 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +11 -0
- package/src/wasm-mesher/tests/mesherLightDependencies.test.ts +83 -0
- package/src/wasm-mesher/tests/mesherWasmColumnTickSelect.test.ts +39 -0
- package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +30 -1
- package/src/wasm-mesher/tests/mesherWasmTickParseCache.test.ts +73 -0
- package/src/wasm-mesher/tests/mesherWasmTickSchedule.test.ts +51 -0
- package/src/wasm-mesher/tests/mesherWasmTopologyPostCache.test.ts +78 -0
- package/src/wasm-mesher/worker/clientLightDarkPublication.probe.test.ts +32 -0
- package/src/wasm-mesher/worker/mesherLightDependencies.ts +43 -0
- package/src/wasm-mesher/worker/mesherWasm.ownerLightSync.test.ts +198 -0
- package/src/wasm-mesher/worker/mesherWasm.ts +506 -96
- package/src/wasm-mesher/worker/mesherWasmChunkBuffer.ts +1 -0
- package/src/wasm-mesher/worker/mesherWasmColumnTickSelect.ts +47 -0
- package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +16 -5
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +17 -1
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +10 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.test.ts +208 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.ts +228 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.test.ts +161 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.ts +112 -0
- package/src/wasm-mesher/worker/mesherWasmTickParseCache.ts +45 -0
- package/src/wasm-mesher/worker/mesherWasmTickSchedule.ts +44 -0
- package/src/wasm-mesher/worker/mesherWasmTopologyPostCache.ts +63 -0
- package/src/worldView/types.ts +1 -0
- package/src/worldView/worldView.geometryReload.test.ts +68 -0
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +46 -1
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { VERTICES_PER_FACE, computeSectionOriginRel } from './shaders/cubeBlockShader'
|
|
13
13
|
import { computeCameraRelativeUniforms, type RenderOrigin } from './shaders/legacyBlockShader'
|
|
14
14
|
import { packWord2Empty } from '../wasm-mesher/bridge/shaderCubeBridge'
|
|
15
|
+
import { isClientLightTraceEnabled, recordClientLightTrace, recordClientLightTraceGpuSample } from '../lighting/debug/clientLightTrace'
|
|
15
16
|
|
|
16
17
|
type WebGLRendererInternals = THREE.WebGLRenderer & {
|
|
17
18
|
properties: {
|
|
@@ -162,9 +163,9 @@ export class GlobalBlockBuffer {
|
|
|
162
163
|
getSectionDrawStart(sectionKey: string): number | undefined {
|
|
163
164
|
const slot = this.sectionSlots.get(sectionKey)
|
|
164
165
|
if (!slot) return undefined
|
|
165
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
166
166
|
const replace = this.pendingReplace.get(sectionKey)
|
|
167
167
|
if (replace) return replace.oldStart
|
|
168
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
168
169
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
169
170
|
return slot.start
|
|
170
171
|
}
|
|
@@ -172,13 +173,37 @@ export class GlobalBlockBuffer {
|
|
|
172
173
|
getSectionDrawCount(sectionKey: string): number | undefined {
|
|
173
174
|
const slot = this.sectionSlots.get(sectionKey)
|
|
174
175
|
if (!slot) return undefined
|
|
175
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
176
176
|
const replace = this.pendingReplace.get(sectionKey)
|
|
177
177
|
if (replace) return replace.oldCount
|
|
178
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
178
179
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
179
180
|
return slot.count
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
getGpuSlotTrace(sectionKey: string):
|
|
184
|
+
| {
|
|
185
|
+
displayedStart?: number
|
|
186
|
+
displayedCount?: number
|
|
187
|
+
candidateStart: number
|
|
188
|
+
candidateCount: number
|
|
189
|
+
pendingReplace: boolean
|
|
190
|
+
pendingMove: boolean
|
|
191
|
+
unuploadedRanges: number
|
|
192
|
+
}
|
|
193
|
+
| undefined {
|
|
194
|
+
const slot = this.sectionSlots.get(sectionKey)
|
|
195
|
+
if (!slot) return undefined
|
|
196
|
+
return {
|
|
197
|
+
displayedStart: this.getSectionDrawStart(sectionKey),
|
|
198
|
+
displayedCount: this.getSectionDrawCount(sectionKey),
|
|
199
|
+
candidateStart: slot.start,
|
|
200
|
+
candidateCount: slot.count,
|
|
201
|
+
pendingReplace: this.pendingReplace.has(sectionKey),
|
|
202
|
+
pendingMove: this.pendingMove?.key === sectionKey,
|
|
203
|
+
unuploadedRanges: this.pendingRanges.length
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
182
207
|
getUploadEpoch(): number {
|
|
183
208
|
return this.uploadEpoch
|
|
184
209
|
}
|
|
@@ -282,6 +307,20 @@ export class GlobalBlockBuffer {
|
|
|
282
307
|
this.markDirty(slot.start, slot.start + faceCount - 1)
|
|
283
308
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
284
309
|
this.layoutVersion++
|
|
310
|
+
if (isClientLightTraceEnabled()) {
|
|
311
|
+
const gpu = this.getGpuSlotTrace(sectionKey)
|
|
312
|
+
recordClientLightTrace({
|
|
313
|
+
phase: 'gpuStaged',
|
|
314
|
+
sectionKey,
|
|
315
|
+
displayedStart: gpu?.displayedStart,
|
|
316
|
+
displayedCount: gpu?.displayedCount,
|
|
317
|
+
candidateStart: gpu?.candidateStart,
|
|
318
|
+
candidateCount: gpu?.candidateCount,
|
|
319
|
+
pendingReplace: gpu?.pendingReplace,
|
|
320
|
+
pendingMove: gpu?.pendingMove,
|
|
321
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
322
|
+
})
|
|
323
|
+
}
|
|
285
324
|
}
|
|
286
325
|
|
|
287
326
|
getLayoutVersion(): number {
|
|
@@ -388,6 +427,20 @@ export class GlobalBlockBuffer {
|
|
|
388
427
|
this.markDirty(newStart, newStart + section.count - 1)
|
|
389
428
|
this.pendingMove = { key: section.key, oldStart, newStart, count: section.count }
|
|
390
429
|
this.layoutVersion++
|
|
430
|
+
if (isClientLightTraceEnabled()) {
|
|
431
|
+
const gpu = this.getGpuSlotTrace(section.key)
|
|
432
|
+
recordClientLightTrace({
|
|
433
|
+
phase: 'gpuStaged',
|
|
434
|
+
sectionKey: section.key,
|
|
435
|
+
displayedStart: gpu?.displayedStart,
|
|
436
|
+
displayedCount: gpu?.displayedCount,
|
|
437
|
+
candidateStart: gpu?.candidateStart,
|
|
438
|
+
candidateCount: gpu?.candidateCount,
|
|
439
|
+
pendingReplace: gpu?.pendingReplace,
|
|
440
|
+
pendingMove: true,
|
|
441
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
442
|
+
})
|
|
443
|
+
}
|
|
391
444
|
}
|
|
392
445
|
|
|
393
446
|
uploadDirtyRange(): void {
|
|
@@ -411,6 +464,13 @@ export class GlobalBlockBuffer {
|
|
|
411
464
|
r.start = offset + count
|
|
412
465
|
}
|
|
413
466
|
this.uploadEpoch++
|
|
467
|
+
if (isClientLightTraceEnabled()) {
|
|
468
|
+
recordClientLightTraceGpuSample('gpuUploaded', this.highWatermark, () => ({
|
|
469
|
+
phase: 'gpuUploaded',
|
|
470
|
+
unuploadedRanges: this.pendingRanges.length,
|
|
471
|
+
drawableFaces: this.highWatermark
|
|
472
|
+
}))
|
|
473
|
+
}
|
|
414
474
|
}
|
|
415
475
|
|
|
416
476
|
setCameraOrigin(renderOrigin: RenderOrigin, x: number, y: number, z: number): void {
|
|
@@ -608,6 +668,9 @@ export class GlobalBlockBuffer {
|
|
|
608
668
|
private findMovableSection(maxCount: number): { key: string; start: number; count: number } | undefined {
|
|
609
669
|
const sections: Array<{ key: string; start: number; count: number }> = []
|
|
610
670
|
for (const [key, slot] of this.sectionSlots) {
|
|
671
|
+
if (this.pendingReplace.has(key)) continue
|
|
672
|
+
if (this.pendingMove?.key === key) continue
|
|
673
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) continue
|
|
611
674
|
sections.push({ key, start: slot.start, count: slot.count })
|
|
612
675
|
}
|
|
613
676
|
if (sections.length === 0) return undefined
|
|
@@ -684,11 +747,26 @@ export class GlobalBlockBuffer {
|
|
|
684
747
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
685
748
|
this.layoutVersion++
|
|
686
749
|
this.uploadEpoch++
|
|
750
|
+
if (isClientLightTraceEnabled()) {
|
|
751
|
+
const gpu = this.getGpuSlotTrace(key)
|
|
752
|
+
recordClientLightTrace({
|
|
753
|
+
phase: 'gpuCommitted',
|
|
754
|
+
sectionKey: key,
|
|
755
|
+
displayedStart: gpu?.displayedStart,
|
|
756
|
+
displayedCount: gpu?.displayedCount,
|
|
757
|
+
candidateStart: gpu?.candidateStart,
|
|
758
|
+
candidateCount: gpu?.candidateCount,
|
|
759
|
+
pendingReplace: false,
|
|
760
|
+
pendingMove: gpu?.pendingMove,
|
|
761
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
762
|
+
})
|
|
763
|
+
}
|
|
687
764
|
}
|
|
688
765
|
|
|
689
766
|
private finalizePendingMove(): void {
|
|
690
767
|
const move = this.pendingMove
|
|
691
768
|
if (!move) return
|
|
769
|
+
const movedKey = move.key
|
|
692
770
|
|
|
693
771
|
const { oldStart, count } = move
|
|
694
772
|
this.zeroAndFreeSlot(oldStart, count)
|
|
@@ -697,6 +775,20 @@ export class GlobalBlockBuffer {
|
|
|
697
775
|
this.pendingMove = null
|
|
698
776
|
this.layoutVersion++
|
|
699
777
|
this.uploadEpoch++
|
|
778
|
+
if (isClientLightTraceEnabled()) {
|
|
779
|
+
const gpu = this.getGpuSlotTrace(movedKey)
|
|
780
|
+
recordClientLightTrace({
|
|
781
|
+
phase: 'gpuCommitted',
|
|
782
|
+
sectionKey: movedKey,
|
|
783
|
+
displayedStart: gpu?.displayedStart,
|
|
784
|
+
displayedCount: gpu?.displayedCount,
|
|
785
|
+
candidateStart: gpu?.candidateStart,
|
|
786
|
+
candidateCount: gpu?.candidateCount,
|
|
787
|
+
pendingReplace: gpu?.pendingReplace,
|
|
788
|
+
pendingMove: false,
|
|
789
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
790
|
+
})
|
|
791
|
+
}
|
|
700
792
|
}
|
|
701
793
|
|
|
702
794
|
private shrinkHighWatermark(): void {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type LegacyMultiDrawScratch
|
|
11
11
|
} from './legacyMultiDraw'
|
|
12
12
|
import { computeCameraRelativeUniforms, type RenderOrigin } from './shaders/legacyBlockShader'
|
|
13
|
+
import { isClientLightTraceEnabled, recordClientLightTrace, recordClientLightTraceGpuSample } from '../lighting/debug/clientLightTrace'
|
|
13
14
|
|
|
14
15
|
const VERTS_PER_QUAD = 4
|
|
15
16
|
const INDICES_PER_QUAD = 6
|
|
@@ -337,6 +338,20 @@ export class GlobalLegacyBuffer {
|
|
|
337
338
|
this.markDirty(slot.start, slot.start + quadCount - 1)
|
|
338
339
|
this.syncDefaultDrawGroups()
|
|
339
340
|
this.layoutVersion++
|
|
341
|
+
if (isClientLightTraceEnabled()) {
|
|
342
|
+
const gpu = this.getGpuSlotTrace(sectionKey)
|
|
343
|
+
recordClientLightTrace({
|
|
344
|
+
phase: 'gpuStaged',
|
|
345
|
+
sectionKey,
|
|
346
|
+
displayedStart: gpu?.displayedStart,
|
|
347
|
+
displayedCount: gpu?.displayedCount,
|
|
348
|
+
candidateStart: gpu?.candidateStart,
|
|
349
|
+
candidateCount: gpu?.candidateCount,
|
|
350
|
+
pendingReplace: gpu?.pendingReplace,
|
|
351
|
+
pendingMove: gpu?.pendingMove,
|
|
352
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
353
|
+
})
|
|
354
|
+
}
|
|
340
355
|
return true
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -367,9 +382,9 @@ export class GlobalLegacyBuffer {
|
|
|
367
382
|
getSectionDrawStart(sectionKey: string): number | undefined {
|
|
368
383
|
const slot = this.sectionSlots.get(sectionKey)
|
|
369
384
|
if (!slot) return undefined
|
|
370
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
371
385
|
const replace = this.pendingReplace.get(sectionKey)
|
|
372
386
|
if (replace) return replace.oldStart
|
|
387
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
373
388
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
374
389
|
return slot.start
|
|
375
390
|
}
|
|
@@ -377,13 +392,37 @@ export class GlobalLegacyBuffer {
|
|
|
377
392
|
getSectionDrawCount(sectionKey: string): number | undefined {
|
|
378
393
|
const slot = this.sectionSlots.get(sectionKey)
|
|
379
394
|
if (!slot) return undefined
|
|
380
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
381
395
|
const replace = this.pendingReplace.get(sectionKey)
|
|
382
396
|
if (replace) return replace.oldCount
|
|
397
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
383
398
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
384
399
|
return slot.count
|
|
385
400
|
}
|
|
386
401
|
|
|
402
|
+
getGpuSlotTrace(sectionKey: string):
|
|
403
|
+
| {
|
|
404
|
+
displayedStart?: number
|
|
405
|
+
displayedCount?: number
|
|
406
|
+
candidateStart: number
|
|
407
|
+
candidateCount: number
|
|
408
|
+
pendingReplace: boolean
|
|
409
|
+
pendingMove: boolean
|
|
410
|
+
unuploadedRanges: number
|
|
411
|
+
}
|
|
412
|
+
| undefined {
|
|
413
|
+
const slot = this.sectionSlots.get(sectionKey)
|
|
414
|
+
if (!slot) return undefined
|
|
415
|
+
return {
|
|
416
|
+
displayedStart: this.getSectionDrawStart(sectionKey),
|
|
417
|
+
displayedCount: this.getSectionDrawCount(sectionKey),
|
|
418
|
+
candidateStart: slot.start,
|
|
419
|
+
candidateCount: slot.count,
|
|
420
|
+
pendingReplace: this.pendingReplace.has(sectionKey),
|
|
421
|
+
pendingMove: this.pendingMove?.key === sectionKey,
|
|
422
|
+
unuploadedRanges: this.pendingRanges.length
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
387
426
|
getPendingMove(): PendingMove | null {
|
|
388
427
|
return this.pendingMove
|
|
389
428
|
}
|
|
@@ -425,6 +464,20 @@ export class GlobalLegacyBuffer {
|
|
|
425
464
|
this.markDirty(newStart, newStart + section.count - 1)
|
|
426
465
|
this.pendingMove = { key: section.key, oldStart, newStart, count: section.count }
|
|
427
466
|
this.layoutVersion++
|
|
467
|
+
if (isClientLightTraceEnabled()) {
|
|
468
|
+
const gpu = this.getGpuSlotTrace(section.key)
|
|
469
|
+
recordClientLightTrace({
|
|
470
|
+
phase: 'gpuStaged',
|
|
471
|
+
sectionKey: section.key,
|
|
472
|
+
displayedStart: gpu?.displayedStart,
|
|
473
|
+
displayedCount: gpu?.displayedCount,
|
|
474
|
+
candidateStart: gpu?.candidateStart,
|
|
475
|
+
candidateCount: gpu?.candidateCount,
|
|
476
|
+
pendingReplace: gpu?.pendingReplace,
|
|
477
|
+
pendingMove: true,
|
|
478
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
479
|
+
})
|
|
480
|
+
}
|
|
428
481
|
}
|
|
429
482
|
|
|
430
483
|
updateDrawSpans(visible: VisibleSectionSpan[], mode: 'opaque' | 'sortedBlend'): void {
|
|
@@ -696,6 +749,13 @@ export class GlobalLegacyBuffer {
|
|
|
696
749
|
r.start = quadOffset + quadCount
|
|
697
750
|
}
|
|
698
751
|
this.uploadEpoch++
|
|
752
|
+
if (isClientLightTraceEnabled()) {
|
|
753
|
+
recordClientLightTraceGpuSample('gpuUploaded', this.highWatermark, () => ({
|
|
754
|
+
phase: 'gpuUploaded',
|
|
755
|
+
unuploadedRanges: this.pendingRanges.length,
|
|
756
|
+
drawableFaces: this.highWatermark
|
|
757
|
+
}))
|
|
758
|
+
}
|
|
699
759
|
}
|
|
700
760
|
|
|
701
761
|
uploadDirtyIndexRange(): void {
|
|
@@ -898,6 +958,9 @@ export class GlobalLegacyBuffer {
|
|
|
898
958
|
private findMovableSection(maxCount: number): { key: string; start: number; count: number } | undefined {
|
|
899
959
|
const sections: Array<{ key: string; start: number; count: number }> = []
|
|
900
960
|
for (const [key, slot] of this.sectionSlots) {
|
|
961
|
+
if (this.pendingReplace.has(key)) continue
|
|
962
|
+
if (this.pendingMove?.key === key) continue
|
|
963
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) continue
|
|
901
964
|
sections.push({ key, start: slot.start, count: slot.count })
|
|
902
965
|
}
|
|
903
966
|
if (sections.length === 0) return undefined
|
|
@@ -1006,11 +1069,26 @@ export class GlobalLegacyBuffer {
|
|
|
1006
1069
|
this.syncDefaultDrawGroups()
|
|
1007
1070
|
this.layoutVersion++
|
|
1008
1071
|
this.uploadEpoch++
|
|
1072
|
+
if (isClientLightTraceEnabled()) {
|
|
1073
|
+
const gpu = this.getGpuSlotTrace(key)
|
|
1074
|
+
recordClientLightTrace({
|
|
1075
|
+
phase: 'gpuCommitted',
|
|
1076
|
+
sectionKey: key,
|
|
1077
|
+
displayedStart: gpu?.displayedStart,
|
|
1078
|
+
displayedCount: gpu?.displayedCount,
|
|
1079
|
+
candidateStart: gpu?.candidateStart,
|
|
1080
|
+
candidateCount: gpu?.candidateCount,
|
|
1081
|
+
pendingReplace: false,
|
|
1082
|
+
pendingMove: gpu?.pendingMove,
|
|
1083
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
1084
|
+
})
|
|
1085
|
+
}
|
|
1009
1086
|
}
|
|
1010
1087
|
|
|
1011
1088
|
private finalizePendingMove(): void {
|
|
1012
1089
|
const move = this.pendingMove
|
|
1013
1090
|
if (!move) return
|
|
1091
|
+
const movedKey = move.key
|
|
1014
1092
|
|
|
1015
1093
|
const { oldStart, count } = move
|
|
1016
1094
|
this.zeroAndFreeSlot(oldStart, count)
|
|
@@ -1019,6 +1097,20 @@ export class GlobalLegacyBuffer {
|
|
|
1019
1097
|
this.pendingMove = null
|
|
1020
1098
|
this.layoutVersion++
|
|
1021
1099
|
this.uploadEpoch++
|
|
1100
|
+
if (isClientLightTraceEnabled()) {
|
|
1101
|
+
const gpu = this.getGpuSlotTrace(movedKey)
|
|
1102
|
+
recordClientLightTrace({
|
|
1103
|
+
phase: 'gpuCommitted',
|
|
1104
|
+
sectionKey: movedKey,
|
|
1105
|
+
displayedStart: gpu?.displayedStart,
|
|
1106
|
+
displayedCount: gpu?.displayedCount,
|
|
1107
|
+
candidateStart: gpu?.candidateStart,
|
|
1108
|
+
candidateCount: gpu?.candidateCount,
|
|
1109
|
+
pendingReplace: gpu?.pendingReplace,
|
|
1110
|
+
pendingMove: false,
|
|
1111
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
1112
|
+
})
|
|
1113
|
+
}
|
|
1022
1114
|
}
|
|
1023
1115
|
|
|
1024
1116
|
private growCapacity(minQuads: number): void {
|
|
@@ -87,15 +87,8 @@ export const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
|
|
|
87
87
|
},
|
|
88
88
|
feedChunkPacket(payload: FeedChunkPacketPayload) {
|
|
89
89
|
// Forward parsed/raw map_chunk + update_light packets from the
|
|
90
|
-
// web-client to
|
|
91
|
-
|
|
92
|
-
// single recipient); useWorkerProxy still gives zero-copy transfer
|
|
93
|
-
// from main into the off-thread renderer worker for free.
|
|
94
|
-
const { kind, ...rest } = payload
|
|
95
|
-
const message = { type: kind, ...rest }
|
|
96
|
-
for (const worker of worldRenderer.workers) {
|
|
97
|
-
worker.postMessage(message)
|
|
98
|
-
}
|
|
90
|
+
// web-client to WASM mesher workers (and the light owner when flagged).
|
|
91
|
+
worldRenderer.feedChunkPacket(payload)
|
|
99
92
|
},
|
|
100
93
|
getChunksDebugState() {
|
|
101
94
|
const loadedSectionsChunks: Record<string, true> = {}
|
|
@@ -4,30 +4,28 @@ import cloudsPng from 'mc-assets/dist/other-textures/latest/environment/clouds.p
|
|
|
4
4
|
import type { WorldRendererThree } from '../worldRendererThree'
|
|
5
5
|
import type { RendererModuleController, RendererModuleManifest } from '../rendererModuleSystem'
|
|
6
6
|
import { vanillaCelestialAngle, vanillaDayBrightness } from '../../lib/skyLight'
|
|
7
|
+
import { loadCloudCover } from '../cloudCover'
|
|
8
|
+
import { buildCloudGeometry, CLOUD_CELL_SIZE, type CloudCover } from '../cloudGeometry'
|
|
7
9
|
|
|
8
|
-
// Vanilla: clouds.png is 256×256, each texel a 12×12 block cell, slab 4 blocks thick.
|
|
9
|
-
const CELL_SIZE = 12
|
|
10
|
-
const TEXTURE_CELLS = 256
|
|
11
|
-
const PATTERN_PERIOD = CELL_SIZE * TEXTURE_CELLS
|
|
12
|
-
const THICKNESS = 4
|
|
13
10
|
/** Vanilla drifts the pattern 0.03 blocks per tick (20 TPS) toward -X. */
|
|
14
11
|
const DRIFT_BLOCKS_PER_SECOND = 0.03 * 20
|
|
15
12
|
/** Vanilla offsets the slab bottom by +0.33 from the dimension's cloud height. */
|
|
16
13
|
const HEIGHT_BIAS = 0.33
|
|
17
14
|
const MIN_RADIUS = 256
|
|
18
|
-
const MAX_RADIUS =
|
|
15
|
+
const MAX_RADIUS = 576
|
|
19
16
|
const OPACITY = 0.8
|
|
20
|
-
/** Vanilla shades the underside darker than the top. */
|
|
21
|
-
const BOTTOM_SHADE = 0.7
|
|
22
17
|
|
|
23
18
|
const threeVersion = parseInt(THREE.REVISION.replaceAll(/\D+/g, ''), 10)
|
|
24
19
|
|
|
25
20
|
const vertexShader = /* glsl */ `
|
|
26
21
|
#include <common>
|
|
27
22
|
#include <logdepthbuf_pars_vertex>
|
|
23
|
+
attribute float shade;
|
|
28
24
|
varying vec2 vLocal;
|
|
25
|
+
varying float vShade;
|
|
29
26
|
void main() {
|
|
30
27
|
vLocal = (modelMatrix * vec4(position, 1.0)).xz;
|
|
28
|
+
vShade = shade;
|
|
31
29
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
32
30
|
#include <logdepthbuf_vertex>
|
|
33
31
|
}
|
|
@@ -36,32 +34,39 @@ const vertexShader = /* glsl */ `
|
|
|
36
34
|
const fragmentShader = /* glsl */ `
|
|
37
35
|
#include <common>
|
|
38
36
|
#include <logdepthbuf_pars_fragment>
|
|
39
|
-
uniform sampler2D map;
|
|
40
|
-
uniform vec2 patternOffset;
|
|
41
37
|
uniform vec3 color;
|
|
42
38
|
uniform float opacity;
|
|
43
39
|
uniform float fadeStart;
|
|
44
40
|
uniform float fadeEnd;
|
|
45
41
|
varying vec2 vLocal;
|
|
42
|
+
varying float vShade;
|
|
46
43
|
void main() {
|
|
47
44
|
#include <logdepthbuf_fragment>
|
|
48
|
-
float cover = texture2D(map, (vLocal + patternOffset) / ${PATTERN_PERIOD.toFixed(1)}).a;
|
|
49
|
-
if (cover < 0.5) discard;
|
|
50
45
|
float alpha = opacity * (1.0 - smoothstep(fadeStart, fadeEnd, length(vLocal)));
|
|
51
46
|
if (alpha <= 0.0) discard;
|
|
52
|
-
gl_FragColor = vec4(color, alpha);
|
|
47
|
+
gl_FragColor = vec4(color * vShade, alpha);
|
|
53
48
|
#include <tonemapping_fragment>
|
|
54
49
|
#include <${threeVersion >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>
|
|
55
50
|
}
|
|
56
51
|
`
|
|
57
52
|
|
|
58
53
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
54
|
+
* Vanilla "fancy" clouds: a 4-block-thick slab of 12×12 cells taken from clouds.png, with real top,
|
|
55
|
+
* bottom and side faces. The cells around the camera are rebuilt only when the camera or the drifting
|
|
56
|
+
* pattern crosses a cell boundary; everything finer than that is a mesh position offset.
|
|
57
|
+
* A colour-less depth pre-pass over the same geometry keeps overlapping faces from blending twice.
|
|
62
58
|
*/
|
|
63
59
|
export class CloudsModule implements RendererModuleController {
|
|
64
|
-
private
|
|
60
|
+
private readonly group = new THREE.Group()
|
|
61
|
+
private readonly uniforms = {
|
|
62
|
+
color: { value: new THREE.Color(1, 1, 1) },
|
|
63
|
+
opacity: { value: OPACITY },
|
|
64
|
+
fadeStart: { value: MIN_RADIUS * 0.55 },
|
|
65
|
+
fadeEnd: { value: MIN_RADIUS }
|
|
66
|
+
}
|
|
67
|
+
private meshes?: THREE.Mesh<THREE.BufferGeometry, THREE.ShaderMaterial>[]
|
|
68
|
+
private cover?: CloudCover
|
|
69
|
+
private builtKey = ''
|
|
65
70
|
private enabled = false
|
|
66
71
|
private drift = 0
|
|
67
72
|
private timeOfDay: number
|
|
@@ -69,19 +74,20 @@ export class CloudsModule implements RendererModuleController {
|
|
|
69
74
|
|
|
70
75
|
constructor(private readonly worldRenderer: WorldRendererThree) {
|
|
71
76
|
this.timeOfDay = worldRenderer.timeOfTheDay
|
|
77
|
+
this.group.name = 'clouds'
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
enable(): void {
|
|
75
81
|
if (this.enabled) return
|
|
76
82
|
this.enabled = true
|
|
77
|
-
if (!this.
|
|
78
|
-
this.
|
|
83
|
+
if (!this.meshes) this.create()
|
|
84
|
+
this.group.visible = true
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
disable(): void {
|
|
82
88
|
if (!this.enabled) return
|
|
83
89
|
this.enabled = false
|
|
84
|
-
|
|
90
|
+
this.group.visible = false
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
autoEnableCheck(): boolean {
|
|
@@ -94,78 +100,96 @@ export class CloudsModule implements RendererModuleController {
|
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
render?: (deltaTime: number) => void = deltaTime => {
|
|
97
|
-
|
|
103
|
+
// Not wrapped: cell indices wrap in the lookup, and wrapping here would force a rebuild.
|
|
104
|
+
this.drift += deltaTime * DRIFT_BLOCKS_PER_SECOND
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
dispose(): void {
|
|
101
|
-
if (!this.
|
|
102
|
-
this.worldRenderer.scene.remove(this.
|
|
103
|
-
this.
|
|
104
|
-
this.mesh.material.
|
|
105
|
-
this.
|
|
106
|
-
this.
|
|
108
|
+
if (!this.meshes) return
|
|
109
|
+
this.worldRenderer.scene.remove(this.group)
|
|
110
|
+
this.meshes[0].geometry.dispose()
|
|
111
|
+
for (const mesh of this.meshes) mesh.material.dispose()
|
|
112
|
+
this.meshes = undefined
|
|
113
|
+
this.cover = undefined
|
|
114
|
+
this.builtKey = ''
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
private create(): void {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
map.wrapT = THREE.RepeatWrapping
|
|
113
|
-
map.magFilter = THREE.NearestFilter
|
|
114
|
-
map.minFilter = THREE.NearestFilter
|
|
115
|
-
map.generateMipmaps = false
|
|
116
|
-
// Keep png row 0 at -Z like vanilla's texture-to-world mapping.
|
|
117
|
-
map.flipY = false
|
|
118
|
-
|
|
119
|
-
const material = new THREE.ShaderMaterial({
|
|
120
|
-
uniforms: {
|
|
121
|
-
map: { value: map },
|
|
122
|
-
patternOffset: { value: new THREE.Vector2() },
|
|
123
|
-
color: { value: new THREE.Color(1, 1, 1) },
|
|
124
|
-
opacity: { value: OPACITY },
|
|
125
|
-
fadeStart: { value: MIN_RADIUS * 0.55 },
|
|
126
|
-
fadeEnd: { value: MIN_RADIUS }
|
|
127
|
-
},
|
|
118
|
+
const shared = {
|
|
119
|
+
uniforms: this.uniforms,
|
|
128
120
|
vertexShader,
|
|
129
121
|
fragmentShader,
|
|
130
|
-
transparent: true,
|
|
131
|
-
depthWrite: false,
|
|
132
|
-
depthTest: true,
|
|
133
122
|
side: THREE.DoubleSide,
|
|
123
|
+
transparent: true,
|
|
134
124
|
forceSinglePass: true
|
|
135
|
-
}
|
|
125
|
+
}
|
|
126
|
+
const main = new THREE.ShaderMaterial({ ...shared, depthWrite: false })
|
|
127
|
+
// Same program, so its depth matches the main pass exactly.
|
|
128
|
+
const depthOnly = new THREE.ShaderMaterial({ ...shared, depthWrite: true, colorWrite: false })
|
|
136
129
|
|
|
137
|
-
const geometry = new THREE.
|
|
138
|
-
geometry.
|
|
130
|
+
const geometry = new THREE.BufferGeometry()
|
|
131
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(0), 3))
|
|
139
132
|
|
|
140
|
-
const mesh = new THREE.Mesh(geometry, material)
|
|
141
|
-
mesh.name = 'clouds'
|
|
142
|
-
mesh.frustumCulled = false
|
|
143
133
|
// After sun/moon (-2) and stars (-1), before other transparents so water/glass composite on top.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
134
|
+
const passes: [THREE.ShaderMaterial, number][] = [
|
|
135
|
+
[depthOnly, -0.6],
|
|
136
|
+
[main, -0.5]
|
|
137
|
+
]
|
|
138
|
+
this.meshes = passes.map(([material, renderOrder]) => {
|
|
139
|
+
const mesh = new THREE.Mesh(geometry, material)
|
|
140
|
+
mesh.frustumCulled = false
|
|
141
|
+
mesh.renderOrder = renderOrder
|
|
142
|
+
this.group.add(mesh)
|
|
143
|
+
return mesh
|
|
144
|
+
})
|
|
145
|
+
// The depth pre-pass is drawn first, so syncing there positions both passes.
|
|
146
|
+
this.meshes[0].onBeforeRender = () => this.syncToCamera()
|
|
147
|
+
this.worldRenderer.scene.add(this.group)
|
|
148
|
+
|
|
149
|
+
loadCloudCover(cloudsPng).then(
|
|
150
|
+
cover => {
|
|
151
|
+
this.cover = cover
|
|
152
|
+
},
|
|
153
|
+
error => console.warn('Failed to load clouds.png, clouds will not render', error)
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private rebuild(centerX: number, centerZ: number, radiusCells: number): void {
|
|
158
|
+
const data = buildCloudGeometry(this.cover!, centerX, centerZ, radiusCells)
|
|
159
|
+
const geometry = new THREE.BufferGeometry()
|
|
160
|
+
geometry.setAttribute('position', new THREE.BufferAttribute(data.positions, 3))
|
|
161
|
+
geometry.setAttribute('shade', new THREE.BufferAttribute(data.shades, 1))
|
|
162
|
+
geometry.setIndex(new THREE.BufferAttribute(data.indices, 1))
|
|
163
|
+
|
|
164
|
+
const meshes = this.meshes!
|
|
165
|
+
const old = meshes[0].geometry
|
|
166
|
+
for (const mesh of meshes) mesh.geometry = geometry
|
|
167
|
+
old.dispose()
|
|
148
168
|
}
|
|
149
169
|
|
|
150
|
-
private syncToCamera(
|
|
170
|
+
private syncToCamera(): void {
|
|
151
171
|
const { sceneOrigin, viewDistance, worldRendererConfig } = this.worldRenderer
|
|
152
|
-
const { uniforms } = mesh.material
|
|
153
172
|
|
|
154
173
|
const radius = THREE.MathUtils.clamp(viewDistance * 16 * 2, MIN_RADIUS, MAX_RADIUS)
|
|
155
|
-
uniforms.fadeStart.value = radius * 0.55
|
|
156
|
-
uniforms.fadeEnd.value = radius
|
|
174
|
+
this.uniforms.fadeStart.value = radius * 0.55
|
|
175
|
+
this.uniforms.fadeEnd.value = radius
|
|
176
|
+
|
|
177
|
+
// Pattern space is world space shifted by the drift, so world x = pattern x - drift.
|
|
178
|
+
const centerX = Math.floor((sceneOrigin.x + this.drift) / CLOUD_CELL_SIZE)
|
|
179
|
+
const centerZ = Math.floor(sceneOrigin.z / CLOUD_CELL_SIZE)
|
|
180
|
+
if (this.cover) {
|
|
181
|
+
const radiusCells = Math.ceil(radius / CLOUD_CELL_SIZE)
|
|
182
|
+
const key = `${centerX},${centerZ},${radiusCells}`
|
|
183
|
+
if (key !== this.builtKey) {
|
|
184
|
+
this.builtKey = key
|
|
185
|
+
this.rebuild(centerX, centerZ, radiusCells)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
157
188
|
|
|
158
|
-
//
|
|
189
|
+
// Compute in doubles so the GPU only ever sees camera-relative offsets.
|
|
159
190
|
const bottom = worldRendererConfig.cloudHeight + HEIGHT_BIAS
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
mesh.position.set(0, planeY - sceneOrigin.y, 0)
|
|
163
|
-
mesh.scale.set(radius, 1, radius)
|
|
164
|
-
mesh.updateMatrixWorld()
|
|
165
|
-
|
|
166
|
-
// Wrap on the CPU in doubles so the shader never sees large world coordinates.
|
|
167
|
-
const wrap = (v: number) => ((v % PATTERN_PERIOD) + PATTERN_PERIOD) % PATTERN_PERIOD
|
|
168
|
-
uniforms.patternOffset.value.set(wrap(sceneOrigin.x + this.drift), wrap(sceneOrigin.z))
|
|
191
|
+
this.group.position.set(centerX * CLOUD_CELL_SIZE - this.drift - sceneOrigin.x, bottom - sceneOrigin.y, centerZ * CLOUD_CELL_SIZE - sceneOrigin.z)
|
|
192
|
+
this.group.updateMatrixWorld(true)
|
|
169
193
|
|
|
170
194
|
const day = vanillaDayBrightness(vanillaCelestialAngle(this.timeOfDay))
|
|
171
195
|
let r = day * 0.9 + 0.1
|
|
@@ -178,9 +202,8 @@ export class CloudsModule implements RendererModuleController {
|
|
|
178
202
|
g = g * 0.05 + grey * 0.95
|
|
179
203
|
b = b * 0.05 + grey * 0.95
|
|
180
204
|
}
|
|
181
|
-
|
|
182
|
-
this.
|
|
183
|
-
uniforms.color.value.copy(this.linearColor)
|
|
205
|
+
this.linearColor.setRGB(r, g, b, THREE.SRGBColorSpace)
|
|
206
|
+
this.uniforms.color.value.copy(this.linearColor)
|
|
184
207
|
}
|
|
185
208
|
}
|
|
186
209
|
|