minecraft-renderer 0.1.48 → 0.1.50
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/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +3740 -183
- package/dist/minecraft-renderer.js +332 -60
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +705 -433
- package/package.json +1 -1
- package/src/graphicsBackend/config.ts +4 -0
- package/src/graphicsBackend/playerState.ts +1 -0
- package/src/graphicsBackend/rendererOptionsSync.ts +2 -0
- package/src/lib/worldrendererCommon.ts +13 -0
- package/src/mesher-shared/exportedGeometryTypes.ts +5 -1
- package/src/mesher-shared/shared.ts +8 -0
- package/src/three/chunkMeshManager.ts +312 -39
- package/src/three/globalBlockBuffer.ts +292 -0
- package/src/three/menuBackground/config.ts +1 -1
- package/src/three/menuBackground/defaultOptions.ts +52 -19
- package/src/three/menuBackground/index.ts +5 -1
- package/src/three/modules/sciFiWorldReveal.ts +162 -68
- package/src/three/modules/starfield.ts +9 -1
- package/src/three/sectionRaycastAabb.ts +167 -0
- package/src/three/shaderCubeMesh.ts +93 -0
- package/src/three/shaders/cubeBlockShader.ts +354 -0
- package/src/three/shaders/textureIndexMapping.ts +122 -0
- package/src/three/shaders/tintPalette.ts +198 -0
- package/src/three/worldGeometryExport.ts +53 -25
- package/src/three/worldRendererThree.ts +56 -23
- package/src/wasm-mesher/bridge/render-from-wasm.ts +62 -185
- package/src/wasm-mesher/bridge/shaderCubeBridge.ts +399 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/tests/sectionRaycastAabb.test.ts +58 -0
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +360 -0
- package/src/wasm-mesher/tests/splitColumnWasmOutput.test.ts +11 -4
- package/src/wasm-mesher/worker/mesherWasm.ts +17 -2
package/package.json
CHANGED
|
@@ -27,6 +27,10 @@ export const defaultWorldRendererConfig = {
|
|
|
27
27
|
|
|
28
28
|
// Performance settings
|
|
29
29
|
wasmMesher: true,
|
|
30
|
+
/** Render full 1×1 cubes through the instanced shader path (requires WebGL2). */
|
|
31
|
+
shaderCubeBlocks: false,
|
|
32
|
+
/** 0=off, 1=holes red, 2=tileIndex, 3=faceId colors, 4=atlas alpha */
|
|
33
|
+
shaderCubeDebugMode: 0,
|
|
30
34
|
mesherWorkers: 1,
|
|
31
35
|
addChunksBatchWaitTime: 200,
|
|
32
36
|
_experimentalSmoothChunkLoading: true,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { subscribe } from 'valtio/vanilla'
|
|
8
8
|
import type { AppViewer } from './appViewer'
|
|
9
9
|
import type { RendererStorageOptions } from '../three/menuBackground/defaultOptions'
|
|
10
|
+
import { rendererShaderCubeDebugModeToValue } from '../three/menuBackground/defaultOptions'
|
|
10
11
|
import type { MenuBackgroundOptions } from '../three/menuBackground/types'
|
|
11
12
|
import type { MenuBackgroundRenderer } from '../three/menuBackground/renderer'
|
|
12
13
|
import { menuBackgroundSpeedToMultiplier } from '../three/menuBackground/config'
|
|
@@ -171,6 +172,7 @@ export function applyRendererOptions(
|
|
|
171
172
|
cfg.starfield = o.starfieldRendering
|
|
172
173
|
cfg.defaultSkybox = o.defaultSkybox
|
|
173
174
|
cfg.fov = o.fov
|
|
175
|
+
cfg.shaderCubeDebugMode = rendererShaderCubeDebugModeToValue(o.rendererShaderCubeDebugMode)
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
/** World-view + hand/camera options (call when WorldView is ready). */
|
|
@@ -149,6 +149,18 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
149
149
|
return false
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Effective instanced cube-shader path (config + runtime caps).
|
|
154
|
+
* WorldRendererThree adds WebGL2; worker uses {@link getMesherConfig}.shaderCubeBlocks.
|
|
155
|
+
*/
|
|
156
|
+
protected isShaderCubeBlocksEnabled(): boolean {
|
|
157
|
+
return this.worldRendererConfig.shaderCubeBlocks === true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
shaderCubeBlocksEnabled(): boolean {
|
|
161
|
+
return this.isShaderCubeBlocksEnabled()
|
|
162
|
+
}
|
|
163
|
+
|
|
152
164
|
worldRendererConfig: WorldRendererConfig
|
|
153
165
|
playerStateReactive: PlayerStateReactive
|
|
154
166
|
playerStateUtils: PlayerStateUtils
|
|
@@ -629,6 +641,7 @@ export abstract class WorldRendererCommon<WorkerSend = any, WorkerReceive = any>
|
|
|
629
641
|
worldMaxY: this.worldMinYRender + this.worldSizeParams.worldHeight,
|
|
630
642
|
disableConversionCache: this.worldRendererConfig.disableMesherConversionCache,
|
|
631
643
|
computeWireframeEdges: this.worldRendererConfig.futuristicReveal === true,
|
|
644
|
+
shaderCubeBlocks: this.isShaderCubeBlocksEnabled(),
|
|
632
645
|
}
|
|
633
646
|
}
|
|
634
647
|
|
|
@@ -20,6 +20,8 @@ export const defaultMesherConfig = {
|
|
|
20
20
|
disableBlockEntityTextures: false,
|
|
21
21
|
disableConversionCache: false,
|
|
22
22
|
computeWireframeEdges: false,
|
|
23
|
+
/** Pack eligible full-cube faces as GPU-instanced shader words during WASM post-processing. */
|
|
24
|
+
shaderCubeBlocks: false,
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export type CustomBlockModels = {
|
|
@@ -63,6 +65,12 @@ export type MesherGeometryOutput = {
|
|
|
63
65
|
blocksCount: number
|
|
64
66
|
wireframePositions?: Float32Array
|
|
65
67
|
customBlockModels?: CustomBlockModels
|
|
68
|
+
/** GPU-instanced full-cube faces packed by the mesher; consumed by ChunkMeshManager. */
|
|
69
|
+
shaderCubes?: {
|
|
70
|
+
words: Uint32Array
|
|
71
|
+
count: number
|
|
72
|
+
formatVersion: 2
|
|
73
|
+
}
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
export interface MesherMainEvents {
|
|
@@ -4,6 +4,15 @@ import * as THREE from 'three'
|
|
|
4
4
|
import * as nbt from 'prismarine-nbt'
|
|
5
5
|
import { Vec3 } from 'vec3'
|
|
6
6
|
import { MesherGeometryOutput } from '../mesher-shared/shared'
|
|
7
|
+
import { getShaderCubeResources } from '../wasm-mesher/bridge/shaderCubeBridge'
|
|
8
|
+
import { createCubeBlockMaterial } from './shaders/cubeBlockShader'
|
|
9
|
+
import { createShaderCubeMesh, disposeShaderCubeMesh } from './shaderCubeMesh'
|
|
10
|
+
import { GlobalBlockBuffer } from './globalBlockBuffer'
|
|
11
|
+
import {
|
|
12
|
+
computeShaderSectionRaycastAabb,
|
|
13
|
+
raycastAabb,
|
|
14
|
+
type ShaderSectionRaycastBox,
|
|
15
|
+
} from './sectionRaycastAabb'
|
|
7
16
|
import { chunkPos } from '../lib/simpleUtils'
|
|
8
17
|
import { renderSign } from '../sign-renderer'
|
|
9
18
|
import { getMesh } from './entity/EntityMesh'
|
|
@@ -20,7 +29,11 @@ export interface ChunkMeshPool {
|
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export interface SectionObject extends THREE.Group {
|
|
23
|
-
mesh?: THREE.Mesh<THREE.BufferGeometry, THREE.
|
|
32
|
+
mesh?: THREE.Mesh<THREE.BufferGeometry, THREE.Material>
|
|
33
|
+
/** Per-section instanced shader mesh (sci-fi reveal defer only). */
|
|
34
|
+
shaderMesh?: THREE.Mesh<THREE.InstancedBufferGeometry, THREE.ShaderMaterial>
|
|
35
|
+
/** Shader cube words kept for migration to global buffer after reveal. */
|
|
36
|
+
deferredShaderCubes?: { words: Uint32Array, count: number }
|
|
24
37
|
tilesCount?: number
|
|
25
38
|
blocksCount?: number
|
|
26
39
|
|
|
@@ -82,6 +95,12 @@ export class ChunkMeshManager {
|
|
|
82
95
|
* section.
|
|
83
96
|
*/
|
|
84
97
|
private readonly chunkBoxMaterial = new THREE.MeshBasicMaterial({ color: 0x00_00_00, transparent: true, opacity: 0 })
|
|
98
|
+
/** Shared across all sections — atlas/tint uniforms updated via {@link syncCubeShaderUniforms}. */
|
|
99
|
+
private cubeShaderMaterial: THREE.ShaderMaterial | null = null
|
|
100
|
+
/** One instanced mesh for all shader-cube faces (single draw call). */
|
|
101
|
+
globalBlockBuffer: GlobalBlockBuffer | null = null
|
|
102
|
+
/** Tight world AABBs for third-person raycast; no per-section Object3D. */
|
|
103
|
+
private readonly shaderSectionRaycastBoxes = new Map<string, ShaderSectionRaycastBox>()
|
|
85
104
|
|
|
86
105
|
// Performance tracking
|
|
87
106
|
private hits = 0
|
|
@@ -138,66 +157,268 @@ export class ChunkMeshManager {
|
|
|
138
157
|
}
|
|
139
158
|
}
|
|
140
159
|
|
|
160
|
+
/** True when section has legacy vertices and/or GPU shader cube instances. */
|
|
161
|
+
sectionHasRenderableContent (geometryData: MesherGeometryOutput): boolean {
|
|
162
|
+
if (geometryData.positions.length > 0) return true
|
|
163
|
+
if (!this.isShaderCubesGpuEnabled()) return false
|
|
164
|
+
return (geometryData.shaderCubes?.count ?? 0) > 0
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
isShaderCubesGpuEnabled (): boolean {
|
|
168
|
+
return this.worldRenderer.shaderCubeBlocksEnabled()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
syncCubeShaderUniforms (): void {
|
|
172
|
+
if (!this.isShaderCubesGpuEnabled()) return
|
|
173
|
+
const mat = this.cubeShaderMaterial ?? this.getCubeShaderMaterial()
|
|
174
|
+
if (!mat) return
|
|
175
|
+
const atlas = (this.material as THREE.MeshBasicMaterial).map ?? null
|
|
176
|
+
mat.uniforms.u_atlas.value = atlas
|
|
177
|
+
const { tintPalette } = getShaderCubeResources()
|
|
178
|
+
if (!tintPalette.isReady()) {
|
|
179
|
+
tintPalette.createTexture()
|
|
180
|
+
}
|
|
181
|
+
mat.uniforms.u_tintPalette.value = tintPalette.getTexture()
|
|
182
|
+
mat.uniforms.u_debugMode.value = this.worldRenderer.worldRendererConfig.shaderCubeDebugMode ?? 0
|
|
183
|
+
mat.needsUpdate = true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private getCubeShaderMaterial (): THREE.ShaderMaterial | null {
|
|
187
|
+
if (!this.isShaderCubesGpuEnabled()) return null
|
|
188
|
+
if (!this.cubeShaderMaterial) {
|
|
189
|
+
this.cubeShaderMaterial = createCubeBlockMaterial()
|
|
190
|
+
this.syncCubeShaderUniforms()
|
|
191
|
+
}
|
|
192
|
+
return this.cubeShaderMaterial
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private getGlobalBlockBuffer (): GlobalBlockBuffer | null {
|
|
196
|
+
const mat = this.getCubeShaderMaterial()
|
|
197
|
+
if (!mat) return null
|
|
198
|
+
if (!this.globalBlockBuffer) {
|
|
199
|
+
this.globalBlockBuffer = new GlobalBlockBuffer(mat, this.scene)
|
|
200
|
+
}
|
|
201
|
+
return this.globalBlockBuffer
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Sci-fi reveal needs per-section shader meshes (or no global add) until the first wave completes. */
|
|
205
|
+
private shouldDeferShaderToPerSection (sectionKey: string): boolean {
|
|
206
|
+
const sciFi = this.worldRenderer.getModule<{
|
|
207
|
+
shouldUseRevealEffect?: (key: string) => boolean
|
|
208
|
+
isInInitialRevealCampaign?: () => boolean
|
|
209
|
+
}>('futuristicReveal')
|
|
210
|
+
if (!sciFi) return false
|
|
211
|
+
if (sciFi.isInInitialRevealCampaign?.()) return true
|
|
212
|
+
return sciFi.shouldUseRevealEffect?.(sectionKey) === true
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Move deferred per-section shader cubes into the global buffer after reveal completes.
|
|
217
|
+
*/
|
|
218
|
+
migrateDeferredShaderToGlobal (sectionKey: string): void {
|
|
219
|
+
const section = this.sectionObjects[sectionKey]
|
|
220
|
+
if (!section?.deferredShaderCubes) return
|
|
221
|
+
|
|
222
|
+
const { words, count } = section.deferredShaderCubes
|
|
223
|
+
const wx = section.worldX
|
|
224
|
+
const wy = section.worldY
|
|
225
|
+
const wz = section.worldZ
|
|
226
|
+
|
|
227
|
+
if (wx !== undefined && wy !== undefined && wz !== undefined) {
|
|
228
|
+
this.registerShaderSectionRaycastBox(sectionKey, words, count, wx, wy, wz)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const global = this.getGlobalBlockBuffer()
|
|
232
|
+
global?.addSection(sectionKey, words, count)
|
|
233
|
+
|
|
234
|
+
const hadShaderAsPrimary = section.mesh === (section.shaderMesh as unknown as THREE.Mesh | undefined)
|
|
235
|
+
if (section.shaderMesh) {
|
|
236
|
+
disposeShaderCubeMesh(section.shaderMesh)
|
|
237
|
+
section.remove(section.shaderMesh)
|
|
238
|
+
section.shaderMesh = undefined
|
|
239
|
+
}
|
|
240
|
+
delete section.deferredShaderCubes
|
|
241
|
+
|
|
242
|
+
if (hadShaderAsPrimary) {
|
|
243
|
+
section.mesh = undefined
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
registerShaderSectionRaycastBox (
|
|
248
|
+
sectionKey: string,
|
|
249
|
+
words: Uint32Array,
|
|
250
|
+
faceCount: number,
|
|
251
|
+
sectionCenterX: number,
|
|
252
|
+
sectionCenterY: number,
|
|
253
|
+
sectionCenterZ: number,
|
|
254
|
+
): void {
|
|
255
|
+
const box = computeShaderSectionRaycastAabb(words, faceCount, sectionCenterX, sectionCenterY, sectionCenterZ)
|
|
256
|
+
if (box) {
|
|
257
|
+
this.shaderSectionRaycastBoxes.set(sectionKey, box)
|
|
258
|
+
} else {
|
|
259
|
+
this.shaderSectionRaycastBoxes.delete(sectionKey)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
unregisterShaderSectionRaycastBox (sectionKey: string): void {
|
|
264
|
+
this.shaderSectionRaycastBoxes.delete(sectionKey)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Closest hit against registered shader-cube AABBs (world-space ray). */
|
|
268
|
+
raycastShaderSectionAABBs (
|
|
269
|
+
originWorld: THREE.Vector3,
|
|
270
|
+
direction: THREE.Vector3,
|
|
271
|
+
maxDist: number,
|
|
272
|
+
maxCenterDistance = 80,
|
|
273
|
+
): number | undefined {
|
|
274
|
+
const ox = originWorld.x
|
|
275
|
+
const oy = originWorld.y
|
|
276
|
+
const oz = originWorld.z
|
|
277
|
+
const dx = direction.x
|
|
278
|
+
const dy = direction.y
|
|
279
|
+
const dz = direction.z
|
|
280
|
+
const maxCenterDistSq = maxCenterDistance * maxCenterDistance
|
|
281
|
+
|
|
282
|
+
let closest = maxDist
|
|
283
|
+
let found = false
|
|
284
|
+
|
|
285
|
+
for (const [key, box] of this.shaderSectionRaycastBoxes) {
|
|
286
|
+
const section = this.sectionObjects[key]
|
|
287
|
+
if (section && !section.visible) continue
|
|
288
|
+
|
|
289
|
+
const dcx = box.cx - ox
|
|
290
|
+
const dcy = box.cy - oy
|
|
291
|
+
const dcz = box.cz - oz
|
|
292
|
+
if (dcx * dcx + dcy * dcy + dcz * dcz > maxCenterDistSq) continue
|
|
293
|
+
|
|
294
|
+
const t = raycastAabb(
|
|
295
|
+
ox, oy, oz, dx, dy, dz,
|
|
296
|
+
box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ,
|
|
297
|
+
closest,
|
|
298
|
+
)
|
|
299
|
+
if (t !== undefined && t < closest) {
|
|
300
|
+
closest = t
|
|
301
|
+
found = true
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return found ? closest : undefined
|
|
306
|
+
}
|
|
307
|
+
|
|
141
308
|
/**
|
|
142
309
|
* Update or create a section with new geometry data
|
|
143
310
|
*/
|
|
144
311
|
updateSection (sectionKey: string, geometryData: MesherGeometryOutput): SectionObject | null {
|
|
312
|
+
const hasLegacy = geometryData.positions.length > 0
|
|
313
|
+
const shaderData = geometryData.shaderCubes
|
|
314
|
+
const hasShader = this.isShaderCubesGpuEnabled() && (shaderData?.count ?? 0) > 0
|
|
315
|
+
|
|
316
|
+
if (!hasLegacy && !hasShader) {
|
|
317
|
+
this.releaseSection(sectionKey)
|
|
318
|
+
return null
|
|
319
|
+
}
|
|
320
|
+
|
|
145
321
|
// Remove existing section object from scene if it exists
|
|
146
322
|
let sectionObject = this.sectionObjects[sectionKey]
|
|
147
323
|
if (sectionObject) {
|
|
148
324
|
this.cleanupSection(sectionKey)
|
|
149
325
|
}
|
|
150
326
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
327
|
+
if (!hasLegacy) {
|
|
328
|
+
this.releasePooledMesh(sectionKey)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
let legacyMesh: THREE.Mesh<THREE.BufferGeometry, THREE.Material> | undefined
|
|
332
|
+
if (hasLegacy) {
|
|
333
|
+
let poolEntry = this.activeSections.get(sectionKey)
|
|
155
334
|
if (!poolEntry) {
|
|
156
|
-
|
|
157
|
-
|
|
335
|
+
poolEntry = this.acquireMesh()
|
|
336
|
+
if (!poolEntry) {
|
|
337
|
+
console.warn(`ChunkMeshManager: No available mesh in pool for section ${sectionKey}`)
|
|
338
|
+
return null
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this.activeSections.set(sectionKey, poolEntry)
|
|
342
|
+
poolEntry.sectionKey = sectionKey
|
|
158
343
|
}
|
|
159
344
|
|
|
160
|
-
|
|
161
|
-
poolEntry.sectionKey = sectionKey
|
|
162
|
-
}
|
|
345
|
+
const { mesh } = poolEntry
|
|
163
346
|
|
|
164
|
-
|
|
347
|
+
this.updateGeometryAttribute(mesh.geometry, 'position', geometryData.positions, 3)
|
|
348
|
+
this.updateGeometryAttribute(mesh.geometry, 'normal', geometryData.normals, 3)
|
|
349
|
+
this.updateGeometryAttribute(mesh.geometry, 'color', geometryData.colors, 3)
|
|
350
|
+
this.updateGeometryAttribute(mesh.geometry, 'uv', geometryData.uvs, 2)
|
|
165
351
|
|
|
166
|
-
|
|
167
|
-
this.updateGeometryAttribute(mesh.geometry, 'position', geometryData.positions, 3)
|
|
168
|
-
this.updateGeometryAttribute(mesh.geometry, 'normal', geometryData.normals, 3)
|
|
169
|
-
this.updateGeometryAttribute(mesh.geometry, 'color', geometryData.colors, 3)
|
|
170
|
-
this.updateGeometryAttribute(mesh.geometry, 'uv', geometryData.uvs, 2)
|
|
352
|
+
mesh.geometry.index = new THREE.BufferAttribute(geometryData.indices as Uint32Array | Uint16Array, 1)
|
|
171
353
|
|
|
172
|
-
|
|
173
|
-
|
|
354
|
+
mesh.geometry.boundingBox = new THREE.Box3(
|
|
355
|
+
new THREE.Vector3(-8, -8, -8),
|
|
356
|
+
new THREE.Vector3(8, 8, 8)
|
|
357
|
+
)
|
|
358
|
+
mesh.geometry.boundingSphere = new THREE.Sphere(
|
|
359
|
+
new THREE.Vector3(0, 0, 0),
|
|
360
|
+
Math.sqrt(3 * 8 ** 2)
|
|
361
|
+
)
|
|
174
362
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
mesh.geometry.boundingSphere = new THREE.Sphere(
|
|
181
|
-
new THREE.Vector3(0, 0, 0),
|
|
182
|
-
Math.sqrt(3 * 8 ** 2)
|
|
183
|
-
)
|
|
363
|
+
this.worldRenderer.sceneOrigin.track(mesh, { updateMatrix: true })
|
|
364
|
+
mesh.position.set(geometryData.sx, geometryData.sy, geometryData.sz)
|
|
365
|
+
mesh.updateMatrix()
|
|
366
|
+
mesh.visible = true
|
|
367
|
+
mesh.name = 'mesh'
|
|
184
368
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
mesh.updateMatrix()
|
|
189
|
-
mesh.visible = true
|
|
190
|
-
mesh.name = 'mesh'
|
|
369
|
+
poolEntry.lastUsedTime = performance.now()
|
|
370
|
+
legacyMesh = mesh as THREE.Mesh<THREE.BufferGeometry, THREE.Material>
|
|
371
|
+
}
|
|
191
372
|
|
|
192
|
-
|
|
373
|
+
const cubeMaterial = hasShader ? this.getCubeShaderMaterial() : null
|
|
374
|
+
let shaderMesh: THREE.Mesh<THREE.InstancedBufferGeometry, THREE.ShaderMaterial> | undefined
|
|
375
|
+
const deferShader = hasShader && this.shouldDeferShaderToPerSection(sectionKey)
|
|
376
|
+
if (hasShader && shaderData) {
|
|
377
|
+
if (deferShader && cubeMaterial) {
|
|
378
|
+
shaderMesh = createShaderCubeMesh(shaderData, cubeMaterial)
|
|
379
|
+
shaderMesh.visible = true
|
|
380
|
+
} else {
|
|
381
|
+
this.getGlobalBlockBuffer()?.addSection(sectionKey, shaderData.words, shaderData.count)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
193
384
|
|
|
194
|
-
// Create or update the section object container
|
|
195
385
|
sectionObject = new THREE.Group() as SectionObject
|
|
196
|
-
|
|
197
|
-
|
|
386
|
+
if (legacyMesh) {
|
|
387
|
+
sectionObject.add(legacyMesh)
|
|
388
|
+
sectionObject.mesh = legacyMesh
|
|
389
|
+
}
|
|
390
|
+
if (shaderMesh) {
|
|
391
|
+
sectionObject.add(shaderMesh)
|
|
392
|
+
sectionObject.shaderMesh = shaderMesh
|
|
393
|
+
if (shaderData) {
|
|
394
|
+
sectionObject.deferredShaderCubes = {
|
|
395
|
+
words: shaderData.words,
|
|
396
|
+
count: shaderData.count,
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!sectionObject.mesh) {
|
|
400
|
+
sectionObject.mesh = shaderMesh as unknown as THREE.Mesh<THREE.BufferGeometry, THREE.Material>
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (hasShader && shaderData) {
|
|
404
|
+
this.registerShaderSectionRaycastBox(
|
|
405
|
+
sectionKey,
|
|
406
|
+
shaderData.words,
|
|
407
|
+
shaderData.count,
|
|
408
|
+
geometryData.sx,
|
|
409
|
+
geometryData.sy,
|
|
410
|
+
geometryData.sz,
|
|
411
|
+
)
|
|
412
|
+
}
|
|
198
413
|
|
|
199
|
-
|
|
200
|
-
|
|
414
|
+
let tilesCount = 0
|
|
415
|
+
if (hasLegacy) {
|
|
416
|
+
tilesCount += geometryData.positions.length / 3 / 4
|
|
417
|
+
}
|
|
418
|
+
if (hasShader && shaderData) {
|
|
419
|
+
tilesCount += shaderData.count
|
|
420
|
+
}
|
|
421
|
+
sectionObject.tilesCount = tilesCount
|
|
201
422
|
sectionObject.blocksCount = geometryData.blocksCount
|
|
202
423
|
sectionObject.worldX = geometryData.sx
|
|
203
424
|
sectionObject.worldY = geometryData.sy
|
|
@@ -495,6 +716,13 @@ export class ChunkMeshManager {
|
|
|
495
716
|
})
|
|
496
717
|
this.disposeContainer(sectionObject.bannersContainer)
|
|
497
718
|
}
|
|
719
|
+
this.globalBlockBuffer?.removeSection(sectionKey)
|
|
720
|
+
this.unregisterShaderSectionRaycastBox(sectionKey)
|
|
721
|
+
if (sectionObject.shaderMesh) {
|
|
722
|
+
disposeShaderCubeMesh(sectionObject.shaderMesh)
|
|
723
|
+
sectionObject.shaderMesh = undefined
|
|
724
|
+
}
|
|
725
|
+
delete sectionObject.deferredShaderCubes
|
|
498
726
|
// Dispose signs and heads containers
|
|
499
727
|
if (sectionObject.signsContainer) {
|
|
500
728
|
this.disposeContainer(sectionObject.signsContainer)
|
|
@@ -526,6 +754,19 @@ export class ChunkMeshManager {
|
|
|
526
754
|
/**
|
|
527
755
|
* Release a section and return its mesh to the pool
|
|
528
756
|
*/
|
|
757
|
+
private releasePooledMesh (sectionKey: string): void {
|
|
758
|
+
const poolEntry = this.activeSections.get(sectionKey)
|
|
759
|
+
if (!poolEntry) return
|
|
760
|
+
|
|
761
|
+
poolEntry.mesh.visible = false
|
|
762
|
+
poolEntry.inUse = false
|
|
763
|
+
poolEntry.sectionKey = undefined
|
|
764
|
+
poolEntry.lastUsedTime = 0
|
|
765
|
+
this.clearGeometry(poolEntry.mesh.geometry)
|
|
766
|
+
this.activeSections.delete(sectionKey)
|
|
767
|
+
this.cleanupExcessMeshes()
|
|
768
|
+
}
|
|
769
|
+
|
|
529
770
|
releaseSection (sectionKey: string): boolean {
|
|
530
771
|
this.cleanupSection(sectionKey)
|
|
531
772
|
|
|
@@ -691,6 +932,32 @@ export class ChunkMeshManager {
|
|
|
691
932
|
let colorBytes = 0
|
|
692
933
|
let uvBytes = 0
|
|
693
934
|
let indexBytes = 0
|
|
935
|
+
let shaderInstanceBytes = 0
|
|
936
|
+
|
|
937
|
+
const globalGeom = this.globalBlockBuffer?.mesh.geometry
|
|
938
|
+
if (globalGeom) {
|
|
939
|
+
for (const name of ['a_w0', 'a_w1', 'a_w2', 'a_w3'] as const) {
|
|
940
|
+
const attr = globalGeom.getAttribute(name)
|
|
941
|
+
if (attr) {
|
|
942
|
+
const bytes = attr.array.byteLength
|
|
943
|
+
shaderInstanceBytes += bytes
|
|
944
|
+
totalBytes += bytes
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
for (const sectionObject of Object.values(this.sectionObjects)) {
|
|
950
|
+
const geom = sectionObject.shaderMesh?.geometry
|
|
951
|
+
if (!geom) continue
|
|
952
|
+
for (const name of ['a_w0', 'a_w1', 'a_w2', 'a_w3'] as const) {
|
|
953
|
+
const attr = geom.getAttribute(name)
|
|
954
|
+
if (attr) {
|
|
955
|
+
const bytes = attr.array.byteLength
|
|
956
|
+
shaderInstanceBytes += bytes
|
|
957
|
+
totalBytes += bytes
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
694
961
|
|
|
695
962
|
for (const poolEntry of this.meshPool) {
|
|
696
963
|
if (poolEntry.inUse && poolEntry.mesh.geometry) {
|
|
@@ -742,6 +1009,7 @@ export class ChunkMeshManager {
|
|
|
742
1009
|
color: `${(colorBytes / (1024 * 1024)).toFixed(2)} MB`,
|
|
743
1010
|
uv: `${(uvBytes / (1024 * 1024)).toFixed(2)} MB`,
|
|
744
1011
|
index: `${(indexBytes / (1024 * 1024)).toFixed(2)} MB`,
|
|
1012
|
+
shaderInstances: `${(shaderInstanceBytes / (1024 * 1024)).toFixed(2)} MB`,
|
|
745
1013
|
}
|
|
746
1014
|
}
|
|
747
1015
|
}
|
|
@@ -767,6 +1035,11 @@ export class ChunkMeshManager {
|
|
|
767
1035
|
this.meshPool.length = 0
|
|
768
1036
|
this.activeSections.clear()
|
|
769
1037
|
this.chunkBoxMaterial.dispose()
|
|
1038
|
+
this.shaderSectionRaycastBoxes.clear()
|
|
1039
|
+
this.globalBlockBuffer?.dispose()
|
|
1040
|
+
this.globalBlockBuffer = null
|
|
1041
|
+
this.cubeShaderMaterial?.dispose()
|
|
1042
|
+
this.cubeShaderMaterial = null
|
|
770
1043
|
// Drop any pending near-first reveal state and cancel safety timers.
|
|
771
1044
|
this.pendingNearReveal.clear()
|
|
772
1045
|
for (const timer of this.nearRevealTimers.values()) clearTimeout(timer)
|