minecraft-renderer 0.1.77 → 0.1.79
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/mesherWasm.js +23 -1
- package/dist/minecraft-renderer.js +86 -64
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +454 -432
- package/package.json +1 -1
- package/src/three/chunkMeshManager.ts +114 -36
- package/src/three/globalBlockBuffer.ts +8 -0
- package/src/three/globalLegacyBuffer.ts +257 -19
- package/src/three/legacyMultiDraw.ts +99 -0
- package/src/three/shaders/cubeBlockShader.ts +36 -1
- package/src/three/tests/chunkMeshManagerLegacy.test.ts +112 -4
- package/src/three/tests/globalLegacyBuffer.test.ts +163 -20
- package/src/three/tests/legacyMultiDraw.test.ts +109 -0
- package/src/three/tests/pr3PerFrameCpuCuts.test.ts +293 -0
- package/src/three/worldRendererThree.ts +20 -4
- package/src/wasm-mesher/tests/shaderCubeInstances.test.ts +12 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import { test, expect, vi, afterEach } from 'vitest'
|
|
3
|
+
import * as THREE from 'three'
|
|
4
|
+
|
|
5
|
+
vi.mock('../entity/EntityMesh', () => ({
|
|
6
|
+
getMesh: vi.fn(),
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
import { ChunkMeshManager } from '../chunkMeshManager'
|
|
10
|
+
import type { GlobalLegacyBuffer } from '../globalLegacyBuffer'
|
|
11
|
+
import type { WorldRendererThree } from '../worldRendererThree'
|
|
12
|
+
import type { MesherGeometryOutput } from '../../mesher-shared/shared'
|
|
13
|
+
|
|
14
|
+
function makeQuadArrays () {
|
|
15
|
+
const positions = new Float32Array([
|
|
16
|
+
-1, -1, -1,
|
|
17
|
+
-1, 1, -1,
|
|
18
|
+
-1, 1, 1,
|
|
19
|
+
-1, -1, 1,
|
|
20
|
+
])
|
|
21
|
+
const colors = new Float32Array(12).fill(1)
|
|
22
|
+
const skyLights = new Float32Array(4).fill(1)
|
|
23
|
+
const blockLights = new Float32Array(4).fill(0)
|
|
24
|
+
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1])
|
|
25
|
+
const indices = new Uint32Array([0, 1, 2, 0, 2, 3])
|
|
26
|
+
return { positions, colors, skyLights, blockLights, uvs, indices }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeBlendOnlyGeometry (): MesherGeometryOutput {
|
|
30
|
+
const blend = makeQuadArrays()
|
|
31
|
+
return {
|
|
32
|
+
sectionYNumber: 0,
|
|
33
|
+
chunkKey: '0,0',
|
|
34
|
+
sectionStartY: 0,
|
|
35
|
+
sectionEndY: 16,
|
|
36
|
+
sectionStartX: 0,
|
|
37
|
+
sectionEndX: 16,
|
|
38
|
+
sectionStartZ: 0,
|
|
39
|
+
sectionEndZ: 16,
|
|
40
|
+
sx: 8,
|
|
41
|
+
sy: 8,
|
|
42
|
+
sz: 8,
|
|
43
|
+
positions: new Float32Array(0),
|
|
44
|
+
normals: new Float32Array(0),
|
|
45
|
+
colors: new Float32Array(0),
|
|
46
|
+
skyLights: new Float32Array(0),
|
|
47
|
+
blockLights: new Float32Array(0),
|
|
48
|
+
uvs: new Float32Array(0),
|
|
49
|
+
indices: new Uint32Array(0),
|
|
50
|
+
indicesCount: 0,
|
|
51
|
+
using32Array: true,
|
|
52
|
+
tiles: {},
|
|
53
|
+
heads: {},
|
|
54
|
+
signs: {},
|
|
55
|
+
banners: {},
|
|
56
|
+
hadErrors: false,
|
|
57
|
+
blocksCount: 1,
|
|
58
|
+
blend: {
|
|
59
|
+
positions: blend.positions,
|
|
60
|
+
normals: new Float32Array(12),
|
|
61
|
+
colors: blend.colors,
|
|
62
|
+
skyLights: blend.skyLights,
|
|
63
|
+
blockLights: blend.blockLights,
|
|
64
|
+
uvs: blend.uvs,
|
|
65
|
+
indices: blend.indices,
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeOpaqueOnlyGeometry (sx = 8, sy = 8, sz = 8): MesherGeometryOutput {
|
|
71
|
+
const opaque = makeQuadArrays()
|
|
72
|
+
return {
|
|
73
|
+
sectionYNumber: 0,
|
|
74
|
+
chunkKey: '0,0',
|
|
75
|
+
sectionStartY: 0,
|
|
76
|
+
sectionEndY: 16,
|
|
77
|
+
sectionStartX: 0,
|
|
78
|
+
sectionEndX: 16,
|
|
79
|
+
sectionStartZ: 0,
|
|
80
|
+
sectionEndZ: 16,
|
|
81
|
+
sx,
|
|
82
|
+
sy,
|
|
83
|
+
sz,
|
|
84
|
+
positions: opaque.positions,
|
|
85
|
+
normals: new Float32Array(12),
|
|
86
|
+
colors: opaque.colors,
|
|
87
|
+
skyLights: opaque.skyLights,
|
|
88
|
+
blockLights: opaque.blockLights,
|
|
89
|
+
uvs: opaque.uvs,
|
|
90
|
+
indices: opaque.indices,
|
|
91
|
+
indicesCount: 6,
|
|
92
|
+
using32Array: true,
|
|
93
|
+
tiles: {},
|
|
94
|
+
heads: {},
|
|
95
|
+
signs: {},
|
|
96
|
+
banners: {},
|
|
97
|
+
hadErrors: false,
|
|
98
|
+
blocksCount: 1,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function makeMixedGeometry (): MesherGeometryOutput {
|
|
103
|
+
const opaque = makeQuadArrays()
|
|
104
|
+
const blend = makeQuadArrays()
|
|
105
|
+
return {
|
|
106
|
+
sectionYNumber: 0,
|
|
107
|
+
chunkKey: '0,0',
|
|
108
|
+
sectionStartY: 0,
|
|
109
|
+
sectionEndY: 16,
|
|
110
|
+
sectionStartX: 0,
|
|
111
|
+
sectionEndX: 16,
|
|
112
|
+
sectionStartZ: 0,
|
|
113
|
+
sectionEndZ: 16,
|
|
114
|
+
sx: 8,
|
|
115
|
+
sy: 8,
|
|
116
|
+
sz: 8,
|
|
117
|
+
positions: opaque.positions,
|
|
118
|
+
normals: new Float32Array(12),
|
|
119
|
+
colors: opaque.colors,
|
|
120
|
+
skyLights: opaque.skyLights,
|
|
121
|
+
blockLights: opaque.blockLights,
|
|
122
|
+
uvs: opaque.uvs,
|
|
123
|
+
indices: opaque.indices,
|
|
124
|
+
indicesCount: 6,
|
|
125
|
+
using32Array: true,
|
|
126
|
+
tiles: {},
|
|
127
|
+
heads: {},
|
|
128
|
+
signs: {},
|
|
129
|
+
banners: {},
|
|
130
|
+
hadErrors: false,
|
|
131
|
+
blocksCount: 2,
|
|
132
|
+
blend: {
|
|
133
|
+
positions: blend.positions,
|
|
134
|
+
normals: new Float32Array(12),
|
|
135
|
+
colors: blend.colors,
|
|
136
|
+
skyLights: blend.skyLights,
|
|
137
|
+
blockLights: blend.blockLights,
|
|
138
|
+
uvs: blend.uvs,
|
|
139
|
+
indices: blend.indices,
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function createManager (): ChunkMeshManager {
|
|
145
|
+
const scene = new THREE.Scene()
|
|
146
|
+
const material = new THREE.MeshBasicMaterial()
|
|
147
|
+
const worldRenderer = {
|
|
148
|
+
shaderCubeBlocksEnabled: () => false,
|
|
149
|
+
getModule: () => undefined,
|
|
150
|
+
sceneOrigin: {
|
|
151
|
+
track: () => {},
|
|
152
|
+
removeAndUntrack: () => {},
|
|
153
|
+
removeAndUntrackAll: () => {},
|
|
154
|
+
},
|
|
155
|
+
blockEntities: {},
|
|
156
|
+
worldRendererConfig: {},
|
|
157
|
+
} as unknown as WorldRendererThree
|
|
158
|
+
return new ChunkMeshManager(worldRenderer, scene, material, 256, 1)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type ManagerInternals = {
|
|
162
|
+
legacyCullSections: Map<string, { worldX: number, worldY: number, worldZ: number }>
|
|
163
|
+
registerLegacyCullSection: (key: string, wx: number, wy: number, wz: number) => void
|
|
164
|
+
maybeUnregisterLegacyCullSection: (key: string) => void
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getLegacyCullSections (manager: ChunkMeshManager): Map<string, { worldX: number, worldY: number, worldZ: number }> {
|
|
168
|
+
return (manager as unknown as ManagerInternals).legacyCullSections
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function makeCamera (x: number, y: number, z: number): THREE.PerspectiveCamera {
|
|
172
|
+
const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000)
|
|
173
|
+
camera.position.set(x, y, z)
|
|
174
|
+
camera.lookAt(8, 8, 8)
|
|
175
|
+
camera.updateMatrixWorld()
|
|
176
|
+
return camera
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
afterEach(() => {
|
|
180
|
+
vi.restoreAllMocks()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('legacyCullSections: gains key on blend add, removed after cleanup', () => {
|
|
184
|
+
const manager = createManager()
|
|
185
|
+
const key = '0,0,0'
|
|
186
|
+
|
|
187
|
+
manager.updateSection(key, makeBlendOnlyGeometry())
|
|
188
|
+
expect(getLegacyCullSections(manager).has(key)).toBe(true)
|
|
189
|
+
|
|
190
|
+
manager.cleanupSection(key)
|
|
191
|
+
expect(getLegacyCullSections(manager).has(key)).toBe(false)
|
|
192
|
+
|
|
193
|
+
manager.dispose()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test('legacyCullSections: mixed opaque+blend keeps key until both buffers cleared', () => {
|
|
197
|
+
const manager = createManager()
|
|
198
|
+
const key = '0,0,0'
|
|
199
|
+
|
|
200
|
+
manager.updateSection(key, makeMixedGeometry())
|
|
201
|
+
expect(getLegacyCullSections(manager).has(key)).toBe(true)
|
|
202
|
+
expect(manager.globalLegacyBuffer?.hasSection(key)).toBe(true)
|
|
203
|
+
expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(true)
|
|
204
|
+
|
|
205
|
+
manager.globalLegacyBuffer?.removeSection(key)
|
|
206
|
+
expect(getLegacyCullSections(manager).has(key)).toBe(true)
|
|
207
|
+
|
|
208
|
+
manager.globalLegacyBlendBuffer?.removeSection(key)
|
|
209
|
+
;(manager as unknown as ManagerInternals).maybeUnregisterLegacyCullSection(key)
|
|
210
|
+
expect(getLegacyCullSections(manager).has(key)).toBe(false)
|
|
211
|
+
|
|
212
|
+
manager.cleanupSection(key)
|
|
213
|
+
manager.dispose()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('updateSectionCullAndSort: same visible set skips span rebuild on camera move', () => {
|
|
217
|
+
const manager = createManager()
|
|
218
|
+
const key = '0,0,0'
|
|
219
|
+
manager.updateSection(key, makeBlendOnlyGeometry())
|
|
220
|
+
|
|
221
|
+
const blendBuffer = manager.globalLegacyBlendBuffer!
|
|
222
|
+
const updateDrawSpansSpy = vi.spyOn(blendBuffer, 'updateDrawSpans')
|
|
223
|
+
|
|
224
|
+
const camera1 = makeCamera(8, 8, 20)
|
|
225
|
+
manager.updateSectionCullAndSort(camera1, 8, 8, 20)
|
|
226
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
|
|
227
|
+
|
|
228
|
+
const camera2 = makeCamera(8, 8, 18)
|
|
229
|
+
manager.updateSectionCullAndSort(camera2, 8, 8, 18)
|
|
230
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
|
|
231
|
+
|
|
232
|
+
manager.cleanupSection(key)
|
|
233
|
+
manager.dispose()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
test('updateSectionCullAndSort: layoutVersion change forces span rebuild', () => {
|
|
237
|
+
const manager = createManager()
|
|
238
|
+
const key = '0,0,0'
|
|
239
|
+
manager.updateSection(key, makeBlendOnlyGeometry())
|
|
240
|
+
|
|
241
|
+
const blendBuffer = manager.globalLegacyBlendBuffer!
|
|
242
|
+
const updateDrawSpansSpy = vi.spyOn(blendBuffer, 'updateDrawSpans')
|
|
243
|
+
|
|
244
|
+
const camera = makeCamera(8, 8, 20)
|
|
245
|
+
manager.updateSectionCullAndSort(camera, 8, 8, 20)
|
|
246
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
|
|
247
|
+
|
|
248
|
+
blendBuffer.removeSection(key)
|
|
249
|
+
blendBuffer.addSection(
|
|
250
|
+
key,
|
|
251
|
+
makeBlendOnlyGeometry().blend!,
|
|
252
|
+
8, 8, 8,
|
|
253
|
+
)
|
|
254
|
+
;(manager as unknown as ManagerInternals).registerLegacyCullSection(key, 8, 8, 8)
|
|
255
|
+
|
|
256
|
+
manager.updateSectionCullAndSort(camera, 8, 8, 20)
|
|
257
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(2)
|
|
258
|
+
|
|
259
|
+
manager.cleanupSection(key)
|
|
260
|
+
manager.dispose()
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
function drainLegacyUploads (buffer: GlobalLegacyBuffer): void {
|
|
264
|
+
while (buffer.hasPendingUploads()) buffer.uploadDirtyRange()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
test('updateSectionCullAndSort: defrag finalize forces span rebuild with static camera', () => {
|
|
268
|
+
const manager = createManager()
|
|
269
|
+
const keys = ['0,0,0', '1,0,0', '2,0,0'] as const
|
|
270
|
+
|
|
271
|
+
for (let i = 0; i < keys.length; i++) {
|
|
272
|
+
manager.updateSection(keys[i]!, makeOpaqueOnlyGeometry(8, 8, 8 + i * 8))
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const opaqueBuf = manager.globalLegacyBuffer!
|
|
276
|
+
const updateDrawSpansSpy = vi.spyOn(opaqueBuf, 'updateDrawSpans')
|
|
277
|
+
const camera = makeCamera(8, 8, 20)
|
|
278
|
+
|
|
279
|
+
manager.updateSectionCullAndSort(camera, 8, 8, 20)
|
|
280
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(1)
|
|
281
|
+
|
|
282
|
+
opaqueBuf.removeSection('1,0,0')
|
|
283
|
+
drainLegacyUploads(opaqueBuf)
|
|
284
|
+
opaqueBuf.compactStep()
|
|
285
|
+
drainLegacyUploads(opaqueBuf)
|
|
286
|
+
opaqueBuf.compactStep()
|
|
287
|
+
|
|
288
|
+
manager.updateSectionCullAndSort(camera, 8, 8, 20)
|
|
289
|
+
expect(updateDrawSpansSpy).toHaveBeenCalledTimes(2)
|
|
290
|
+
|
|
291
|
+
for (const key of keys) manager.cleanupSection(key)
|
|
292
|
+
manager.dispose()
|
|
293
|
+
})
|
|
@@ -549,6 +549,12 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
549
549
|
this.chunkMeshManager.syncCubeShaderUniforms()
|
|
550
550
|
this.chunkMeshManager.syncLegacyShaderUniforms()
|
|
551
551
|
})
|
|
552
|
+
const pushShading = () => {
|
|
553
|
+
const cfg = this.worldRendererConfig
|
|
554
|
+
this.chunkMeshManager.setShadingTheme(cfg.shadingTheme, cfg.cardinalLight)
|
|
555
|
+
}
|
|
556
|
+
this.onReactiveConfigUpdated('shadingTheme', pushShading)
|
|
557
|
+
this.onReactiveConfigUpdated('cardinalLight', pushShading)
|
|
552
558
|
this.onReactiveConfigUpdated('futuristicReveal', () => {
|
|
553
559
|
this.updateModulesFromConfig()
|
|
554
560
|
})
|
|
@@ -1324,12 +1330,22 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1324
1330
|
globalBuffer.suppressThreeDraw()
|
|
1325
1331
|
}
|
|
1326
1332
|
const globalLegacyBuffer = this.chunkMeshManager.globalLegacyBuffer
|
|
1327
|
-
if (globalLegacyBuffer
|
|
1328
|
-
globalLegacyBuffer.
|
|
1333
|
+
if (globalLegacyBuffer) {
|
|
1334
|
+
globalLegacyBuffer.setDebugOverlay(this.displayOptions.inWorldRenderingConfig.enableDebugOverlay)
|
|
1335
|
+
globalLegacyBuffer.compactStep()
|
|
1336
|
+
if (globalLegacyBuffer.hasPendingUploads()) {
|
|
1337
|
+
globalLegacyBuffer.uploadDirtyRange()
|
|
1338
|
+
}
|
|
1339
|
+
globalLegacyBuffer.suppressThreeDraw()
|
|
1329
1340
|
}
|
|
1330
1341
|
const globalLegacyBlendBuffer = this.chunkMeshManager.globalLegacyBlendBuffer
|
|
1331
|
-
if (globalLegacyBlendBuffer
|
|
1332
|
-
globalLegacyBlendBuffer.
|
|
1342
|
+
if (globalLegacyBlendBuffer) {
|
|
1343
|
+
globalLegacyBlendBuffer.setDebugOverlay(this.displayOptions.inWorldRenderingConfig.enableDebugOverlay)
|
|
1344
|
+
globalLegacyBlendBuffer.compactStep()
|
|
1345
|
+
if (globalLegacyBlendBuffer.hasPendingUploads()) {
|
|
1346
|
+
globalLegacyBlendBuffer.uploadDirtyRange()
|
|
1347
|
+
}
|
|
1348
|
+
globalLegacyBlendBuffer.suppressThreeDraw()
|
|
1333
1349
|
}
|
|
1334
1350
|
this.chunkMeshManager.setLegacyCameraOrigin(camX, camY, camZ)
|
|
1335
1351
|
this.chunkMeshManager.updateCullDirtyFromCamera(cam, camX, camY, camZ)
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '../bridge/shaderCubeBridge'
|
|
18
18
|
import { GlobalBlockBuffer } from '../../three/globalBlockBuffer'
|
|
19
19
|
import { buildVisibleCubeSpans } from '../../three/cubeDrawSpans'
|
|
20
|
-
import { createCubeBlockMaterial, computeSectionOriginRel } from '../../three/shaders/cubeBlockShader'
|
|
20
|
+
import { createCubeBlockMaterial, computeSectionOriginRel, setCubeShadingTheme } from '../../three/shaders/cubeBlockShader'
|
|
21
21
|
import * as THREE from 'three'
|
|
22
22
|
import { renderWasmOutputToGeometry } from '../bridge/render-from-wasm'
|
|
23
23
|
|
|
@@ -36,6 +36,17 @@ beforeEach(() => {
|
|
|
36
36
|
resetShaderCubeResources()
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
+
test('setCubeShadingTheme: maps theme and cardinalLight to shader uniforms', () => {
|
|
40
|
+
const mat = createCubeBlockMaterial()
|
|
41
|
+
setCubeShadingTheme(mat, 'vanilla', 'default')
|
|
42
|
+
expect(mat.uniforms.u_shadingTheme.value).toBe(0)
|
|
43
|
+
expect(mat.uniforms.u_cardinalLight.value).toBe(0)
|
|
44
|
+
setCubeShadingTheme(mat, 'high-contrast', 'nether')
|
|
45
|
+
expect(mat.uniforms.u_shadingTheme.value).toBe(1)
|
|
46
|
+
expect(mat.uniforms.u_cardinalLight.value).toBe(1)
|
|
47
|
+
mat.dispose()
|
|
48
|
+
})
|
|
49
|
+
|
|
39
50
|
test('packWord2: AO diagonal flip sets bit 12', () => {
|
|
40
51
|
const words: number[] = []
|
|
41
52
|
const block = {
|