multi target renderer instead of background scissor
This commit is contained in:
75
engine.js
75
engine.js
@@ -62,20 +62,15 @@ function compileShader(gl, type, src) {
|
||||
|
||||
export class Engine {
|
||||
constructor() {
|
||||
// One solitary hidden canvas to drive all WebGL multi-target logic
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.style.position = 'fixed';
|
||||
this.canvas.style.top = '0';
|
||||
this.canvas.style.left = '0';
|
||||
this.canvas.style.width = '100vw';
|
||||
this.canvas.style.height = '100vh';
|
||||
this.canvas.style.zIndex = '-1';
|
||||
this.canvas.style.display = 'none';
|
||||
document.body.appendChild(this.canvas);
|
||||
|
||||
this.gl = this.canvas.getContext('webgl2', {antialias: true, alpha: true});
|
||||
const gl = this.gl;
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
gl.enable(gl.SCISSOR_TEST);
|
||||
|
||||
const vs = compileShader(gl, gl.VERTEX_SHADER, VS_SRC);
|
||||
const fs = compileShader(gl, gl.FRAGMENT_SHADER, FS_SRC);
|
||||
@@ -93,7 +88,6 @@ export class Engine {
|
||||
|
||||
this.atlasTexture = gl.createTexture();
|
||||
|
||||
// --- Initialize the new Resource Pipeline ---
|
||||
this.cache = new Cache('assets');
|
||||
this.atlas = new TextureHandler(256);
|
||||
this.cache.register('blockstates', new BlockstateHandler());
|
||||
@@ -103,22 +97,18 @@ export class Engine {
|
||||
this.dioramas = [];
|
||||
this.worldMeshes = new Map();
|
||||
this.renderRequested = false;
|
||||
|
||||
this.observedWorlds = new Set();
|
||||
this.updateRequested = false;
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.resize();
|
||||
this.requestRender();
|
||||
});
|
||||
window.addEventListener('scroll', () => this.requestRender(), {passive: true});
|
||||
this.resize();
|
||||
window.addEventListener('resize', () => this.requestRender());
|
||||
|
||||
// to make sure offscreen dioramas are rendered as soon as they become visible
|
||||
window.addEventListener('scroll', () => this.requestRender());
|
||||
}
|
||||
|
||||
requestUpdate() {
|
||||
if (!this.updateRequested) {
|
||||
this.updateRequested = true;
|
||||
// Add 'async' here and 'await' updateAll
|
||||
Promise.resolve().then(async () => {
|
||||
await this.updateAll();
|
||||
this.updateRequested = false;
|
||||
@@ -136,14 +126,9 @@ export class Engine {
|
||||
}
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.canvas.width = window.innerWidth;
|
||||
this.canvas.height = window.innerHeight;
|
||||
}
|
||||
|
||||
addDiorama(diorama) {
|
||||
this.dioramas.push(diorama);
|
||||
if (!this.observedWorlds.has(diorama.worlds)) {
|
||||
if (!this.observedWorlds.has(diorama.world)) {
|
||||
this.observedWorlds.add(diorama.world);
|
||||
diorama.world.subscribe(() => this.requestUpdate());
|
||||
}
|
||||
@@ -153,14 +138,12 @@ export class Engine {
|
||||
const uniqueWorlds = new Set(this.dioramas.map(d => d.world));
|
||||
if (uniqueWorlds.size === 0) return;
|
||||
|
||||
// --- SWEEP 1: Collect & Fetch Blockstates ---
|
||||
const uniqueBlockIds = new Set();
|
||||
for (const world of uniqueWorlds) {
|
||||
for (const block of world.blocks.values()) uniqueBlockIds.add(block.id);
|
||||
}
|
||||
await Promise.all(Array.from(uniqueBlockIds).map(id => this.cache.get(id, 'blockstates')));
|
||||
|
||||
// --- SWEEP 2: Resolve Parts & Fetch Models ---
|
||||
const uniqueModelIds = new Set();
|
||||
const parsedWorlds = new Map();
|
||||
|
||||
@@ -176,7 +159,6 @@ export class Engine {
|
||||
}
|
||||
await Promise.all(Array.from(uniqueModelIds).map(id => this.cache.get(id, 'models')));
|
||||
|
||||
// --- SWEEP 3: Pool Instances & Fetch Textures ---
|
||||
const textureTasks = [];
|
||||
const worldPools = new Map();
|
||||
|
||||
@@ -209,7 +191,6 @@ export class Engine {
|
||||
for (const face of Object.values(el.faces || {})) {
|
||||
const texPath = blockModel.resolveTexture(face.texture);
|
||||
if (texPath) {
|
||||
// Synchronously allocates UV, asynchronously fetches image
|
||||
textureTasks.push(this.cache.get(texPath, 'textures', 'png'));
|
||||
}
|
||||
}
|
||||
@@ -221,7 +202,6 @@ export class Engine {
|
||||
await Promise.all(textureTasks);
|
||||
this.updateAtlasTexture();
|
||||
|
||||
// --- SWEEP 4: Bake Geometry ---
|
||||
for (const world of uniqueWorlds) {
|
||||
const newMeshes = [];
|
||||
for (const pool of worldPools.get(world).values()) {
|
||||
@@ -295,23 +275,39 @@ export class Engine {
|
||||
|
||||
render() {
|
||||
const gl = this.gl;
|
||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
gl.scissor(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
gl.useProgram(this.program);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.atlasTexture);
|
||||
gl.uniform1i(this.uniforms.texture, 0);
|
||||
|
||||
// High-Performance Multi-Target Loop
|
||||
for (const diorama of this.dioramas) {
|
||||
const rect = diorama.element.getBoundingClientRect();
|
||||
if (rect.bottom < 0 || rect.top > gl.canvas.height || rect.right < 0 || rect.left > gl.canvas.width) continue;
|
||||
const rect = diorama.canvas.getBoundingClientRect();
|
||||
|
||||
const bottom = gl.canvas.height - rect.bottom;
|
||||
gl.viewport(rect.left, bottom, rect.width, rect.height);
|
||||
gl.scissor(rect.left, bottom, rect.width, rect.height);
|
||||
// Fast Frustum Culling via native DOM properties
|
||||
if (rect.bottom < 0 || rect.top > window.innerHeight || rect.right < 0 || rect.left > window.innerWidth) continue;
|
||||
|
||||
// Sync layout device-pixel ratios explicitly to prevent jagged texturing
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const targetW = Math.floor(rect.width * dpr);
|
||||
const targetH = Math.floor(rect.height * dpr);
|
||||
|
||||
// Guard: Only update diorama canvas internal buffers if physical dimensions shifted
|
||||
if (diorama.canvas.width !== targetW || diorama.canvas.height !== targetH) {
|
||||
diorama.canvas.width = targetW;
|
||||
diorama.canvas.height = targetH;
|
||||
}
|
||||
|
||||
// Apply size changes to hidden canvas ONLY when growing
|
||||
if (this.canvas.width < targetW || this.canvas.height < targetH) {
|
||||
this.canvas.width = targetW;
|
||||
this.canvas.height = targetH;
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, targetW, targetH);
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
const aspect = rect.width / rect.height;
|
||||
const viewProj = diorama.updateMatrices(aspect);
|
||||
@@ -325,6 +321,13 @@ export class Engine {
|
||||
gl.bindVertexArray(mesh.vao);
|
||||
gl.drawElementsInstanced(gl.TRIANGLES, mesh.indexCount, gl.UNSIGNED_SHORT, 0, mesh.instanceCount);
|
||||
}
|
||||
|
||||
diorama.ctx2d.clearRect(0, 0, targetW, targetH);
|
||||
diorama.ctx2d.drawImage(
|
||||
this.canvas,
|
||||
0, 0, targetW, targetH, // Source sub-rect coordinates from WebGL
|
||||
0, 0, targetW, targetH // Destination coordinates on 2D surface
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user