//@ts-nocheck import { test, expect, vi } from 'vitest' import * as THREE from 'three' vi.mock('../entity/EntityMesh', () => ({ getMesh: vi.fn(), })) import { ChunkMeshManager } from '../chunkMeshManager' import type { WorldRendererThree } from '../worldRendererThree' import type { MesherGeometryOutput } from '../../mesher-shared/shared' import { renderWasmOutputToGeometry } from '../../wasm-mesher/bridge/render-from-wasm' function makeQuadArrays () { const positions = new Float32Array([ -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, ]) const colors = new Float32Array(12).fill(1) const skyLights = new Float32Array(4).fill(1) const blockLights = new Float32Array(4).fill(0) const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]) const indices = new Uint32Array([0, 1, 2, 0, 2, 3]) return { positions, colors, skyLights, blockLights, uvs, indices } } function makeBlendOnlyGeometry (): MesherGeometryOutput { const blend = makeQuadArrays() return { sectionYNumber: 0, chunkKey: '0,0', sectionStartY: 0, sectionEndY: 16, sectionStartX: 0, sectionEndX: 16, sectionStartZ: 0, sectionEndZ: 16, sx: 8, sy: 8, sz: 8, positions: new Float32Array(0), normals: new Float32Array(0), colors: new Float32Array(0), skyLights: new Float32Array(0), blockLights: new Float32Array(0), uvs: new Float32Array(0), indices: new Uint32Array(0), indicesCount: 0, using32Array: true, tiles: {}, heads: {}, signs: {}, banners: {}, hadErrors: false, blocksCount: 1, blend: { positions: blend.positions, normals: new Float32Array(12), colors: blend.colors, skyLights: blend.skyLights, blockLights: blend.blockLights, uvs: blend.uvs, indices: blend.indices, }, } } function makeMixedGeometry (): MesherGeometryOutput { const opaque = makeQuadArrays() const blend = makeQuadArrays() return { sectionYNumber: 0, chunkKey: '0,0', sectionStartY: 0, sectionEndY: 16, sectionStartX: 0, sectionEndX: 16, sectionStartZ: 0, sectionEndZ: 16, sx: 8, sy: 8, sz: 8, positions: opaque.positions, normals: new Float32Array(12), colors: opaque.colors, skyLights: opaque.skyLights, blockLights: opaque.blockLights, uvs: opaque.uvs, indices: opaque.indices, indicesCount: 6, using32Array: true, tiles: {}, heads: {}, signs: {}, banners: {}, hadErrors: false, blocksCount: 2, blend: { positions: blend.positions, normals: new Float32Array(12), colors: blend.colors, skyLights: blend.skyLights, blockLights: blend.blockLights, uvs: blend.uvs, indices: blend.indices, }, } } function makeInvalidBlendGeometry (): MesherGeometryOutput { const geo = makeBlendOnlyGeometry() const blend = geo.blend! return { ...geo, blend: { ...blend, positions: new Float32Array([0, 0, 0, 1, 0, 0, 2, 0, 0]), indices: new Uint32Array([0, 1, 2, 0, 2, 1, 3]), }, } } type ManagerOptions = { revealDefer?: boolean shaderCubes?: boolean finishedChunks?: Record } function makeShaderCubeOnlyGeometry (): MesherGeometryOutput { const block = { position: [0, 0, 0] as [number, number, number], block_state_id: 1, visible_faces: (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5), ao_data: Array.from({ length: 6 }, () => [3, 3, 3, 3]), light_data: Array.from({ length: 6 }, () => [1, 1, 1, 1]), light_combined: Array.from({ length: 6 }, () => [255, 255, 255, 255]), } const out = renderWasmOutputToGeometry( { blocks: [block], block_count: 1, block_iterations: 0 }, '1.16.5', '0,0,0', { x: 8, y: 8, z: 8 }, undefined, { shaderCubes: true }, ) return { sectionYNumber: 0, chunkKey: '0,0', sectionStartY: 0, sectionEndY: 16, sectionStartX: 0, sectionEndX: 16, sectionStartZ: 0, sectionEndZ: 16, sx: 8, sy: 8, sz: 8, positions: new Float32Array(0), normals: new Float32Array(0), colors: new Float32Array(0), skyLights: new Float32Array(0), blockLights: new Float32Array(0), uvs: new Float32Array(0), indices: new Uint32Array(0), indicesCount: 0, using32Array: true, tiles: {}, heads: {}, signs: {}, banners: {}, hadErrors: false, blocksCount: 1, shaderCubes: out.shaderCubes, } } function makeCullCamera (): THREE.PerspectiveCamera { const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000) camera.position.set(8, 8, 20) camera.lookAt(8, 8, 8) camera.updateMatrixWorld() return camera } function createManager (opts: ManagerOptions = {}): ChunkMeshManager { const scene = new THREE.Scene() const material = new THREE.MeshBasicMaterial() const revealModule = opts.revealDefer ? { shouldDeferSectionGeometry: () => true, } : undefined const worldRenderer = { shaderCubeBlocksEnabled: () => opts.shaderCubes ?? false, getModule: (name: string) => (name === 'futuristicReveal' ? revealModule : undefined), sceneOrigin: { track: () => {}, removeAndUntrack: () => {}, removeAndUntrackAll: () => {}, }, blockEntities: {}, worldRendererConfig: opts.shaderCubes ? { wasmMesher: true } : {}, displayOptions: { inWorldRenderingConfig: {} }, finishedChunks: opts.finishedChunks ?? (opts.shaderCubes ? { '0,0': true } : {}), } as unknown as WorldRendererThree return new ChunkMeshManager(worldRenderer, scene, material, 256, 1) } test('ChunkMeshManager: blend section routes to global blend buffer', () => { const manager = createManager() const key = '0,0,0' const geo = makeBlendOnlyGeometry() manager.updateSection(key, geo) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(true) expect(manager.sectionObjects[key]?.hasBlendMesh).toBe(false) expect(manager.sectionUsesPooledLegacyMesh(key)).toBe(false) manager.cleanupSection(key) manager.dispose() }) test('ChunkMeshManager: cleanup removes blend from global buffer', () => { const manager = createManager() const key = '0,0,0' manager.updateSection(key, makeBlendOnlyGeometry()) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(true) manager.cleanupSection(key) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(false) manager.dispose() }) test('ChunkMeshManager: hidden section excluded from draw spans', () => { const manager = createManager() const key = '0,0,0' manager.updateSection(key, makeBlendOnlyGeometry()) const section = manager.sectionObjects[key]! section.visible = false const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000) camera.position.set(8, 8, 20) camera.lookAt(8, 8, 8) camera.updateMatrixWorld() manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalLegacyBlendBuffer?.getVisibleIndexSpans().length).toBe(0) section.visible = true manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalLegacyBlendBuffer?.getVisibleIndexSpans().length).toBeGreaterThan(0) manager.cleanupSection(key) manager.dispose() }) test('ChunkMeshManager: reveal defer blend migrates to global and releases pool', () => { const manager = createManager({ revealDefer: true }) const key = '0,0,0' const geo = makeBlendOnlyGeometry() manager.updateSection(key, geo) expect(manager.sectionObjects[key]?.hasBlendMesh).toBe(true) expect(manager.sectionObjects[key]?.deferredLegacyBlend).toBeDefined() expect(manager.globalLegacyBlendBuffer?.hasSection(key) ?? false).toBe(false) expect(manager.sectionUsesPooledLegacyMesh(key)).toBe(true) manager.migrateDeferredLegacyToGlobal(key) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(true) expect(manager.sectionObjects[key]?.hasBlendMesh).toBe(false) expect(manager.sectionObjects[key]?.deferredLegacyBlend).toBeUndefined() expect(manager.sectionUsesPooledLegacyMesh(key)).toBe(false) manager.cleanupSection(key) manager.dispose() }) test('ChunkMeshManager: invalid blend geometry falls back to pooled mesh', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) const manager = createManager() const key = '0,0,0' manager.updateSection(key, makeInvalidBlendGeometry()) expect(warn).toHaveBeenCalledWith(expect.stringContaining('blend invariant violation')) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(false) expect(manager.sectionObjects[key]?.hasBlendMesh).toBe(true) expect(manager.sectionUsesPooledLegacyMesh(key)).toBe(true) warn.mockRestore() manager.cleanupSection(key) manager.dispose() }) test('ChunkMeshManager: raycastGlobalLegacySections rejects off-ray sections within center distance', () => { const manager = createManager() const onRayKey = '0,0,0' const offRayKey = '0,2,0' manager.updateSection(onRayKey, makeBlendOnlyGeometry()) const offRayGeo = makeBlendOnlyGeometry() offRayGeo.sz = 40 manager.updateSection(offRayKey, offRayGeo) expect(manager.globalLegacyBlendBuffer?.hasSection(onRayKey)).toBe(true) expect(manager.globalLegacyBlendBuffer?.hasSection(offRayKey)).toBe(true) expect(manager.sectionObjects[offRayKey]?.worldZ).toBe(40) const origin = new THREE.Vector3(4, 8, 8) const direction = new THREE.Vector3(1, 0, 0).normalize() const raycaster = new THREE.Raycaster(origin, direction) raycaster.far = 4 const hit = manager.raycastGlobalLegacySections(raycaster, origin, 80) expect(hit).toBeDefined() expect(hit!).toBeGreaterThan(2) expect(hit!).toBeLessThan(4) manager.cleanupSection(onRayKey) manager.cleanupSection(offRayKey) manager.dispose() }) test('ChunkMeshManager: mixed opaque and blend route to separate global buffers', () => { const manager = createManager() const key = '0,0,0' manager.updateSection(key, makeMixedGeometry()) expect(manager.globalLegacyBuffer?.hasSection(key)).toBe(true) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(true) expect(manager.sectionObjects[key]?.hasBlendMesh).toBe(false) expect(manager.sectionUsesPooledLegacyMesh(key)).toBe(false) manager.cleanupSection(key) expect(manager.globalLegacyBuffer?.hasSection(key)).toBe(false) expect(manager.globalLegacyBlendBuffer?.hasSection(key)).toBe(false) manager.dispose() }) test('ChunkMeshManager: hidden cube section excluded from draw spans', () => { const manager = createManager({ shaderCubes: true }) const key = '0,0,0' manager.updateSection(key, makeShaderCubeOnlyGeometry()) const section = manager.sectionObjects[key]! expect(manager.globalBlockBuffer?.hasSection(key)).toBe(true) const camera = makeCullCamera() manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalBlockBuffer?.getVisibleSpans().length).toBeGreaterThan(0) section.visible = false manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalBlockBuffer?.getVisibleSpans().length).toBe(0) section.visible = true manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalBlockBuffer?.getVisibleSpans().length).toBeGreaterThan(0) manager.cleanupSection(key) manager.dispose() }) test('ChunkMeshManager: finishChunkDisplay reveals cube spans and marks cull dirty', () => { const manager = createManager({ shaderCubes: true, finishedChunks: {} }) const key = '0,0,0' manager.updateSection(key, makeShaderCubeOnlyGeometry()) expect(manager.sectionObjects[key]?.visible).toBe(false) expect(manager.globalBlockBuffer?.hasSection(key)).toBe(true) const camera = makeCullCamera() manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalBlockBuffer?.getVisibleSpans().length).toBe(0) const markCullDirtySpy = vi.spyOn(manager, 'markCullDirty') manager.finishChunkDisplay('0,0') expect(manager.sectionObjects[key]?.visible).toBe(true) expect(markCullDirtySpy).toHaveBeenCalled() manager.updateSectionCullAndSort(camera, 8, 8, 20) expect(manager.globalBlockBuffer?.getVisibleSpans().length).toBeGreaterThan(0) markCullDirtySpy.mockRestore() manager.cleanupSection(key) manager.dispose() })