@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
121 lines (120 loc) • 4.35 kB
JavaScript
"use strict";
import { Matrix4, Vector3 } from "three";
const _vector = new Vector3();
const _viewMatrix = new Matrix4();
const _viewProjectionMatrix = new Matrix4();
const _a = new Vector3();
const _b = new Vector3();
export class CSS2DRenderer {
constructor(parameters = {}) {
this._width = 0;
this._height = 0;
this._widthHalf = 0;
this._heightHalf = 0;
this.dataByObject = /* @__PURE__ */ new WeakMap();
this.appendedObjects = /* @__PURE__ */ new Set();
this.objectsToRender = /* @__PURE__ */ new Set();
this.objectsToRemove = /* @__PURE__ */ new Set();
const domElement = parameters.element !== void 0 ? parameters.element : document.createElement("div");
domElement.style.overflow = "hidden";
this.domElement = domElement;
}
getSize() {
return {
width: this._width,
height: this._height
};
}
render(scene, camera) {
_viewMatrix.copy(camera.matrixWorldInverse);
_viewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, _viewMatrix);
this.removeElementsDeletedFromSceneGraph(scene);
this.renderObject(scene, scene, camera);
this.zOrder(scene);
}
removeElementsDeletedFromSceneGraph(scene) {
this.objectsToRender.clear();
scene.traverse((object) => {
if (object.isCSS2DObject) {
this.objectsToRender.add(object);
}
});
this.objectsToRemove.clear();
this.appendedObjects.forEach((appendedObject) => {
if (!this.objectsToRender.has(appendedObject)) {
this.objectsToRemove.add(appendedObject);
}
});
this.objectsToRemove.forEach((object) => {
this.domElement.removeChild(object.element);
this.appendedObjects.delete(object);
});
}
setSize(width, height) {
this._width = width;
this._height = height;
this._widthHalf = this._width / 2;
this._heightHalf = this._height / 2;
this.domElement.style.width = width + "px";
this.domElement.style.height = height + "px";
}
renderObject(object, scene, camera) {
if (object.isCSS2DObject) {
_vector.setFromMatrixPosition(object.matrixWorld);
_vector.applyMatrix4(_viewProjectionMatrix);
const visible = object.visible === true && _vector.z >= -1 && _vector.z <= 1 && object.layers.test(camera.layers) === true;
object.element.style.display = visible === true ? "" : "none";
if (visible === true) {
object.onBeforeRender(this, scene, camera);
const element = object.element;
element.style.transform = "translate(-50%,-50%) translate(" + (_vector.x * this._widthHalf + this._widthHalf) + "px," + (-_vector.y * this._heightHalf + this._heightHalf) + "px)";
if (element.parentNode !== this.domElement) {
this.domElement.appendChild(element);
this.appendedObjects.add(object);
}
object.onAfterRender(this, scene, camera);
}
let objectData = this.dataByObject.get(object);
const distanceToCameraSquared = this.getDistanceToSquared(camera, object);
if (!objectData) {
objectData = {
distanceToCameraSquared
};
this.dataByObject.set(object, objectData);
} else {
objectData.distanceToCameraSquared = distanceToCameraSquared;
}
}
for (const child of object.children) {
this.renderObject(child, scene, camera);
}
}
getDistanceToSquared(object1, object2) {
_a.setFromMatrixPosition(object1.matrixWorld);
_b.setFromMatrixPosition(object2.matrixWorld);
return _a.distanceToSquared(_b);
}
filterAndFlatten(scene) {
const result = [];
scene.traverse(function(object) {
if (object.isCSS2DObject)
result.push(object);
});
return result;
}
zOrder(scene) {
const sorted = this.filterAndFlatten(scene).sort((a, b) => {
var _a2, _b2;
if (a.renderOrder !== b.renderOrder) {
return b.renderOrder - a.renderOrder;
}
const distanceA = ((_a2 = this.dataByObject.get(a)) == null ? void 0 : _a2.distanceToCameraSquared) || 0;
const distanceB = ((_b2 = this.dataByObject.get(b)) == null ? void 0 : _b2.distanceToCameraSquared) || 0;
return distanceA - distanceB;
});
const zMax = sorted.length;
for (let i = 0, l = sorted.length; i < l; i++) {
sorted[i].element.style.zIndex = `${zMax - i}`;
}
}
}