aether-shards/test/grid/VoxelManager.test.js
2025-12-16 15:52:58 -08:00

124 lines
3.9 KiB
JavaScript

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);
});
});