import { expect } from "@esm-bundle/chai"; import * as THREE from "three"; import sinon from "sinon"; import { VoxelManager } from "../../src/grid/VoxelManager.js"; import { VoxelGrid } from "../../src/grid/VoxelGrid.js"; describe("Phase 1: VoxelManager (Renderer)", () => { let grid; let scene; let manager; beforeEach(() => { // Setup a basic scene and grid scene = new THREE.Scene(); grid = new VoxelGrid(4, 4, 4); // Small 4x4x4 grid manager = new VoxelManager(grid, scene); }); it("CoA 1: Should initialize and add InstancedMesh to the scene", () => { // Check if something was added to the scene expect(scene.children.length).to.equal(1); const mesh = scene.children[0]; expect(mesh).to.be.instanceOf(THREE.InstancedMesh); // Max capacity should match grid size expect(mesh.count).to.equal(4 * 4 * 4); }); it("CoA 2: Should update visual instances based on grid data", () => { // 1. Setup Data: Place 3 voxels grid.setVoxel(0, 0, 0, 1); // Stone grid.setVoxel(1, 0, 0, 2); // Dirt grid.setVoxel(0, 1, 0, 3); // Grass // 2. Act: Trigger Update // (VoxelGrid sets dirty=true automatically on setVoxel) manager.update(); // 3. Assert: Mesh count should represent only ACTIVE voxels // Note: In the implementation, we update `mesh.count` to the number of visible instances const mesh = manager.mesh; expect(mesh.count).to.equal(3); }); it("CoA 3: Should respect the Dirty Flag (Performance)", () => { // Setup: Add a voxel so instanceColor buffer is initialized by Three.js grid.setVoxel(0, 0, 0, 1); const mesh = manager.mesh; // 1. First Update (Dirty is true by default) manager.update(); expect(grid.dirty).to.be.false; // Capture current versions. Three.js increments these when needsUpdate = true. const matrixVersion = mesh.instanceMatrix.version; // instanceColor might be null if no color set logic ran, but manager.update() ensures it if voxels exist. const colorVersion = mesh.instanceColor ? mesh.instanceColor.version : -1; // 2. Call Update again (Dirty is false) manager.update(); // 3. Assert: Versions should NOT have incremented expect(mesh.instanceMatrix.version).to.equal(matrixVersion); if (mesh.instanceColor) { expect(mesh.instanceColor.version).to.equal(colorVersion); } // 4. Change data -> Dirty becomes true grid.setVoxel(3, 3, 3, 1); expect(grid.dirty).to.be.true; // 5. Update again manager.update(); // 6. Assert: Versions SHOULD increment expect(mesh.instanceMatrix.version).to.be.greaterThan(matrixVersion); if (mesh.instanceColor) { expect(mesh.instanceColor.version).to.be.greaterThan(colorVersion); } }); it("CoA 4: Should apply correct colors from palette", () => { // ID 4 is Cyan (Aether Crystal) based on Manager code grid.setVoxel(0, 0, 0, 4); manager.update(); const mesh = manager.mesh; const color = new THREE.Color(); // Get color of the first instance (index 0) mesh.getColorAt(0, color); // Check against the palette definition in VoxelManager const expectedColor = new THREE.Color(0x00f0ff); // Cyan expect(color.r).to.be.closeTo(expectedColor.r, 0.01); expect(color.g).to.be.closeTo(expectedColor.g, 0.01); expect(color.b).to.be.closeTo(expectedColor.b, 0.01); }); it("CoA 5: Should position instances correctly in 3D space", () => { const targetX = 2; const targetY = 3; const targetZ = 1; grid.setVoxel(targetX, targetY, targetZ, 1); manager.update(); const mesh = manager.mesh; const matrix = new THREE.Matrix4(); // Get matrix of first instance mesh.getMatrixAt(0, matrix); const position = new THREE.Vector3(); position.setFromMatrixPosition(matrix); expect(position.x).to.equal(targetX); expect(position.y).to.equal(targetY); expect(position.z).to.equal(targetZ); }); });