// // generic.js // // // Created by James B. Pollack @imgntnon 9/26/2015 // Copyright 2014 High Fidelity, Inc. // // Web app side of the App - contains GUI. // This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // todo: quaternion folders, color pickers, read-only properties, animation settings and other nested objects, scale gui width with window resizing /*global window, EventBridge, dat, convertBinaryToBoolean, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/ var Settings = function() { return; }; var controllers = []; var folders = []; var gui; var settings = new Settings(); function convertBinaryToBoolean(value) { if (value === 0) { return false; } return true; } function loadGUI() { console.log('loadGUI '); //whether or not to autoplace gui = new dat.GUI({ autoPlace: false }); //if not autoplacing, put gui in a custom container if (gui.autoPlace === false) { var customContainer = document.getElementById('my-gui-container'); customContainer.appendChild(gui.domElement); gui.width = 400; } //add save settings ability (try not to use localstorage) gui.remember(settings); //get object keys var keys = _.keys(settings); //for each key... _.each(keys, function(key) { // //FOR NOW TO SHOW THAT IT WORKS RESTRICT TO A FEW PROPERTIES. NEED TO ADD SUPPORT FOR VEC3, QUAT, COLORS if (key.indexOf('name') !== -1 || key.indexOf('description') !== -1 || key.indexOf('visible') !== -1 || key.indexOf('collisionsWillMove') !== -1) { // we aren't getting checkboxes for collisionsWillMove because it comes across the wire as a 1, not a true if (key.indexOf('visible') !== -1) { settings.visible = convertBinaryToBoolean(settings.visible); } if (key.indexOf('collisionsWillMove') !== -1) { settings.collisionsWillMove = convertBinaryToBoolean(settings.collisionsWillMove); } } else { return; } console.log('key:::' + key); //add this key as a controller to the gui var controller = gui.add(settings, key).listen(); // the call below is potentially expensive but will enable two way binding. needs testing to see how many it supports at once. // keep track of our controller controllers.push(controller); //hook into change events for this gui controller controller.onChange(function(value) { // Fires on every change, drag, keypress, etc. writeDataToInterface(this.property, value); }); // controller.onFinishChange(function(value) { // // Fires when a controller loses focus. // //writeDataToInterface(this.property, value); // return; // }); }); createVec3Folder('dimensions'); createVec3Folder('velocity'); createVec3Folder('gravity'); createVec3Folder('acceleration'); createVec3Folder('angularVelocity'); createQuatFolder('rotation'); } function createVec3Folder(category) { var folder = gui.addFolder(category); folder.add(settings[category], 'x').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category][this.property] = value; obj[category].y = settings[category].y; obj[category].z = settings[category].z; writeVec3ToInterface(obj); }); folder.add(settings[category], 'y').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category][this.property] = value; obj[category].x = settings[category].x; obj[category].z = settings[category].z; writeVec3ToInterface(obj); }); folder.add(settings[category], 'z').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category][this.property] = value; obj[category].y = settings[category].y; obj[category].x = settings[category].x; writeVec3ToInterface(obj); }); folder.open(); folders.push(folder); } function createQuatFolder(category) { var folder = gui.addFolder(category); folder.add(settings[category], 'x').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category][this.property] = value; obj[category].y = settings[category].y; obj[category].z = settings[category].z; obj[category].w = settings[category].w; writeVec3ToInterface(obj); }); folder.add(settings[category], 'y').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category].x = settings[category].x; obj[category][this.property] = value; obj[category].z = settings[category].z; obj[category].w = settings[category].w; writeVec3ToInterface(obj); }); folder.add(settings[category], 'z').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category].x = settings[category].x; obj[category].y = settings[category].y; obj[category][this.property] = value; obj[category].w = settings[category].w; writeVec3ToInterface(obj); }); folder.add(settings[category], 'w').step(0.1).listen().onChange(function(value) { // Fires when a controller loses focus. var obj = {}; obj[category] = {}; obj[category].x = settings[category].x; obj[category].y = settings[category].y; obj[category].z = settings[category].z; obj[category][this.property] = value; writeVec3ToInterface(obj); }); folder.open(); folders.push(folder); } function writeDataToInterface(property, value) { console.log('WRITE SOME DATA TO INTERFACE' + property + ":::" + value); var data = {}; data[property] = value; var sendData = { messageType: "settings_update", updatedSettings: data, }; var stringifiedData = JSON.stringify(sendData); EventBridge.emitWebEvent( stringifiedData ); } function writeVec3ToInterface(obj) { console.log('VEC3 UPDATE TO INTERFACE FROM SETTINGS'); var sendData = { messageType: "settings_update", updatedSettings: obj, }; var stringifiedData = JSON.stringify(sendData); EventBridge.emitWebEvent( stringifiedData ); } window.onload = function() { console.log('GUI PAGE LOADED'); if (typeof EventBridge !== 'undefined') { console.log('WE HAVE AN EVENT BRIDGE, SEND PAGE LOADED EVENT '); var stringifiedData = JSON.stringify({ messageType: 'page_loaded' }); console.log('SEND PAGE LOADED EVENT FROM GUI TO INTERFACE '); EventBridge.emitWebEvent( stringifiedData ); listenForSettingsUpdates(); } else { console.log('No event bridge, probably not in interface.'); } }; function listenForSettingsUpdates() { console.log('GUI IS LISTENING FOR MESSAGES FROM INTERFACE'); EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); if (data.messageType === 'object_update') { _.each(data.objectSettings, function(value, key) { settings[key] = value; if (key.indexOf('visible') !== -1) { settings.visible = convertBinaryToBoolean(settings.visible); } if (key.indexOf('collisionsWillMove') !== -1) { settings.collisionsWillMove = convertBinaryToBoolean(settings.collisionsWillMove); } }); } if (data.messageType === 'initial_settings') { console.log('INITIAL SETTINGS FROM INTERFACE:::' + JSON.stringify(data.initialSettings)); _.each(data.initialSettings, function(value, key) { console.log('key ' + key); console.log('value ' + JSON.stringify(value)); settings[key] = {}; settings[key] = value; }); loadGUI(); } if (data.messageType === 'settings_update') { console.log('SETTINGS UPDATE FROM INTERFACE:::' + JSON.stringify(data.updatedSettings)); _.each(data.updatedSettings, function(value, key) { settings[key] = value; }); } }); } function manuallyUpdateDisplay(gui) { // Iterate over all controllers var i; for (i in gui.__controllers) { gui.__controllers[i].updateDisplay(); } } function removeContainerDomElement() { var elem = document.getElementById("my-gui-container"); elem.parentNode.removeChild(elem); }