mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-04-02 05:27:24 +02:00
103 lines
3 KiB
TypeScript
103 lines
3 KiB
TypeScript
import {
|
|
Group,
|
|
OrthographicCamera,
|
|
Scene as ThreeScene,
|
|
WebGLRenderer,
|
|
} from "three";
|
|
|
|
export interface WebGLLayerConfig {
|
|
id: string;
|
|
setup: (group: Group) => void; // called once after WebGL2 confirmed; add meshes to group
|
|
render?: (group: Group) => void; // called each frame before renderer.render(); update uniforms/geometry
|
|
dispose: (group: Group) => void; // called on unregister(); dispose all GPU objects in group
|
|
}
|
|
interface RegisteredLayer {
|
|
config: WebGLLayerConfig;
|
|
group: Group;
|
|
}
|
|
|
|
export class WebGL2LayerClass {
|
|
private renderer: WebGLRenderer | null = null;
|
|
private camera: OrthographicCamera | null = null;
|
|
private scene: ThreeScene | null = null;
|
|
private layers: Map<string, RegisteredLayer> = new Map();
|
|
private pendingConfigs: WebGLLayerConfig[] = []; // queue for register() before init()
|
|
private rafId: number | null = null;
|
|
|
|
init(): boolean {
|
|
const canvas = Scene.getCanvas();
|
|
const { graphWidth, graphHeight } = Scene.getViewport();
|
|
|
|
this.renderer = new WebGLRenderer({
|
|
canvas,
|
|
antialias: false,
|
|
alpha: true,
|
|
});
|
|
this.renderer.setPixelRatio(window.devicePixelRatio || 1);
|
|
canvas.style.width = `${graphWidth}px`;
|
|
canvas.style.height = `${graphHeight}px`;
|
|
this.renderer.setSize(graphWidth, graphHeight, false);
|
|
this.scene = new ThreeScene();
|
|
this.camera = new OrthographicCamera(0, graphWidth, 0, graphHeight, -1, 1);
|
|
|
|
// Process pre-init registrations (register() before init() is explicitly safe)
|
|
for (const config of this.pendingConfigs) {
|
|
const group = new Group();
|
|
config.setup(group);
|
|
this.scene.add(group);
|
|
this.layers.set(config.id, { config, group });
|
|
}
|
|
this.pendingConfigs = [];
|
|
|
|
return true;
|
|
}
|
|
|
|
private syncTransform() {
|
|
if (!this.camera) return;
|
|
const { bottom, left, right, top } = Scene.getCameraBounds();
|
|
|
|
this.camera.left = left;
|
|
this.camera.right = right;
|
|
this.camera.top = top;
|
|
this.camera.bottom = bottom;
|
|
this.camera.updateProjectionMatrix();
|
|
}
|
|
|
|
private render() {
|
|
if (!this.renderer || !this.scene || !this.camera) return;
|
|
this.syncTransform();
|
|
for (const layer of this.layers.values()) {
|
|
if (layer.group.visible && layer.config.render)
|
|
layer.config.render(layer.group);
|
|
}
|
|
this.renderer.render(this.scene, this.camera);
|
|
}
|
|
|
|
register(config: WebGLLayerConfig) {
|
|
if (!this.scene) {
|
|
// init() has not been called yet — queue for processing in init()
|
|
this.pendingConfigs.push(config);
|
|
return;
|
|
}
|
|
|
|
// Post-init registration: create group immediately
|
|
const group = new Group();
|
|
config.setup(group);
|
|
this.scene.add(group);
|
|
this.layers.set(config.id, { config, group });
|
|
}
|
|
|
|
rerender() {
|
|
if (this.rafId !== null) return;
|
|
this.rafId = requestAnimationFrame(() => {
|
|
this.rafId = null;
|
|
this.render();
|
|
});
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
var WebGLLayer: WebGL2LayerClass;
|
|
}
|
|
|
|
window.WebGLLayer = new WebGL2LayerClass();
|