@babylonjs/gui
Version:
Babylon.js GUI module =====================
400 lines • 16.9 kB
JavaScript
import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial.js";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode.js";
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
import { CreatePlane } from "@babylonjs/core/Meshes/Builders/planeBuilder.js";
import { CreateBox } from "@babylonjs/core/Meshes/Builders/boxBuilder.js";
import { FadeInOutBehavior } from "@babylonjs/core/Behaviors/Meshes/fadeInOutBehavior.js";
import { FluentMaterial } from "../materials/fluent/fluentMaterial.js";
import { FluentButtonMaterial } from "../materials/fluentButton/fluentButtonMaterial.js";
import { StackPanel } from "../../2D/controls/stackPanel.js";
import { Image } from "../../2D/controls/image.js";
import { TextBlock } from "../../2D/controls/textBlock.js";
import { AdvancedDynamicTexture } from "../../2D/advancedDynamicTexture.js";
import { Color3 } from "@babylonjs/core/Maths/math.color.js";
import { TouchButton3D } from "./touchButton3D.js";
import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader.js";
import { IsDocumentAvailable } from "@babylonjs/core/Misc/domManagement.js";
import { Scalar } from "@babylonjs/core/Maths/math.scalar.js";
import { Tools } from "@babylonjs/core/Misc/tools.js";
/**
* Class used to create a holographic button in 3D
* @since 5.0.0
*/
export class TouchHolographicButton extends TouchButton3D {
_disposeTooltip() {
this._tooltipFade = null;
if (this._tooltipTextBlock) {
this._tooltipTextBlock.dispose();
}
if (this._tooltipTexture) {
this._tooltipTexture.dispose();
}
if (this._tooltipMesh) {
this._tooltipMesh.dispose();
}
this.onPointerEnterObservable.remove(this._tooltipHoverObserver);
this.onPointerOutObservable.remove(this._tooltipOutObserver);
}
/**
* Rendering ground id of all the mesh in the button
*/
set renderingGroupId(id) {
this._backPlate.renderingGroupId = id;
this._textPlate.renderingGroupId = id;
this._frontPlate.renderingGroupId = id;
if (this._tooltipMesh) {
this._tooltipMesh.renderingGroupId = id;
}
}
get renderingGroupId() {
return this._backPlate.renderingGroupId;
}
/**
* Gets the mesh used to render this control
*/
get mesh() {
return this._backPlate;
}
/**
* Text to be displayed on the tooltip shown when hovering on the button. When set to null tooltip is disabled. (Default: null)
*/
set tooltipText(text) {
if (!text) {
this._disposeTooltip();
return;
}
if (!this._tooltipFade) {
const rightHandedScene = this._backPlate._scene.useRightHandedSystem;
// Create tooltip with mesh and text
this._tooltipMesh = CreatePlane("", { size: 1 }, this._backPlate._scene);
const tooltipBackground = CreatePlane("", { size: 1, sideOrientation: Mesh.DOUBLESIDE }, this._backPlate._scene);
const mat = new StandardMaterial("", this._backPlate._scene);
mat.diffuseColor = Color3.FromHexString("#212121");
tooltipBackground.material = mat;
tooltipBackground.isPickable = false;
this._tooltipMesh.addChild(tooltipBackground);
tooltipBackground.position = Vector3.Forward(rightHandedScene).scale(0.05);
this._tooltipMesh.scaling.y = 1 / 3;
this._tooltipMesh.position = Vector3.Up().scale(0.7).add(Vector3.Forward(rightHandedScene).scale(-0.15));
this._tooltipMesh.isPickable = false;
this._tooltipMesh.parent = this._backPlate;
// Create text texture for the tooltip
this._tooltipTexture = AdvancedDynamicTexture.CreateForMesh(this._tooltipMesh);
this._tooltipTextBlock = new TextBlock();
this._tooltipTextBlock.scaleY = 3;
this._tooltipTextBlock.color = "white";
this._tooltipTextBlock.fontSize = 130;
this._tooltipTexture.addControl(this._tooltipTextBlock);
// Add hover action to tooltip
this._tooltipFade = new FadeInOutBehavior();
this._tooltipFade.delay = 500;
this._tooltipMesh.addBehavior(this._tooltipFade);
this._tooltipHoverObserver = this.onPointerEnterObservable.add(() => {
if (this._tooltipFade) {
this._tooltipFade.fadeIn(true);
}
});
this._tooltipOutObserver = this.onPointerOutObservable.add(() => {
if (this._tooltipFade) {
this._tooltipFade.fadeIn(false);
}
});
}
if (this._tooltipTextBlock) {
this._tooltipTextBlock.text = text;
}
}
get tooltipText() {
if (this._tooltipTextBlock) {
return this._tooltipTextBlock.text;
}
return null;
}
/**
* Gets or sets text for the button
*/
get text() {
return this._text;
}
set text(value) {
if (this._text === value) {
return;
}
this._text = value;
this._rebuildContent();
}
/**
* Gets or sets the image url for the button
*/
get imageUrl() {
return this._imageUrl;
}
set imageUrl(value) {
if (this._imageUrl === value) {
return;
}
this._imageUrl = value;
this._rebuildContent();
}
/**
* Gets the back material used by this button
*/
get backMaterial() {
return this._backMaterial;
}
/**
* Gets the front material used by this button
*/
get frontMaterial() {
return this._frontMaterial;
}
/**
* Gets the plate material used by this button
*/
get plateMaterial() {
return this._plateMaterial;
}
/**
* Gets a boolean indicating if this button shares its material with other HolographicButtons
*/
get shareMaterials() {
return this._shareMaterials;
}
/**
* Sets whether the backplate is visible or hidden. Hiding the backplate is not recommended without some sort of replacement
*/
set isBackplateVisible(isVisible) {
if (this.mesh && !!this._backMaterial) {
if (isVisible && !this._isBackplateVisible) {
this._backPlate.visibility = 1;
}
else if (!isVisible && this._isBackplateVisible) {
this._backPlate.visibility = 0;
}
}
this._isBackplateVisible = isVisible;
}
/**
* Creates a new button
* @param name defines the control name
* @param shareMaterials
*/
constructor(name, shareMaterials = true) {
super(name);
this._shareMaterials = true;
this._isBackplateVisible = true;
this._frontPlateDepth = 0.5;
this._backPlateDepth = 0.04;
this._backplateColor = new Color3(0.08, 0.15, 0.55);
this._backplateToggledColor = new Color3(0.25, 0.4, 0.95);
this._shareMaterials = shareMaterials;
this.pointerEnterAnimation = () => {
this._frontMaterial.leftBlobEnable = true;
this._frontMaterial.rightBlobEnable = true;
};
this.pointerOutAnimation = () => {
this._frontMaterial.leftBlobEnable = false;
this._frontMaterial.rightBlobEnable = false;
};
this.pointerDownAnimation = () => {
if (this._frontPlate && !this.isActiveNearInteraction) {
this._frontPlate.scaling.z = this._frontPlateDepth * 0.2;
this._frontPlate.position = Vector3.Forward(this._frontPlate._scene.useRightHandedSystem).scale((this._frontPlateDepth - 0.2 * this._frontPlateDepth) / 2);
this._textPlate.position = Vector3.Forward(this._textPlate._scene.useRightHandedSystem).scale(-(this._backPlateDepth + 0.2 * this._frontPlateDepth) / 2);
}
};
this.pointerUpAnimation = () => {
if (this._frontPlate) {
this._frontPlate.scaling.z = this._frontPlateDepth;
this._frontPlate.position = Vector3.Forward(this._frontPlate._scene.useRightHandedSystem).scale((this._frontPlateDepth - this._frontPlateDepth) / 2);
this._textPlate.position = Vector3.Forward(this._textPlate._scene.useRightHandedSystem).scale(-(this._backPlateDepth + this._frontPlateDepth) / 2);
}
};
this.onPointerMoveObservable.add((position) => {
if (this._frontPlate && this.isActiveNearInteraction) {
const scale = Vector3.Zero();
if (this._backPlate.getWorldMatrix().decompose(scale, undefined, undefined)) {
let interactionHeight = this._getInteractionHeight(position, this._backPlate.getAbsolutePosition()) / scale.z;
interactionHeight = Scalar.Clamp(interactionHeight - this._backPlateDepth / 2, 0.2 * this._frontPlateDepth, this._frontPlateDepth);
this._frontPlate.scaling.z = interactionHeight;
this._frontPlate.position = Vector3.Forward(this._frontPlate._scene.useRightHandedSystem).scale((this._frontPlateDepth - interactionHeight) / 2);
this._textPlate.position = Vector3.Forward(this._textPlate._scene.useRightHandedSystem).scale(-(this._backPlateDepth + interactionHeight) / 2);
}
}
});
this._pointerHoverObserver = this.onPointerMoveObservable.add((hoverPosition) => {
this._frontMaterial.globalLeftIndexTipPosition = hoverPosition;
});
}
_getTypeName() {
return "TouchHolographicButton";
}
_rebuildContent() {
this._disposeFacadeTexture();
const panel = new StackPanel();
panel.isVertical = true;
if (IsDocumentAvailable() && !!document.createElement) {
if (this._imageUrl) {
const image = new Image();
image.source = this._imageUrl;
image.paddingTop = "40px";
image.height = "180px";
image.width = "100px";
image.paddingBottom = "40px";
panel.addControl(image);
}
}
if (this._text) {
const text = new TextBlock();
text.text = this._text;
text.color = "white";
text.height = "30px";
text.fontSize = 24;
panel.addControl(text);
}
this.content = panel;
}
// Mesh association
_createNode(scene) {
this.name = this.name ?? "TouchHolographicButton";
const collisionMesh = CreateBox(`${this.name}_collisionMesh`, {
width: 1.0,
height: 1.0,
depth: this._frontPlateDepth,
}, scene);
collisionMesh.isPickable = true;
collisionMesh.isNearPickable = true;
collisionMesh.visibility = 0;
collisionMesh.position = Vector3.Forward(scene.useRightHandedSystem).scale(-this._frontPlateDepth / 2);
const baseUrl = Tools.GetAssetUrl(TouchHolographicButton.MODEL_BASE_URL);
// eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
SceneLoader.ImportMeshAsync(undefined, baseUrl, TouchHolographicButton.MODEL_FILENAME, scene).then((result) => {
const alphaMesh = CreateBox("${this.name}_alphaMesh", {
width: 1.0,
height: 1.0,
depth: 1.0,
}, scene);
alphaMesh.isPickable = false;
alphaMesh.material = new StandardMaterial("${this.name}_alphaMesh_material", scene);
alphaMesh.material.alpha = 0.15;
const importedFrontPlate = result.meshes[1];
importedFrontPlate.name = `${this.name}_frontPlate`;
importedFrontPlate.isPickable = false;
importedFrontPlate.scaling.z = this._frontPlateDepth;
alphaMesh.parent = importedFrontPlate;
importedFrontPlate.parent = collisionMesh;
if (this._frontMaterial) {
importedFrontPlate.material = this._frontMaterial;
}
this._frontPlate = importedFrontPlate;
});
this._backPlate = CreateBox(`${this.name}_backPlate`, {
width: 1.0,
height: 1.0,
depth: this._backPlateDepth,
}, scene);
this._backPlate.position = Vector3.Forward(scene.useRightHandedSystem).scale(this._backPlateDepth / 2);
this._backPlate.isPickable = false;
this._textPlate = super._createNode(scene);
this._textPlate.name = `${this.name}_textPlate`;
this._textPlate.isPickable = false;
this._textPlate.position = Vector3.Forward(scene.useRightHandedSystem).scale(-this._frontPlateDepth / 2);
this._backPlate.addChild(collisionMesh);
this._backPlate.addChild(this._textPlate);
const tn = new TransformNode(`{this.name}_root`, scene);
this._backPlate.setParent(tn);
this.collisionMesh = collisionMesh;
this.collidableFrontDirection = this._backPlate.forward.negate(); // Mesh is facing the wrong way
return tn;
}
_applyFacade(facadeTexture) {
this._plateMaterial.emissiveTexture = facadeTexture;
this._plateMaterial.opacityTexture = facadeTexture;
this._plateMaterial.diffuseColor = new Color3(0.4, 0.4, 0.4);
}
_createBackMaterial(mesh) {
this._backMaterial = new FluentMaterial(this.name + "backPlateMaterial", mesh.getScene());
this._backMaterial.albedoColor = this._backplateColor;
this._backMaterial.renderBorders = true;
this._backMaterial.renderHoverLight = false;
}
_createFrontMaterial(mesh) {
this._frontMaterial = new FluentButtonMaterial(this.name + "Front Material", mesh.getScene());
}
_createPlateMaterial(mesh) {
this._plateMaterial = new StandardMaterial(this.name + "Plate Material", mesh.getScene());
this._plateMaterial.specularColor = Color3.Black();
}
_onToggle(newState) {
if (this._backMaterial) {
if (newState) {
this._backMaterial.albedoColor = this._backplateToggledColor;
}
else {
this._backMaterial.albedoColor = this._backplateColor;
}
}
super._onToggle(newState);
}
_affectMaterial(mesh) {
if (this._shareMaterials) {
// Back
if (!this._host._touchSharedMaterials["backFluentMaterial"]) {
this._createBackMaterial(mesh);
this._host._touchSharedMaterials["backFluentMaterial"] = this._backMaterial;
}
else {
this._backMaterial = this._host._touchSharedMaterials["backFluentMaterial"];
}
// Front
if (!this._host._touchSharedMaterials["frontFluentMaterial"]) {
this._createFrontMaterial(mesh);
this._host._touchSharedMaterials["frontFluentMaterial"] = this._frontMaterial;
}
else {
this._frontMaterial = this._host._touchSharedMaterials["frontFluentMaterial"];
}
}
else {
this._createBackMaterial(mesh);
this._createFrontMaterial(mesh);
}
this._createPlateMaterial(mesh);
this._backPlate.material = this._backMaterial;
this._textPlate.material = this._plateMaterial;
if (!this._isBackplateVisible) {
this._backPlate.visibility = 0;
}
if (this._frontPlate) {
this._frontPlate.material = this._frontMaterial;
}
this._rebuildContent();
}
/**
* Releases all associated resources
*/
dispose() {
super.dispose(); // will dispose main mesh ie. back plate
this._disposeTooltip();
this.onPointerMoveObservable.remove(this._pointerHoverObserver);
if (!this.shareMaterials) {
this._backMaterial.dispose();
this._frontMaterial.dispose();
this._plateMaterial.dispose();
if (this._pickedPointObserver) {
this._host.onPickedPointChangedObservable.remove(this._pickedPointObserver);
this._pickedPointObserver = null;
}
}
}
}
/**
* Base Url for the button model.
*/
TouchHolographicButton.MODEL_BASE_URL = "https://assets.babylonjs.com/core/MRTK/";
/**
* File name for the button model.
*/
TouchHolographicButton.MODEL_FILENAME = "mrtk-fluent-button.glb";
//# sourceMappingURL=touchHolographicButton.js.map