124 lines
3.9 KiB
JavaScript
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);
|
|
});
|
|
});
|