playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
239 lines (238 loc) • 7.28 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { Vec3 } from "../../core/math/vec3.js";
import { SKYTYPE_INFINITE } from "../constants.js";
import { FisheyeProjection } from "../graphics/fisheye-projection.js";
import { GraphNode } from "../graph-node.js";
import { SkyMesh } from "./sky-mesh.js";
class Sky {
/**
* Constructs a new sky.
*
* @param {Scene} scene - The scene owning the sky.
* @ignore
*/
constructor(scene) {
/**
* The type of the sky. One of the SKYTYPE_* constants.
*
* @type {string}
* @private
*/
__publicField(this, "_type", SKYTYPE_INFINITE);
/**
* The center of the sky.
*
* @private
*/
__publicField(this, "_center", new Vec3(0, 1, 0));
/**
* The sky mesh of the scene.
*
* @type {SkyMesh|null}
* @ignore
*/
__publicField(this, "skyMesh", null);
/** @private */
__publicField(this, "_depthWrite", false);
/** @private */
__publicField(this, "_fisheye", 0);
/**
* Lazily created on first non-zero fisheye set.
*
* @type {FisheyeProjection|null}
* @private
*/
__publicField(this, "_fisheyeProj", null);
/**
* A graph node with a transform used to render the sky mesh. Adjust the position, rotation and
* scale of this node to orient the sky mesh. Ignored for {@link SKYTYPE_INFINITE}.
*
* @type {GraphNode}
* @readonly
*/
__publicField(this, "node", new GraphNode("SkyMeshNode"));
this.device = scene.device;
this.scene = scene;
this.center = new Vec3(0, 1, 0);
this.centerArray = new Float32Array(3);
this.projectedSkydomeCenterId = this.device.scope.resolve("projectedSkydomeCenter");
this._preRenderEvt = scene.on("prerender", this._onPreRender, this);
}
destroy() {
this._preRenderEvt.off();
this.resetSkyMesh();
}
applySettings(render) {
this.type = render.skyType ?? SKYTYPE_INFINITE;
this.node.setLocalPosition(new Vec3(render.skyMeshPosition ?? [0, 0, 0]));
this.node.setLocalEulerAngles(new Vec3(render.skyMeshRotation ?? [0, 0, 0]));
this.node.setLocalScale(new Vec3(render.skyMeshScale ?? [1, 1, 1]));
if (render.skyCenter) {
this._center = new Vec3(render.skyCenter);
}
}
/**
* Sets the type of the sky. Can be:
*
* - {@link SKYTYPE_INFINITE}
* - {@link SKYTYPE_BOX}
* - {@link SKYTYPE_DOME}
*
* Defaults to {@link SKYTYPE_INFINITE}.
*
* @type {string}
*/
set type(value) {
if (this._type !== value) {
this._type = value;
this.scene.updateShaders = true;
this.updateSkyMesh();
}
}
/**
* Gets the type of the sky.
*
* @type {string}
*/
get type() {
return this._type;
}
/**
* Sets the center of the sky. Ignored for {@link SKYTYPE_INFINITE}. Typically only the
* y-coordinate is used, representing the tripod height. Defaults to (0, 1, 0).
*
* @type {Vec3}
*/
set center(value) {
this._center.copy(value);
}
/**
* Gets the center of the sky.
*
* @type {Vec3}
*/
get center() {
return this._center;
}
/**
* Sets whether depth writing is enabled for the sky. Defaults to false.
*
* Writing a depth value for the skydome is supported when its type is not
* {@link SKYTYPE_INFINITE}. When enabled, the depth is written during a prepass render pass and
* can be utilized by subsequent passes to apply depth-based effects, such as Depth of Field.
*
* Note: For the skydome to be rendered during the prepass, the Sky Layer must be ordered before
* the Depth layer, which is the final layer used in the prepass.
*
* @type {boolean}
*/
set depthWrite(value) {
if (this._depthWrite !== value) {
this._depthWrite = value;
if (this.skyMesh) {
this.skyMesh.depthWrite = value;
}
}
}
/**
* Gets whether depth writing is enabled for the sky.
*
* @type {boolean}
*/
get depthWrite() {
return this._depthWrite;
}
/**
* Sets the fisheye projection strength for the sky. The value is in the range [0, 1]:
*
* - 0: Standard rectilinear (perspective) projection.
* - (0, 1]: Increasing barrel distortion, producing a wider field of view.
*
* Only supported with {@link SKYTYPE_INFINITE}. Has no effect on dome or box sky types,
* and has no effect with orthographic cameras. Defaults to 0.
*
* @type {number}
*/
set fisheye(value) {
if (this._fisheye !== value) {
const wasEnabled = this._fisheye > 0;
this._fisheye = value;
const isEnabled = value > 0;
if (wasEnabled !== isEnabled) {
this._fisheyeProj ?? (this._fisheyeProj = new FisheyeProjection());
if (this._type === SKYTYPE_INFINITE) {
this._setFisheyeDefine(isEnabled);
}
}
}
}
/**
* Gets the fisheye projection strength for the sky.
*
* @type {number}
*/
get fisheye() {
return this._fisheye;
}
updateSkyMesh() {
const texture = this.scene._getSkyboxTex();
if (texture) {
this.resetSkyMesh();
this.skyMesh = new SkyMesh(this.device, this.scene, this.node, texture, this.type);
this.skyMesh.depthWrite = this._depthWrite;
if (this._fisheye > 0 && this.type === SKYTYPE_INFINITE) {
this._setFisheyeDefine(true);
}
this.scene.fire("set:skybox", texture);
}
}
resetSkyMesh() {
this.skyMesh?.destroy();
this.skyMesh = null;
}
update() {
if (this.type !== SKYTYPE_INFINITE) {
const { center, centerArray } = this;
const temp = new Vec3();
this.node.getWorldTransform().transformPoint(center, temp);
centerArray[0] = temp.x;
centerArray[1] = temp.y;
centerArray[2] = temp.z;
this.projectedSkydomeCenterId.setValue(centerArray);
}
}
/**
* @param {boolean} enabled - Whether to enable the SKY_FISHEYE define.
* @private
*/
_setFisheyeDefine(enabled) {
if (this.skyMesh?.meshInstance) {
const material = this.skyMesh.meshInstance.material;
material.setDefine("SKY_FISHEYE", enabled);
material.update();
}
}
/**
* Per-camera prerender callback that updates fisheye uniforms for the active camera.
*
* @param {import('../../framework/components/camera/component.js').CameraComponent} cameraComponent - The camera about to render.
* @private
*/
_onPreRender(cameraComponent) {
if (this._fisheye > 0 && this._fisheyeProj && this.skyMesh?.meshInstance) {
const camera = cameraComponent.camera;
const proj = this._fisheyeProj;
proj.update(this._fisheye, camera.fov, camera.projectionMatrix);
const material = this.skyMesh.meshInstance.material;
material.setParameter("fisheye_k", proj.k);
material.setParameter("fisheye_invK", proj.invK);
material.setParameter("fisheye_projMat00", proj.projMat00);
material.setParameter("fisheye_projMat11", proj.projMat11);
}
}
}
export {
Sky
};