playcanvas
Version:
PlayCanvas WebGL game engine
448 lines (445 loc) • 13.1 kB
JavaScript
import { LAYERID_UI, LAYERID_DEPTH, ASPECT_AUTO } from '../../../scene/constants.js';
import { Camera } from '../../../scene/camera.js';
import { ShaderPass } from '../../../scene/shader-pass.js';
import { Component } from '../component.js';
import { PostEffectQueue } from './post-effect-queue.js';
class CameraComponent extends Component {
constructor(system, entity){
super(system, entity), this.onPostprocessing = null, this._renderSceneDepthMap = 0, this._renderSceneColorMap = 0, this._sceneDepthMapRequested = false, this._sceneColorMapRequested = false, this._priority = 0, this._disablePostEffectsLayer = LAYERID_UI, this._camera = new Camera(), this._evtLayersChanged = null, this._evtLayerAdded = null, this._evtLayerRemoved = null;
this._camera.node = entity;
this._postEffects = new PostEffectQueue(system.app, this);
}
setShaderPass(name) {
const shaderPass = ShaderPass.get(this.system.app.graphicsDevice);
const shaderPassInfo = name ? shaderPass.allocate(name, {
isForward: true
}) : null;
this._camera.shaderPassInfo = shaderPassInfo;
return shaderPassInfo.index;
}
getShaderPass() {
return this._camera.shaderPassInfo?.name;
}
set renderPasses(passes) {
this._camera.renderPasses = passes || [];
this.dirtyLayerCompositionCameras();
this.system.app.scene.updateShaders = true;
}
get renderPasses() {
return this._camera.renderPasses;
}
get shaderParams() {
return this._camera.shaderParams;
}
set gammaCorrection(value) {
this.camera.shaderParams.gammaCorrection = value;
}
get gammaCorrection() {
return this.camera.shaderParams.gammaCorrection;
}
set toneMapping(value) {
this.camera.shaderParams.toneMapping = value;
}
get toneMapping() {
return this.camera.shaderParams.toneMapping;
}
set fog(value) {
this._camera.fogParams = value;
}
get fog() {
return this._camera.fogParams;
}
set aperture(value) {
this._camera.aperture = value;
}
get aperture() {
return this._camera.aperture;
}
set aspectRatio(value) {
this._camera.aspectRatio = value;
}
get aspectRatio() {
return this._camera.aspectRatio;
}
set aspectRatioMode(value) {
this._camera.aspectRatioMode = value;
}
get aspectRatioMode() {
return this._camera.aspectRatioMode;
}
set calculateProjection(value) {
this._camera.calculateProjection = value;
}
get calculateProjection() {
return this._camera.calculateProjection;
}
set calculateTransform(value) {
this._camera.calculateTransform = value;
}
get calculateTransform() {
return this._camera.calculateTransform;
}
get camera() {
return this._camera;
}
set clearColor(value) {
this._camera.clearColor = value;
}
get clearColor() {
return this._camera.clearColor;
}
set clearColorBuffer(value) {
this._camera.clearColorBuffer = value;
this.dirtyLayerCompositionCameras();
}
get clearColorBuffer() {
return this._camera.clearColorBuffer;
}
set clearDepthBuffer(value) {
this._camera.clearDepthBuffer = value;
this.dirtyLayerCompositionCameras();
}
get clearDepthBuffer() {
return this._camera.clearDepthBuffer;
}
set clearStencilBuffer(value) {
this._camera.clearStencilBuffer = value;
this.dirtyLayerCompositionCameras();
}
get clearStencilBuffer() {
return this._camera.clearStencilBuffer;
}
set cullFaces(value) {
this._camera.cullFaces = value;
}
get cullFaces() {
return this._camera.cullFaces;
}
set disablePostEffectsLayer(layer) {
this._disablePostEffectsLayer = layer;
this.dirtyLayerCompositionCameras();
}
get disablePostEffectsLayer() {
return this._disablePostEffectsLayer;
}
set farClip(value) {
this._camera.farClip = value;
}
get farClip() {
return this._camera.farClip;
}
set flipFaces(value) {
this._camera.flipFaces = value;
}
get flipFaces() {
return this._camera.flipFaces;
}
set fov(value) {
this._camera.fov = value;
}
get fov() {
return this._camera.fov;
}
get frustum() {
return this._camera.frustum;
}
set frustumCulling(value) {
this._camera.frustumCulling = value;
}
get frustumCulling() {
return this._camera.frustumCulling;
}
set horizontalFov(value) {
this._camera.horizontalFov = value;
}
get horizontalFov() {
return this._camera.horizontalFov;
}
set layers(newValue) {
const oldLayers = this._camera.layers;
const scene = this.system.app.scene;
oldLayers.forEach((layerId)=>{
const layer = scene.layers.getLayerById(layerId);
layer?.removeCamera(this);
});
this._camera.layers = newValue;
if (this.enabled && this.entity.enabled) {
newValue.forEach((layerId)=>{
const layer = scene.layers.getLayerById(layerId);
layer?.addCamera(this);
});
}
this.fire('set:layers');
}
get layers() {
return this._camera.layers;
}
get layersSet() {
return this._camera.layersSet;
}
set jitter(value) {
this._camera.jitter = value;
}
get jitter() {
return this._camera.jitter;
}
set nearClip(value) {
this._camera.nearClip = value;
}
get nearClip() {
return this._camera.nearClip;
}
set orthoHeight(value) {
this._camera.orthoHeight = value;
}
get orthoHeight() {
return this._camera.orthoHeight;
}
get postEffects() {
return this._postEffects;
}
get postEffectsEnabled() {
return this._postEffects.enabled;
}
set priority(newValue) {
this._priority = newValue;
this.dirtyLayerCompositionCameras();
}
get priority() {
return this._priority;
}
set projection(value) {
this._camera.projection = value;
}
get projection() {
return this._camera.projection;
}
get projectionMatrix() {
return this._camera.projectionMatrix;
}
set rect(value) {
this._camera.rect = value;
this.fire('set:rect', this._camera.rect);
}
get rect() {
return this._camera.rect;
}
set renderSceneColorMap(value) {
if (value && !this._sceneColorMapRequested) {
this.requestSceneColorMap(true);
this._sceneColorMapRequested = true;
} else if (this._sceneColorMapRequested) {
this.requestSceneColorMap(false);
this._sceneColorMapRequested = false;
}
}
get renderSceneColorMap() {
return this._renderSceneColorMap > 0;
}
set renderSceneDepthMap(value) {
if (value && !this._sceneDepthMapRequested) {
this.requestSceneDepthMap(true);
this._sceneDepthMapRequested = true;
} else if (this._sceneDepthMapRequested) {
this.requestSceneDepthMap(false);
this._sceneDepthMapRequested = false;
}
}
get renderSceneDepthMap() {
return this._renderSceneDepthMap > 0;
}
set renderTarget(value) {
this._camera.renderTarget = value;
this.dirtyLayerCompositionCameras();
}
get renderTarget() {
return this._camera.renderTarget;
}
set scissorRect(value) {
this._camera.scissorRect = value;
}
get scissorRect() {
return this._camera.scissorRect;
}
set sensitivity(value) {
this._camera.sensitivity = value;
}
get sensitivity() {
return this._camera.sensitivity;
}
set shutter(value) {
this._camera.shutter = value;
}
get shutter() {
return this._camera.shutter;
}
get viewMatrix() {
return this._camera.viewMatrix;
}
_enableDepthLayer(value) {
const hasDepthLayer = this.layers.find((layerId)=>layerId === LAYERID_DEPTH);
if (hasDepthLayer) {
const depthLayer = this.system.app.scene.layers.getLayerById(LAYERID_DEPTH);
if (value) {
depthLayer?.incrementCounter();
} else {
depthLayer?.decrementCounter();
}
} else if (value) {
return false;
}
return true;
}
requestSceneColorMap(enabled) {
this._renderSceneColorMap += enabled ? 1 : -1;
this._enableDepthLayer(enabled);
this.camera._enableRenderPassColorGrab(this.system.app.graphicsDevice, this.renderSceneColorMap);
this.system.app.scene.layers.markDirty();
}
requestSceneDepthMap(enabled) {
this._renderSceneDepthMap += enabled ? 1 : -1;
this._enableDepthLayer(enabled);
this.camera._enableRenderPassDepthGrab(this.system.app.graphicsDevice, this.system.app.renderer, this.renderSceneDepthMap);
this.system.app.scene.layers.markDirty();
}
dirtyLayerCompositionCameras() {
const layerComp = this.system.app.scene.layers;
layerComp._dirty = true;
}
screenToWorld(screenx, screeny, cameraz, worldCoord) {
const device = this.system.app.graphicsDevice;
const { width, height } = device.clientRect;
return this._camera.screenToWorld(screenx, screeny, cameraz, width, height, worldCoord);
}
worldToScreen(worldCoord, screenCoord) {
const device = this.system.app.graphicsDevice;
const { width, height } = device.clientRect;
return this._camera.worldToScreen(worldCoord, width, height, screenCoord);
}
onAppPrerender() {
this._camera._viewMatDirty = true;
this._camera._viewProjMatDirty = true;
}
addCameraToLayers() {
const layers = this.layers;
for(let i = 0; i < layers.length; i++){
const layer = this.system.app.scene.layers.getLayerById(layers[i]);
if (layer) {
layer.addCamera(this);
}
}
}
removeCameraFromLayers() {
const layers = this.layers;
for(let i = 0; i < layers.length; i++){
const layer = this.system.app.scene.layers.getLayerById(layers[i]);
if (layer) {
layer.removeCamera(this);
}
}
}
onLayersChanged(oldComp, newComp) {
this.addCameraToLayers();
oldComp.off('add', this.onLayerAdded, this);
oldComp.off('remove', this.onLayerRemoved, this);
newComp.on('add', this.onLayerAdded, this);
newComp.on('remove', this.onLayerRemoved, this);
}
onLayerAdded(layer) {
const index = this.layers.indexOf(layer.id);
if (index < 0) return;
layer.addCamera(this);
}
onLayerRemoved(layer) {
const index = this.layers.indexOf(layer.id);
if (index < 0) return;
layer.removeCamera(this);
}
onEnable() {
const scene = this.system.app.scene;
const layers = scene.layers;
this.system.addCamera(this);
this._evtLayersChanged?.off();
this._evtLayersChanged = scene.on('set:layers', this.onLayersChanged, this);
if (layers) {
this._evtLayerAdded?.off();
this._evtLayerAdded = layers.on('add', this.onLayerAdded, this);
this._evtLayerRemoved?.off();
this._evtLayerRemoved = layers.on('remove', this.onLayerRemoved, this);
}
if (this.enabled && this.entity.enabled) {
this.addCameraToLayers();
}
this.postEffects.enable();
}
onDisable() {
const scene = this.system.app.scene;
const layers = scene.layers;
this.postEffects.disable();
this.removeCameraFromLayers();
this._evtLayersChanged?.off();
this._evtLayersChanged = null;
if (layers) {
this._evtLayerAdded?.off();
this._evtLayerAdded = null;
this._evtLayerRemoved?.off();
this._evtLayerRemoved = null;
}
this.system.removeCamera(this);
}
onRemove() {
this.onDisable();
this.off();
this.camera.destroy();
}
calculateAspectRatio(rt) {
const device = this.system.app.graphicsDevice;
const width = rt ? rt.width : device.width;
const height = rt ? rt.height : device.height;
return width * this.rect.z / (height * this.rect.w);
}
frameUpdate(rt) {
if (this.aspectRatioMode === ASPECT_AUTO) {
this.aspectRatio = this.calculateAspectRatio(rt);
}
}
startXr(type, spaceType, options) {
this.system.app.xr.start(this, type, spaceType, options);
}
endXr(callback) {
if (!this._camera.xr) {
if (callback) callback(new Error('Camera is not in XR'));
return;
}
this._camera.xr.end(callback);
}
copy(source) {
this.aperture = source.aperture;
this.aspectRatio = source.aspectRatio;
this.aspectRatioMode = source.aspectRatioMode;
this.calculateProjection = source.calculateProjection;
this.calculateTransform = source.calculateTransform;
this.clearColor = source.clearColor;
this.clearColorBuffer = source.clearColorBuffer;
this.clearDepthBuffer = source.clearDepthBuffer;
this.clearStencilBuffer = source.clearStencilBuffer;
this.cullFaces = source.cullFaces;
this.disablePostEffectsLayer = source.disablePostEffectsLayer;
this.farClip = source.farClip;
this.flipFaces = source.flipFaces;
this.fov = source.fov;
this.frustumCulling = source.frustumCulling;
this.horizontalFov = source.horizontalFov;
this.layers = source.layers;
this.nearClip = source.nearClip;
this.orthoHeight = source.orthoHeight;
this.priority = source.priority;
this.projection = source.projection;
this.rect = source.rect;
this.renderTarget = source.renderTarget;
this.scissorRect = source.scissorRect;
this.sensitivity = source.sensitivity;
this.shutter = source.shutter;
}
}
export { CameraComponent };