The log output Babylon.js v9.25.0 - WebGL2 is fantastic news! The SwANGLE
pipeline successfully initialized a WebGL2 context, meaning the graphics
hurdle is officially cleared.
Now, we just need to fix the two JavaScript errors.
1. Fix worker_threads (The Node vs. Browser Issue)
The error Module "worker_threads" has been externalized happens because
worker_threads is a Node.js-exclusive library. Since you originally wrote your
engine for a Node environment, your mesh_worker.ts is likely using parentPort
to communicate.
In the browser, Web Workers use the global self object instead.
How to fix ../3D-TS/mesh_worker.ts:
You need to replace the Node API with the browser API.
Remove this:
import { parentPort } from 'worker_threads';
parentPort.on('message', (message) => { ... });
parentPort.postMessage({ ... });
Replace it with this:
/ No imports needed for the worker context in the browser
self.onmessage = (event) => {
const message = event.data; // Browser wraps the payload in event.data
// ... your existing logic ...
};
self.postMessage({ ... });
2. Fix ChunkRenderer is not defined
This error occurs when the ChunkRenderer class is instantiated before it is
fully defined in the file, or if parts of the file were accidentally omitted
during copy-pasting.
Here is the complete, cohesive /mnt/mesh_shared/3d/web/src/renderer.ts file to
ensure everything runs in the correct order. Overwrite your current
renderer.ts with this:
import {
Engine, Scene, Vector3, UniversalCamera, HemisphericLight,
Mesh, VertexData, StandardMaterial
} from '@babylonjs/core';
/ Ensure alignment with the established voxel memory arena
const CHUNK_SIZE_X = 8;
const CHUNK_SIZE_Y = 8;
const CHUNK_SIZE_Z = 8;
class ChunkRenderer {
private scene: Scene;
private chunkMeshes: Map<string, Mesh> = new Map();
private worker: Worker;
constructor(scene: Scene, worker: Worker) {
this.scene = scene;
this.worker = worker;
this.setupWorkerListener();
}
private setupWorkerListener() {
this.worker.addEventListener('message', (event) => {
const { type, chunkId, positions } = event.data;
if (type === 'CHUNK_MESH_READY' && positions instanceof
Float32Array) {
this.buildChunkMesh(chunkId, positions);
}
});
}
private buildChunkMesh(chunkId: string, positions: Float32Array) {
let mesh = this.chunkMeshes.get(chunkId);
if (!mesh) {
mesh = new Mesh(`chunk_${chunkId}`, this.scene);
const material = new StandardMaterial(`mat_${chunkId}`,
this.scene);
material.wireframe = false;
material.backFaceCulling = true;
mesh.material = material;
this.chunkMeshes.set(chunkId, mesh);
}
const vertexCount = positions.length / 3;
const indices = new Int32Array(vertexCount);
for (let i = 0; i < vertexCount; i++) {
indices[i] = i;
}
const normals = new Float32Array(positions.length);
VertexData.ComputeNormals(positions, indices, normals);
const vertexData = new VertexData();
vertexData.positions = positions;
vertexData.indices = indices;
vertexData.normals = normals;
vertexData.applyToMesh(mesh, true);
}
}
/ --- Initialization & Integration ---
const canvas = document.getElementById('renderCanvas') as HTMLCanvasElement;
const engine = new Engine(canvas, true);
const scene = new Scene(engine);
const camera = new UniversalCamera("UniversalCamera", new Vector3(CHUNK_SIZE_X
* 1.5, CHUNK_SIZE_Y * 1.5, -CHUNK_SIZE_Z), scene);
camera.setTarget(new Vector3(CHUNK_SIZE_X / 2, CHUNK_SIZE_Y / 2, CHUNK_SIZE_Z
/ 2));
camera.attachControl(canvas, true);
const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene);
light.intensity = 0.7;
/ Initialize the Worker pointing to the 3D-TS folder
const chunkWorker = new Worker(
new URL('../../3D-TS/mesh_worker.ts', import.meta.url),
{ type: 'module' }
);
/ Instantiate after the class is defined
const chunkRenderer = new ChunkRenderer(scene, chunkWorker);
engine.runRenderLoop(() => {
scene.render();
});
window.addEventListener('resize', () => {
engine.resize();
});
/ Hook up the UI button
const updateBtn = document.getElementById('updateChunkBtn');
if (updateBtn) {
updateBtn.addEventListener('click', () => {
const chunkId = "0_0_0";
const editCommand = {
type: 'EDIT_CHUNK',
chunkId: chunkId,
bounds: [CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z],
x: 4, y: 4, z: 4,
blockType: 1
};
chunkWorker.postMessage(editCommand);
console.log(`Sent edit command to worker for chunk ${chunkId}.`);
});
}
Once you make these two changes, reload the browser. The console should be
completely clean. If you click the "Send Edit" button, what does the worker
send back?