@openhps/core
Version:
Open Hybrid Positioning System - Core component
181 lines (173 loc) • 6.45 kB
JavaScript
import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';
import { Object3D } from '../core/Object3D.js';
import { PerspectiveCamera } from './PerspectiveCamera.js';
const fov = -90; // negative fov is not an error
const aspect = 1;
/**
* A special type of camera that is positioned in 3D space to render its surroundings into a
* cube render target. The render target can then be used as an environment map for rendering
* realtime reflections in your scene.
*
* ```js
* // Create cube render target
* const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter } );
*
* // Create cube camera
* const cubeCamera = new THREE.CubeCamera( 1, 100000, cubeRenderTarget );
* scene.add( cubeCamera );
*
* // Create car
* const chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeRenderTarget.texture } );
* const car = new THREE.Mesh( carGeometry, chromeMaterial );
* scene.add( car );
*
* // Update the render target cube
* car.visible = false;
* cubeCamera.position.copy( car.position );
* cubeCamera.update( renderer, scene );
*
* // Render the scene
* car.visible = true;
* renderer.render( scene, camera );
* ```
*
* @augments Object3D
*/
class CubeCamera extends Object3D {
/**
* Constructs a new cube camera.
*
* @param {number} near - The camera's near plane.
* @param {number} far - The camera's far plane.
* @param {WebGLCubeRenderTarget} renderTarget - The cube render target.
*/
constructor(near, far, renderTarget) {
super();
this.type = 'CubeCamera';
/**
* A reference to the cube render target.
*
* @type {WebGLCubeRenderTarget}
*/
this.renderTarget = renderTarget;
/**
* The current active coordinate system.
*
* @type {?(WebGLCoordinateSystem|WebGPUCoordinateSystem)}
* @default null
*/
this.coordinateSystem = null;
/**
* The current active mipmap level
*
* @type {number}
* @default 0
*/
this.activeMipmapLevel = 0;
const cameraPX = new PerspectiveCamera(fov, aspect, near, far);
cameraPX.layers = this.layers;
this.add(cameraPX);
const cameraNX = new PerspectiveCamera(fov, aspect, near, far);
cameraNX.layers = this.layers;
this.add(cameraNX);
const cameraPY = new PerspectiveCamera(fov, aspect, near, far);
cameraPY.layers = this.layers;
this.add(cameraPY);
const cameraNY = new PerspectiveCamera(fov, aspect, near, far);
cameraNY.layers = this.layers;
this.add(cameraNY);
const cameraPZ = new PerspectiveCamera(fov, aspect, near, far);
cameraPZ.layers = this.layers;
this.add(cameraPZ);
const cameraNZ = new PerspectiveCamera(fov, aspect, near, far);
cameraNZ.layers = this.layers;
this.add(cameraNZ);
}
/**
* Must be called when the coordinate system of the cube camera is changed.
*/
updateCoordinateSystem() {
const coordinateSystem = this.coordinateSystem;
const cameras = this.children.concat();
const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = cameras;
for (const camera of cameras) this.remove(camera);
if (coordinateSystem === WebGLCoordinateSystem) {
cameraPX.up.set(0, 1, 0);
cameraPX.lookAt(1, 0, 0);
cameraNX.up.set(0, 1, 0);
cameraNX.lookAt(-1, 0, 0);
cameraPY.up.set(0, 0, -1);
cameraPY.lookAt(0, 1, 0);
cameraNY.up.set(0, 0, 1);
cameraNY.lookAt(0, -1, 0);
cameraPZ.up.set(0, 1, 0);
cameraPZ.lookAt(0, 0, 1);
cameraNZ.up.set(0, 1, 0);
cameraNZ.lookAt(0, 0, -1);
} else if (coordinateSystem === WebGPUCoordinateSystem) {
cameraPX.up.set(0, -1, 0);
cameraPX.lookAt(-1, 0, 0);
cameraNX.up.set(0, -1, 0);
cameraNX.lookAt(1, 0, 0);
cameraPY.up.set(0, 0, 1);
cameraPY.lookAt(0, 1, 0);
cameraNY.up.set(0, 0, -1);
cameraNY.lookAt(0, -1, 0);
cameraPZ.up.set(0, -1, 0);
cameraPZ.lookAt(0, 0, 1);
cameraNZ.up.set(0, -1, 0);
cameraNZ.lookAt(0, 0, -1);
} else {
throw new Error('THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem);
}
for (const camera of cameras) {
this.add(camera);
camera.updateMatrixWorld();
}
}
/**
* Calling this method will render the given scene with the given renderer
* into the cube render target of the camera.
*
* @param {(Renderer|WebGLRenderer)} renderer - The renderer.
* @param {Scene} scene - The scene to render.
*/
update(renderer, scene) {
if (this.parent === null) this.updateMatrixWorld();
const {
renderTarget,
activeMipmapLevel
} = this;
if (this.coordinateSystem !== renderer.coordinateSystem) {
this.coordinateSystem = renderer.coordinateSystem;
this.updateCoordinateSystem();
}
const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children;
const currentRenderTarget = renderer.getRenderTarget();
const currentActiveCubeFace = renderer.getActiveCubeFace();
const currentActiveMipmapLevel = renderer.getActiveMipmapLevel();
const currentXrEnabled = renderer.xr.enabled;
renderer.xr.enabled = false;
const generateMipmaps = renderTarget.texture.generateMipmaps;
renderTarget.texture.generateMipmaps = false;
renderer.setRenderTarget(renderTarget, 0, activeMipmapLevel);
renderer.render(scene, cameraPX);
renderer.setRenderTarget(renderTarget, 1, activeMipmapLevel);
renderer.render(scene, cameraNX);
renderer.setRenderTarget(renderTarget, 2, activeMipmapLevel);
renderer.render(scene, cameraPY);
renderer.setRenderTarget(renderTarget, 3, activeMipmapLevel);
renderer.render(scene, cameraNY);
renderer.setRenderTarget(renderTarget, 4, activeMipmapLevel);
renderer.render(scene, cameraPZ);
// mipmaps are generated during the last call of render()
// at this point, all sides of the cube render target are defined
renderTarget.texture.generateMipmaps = generateMipmaps;
renderer.setRenderTarget(renderTarget, 5, activeMipmapLevel);
renderer.render(scene, cameraNZ);
renderer.setRenderTarget(currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel);
renderer.xr.enabled = currentXrEnabled;
renderTarget.texture.needsPMREMUpdate = true;
}
}
export { CubeCamera };