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.
@@ -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?.hasPendingUploads()) {
1328
- globalLegacyBuffer.uploadDirtyRange()
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?.hasPendingUploads()) {
1332
- globalLegacyBlendBuffer.uploadDirtyRange()
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 = {