UNPKG

@vrspace/babylonjs

Version:

vrspace.org babylonjs client

186 lines (166 loc) 6.9 kB
import { VRSPACEUI } from '../vrspace-ui.js'; import { TextWriter } from '../../core/text-writer.js'; import { TextArea } from '../widget/text-area.js'; /** * A 3D panel displayed in the world, with arbitrary number of holographic buttons. * Each button must have text and image. Text is displayed when pointer is over the button, * and it can be either 3D (framerate impact) or texture (readability impact). This is controlled by * text3d property, false by default. */ export class ScrollablePanel { constructor(scene, name) { this.scene = scene; this.uiRoot = new BABYLON.TransformNode(name); this.text3d = false; this.uiRoot.position = new BABYLON.Vector3(0, 3, 0); this.uiRoot.rotation = new BABYLON.Vector3(0, 0, 0); this.uiRoot.scaling = new BABYLON.Vector3(.5, .5, .5); //this.guiManager = new BABYLON.GUI.GUI3DManager(this.scene); // causes transparency issues this.guiManager = VRSPACEUI.guiManager; this.panel = new BABYLON.GUI.CylinderPanel(); this.panel.blocklayout = true; // optimization, requires updateLayout() call this.panel.margin = 0.05; this.panel.columns = 6; this.guiManager.addControl(this.panel); this.panel.linkToTransformNode(this.uiRoot); this.buttonPrev = new BABYLON.GUI.HolographicButton("prev"); this.buttonPrev.imageUrl = VRSPACEUI.contentBase + "/content/icons/upload.png"; this.guiManager.addControl(this.buttonPrev); this.buttonPrev.linkToTransformNode(this.uiRoot); this.buttonPrev.position = new BABYLON.Vector3(-4, 0, 4); this.buttonPrev.mesh.rotation = new BABYLON.Vector3(0, 0, Math.PI / 2); this.buttonPrev.mesh.isNearPickable = VRSPACEUI.allowHands; this.buttonPrev.tooltipText = "Previous"; this.buttonPrev.isVisible = false; this.buttonNext = new BABYLON.GUI.HolographicButton("next"); this.buttonNext.imageUrl = VRSPACEUI.contentBase + "/content/icons/upload.png"; this.guiManager.addControl(this.buttonNext); this.buttonNext.linkToTransformNode(this.uiRoot); this.buttonNext.position = new BABYLON.Vector3(4, 0, 4); this.buttonNext.mesh.rotation = new BABYLON.Vector3(0, 0, -Math.PI / 2); this.buttonNext.mesh.isNearPickable = VRSPACEUI.allowHands; this.buttonNext.tooltipText = "Next"; this.buttonNext.isVisible = false; // same material used for all buttons in this UI: this.buttonNext.backMaterial.alpha = .5; } /** * Relocate panel to given distance from the camera, by default 6 */ relocatePanel(distanceFromCamera = 6) { var forwardDirection = VRSPACEUI.hud.camera.getForwardRay(distanceFromCamera).direction; this.uiRoot.position = VRSPACEUI.hud.camera.position.add(forwardDirection); this.uiRoot.rotation = new BABYLON.Vector3(VRSPACEUI.hud.cameraRotation().x, VRSPACEUI.hud.cameraRotation().y, VRSPACEUI.hud.cameraRotation().z); } /** * Call this before consecutive addButton calls. * @param hasPrevious true if previous button is to be rendered * @param hasNext true if next button is to be rendered * @param onPrevious callback to be executed when previous button is activated * @param onNext callback to be executed when next button is activated */ beginUpdate(hasPrevious, hasNext, onPrevious, onNext) { // workaround for panel buttons all messed up this.previousCoord = { pos: this.uiRoot.position, rot: this.uiRoot.rotation }; this.uiRoot.position = new BABYLON.Vector3(0, 2, 0); this.uiRoot.rotation = new BABYLON.Vector3(0, 0, 0); this.panel.linkToTransformNode(); this.panel.children.forEach((button) => { button.dispose() }); this.buttonPrev.isVisible = hasPrevious; this.buttonPrev.onPointerDownObservable.clear(); this.buttonPrev.onPointerDownObservable.add(onPrevious); this.buttonNext.isVisible = hasNext; this.buttonNext.onPointerDownObservable.clear(); this.buttonNext.onPointerDownObservable.add(onNext); } /** * Call this after all buttons are added. Optionally relocates the panel. */ endUpdate(relocate) { this.panel.linkToTransformNode(this.uiRoot); this.panel.updateLayout(); if (relocate) { this.relocatePanel(); } else { this.uiRoot.position = this.previousCoord.pos; this.uiRoot.rotation = this.previousCoord.rot; } } /** * Create and add a holographic button to the panel. * @param text to be rendered on pointer over, String or array of String * @param image url to display over the button * @param callback function called on pointer down, takes the button as the argument */ addButton(text, image, callback) { if (typeof (text) === "string") { text = [text]; } var button = new BABYLON.GUI.HolographicButton(text[0]); this.panel.addControl(button); button.mesh.isNearPickable = VRSPACEUI.allowHands; button.imageUrl = image; button.plateMaterial.disableLighting = true; button.content.scaleX = 2; button.content.scaleY = 2; button.onPointerEnterObservable.add(() => { this.buttonTextWrite(button.node, text); }); button.onPointerOutObservable.add(() => { this.buttonTextClear(button.node); }); button.onPointerDownObservable.add(() => callback(button)); } /** * Internally called to show tooltip text on pointer enter */ buttonTextWrite(node, lines) { if (this.text3d) { if (!this.writer) { this.writer = new TextWriter(this.scene); } this.writer.writeArray(node, lines); } else { if (!this.textArea) { this.textArea = new TextArea(this.scene); this.textArea.addHandles = false; this.textArea.size = 1; this.textArea.addBackground = false; this.textArea.height = 128; this.textArea.width = 256; this.textArea.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER; this.textArea.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER; this.textArea.position = new BABYLON.Vector3(0, 1, -.2); this.textArea.show(); } this.textArea.group.parent = node; this.textArea.writeArray(lines); } } /** * Internally called to remove tooltip on pointer exit */ buttonTextClear(node) { if (this.text3d) { this.writer.clear(node); } else if (this.textArea) { this.textArea.dispose(); this.textArea = null; } } /** * Clean up */ dispose() { if (this.textArea) { this.textArea.dispose(); } if (this.writer) { // currently we can't dispose of writer //this.writer.dispose(); } this.buttonPrev.dispose(); this.buttonNext.dispose(); this.panel.dispose(); } }