import * as THREE from "three"; /** * VoxelManager.js * Handles the Three.js rendering of the VoxelGrid. * Uses InstancedMesh for performance (1 draw call for 10,000 blocks). */ export class VoxelManager { constructor(grid, scene) { this.grid = grid; this.scene = scene; this.mesh = null; // Define Material Palette (ID -> Color) this.palette = [ null, // 0: Air (Invisible) new THREE.Color(0x888888), // 1: Stone (Grey) new THREE.Color(0x8b4513), // 2: Dirt (Brown) new THREE.Color(0x228b22), // 3: Grass (Green) new THREE.Color(0x00f0ff), // 4: Aether Crystal (Cyan) ]; this.init(); } init() { // Create geometry once const geometry = new THREE.BoxGeometry(1, 1, 1); // Basic material allows for coloring individual instances const material = new THREE.MeshLambertMaterial({ color: 0xffffff }); // Calculate max capacity based on grid size const count = this.grid.width * this.grid.height * this.grid.depth; // Create the InstancedMesh this.mesh = new THREE.InstancedMesh(geometry, material, count); this.mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage); // Add to scene this.scene.add(this.mesh); } /** * Rebuilds the visual mesh based on the grid data. * Call this in the game loop if grid.dirty is true. */ update() { if (!this.grid.dirty) return; let instanceId = 0; const dummy = new THREE.Object3D(); for (let y = 0; y < this.grid.height; y++) { for (let z = 0; z < this.grid.depth; z++) { for (let x = 0; x < this.grid.width; x++) { const typeId = this.grid.getVoxel(x, y, z); if (typeId !== 0) { // Position the dummy object dummy.position.set(x, y, z); dummy.updateMatrix(); // Update the instance matrix this.mesh.setMatrixAt(instanceId, dummy.matrix); // Update the instance color based on ID const color = this.palette[typeId] || new THREE.Color(0xff00ff); // Magenta = Error this.mesh.setColorAt(instanceId, color); instanceId++; } } } } // Hide unused instances by scaling them to zero (or moving them to infinity) // For simplicity in this prototype, we define count as max possible, // but in production, we would manage the count property more dynamically. this.mesh.count = instanceId; this.mesh.instanceMatrix.needsUpdate = true; // instanceColor is lazy-created by Three.js only when setColorAt is called. // If the grid is empty, instanceColor might be null. if (this.mesh.instanceColor) { this.mesh.instanceColor.needsUpdate = true; } this.grid.dirty = false; } }