minecraft-renderer 0.1.78 → 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/minecraft-renderer.js +28 -28
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +395 -395
- package/package.json +1 -1
- package/src/three/chunkMeshManager.ts +106 -35
- package/src/three/globalBlockBuffer.ts +8 -0
- package/src/three/globalLegacyBuffer.ts +257 -19
- package/src/three/legacyMultiDraw.ts +99 -0
- 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 +14 -4
|
@@ -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
|
+
})
|
|
@@ -1330,12 +1330,22 @@ export class WorldRendererThree extends WorldRendererCommon {
|
|
|
1330
1330
|
globalBuffer.suppressThreeDraw()
|
|
1331
1331
|
}
|
|
1332
1332
|
const globalLegacyBuffer = this.chunkMeshManager.globalLegacyBuffer
|
|
1333
|
-
if (globalLegacyBuffer
|
|
1334
|
-
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()
|
|
1335
1340
|
}
|
|
1336
1341
|
const globalLegacyBlendBuffer = this.chunkMeshManager.globalLegacyBlendBuffer
|
|
1337
|
-
if (globalLegacyBlendBuffer
|
|
1338
|
-
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()
|
|
1339
1349
|
}
|
|
1340
1350
|
this.chunkMeshManager.setLegacyCameraOrigin(camX, camY, camZ)
|
|
1341
1351
|
this.chunkMeshManager.updateCullDirtyFromCamera(cam, camX, camY, camZ)
|