@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
289 lines • 12.3 kB
JavaScript
import { TransformNode } from "../Meshes/transformNode.js";
import { Mesh } from "../Meshes/mesh.js";
import { Texture } from "../Materials/Textures/texture.js";
import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial.js";
import { CreateSphere } from "../Meshes/Builders/sphereBuilder.js";
import { Observable } from "../Misc/observable.js";
import { Vector3 } from "../Maths/math.vector.js";
import { Axis } from "../Maths/math.js";
/**
* Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
* As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
* This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
* Potential additions to this helper include zoom and and non-infinite distance rendering effects.
*/
export class TextureDome extends TransformNode {
/**
* Gets the texture being displayed on the sphere
*/
get texture() {
return this._texture;
}
/**
* Sets the texture being displayed on the sphere
*/
set texture(newTexture) {
if (this._texture === newTexture) {
return;
}
this._texture = newTexture;
if (this._useDirectMapping) {
this._texture.wrapU = Texture.CLAMP_ADDRESSMODE;
this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
this._material.diffuseTexture = this._texture;
}
else {
this._texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
this._material.reflectionTexture = this._texture;
}
this._changeTextureMode(this._textureMode);
}
/**
* Gets the mesh used for the dome.
*/
get mesh() {
return this._mesh;
}
/**
* The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
* Also see the options.resolution property.
*/
get fovMultiplier() {
return this._material.fovMultiplier;
}
set fovMultiplier(value) {
this._material.fovMultiplier = value;
}
/**
* Gets or set the current texture mode for the texture. It can be:
* * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
* * TextureDome.MODE_TOPBOTTOM : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
* * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
*/
get textureMode() {
return this._textureMode;
}
/**
* Sets the current texture mode for the texture. It can be:
* * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
* * TextureDome.MODE_TOPBOTTOM : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
* * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
*/
set textureMode(value) {
if (this._textureMode === value) {
return;
}
this._changeTextureMode(value);
}
/**
* Is it a 180 degrees dome (half dome) or 360 texture (full dome)
*/
get halfDome() {
return this._halfDome;
}
/**
* Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
*/
set halfDome(enabled) {
this._halfDome = enabled;
this._halfDomeMask.setEnabled(enabled);
this._changeTextureMode(this._textureMode);
}
/**
* Set the cross-eye mode. If set, images that can be seen when crossing eyes will render correctly
*/
set crossEye(enabled) {
this._crossEye = enabled;
this._changeTextureMode(this._textureMode);
}
/**
* Is it a cross-eye texture?
*/
get crossEye() {
return this._crossEye;
}
/**
* The background material of this dome.
*/
get material() {
return this._material;
}
/**
* Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
* @param name Element's name, child elements will append suffixes for their own names.
* @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
* @param options An object containing optional or exposed sub element properties
* @param options.resolution
* @param options.clickToPlay
* @param options.autoPlay
* @param options.loop
* @param options.size
* @param options.poster
* @param options.faceForward
* @param options.useDirectMapping
* @param options.halfDomeMode
* @param options.crossEyeMode
* @param options.generateMipMaps
* @param options.mesh
* @param scene
* @param onError
*/
constructor(name, textureUrlOrElement, options, scene,
// eslint-disable-next-line @typescript-eslint/naming-convention
onError = null) {
super(name, scene);
this.onError = onError;
this._halfDome = false;
this._crossEye = false;
this._useDirectMapping = false;
this._textureMode = TextureDome.MODE_MONOSCOPIC;
/**
* Oberserver used in Stereoscopic VR Mode.
*/
this._onBeforeCameraRenderObserver = null;
/**
* Observable raised when an error occurred while loading the texture
*/
this.onLoadErrorObservable = new Observable();
/**
* Observable raised when the texture finished loading
*/
this.onLoadObservable = new Observable();
scene = this.getScene();
// set defaults and manage values
name = name || "textureDome";
options.resolution = Math.abs(options.resolution) | 0 || 32;
options.clickToPlay = Boolean(options.clickToPlay);
options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
options.loop = options.loop === undefined ? true : Boolean(options.loop);
options.size = Math.abs(options.size) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
if (options.useDirectMapping === undefined) {
this._useDirectMapping = true;
}
else {
this._useDirectMapping = options.useDirectMapping;
}
if (options.faceForward === undefined) {
options.faceForward = true;
}
this._setReady(false);
if (!options.mesh) {
this._mesh = CreateSphere(name + "_mesh", { segments: options.resolution, diameter: options.size, updatable: false, sideOrientation: Mesh.BACKSIDE }, scene);
}
else {
this._mesh = options.mesh;
}
// configure material
const material = (this._material = new BackgroundMaterial(name + "_material", scene));
material.useEquirectangularFOV = true;
material.fovMultiplier = 1.0;
material.opacityFresnel = false;
const texture = this._initTexture(textureUrlOrElement, scene, options);
this.texture = texture;
// configure mesh
this._mesh.material = material;
this._mesh.parent = this;
// create a (disabled until needed) mask to cover unneeded segments of 180 texture.
this._halfDomeMask = CreateSphere("", { slice: 0.5, diameter: options.size * 0.98, segments: options.resolution * 2, sideOrientation: Mesh.BACKSIDE }, scene);
this._halfDomeMask.rotate(Axis.X, -Math.PI / 2);
// set the parent, so it will always be positioned correctly AND will be disposed when the main sphere is disposed
this._halfDomeMask.parent = this._mesh;
this._halfDome = !!options.halfDomeMode;
// enable or disable according to the settings
this._halfDomeMask.setEnabled(this._halfDome);
this._crossEye = !!options.crossEyeMode;
// create
this._texture.anisotropicFilteringLevel = 1;
this._texture.onLoadObservable.addOnce(() => {
this._setReady(true);
});
// Initial rotation
if (options.faceForward && scene.activeCamera) {
const camera = scene.activeCamera;
const forward = Vector3.Forward();
const direction = Vector3.TransformNormal(forward, camera.getViewMatrix());
direction.normalize();
this.rotation.y = Math.acos(Vector3.Dot(forward, direction));
}
this._changeTextureMode(this._textureMode);
}
_changeTextureMode(value) {
this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
this._textureMode = value;
// Default Setup and Reset.
this._texture.uScale = 1;
this._texture.vScale = 1;
this._texture.uOffset = 0;
this._texture.vOffset = 0;
this._texture.vAng = 0;
switch (value) {
case TextureDome.MODE_MONOSCOPIC:
if (this._halfDome) {
this._texture.uScale = 2;
this._texture.uOffset = -1;
}
break;
case TextureDome.MODE_SIDEBYSIDE: {
// in half-dome mode the uScale should be double of 360 texture
// Use 0.99999 to boost perf by not switching program
this._texture.uScale = this._halfDome ? 0.99999 : 0.5;
const rightOffset = this._halfDome ? 0.0 : 0.5;
const leftOffset = this._halfDome ? -0.5 : 0.0;
this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
let isRightCamera = camera.isRightCamera;
if (this._crossEye) {
isRightCamera = !isRightCamera;
}
if (isRightCamera) {
this._texture.uOffset = rightOffset;
}
else {
this._texture.uOffset = leftOffset;
}
});
break;
}
case TextureDome.MODE_TOPBOTTOM:
// in half-dome mode the vScale should be double of 360 texture
// Use 0.99999 to boost perf by not switching program
this._texture.vScale = this._halfDome ? 0.99999 : 0.5;
this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
let isRightCamera = camera.isRightCamera;
// allow "cross-eye" if left and right were switched in this mode
if (this._crossEye) {
isRightCamera = !isRightCamera;
}
this._texture.vOffset = isRightCamera ? 0.5 : 0.0;
});
break;
}
}
/**
* Releases resources associated with this node.
* @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
* @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
*/
dispose(doNotRecurse, disposeMaterialAndTextures = false) {
this._texture.dispose();
this._mesh.dispose();
this._material.dispose();
this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
this.onLoadErrorObservable.clear();
this.onLoadObservable.clear();
super.dispose(doNotRecurse, disposeMaterialAndTextures);
}
}
/**
* Define the source as a Monoscopic panoramic 360/180.
*/
TextureDome.MODE_MONOSCOPIC = 0;
/**
* Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
*/
TextureDome.MODE_TOPBOTTOM = 1;
/**
* Define the source as a Stereoscopic Side by Side panoramic 360/180.
*/
TextureDome.MODE_SIDEBYSIDE = 2;
//# sourceMappingURL=textureDome.js.map