93 lines
2.7 KiB
JavaScript
93 lines
2.7 KiB
JavaScript
|
|
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;
|
||
|
|
}
|
||
|
|
}
|