UNPKG

@vrspace/babylonjs

Version:

vrspace.org babylonjs client

164 lines (149 loc) 6.35 kB
import { Label } from './label.js'; /** Menu consisting of vertical buttons in 3D space and associated labels. */ export class Buttons { /** @param scene @param title string displayed above the menu @param options array of options, string labels or objects @param callback executed when button is activated @param property optional, if options are object, this specifies string property to display as label */ constructor(scene,title,options,callback,property) { this.scene = scene; this.title = title; this.options = options; this.callback = callback; this.property = property; this.buttonHeight = 1; this.spacing = 1.1; this.color = "white"; //this.background = true; //experimental this.group = new BABYLON.TransformNode("ButtonGroup:"+this.title, scene); this.groupWidth = 0; this.buttons = []; this.selectedOption = -1; this.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT; this.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP; this.controls = []; this.textures = []; this.materials = []; this.display(); } /** Dispose of everything */ dispose() { delete this.selectedMaterial; delete this.unselectedMaterial; this.group.dispose(); for ( var i = 0; i < this.controls.length; i++ ) { this.controls[i].dispose(); } for ( var i = 0; i < this.textures.length; i++ ) { this.textures[i].dispose(); } for ( var i = 0; i < this.materials.length; i++ ) { this.materials[i].dispose(); } console.log("Disposed of buttons "+this.title); } /** Set the height, rescales the menu */ setHeight(height) { var scale = height/this.options.length; this.group.scaling = new BABYLON.Vector3(scale, scale, scale); } /** Display the menu, adds a pointer observable */ display() { // CHECKME: better use emissive color? this.selectedMaterial = new BABYLON.StandardMaterial("selectedButtonMaterial", this.scene); this.selectedMaterial.diffuseColor = new BABYLON.Color3(0,0,0); this.selectedMaterial.emissiveColor = new BABYLON.Color3(.4,.8,.4); this.selectedMaterial.disableLighting = true; this.materials.push(this.selectedMaterial); this.unselectedMaterial = new BABYLON.StandardMaterial("unselectedButtonMaterial", this.scene); this.unselectedMaterial.diffuseColor = new BABYLON.Color3(0,0,0); this.unselectedMaterial.emissiveColor = new BABYLON.Color3(.2,.2,.2); this.unselectedMaterial.disableLighting = true; this.materials.push(this.unselectedMaterial); if ( this.title && this.title.length > 0 ) { var titleLabel = new Label( this.title, new BABYLON.Vector3(this.title.length/Label.fontRatio,this.spacing*2,0), this.group ); titleLabel.height = 2; titleLabel.horizontalAlignment = this.horizontalAlignment; titleLabel.verticalAlignment = this.verticalAlignment; titleLabel.display(); this.controls.push(titleLabel); } for ( var i = 0; i < this.options.length; i ++ ) { if ( this.property ) { var option = this.options[i][this.property]; } else { var option = this.options[i]; } this.groupWidth = Math.max( this.groupWidth, option.length); var buttonLabel = new Label( option, new BABYLON.Vector3(option.length/(Label.fontRatio*2)+this.buttonHeight,-i*this.spacing,0), this.group ); buttonLabel.horizontalAlignment = this.horizontalAlignment; buttonLabel.verticalAlignment = this.verticalAlignment; buttonLabel.display(); this.controls.push(buttonLabel); var button = BABYLON.MeshBuilder.CreateCylinder("Button"+option, {height:.1, diameter:this.buttonHeight*.8}, this.scene); button.material = this.unselectedMaterial; button.rotation = new BABYLON.Vector3(Math.PI/2, 0, 0); button.position = new BABYLON.Vector3(this.buttonHeight/2, -i*this.spacing, 0); button.parent = this.group; this.buttons.push(button); } this.scene.onPointerObservable.add( (e) => { if(e.type == BABYLON.PointerEventTypes.POINTERDOWN){ var p = e.pickInfo; for ( var i = 0; i < this.options.length; i++ ) { if ( p.pickedMesh == this.buttons[i] ) { // CHECKME we may want to handle double click somehow if ( i != this.selectedOption || this.turnOff) { this.select(i); } break; } } } }); // paints background plane, can't be semi-transparent though if ( this.background ) { console.log("Group width: "+this.groupWidth); var backgroundWidth = this.groupWidth/1.8; var backgroundHeight = this.options.length*this.spacing; var backgroundOffset = this.buttonHeight*.8; // same as button cylinder diameter var backPlane = BABYLON.MeshBuilder.CreatePlane("ButtonBackground:"+this.title, {height:backgroundHeight,width:backgroundWidth}, this.scene); backPlane.position = new BABYLON.Vector3(backgroundWidth/2+backgroundOffset,-backgroundHeight/2+this.spacing/2,.2); backPlane.parent = this.group; var backgroundMaterial = new BABYLON.StandardMaterial("unselectedButtonMaterial", this.scene); backgroundMaterial.disableLighting = true; //backgroundMaterial.alpha = 0.5; // produces weird transparency effects this.materials.push(backgroundMaterial); backPlane.material = backgroundMaterial; } } /** Select an option, executed when a button is pressed. Executes the callback passing selected option as parameter. */ select(i) { console.log("Selected: "+this.options[i].name); if ( this.callback ) { this.callback(this.options[i]); } this.buttons[i].material = this.selectedMaterial; if ( this.selectedOption > -1 ) { this.buttons[this.selectedOption].material = this.unselectedMaterial; } if ( i != this.selectedOption ) { this.selectedOption = i; } else { this.selectedOption = -1; } } // CHECKME: not used so far hide() { this.group.isEnabled = false; } show() { this.group.isEnabled = true; } }