@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
143 lines (142 loc) • 5.09 kB
JavaScript
"use strict";
import { CoreMapboxTransform } from "../Transform";
import {
Matrix4,
Vector3,
WebGLRenderer
} from "three";
import mapboxgl from "mapbox-gl";
const ID = "threejs_layer";
export class ThreejsLayer {
constructor(options) {
this.id = ID;
this.type = "custom";
this.renderingMode = "3d";
// from https://docs.mapbox.com/mapbox-gl-js/example/add-3d-model/
// this now rotates objects correctly
this._vX = new Vector3(1, 0, 0);
this._vY = new Vector3(0, 1, 0);
this._vZ = new Vector3(0, 0, 1);
this.mRX = new Matrix4();
this.mRY = new Matrix4();
this.mRZ = new Matrix4();
this.s = new Vector3();
this.m = new Matrix4();
this.l = new Matrix4();
this._map = options.map;
this._camera = options.camera;
this._lngLat = options.lngLat;
this._displayScene = options.scene.threejsScene();
this._renderFunc = options.renderFunc;
this._viewer = options.viewer;
}
onAdd(map, gl) {
this.createRenderer(gl);
}
// onRemove() {
// this._renderer?.dispose();
// }
createRenderer(gl) {
this._renderer = new WebGLRenderer({
// alpha: true
antialias: true,
canvas: this._map.getCanvas(),
context: gl
});
this._viewer.setRenderer(this._renderer);
this._renderer.autoClear = false;
this._renderer.shadowMap.enabled = true;
}
// resize(size: Vector2) {
// // TODO: resize is currently broken.
// // re-creating a renderer is the only way I found to reliably resize.
// // It seems that if I only resize,
// // the render will appear to be the right size (as in no antialiasing issue)
// // but 3d object end up slipping on the map (as opposed to staying anchored where they are expected to)
// this.createRenderer();
// // this._renderer?.setSize(size.x, size.y);
// // this._cssRendererConfig?.cssRenderer.setSize(size.x, size.y);
// }
async render(gl, matrix) {
if (!this._renderer) {
console.warn("no renderer");
return;
}
let previousBackground = this._displayScene.background;
if (this._displayScene.background) {
this._displayScene.background = null;
}
this._updateCameraMatrix(matrix);
this._renderer.resetState();
this._renderFunc(this._renderer);
this._map.triggerRepaint();
this._displayScene.background = previousBackground;
}
_updateCameraMatrix(matrix) {
const mercator = mapboxgl.MercatorCoordinate.fromLngLat([this._lngLat.lng, this._lngLat.lat], 0);
const transform = {
position: mercator,
rotation: { x: Math.PI / 2, y: 0, z: 0 },
scale: CoreMapboxTransform.WORLD_SCALE
};
this.mRX.identity();
this.mRY.identity();
this.mRZ.identity();
const rotationX = this.mRX.makeRotationAxis(this._vX, transform.rotation.x);
const rotationY = this.mRY.makeRotationAxis(this._vY, transform.rotation.y);
const rotationZ = this.mRZ.makeRotationAxis(this._vZ, transform.rotation.z);
this.s.x = transform.scale;
this.s.y = -transform.scale;
this.s.z = transform.scale;
this.m.fromArray(matrix);
this.l.identity();
this.l.makeTranslation(1 * transform.position.x, 1 * transform.position.y, 1 * (transform.position.z || 0)).scale(this.s).multiply(rotationX).multiply(rotationY).multiply(rotationZ);
this._camera.projectionMatrix.elements = matrix;
this._camera.projectionMatrix = this.m.multiply(this.l);
}
// This is a very dirty hack that seems to allow objects to render properly.
// If this was not called,
// all objects created would render for a couple frames and then disappear.
// There sometimes would be an WebGL warning along the lines of "buffer not large enough"
// but it is completely unclear what could have caused it.
//
// What I tried to debug this:
//
// - upgrade from mapbox 1 to 2
// this made no difference
//
// - using Babylon Spector
// but I was unable to isolate which call was problematic
//
// - fiddle with renderes options
// that solved nothing
//
// - integrate the mapbox example as a layer instead of this one
// When using the example layer and its included THREE.Scene,
// it renders just fine.
// But as soon as I replace the included scene with the one created by Polygonjs,
// Then the problem reappears.
// That's even if the scene is as simple as a Hemisphere Light and a Plane.
// So that did not allow me to find a solution.
//
// - use src/debug.js (I forgot now where I copied it from)
// to help find bad webgl calls.
// but that didn't help.
//
// - setting Polygonjs scene's objects to
// matrixAutoUpdate = true
// or
// frustumCulled = false
// But that solved nothing.
//
// In short.... WFT?!?!
// But for now, with this hack, it seems to work fine.
// private _hack() {
// const hackObject = new Mesh(new PlaneGeometry());
// hackObject.frustumCulled = false;
// hackObject.position.z = -1000;
// hackObject.scale.set(0.01, 0.01, 0.01);
// const scene = this._scene.threejsScene();
// scene.add(hackObject);
// }
}