From 7bcd9f9f9544256638657ca15c78671c4b7e47d6 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 12:22:11 -0400 Subject: [PATCH 01/85] * integrate support for room scale "moves as you move" * migrate doppleganger debug controls into separate module * misc cleanup --- .../app-doppleganger-attachments.js | 106 +- .../dist/app-doppleganger-marketplace.js | 1501 +++++++++++++++++ .../doppleganger-attachments.js | 53 +- .../doppleganger-debug.js | 158 ++ .../doppleganger-attachments/doppleganger.js | 238 +-- .../doppleganger-attachments/makefile | 11 + .../doppleganger-attachments/model-helper.js | 9 +- .../doppleganger-attachments/package.json | 5 + .../doppleganger-attachments/readme.md | 4 + .../doppleganger-attachments/utils.js | 8 + 10 files changed, 1843 insertions(+), 250 deletions(-) create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/makefile create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/package.json create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/readme.md diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js index 4617cf47b6..c09ad602f8 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js @@ -9,21 +9,44 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ -var require = Script.require; +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (typeof require !== 'function') { + require = Script.require; +} +var VERSION = '0.0.0'; var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); var DopplegangerClass = require('./doppleganger.js'), DopplegangerAttachments = require('./doppleganger-attachments.js'), - modelHelper = require('./model-helper.js').modelHelper; + DebugControls = require('./doppleganger-debug.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: require('./doppleganger-i.svg.json'), + activeIcon: require('./doppleganger-a.svg.json'), +}); var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), - button = tablet.addButton({ - icon: Script.resolvePath('./doppleganger-i.svg'), - activeIcon: Script.resolvePath('./doppleganger-a.svg'), - text: 'MIRROR' - }); + button = tablet.addButton(buttonConfig); Script.scriptEnding.connect(function() { tablet.removeButton(button); @@ -65,6 +88,59 @@ var doppleganger = new DopplegangerClass({ } } +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + // hide the doppleganger if this client script is unloaded Script.scriptEnding.connect(doppleganger, 'stop'); @@ -103,15 +179,21 @@ doppleganger.modelLoaded.connect(function(error, result) { // add debug indicators, but only if the user has configured the settings value if (Settings.getValue('debug.doppleganger', false)) { - WANT_DEBUG = true; - DopplegangerClass.addDebugControls(doppleganger); + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { - if (WANT_DEBUG) { - print('app-doppleganger | ' + [].slice.call(arguments).join(' ')); - } + WANT_DEBUG && log.apply(this, arguments); } + // ---------------------------------------------------------------------------- UserActivityLogger.logAction('doppleganger_app_load'); diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js new file mode 100644 index 0000000000..6abd3a0fc5 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -0,0 +1,1501 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/* eslint-env commonjs */ +/* global console */ + +module.exports = { + version: '0.0.1', + bind: bind, + signal: signal, + assign: assign, + assert: assert +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +// @function - bind a function to a `this` context +// @param {Object} - the `this` context +// @param {Function|String} - function or method name +// @param {value} varargs... - optional curry-right arguments (passed to method after any explicit arguments) +function bind(thiz, method, varargs) { + method = thiz[method] || method; + varargs = [].slice.call(arguments, 2); + return function() { + var args = [].slice.call(arguments).concat(varargs); + return method.apply(thiz, args); + }; +} + +// @function - Qt signal polyfill +function signal(template) { + var callbacks = []; + return Object.defineProperties(function() { + var args = [].slice.call(arguments); + callbacks.forEach(function(obj) { + obj.handler.apply(obj.scope, args); + }); + }, { + connect: { value: function(scope, handler) { + var callback = {scope: scope, handler: scope[handler] || handler || scope}; + if (!callback.handler || !callback.handler.apply) { + throw new Error('invalid arguments to connect:' + [template, scope, handler]); + } + callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); + }}, + disconnect: { value: function(scope, handler) { + var match = {scope: scope, handler: scope[handler] || handler || scope}; + callbacks = callbacks.filter(function(obj) { + return !(obj.scope === match.scope && obj.handler === match.handler); + }); + }} + }); +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill +/* eslint-disable */ +function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; +} +/* eslint-enable */ +// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill + +// examples: +// assert(function assertion() { return (conditions === true) }, 'assertion failed!') +// var neededValue = assert(idString, 'idString not specified!'); +// assert(false, 'unexpected state'); +function assert(truthy, message) { + message = message || 'Assertion Failed:'; + + if (typeof truthy === 'function' && truthy.name === 'assertion') { + // extract function body to display with the assertion message + var assertion = (truthy+'').replace(/[\r\n]/g, ' ') + .replace(/^[^{]+\{|\}$|^\s*|\s*$/g, '').trim() + .replace(/^return /,'').replace(/\s[\r\n\t\s]+/g, ' '); + message += ' ' + JSON.stringify(assertion); + try { + truthy = truthy(); + } catch (e) { + message += '(exception: ' + e +')'; + } + } + if (!truthy) { + message += ' ('+truthy+')'; + throw new Error(message); + } + return truthy; +} + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +// model-helper.js +// +// Created by Timothy Dedischew on 06/01/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module model-helper +// +// This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and +// also initial plumbing helpers to eliminate unnecessary API differences when working with Model Overlays and +// Model Entities at a high-level from scripting. + +var utils = __webpack_require__(0), + assert = utils.assert; + +module.exports = { + version: '0.0.1', + ModelReadyWatcher: ModelReadyWatcher +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +var _objectDeleted = utils.signal(function objectDeleted(objectID){}); +// proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal +var objectDeleted = utils.assign(function objectDeleted(objectID){}, { + connect: function() { + Overlays.overlayDeleted.connect(_objectDeleted); + // Entities.deletingEntity.connect(objectDeleted); + Script.scriptEnding.connect(function() { + Overlays.overlayDeleted.disconnect(_objectDeleted); + // Entities.deletingEntity.disconnect(objectDeleted); + }); + // hereafter _objectDeleted.connect will be used instead + this.connect = utils.bind(_objectDeleted, 'connect'); + return this.connect.apply(this, arguments); + }, + disconnect: utils.bind(_objectDeleted, 'disconnect') +}); + +var modelHelper = module.exports.modelHelper = { + // Entity <-> Overlay property translations + _entityFromOverlay: { + modelURL: function url() { + return this.url; + }, + dimensions: function dimensions() { + return Vec3.multiply(this.scale, this.naturalDimensions); + } + }, + _overlayFromEntity: { + url: function modelURL() { + return this.modelURL; + }, + scale: function scale() { + return this.dimensions && this.naturalDimensions && { + x: this.dimensions.x / this.naturalDimensions.x, + y: this.dimensions.y / this.naturalDimensions.y, + z: this.dimensions.z / this.naturalDimensions.z + }; + } + }, + objectDeleted: objectDeleted, + type: function(objectID) { + // TODO: support Model Entities (by detecting type from objectID, which is already possible) + return !Uuid.isNull(objectID) ? 'overlay' : null; + }, + addObject: function(properties) { + var type = 'overlay'; // this.resolveType(properties) + switch (type) { + case 'overlay': return Overlays.addOverlay(properties.type, this.toOverlayProps(properties)); + } + return false; + }, + editObject: function(objectID, properties) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.editOverlay(objectID, this.toOverlayProps(properties)); + } + return false; + }, + deleteObject: function(objectID) { + return this.type(objectID) === 'overlay' && Overlays.deleteOverlay(objectID); + }, + getProperty: function(objectID, propertyName) { + return this.type(objectID) === 'overlay' && Overlays.getProperty(objectID, propertyName); + }, + getProperties: function(objectID, filter) { + switch (this.type(objectID)) { + case 'overlay': + filter = Array.isArray(filter) ? filter : [ + 'position', 'rotation', 'localPosition', 'localRotation', 'parentID', + 'parentJointIndex', 'scale', 'name', 'visible', 'type', 'url', + 'dimensions', 'naturalDimensions', 'grabbable' + ]; + var properties = filter.reduce(function(out, propertyName) { + out[propertyName] = Overlays.getProperty(objectID, propertyName); + return out; + }, {}); + return this.toEntityProps(properties); + } + return null; + }, + // adapt Entity conventions to Overlay (eg: { modelURL: ... } -> { url: ... }) + toOverlayProps: function(properties) { + var result = {}; + for (var from in this._overlayFromEntity) { + var adapter = this._overlayFromEntity[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + // adapt Overlay conventions to Entity (eg: { url: ... } -> { modelURL: ... }) + toEntityProps: function(properties) { + var result = {}; + for (var from in this._entityToOverlay) { + var adapter = this._entityToOverlay[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + editObjects: function(updatedObjects) { + var objectIDs = Object.keys(updatedObjects), + type = this.type(objectIDs[0]); + switch (type) { + case 'overlay': + var translated = {}; + for (var objectID in updatedObjects) { + translated[objectID] = this.toOverlayProps(updatedObjects[objectID]); + } + return Overlays.editOverlays(translated); + } + return false; + }, + getJointIndex: function(objectID, name) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames').indexOf(name); + } + return -1; + }, + getJointNames: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames'); + } + return []; + }, + // @function - derives mirrored joint names from a list of regular joint names + // @param {Array} - list of joint names to mirror + // @return {Array} - list of mirrored joint names (note: entries for non-mirrored joints will be `undefined`) + deriveMirroredJointNames: function(jointNames) { + return jointNames.map(function(name, i) { + if (/Left/.test(name)) { + return name.replace('Left', 'Right'); + } + if (/Right/.test(name)) { + return name.replace('Right', 'Left'); + } + return undefined; + }); + }, + getJointPosition: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions')[index]; + } + return Vec3.ZERO; + }, + getJointPositions: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions'); + } + return []; + }, + getJointOrientation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations')[index]; + } + return Quat.normalize({}); + }, + getJointOrientations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations'); + } + }, + getJointTranslation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations')[index]; + } + return Vec3.ZERO; + }, + getJointTranslations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations'); + } + return []; + }, + getJointRotation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations')[index]; + } + return Quat.normalize({}); + }, + getJointRotations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations'); + } + return []; + } +}; // modelHelper + + +// @property {PreconditionFunction} - indicates when the model's jointNames have become available +ModelReadyWatcher.JOINTS = function(state) { + return Array.isArray(state.jointNames); +}; +// @property {PreconditionFunction} - indicates when a model's naturalDimensions have become available +ModelReadyWatcher.DIMENSIONS = function(state) { + return Vec3.length(state.naturalDimensions) > 0; +}; +// @property {PreconditionFunction} - indicates when both a model's naturalDimensions and jointNames have become available +ModelReadyWatcher.JOINTS_AND_DIMENSIONS = function(state) { + // eslint-disable-next-line new-cap + return ModelReadyWatcher.JOINTS(state) && ModelReadyWatcher.DIMENSIONS(state); +}; +// @property {int} - interval used for continually rechecking model readiness, until ready or a timeout occurs +ModelReadyWatcher.RECHECK_MS = 50; +// @property {int} - default wait time before considering a model unready-able. +ModelReadyWatcher.DEFAULT_TIMEOUT_SECS = 10; + +// @private @class - waits for model to become usable inworld and tracks errors/timeouts +// @param [Object} options -- key/value config options: +// @param {ModelResource} options.resource - a ModelCache prefetched resource to observe for determining load state +// @param {Uuid} options.objectID - an inworld object to observe for determining readiness states +// @param {Function} [options.precondition=ModelReadyWatcher.JOINTS] - the precondition used to determine if the model is usable +// @param {int} [options.maxWaitSeconds=10] - max seconds to wait for the model to become usable, after which a timeout error is emitted +// @return {ModelReadyWatcher} +function ModelReadyWatcher(options) { + options = utils.assign({ + precondition: ModelReadyWatcher.JOINTS, + maxWaitSeconds: ModelReadyWatcher.DEFAULT_TIMEOUT_SECS + }, options); + + assert(!Uuid.isNull(options.objectID), 'Error: invalid options.objectID'); + assert(options.resource && 'state' in options.resource, 'Error: invalid options.resource'); + assert(typeof options.precondition === 'function', 'Error: invalid options.precondition'); + + utils.assign(this, { + resource: options.resource, + objectID: options.objectID, + precondition: options.precondition, + + // @signal - emitted when the model becomes ready, or with the error that prevented it + modelReady: utils.signal(function modelReady(error, result){}), + + // @public + ready: false, // tracks readiness state + jointNames: null, // populated with detected jointNames + naturalDimensions: null, // populated with detected naturalDimensions + + _startTime: new Date, + _watchdogTimer: Script.setTimeout(utils.bind(this, function() { + this._watchdogTimer = null; + }), options.maxWaitSeconds * 1000), + _interval: Script.setInterval(utils.bind(this, '_waitUntilReady'), ModelReadyWatcher.RECHECK_MS) + }); + + this.modelReady.connect(this, function(error, result) { + this.ready = !error && result; + }); +} + +ModelReadyWatcher.prototype = { + contructor: ModelReadyWatcher, + // @public method -- cancels monitoring and (if model was not yet ready) emits an error + cancel: function() { + return this._stop() && !this.ready && this.modelReady('cancelled', null); + }, + // stop pending timers + _stop: function() { + var stopped = 0; + if (this._watchdogTimer) { + Script.clearTimeout(this._watchdogTimer); + this._watchdogTimer = null; + stopped++; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = null; + stopped++; + } + return stopped; + }, + // the monitoring thread func + _waitUntilReady: function() { + var error = null, result = null; + if (!this._watchdogTimer) { + error = this.precondition.name || 'timeout'; + } else if (this.resource.state === Resource.State.FAILED) { + error = 'prefetch_failed'; + } else if (this.resource.state === Resource.State.FINISHED) { + // in theory there will always be at least one "joint name" that represents the main submesh + var names = modelHelper.getJointNames(this.objectID); + if (Array.isArray(names) && names.length) { + this.jointNames = names; + } + var props = modelHelper.getProperties(this.objectID, ['naturalDimensions']); + if (props && props.naturalDimensions && Vec3.length(props.naturalDimensions)) { + this.naturalDimensions = props.naturalDimensions; + } + var state = { + resource: this.resource, + objectID: this.objectID, + waitTime: (new Date - this._startTime) / 1000, + jointNames: this.jointNames, + naturalDimensions: this.naturalDimensions + }; + if (this.precondition(state)) { + result = state; + } + } + if (error || result !== null) { + this._stop(); + this.modelReady(error, result); + } + } +}; // ModelReadyWatcher.prototype + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// doppleganger.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module doppleganger +// +// This module contains the `Doppleganger` class implementation for creating an inspectable replica of +// an Avatar (as a model directly in front of and facing them). Joint positions and rotations are copied +// over in an update thread, so that the model automatically mirrors the Avatar's joint movements. +// An Avatar can then for example walk around "themselves" and examine from the back, etc. +// +// This should be helpful for inspecting your own look and debugging avatars, etc. +// +// The doppleganger is created as an overlay so that others do not see it -- and this also allows for the +// highest possible update rate when keeping joint data in sync. + +module.exports = Doppleganger; + +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +// @property {bool} - toggle verbose debug logging on/off +Doppleganger.WANT_DEBUG = false; + +// @property {bool} - when set true, Script.update will be used instead of setInterval for syncing joint data +Doppleganger.USE_SCRIPT_UPDATE = false; + +// @property {int} - the frame rate to target when using setInterval for joint updates +Doppleganger.TARGET_FPS = 60; + +// @class Doppleganger - Creates a new instance of a Doppleganger. +// @param {Avatar} [options.avatar=MyAvatar] - Avatar used to retrieve position and joint data. +// @param {bool} [options.mirrored=true] - Apply "symmetric mirroring" of Left/Right joints. +// @param {bool} [options.autoUpdate=true] - Automatically sync joint data. +function Doppleganger(options) { + this.options = options = options || {}; + this.avatar = options.avatar || MyAvatar; + this.mirrored = 'mirrored' in options ? options.mirrored : true; + this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; + + // @public + this.active = false; // whether doppleganger is currently being displayed/updated + this.objectID = null; // current doppleganger's Overlay or Entity id + this.frame = 0; // current joint update frame + + // @signal - emitted when .active state changes + this.activeChanged = utils.signal(function(active, reason) {}); + // @signal - emitted once model is either loaded or errors out + this.modelLoaded = utils.signal(function(error, result){}); + // @signal - emitted each time the model's joint data has been synchronized + this.jointsUpdated = utils.signal(function(objectID){}); +} + +Doppleganger.prototype = { + // @public @method - toggles doppleganger on/off + toggle: function() { + if (this.active) { + debugPrint('toggling off'); + this.stop(); + } else { + debugPrint('toggling on'); + this.start(); + } + return this.active; + }, + + // @public @method - synchronize the joint data between Avatar / doppleganger + update: function() { + this.frame++; + try { + if (!this.objectID) { + throw new Error('!this.objectID'); + } + + if (this.avatar.skeletonModelURL !== this.skeletonModelURL) { + return this.stop('avatar_changed'); + } + + var rotations = this.avatar.getJointRotations(); + var translations = this.avatar.getJointTranslations(); + var size = rotations.length; + + // note: this mismatch can happen when the avatar's model is actively changing + if (size !== translations.length || + (this.jointStateCount && size !== this.jointStateCount)) { + debugPrint('mismatched joint counts (avatar model likely changed)', size, translations.length, this.jointStateCount); + this.stop('avatar_changed_joints'); + return; + } + this.jointStateCount = size; + + if (this.mirrored) { + var mirroredIndexes = this.mirroredIndexes; + var outRotations = new Array(size); + var outTranslations = new Array(size); + for (var i=0; i < size; i++) { + var index = mirroredIndexes[i]; + if (index < 0 || index === false) { + index = i; + } + var rot = rotations[index]; + var trans = translations[index]; + trans.x *= -1; + rot.y *= -1; + rot.z *= -1; + outRotations[i] = rot; + outTranslations[i] = trans; + } + rotations = outRotations; + translations = outTranslations; + } + var jointUpdates = { + jointRotations: rotations, + jointTranslations: translations + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); + } catch (e) { + log('.update error: '+ e, index, e.stack); + this.stop('update_error'); + throw e; + } + }, + + // @public @method - show the doppleganger (and start the update thread, if options.autoUpdate was specified). + // @param {vec3} [options.position=(in front of avatar)] - starting position + // @param {quat} [options.orientation=avatar.orientation] - starting orientation + start: function(options) { + options = utils.assign(this.options, options); + if (this.objectID) { + log('start() called but object model already exists', this.objectID); + return; + } + var avatar = this.avatar; + if (!avatar.jointNames.length) { + return this.stop('joints_unavailable'); + } + + this.frame = 0; + var localPosition = Vec3.multiply(2, Quat.getForward(avatar.orientation)); + this.position = options.position || Vec3.sum(avatar.position, localPosition); + this.orientation = options.orientation || avatar.orientation; + this.skeletonModelURL = avatar.skeletonModelURL; + this.scale = avatar.scale || 1.0; + this.jointStateCount = 0; + this.jointNames = avatar.jointNames; + this.type = options.type || 'overlay'; + this.mirroredNames = modelHelper.deriveMirroredJointNames(this.jointNames); + this.mirroredIndexes = this.mirroredNames.map(function(name) { + return name ? avatar.getJointIndex(name) : false; + }); + + this.objectID = modelHelper.addObject({ + type: this.type === 'overlay' ? 'model' : 'Model', + modelURL: this.skeletonModelURL, + position: this.position, + rotation: this.orientation, + scale: this.scale + }); + Script.scriptEnding.connect(this, function() { + modelHelper.deleteObject(this.objectID); + }); + debugPrint('doppleganger created; objectID =', this.objectID); + + // trigger clean up (and stop updates) if the object gets deleted + this.onObjectDeleted = function(uuid) { + if (uuid === this.objectID) { + log('onObjectDeleted', uuid); + this.stop('object_deleted'); + } + }; + modelHelper.objectDeleted.connect(this, 'onObjectDeleted'); + + if ('onLoadComplete' in avatar) { + // stop the current doppleganger if Avatar loads a different model URL + this.onLoadComplete = function() { + if (avatar.skeletonModelURL !== this.skeletonModelURL) { + this.stop('avatar_changed_load'); + } + }; + avatar.onLoadComplete.connect(this, 'onLoadComplete'); + } + + this.onModelLoaded = function(error, result) { + if (error) { + return this.stop(error); + } + debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); + var props = { visible: true }; + if (naturalDimensions) { + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); + } + debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); + modelHelper.editObject(this.objectID, props); + if (!options.position) { + this.syncVerticalPosition(); + } + if (this.autoUpdate) { + this._createUpdateThread(); + } + }; + + this._resource = ModelCache.prefetch(this.skeletonModelURL); + this._modelReadier = new ModelReadyWatcher({ + resource: this._resource, + objectID: this.objectID + }); + this._modelReadier.modelReady.connect(this, 'onModelLoaded'); + this.activeChanged(this.active = true, 'start'); + }, + + // @public @method - hide the doppleganger + // @param {String} [reason=stop] - the reason stop was called + stop: function(reason) { + reason = reason || 'stop'; + if (this.onUpdate) { + Script.update.disconnect(this, 'onUpdate'); + delete this.onUpdate; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = undefined; + } + if (this.onObjectDeleted) { + modelHelper.objectDeleted.disconnect(this, 'onObjectDeleted'); + delete this.onObjectDeleted; + } + if (this.onLoadComplete) { + this.avatar.onLoadComplete.disconnect(this, 'onLoadComplete'); + delete this.onLoadComplete; + } + if (this.onModelLoaded) { + this._modelReadier && this._modelReadier.modelReady.disconnect(this, 'onModelLoaded'); + this._modelReadier = this.onModelLoaded = undefined; + } + if (this.objectID) { + modelHelper.deleteObject(this.objectID); + this.objectID = undefined; + } + if (this.active) { + this.activeChanged(this.active = false, reason); + } else if (reason) { + debugPrint('already stopped so not triggering another activeChanged; latest reason was:', reason); + } + }, + // @public @method - Reposition the doppleganger so it sees "eye to eye" with the Avatar. + // @param {String} [byJointName=Hips] - the reference joint used to align the Doppleganger and Avatar + syncVerticalPosition: function(byJointName) { + byJointName = byJointName || 'Hips'; + var positions = modelHelper.getJointPositions(this.objectID), + properties = modelHelper.getProperties(this.objectID), + dopplePosition = properties.position, + doppleJointIndex = modelHelper.getJointIndex(this.objectID, byJointName),// names.indexOf(byJointName), + doppleJointPosition = positions[doppleJointIndex]; + + debugPrint('........... doppleJointPosition', JSON.stringify({ + byJointName: byJointName, + dopplePosition: dopplePosition, + doppleJointIndex: doppleJointIndex, + doppleJointPosition: doppleJointPosition, + properties: properties.type, + positions: positions[0] + },0,2)); + var avatarPosition = this.avatar.position, + avatarJointIndex = this.avatar.getJointIndex(byJointName), + avatarJointPosition = this.avatar.getJointPosition(avatarJointIndex); + + var offset = (avatarJointPosition.y - doppleJointPosition.y); + debugPrint('adjusting for offset', offset); + if (properties.type === 'model') { + dopplePosition.y = avatarPosition.y + offset; + } else { + dopplePosition.y = avatarPosition.y - offset; + } + + this.position = dopplePosition; + modelHelper.editObject(this.objectID, { position: this.position }); + }, + + // @private @method - creates the update thread to synchronize joint data + _createUpdateThread: function() { + if (Doppleganger.USE_SCRIPT_UPDATE) { + debugPrint('creating Script.update thread'); + this.onUpdate = this.update; + Script.update.connect(this, 'onUpdate'); + } else { + debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); + var timeout = 1000 / Doppleganger.TARGET_FPS; + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); + } + } + +}; + +// @function - debug logging +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + Doppleganger.WANT_DEBUG && log.apply(this, arguments); +} + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +// doppleganger-app.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// This Client script creates an instance of a Doppleganger that can be toggled on/off via tablet button. +// (for more info see doppleganger.js) +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ + +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (false) { + require = Script.require; +} + +var VERSION = '0.0.0'; +var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); + +var DopplegangerClass = __webpack_require__(2), + DopplegangerAttachments = __webpack_require__(4), + DebugControls = __webpack_require__(5), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: __webpack_require__(6), + activeIcon: __webpack_require__(7), +}); + +var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), + button = tablet.addButton(buttonConfig); + +Script.scriptEnding.connect(function() { + tablet.removeButton(button); + button = null; +}); + +var doppleganger = new DopplegangerClass({ + avatar: MyAvatar, + mirrored: false, + autoUpdate: true, + type: 'overlay' +}); + +// add support for displaying regular (non-soft) attachments on the doppleganger +{ + var RECHECK_ATTACHMENT_MS = 1000; + var dopplegangerAttachments = new DopplegangerAttachments(doppleganger), + attachmentInterval = null, + lastHash = dopplegangerAttachments.getAttachmentsHash(); + + // monitor for attachment changes, but only when the doppleganger is active + doppleganger.activeChanged.connect(function(active, reason) { + if (attachmentInterval) { + Script.clearInterval(attachmentInterval); + } + if (active) { + attachmentInterval = Script.setInterval(checkAttachmentsHash, RECHECK_ATTACHMENT_MS); + } else { + attachmentInterval = null; + } + }); + function checkAttachmentsHash() { + var currentHash = dopplegangerAttachments.getAttachmentsHash(); + if (currentHash !== lastHash) { + lastHash = currentHash; + debugPrint('app-doppleganger | detect attachment change'); + dopplegangerAttachments.refreshAttachments(); + } + } +} + +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + +// hide the doppleganger if this client script is unloaded +Script.scriptEnding.connect(doppleganger, 'stop'); + +// hide the doppleganger if the user switches domains (which might place them arbitrarily far away in world space) +function onDomainChanged() { + if (doppleganger.active) { + doppleganger.stop('domain_changed'); + } +} +Window.domainChanged.connect(onDomainChanged); +Window.domainConnectionRefused.connect(onDomainChanged); +Script.scriptEnding.connect(function() { + Window.domainChanged.disconnect(onDomainChanged); + Window.domainConnectionRefused.disconnect(onDomainChanged); +}); + +// toggle on/off via tablet button +button.clicked.connect(doppleganger, 'toggle'); + +// highlight tablet button based on current doppleganger state +doppleganger.activeChanged.connect(function(active, reason) { + if (button) { + button.editProperties({ isActive: active }); + debugPrint('doppleganger.activeChanged', active, reason); + } +}); + +// alert the user if there was an error applying their skeletonModelURL +doppleganger.modelLoaded.connect(function(error, result) { + if (doppleganger.active && error) { + Window.alert('doppleganger | ' + error + '\n' + doppleganger.skeletonModelURL); + } +}); + +// ---------------------------------------------------------------------------- + +// add debug indicators, but only if the user has configured the settings value +if (Settings.getValue('debug.doppleganger', false)) { + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + WANT_DEBUG && log.apply(this, arguments); +} + +// ---------------------------------------------------------------------------- + +UserActivityLogger.logAction('doppleganger_app_load'); +doppleganger.activeChanged.connect(function(active, reason) { + if (active) { + UserActivityLogger.logAction('doppleganger_enable'); + } else { + if (reason === 'stop') { + // user intentionally toggled the doppleganger + UserActivityLogger.logAction('doppleganger_disable'); + } else { + debugPrint('doppleganger stopped:', reason); + UserActivityLogger.logAction('doppleganger_autodisable', { reason: reason }); + } + } +}); +dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { + UserActivityLogger.logAction('doppleganger_attachments', { count: attachments.length }); +}); + + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } +module.exports = DopplegangerAttachments; + +DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.WANT_DEBUG = false; + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); +} +log(DopplegangerAttachments.version); + +function debugPrint() { + DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); +} + +function DopplegangerAttachments(doppleganger, options) { + utils.assign(this, { + _options: options, + doppleganger: doppleganger, + attachments: undefined, + manualJointSync: true, + attachmentsUpdated: utils.signal(function attachmentsUpdated(currentAttachments, previousAttachments){}), + }); + this._initialize(); + debugPrint('DopplegangerAttachments...', JSON.stringify(options)); +} +DopplegangerAttachments.prototype = { + // "hash" the current attachments (so that changes can be detected) + getAttachmentsHash: function() { + return JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant()); + }, + // create a pure Object copy of the current native attachments variant + _cloneAttachmentsVariant: function() { + return JSON.parse(JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant())); + }, + // fetch and resolve attachments to include jointIndex and other relevant $metadata + _getResolvedAttachments: function() { + var attachments = this._cloneAttachmentsVariant(), + objectID = this.doppleganger.objectID; + function toString() { + return '[attachment #' + this.$index + ' ' + this.$path + ' @ ' + this.jointName + '{' + this.$jointIndex + '}]'; + } + return attachments.map(function(attachment, i) { + var jointIndex = modelHelper.getJointIndex(objectID, attachment.jointName), + path = (attachment.modelUrl+'').split(/[?#]/)[0].split('/').slice(-3).join('/'); + return Object.defineProperties(attachment, { + $hash: { value: JSON.stringify(attachment) }, + $index: { value: i }, + $jointIndex: { value: jointIndex }, + $path: { value: path }, + toString: { value: toString }, + }); + }); + }, + // compare before / after attachment sets to determine which ones need to be (re)created + refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } + var before = this.attachments || [], + beforeIndex = before.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + var after = this._getResolvedAttachments(), + afterIndex = after.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + + Object.keys(beforeIndex).concat(Object.keys(afterIndex)).forEach(function(hash) { + if (hash in beforeIndex && hash in afterIndex) { + // print('reusing previous attachment', hash); + after[afterIndex[hash]] = before[beforeIndex[hash]]; + } else if (!(hash in afterIndex)) { + var attachment = before[beforeIndex[hash]]; + attachment.properties && attachment.properties.objectID && + modelHelper.deleteObject(attachment.properties.objectID); + delete attachment.properties; + } + }); + this.attachments = after; + this._createAttachmentObjects(); + this.attachmentsUpdated(after, before); + }, + _createAttachmentObjects: function() { + try { + var attachments = this.attachments, + parentID = this.doppleganger.objectID, + jointNames = this.doppleganger.jointNames, + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); + debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ + modelType: modelType, + attachments: attachments.length, + parentID: parentID, + jointNames: jointNames.join(' | '), + },0,2)); + return attachments.map(utils.bind(this, function(attachment, i) { + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); + return attachment; + } + var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), + scale = this.doppleganger.avatar.scale * (attachment.scale||1.0); + + attachment.properties = utils.assign({ + name: attachment.toString(), + type: modelType, + modelURL: attachment.modelUrl, + scale: scale, + dimensions: modelHelper.type(parentID) === 'entity' ? + Vec3.multiply(attachment.scale||1.0, Vec3.ONE) : undefined, + visible: false, + collisionless: true, + dynamic: false, + shapeType: 'none', + lifetime: 60, + grabbable: true, + }, !this.manualJointSync && { + parentID: parentID, + parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), + }); + var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); + attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); + attachment._modelReadier = new ModelReadyWatcher({ + resource: attachment._resource, + objectID: objectID, + }); + this.doppleganger.activeChanged.connect(attachment._modelReadier, '_stop'); + + attachment._modelReadier.modelReady.connect(this, function(err, result) { + if (err) { + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); + modelHelper.deleteObject(objectID); + return objectID = null; + } + debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); + var properties = modelHelper.getProperties(result.objectID), + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { + dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, + }); + this.onJointsUpdated(parentID); // trigger once to initialize position/rotation + // give time for model overlay to "settle", then make it visible + Script.setTimeout(utils.bind(this, function() { + modelHelper.editObject(objectID, { + visible: true, + }); + attachment._loaded = true; + }), 100); + }); + return attachment; + })); + } catch (e) { + log('_createAttachmentObjects ERROR:', e.stack || e, e.fileName, e.lineNumber); + } + }, + + _removeAttachmentObjects: function() { + if (this.attachments) { + this.attachments.forEach(function(attachment) { + if (attachment.properties) { + if (attachment.properties.objectID) { + modelHelper.deleteObject(attachment.properties.objectID); + } + delete attachment.properties.objectID; + } + }); + delete this.attachments; + } + }, + + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { + var jointOrientations = modelHelper.getJointOrientations(objectID), + jointPositions = modelHelper.getJointPositions(objectID), + parentID = objectID, + avatarScale = this.doppleganger.scale, + manualJointSync = this.manualJointSync; + + if (!this.attachments) { + this.refreshAttachments(); + } + var updatedObjects = this.attachments.reduce(function(updates, attachment, i) { + if (!attachment.properties || !attachment._loaded) { + return updates; + } + var objectID = attachment.properties.objectID, + jointIndex = attachment.$jointIndex, + jointOrientation = jointOrientations[jointIndex], + jointPosition = jointPositions[jointIndex]; + + var translation = Vec3.multiply(avatarScale, attachment.translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + localRotation = rotation; + + updates[objectID] = manualJointSync ? { + visible: true, + position: Vec3.sum(jointPosition, localPosition), + rotation: Quat.multiply(jointOrientation, localRotation), + scale: avatarScale * attachment.scale, + } : { + visible: true, + parentID: parentID, + parentJointIndex: jointIndex, + localRotation: localRotation, + localPosition: localPosition, + scale: attachment.scale, + }; + return updates; + }, {}); + modelHelper.editObjects(updatedObjects); + }, + + _initialize: function() { + var doppleganger = this.doppleganger; + if ('$attachmentControls' in doppleganger) { + throw new Error('only one set of attachment controls can be added per doppleganger'); + } + doppleganger.$attachmentControls = this; + doppleganger.activeChanged.connect(this, function(active) { + if (active) { + doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } else { + doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + this._removeAttachmentObjects(); + } + }); + + Script.scriptEnding.connect(this, '_removeAttachmentObjects'); + }, +}; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = __webpack_require__(2), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index a3b3873c2d..5dee264fae 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -1,10 +1,12 @@ "use strict"; /* eslint-env commonjs */ /* eslint-disable comma-dangle */ +/* global console */ +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.0'; +DopplegangerAttachments.version = '0.0.1a'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -13,8 +15,10 @@ var _modelHelper = require('./model-helper.js'), utils = require('./utils.js'); function log() { - print('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); } +log(DopplegangerAttachments.version); function debugPrint() { DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); @@ -61,6 +65,9 @@ DopplegangerAttachments.prototype = { }, // compare before / after attachment sets to determine which ones need to be (re)created refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } var before = this.attachments || [], beforeIndex = before.reduce(function(out, att, index) { out[att.$hash] = index; return out; @@ -90,18 +97,19 @@ DopplegangerAttachments.prototype = { var attachments = this.attachments, parentID = this.doppleganger.objectID, jointNames = this.doppleganger.jointNames, - properties = modelHelper.getProperties(this.doppleganger.objectID); - + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ - type: properties.type, + modelType: modelType, attachments: attachments.length, parentID: parentID, jointNames: jointNames.join(' | '), },0,2)); return attachments.map(utils.bind(this, function(attachment, i) { - var type = modelHelper.type(attachment.properties && attachment.properties.objectID); - if (type === 'overlay') { - debugPrint('skipping already-provisioned attachment object', type, attachment.properties && attachment.properties.name); + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); return attachment; } var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), @@ -109,7 +117,7 @@ DopplegangerAttachments.prototype = { attachment.properties = utils.assign({ name: attachment.toString(), - type: properties.type, + type: modelType, modelURL: attachment.modelUrl, scale: scale, dimensions: modelHelper.type(parentID) === 'entity' ? @@ -123,10 +131,13 @@ DopplegangerAttachments.prototype = { }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), }); var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); - attachment._modelReadier = new ModelReadyWatcher( { + attachment._modelReadier = new ModelReadyWatcher({ resource: attachment._resource, objectID: objectID, }); @@ -134,21 +145,23 @@ DopplegangerAttachments.prototype = { attachment._modelReadier.modelReady.connect(this, function(err, result) { if (err) { - log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.modelUrl); + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); modelHelper.deleteObject(objectID); return objectID = null; } debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', - result.jointNames && result.jointNames.length, result.naturalDimensions, result.objectID); + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); var properties = modelHelper.getProperties(result.objectID), - naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions; - modelHelper.editObject(result.objectID, { + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, }); this.onJointsUpdated(parentID); // trigger once to initialize position/rotation // give time for model overlay to "settle", then make it visible Script.setTimeout(utils.bind(this, function() { - modelHelper.editObject(result.objectID, { + modelHelper.editObject(objectID, { visible: true, }); attachment._loaded = true; @@ -175,7 +188,7 @@ DopplegangerAttachments.prototype = { } }, - onJointsUpdated: function onJointsUpdated(objectID) { + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { var jointOrientations = modelHelper.getJointOrientations(objectID), jointPositions = modelHelper.getJointPositions(objectID), parentID = objectID, @@ -195,8 +208,9 @@ DopplegangerAttachments.prototype = { jointPosition = jointPositions[jointIndex]; var translation = Vec3.multiply(avatarScale, attachment.translation), - rotation = Quat.fromVec3Degrees(attachment.rotation), - localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), localRotation = rotation; updates[objectID] = manualJointSync ? { @@ -212,7 +226,6 @@ DopplegangerAttachments.prototype = { localPosition: localPosition, scale: attachment.scale, }; - onJointsUpdated[objectID] = updates[objectID]; return updates; }, {}); modelHelper.editObjects(updatedObjects); @@ -221,7 +234,7 @@ DopplegangerAttachments.prototype = { _initialize: function() { var doppleganger = this.doppleganger; if ('$attachmentControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); + throw new Error('only one set of attachment controls can be added per doppleganger'); } doppleganger.$attachmentControls = this; doppleganger.activeChanged.connect(this, function(active) { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js new file mode 100644 index 0000000000..b0a83117fb --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js @@ -0,0 +1,158 @@ +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + +"use strict"; +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = require('./doppleganger.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js index bebd36df45..190a8aa69e 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js @@ -10,6 +10,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module doppleganger // // This module contains the `Doppleganger` class implementation for creating an inspectable replica of @@ -24,9 +25,13 @@ module.exports = Doppleganger; +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + var _modelHelper = require('./model-helper.js'), modelHelper = _modelHelper.modelHelper, - ModelReadyWatcher = _modelHelper.ModelReadyWatcher; + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = require('./utils.js'); // @property {bool} - toggle verbose debug logging on/off Doppleganger.WANT_DEBUG = false; @@ -48,16 +53,16 @@ function Doppleganger(options) { this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; // @public - this.active = false; // whether doppleganger is currently being displayed/updated + this.active = false; // whether doppleganger is currently being displayed/updated this.objectID = null; // current doppleganger's Overlay or Entity id - this.frame = 0; // current joint update frame + this.frame = 0; // current joint update frame // @signal - emitted when .active state changes - this.activeChanged = signal(function(active, reason) {}); + this.activeChanged = utils.signal(function(active, reason) {}); // @signal - emitted once model is either loaded or errors out - this.modelLoaded = signal(function(error, result){}); + this.modelLoaded = utils.signal(function(error, result){}); // @signal - emitted each time the model's joint data has been synchronized - this.jointsUpdated = signal(function(objectID){}); + this.jointsUpdated = utils.signal(function(objectID){}); } Doppleganger.prototype = { @@ -118,11 +123,12 @@ Doppleganger.prototype = { rotations = outRotations; translations = outTranslations; } - modelHelper.editObject(this.objectID, { + var jointUpdates = { jointRotations: rotations, jointTranslations: translations - }); - this.jointsUpdated(this.objectID); + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); } catch (e) { log('.update error: '+ e, index, e.stack); this.stop('update_error'); @@ -134,7 +140,7 @@ Doppleganger.prototype = { // @param {vec3} [options.position=(in front of avatar)] - starting position // @param {quat} [options.orientation=avatar.orientation] - starting orientation start: function(options) { - options = assign(this.options, options); + options = utils.assign(this.options, options); if (this.objectID) { log('start() called but object model already exists', this.objectID); return; @@ -194,11 +200,11 @@ Doppleganger.prototype = { return this.stop(error); } debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); - var naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); var props = { visible: true }; if (naturalDimensions) { - props.dimensions = Vec3.multiply(this.scale, naturalDimensions); + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); } debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); modelHelper.editObject(this.objectID, props); @@ -296,220 +302,18 @@ Doppleganger.prototype = { } else { debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); var timeout = 1000 / Doppleganger.TARGET_FPS; - this._interval = Script.setInterval(bind(this, 'update'), timeout); + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); } } }; -// @function - bind a function to a `this` context -// @param {Object} - the `this` context -// @param {Function|String} - function or method name -function bind(thiz, method) { - method = thiz[method] || method; - return function() { - return method.apply(thiz, arguments); - }; -} - -// @function - Qt signal polyfill -function signal(template) { - var callbacks = []; - return Object.defineProperties(function() { - var args = [].slice.call(arguments); - callbacks.forEach(function(obj) { - obj.handler.apply(obj.scope, args); - }); - }, { - connect: { value: function(scope, handler) { - var callback = {scope: scope, handler: scope[handler] || handler || scope}; - if (!callback.handler || !callback.handler.apply) { - throw new Error('invalid arguments to connect:' + [template, scope, handler]); - } - callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); - }}, - disconnect: { value: function(scope, handler) { - var match = {scope: scope, handler: scope[handler] || handler || scope}; - callbacks = callbacks.filter(function(obj) { - return !(obj.scope === match.scope && obj.handler === match.handler); - }); - }} - }); -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill -/* eslint-disable */ -function assign(target, varArgs) { // .length of function is 2 - 'use strict'; - if (target == null) { // TypeError if undefined or null - throw new TypeError('Cannot convert undefined or null to object'); - } - - var to = Object(target); - - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - - if (nextSource != null) { // Skip over if undefined or null - for (var nextKey in nextSource) { - // Avoid bugs when hasOwnProperty is shadowed - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; -} -/* eslint-enable */ -// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill - // @function - debug logging function log() { - print('doppleganger | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { Doppleganger.WANT_DEBUG && log.apply(this, arguments); } - -// -- ADVANCED DEBUGGING -- -// @function - Add debug joint indicators / extra debugging info. -// @param {Doppleganger} - existing Doppleganger instance to add controls to -// -// @note: -// * rightclick toggles mirror mode on/off -// * shift-rightclick toggles the debug indicators on/off -// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. -// -// Example use: -// var doppleganger = new Doppleganger(); -// Doppleganger.addDebugControls(doppleganger); -Doppleganger.addDebugControls = function(doppleganger) { - DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; - DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; - - function DebugControls() { - this.enableIndicators = true; - this.selectedJointName = null; - this.debugOverlayIDs = undefined; - this.jointSelected = signal(function(result) {}); - } - DebugControls.prototype = { - start: function() { - if (!this.onMousePressEvent) { - this.onMousePressEvent = this._onMousePressEvent; - Controller.mousePressEvent.connect(this, 'onMousePressEvent'); - } - }, - - stop: function() { - this.removeIndicators(); - if (this.onMousePressEvent) { - Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); - delete this.onMousePressEvent; - } - }, - - createIndicators: function(jointNames) { - this.jointNames = jointNames; - return jointNames.map(function(name, i) { - return Overlays.addOverlay('shape', { - shape: 'Icosahedron', - scale: 0.1, - solid: false, - alpha: 0.5 - }); - }); - }, - - removeIndicators: function() { - if (this.debugOverlayIDs) { - this.debugOverlayIDs.forEach(Overlays.deleteOverlay); - this.debugOverlayIDs = undefined; - } - }, - - onJointsUpdated: function(overlayID) { - if (!this.enableIndicators) { - return; - } - var jointNames = Overlays.getProperty(overlayID, 'jointNames'), - jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), - jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), - selectedIndex = jointNames.indexOf(this.selectedJointName); - - if (!this.debugOverlayIDs) { - this.debugOverlayIDs = this.createIndicators(jointNames); - } - - // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) - var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { - updates[id] = { - position: jointPositions[i], - rotation: jointOrientations[i], - color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, - solid: i === selectedIndex - }; - return updates; - }, {}); - Overlays.editOverlays(updatedOverlays); - }, - - _onMousePressEvent: function(evt) { - if (!evt.isLeftButton || !this.enableIndicators || !this.debugOverlayIDs) { - return; - } - var ray = Camera.computePickRay(evt.x, evt.y), - hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); - - hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); - hit.jointName = this.jointNames[hit.jointIndex]; - this.jointSelected(hit); - } - }; - - if ('$debugControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); - } - var debugControls = new DebugControls(); - doppleganger.$debugControls = debugControls; - - function onMousePressEvent(evt) { - if (evt.isRightButton) { - if (evt.isShifted) { - debugControls.enableIndicators = !debugControls.enableIndicators; - if (!debugControls.enableIndicators) { - debugControls.removeIndicators(); - } - } else { - doppleganger.mirrored = !doppleganger.mirrored; - } - } - } - - doppleganger.activeChanged.connect(function(active) { - if (active) { - debugControls.start(); - doppleganger.jointsUpdated.connect(debugControls, 'onJointsUpdated'); - Controller.mousePressEvent.connect(onMousePressEvent); - } else { - Controller.mousePressEvent.disconnect(onMousePressEvent); - doppleganger.jointsUpdated.disconnect(debugControls, 'onJointsUpdated'); - debugControls.stop(); - } - }); - - debugControls.jointSelected.connect(function(hit) { - debugControls.selectedJointName = hit.jointName; - if (hit.jointIndex < 0) { - return; - } - hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; - log('selected joint:', JSON.stringify(hit, 0, 2)); - }); - - Script.scriptEnding.connect(debugControls, 'removeIndicators'); - - return doppleganger; -}; diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/makefile b/unpublishedScripts/marketplace/doppleganger-attachments/makefile new file mode 100644 index 0000000000..eaf02dbed9 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/makefile @@ -0,0 +1,11 @@ +all: + @echo "make dist" + +dist: doppleganger-a.svg.json doppleganger-i.svg.json dist/app-doppleganger-marketplace.js + @echo "OK" + +%.svg.json: %.svg + cat $< | jq -sR '"data:image/svg+xml;xml,"+.' > $@ + +dist/app-doppleganger-marketplace.js: *.js + ./node_modules/.bin/webpack --verbose app-doppleganger-attachments.js $@ diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js index 2dda2c12ec..8edaf50cb7 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js @@ -8,6 +8,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module model-helper // // This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and @@ -18,10 +19,16 @@ var utils = require('./utils.js'), assert = utils.assert; module.exports = { - version: '0.0.0', + version: '0.0.1', ModelReadyWatcher: ModelReadyWatcher }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + var _objectDeleted = utils.signal(function objectDeleted(objectID){}); // proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal var objectDeleted = utils.assign(function objectDeleted(objectID){}, { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/package.json b/unpublishedScripts/marketplace/doppleganger-attachments/package.json new file mode 100644 index 0000000000..f18136fc1b --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "webpack": "^3.0.0" + } +} diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/readme.md b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md new file mode 100644 index 0000000000..d0ebf2d6e1 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md @@ -0,0 +1,4 @@ +note: to rebuild webpack version: +* install `jq` https://stedolan.github.io/jq (used to encode the icon.svg's as Data URI JSON strings) +* `npm install` +* `make dist` diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js index 76c6e1ef7f..b34af1e632 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js @@ -1,12 +1,20 @@ /* eslint-env commonjs */ +/* global console */ module.exports = { + version: '0.0.1', bind: bind, signal: signal, assign: assign, assert: assert }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + // @function - bind a function to a `this` context // @param {Object} - the `this` context // @param {Function|String} - function or method name From 96f52a9812bd77d3931c46c03a91883a73002147 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:05:16 -0700 Subject: [PATCH 02/85] First pass at getting overlays to show up on hover --- interface/src/Application.cpp | 2 +- .../src/ui/overlays/HoverOverlayInterface.cpp | 72 +++++++++++++++++++ .../src/ui/overlays}/HoverOverlayInterface.h | 17 ++++- .../src/ui/overlays}/HoverOverlayLogging.cpp | 2 +- .../src/ui/overlays}/HoverOverlayLogging.h | 2 +- .../src/EntityTreeRenderer.cpp | 7 -- .../entities/src/HoverOverlayInterface.cpp | 30 -------- 7 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayInterface.h (67%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.cpp (92%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.h (93%) delete mode 100644 libraries/entities/src/HoverOverlayInterface.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c73d6614f1..bc9fa25af0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include +#include "ui/overlays/HoverOverlayInterface.h" #include #include #include diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp new file mode 100644 index 0000000000..8576c0b622 --- /dev/null +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -0,0 +1,72 @@ +// +// HoverOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HoverOverlayInterface.h" +#include "Application.h" +#include + +HoverOverlayInterface::HoverOverlayInterface() { + // "hover_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { + _hoverOverlay = std::make_shared(); + _hoverOverlay->setAlpha(1.0f); + _hoverOverlay->setBorderSize(1.0f); + _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); + _hoverOverlay->setIsSolid(true); + _hoverOverlay->setPulseMin(0.5); + _hoverOverlay->setPulseMax(1.0); + _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setIgnoreRayIntersection(false); + _hoverOverlay->setDrawInFront(false); + _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); + } + + _hoverOverlay->setPosition(entityProperties.getPosition()); + _hoverOverlay->setRotation(entityProperties.getRotation()); + _hoverOverlay->setVisible(true); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(QUuid()); + + qApp->getOverlays().deleteOverlay(_hoverOverlayID); + _hoverOverlay = NULL; + _hoverOverlayID = UNKNOWN_OVERLAY_ID; +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); +} diff --git a/libraries/entities/src/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h similarity index 67% rename from libraries/entities/src/HoverOverlayInterface.h rename to interface/src/ui/overlays/HoverOverlayInterface.h index 1e56cc03dd..8322c3e75c 100644 --- a/libraries/entities/src/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -1,6 +1,6 @@ // // HoverOverlayInterface.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-14. // Copyright 2017 High Fidelity, Inc. @@ -18,14 +18,24 @@ #include #include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Overlays.h" #include "EntityTree.h" #include "HoverOverlayLogging.h" +/**jsdoc +* @namespace HoverOverlay +*/ class HoverOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); @@ -34,11 +44,14 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyHoverOverlay(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; - QUuid _currentHoveredEntity{}; + QUuid _currentHoveredEntity {}; + }; #endif // hifi_HoverOverlayInterface_h diff --git a/libraries/entities/src/HoverOverlayLogging.cpp b/interface/src/ui/overlays/HoverOverlayLogging.cpp similarity index 92% rename from libraries/entities/src/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/HoverOverlayLogging.cpp index 99a2dff782..69c977a58c 100644 --- a/libraries/entities/src/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/HoverOverlayLogging.cpp @@ -1,6 +1,6 @@ // // HoverOverlayLogging.cpp -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities/src/HoverOverlayLogging.h b/interface/src/ui/overlays/HoverOverlayLogging.h similarity index 93% rename from libraries/entities/src/HoverOverlayLogging.h rename to interface/src/ui/overlays/HoverOverlayLogging.h index f0a024bba9..965a1c6d03 100644 --- a/libraries/entities/src/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/HoverOverlayLogging.h @@ -1,6 +1,6 @@ // // HoverOverlayLogging.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 475471b0fb..a8eca41077 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include "RenderableEntityItem.h" @@ -453,8 +452,6 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) { - auto hoverOverlayInterface = DependencyManager::get().data(); - connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); @@ -464,12 +461,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity); connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); - connect(this, &EntityTreeRenderer::hoverEnterEntity, hoverOverlayInterface, &HoverOverlayInterface::createHoverOverlay); - connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, hoverOverlayInterface, &HoverOverlayInterface::destroyHoverOverlay); connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); diff --git a/libraries/entities/src/HoverOverlayInterface.cpp b/libraries/entities/src/HoverOverlayInterface.cpp deleted file mode 100644 index 85b2738fc4..0000000000 --- a/libraries/entities/src/HoverOverlayInterface.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// HoverOverlayInterface.cpp -// libraries/entities/src -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); -} From 67c8ddfd57c0861af35209f5269395f3405b700a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:09:40 -0700 Subject: [PATCH 03/85] Merge in changes from laser PR --- interface/src/Application.cpp | 2 ++ scripts/system/controllers/handControllerGrab.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc9fa25af0..327643df1d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2124,6 +2124,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5826,6 +5827,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); + scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 04921fe14d..78c4b2960e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var entitiesWithHoverOverlays = []; + var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2201,6 +2203,15 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID); + entitiesWithHoverOverlays.push(rayPickInfo.entityID); + } + var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); @@ -3763,6 +3774,11 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 2b1a0921da5d0961c446064cd7ecf6d16059a2b0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:01:16 -0700 Subject: [PATCH 04/85] Initial work on clickable overlays --- interface/src/Application.cpp | 9 ++++- .../src/ui/overlays/HoverOverlayInterface.cpp | 10 +++-- .../src/ui/overlays/HoverOverlayInterface.h | 2 +- interface/src/ui/overlays/Overlays.cpp | 12 +++--- interface/src/ui/overlays/Overlays.h | 12 +++--- .../system/controllers/handControllerGrab.js | 39 +++++++++++++------ 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 327643df1d..b9a76ef58c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1323,12 +1323,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Keyboard focus handling for Web overlays. auto overlays = &(qApp->getOverlays()); - connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) { + connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); }); - connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) { + connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { if (overlayID == _keyboardFocusedOverlay.get()) { setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); } @@ -1343,6 +1343,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); + connect(overlays, + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 8576c0b622..655f167074 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -54,10 +54,6 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, _hoverOverlay->setVisible(true); } -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); -} - void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; setCurrentHoveredEntity(QUuid()); @@ -70,3 +66,9 @@ void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); } + +void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _hoverOverlayID) { + qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 8322c3e75c..7cda22a0c1 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -44,9 +44,9 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyHoverOverlay(const EntityItemID& entityItemID); + void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 72682fcb8c..b3ab4599a5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -705,27 +705,27 @@ bool Overlays::isAddedOverlay(OverlayID id) { return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); } -void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mousePressOnOverlay(overlayID, event); } -void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseReleaseOnOverlay(overlayID, event); } -void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseMoveOnOverlay(overlayID, event); } -void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverEnterOverlay(id, event); } -void Overlays::sendHoverOverOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverOverOverlay(id, event); } -void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverLeaveOverlay(id, event); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 100f853a96..3d9ac93c81 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -288,13 +288,13 @@ public slots: #endif - void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); + void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); - void sendHoverEnterOverlay(OverlayID id, PointerEvent event); - void sendHoverOverOverlay(OverlayID id, PointerEvent event); - void sendHoverLeaveOverlay(OverlayID id, PointerEvent event); + void sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event); OverlayID getKeyboardFocusOverlay(); void setKeyboardFocusOverlay(OverlayID id); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 78c4b2960e..dc9d2eb521 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entitiesWithHoverOverlays = []; +var entityWithHoverOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -1391,6 +1391,11 @@ function MyController(hand) { color: color, endParentID: farParentID }); + + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2203,13 +2208,21 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID); - entitiesWithHoverOverlays.push(rayPickInfo.entityID); + if (rayPickInfo.entityID) + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + } + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); + entityWithHoverOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3484,6 +3497,8 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); + //HoverOverlay + this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3774,10 +3789,10 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } if (this.grabbedThingID !== null) { From 084f4d2ab023a20b1dc0827122fb99696e7ed360 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:42:59 -0700 Subject: [PATCH 05/85] Why were Web3DOverlays done this way? --- interface/src/Application.cpp | 8 +++++-- interface/src/ui/overlays/Overlays.cpp | 31 +++++++++++++------------- interface/src/ui/overlays/Overlays.h | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9a76ef58c..eacd9bf794 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1324,8 +1324,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { - setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - setKeyboardFocusOverlay(overlayID); + auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); + // Only Web overlays can have focus. + if (thisOverlay) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + setKeyboardFocusOverlay(overlayID); + } }); connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b3ab4599a5..862152bea9 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,7 +818,7 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { @@ -875,12 +875,13 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + PointerEvent pointerEvent; + if (thisWeb3DOverlay) { + pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); } + emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mousePressOffOverlay(); return false; @@ -895,9 +896,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -914,9 +915,9 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { if (rayPickResult.intersects) { // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Release); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } } @@ -935,14 +936,14 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { // Only Web overlays can have focus. auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); // If previously hovering over a different overlay then leave hover on that overlay. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } } @@ -962,7 +963,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3d9ac93c81..7235937077 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From 265f978a06ff26c1a6645eed3d205146843c5e4a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 15:40:10 -0700 Subject: [PATCH 06/85] It's working! --- .../src/ui/overlays/HoverOverlayInterface.cpp | 16 +++---- .../src/ui/overlays/HoverOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 44 +++++++------------ 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 655f167074..1ccbbafb1b 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -36,16 +36,16 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); + _hoverOverlay = std::make_shared(); _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setBorderSize(1.0f); - _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); - _hoverOverlay->setIsSolid(true); - _hoverOverlay->setPulseMin(0.5); - _hoverOverlay->setPulseMax(1.0); - _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setPulseMin(0.75f); + _hoverOverlay->setPulseMax(1.0f); + _hoverOverlay->setColorPulse(1.0f); _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(false); + _hoverOverlay->setDrawInFront(true); + _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _hoverOverlay->setIsFacingAvatar(true); + _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); } diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 7cda22a0c1..4ae14ffd1d 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -19,7 +19,7 @@ #include #include #include "EntityScriptingInterface.h" -#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" #include "EntityTree.h" @@ -35,7 +35,7 @@ class HoverOverlayInterface : public QObject, public Dependency { QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index dc9d2eb521..7ee9a33485 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1391,11 +1391,6 @@ function MyController(hand) { color: color, endParentID: farParentID }); - - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; - } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2208,14 +2203,14 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID) + if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { if (entityWithHoverOverlay) { HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); } var pointerEvent = { type: "Move", id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, @@ -2479,27 +2474,24 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if (Overlays.getProperty(overlay, "type") != "web3d") { - return false; - } - if (Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; - - pointerEvent = { - type: "Move", - id: HARDWARE_MOUSE_ID, - pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); } + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && @@ -3497,8 +3489,6 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); - //HoverOverlay - this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3595,7 +3585,7 @@ function MyController(hand) { }; this.overlayLaserTouchingEnter = function () { - // Test for intersection between controller laser and Web overlay plane. + // Test for intersection between controller laser and overlay plane. var controllerLocation = getControllerWorldLocation(this.handToController(), true); var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation); if (intersectInfo) { From 8f6af3a1ab3bc0ee5c8746aa45d44d0b29980fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:11:43 -0700 Subject: [PATCH 07/85] ContextOverlay --- interface/src/Application.cpp | 14 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 74 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 57 ++++++++++++++ ...yLogging.cpp => ContextOverlayLogging.cpp} | 6 +- ...erlayLogging.h => ContextOverlayLogging.h} | 10 +-- .../src/ui/overlays/HoverOverlayInterface.cpp | 74 ------------------- .../src/ui/overlays/HoverOverlayInterface.h | 57 -------------- .../system/controllers/handControllerGrab.js | 18 ++--- 8 files changed, 155 insertions(+), 155 deletions(-) create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.cpp create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.h rename interface/src/ui/overlays/{HoverOverlayLogging.cpp => ContextOverlayLogging.cpp} (67%) rename interface/src/ui/overlays/{HoverOverlayLogging.h => ContextOverlayLogging.h} (62%) delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eacd9bf794..8689d17032 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include "ui/overlays/HoverOverlayInterface.h" +#include "ui/overlays/ContextOverlayInterface.h" #include #include #include @@ -589,7 +589,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -1325,7 +1325,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); - // Only Web overlays can have focus. + // Only Web overlays can have keyboard focus. if (thisOverlay) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); @@ -1349,8 +1349,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; @@ -2133,7 +2133,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); - surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); + surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5836,7 +5836,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); - scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp new file mode 100644 index 0000000000..cb15c544c7 --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -0,0 +1,74 @@ +// +// ContextOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ContextOverlayInterface.h" +#include "Application.h" +#include + +ContextOverlayInterface::ContextOverlayInterface() { + // "context_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); +} + +void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +} + +void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _contextOverlayID) { + qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h new file mode 100644 index 0000000000..05215e3e5c --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -0,0 +1,57 @@ +// +// ContextOverlayInterface.h +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_ContextOverlayInterface_h +#define hifi_ContextOverlayInterface_h + +#include +#include + +#include +#include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Overlays.h" + +#include "EntityTree.h" +#include "ContextOverlayLogging.h" + +/**jsdoc +* @namespace ContextOverlay +*/ +class ContextOverlayInterface : public QObject, public Dependency { + Q_OBJECT + + Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _contextOverlay { nullptr }; +public: + ContextOverlayInterface(); + + Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } + void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + +public slots: + void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID); + void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + +private: + bool _verboseLogging { true }; + QUuid _currentEntityWithContextOverlay{}; + +}; + +#endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/HoverOverlayLogging.cpp b/interface/src/ui/overlays/ContextOverlayLogging.cpp similarity index 67% rename from interface/src/ui/overlays/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/ContextOverlayLogging.cpp index 69c977a58c..c2c3fb7734 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/ContextOverlayLogging.cpp @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.cpp +// ContextOverlayLogging.cpp // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -9,6 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "HoverOverlayLogging.h" +#include "ContextOverlayLogging.h" -Q_LOGGING_CATEGORY(hover_overlay, "hifi.hover_overlay") +Q_LOGGING_CATEGORY(context_overlay, "hifi.context_overlay") diff --git a/interface/src/ui/overlays/HoverOverlayLogging.h b/interface/src/ui/overlays/ContextOverlayLogging.h similarity index 62% rename from interface/src/ui/overlays/HoverOverlayLogging.h rename to interface/src/ui/overlays/ContextOverlayLogging.h index 965a1c6d03..182ebc1425 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/ContextOverlayLogging.h @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.h +// ContextOverlayLogging.h // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -10,11 +10,11 @@ // #pragma once -#ifndef hifi_HoverOverlayLogging_h -#define hifi_HoverOverlayLogging_h +#ifndef hifi_ContextOverlayLogging_h +#define hifi_ContextOverlayLogging_h #include -Q_DECLARE_LOGGING_CATEGORY(hover_overlay) +Q_DECLARE_LOGGING_CATEGORY(context_overlay) -#endif // hifi_HoverOverlayLogging_h +#endif // hifi_ContextOverlayLogging_h diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp deleted file mode 100644 index 1ccbbafb1b..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// HoverOverlayInterface.cpp -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" -#include "Application.h" -#include - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); - - _entityScriptingInterface = DependencyManager::get(); - _entityPropertyFlags += PROP_POSITION; - _entityPropertyFlags += PROP_ROTATION; - - auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); - - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - - if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); - _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setPulseMin(0.75f); - _hoverOverlay->setPulseMax(1.0f); - _hoverOverlay->setColorPulse(1.0f); - _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(true); - _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _hoverOverlay->setIsFacingAvatar(true); - _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); - } - - _hoverOverlay->setPosition(entityProperties.getPosition()); - _hoverOverlay->setRotation(entityProperties.getRotation()); - _hoverOverlay->setVisible(true); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); - - qApp->getOverlays().deleteOverlay(_hoverOverlayID); - _hoverOverlay = NULL; - _hoverOverlayID = UNKNOWN_OVERLAY_ID; -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); -} - -void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _hoverOverlayID) { - qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; - } -} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h deleted file mode 100644 index 4ae14ffd1d..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// HoverOverlayInterface.h -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once -#ifndef hifi_HoverOverlayInterface_h -#define hifi_HoverOverlayInterface_h - -#include -#include - -#include -#include -#include "EntityScriptingInterface.h" -#include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Overlays.h" - -#include "EntityTree.h" -#include "HoverOverlayLogging.h" - -/**jsdoc -* @namespace HoverOverlay -*/ -class HoverOverlayInterface : public QObject, public Dependency { - Q_OBJECT - - Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) - QSharedPointer _entityScriptingInterface; - EntityPropertyFlags _entityPropertyFlags; - OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; -public: - HoverOverlayInterface(); - - Q_INVOKABLE QUuid getCurrentHoveredEntity() { return _currentHoveredEntity; } - void setCurrentHoveredEntity(const QUuid& entityID) { _currentHoveredEntity = entityID; } - -public slots: - void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID); - void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); - -private: - bool _verboseLogging { true }; - QUuid _currentHoveredEntity {}; - -}; - -#endif // hifi_HoverOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 7ee9a33485..1ab49d223e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entityWithHoverOverlay = false; +var entityWithContextOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2203,9 +2203,9 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); } var pointerEvent = { type: "Move", @@ -2216,8 +2216,8 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "None" }; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); - entityWithHoverOverlay = rayPickInfo.entityID; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + entityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3779,9 +3779,9 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } if (this.grabbedThingID !== null) { From 82111a8f6afc83f3347494a863ff214bc5d0a58a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:40:18 -0700 Subject: [PATCH 08/85] Timer for hand controller lasers --- .../system/controllers/handControllerGrab.js | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1ab49d223e..2b84c3d507 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -224,6 +224,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", enterMethod: "searchEnter", + exitMethod: "searchExit", updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { @@ -2174,6 +2175,13 @@ function MyController(hand) { } }; + this.searchExit = function () { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + }; + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -2206,17 +2214,22 @@ function MyController(hand) { if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + Script.setTimeout(function() { + if (rayPickInfo.entityID === entityWithContextOverlay) { + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + } + }, 500); entityWithContextOverlay = rayPickInfo.entityID; } @@ -3779,11 +3792,6 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } - if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 761d35d6335fa87468a09f2e6dec24500a91fb6c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 17:10:23 -0700 Subject: [PATCH 09/85] Getting there! --- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 +- .../system/controllers/handControllerGrab.js | 8 +-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cb15c544c7..33d492bc7e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -25,33 +25,36 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } -void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); +void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + if (event.getButton() == PointerEvent::SecondaryButton) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); + } else { + destroyContextOverlay(entityItemID, event); } - - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { @@ -68,7 +71,8 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 05215e3e5c..01129ee4f5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -43,7 +43,7 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } public slots: - void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2b84c3d507..8cd71131ce 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -370,7 +370,9 @@ function projectOntoOverlayXYPlane(overlayID, worldPos) { dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); - dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + if (dimensions.z) { + dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + } } return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); @@ -2225,9 +2227,9 @@ function MyController(hand) { pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, - button: "None" + button: "Secondary" }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); entityWithContextOverlay = rayPickInfo.entityID; From f06ec715f23f6b54531940bce3dcbd1feb0eb1a7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 09:59:44 -0700 Subject: [PATCH 10/85] Quick bugfix --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 33d492bc7e..9182346352 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); - } else { + } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } } From c866bd814d6300442a05a23c1b43c7d63166384a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 12:21:06 -0700 Subject: [PATCH 11/85] Make clicks work right --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlays.cpp | 87 +++++++------------ interface/src/ui/overlays/Overlays.h | 2 +- interface/src/ui/overlays/Planar3DOverlay.h | 1 + .../system/controllers/handControllerGrab.js | 23 +++-- 5 files changed, 50 insertions(+), 65 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9182346352..915ddca7bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,10 +45,10 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 862152bea9..3f175ffcc7 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,15 +818,17 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { + auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (!overlay) { + return PointerEvent(); + } + glm::vec3 position = overlay->getPosition(); + glm::quat rotation = overlay->getRotation(); + glm::vec2 dimensions = overlay->getSize(); - auto thisOverlay = std::dynamic_pointer_cast(overlay); - - auto position = thisOverlay->getPosition(); - auto rotation = thisOverlay->getRotation(); - auto dimensions = thisOverlay->getSize(); glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult); @@ -874,12 +876,7 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - PointerEvent pointerEvent; - if (thisWeb3DOverlay) { - pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - } + PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -895,13 +892,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; - } + auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); + emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mouseDoublePressOffOverlay(); return false; @@ -913,13 +906,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { - - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); - emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); + emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } _currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID; @@ -932,40 +920,29 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); + emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - - // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } - } - - // If hovering over a new overlay then enter hover on that overlay. - if (rayPickResult.overlayID != _currentHoverOverOverlayID) { - emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); - } - - // Hover over current overlay. - emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); - - _currentHoverOverOverlayID = rayPickResult.overlayID; + // If previously hovering over a different overlay then leave hover on that overlay. + if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } + + // If hovering over a new overlay then enter hover on that overlay. + if (rayPickResult.overlayID != _currentHoverOverOverlayID) { + emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); + } + + // Hover over current overlay. + emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); + + _currentHoverOverOverlayID = rayPickResult.overlayID; } else { // If previously hovering an overlay then leave hover. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); _currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 7235937077..d52f6d5947 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 9c502ab75e..2ccf2c4513 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,6 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; + glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f173295a1e..5fd4df0e4d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -188,6 +188,7 @@ var USE_BLACKLIST = true; var blacklist = []; var entityWithContextOverlay = false; +var contextualHand = -1; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -352,7 +353,9 @@ function projectOntoXYPlane(worldPos, position, rotation, dimensions, registrati function projectOntoEntityXYPlane(entityID, worldPos) { var props = entityPropertiesCache.getProps(entityID); - return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + if (props) { + return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + } } function projectOntoOverlayXYPlane(overlayID, worldPos) { @@ -2186,10 +2189,7 @@ function MyController(hand) { }; this.searchExit = function () { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + contextualHand = -1; }; this.search = function(deltaTime, timestamp) { @@ -2226,11 +2226,11 @@ function MyController(hand) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; } - Script.setTimeout(function() { - if (rayPickInfo.entityID === entityWithContextOverlay) { + Script.setTimeout(function () { + if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { var pointerEvent = { type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse + id: contextualHand + 1, // 0 is reserved for hardware mouse pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, @@ -2241,6 +2241,7 @@ function MyController(hand) { } }, 500); entityWithContextOverlay = rayPickInfo.entityID; + contextualHand = this.hand; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3488,6 +3489,11 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + if (isInEditMode()) { this.searchSphereDistance = existingSearchDistance; } @@ -3624,6 +3630,7 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); + entityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From a8ab115b1240ea0caa6b4e20152f4af4bfcfe6fb Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:11:14 -0700 Subject: [PATCH 12/85] Open marketplace when we know the marketplaceID of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 24 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 915ddca7bd..f887aae7ff 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,7 +11,10 @@ #include "ContextOverlayInterface.h" #include "Application.h" +#include "scripting/HMDScriptingInterface.h" + #include +#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -73,6 +76,27 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } +static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; + +void ContextOverlayInterface::openMarketplace() { + // lets open the tablet and go to the current item in + // the marketplace (if the current entity has a + // marketplaceID) + if (!_currentEntityWithContextOverlay.isNull()) { + auto hmd = DependencyManager::get(); + auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); + + if (entity->getMarketplaceID().length() > 0) { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); + tablet->gotoWebScreen(url); + hmd->openTablet(); + } + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 01129ee4f5..cd2d00f0a9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - + void openMarketplace(); }; #endif // hifi_ContextOverlayInterface_h From f0091f16a18669de27c08ad99da25e34645c71ac Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:46:10 -0700 Subject: [PATCH 13/85] cr feedback --- .../src/ui/overlays/ContextOverlayInterface.cpp | 12 +++++------- interface/src/ui/overlays/ContextOverlayInterface.h | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f887aae7ff..948ade840e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,10 +11,8 @@ #include "ContextOverlayInterface.h" #include "Application.h" -#include "scripting/HMDScriptingInterface.h" #include -#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -24,6 +22,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); _entityScriptingInterface = DependencyManager::get(); + _hmdScriptingInterface = DependencyManager::get(); + _tabletScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; @@ -87,16 +88,13 @@ void ContextOverlayInterface::openMarketplace() { // the marketplace (if the current entity has a // marketplaceID) if (!_currentEntityWithContextOverlay.isNull()) { - auto hmd = DependencyManager::get(); auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); tablet->gotoWebScreen(url); - hmd->openTablet(); + _hmdScriptingInterface->openTablet(); } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index cd2d00f0a9..4959b3d985 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -18,9 +18,12 @@ #include #include +#include + #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" +#include "scripting/HMDScriptingInterface.h" #include "EntityTree.h" #include "ContextOverlayLogging.h" @@ -34,6 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; + QSharedPointer _hmdScriptingInterface; + QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; public: From 44c9f0e65ea7d948bc805c17f39f922b372d4231 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:13:26 -0700 Subject: [PATCH 14/85] Overlay appears only on marketplace items --- .../ui/overlays/ContextOverlayInterface.cpp | 53 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 + 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..c2473feccc 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -27,6 +27,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; + _entityPropertyFlags += PROP_MARKETPLACE_ID; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -35,27 +36,30 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + if (entityProperties.getMarketplaceID().length() != 0) { + _marketplaceID = entityProperties.getMarketplaceID(); + setCurrentEntityWithContextOverlay(entityItemID); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); } - - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } @@ -87,14 +91,11 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull()) { - auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); - tablet->gotoWebScreen(url); - _hmdScriptingInterface->openTablet(); - } + if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + _marketplaceID; + tablet->gotoWebScreen(url); + _hmdScriptingInterface->openTablet(); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 4959b3d985..f3eb8c2ba9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,6 +56,8 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; + QString _marketplaceID; + void openMarketplace(); }; From 4374d4d1e5cebf1ca14a5eb0a5ca7d07166c96db Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 15:27:48 -0700 Subject: [PATCH 15/85] Small improvements to overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlay.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..3ab01ea8b1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index b650da3522..675dff7e93 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -20,7 +20,7 @@ Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), _isLoaded(true), _alpha(DEFAULT_ALPHA), - _pulse(0.0f), + _pulse(1.0f), _pulseMax(0.0f), _pulseMin(0.0f), _pulsePeriod(1.0f), From ecd419e0d393cf8a9ae85d356f5f8aa2210fed2b Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:32:03 -0700 Subject: [PATCH 16/85] cr feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c2473feccc..79edc7f863 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -35,10 +35,10 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _marketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); @@ -72,6 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _marketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { From 920ac17caf0d7b002c5548995e15cb5b2fc9c1f0 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:46:28 -0700 Subject: [PATCH 17/85] minor renaming --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 ++++---- interface/src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9a86d1efde..148d511acb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,7 +39,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - _marketplaceID = entityProperties.getMarketplaceID(); + _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -72,7 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - _marketplaceID.clear(); + _entityMarketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { @@ -92,10 +92,10 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + _marketplaceID; + QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f3eb8c2ba9..c79ee247ab 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,7 +56,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - QString _marketplaceID; + QString _entityMarketplaceID; void openMarketplace(); }; From 40ec770bc10f80802a8b69a2d58213b8e519720f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:06:17 -0700 Subject: [PATCH 18/85] Highlight using simple yellow bounding box --- .../src/ui/overlays/ContextOverlayInterface.cpp | 17 +++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..1582f7f53f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -28,10 +28,12 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; + _entityPropertyFlags += PROP_DIMENSIONS; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } +static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { @@ -42,6 +44,18 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setVisible(true); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(1.0f); @@ -70,8 +84,11 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt setCurrentEntityWithContextOverlay(QUuid()); qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); _contextOverlay = NULL; + _bbOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c79ee247ab..b20b9b35d5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -22,6 +22,7 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -40,7 +41,9 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; + std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); From 2dc38441168ee0ff1b953d0425b657b6e4f60699 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:07:57 -0700 Subject: [PATCH 19/85] Improve reliability of hand lasers; increase size of target --- .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- .../system/controllers/handControllerGrab.js | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3ab01ea8b1..c52ef1bb16 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5fd4df0e4d..9a1540cc78 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var potentialEntityWithContextOverlay = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2221,13 +2222,13 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + if (rayPickInfo.entityID && !entityWithContextOverlay) { Script.setTimeout(function () { - if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { + if (rayPickInfo.entityID === potentialEntityWithContextOverlay && + !entityWithContextOverlay + && contextualHand !== -1) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2240,8 +2241,8 @@ function MyController(hand) { ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); - entityWithContextOverlay = rayPickInfo.entityID; contextualHand = this.hand; + potentialEntityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3492,6 +3493,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; } if (isInEditMode()) { @@ -3631,6 +3633,7 @@ function MyController(hand) { Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 2dce2549289584106deea64f87e7b8dbbf7620d4 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:11:45 -0700 Subject: [PATCH 20/85] Actually increase target size --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..378d8937bb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -55,7 +55,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); From 00238329385ce2cbd2dc0786ea2ad9068ab29050 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:38:19 -0700 Subject: [PATCH 21/85] simple fixes --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bb1424b86a..83ab4c6b13 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -69,7 +70,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); @@ -97,7 +98,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { + if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From b236a21adc3d98f5e4b5c5525ef77c793af284e5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 07:28:45 -0700 Subject: [PATCH 22/85] no clicks on 'highlight' overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83ab4c6b13..0ef5a0ad43 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,7 +49,6 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -98,7 +97,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From 110efd63c835038d19bc813268895c452d9156b6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:08:49 -0700 Subject: [PATCH 23/85] enabled property so we can turn it off during edit, etc... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 0ef5a0ad43..e66b8ee39e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,10 +33,11 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } + static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (event.getButton() == PointerEvent::SecondaryButton) { + if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b20b9b35d5..92173de050 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -36,6 +36,7 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -49,6 +50,8 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + void setEnabled(bool enabled) { _enabled = enabled; } + bool getEnabled() { return _enabled; } public slots: void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -58,6 +61,7 @@ public slots: private: bool _verboseLogging { true }; + bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; From 0544c1e56173e2f12c09142b0381f88eacddebce Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:18:37 -0700 Subject: [PATCH 24/85] build issue --- interface/src/ui/overlays/Overlays.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index d52f6d5947..a6cc300b67 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -131,7 +131,7 @@ public slots: OverlayID cloneOverlay(OverlayID id); /**jsdoc - * Edit an overlay's properties. + * Edit an overlay's properties. * * @function Overlays.editOverlay * @param {Overlays.OverlayID} overlayID The ID of the overlay to edit. @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From b7be727b4ce89e4556ca2716e66378ff78e8ca69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 11:30:55 -0700 Subject: [PATCH 25/85] turn off ContextOverlay for pal, edit. Improve clicks too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++++-- scripts/system/edit.js | 5 +++-- scripts/system/pal.js | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..35ed81bf03 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -29,6 +29,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; _entityPropertyFlags += PROP_DIMENSIONS; + _entityPropertyFlags += PROP_REGISTRATION_POINT; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -40,6 +41,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 position = entityProperties.getPosition(); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -50,11 +52,13 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setPosition(position); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -67,11 +71,12 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setPosition(position); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6bb0675bc8..57cb8b2407 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -14,7 +14,7 @@ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE - + "use strict"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -388,7 +388,6 @@ var toolBar = (function () { function initialize() { Script.scriptEnding.connect(cleanup); - Window.domainChanged.connect(function () { that.setActive(false); that.clearEntityList(); @@ -614,6 +613,7 @@ var toolBar = (function () { }; that.setActive = function (active) { + ContextOverlay.enabled = !active; Settings.setValue(EDIT_SETTING, active); if (active) { Controller.captureEntityClickEvents(); @@ -2184,6 +2184,7 @@ var PopupMenu = function () { }; function cleanup() { + ContextOverlay.enabled = true; for (var i = 0; i < overlays.length; i++) { Overlays.deleteOverlay(overlays[i]); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 8ea22192fc..112e1a7fd8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -732,7 +732,9 @@ function onTabletButtonClicked() { if (onPalScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); + ContextOverlay.enabled = true; } else { + ContextOverlay.enabled = false; tablet.loadQMLSource(PAL_QML_SOURCE); tablet.tabletShownChanged.connect(tabletVisibilityChanged); Users.requestsDomainListData = true; @@ -883,6 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } + ContextOverlay.enable = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From f515a24568b7f3690f1364111b64849faa1f0fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:00:13 -0700 Subject: [PATCH 26/85] UX improvements --- .../ui/overlays/ContextOverlayInterface.cpp | 51 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 ++-- .../system/controllers/handControllerGrab.js | 7 +-- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..bdbbc3f5e8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,7 +36,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; -void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { +bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); @@ -59,7 +59,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setAlpha(0.85f); _contextOverlay->setPulseMin(0.75f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); @@ -69,32 +69,45 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 contextOverlayPosition; + if (distanceToEntity > 1.5f) { + contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + } else { + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + } + _contextOverlay->setPosition(contextOverlayPosition); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } - } else if (_currentEntityWithContextOverlay == entityItemID) { - destroyContextOverlay(entityItemID, event); + } else { + return destroyContextOverlay(entityItemID, event); } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(QUuid()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; - _entityMarketplaceID.clear(); + if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _contextOverlay = NULL; + _bbOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; + _entityMarketplaceID.clear(); + return true; + } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { - ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 92173de050..25179e99a8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,6 +29,10 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /**jsdoc * @namespace ContextOverlay */ @@ -54,9 +58,9 @@ public: bool getEnabled() { return _enabled; } public slots: - void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID); + bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a1540cc78..fde237e697 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2227,8 +2227,6 @@ function MyController(hand) { if (rayPickInfo.entityID === potentialEntityWithContextOverlay && !entityWithContextOverlay && contextualHand !== -1) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2238,7 +2236,10 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "Secondary" }; - ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; + } } }, 500); contextualHand = this.hand; From f365ce8abd545e0aa9f52c3f1a2dc67e06802fab Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 12:58:06 -0700 Subject: [PATCH 27/85] typo --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 112e1a7fd8..7f0629abaa 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -885,7 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enable = true; + ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From a1a7ab945a048a4b7c4981d5257cb34520a80bab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:56:46 -0700 Subject: [PATCH 28/85] Make dimensions better --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bdbbc3f5e8..d5244140d9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -72,13 +72,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); } else { - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + // If the entity is too close to the camera, rotate the context overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); + _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); return true; @@ -90,7 +95,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 25179e99a8..5ba70d7614 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,10 +29,6 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /**jsdoc * @namespace ContextOverlay */ From 46d09a12fe36535ca8ce3e386491f43fcfb74386 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 13:26:43 -0700 Subject: [PATCH 29/85] Fix bug where overlay would get stuck --- scripts/system/controllers/handControllerGrab.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index fde237e697..b191c6bf89 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3633,8 +3633,6 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); - entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 10503c3a16c54b70d87ebf1ce61fdaa94ccd2606 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:18:07 -0700 Subject: [PATCH 30/85] registration point --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 80c5665127..7f20ced1c3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -41,7 +41,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 position = entityProperties.getPosition(); + glm::vec3 bbPosition = entityProperties.getPosition(); + glm::vec3 dimensions = entityProperties.getDimensions(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -58,7 +63,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(position); + _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -71,7 +76,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); From c1de09c7f65635ec81802128a62567c9ca2405d2 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:24:16 -0700 Subject: [PATCH 31/85] whitespace issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7f20ced1c3..c15bfaf9a2 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; From c8015e4eb608d663ebadfb17bc8dc152badb32ff Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:52:58 -0700 Subject: [PATCH 32/85] cleanup, fixed build issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c15bfaf9a2..50fcf50b90 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -79,7 +79,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(position, cameraPosition); + float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { From 5cd05da31e46ec60cfb19f1c87bff9fed460a9d3 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 15:16:40 -0700 Subject: [PATCH 33/85] strict comparisons --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index b191c6bf89..f0f1267bcf 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2500,7 +2500,7 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") === "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; } From e68f9499fa018884784b6263a577602008036e01 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 16:51:00 -0700 Subject: [PATCH 34/85] Add hover/unhover handlers to context overlays --- interface/src/Application.cpp | 16 ++++++++++--- .../ui/overlays/ContextOverlayInterface.cpp | 23 ++++++++++++++++++- .../src/ui/overlays/ContextOverlayInterface.h | 2 ++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cf5037f57..699ddfcb2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1348,9 +1348,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(overlays, - SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 50fcf50b90..78ca0d591c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -69,7 +69,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMin(0.6f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); _contextOverlay->setIgnoreRayIntersection(false); @@ -130,6 +130,27 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } + +void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(0.0f); + _contextOverlay->setPulsePeriod(0.0f); + _contextOverlay->setAlpha(1.0f); + } +} + +void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setPulsePeriod(1.0f); + _contextOverlay->setAlpha(0.85f); + } +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 5ba70d7614..f06f85eab7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,6 +58,8 @@ public slots: bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; From 9c0026bf697c95f2bb210d900b1e739a9008859c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 08:40:12 -0700 Subject: [PATCH 35/85] make sure context overlays are on after pal is dismissed --- scripts/system/pal.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7f0629abaa..58a54ef859 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -718,10 +718,12 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; + ContextOverlay.enabled = true; } function tabletVisibilityChanged() { if (!tablet.tabletShown) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -865,6 +867,7 @@ function avatarDisconnected(nodeID) { function clearLocalQMLDataAndClosePAL() { sendToQml({ method: 'clearLocalQMLData' }); if (onPalScreen) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -885,7 +888,6 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From 7d125f44e417d13a39e411767c2f520011820570 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 09:35:49 -0700 Subject: [PATCH 36/85] Set parentID even when overlay already exists --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 78ca0d591c..de1c4156a6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -58,9 +58,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } + _bbOverlay->setParentID(entityItemID); _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); From a800f6c05a6e57be7371e979c6817132e43d6d5f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:44:44 -0700 Subject: [PATCH 37/85] No magic numbers; Move (i) based on laser hand; Tablet to Tab key --- .../resources/controllers/keyboardMouse.json | 2 +- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++++------- .../src/input-plugins/KeyboardMouseDevice.cpp | 1 + 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8baf56684a..a4f7cd8db1 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -116,6 +116,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } ] } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index de1c4156a6..7d1131218a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,10 +36,22 @@ ContextOverlayInterface::ContextOverlayInterface() { } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; +static const uint32_t LEFT_HAND_HW_ID = 1; +static const uint32_t RIGHT_HAND_HW_ID = 2; +static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters +static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; +static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); glm::vec3 bbPosition = entityProperties.getPosition(); glm::vec3 dimensions = entityProperties.getDimensions(); @@ -68,10 +80,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.6f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); + _contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN); + _contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); @@ -82,14 +94,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > 1.5f) { + if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); @@ -134,20 +150,20 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(0.0f); - _contextOverlay->setPulsePeriod(0.0f); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(0.0f); // pulse off + _contextOverlay->setPulsePeriod(0.0f); // pulse off + _contextOverlay->setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA); } } void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setPulsePeriod(1.0f); - _contextOverlay->setAlpha(0.85f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); + _contextOverlay->setPulsePeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index b5a2fc6b3c..e9ec6d8910 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -236,6 +236,7 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); From a346e5ccf6b42a6a43f45e5e29e125f91aa11b07 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:56:06 -0700 Subject: [PATCH 38/85] New inspect icon --- interface/resources/images/inspect-icon.png | Bin 0 -> 27911 bytes .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 interface/resources/images/inspect-icon.png diff --git a/interface/resources/images/inspect-icon.png b/interface/resources/images/inspect-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9259de23e9f5e966975da9652a5ecb1a15b8c248 GIT binary patch literal 27911 zcmXt9Wl)<<*A9i^7F>e6OOfDEf);lu?$F{cMT!O20tFrj4Gc$xSum@o_W_(*~VbsEa)#&_#SNcr1wD>en)+vB| zw3R~F?V*^zvN1|i{A4nrIH2!a1XP73FE7PQ#s<%RP-%8035=a4<05kwH!0PFnYZ+E3fRRS#GvF=Y0T2ubF4FqdWraX5 zpvPQET0L)DcJ*^|a-s-B189^HBi$(gy&i|p*SHATvZ-(@3U|X^AGc9G zpsw=_`w|@}>L^6D{415o1t6%Sqoc#7k(Z4{|E_H6G6(EojXmhW0Z79`m-lpat=*G= z6>fp?CC8N1=foj$2ZWhntH;-d`ubuhLa2`1oSe?iuCBGO-JQ?3N51oBRcVv--$?~s zwb>dcjW4oHYbYwB`$zQ2O39UD7f)bznb%PZJ7*w1id<7l2m8AgNPimr{pZg?&`Q9L z-3OtZk1+@&!1C|}U$0LzUp}VH`jzG6o_GD?q1fufe?{^!A@|pz_lv7AM39H5u&{6} z%;1eDU_U4t;~j7Fsg`n^9l)rsm}A1GM;U1`>{+zM>V@)YKf> z>grB4L|_nPl8}*QiUGH)F!i0c)5XEY9(d24`3hjNPjksI?5XbLw0Cs4*y^^>U^AGRNG(cRSy2J3aDdjP z6clLEVoV4(HZ|SuO_dQ)in=aM$MbdHtREa4Xamy99o8+s{Ngu`K*t-R5^@}$!6D-& zTwGYd|MKO_r`z9b1=Y_NQ<{FX(!p&CglPNhpC$3v4=6=GX78O1^U+Dz|H&R^)yM;n z*f-(5l5L$1B>YT2T>$p5{&#ozZ7gHFXp-8Kx; z<~Dqot<8EZ2WBOXg}e_4xZP)Ym-%2ll)*9YJsVGtqz_41=?eNs8?c%ByTkV|P>wKi z2}90TinN)bA;$~WJK7AJ%XooNzn6hexD5LgfL|N5b#u zVYWC~I`DTG3hISWkTMvGX0!e0D`j2x-F_V>7gy_0Blo0e>Mk~Ia@oiJI8x0&xuS0M z^B+sG6yS0fdY+!1b3sQx9L~Bw-LItB{fLzgg@_VLhCJLjyvyYN`o?=#i)%!X2p<;1 z?>t>Ton;=lXXx4*#P(?i>d2rnc`E$juVld9w=8^qszjkvWiZqq@lj+WN#cm2Ipp!q z!Opn0c#NuuzI$?VlEAIwux0PZj~_ymDqzg-Q*{~E&k?sa=!cx-;UPFoj5H9yMo}e->Af_YRYBvs&KiV=KN~$ z4b$Ove-r4bpQd2rV77H6>=5Evs=XZqvpnN=fi zF4_JK7DiE6%;}QX*z%$0kD zQa8K_R420^o?EM|>N(T7_7Yea9+{h4TLNQJ*Gr~Z!{3v;c_6RG+ni@AWz_P(sj662 zMqs0TbRTPx+|v^TfrY85Y3Ejc&{4pw^LPAWa|WWYX|B4?GtKv{t~`PNYFdwmMn=9( zcXCEX%G|1Zf~5WL_R418b7#g*b3tBBI;?aCjFFR*9~(70PK*h@c~haJ z42Dh#`0P(#BvOk$t>W-?*4y-kq0DM06UHWu*i+y2q7npme!9OBZe0zzzYUaP+0sYV z6E}Em>)`N)Ksk2wm6gjvi% zsQ7{ZRVXBqj}cA{T#JWd#t|{5;mTz)3<)w*rqIi0qU=fVibeP$EdeLos7Y3ZR+PI& zWQuq=P0=y2JC@V#%9Bnh&f|>zg$j*w@gx!Gdx}z%2_{C(lGWn0AbCcwXh+3FDxV#4 zi(rqDo~O&Y9?8pT-8xzcUp1I315%lSw|y5jE%SgRNLJ66yu}Al?Wc~$%6ri#5cA4Z zE}`fJy(;&9t+jyz;~-S@;(t{@59d+)lBMr14vj^*|VDeLIrq(JR$EJnuRRZfC2!TM8>Ag;-U;NJMG{m&oxIM`_; zyKbUISt=M(V-tm7JdxZ(*o!Lvbq1X0-;cP3oZDJi6{QD8MHUYV1YI43#FOy^inFoB z;CJ`Pr^_VR@Y3N6wQDbJ4iu5`adx2yp)p~4W1@`-j`QPsfAqtV_qLLNM5ChOq3%BK zaBot(eVZhQYik0i_ZI+^gW=aiB(saS317$;lky1!Sln8lJxySQ{aB$?#uZE2fEaD9JN1bq{hgqf6ZU~Iq!*>&y z6UG{C{Bsf|iPknh)OB4g#?S`tMzpkuP=T32rxE?ueed|XF5;=(I#2f?=YqDB>>tiy z^(}bsD{~vl`l4*Q3E#rdx^1ddKjSo|32-ciGgKJt zw*Ac592y#Gt?hZbi}5{}n~-Jt78DgR3WoJ={!S0#G4H&glaI#z`XBokD3h5)w7p@^ z_Kk?XXmQC*e6%H+C4~`Kq;`~L9V^Moay#4Y{x2o4{z?zWz)%>`n2Y$K!m2i;I>7Qt zM?m}ymVwK<+O71byQNOM;Y<}xna2xwS$^Ptx)wVGwKSJLxtLmjD$)Ppbg-`d`RP8Z zo;!DHsz8)ch27cR>hM(YW36jnxCq}85J;676>HI{M(fkr<(WV}sP@UU(miCtTx*saWV>L=iwHo{91f2E5m2Ky~b%r?RCK ziea2vS%Y_wUfwjrXt!LX9gWZ_S!skmj$NTQH8u{I`z`xq`FE{+QczIv{`bJ`4hAuU zmisPeEo2<$8|~=usD#P~UDam-Bm8@+$wHURquH)Nh@49MX>=z3`a|m?JIWM;aw7=f zM6Cn5YebgVhuK}}x4v9_Ip^L9Ozb91yD%xC_K^xuDx9^;GV_&}lM8nk&g8MUe+;3b za9{^D>-&&73Q=|5Zs(rA7`GpAAa!^Pheg(unViTO(}?Ja7EhAjU9k$e$Y0_z1D=v{ zukd-2W523ozbPc=JxY(Z{WPZT=>BkXdhObII?&;FFgKxAT=-$!V8-}`#Zj(&x?b)# zH1+7$1qA9nY;b4-+-St3l)A+~_Z-T^E~~pj1ucD6 ze^lwJa-~EzC`mCHWoiCS)ZF2fTtE}Vkwu3}C$0*p&Jnthj_GO; z-GSs6lJTZhz+i%*SAY1`hQ4K)-<>}_-b?r$%rz|j`{s2%ij5Jq;fs>AagrSRuyNLL z+2%|b#%lv2ZQ>+BeYdhgk&POa>i2Fgr4 zY0ZNUzbkEz6+CkS z-gU|(vvIF=F@5*99AQvvod%rvK+%_l&pTI)LrLcT@I3Kg17Ij^6Bizv^ z{;v-+leT)`lSRh*H~BKH4FU!gL|<=1p$X(?@~Qgg-`nnJAFAHhH+JGDb`yGjLZzcb_%Y!KC&30+7>(Kj_kMlz>M>_4;*aTI=+6=0`(TyF) z3VW5pJ0sb&=!m3mi%XE%8`kPsF6BAB#keh{e_A_u41>0{sL7wBj`-yR9V2os|%u(At~;(c-? z54ly~z;AzWcMPE5=%WYP3E`r~WGcNk>Mghi4Jyzb{alA_xW`1beJTw{#W3#-GNMo3klEbY@}L%VHI=j*${1XqcybZ0X}gZsdHA*qc1C5G6P9#Q0XCe< z`jfc;fmNC!R#UO8M$MAw;(t5>0x~w*{(PwYu}x*1=@)W2V_5cUqp+s(I4;_0x!vcP zmfy0s=G6XPC5x_S(HD(?j8-9bDu!A89?$JFlw}nJGsJ-V_rOpTd|ZAtP^~wPlZ>7J z<FEZWI_tS&l zgZtMY{q#>!4t3SMkYN92e4l*4sF%_DDL&NoV6I_y;qa>c<&wyPRJKHBO9lBJ1s!)? zS1h~Ik!$+a$xJ{OMM1qEl$?7;m=rp6B69@XxdG_!|NUoVrcJ;zZ0(rCv%;I_X)~DA zb@p&RM&LSEZxzI7Kc{|!IGt~DATVwDIKKlLOoWo>RxP1CI3rL@Pv^}KK3??}ee=R) zOzLjOT^m0uCs1@B%#?2n?w_60rVjP#!6U8^M*;nc~8A4JS8R3iM7xMvuS-bOBnAM5jU9uaTMQIm((MGkHYxAvc zPLKq3W*t{i5R&9bk|Z&&?Sz-OaR3e44Muduyzk2HYR7C1@gNlLBLtb`$FTyXUUfvH z;;jAdVsk%L>ZtE-6sSF*6R`gI--p@SPfSu$Qbpb0u-HIjGS3eR$KCfw&Pf}3J!&bo zYg_B-Z8sZcH|32$d^`dsg?B(H+}e$&!6y;AT9YP%p4&zem%f`*X~hHzf#;?%>HE;V zy}cYrfCJVgY2elu!_pO>Su-xmXcdEw8d4YOKex@8ejmlq3?Y&7@=l|%`H5fL#^CqK z>zt+!r@S8}DP{U^6%!~<&xJqy?M%OU#~xE znMpAryTf(4eO5`a-Y-6R)<@)eZM!L==N`6@PKex{}SQO2(GIbZP8yaEw`2V3Jre?^^4f)CzufOTDLPU?$FCLQq`&+}sfYf3jX4CMlHu=7>KPU??7;fNMkCrL;x7;w38Zkm$!}i+FPmR@acrAWVFFKp( zAF?k89d{kC1fP!}0^hD)Vov zC;1rsF3rTNuI3&hui0az_eNof4h&NuHfVW~9i>qKe)1=?a0wAmn1z-@B~gt*P~Qv0 z)va^|4IYO0nBok+aQCKMx9;2L>9$vQk1b4X=T(St?`bnu%(vk*nCSH%0y=}s(Pu;3 z$rGPaI1fQWj^jSZUDqq=%pmMHZY!M=IAq-Zs9J`xhlTX(Nn-?Su`pug^#I3R!aRDC z=W;P`NdtZ1jRnoCwKm=Vw#){T>7IYRD579v{dnEYG=ja0WQ?M(&U2$?t4JR8HkXgyQP=ic}@IGtd2!=7|BPuK@W^3l- z$GCF!9Evr{hCiM5ldm2vw$^1v7Uxble}6H?^5flQhq}7&m+yfdhx?2qA^|CHVRD3( zBm`W8a8jUv-*QNGWx3&hiCux0Ih~$cLkcD?!H!Bxn1gO1=ehCrWBHGM!t>ts^ARrs zgys@(P$j`#u-^7lyf_0~y@0Ma#_oG%liSH!-*xBBdi=RExIOlFI-7&k?PmHjdLfmV z^!Pzvp62y>ibR+#5#DDg&ILCPRxeMk>d6X{YG(d=*y8Cd%3t{%ED*`g&c3!9_^(FP zI8%QFsjZSu-DF6Pgo=jQ=0hUr*WXD}0C2b6q{|gg>pM>esNIrjrA%h*YV~K=p8Umm*6$g>qSJ}t^dUzr57C~1BU-fi!i0F|wKyVUb(XHpv-NaQ*SBu*DE4?5hfYMs(GaZs z`Y=^Pz&9S(gK`&nD;H|j<-^r5=6uvIIffXDNm1_zJ~2oN&a@a(ydbKLd0k9^rQED8_iXMnc>=qqAcTND<=`t6+Rj-oJM}}8Eowo z_n(ImN-36w2fr(22L&Cm$#_kn z{qf{gu!w;KN}Kt5D+F(RzaanL7G**A!2wkN%W)2{H&u>47DOuzNeod_Zdy+&`sA+A z<81`1cc0T){_GH+giNm_GxARw!m0wG%X&e6Qp8YJ)lwyTe_>WkfH1PLY2<+eXKT#_ z#=fQ%6x8J-JeuYczP-zmE=HdKgbm^hpX?V=&8Y|x#brXFBs|GGY-E;?)eUVSmIitd zQ~>WEU(0^|;?p689XQ;}9g9&}+&Vtt>L;`#N2<-|lRzg>j?~vlC})X4`euzzMMb5u zUP%~!Iw(_A*!>Lw;Ke$;^y2`arDwp7`&hI{Z4UJK}cru=xV)XZV9s>ea z@W>29^8h=R;YNY1shL44_eJt={Ab+$;y=_d85?o4=#dl10Dq4=%M!{jtNFWje>WyO zg$^p55I{{rR`F}7m}0(yRRj(pi`uA)c9c4kVgf}^(Djku(C~0qL!%t&C0LD>_~~{m zly@D>EBd$Pz{MSDn5(*ACQpNf=_#4jf2=~OGnYj3#fzzj^sP2T+W>F|k)n%9mb&^)oF9iqM*cgW z*3~r2s)q}BtV8bMQ8-yY?-U}@uzFUEK7VpCXFdguWTF-EaIb|k{AhD4sf<&x)(a#G z2@R3)M@uzN!ya$MeVdkG;leGakgG{5GEyM(Jg;4>_>Hy1o!g{&mrru+I`;D2&GHqK zkx%1S-W!%QM@&ZEqb~_P33P?3_hh)g`=`|;T#PHwa0X#;a<~gMelJbZr;;yymb;Jp zRdv!8Q~j)_V0*a7>ZfaU&>Qi|J#pb!v(4+HbxJ^a4_E1=@955 z8g52@Nh&vIXulQ%;qd^99)wixv1go1BQES`@1dUDhF;S)Wfp^xdB(zmA7Qu+V+ zXh_x{VscBqvueePd{5{xyY>-RMc?N)s_7CNUi#!m_w?B!ssnM_I@*dpoO_M(KG1d_ z2A%0rtDA-VTg*XXO-YaBK1&MPVOqC~n3I2r7;8Z0_J&;9NEWOeql-2hBf6Qzp(fmr zzcQ%6*hqPvdcYql{Ny9{!OZ>4?8V~Qe;7R{aGdK)e`L0VpX;ia*Y*SNU)P;a78%wS zxdbBl!HEz!lX9DtY+d%!44Am|Qbi$712xsRS}!8WiZH`=>9SvL`u?pztHbu6uLKqW zTVEc7s@=0k>|ada2C!~dIx(H_;FkC+8;e7XtDTt&+YktnHCUR&~B{LKH^A2olG`u;pAZfaq z`|IPnU$oWM@Y6qU#<=+@aOJJ%8)I}XhK|1BGnNtLgsi$m*! zBLCM5Fu{K~gL-@qBXQ|d{;2TBnWoYI)LMb4pOX=k&Zeo+bJYINI*|TpC6%f(c$4J7 zyEZiiVoTX|X>!p@y)o+Bp&^f4=VXoDIq_-22<7IE|H1RdVKmtLPqu)Q)LSrZ&_RO| zprpU@jb__}zikku8c2zlUk*z}a1zg?gu>gyp(E3?tb&U2@LIy0TPlil>r&u*l-+zY zTfYA~FhBX|mDoO$=SpV)YMI(s&!B2?oisbi=1YFgM+S=BZbdlbkf2V2iQ~FEu2!x5 zB?8uUae!D%D*Q{mxUe==s{Y>n@p6{@XN~*rW#Ge}W@sQNjKSc)w&oHlx_rk-dHH~H zB1$>*KRGu`r59Bt*fggb*6AU@qIKPvV>#KfAncHk=fSXNt zLhct=rIRSW38&hwO}Hqzb5fLMFrpdJyZ(Eh=ZzuP4wTvW(<^xM38_7OYu#?wFLnOs zO|pZ%{Xmwxn^o;%ab8w}vq^G#?c)c20VP022ST%Y#oK#$X zf^N0x_(jnh6JxkgnlS0|N-pcRy&G?^lVQO%QIvojfZ)HeK~7|og*488Oh4+nUimuV z-|c!W;@WljSIK5Zn_5%E%E_1~%_%)(P6QfYF-Yn^vM0#r z5~tObAmcVVQMR|YZw>j^VFo_xd477Q&N}tFVYe`hetk@0h#^8yh;UUY&F-Vv43X$n z{uz2hX(N`s>MzQk^&(__NgFnXO`D~Ps%nn=nAG(jts3{2+U83NR=#1sa28(=S%pr% z1!L5R4|iy&sH9whQjlMDYwJXDhGL4FyzZ|n8y0lLS33DQI!BwqXH>t|x=d*`Qv>%H z5uR0hlRF|Hwpi|qagzLr&9Z2Q@)VWAnCw0;q&oRD+|Hrg`}Q=@NqAXqA7$>lq_pHj zm2(s&XMMB>PZYB8j@s`fav#GQf}1J0iLo+%w^L(BiO9=GwkjigS1QPn3s@rm<=?R&;nj z1%k2uQ`2(G_+zz~^?a&s<@eK(N#{;uZKbbyL33@3Gn;+?Fe0Z zWBRT8A_%102*cKgnT*k&?tESc!_eZoS;V(!yttnln_jUkqs+{=6t|EPxN=2A35s2V zxvB8^ND30{l)?8W2!hqB8Rr>>7x>`aU2HwpZ`h61Ocu4Y(|-f(GRyJA-Si*zLW>8jXWVaQk-F?I zrvpQ1h(ytqHu`P&OCfpMX{ywWE}quEt{UA!xZQHY2?lhar!}GFqmz!35Y7BKeQp%e zj6Yk=3-@rtRFPD=#SP-8ik9!a$Ig@fbf->k>Mp0M7G?AFa4Y)Co&E7-1sXdks}e=)k3G!NXP%wnR&tO%2KA&|mLMh4&j3H#!pB19C%jF(KnxvxFFmY{Oa zJzOgNaDtFY{_1{lylN42Ib*m=79aEMx5q*9J<#fY=81PRu z!!Fpn8GlN8Dazr>5dO1aT$czD8Eyk;r60JcwmIAU-MX{6ndkAXtcj{dbT#;##l9(^ zaFXB$wPXRAMp!GwmjbBUVC!ru301a3wzm%p};x3k$z-4ram zbRd&_7z%O9)w}S-IrAy1^eY~+;lh7)Jx>XBnt2|DZSx-o@I;pg(@oZ&kG1er^FqMv zqyOzHUU1_`;RKs6#*KYgg|MnCzRm`rPFb0B(%wOVWf3KOC8 zN3Yi!I&Kr;yc(=hJA-TYFu zCro38KqygEKEF>{a4QwWRRf_)4Z@XiXx2m5D0(?!{XHz~)FM&Cp0;z{x{;69tIz4v z{nL*QLOD3t*a^gJ8V^4vHTM#sMuGKlj1`zsY+zKbF?i7ya{P?nlN(|jZAdslMKn&4 z&`SdZrZJybu8q4Nh8iN|#u~j*^KO835racRtOqXc{UwikQ)R2!1qF37=~4PwUnTsG z`q3j8H1}d2N?^1>ChTc(&@NH90Im=&D&DZ{aLcpkAio|)^lsxU5(w!uC&~pGa1USg zJe&=eLCnD1WH>JX9-t~?2}wH*R3Q~2X;CJG5Wp*u^$-52oO#{Z&7t>)t*amTDPUZn zje*4B-=m`j4_d(+zga&zx%(VQ5TnUm;0obJM9ZSN(N-s^+&|u57YH~{mz!&oCK)%` zo1ykrUm$pRDquiGLP+vY81m$4LR{z$5!X7xpKuvX?YgUzw!n*}Qu8B>JM+=F~Jlp!QYp+mx|vJ2U776u$#V%&ul{KN|&9hdc8T zqfHbGcyVLrF3ov2im_^pRU0>j{5^zER2lMxtpALiWq%YJPX4=rHbcaQ$%v8yc<4bi zaD0#qINSUH!dEI2Dw=|O{AS5YXg1>B;kcy-stm*^ec(XLqK8J($2v6Z>ER@`a9HgA zM!MtXinuJKxco*@McJW1yRpsny<Bv=pYq~(r9mm|c2M%gq} zw7(W!DO`6+rj@!AtT71kg$o4VoCH_Rh^wg}-o0O%TUm?zQtZO{nVyJ%XLzkdCi43r zHK|*N-|h^-rBHwJWGqP-#sd+35y8|yx^I62FNM@K%Zg&^MM6m% zq$LG9xw$`5jU_Qjndq;9h@3n_8=4$5zkPoHC_4GjVuog`bimo>yHO)>G(JsEUT&_0 zfl9=+XnR9hKPryd-8)BHO2VzHf}k@3?NUGWy!U>BM1>8c7VPEXnLu2B7^)p_ku9$^dlm*JF^z=mq~dMly3rXf&I+TwUx< zg`RxOeQpSqw1P)gs8Ug&kHK2v#RHG+Wt;EG$VS^`V}mmw_Qs`o! zq$({#f2vcW%GPD1rg7WY=o8z))!iY+iqYCx{A2HTa9|uHC`xd-2Y_Nye?BX@M*AyO z(f?lB@qlKe_zJBRNk1OI>X+-k)A zA92$8n2gMuil5+;uHP0lkMnL+)Fk80E;ZT^pJ9jg4h{rI zt35r%x^QhREq)^Ge7EM;OmoX?Fc;2OlBl-XL=IK^B@(i~C1VR9V}js)ENhF1cSs*l zx?sq|&AB)WD4kVZ$2)glHl2E_oBo@Yrn~KrM|%GLY>M~#K$W-O*)VAW79ebxu|(J( z7D?Q$Ve!0FkLhr$)*rvux3W6Ke!T z0u)6Jn%>wxDE3k7Yc}y!T}&vO8p(Yb;~LYMe$YoWcke0>REWT zN`g2y+cL_{uPIjX*k~)QEdm>h;>I12NlnYx2@=s1ss|oOq%L!uC{42`s6@4L{AI@W zGS`0kvwBK{)*ejD6!t;6F`@2XUR=v>k33!(ndeSA68uEPh^NQB%Iyfx`KrNf!YL@^zX2jkGgMhZ$F{BcPP9DWXw3_CsR;^_! zzDnQzIo!^@Z6pTWLV3a$2OB$@5;uAZXK<}ALf1#!Oy*mH6IP@Lv=K?Pnj-55e)Sn@ zVv*9~7|R<7I@|$$d~uJ@vgt$`X$6N zP;+V@N`V)SDI|0i%i9ZkeA4tNem~)>&Z;|Fu3bE-$ptD_N~SF@5x~fiJi%gA_(GTh z$OfZVV0jK`|HFZ?aBCTuuH*hS#Er{c!WR9Vedrt1bBu*C+{n((P5~2aF}-xWTd6_^boDz}K&12o%S0q{Pp<62Unj{s4W7CYlwBzzC%yo@rV*h6;w zt)|PhDaqnlbbrKdD9NXA761<+w)7)~Q6h4Z5W$ao zort&k#0rSeQ3SbOGRp3eLEWVAIjrypq^wXby9wQb4MSTQ2{ zo`YMpQo}FMIM$|#FpGH!8ImKwHs0J3*kyE9ubW&?B;BT*zZ*aM z3|R3~jB20tapg(3_sBhv$9mq@4H1WNF@`Z9=|}az6zDVE^d_(3R_J#I5-8JIREUhU zNl*YUo$?0*ZG7B?1MgbxV~^WcWbI*?F%q(i%|GWq^%5XA<9o<3Yn54Wbe0R@P=9cP%3?skmdUi%w+_i)rNbNHO2~lH*ujI zJGXo-Cu?tC+Gojc%OUFV;200xtRx03@IAAfgFLGTBK=p0HRxn*3c&ZV5jGkHFm;V= z{-Ra{5-(mWC(t(M(u0Tns6I^G0?OM> zmM$0NF-=YR&HhXGs!CmWrk%c2Gu5M&&}*hO#~WgY*i1XEV*&&ic*U7cM zh#E=2+((iAQBhk!nWUVf<2vCpYW&q!e6&^JZGle$S}zBtqjS8aW;}V?5+*-jj9+{2F?KYOz+9u$ zmS0DuRGC`FY7<5>OLK5w6)Ps{a4UUb<1Q3ae*F=86k)S4rsB`P=0lZdBUpBxwJ2}w%)iTDLqn?Cl{2NX6ttWx5D1EPS}JU+Fw3~B;@v{i z`XnrNmzK{o&9?tv^3LXFNj;36ktnu=bH=e)2CavEf(9S982d|bGSOtloSB4~vW(s4 z)x^m3T}GD_FXrb6;oOlt^EyBFZI2rSrgr;lM(jvi${%D>Le=6*SzOG8f5{{BTbyE# zKBMfDUV$On%}t1Cyf<50TgDmA&0>V5S?ZA2DrsLNnGXbgy6H_2ucFq8mMcL#a&|ox z_%TeeU+m_wU;lGwe@uZ`tHVN6_DPd;^8tCl)VM6*GWu9^P@}3MQ4+Q2=+G;EE)LL0 zIRBSF%&<4HMui3H{XqRA0GdiExq>$}Rdo0pX8O6*EmMX+ADM{+P_?)VeBRN&(!qyy z0PW}%hM9(8D^SjFZ@v=6u6gjgM>An!r=^YL%yoad&jBatRx1?He`Z^zZ~qGAMifmI zX2pF7%)7Yhn-n3PNaV*QBxFW=tssTZB;mQK{AP^H%gijp?uL<(;tULRW##_8>+%-? zn<}4{#uCn_Hw$KICvCC$7{C~Z9h3jM&ZueG2xhL7(AOS@;Z`zDJCw z4a#|6&t=)(Fn&%`-#22<){tUs2?A}_5-%rl7uTtG0riFR=}nkkDd+*0u@U^3s)C(X z3R3trFgSxU(P!`5Watp?t2>qoQCQ^wHRL^{L;?h9CC;?{HU<{r2K2+V;IITJvu-6V z?0qW02#hag5aH}1d2}W};}jIz7O8MA;-ai0R2k<`WXbNTOu{g}$UVB^Q)Pe2CHKXzV)ZRg+)5M5mH5GszJOluWgYjwvF?#?^t2cW+P>71K$xf00 z3B^K>7#LA20`Ua(^Oou-x7gTnp`6&OiMqa3aZA*&4y<=XjcS2L&7ICJ)n{nEx)4&J zg1_Atd2191$vN04+Vhfd`M{%@O;qp0)0V zz+Yi<&g3N7RFGjzd>W0$HS+gi6Rq~fAWUN)!3S=+ans(vrsn)A(y`&>xYOlBI0C}FDYg~(pf&U6P*Zx3Hq9xeyLQbhOWMnxUt zn6EB)y{20!Of%S9jbuo5C0uS;NVD6l0K7TE&b8>Qm zKQa?}aBX|kbE{F|GalX&El1`sRldAvKxcZ{HO##^2t=0XNjo_DpM=ywG#+&@(%q;* zF_9`s5+C-SH6hwA)j{{T&%Z_#`WK2B-BJm4WI(0L3|Emuu7O8KqTezUa1Y0s)hJJ% zrR*j|fERBWQAP0LO-UX4rI2S#j5_SRKq&}-43B`OY?m@7-Hu`a=k>8?3DtL!q<4A3 zKx4&d^LX##z;x<-?_0Tu|Ddd&xtUZ3C{&sP3si|H(^u2!!>3{1~DC_v2HaktRk_WBzhL6V*vze+2=QvMu{`Wg|H&$6zCRgSA z_V?FJj<}IUEXfg2*2d}!Ysp^zgj_ocMQe&ug$jeI1o3P!gFUJr5X}fXy-zX4eRPWQ z{AyXx(DL-6kH9x{gB~v22>!KzZewHQ8hc|LnA!oAEJ^>nd*0a%l8pIR2{k0+x6mHx zhpO7zQC$OrZR`Iu*sc!dr%Ln?B0K~gNo+4v31Y0+n5o3qlr!9qPMFI4vf=iybz}5B z#jTdgJPik3d3pJ4JoJe2ii!&TN~@+!rAl+uaMxsF2W3cFs?{{3LVr=)mm@$IJ7eW9 zLCbBU5>7CIx40SV*tsQTbaGz#(f=C41>W|cFw=!4E9A}e8>o1lQuwQJ_AGFKvHZk^iOXUA!%A0c${XV>B_QW z5+j8QY34uNm(tSGoLd+1GVP2>$jL3j90hI}gHlzstLkB;}G0~yM1O3WVP!)>gyYGXsZz3EhFLXT&mI5s>M~X0KDA{2D z0)_<}{{$*5E_L|12@f|~UIt2C9j~q~EiF~gnI%b90m<@<<8mNbKs)-_9X&JNmLZz3 z_ZjSqm_RdIcA7L=>A(v|nwRB3MOy4=vJGBIYEAfG(Rmn4KtCzlcJ|O~CbRO}f2y?7 z5!v;#!0-@-7HOXETly@==l}jS81yjha0ke$tE+$4L7($^d4)n)`im1*^^R6WFgGX2!YClO|y4ovLaQ2Ybfu2jR3qxxCcD zR0rY8yAIPmd@Xif;AKEcW@)_XRQpHe~;vf8U|~EeUDd`*G~OO-btm|{bgD#kymCc zh}Z?exS90pw0%4g&YA$%@V6eZyKq5V>@-qR{7^5i^Vnmon86LKsJBHMs>W7pVz~9W zz`|dgu&JXT33^>*PKrJb{Vbw6(4$w_$`fbc!P-3co8a!8uAu9`1sY&2Jw0#8Z+waz z=Wi;C-Z?4UOoP^88Y92){(Ct%189|rJVydIjlOgUo9pW89(jw)uM*>{q`BXmAc+*B zm`1Sk?e@a*c1hBhi<5`W^YnvI*PIICpo}Ogis8#>sCA}EmGUw8U+#jXgEheb_9GA6 z`rL_kW#P)^7!xXYm0+bWe2lPx7v2u>PPM~T`Of)Ig-*u&VcFt2%Uw@j|BDhMFiU5^ z*7#!A?G3%`79dumw{ai8-vujDn+|h|2A?s|Ty!Bln3EfHmM;~g8ma}xrxBkn*+dZ= zbTgp=SUuV6c%9%7eC%7K|)y|^Qoz}A+snIgklyLv>6U9ZP&wR=N` z9i&mF%gMnZR&J}5SMU`Q6ALM0O^_cf&WejFUf77yEEL~>Twp?p(3!ucRIEuly0}Q` zSXdlC$sq~RP`0+V6jXhGj_YwvHdBqelAhY2u~2rY1WiCa5)W zw*Tej;aT4C7C()EqX43!qVB&OKtohmag_xbWf%2-CaY5zzon}}ah8@YtZsu$TTvto z)pIRuc7M9}eLg<^qzP74RmDej)E#2aw{s5(Bznt;g>lNnZqZy?wT;YRlYL<{Yk;Z4Z+pi`91 za3+refD!xXt)-3|2AZnBa-Q!u7N+HLg-W@u-*{C^D3OpCn3q@1oER%d{KCzJ9lFjp zZZ&KCbQ-+v{&b%d)U|@mg4fBvh(atBdG7bWUVt&}70&hhbj7q(m%s93Rp1{*!%BQ{ zP^u6)c)1X@4r%obo62Mbjf5{~zuA3yW=26(Rkhoo(FVQD$h~$XjNZi%_$(1-NBP5` zxYDKC8-u+!0{VX?on=%UO}B-I;F7_8aF+ltI=D-4_uzrSCAhmYI0SchOOOD8!6CT& zAi)W)f!xLY{?}R6T~&S7sjmI(ItI0K8Lc>0H#awnrl+TGs$!x795y;V;!@@G=6F`@x2y~ku<^(DV`BqFL_`?TCeIb<|NZK68XkH80&tb6u`bXs zy*`)3-%0oNu=c>;G+FZxPD%4^$Yw24%MV-C1J z2*}QO#>K{d)6v#O>S6~0E*A7m9W^^kIHr^kmC%EtfuSk!n~b6kLMCne`7V!=Rg2M( z?NoZKrkvGr2ey&lzjDMg1Fh8mRk}E=wpQT;8+{C8x=|hK(4`CI#Yb;FI2dg@)alM{ zaDSZ1uZyMb%uHg2r>qk|1ZAaX0&)bLH}5Qi;PQ1VogVgY^-QttqNC9Ja%Oy<+DCnNzR_iJ1nep{CG*B`q!&{E zB9qQMUU`R9SaPZ8&SN6kuMkBFn@#Kf(Bt)I^R~|n$L8%d;ZamhhdAPc?bK|fbSb|s zX7hlCu9qM;9d^)%5HzxP=8)0dx4jv&xpe_=*&o!8RDloh&VnZ;aTd^<{@PV2Pd=@6 zedJ3!EoXwAJ3Y9V9aaxds@vI%yzQaRmcos#r0oni6?PY?Kdd218f-@Aci`C4@e!=C~b z+|APut?54w@f}z(2mV*o*m#jT`L|nj(7VpC%}yQ+606!n!K z8_n&QtWaur6qUupg{k`-TM|{#mgof z8R21vj7T4c))Bf1?kxnI*4wW}lIe<^h=CdeP5zHp*cLNc*Hh!=xanr$45V3%6sNuk zc8+OQSt91e&qTB-@gcf&^@2>&mqmXshpB>=(}SkVCJe?iIXQ29yXDgZt8F`~NK!3| za`w=FyYA^@xnC&LV~I%))PCVks@1#OOc~!_?J|c&c5C26o12@biB0`4n`SAh4-D49 zTyah*OnA-lf&3ow+DAS>Lns3B3$9HZGa`!+r2v-ptGfT%H^)B<3kx1cyQ}-g-Bdw; zFP{Mf;x1pck|IKM=|bJpMI_CVY_#qvfTF{+1{Dz!(kWo2TwDaby>fGjW`m>h7tZb* z&CI+I4$R?dotjv+JfSwNqlzzweCuqy6 zP9qrm{gZ(~hxvHMhja~MV8vVvpZ(%rVIm6uxsH`X$9YYaD@rK)xWeMq2=|G$^T{Jd zlnS0Gww}yD{3g(Ndc59yO(~NjVD8}B<8dob!=UPZ)6Y4AeY{Rl+zPr7+;L#lFJ06j z`d0aD6zcpIgnU_x+36N4Wq%;;Y>Ibs4wbD< z<1t=R3nd~+B!vh^-Nwic&TU^$iT}IHdfnwAg6@xJI^TPX$Tl5bn7%&?Dg zUxWN?D2YFJy#AZIp1^Dt9od@u1C5%QdEZFV`a=U`%0 z8Gj+qfM*!|ma3BE_>9F-7>W`7^Cwozl*srW>}jC`zsUw!vwWN1fxRZzZUO%-1#H}3 z`=6hf)Z0&4%Y%PZ6kw>yr&hvj;Rw#S>{>4=3eH}jzh8oWEH((gBjAQOVDWmQpk#z4 z?Dk!Wv@aU`{_}#9iywKwCGcNyi4yD`;0?|08NC@+dN+-IwZHb=c?)*^_mez0AaKhG z%_9}2SPZNfP+`Q)`(@(BVnRWXpoNdRQ_>0+A*YD#38X~R9HTC?)z=3yW`yl~1)@zx zLCupXcjDwrghaa0vU75jzBz69o=OG|A*!78JpRrp-yKf;qpGCj*Hc2i^=pk{rOAAx zTOA={#JVEfG!a}%I|(8=E*favgnwNW26;l|!&Xr027&mn{yX9DU^`djd~pMewna1s zmDDCU(s~>{F}=JH{p!PM%hYB=ZEdTtG8kZXFqxM#J3IS|Q3%7{m^zvx?5bZZ+#Hkm zsF@~2NRV41{+TxdC;c5wP$P3<-tNbFxE1e;LHzssKNlE5fS=d?XNz@4;(^lz*FUws zzDa_|7Hagwy<||seV8wo!qG@ZO`=+=P)OX!$t9FjuDh(st>{-u=48k*vin2R6u`ww zi6Ft|w#LSbkU!g^jV}7^M75^9fng;=7S>Y4^p8GC6CJ9NF~(O&fO(P$NaKH{V}$|_{e1ag<51#f53iB?%=Gj#7IyYpocOm5z@^bQ2ZNJJU=|5}=uA-$gtkSy zA{Zq?s3igXPNHmbFWCND_??8k&3`3&HUr4sLOUmCHF^yuHe4jDhop{li}j!UBr4|8 z!TogH*+XnqGwSLDRU^NVMj#ko3A&A?JQ^zw??$-O5C3xo`TBOyFbU4mY?+#v1iIXx zZ-}>T?A0e!R#!I*b03o|u3&5VMHVOTVd06o#Y)w!U?jxJQ-HA7=Z!#aR;kn9zLEli z)SxnjUDDiQUutyge8(O5zr(8~$eTUSRuglwv&SeF1pggU$#SO)A!uex)vZ8{epzSU zg$-H2KD7|K;kia)C{YXHVa8t)`w=KihLsnKoy+&c5S z9|3T()UXBIjw}(flaoS{3xAN3=N}t0`h~a?X$Lj;*^!1eLk7f9AyyAYZX6j5)SnZV-XHkLLe8O8Rz7E|ZPHzyXF_w7;$5G@ zORB5Ke%Q{H6GBhhGf`z|X7ps8(&7lng7bFOUFsG+=%Cg-kmmn56tf+$@?$XiAM$CS z+#v=XZu{JEgR@RkHeHRLXR8~&-LJ(ZKYmxFSw3!)G>WL5dqxweRUt*O9=i;ixpsV8 z(+BFHxDn{Ze+5%+Xk+~N4?JYfpO%?<@MpcFWdq}^EyqP$Pj5}^VLR+I^z>&GE#iM{ z-+0Izu~y6oXxy@;B4%RqGHX87pONth# z$^|UX9?3Zt&U1b-ekFnCR^s8xWk|PHkn3W_d3=2@GPT5M*a}Jiwb)}oyc%Hqdw)N` z?`*YA6nZ*c>lETGR)pEg?>43w|1d&7qO~=2lr5KO`{i>BDQGm9n#`NB@k&G&J3;bm z)U3HDNYvMT;b^X6?M3Ee7iQFk({QnDT*ifnv%A%BPs%Eybl8v4K?z*iB4&LsC8nye zRLS5uo@4!VmPG&i%V~(^#rJj&l9OPvo%`vN+srR+ZkuP(e~y!i-VC17s6>6;!{~$; zwKxXX=zL*DtTY|9GSQi7L-%B&`QjSKcm`kCJggN)huWDT7W!D|SN-;3#T0hKi*-iC zpL+Z|r15@uEazIUG=HwC(>+m<7vg7FCU2;?3(l9JC>}877`(d@_I_MrQ{7N4$s43d zO;3v>MBs-;6)1<^((P_DWC~3{*S<`OxUNMLeq7X$gFPSQ(+xl{r{{$_HUeG#-^7oO@sHw$$>37T^)~^i@?= z%&e@(jhuTvlzI&&9|C`8*$gSs;|0kJi83ssdiY}YbeBX3`>4yt^C+v17&p* z(A+cW#`GA-o}Kw}E-%x`*o8chYdEyGyE-$x0D>9}%KDUzZlG`FOlA<9sGc5m9i7{i zhs#~lKZg+5)>iLsL`hp8f0?FFz~OXJDo7M3((6v%RRy1eFQ+I4=MskwFE+F*d`PF8 zlqz#+$6p11y%bNzyF*eAvq7!q#u`*#39J6ou-jJbcG{^MXC%kA&VW}*&%3gQ!=>Vh z!iIA5vjw=h1eoUaIG(?>YwLua03V*8!^TEO`#L>Ngnle2mf^|D%2F{fGOB)Y$2OiL zyR4Y%8Fp-WPbniJ^Bz^g#zs<0BDMf^$Q@fs_q)UZs<$BfpVZOG9mNs^8@uca$8?mf zr6YCQ=68kL3}vHDM#+Uu|2-%vGxH~?hL?0X3_~fd^L>72iOBnVxx2USh9d%=?*CAE zAJ11|a%Ztm%!KVc)S>)+yvE#q%&4tJy0kPC)HeW%akJq4)g}QQ$05-491?EFaHZp& zbAQYUs@NPV+_ZvuS+-KPUNR$yaI@xb#ISpwl*>o7B2qSD`WUGF!yUu5%;cRJs01htR zmeX=6v3(GJh>(7;gpd+lZk{ivBpZH792_KNlf*CnaDWU9OT`<~7U z1PuOTCpvwJz&L(|(UuuzaEsCpBu+vTc4^9(9aIzL-I5YvXI0hJLSnYFrMFTS3Do5> zgM)*&HF^#COlg^8;&VGmD=XM!nu611g)?p)>QZg-VHw!xL6wWxF?6|9!c(vb*Aenf zOoNSg5oRe)uYVuEkdc!M7KyNhnmu0aqjH-3_{xN2B(ksR|1~R`AV`gO*?^W3DHOBs zN}?ri&mndVQ4Qg0YMu2pnNDUFm*Ki^z<~d)zC2p=M?=`-Qv06iF#EU zfkOH`10Ss(fGS~cb783HwMQ?H(O-UieB6ym#Qq=wndvZlfJafqH~hF7YXPuJH8L$u zAqqFA;8Z5{%h+Y$qp5O|spz6`(#vh@pe`-8JL3;;lH*) z;16~Jzo)1NGkdvr>kPc?^fa^~+pW9?q!VWp&;^)9gazLHqLK42k$|V0PeqT4F8zJ3 zpmMc>p8D$QW1gbrG|CxtquE`AV5>O>oLroQ1t@8gxxPTP03^hCQp9ymY8a0I4v)wd$sO#u5^kej> ztMSGUpX>7U%uLL0idlt;rOwNZ@SDwUzkeL`Ipuf2;b)ko`ERhk&)eka%a)9endvr6 zdON#|?7dTTdb(S4{`dLMW_P;OT|r?)*e-Y?i|3$#kdSaK+-m9Ofx|n|eiIq?%7w9ypf4KZqTy zrotp}@*oq5*u#;nU1o5!Uatin?BLdJ@kR~N-Nn+bVr zCUf--3~UK%5@kP4I85b>3NWc;-+%!L(+<~KV~x9NAHBu?d#A#W;^{V?D**|bY2+Tk zx`p)(KhdyE8zuEkmwrqgdh1um;;!Ye#_adOf$7NjxF6oTD)^<+9v+!<;$cKvm|y?c zC6_Dx9W~DCXQHX=>1C3?42%Q?BH8gSY63Lbw&UaDIcWt2|H77E&ItBX4}Rs`-@a)z z-{I^*-BsP_uXym`oS8*2G&#SKq>L=!U=jo1II4`fmNl)&zNX}AhwJVT>Gkhl!aG=E z6?va8Ch|r7rpi@vURkxDhvblVrwl#Y6cm(>zC*4d@MdQv@p5M*SURCeRS#A4lc0`9 zPe_oAEnxB#Pop1Ex$vu8I3`SGRPlvDc!eUiUS3}En0DHS|2|$XRW2Mah1;11=YZBI z75ii7DI48Hu=dIYLoT`+zn|ME$6yQ8qWd>%#n8M`Q^!M*`khO&_J3aeG(s$CIxC`gcgw^y;Pte! zww|o3ulMW>EX;Ft(b3bRqocEQwY5DmlKOTHKbL&CxrL(BlJFyOqhk&%Z% zuo*BY&*O-nJ&4^)O$-e?1?cE@SB{VED*P*LZ78hx`1nRfM)WInYmh%sCX1avxGQs; z{MmYWoh1yBcp$YunVr|F!>^mcMF{<-SEoxgF*5SiSWC;&Yf-Ey&*$gD{5&-e&zB;@ zI+G7p!uxn+N~m%r+wh*~JNK|5Ij0rq=p zC1Eh@Il}WUL{CdAthTl`bKdUOt!OQKr47G_OaQPlQv_+|DFqYseKz-@Bx4Js$@_8f zxJ)qdXHDfIO5yDQXl!iEx467~=v9@mTO|DG{Bx?Z{7pH9Qng%G1-?xq65ZAk#Hw=0KQO z#=yYP5YxA5+YP&1*hShOB91~4UFAFg zCl^Qo3#|w~!0$5KJw&^EZVE8bK1kxCSmA4pl;)Z5y6bB#+UrwsPHtKD39nktU7%!Al5+8HF)xU7c*O3U0rC)?|j<+OS2ua!OY8=4-x% z14ftaT)Cj-Ff;&0+Om!ZhtS2w#**=Hb5nM??T^X*w@P(ZBW2<%(&JxC#psC{n!H=s z)IxtxSR~EW2;%ibK@|3Z&rQgj2Gvr@g+Ir&33jUF@Fx`T*)Kkq+Qhj0Wlc{{zv~M_ zo!aaPcoqjlyG`bk?ZhQg9mR1Dm2Nk=1RWaL1j|UEoiUQ|qr~*EI#3WMat>Em7`sP9 z660Ffjk5Cc^7!l*YXeHX{&8yVg#J^uw4^In&Fif0=y-fu@Ax91MX>*2$TJ@6et5(s z2vk)G>k{7_NIsTZ_MzM3R7a9+bsBV;wn^f?&kAF z{a6*S@!=2BBKPZG3i3t$zFDk&a})*)#I{iwY`jZe_<&=ov$)$Gm;O*(jjIcdN=#$) zm_qNx1P14u_qm8@3j3JL8H4kq(ZiyYX`jBOr>E~Hk_$Lx7}hN&=fD0*`gq`M_I!7C zz{A5MudS;~iRWj;-RUXSw;TxOW<*ZyLJdJdPDlvJJ1xc!M9y~>WGJ)Ik4L-etDiv! z^V1N{?RJ49ARuAS(_a%46HGCXzd{^A2p8YoUYeWlPH0?Zr=!gZ)|Wxc*vEaj+1Ahzdp8=D6dJs7&)_=Eg~sN2VK4 zfeM8Y3Mdol`%t>YqRikdg(I3-2_BLX6*;*nACboL!7P$fK{1p)tQDilL)ZRCH%D_+ zrr#V^GE0|YTv`t#V40?YRrPgsqHo?>1QAlWzp>7)G{(5-C$QB9!)a!w5c$dDFv3U; zg)y^>sTrQBIYROdiazUei6jyPsj(l<5&q*5A;P!p$2kabQUd_6T;A>o_+8{){Y_U} z`;hT*%qIs2+lw_Hl<%md=GY%%sk41+aa=cz7 zf7GyP+{qZl;2D;d)3V~(IAW9z&i`y~21a7GB3b%?(}7rWdHgeeXjEt<5RKE+rX>z_t9h*T;-n~CnRC-3> zCnoQ3(GWzoI~@Urs2NMEWI!8%Xl52eY3e?P&D^3)i?L;zj(B^tVeFoR4 z|E(n1gyily zJ;3XM@~kHpI-e0a_(`W7ENMz?E9tESl@u(@%>}<20J*c}YBR~@BA$1XN2p69pQQGY zh9N=Yf9nei%|>etS{CP*mQp2WP}Btl1@*r>QC!N9OV05UaTtGn15KTh6zH4kr>%vD z;lrKQpstnR24}u)`2VT7!j$RK2}e}el?WC)YUFNFio*2xFa@2^GhK;2;3?{% zU+G-vdN49F5>qLYBk%e3`492sUcF`x4h4lucu6;jn1B1$YMXr~69dC9w<=N&Y~$I( zOGghn3}<_n9@QvvMm8{%X+}Y~?>abZ*X*|;cX9-)RrLM+z{TV%3GR8KScpk+)$T?{ zuBe~q%jrr>rg6JVh=Vl_90!|{l2ZSN_vJFNiMM%zGULg4tXL(Vq3dfskxg}ux^~TA z86%=yoo6tr74LfibYxQ8lr2&XfG#vFYZoCuDp5HW!YM$H>SO6r$%zvFKs%tQ}QRRn0#-=Gi?lq?_DW*m*Ym5Ljz3mo*tK zOe74>M^({qNmW-@w<>uFL_!UQdw=c7C}RR#WGc;U%)LiP$X7tW>kG!Ay1BV_8Liye zV&UM}Y&Yu*m9?47wWTkqcnJ@4fO@?tp5DLN^0j$i?!0jRxac?H&Z^C1dkQHIW;P`( z_Sf;|X2b$vuRHJ;mG+Pl8;O55q;n##n_e{tMuo(NhB`f}6YRH+E`VE&IoiI<;or;j zt4PsJs6Z(<&krfKW5n`)B#|5@oQs{yFb9SVT68q~-X0U{)f+zw#&8R58!+O^N6Ax} zC}nbd60w`F1OeQ3l7nRJ+e$QThW6z|>+P`G=OGfZE$BLOY}u#mwd`J|5mvm&46>3^ zk_5&5ZE~MLQ?Rk?#xEl6i|>tCRQilqC8KjdxF$}~m$wqNy{Hy1A~;7SQjLm|D8oUK zZ==v?))y);nJXxbr;;2Evn@&d-P+ok`FpCM_kNJr)R>TxQY_Sa-^QPS`vyI&NUd<` zrI%-wbUw?Waz4vN+NR&54p+e4dY{E1`6ypcaGFgTWHIPKf5IR~T1;C+aUc{cDXPdGj1i zlV!UiEji?ZeEP`UhUkOg&8$3CfP1VADgBNMX@7rzB_W&cL^k*5F--u?0A25w>e0(- z!?Q|(HJ4#C$?nluVL7(y_eAqqwbO>AkUCFD9hSQ%if2Yt`mR~mcblKr*xFmkelckyRvB=hQzkTxcl3Ym)kuXk?iL1p7tO&~#X4Mn6H#(((FOk^Kp{nafJ6`>0Cc z7jRg|^}$ro!PD&tmMdxu*f{q1*zPsUCf_IM`R)w$Et7z0Xv|SY$|knr)5>wS8Rdg> zKuv*^|1SS6>jQ}*!(z4(O18|UDH-I~n&{eYEFxBT-Xmo$nTp$t={On?CXP>?t(K!H3=M&ID>kJ< z1$3jMqco~G0M>MjF9(wp-Y1K7bRr=V55(Eo*^_&}e?xUy)Av5p1V@J{$03EzKx9lz zHoe|1tuX8jd`U$KlT`U3MO|odvRKEHnvxRvX24xJXhB1X3y=XL$?512y1BVs;p5?H zUSD6EB1zyi;~L|h^@X8+iF!}I$SExR95yzl*qkXD9(GYPR~w75o2n+(!T90BhsO&z zj13`4Xaq^`r=g~1v+K@449lB``u>`8?*dLH4xIY0DwIk*@E#sT6)5-?F<+(S1Bf4B zLt+YNV__Mcnw+FuZZPd#@w+?iP&Jkc54+fY=O!vD8n8K$#q(_1;r5va;O3IIO;EXT z9I&#oBK>rGa=*7&XB5E1Ltlso3O%e@DrnG-pLjjdy zF55q23 z)Idc{MfFEApOqGbL`=)W!*jUdwUwu#q2U^T;6v_1h4&f+;my7-2z)vcpPifYOF9fi zL+u;9yu9q2uhQDR|FhLAQ!$rE*PzfZ>cGFYkI$r9o6l=I`?Ii+I>j**4HXyI|Br!} zH!Yvv(Xba0705nk*Cu`;GL^hkF&-nkQl(X~$Z6c}qWx#Hdvw9+9WJonnTee}K3~Yg zZmcKZ*~HP#j+J)YKozrM2BN5%C)8#Q?~kDKy+1#blM{#cqN7#`sy>L)hTh!VY=)s? zt4@uKjKoGqlK_5Zddszvu(GjDeQI}s*Mx?KzR~Q@my>zIrRT^YXsAU2>Q&m&adDVR zYHCyuhb4(nXiN<0&e_=pesH+Ae0w9i-`VL(OGP4;c;L-I41UE_uAr7JcUH6|nhY3? zLe1N|-QnhN=D(_{sxVY65-wT}j(RGUx#R&JAaQIC0|UcBTx={xTx_f=F)^`er`tZp z$5A!ven}i60@)(@M9S(S>8K$rGTzD{H@Bh7i4r5B>;B{sv@l*bsGecY3qroo+ zlX-$)ek)%d%~fz_W@etm*rPeJfHZedq>NzKB4cA?@t;3`KKSmi;+9{2bl(qy?FT&F z90{5I3?3*gElm>(c%pxPd>kCvzrslhwFqMV+GtqkMMg$epOKbk4E8!-@7USdiDO`3 zNdNA%(R?MMCL#k7h<3qQrGW0bT>n;C`;{Xwe)ng~6lEKqii!#e1;zJm&W$uRH651f zKRpi&4h|xtqTXLzU11;>(&l$1npDi`EX~iSnfCY_1e~q56(}kyau5;{>MSlT37oi> zfZtbPhS%07X9tHL{#SdWLRRr8wr}^qBP$smL`cGXu{}3>k6#sIu_ap%T0Ki9CRhfDzv(Wzo4El7H literal 0 HcmV?d00001 diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7d1131218a..53b767525f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -86,7 +86,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png"); _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } From 9e31024a0e3839756a9bd333cd1121dbbe441120 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 13:16:41 -0700 Subject: [PATCH 39/85] merged with new stuff too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 53b767525f..6eec65a7d4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; +static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { @@ -95,7 +96,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 796292dd4c6efffedff555f5756d8aaaca705f0d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 13:21:13 -0700 Subject: [PATCH 40/85] Fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 3f175ffcc7..b0aa94b884 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -827,6 +827,9 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } glm::vec2 dimensions = overlay->getSize(); From 811be15bbcd03fe757f02a2003e9e3aeccca5b11 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 14:02:19 -0700 Subject: [PATCH 41/85] Another attmept to fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b0aa94b884..40ab4309b5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -822,14 +822,14 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } if (!overlay) { return PointerEvent(); } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); - if (getOverlayType(overlayID) == "web3d") { - overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); - } glm::vec2 dimensions = overlay->getSize(); From e7c6ba9ae2a4183d32d9542d6236adb1977cf0f5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 14:44:45 -0700 Subject: [PATCH 42/85] unused var --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6eec65a7d4..83c86bfd82 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -37,7 +37,6 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; -static const uint32_t RIGHT_HAND_HW_ID = 2; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims From 223ed5b59a11a41890b89e234bb6015775e96513 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 14:11:45 -0700 Subject: [PATCH 43/85] Hack to mod tablet orientation when clicking context overlay --- .../src/ui/overlays/ContextOverlayInterface.cpp | 13 +++++++++++++ interface/src/ui/overlays/ContextOverlayInterface.h | 2 ++ scripts/system/tablet-ui/tabletUI.js | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83c86bfd82..6069ecf6e4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,6 +33,18 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { + if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { + QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); + QVariantMap props; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); + props.insert("position", vec3toVariant(position)); + props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + qApp->getOverlays().editOverlay(tabletFrameID, props); + _contextOverlayJustClicked = false; + } + }); } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; @@ -149,6 +161,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); + _contextOverlayJustClicked = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f06f85eab7..812914a82e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -19,6 +19,7 @@ #include #include #include +#include "avatar/AvatarManager.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" @@ -66,6 +67,7 @@ private: bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; + bool _contextOverlayJustClicked { false }; void openMarketplace(); }; diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 9188f39a2e..257a56bf09 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -104,7 +104,6 @@ function showTabletUI() { checkTablet() - gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -123,6 +122,7 @@ Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); } + gTablet.tabletShown = true; } function hideTabletUI() { From c22798a8e64adfbd0682b5be512b8c06a5b7dd25 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 17:16:07 -0700 Subject: [PATCH 44/85] Offset tablet position to left of screen per feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6069ecf6e4..a9efc8e6f6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -38,9 +38,8 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); - props.insert("position", vec3toVariant(position)); - props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } From 256b7efdd2f9bdef50e1dad58550e64645f050b8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:06:54 -0700 Subject: [PATCH 45/85] Handle the case when the camera is inside the bounding box of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index a9efc8e6f6..cc4f645c0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -14,6 +14,9 @@ #include +static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable @@ -38,8 +41,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); - props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); + glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } @@ -68,7 +72,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -84,7 +88,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setDimensions(dimensions); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); @@ -105,15 +109,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { + if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box, position the context overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; @@ -122,6 +122,14 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); From c71dca5b4b44e2c6764094e727c0b362ba42155a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:15:44 -0700 Subject: [PATCH 46/85] Missed a magic number --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cc4f645c0a..95bdd7186c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -53,6 +53,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims @@ -111,7 +112,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec2 contextOverlayDimensions; if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 7702bfceaa49cc555d39b1800ba7f9adea593a27 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 16:12:32 -0700 Subject: [PATCH 47/85] Big cleanup and preparation; Actually fix desktop tablet --- .../ui/overlays/ContextOverlayInterface.cpp | 138 +++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 3 + interface/src/ui/overlays/Planar3DOverlay.h | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.h | 2 +- 5 files changed, 92 insertions(+), 55 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 95bdd7186c..18cb41fda5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,9 +39,9 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); - QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + QVariantMap props; props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); @@ -68,32 +68,57 @@ static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - glm::vec3 dimensions = entityProperties.getDimensions(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); - } - if (entityProperties.getMarketplaceID().length() != 0) { + if (contextOverlayFilterPassed(entityItemID)) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + + // Add all necessary variables to the stack + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 entityDimensions = entityProperties.getDimensions(); + glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; + + // Draw the outline overlay + // This also gives us the centerpoint of the entity even if + // the the entity doesn't use the default registration point. + glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + + // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); + // Here, we determine the position and dimensions of the Context Overlay. + if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box... + // ...position the Context Overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { + // Else if the entity is too close to the camera... + // ...rotate the Context Overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + // Else, place the Context Overlay some offset away from the entity's bounding + // box in the direction of the camera. + auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(dimensions); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); + // Finally, setup and draw the Context Overlay if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); @@ -106,36 +131,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(bbPosition, cameraPosition); - glm::vec3 contextOverlayPosition; - glm::vec2 contextOverlayDimensions; - if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { - // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { - // If the entity is too close to the camera, rotate the context overlay to the right of the entity. - // This makes it easy to inspect things you're holding. - float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; - if (event.getID() == LEFT_HAND_HW_ID) { - offsetAngle *= -1; - } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } } else { @@ -144,17 +144,51 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return false; } +bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + return (entityProperties.getMarketplaceID().length() != 0); +} + +glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 bbPosition = entityProperties.getPosition(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + // Setup and draw the bounding box around the entity + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setParentID(entityItemID); + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(bbPosition); + _bbOverlay->setVisible(true); + // This returned position should always be the center of the entity + // even if the registration point isn't the default. + return bbPosition; +} + bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); + // Destroy the Context Overlay + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + // Destroy the outline overlay + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _bbOverlay = NULL; + _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 812914a82e..623eb85401 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -70,6 +70,9 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 2ccf2c4513..8127d4ebb9 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,7 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; - glm::vec2 getSize() const { return _dimensions; }; + virtual glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..1dd3389352 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -613,7 +613,7 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { } } -glm::vec2 Web3DOverlay::getSize() { +glm::vec2 Web3DOverlay::getSize() const { return _resolution / _dpi * INCHES_TO_METERS * getDimensions(); }; diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 1e3706ed25..3d2f9dd514 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -50,7 +50,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - glm::vec2 getSize(); + glm::vec2 getSize() const override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; From 9020607ccfa75fd5ded41d1a08079c299966b20f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 17:54:46 -0700 Subject: [PATCH 48/85] First pass --- interface/src/Application.cpp | 6 + .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 12 + libraries/render/src/render/Args.h | 6 + scripts/system/marketplaces/marketplaces.js | 206 +++++++++--------- 5 files changed, 133 insertions(+), 103 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 699ddfcb2d..978f20d743 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5393,6 +5393,12 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } renderArgs->_debugFlags = renderDebugFlags; //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); + + RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = static_cast(renderOutlineFlags | + static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..7c768dbfa0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -37,7 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) - Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) + Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -53,6 +54,8 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setEnabled(bool enabled) { _enabled = enabled; } bool getEnabled() { return _enabled; } + bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } + void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -70,6 +73,7 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9884debcce..52308499d7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,6 +372,18 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } + bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); + if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); + gpu::Batch& batch = *args->_batch; + bool success; + auto shapeTransform = getTransformToCenter(success); + if (success) { + batch.setModelTransform(shapeTransform); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(args, batch, yellowColor); + } + } + if (!hasModel() || (_model && _model->didVisualGeometryRequestFail())) { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 449a3ac22b..f53454cf75 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -63,6 +63,11 @@ namespace render { public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; + enum OutlineFlags { + RENDER_OUTLINE_NONE = 0, + RENDER_OUTLINE_WIREFRAMES = 1, + RENDER_OUTLINE_SHADER = 2 + }; enum DebugFlags { RENDER_DEBUG_NONE = 0, RENDER_DEBUG_HULLS = 1 @@ -112,6 +117,7 @@ namespace render { int _boundaryLevelAdjust { 0 }; RenderMode _renderMode { DEFAULT_RENDER_MODE }; DisplayMode _displayMode { MONO }; + OutlineFlags _outlineFlags{ RENDER_OUTLINE_NONE }; DebugFlags _debugFlags { RENDER_DEBUG_NONE }; gpu::Batch* _batch = nullptr; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3be8143830..462beac538 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -11,135 +11,137 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE -Script.include("../libraries/WebTablet.js"); + Script.include("../libraries/WebTablet.js"); -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. -var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. + var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); + var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); -var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// Event bridge messages. -var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; -var CLARA_IO_STATUS = "CLARA.IO STATUS"; -var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; -var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; -var GOTO_DIRECTORY = "GOTO_DIRECTORY"; -var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; -var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; -var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + // Event bridge messages. + var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; + var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; + var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; + var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; -var CLARA_DOWNLOAD_TITLE = "Preparing Download"; -var messageBox = null; -var isDownloadBeingCancelled = false; + var CLARA_DOWNLOAD_TITLE = "Preparing Download"; + var messageBox = null; + var isDownloadBeingCancelled = false; -var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel -var NO_BUTTON = 0; // QMessageBox::NoButton + var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel + var NO_BUTTON = 0; // QMessageBox::NoButton -var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; + var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; -function onMessageBoxClosed(id, button) { - if (id === messageBox && button === CANCEL_BUTTON) { - isDownloadBeingCancelled = true; - messageBox = null; - tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; + tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + } } -} -Window.messageBoxClosed.connect(onMessageBoxClosed); + Window.messageBoxClosed.connect(onMessageBoxClosed); -var onMarketplaceScreen = false; + var onMarketplaceScreen = false; -function showMarketplace() { - UserActivityLogger.openedMarketplace(); - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - tablet.webEventReceived.connect(function (message) { + function showMarketplace() { + UserActivityLogger.openedMarketplace(); + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.webEventReceived.connect(function (message) { - if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } + if (message === GOTO_DIRECTORY) { + tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + } - if (message === QUERY_CAN_WRITE_ASSETS) { - tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } + if (message === QUERY_CAN_WRITE_ASSETS) { + tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } + if (message === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + } - if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } return; } - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; } - return; - } - if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; } - return; - } + }); + } - if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var marketplaceButton = tablet.addButton({ + icon: "icons/tablet-icons/market-i.svg", + activeIcon: "icons/tablet-icons/market-a.svg", + text: "MARKET", + sortOrder: 9 }); -} -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var marketplaceButton = tablet.addButton({ - icon: "icons/tablet-icons/market-i.svg", - activeIcon: "icons/tablet-icons/market-a.svg", - text: "MARKET", - sortOrder: 9 -}); - -function onCanWriteAssetsChanged() { - var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - tablet.emitScriptEvent(message); -} - -function onClick() { - if (onMarketplaceScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); - showMarketplace(); + function onCanWriteAssetsChanged() { + var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + tablet.emitScriptEvent(message); } -} -function onScreenChanged(type, url) { - onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL - // for toolbar mode: change button to active when window is first openend, false otherwise. - marketplaceButton.editProperties({isActive: onMarketplaceScreen}); -} - -marketplaceButton.clicked.connect(onClick); -tablet.screenChanged.connect(onScreenChanged); -Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); - -Script.scriptEnding.connect(function () { - if (onMarketplaceScreen) { - tablet.gotoHomeScreen(); + function onClick() { + if (onMarketplaceScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + ContextOverlay.isInMarketplaceInspectionMode = false; + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + showMarketplace(); + ContextOverlay.isInMarketplaceInspectionMode = true; + } } - tablet.removeButton(marketplaceButton); - tablet.screenChanged.disconnect(onScreenChanged); - Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); -}); + + function onScreenChanged(type, url) { + onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL + // for toolbar mode: change button to active when window is first openend, false otherwise. + marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + } + + marketplaceButton.clicked.connect(onClick); + tablet.screenChanged.connect(onScreenChanged); + Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); + + Script.scriptEnding.connect(function () { + if (onMarketplaceScreen) { + tablet.gotoHomeScreen(); + } + tablet.removeButton(marketplaceButton); + tablet.screenChanged.disconnect(onScreenChanged); + Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); + }); }()); // END LOCAL_SCOPE From 9147bc21d0a3dadebf61fcf4220899fe45511c49 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 11:41:27 -0700 Subject: [PATCH 49/85] Getting there. --- .../ui/overlays/ContextOverlayInterface.cpp | 76 ++++++++----------- .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 20 +++++ libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityItemProperties.cpp | 6 ++ libraries/entities/src/EntityItemProperties.h | 1 + .../src/EntityItemPropertiesDefaults.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/marketplaces/marketplaces.js | 7 +- 12 files changed, 76 insertions(+), 53 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..2562d7aa76 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,6 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -76,13 +78,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 entityDimensions = entityProperties.getDimensions(); - glm::vec3 contextOverlayPosition; + glm::vec3 entityPosition = entityProperties.getPosition(); + glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec2 contextOverlayDimensions; - // Draw the outline overlay - // This also gives us the centerpoint of the entity even if - // the the entity doesn't use the default registration point. - glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + // Update the position of the overlay if the registration point of the entity + // isn't default + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -91,7 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& setCurrentEntityWithContextOverlay(entityItemID); // Here, we determine the position and dimensions of the Context Overlay. - if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + if (boundingBox.contains(cameraPosition)) { // If the camera is inside the box... // ...position the Context Overlay 1 meter in front of the camera. contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); @@ -104,17 +111,17 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // Else, place the Context Overlay some offset away from the entity's bounding // box in the direction of the camera. - auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); + float distance; + BoxFace face; + glm::vec3 normal; + bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } @@ -149,33 +156,6 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent return (entityProperties.getMarketplaceID().length() != 0); } -glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); - } - - // Setup and draw the bounding box around the entity - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); - } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); - // This returned position should always be the center of the entity - // even if the registration point isn't the default. - return bbPosition; -} - bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; @@ -185,10 +165,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - // Destroy the outline overlay - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _bbOverlay = NULL; - _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; @@ -227,6 +203,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } +void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + //if (contextOverlayFilterPassed(entityID)) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + //} +} + +void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 7c768dbfa0..c15e4854bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -23,7 +23,6 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -44,9 +43,7 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; - OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; - std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); @@ -64,6 +61,8 @@ public slots: void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; @@ -76,7 +75,6 @@ private: bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); - glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 52308499d7..a03360a0e5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,8 +372,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); - if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; bool success; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..82ddb9c90d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; requestedProperties += PROP_MARKETPLACE_ID; + requestedProperties += PROP_SHOULD_HIGHLIGHT; requestedProperties += PROP_NAME; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; @@ -277,6 +278,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); @@ -828,6 +830,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID); } + if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT) { + READ_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); + } + READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); @@ -2807,6 +2813,20 @@ void EntityItem::setMarketplaceID(const QString& value) { }); } +bool EntityItem::getShouldHighlight() const { + bool result; + withReadLock([&] { + result = _shouldHighlight; + }); + return result; +} + +void EntityItem::setShouldHighlight(const bool value) { + withWriteLock([&] { + _shouldHighlight = value; + }); +} + uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 92c83651aa..36ac6ba1cc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -316,6 +316,9 @@ public: QString getMarketplaceID() const; void setMarketplaceID(const QString& value); + bool getShouldHighlight() const; + void setShouldHighlight(const bool value); + // TODO: get rid of users of getRadius()... float getRadius() const; @@ -532,6 +535,7 @@ protected: QString _userData; SimulationOwner _simulationOwner; QString _marketplaceID; + bool _shouldHighlight { false }; QString _name; QString _href; //Hyperlink href QString _description; //Hyperlink description diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a207902789..aa7075f5e0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -289,6 +289,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); + CHECK_PROPERTY_CHANGE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); @@ -406,6 +407,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); @@ -982,6 +984,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); + ADD_PROPERTY_TO_MAP(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); @@ -1334,6 +1337,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, properties.getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -1632,6 +1636,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); @@ -1746,6 +1751,7 @@ void EntityItemProperties::markAllChanged() { //_alphaFinishChanged = true; _marketplaceIDChanged = true; + _shouldHighlightChanged = true; _keyLight.markAllChanged(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b526ac663c..916532b47c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -170,6 +170,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); + DEFINE_PROPERTY_REF(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool, ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index d52c5d9aab..43d0e33ba6 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -27,6 +27,7 @@ const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f); const bool ENTITY_ITEM_DEFAULT_LOCKED = false; const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString(""); +const bool ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT = false; const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid(); const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b3cfc143c2..9600d0d4fe 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -78,6 +78,7 @@ enum EntityPropertyList { PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_MARKETPLACE_ID, // all entities + PROP_SHOULD_HIGHLIGHT, // all entities PROP_ACCELERATION, // all entities PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID PROP_NAME, // all entities diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 240697d890..d2500196d9 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -62,7 +62,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_BULLET_DYNAMICS; + return VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 848bfd97cf..cb3db791b4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -218,6 +218,7 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; +const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 462beac538..418cf61834 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -116,12 +116,10 @@ if (onMarketplaceScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); - ContextOverlay.isInMarketplaceInspectionMode = false; } else { var entity = HMD.tabletID; Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); showMarketplace(); - ContextOverlay.isInMarketplaceInspectionMode = true; } } @@ -129,6 +127,11 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + if (onMarketplaceScreen) { + ContextOverlay.isInMarketplaceInspectionMode = true; + } else { + ContextOverlay.isInMarketplaceInspectionMode = false; + } } marketplaceButton.clicked.connect(onClick); From 24ddbb4b22353606fb48be2bcae53a9aeed93fe9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:08:55 -0700 Subject: [PATCH 50/85] Silly bug preventing the whole thing from working --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 978f20d743..27848a1193 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5396,9 +5396,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = static_cast(renderOutlineFlags | - static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; } + renderArgs->_outlineFlags = renderOutlineFlags; } } From 62f6c10b2b04306744b0bb87697997d4e8f77258 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:45:10 -0700 Subject: [PATCH 51/85] Renaming things; bugfixes; improvements --- interface/src/Application.cpp | 6 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 30 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 +++---- .../src/EntityTreeRenderer.cpp | 2 +- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27848a1193..d9b014d48d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1350,17 +1350,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 2562d7aa76..3fdd005672 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,8 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverLeaveEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -89,6 +89,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID @@ -159,6 +162,8 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -174,7 +179,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } -void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); @@ -183,7 +188,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co } } -void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -193,7 +198,7 @@ void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -203,16 +208,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - //if (contextOverlayFilterPassed(entityID)) { +void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (contextOverlayFilterPassed(entityID)) { qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); - //} + } } -void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (_currentEntityWithContextOverlay != entityID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + } } static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; @@ -227,5 +234,6 @@ void ContextOverlayInterface::openMarketplace() { QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); + _isInMarketplaceInspectionMode = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c15e4854bd..fc539661c4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,11 +58,11 @@ public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); - void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); - void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a8eca41077..d55352d31e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -675,7 +675,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = false; // for mouse moves we do not do precision picking + bool precisionPicking = true; // for mouse moves we do precision picking RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { From 65c28ca82123234ab22057668925b45e9b581236 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 13:17:51 -0700 Subject: [PATCH 52/85] Bugfixes --- .../src/ui/overlays/ContextOverlayInterface.cpp | 14 +++++++++++--- scripts/system/marketplaces/marketplaces.js | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3fdd005672..4ecf919cfe 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -97,6 +97,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); @@ -162,8 +168,10 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -222,7 +230,7 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; +static const QString MARKETPLACE_BASE_URL = "https://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 418cf61834..7b25589e92 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -127,7 +127,7 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); - if (onMarketplaceScreen) { + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { ContextOverlay.isInMarketplaceInspectionMode = false; From 3a05219c59ae3de69f7fe81e5883d2073f63ff97 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 14:10:41 -0700 Subject: [PATCH 53/85] Closer still --- .../ui/overlays/ContextOverlayInterface.cpp | 25 ++++--- .../src/ui/overlays/ContextOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 73 +++++++++++++------ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 4ecf919cfe..aa162e6101 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -89,8 +89,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + enableEntityHighlight(entityItemID); AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); @@ -99,8 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } // Update the cached "Current Entity with Context Overlay" variable @@ -169,8 +167,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); @@ -218,15 +215,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { if (contextOverlayFilterPassed(entityID)) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { if (_currentEntityWithContextOverlay != entityID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + disableEntityHighlight(entityID); } } @@ -245,3 +240,13 @@ void ContextOverlayInterface::openMarketplace() { _isInMarketplaceInspectionMode = true; } } + +void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); +} + +void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index fc539661c4..4b9c67c5d1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -63,6 +63,7 @@ public slots: void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; @@ -74,7 +75,8 @@ private: void openMarketplace(); bool _isInMarketplaceInspectionMode { false }; - bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + void enableEntityHighlight(const EntityItemID& entityItemID); + void disableEntityHighlight(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3268031f8b..247246b11d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var potentialEntityWithContextOverlay = false; +var hoveredEntityID = false; +var contextOverlayTimer = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2231,28 +2232,54 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && !entityWithContextOverlay) { - Script.setTimeout(function () { - if (rayPickInfo.entityID === potentialEntityWithContextOverlay && - !entityWithContextOverlay - && contextualHand !== -1) { - var pointerEvent = { - type: "Move", - id: contextualHand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; - } + if (rayPickInfo.entityID) { + if (hoveredEntityID !== rayPickInfo.entityID) { + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } - }, 500); - contextualHand = this.hand; - potentialEntityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = rayPickInfo.entityID; + Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); + } + + if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { + Script.clearTimeout(contextOverlayTimer); + } + + // If we already have a context overlay, we don't want to move it to + // another entity while we're searching. + if (!entityWithContextOverlay) { + contextOverlayTimer = Script.setTimeout(function () { + if (rayPickInfo.entityID === hoveredEntityID && + !entityWithContextOverlay + && contextualHand !== -1) { + var pointerEvent = { + type: "Move", + id: contextualHand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = false; + } + } + contextOverlayTimer = false; + }, 500); + contextualHand = this.hand; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3503,7 +3530,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; + hoveredEntityID = false; } if (isInEditMode()) { From 17c7e38fcecaf6b6c463d4a4da59724c007d1927 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:02:52 -0700 Subject: [PATCH 54/85] Bugfixes? --- .../system/controllers/handControllerGrab.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 247246b11d..37712ff90c 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2201,6 +2201,10 @@ function MyController(hand) { this.searchExit = function () { contextualHand = -1; + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + } + hoveredEntityID = false; }; this.search = function(deltaTime, timestamp) { @@ -2486,8 +2490,10 @@ function MyController(hand) { button: "None" }; + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); this.hoverEntity = entity; - Entities.sendHoverEnterEntity(entity, pointerEvent); + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); } // send mouse events for button highlights and tooltips. @@ -2551,8 +2557,11 @@ function MyController(hand) { button: "None" }; - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + if (this.hoverOverlay !== overlay) { + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(this.hoverOverlay, pointerEvent); + } // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || @@ -3527,10 +3536,13 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - hoveredEntityID = false; } if (isInEditMode()) { From 1336a59b6c35aaf9cbe21f259460b4e6d123e007 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:14:58 -0700 Subject: [PATCH 55/85] Stupid bracket --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 37712ff90c..a3bf6d4bd8 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2490,10 +2490,11 @@ function MyController(hand) { button: "None" }; - if (this.hoverEntity !== entity) { - Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); - this.hoverEntity = entity; - Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = entity; + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + } } // send mouse events for button highlights and tooltips. From 39cb0bc357e881f2cb7cdb6fff998205dd28a791 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:12:54 -0700 Subject: [PATCH 56/85] WHYYYY DOESN'T THIS WORK --- .../ui/overlays/ContextOverlayInterface.cpp | 12 +++-- .../system/controllers/handControllerGrab.js | 44 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index aa162e6101..e6d1af0af5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -242,11 +242,15 @@ void ContextOverlayInterface::openMarketplace() { } void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + } } void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + } } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index a3bf6d4bd8..82691003e2 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2236,18 +2236,22 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; if (rayPickInfo.entityID) { + print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { - pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } if (hoveredEntityID) { Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } @@ -2255,17 +2259,14 @@ function MyController(hand) { Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); } - if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { - Script.clearTimeout(contextOverlayTimer); - } - // If we already have a context overlay, we don't want to move it to // another entity while we're searching. - if (!entityWithContextOverlay) { + if (!entityWithContextOverlay && !contextOverlayTimer) { contextOverlayTimer = Script.setTimeout(function () { if (rayPickInfo.entityID === hoveredEntityID && - !entityWithContextOverlay - && contextualHand !== -1) { + !entityWithContextOverlay && + contextualHand !== -1 && + contextOverlayTimer) { var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2284,6 +2285,15 @@ function MyController(hand) { }, 500); contextualHand = this.hand; } + } else { + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); From 8a7561d61ba30391147691349c3ecab9416cf46a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:35:55 -0700 Subject: [PATCH 57/85] IT'S WORKING SOB SOB SOB --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++---- scripts/system/controllers/handControllerGrab.js | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e6d1af0af5..668766abbf 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -153,7 +153,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return true; } } else { - return destroyContextOverlay(entityItemID, event); + if (!_currentEntityWithContextOverlay.isNull()) { + return destroyContextOverlay(_currentEntityWithContextOverlay, event); + } + return false; } return false; } @@ -166,9 +169,7 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - disableEntityHighlight(_currentEntityWithContextOverlay); - } + disableEntityHighlight(entityItemID); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 82691003e2..bd8ab26506 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2246,7 +2246,6 @@ function MyController(hand) { button: "None" }; if (rayPickInfo.entityID) { - print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { if (contextOverlayTimer) { Script.clearTimeout(contextOverlayTimer); From bb7110d81b47957a6757b801ccf3af39c6f2fc2a Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 30 Jul 2017 19:22:54 +0200 Subject: [PATCH 58/85] Desktop Running scripts window now closed and as soon as Tablet opened, it will show Running Script. TODO: make Tablet opens automatically --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++++++++++++- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..9ed87ea337 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -208,12 +208,13 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _toolbarMode = toolbarMode; + auto offscreenUi = DependencyManager::get(); + if (toolbarMode) { removeButtonsFromHomeScreen(); addButtonsToToolbar(); // create new desktop window - auto offscreenUi = DependencyManager::get(); auto tabletRootWindow = new TabletRootWindow(); tabletRootWindow->initQml(QVariantMap()); auto quickItem = tabletRootWindow->asQuickItem(); @@ -230,16 +231,37 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { removeButtonsFromToolbar(); addButtonsToHomeScreen(); + //check if running scripts opened and save it for reopen in Tablet + if (offscreenUi->isVisible("RunningScripts")) { + offscreenUi->hide("RunningScripts"); + _showRunningScripts = true; + } // destroy desktop window if (_desktopWindow) { _desktopWindow->deleteLater(); _desktopWindow = nullptr; } } + loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); + + //connect to Tablet shown signal to open running scripts + if (_showRunningScripts) { + QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); + //qApp->toggleTabletUI(true); + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + } } + static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (buttonProxy == NULL){ diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index e61398585e..82b7ad52b6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -252,6 +252,7 @@ protected: enum class State { Uninitialized, Home, Web, Menu, QML }; State _state { State::Uninitialized }; bool _landscape { false }; + bool _showRunningScripts { false }; }; Q_DECLARE_METATYPE(TabletProxy*); From b5f5a037f007e91b22d27cb39507e347637e7bed Mon Sep 17 00:00:00 2001 From: humbletim Date: Sun, 30 Jul 2017 16:25:56 -0400 Subject: [PATCH 59/85] remove "grabbable: true" property from attachment Overlays --- .../dist/app-doppleganger-marketplace.js | 3 +-- .../doppleganger-attachments/doppleganger-attachments.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js index 6abd3a0fc5..c2cf2a2353 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -1075,7 +1075,7 @@ dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = __webpack_require__(1), @@ -1196,7 +1196,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index 5dee264fae..7526f56511 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -6,7 +6,7 @@ // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -127,7 +127,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, From e30e0b1a5f43f92c6ef0cc7e6117da209a6f3812 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 31 Jul 2017 16:45:21 +0200 Subject: [PATCH 60/85] Make sure Tablet will be shown with opened running scripts dialog. Fixed potential stack corruption --- .../resources/qml/hifi/tablet/TabletRoot.qml | 8 ++++- .../ui/src/ui/TabletScriptingInterface.cpp | 35 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index faa4013bce..c7df6ac64f 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -202,5 +202,11 @@ Item { width: 480 height: 706 - function setShown(value) {} + function setShown(value) { + if (value === true) { + HMD.openTablet() + } else { + HMD.closeTablet() + } + } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 9ed87ea337..74150a5a4b 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -245,20 +245,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); - - //connect to Tablet shown signal to open running scripts - if (_showRunningScripts) { - QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); - //qApp->toggleTabletUI(true); - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); - if (_tabletShown && _showRunningScripts) { - _showRunningScripts = false; - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); - } } @@ -384,9 +370,26 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { }); if (_initialScreen) { - pushOntoStack(_initialPath); + if (!_showRunningScripts) { + pushOntoStack(_initialPath); + } _initialScreen = false; } + + if (_showRunningScripts) { + _showRunningScripts = false; + //connect to Tablet shown signal to open running scripts + //we cant just call pushOnStack here since RunningScripts draws incorrectly + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); //not needed anymore, disconnect + if (_tabletShown) { + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + //show Tablet. Make sure, setShown implemented in TabletRoot.qml + QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); + } } else { removeButtonsFromHomeScreen(); _state = State::Uninitialized; @@ -523,7 +526,7 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } - return root; + return (root != nullptr); } void TabletProxy::popFromStack() { From 43c8079f0383c8f92ca44d1d21773d87a6c14221 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 13:55:43 -0700 Subject: [PATCH 61/85] temporary inspectionMode setting to turn ContextOverlays completely off/on --- .../src/ui/overlays/ContextOverlayInterface.cpp | 15 +++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..0235a8d4f4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -34,6 +34,11 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; + // initially, set _enabled to match the switch. Later we enable/disable via the getter/setters + // if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all + // the time after getting edge highlighting, etc... + _enabled = _settingSwitch.get(); + auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { @@ -66,6 +71,16 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; +void ContextOverlayInterface::setEnabled(bool enabled) { + // only enable/disable if the setting in 'on'. If it is 'off', + // make sure _enabled is always false. + if (_settingSwitch.get()) { + _enabled = enabled; + } else { + _enabled = false; + } +} + bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..1ec3d3aefd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } - void setEnabled(bool enabled) { _enabled = enabled; } + void setEnabled(bool enabled); bool getEnabled() { return _enabled; } public slots: @@ -73,6 +73,8 @@ private: bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); + + Setting::Handle _settingSwitch { "inspectionMode", false }; }; #endif // hifi_ContextOverlayInterface_h From b371c875c47bf53a24857064c2dc5c3907fdbbc6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 14:46:26 -0700 Subject: [PATCH 62/85] undo tab mapping to bring up tablet, for now --- interface/resources/controllers/keyboardMouse.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index e3d2132e01..f377e02e5f 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -15,7 +15,7 @@ { "comment" : "Mouse turn need to be small continuous increments", "from": { "makeAxis" : [ [ "Keyboard.MouseMoveLeft" ], - [ "Keyboard.MouseMoveRight" ] + [ "Keyboard.MouseMoveRight" ] ] }, "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], @@ -31,8 +31,8 @@ { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", "from": { "makeAxis" : [ [ "Keyboard.TouchpadLeft" ], - [ "Keyboard.TouchpadRight" ] - ] + [ "Keyboard.TouchpadRight" ] + ] }, "when": [ "Application.InHMD", "Application.SnapTurn" ], "to": "Actions.StepYaw", @@ -131,6 +131,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } + { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } ] } From de97dfc9d74309f1ea34c5f7eb0e1e721dbcd26c Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 15:25:02 -0700 Subject: [PATCH 63/85] install the bundled vcredist_x64 instead of relying on DLLs being setup by cmake directly in the install folder --- cmake/templates/NSIS.template.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 5417220ef1..de63e164e6 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,9 +853,11 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" + ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - Delete "$INSTDIR\vcredist_x64.exe" + ;Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From ae31df426c32094f780abecbed1c09ec70e9f648 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:52:38 -0700 Subject: [PATCH 64/85] remove vcredist_x64 after installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index de63e164e6..2445933b52 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -857,7 +857,7 @@ Section "-Core installation" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - ;Delete "$INSTDIR\vcredist_x64.exe" + Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From bea91be521c8668333cf3778f04599c0c55af18a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 12:27:12 -0700 Subject: [PATCH 65/85] Fix for potential crash in JS console --- interface/src/ui/JSConsole.cpp | 69 ++++++++++++++++-------------- interface/src/ui/JSConsole.h | 10 ++--- interface/src/ui/TestingDialog.cpp | 8 ++-- interface/src/ui/TestingDialog.h | 2 +- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index a62fb2270b..8c1ff87d13 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "JSConsole.h" + #include #include #include @@ -20,7 +22,6 @@ #include #include "Application.h" -#include "JSConsole.h" #include "ScriptHighlighting.h" const int NO_CURRENT_HISTORY_COMMAND = -1; @@ -60,14 +61,12 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } -JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : +JSConsole::JSConsole(QWidget* parent, const QSharedPointer& scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), - _commandHistory(_readLines(_savedHistoryFilename)), - _ownScriptEngine(scriptEngine == NULL), - _scriptEngine(NULL) { + _commandHistory(_readLines(_savedHistoryFilename)) { _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -90,36 +89,37 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : } JSConsole::~JSConsole() { - disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); + if (_scriptEngine) { + disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + _scriptEngine.reset(); } delete _ui; } -void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { +void JSConsole::setScriptEngine(const QSharedPointer& scriptEngine) { if (_scriptEngine == scriptEngine && scriptEngine != NULL) { return; } if (_scriptEngine != NULL) { - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); - } + disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + _scriptEngine.reset(); } // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine - _ownScriptEngine = (scriptEngine == NULL); - _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; + if (scriptEngine.isNull()) { + _scriptEngine = QSharedPointer(DependencyManager::get()->loadScript(_consoleFileName, false), &QObject::deleteLater); + } else { + _scriptEngine = scriptEngine; + } - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); + connect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + connect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); } void JSConsole::executeCommand(const QString& command) { @@ -135,19 +135,22 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QFuture future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command); + QWeakPointer weakScriptEngine = _scriptEngine; + auto consoleFileName = _consoleFileName; + QFuture future = QtConcurrent::run([weakScriptEngine, consoleFileName, command]()->QScriptValue{ + QScriptValue result; + auto scriptEngine = weakScriptEngine.lock(); + if (scriptEngine) { + BLOCKING_INVOKE_METHOD(scriptEngine.data(), "evaluate", + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, command), + Q_ARG(const QString&, consoleFileName)); + } + return result; + }); _executeWatcher.setFuture(future); } -QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { - QScriptValue result; - BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate", - Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, command), - Q_ARG(const QString&, _consoleFileName)); - return result; -} - void JSConsole::commandFinished() { QScriptValue result = _executeWatcher.result(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 59280f65aa..5010b5b9ca 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "ui_console.h" #include "ScriptEngine.h" @@ -29,10 +30,10 @@ const int CONSOLE_HEIGHT = 200; class JSConsole : public QWidget { Q_OBJECT public: - JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL); + JSConsole(QWidget* parent, const QSharedPointer& scriptEngine = QSharedPointer()); ~JSConsole(); - void setScriptEngine(ScriptEngine* scriptEngine = NULL); + void setScriptEngine(const QSharedPointer& scriptEngine = QSharedPointer()); void clear(); public slots: @@ -58,17 +59,14 @@ private: void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); - QScriptValue executeCommandInWatcher(const QString& command); QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QString _savedHistoryFilename; QList _commandHistory; - // Keeps track if the script engine is created inside the JSConsole - bool _ownScriptEngine; QString _rootCommand; - ScriptEngine* _scriptEngine; + QSharedPointer _scriptEngine; static const QString _consoleFileName; }; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index f55eb63a5b..bba14cd5d7 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -24,12 +24,12 @@ TestingDialog::TestingDialog(QWidget* parent) : _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); auto _engines = DependencyManager::get(); - _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _engine.reset(_engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath)); _console->setScriptEngine(_engine); - connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); + connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } void TestingDialog::onTestingFinished(const QString& scriptPath) { - _engine = nullptr; - _console->setScriptEngine(nullptr); + _engine.reset(); + _console->setScriptEngine(); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index b90b8f2e99..055e43eaf7 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -29,7 +29,7 @@ public: private: std::unique_ptr _console; - ScriptEngine* _engine; + QSharedPointer _engine; }; #endif From 762b4098143b4fa68cf10e16e232ca5229fe6a7f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:20:33 -0700 Subject: [PATCH 66/85] unused variable --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cbe184f31e..b0fc826f0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -142,7 +142,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } From 588afaaa85861ea2f0ca70446cbc0d510d70c6a6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:59:42 -0700 Subject: [PATCH 67/85] unused variable... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index b0fc826f0a..e406d139d0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -57,7 +57,6 @@ ContextOverlayInterface::ContextOverlayInterface() { }); } -static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters From 106b0ad8bb7e1118d6abd2f0f7aa747588fedc3e Mon Sep 17 00:00:00 2001 From: seefo Date: Mon, 31 Jul 2017 14:37:40 -0700 Subject: [PATCH 68/85] Added Vive pucks as an input channel --- interface/resources/controllers/standard.json | 19 ++++++++++++++++++- interface/resources/controllers/vive.json | 19 ++++++++++++++++++- .../controllers/src/controllers/Actions.cpp | 17 +++++++++++++++++ .../controllers/src/controllers/Actions.h | 17 +++++++++++++++++ .../src/controllers/StandardController.cpp | 17 +++++++++++++++++ scripts/developer/tests/puck-attach.js | 6 +++--- scripts/developer/tests/viveTrackedObjects.js | 2 +- 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 166f1a6869..0d5c095585 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -109,6 +109,23 @@ { "from": "Standard.Head", "to": "Actions.Head" }, { "from": "Standard.LeftArm", "to": "Actions.LeftArm" }, - { "from": "Standard.RightArm", "to": "Actions.RightArm" } + { "from": "Standard.RightArm", "to": "Actions.RightArm" }, + + { "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" }, + { "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" }, + { "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" }, + { "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" }, + { "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" }, + { "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" }, + { "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" }, + { "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" }, + { "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" }, + { "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" }, + { "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" }, + { "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" }, + { "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" }, + { "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" }, + { "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" }, + { "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" } ] } diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 73ab5cb2ae..02fc09c815 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -77,6 +77,23 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, + + { "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" }, + { "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" }, + { "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" }, + { "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" }, + { "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" }, + { "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" }, + { "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" }, + { "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" }, + { "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" }, + { "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" }, + { "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" }, + { "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" }, + { "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" }, + { "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" }, + { "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" }, + { "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" } ] } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index b3c0ed3f05..d8dd7f5e35 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -101,6 +101,23 @@ namespace controller { makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"), makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"), + makePosePair(Action::TRACKED_OBJECT_00, "TrackedObject00"), + makePosePair(Action::TRACKED_OBJECT_01, "TrackedObject01"), + makePosePair(Action::TRACKED_OBJECT_02, "TrackedObject02"), + makePosePair(Action::TRACKED_OBJECT_03, "TrackedObject03"), + makePosePair(Action::TRACKED_OBJECT_04, "TrackedObject04"), + makePosePair(Action::TRACKED_OBJECT_05, "TrackedObject05"), + makePosePair(Action::TRACKED_OBJECT_06, "TrackedObject06"), + makePosePair(Action::TRACKED_OBJECT_07, "TrackedObject07"), + makePosePair(Action::TRACKED_OBJECT_08, "TrackedObject08"), + makePosePair(Action::TRACKED_OBJECT_09, "TrackedObject09"), + makePosePair(Action::TRACKED_OBJECT_10, "TrackedObject10"), + makePosePair(Action::TRACKED_OBJECT_11, "TrackedObject11"), + makePosePair(Action::TRACKED_OBJECT_12, "TrackedObject12"), + makePosePair(Action::TRACKED_OBJECT_13, "TrackedObject13"), + makePosePair(Action::TRACKED_OBJECT_14, "TrackedObject14"), + makePosePair(Action::TRACKED_OBJECT_15, "TrackedObject15"), + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index ec4800c9aa..6319b5746e 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -146,6 +146,23 @@ enum class Action { RIGHT_HAND_PINKY3, RIGHT_HAND_PINKY4, + TRACKED_OBJECT_00, + TRACKED_OBJECT_01, + TRACKED_OBJECT_02, + TRACKED_OBJECT_03, + TRACKED_OBJECT_04, + TRACKED_OBJECT_05, + TRACKED_OBJECT_06, + TRACKED_OBJECT_07, + TRACKED_OBJECT_08, + TRACKED_OBJECT_09, + TRACKED_OBJECT_10, + TRACKED_OBJECT_11, + TRACKED_OBJECT_12, + TRACKED_OBJECT_13, + TRACKED_OBJECT_14, + TRACKED_OBJECT_15, + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 40b87bc6b2..ed729867df 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -166,6 +166,23 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(DD, "Down"), makePair(DL, "Left"), makePair(DR, "Right"), + + makePair(TRACKED_OBJECT_00, "TrackedObject00"), + makePair(TRACKED_OBJECT_01, "TrackedObject01"), + makePair(TRACKED_OBJECT_02, "TrackedObject02"), + makePair(TRACKED_OBJECT_03, "TrackedObject03"), + makePair(TRACKED_OBJECT_04, "TrackedObject04"), + makePair(TRACKED_OBJECT_05, "TrackedObject05"), + makePair(TRACKED_OBJECT_06, "TrackedObject06"), + makePair(TRACKED_OBJECT_07, "TrackedObject07"), + makePair(TRACKED_OBJECT_08, "TrackedObject08"), + makePair(TRACKED_OBJECT_09, "TrackedObject09"), + makePair(TRACKED_OBJECT_10, "TrackedObject10"), + makePair(TRACKED_OBJECT_11, "TrackedObject11"), + makePair(TRACKED_OBJECT_12, "TrackedObject12"), + makePair(TRACKED_OBJECT_13, "TrackedObject13"), + makePair(TRACKED_OBJECT_14, "TrackedObject14"), + makePair(TRACKED_OBJECT_15, "TrackedObject15") }; return availableInputs; } diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 2d0a2e6d02..dd94f81a99 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -84,7 +84,7 @@ function getAvailableTrackedObjects() { var i; for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { var key = indexToTrackedObjectName(i); - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose && pose.valid) { available.push(i); } @@ -126,8 +126,8 @@ function pad(num, size) { } function update() { - if (attachedEntity && attachedObj && Controller.Hardware.Vive) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[attachedObj.key]); + if (attachedEntity && attachedObj && Controller.Standard) { + var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); diff --git a/scripts/developer/tests/viveTrackedObjects.js b/scripts/developer/tests/viveTrackedObjects.js index 4155afb82b..1d60f658d9 100644 --- a/scripts/developer/tests/viveTrackedObjects.js +++ b/scripts/developer/tests/viveTrackedObjects.js @@ -23,7 +23,7 @@ var BLUE = {x: 0, y: 0, z: 1, w: 1}; function update(dt) { if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose.valid) { DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); } else { From 620f1a65c0f4cfe1519e008749a9a18c4c6d32c0 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 12:29:30 -0700 Subject: [PATCH 69/85] New puck-attach script --- scripts/developer/tests/puck-attach.js | 205 +++++++++++++++---------- 1 file changed, 121 insertions(+), 84 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index dd94f81a99..25aeee54eb 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -1,21 +1,13 @@ // // Created by Anthony J. Thibault on 2017/06/20 +// Modified by Robbie Uvanni to support multiple pucks and easier placement of pucks on entities, on 2017/08/01 // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // When this script is running, a new app button, named "PUCKTACH", will be added to the toolbar/tablet. -// Click this app to bring up the puck attachment panel. This panel contains the following fields. -// -// * Tracked Object - A drop down list of all the available pucks found. If no pucks are found this list will only have a single NONE entry. -// Closing and re-opening the app will refresh this list. -// * Model URL - A model url of the model you wish to be attached to the specified puck. -// * Position X, Y, Z - used to apply an offset between the puck and the attached model. -// * Rot X, Y, Z - used to apply euler angle offsets, in degrees, between the puck and the attached model. -// * Create Attachment - when this button is pressed a new Entity will be created at the specified puck's location. -// If a puck atttachment already exists, it will be destroyed before the new entity is created. -// * Destroy Attachmetn - destroies the entity attached to the puck. +// Click this app to bring up the puck attachment panel. // /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -25,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var HTML_URL = "https://s3.amazonaws.com/hifi-public/tony/html/puck-attach.html"; +var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -34,20 +26,9 @@ var tabletButton = tablet.addButton({ activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/puck-a.svg" }); -tabletButton.clicked.connect(function () { - if (shown) { - tablet.gotoHomeScreen(); - } else { - tablet.gotoWebScreen(HTML_URL); - } -}); - var shown = false; -var attachedEntity; -var attachedObj; - function onScreenChanged(type, url) { - if (type === "Web" && url === HTML_URL) { + if (type === "Web" && url === TABLET_APP_URL) { tabletButton.editProperties({isActive: true}); if (!shown) { // hook up to event bridge @@ -57,9 +38,7 @@ function onScreenChanged(type, url) { // send available tracked objects to the html running in the tablet. var availableTrackedObjects = getAvailableTrackedObjects(); tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - - // print("PUCK-ATTACH: availableTrackedObjects = " + JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. + }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -71,13 +50,15 @@ function onScreenChanged(type, url) { shown = false; } } - tablet.screenChanged.connect(onScreenChanged); +function pad(num, size) { + var tempString = "000000000" + num; + return tempString.substr(tempString.length - size); +} function indexToTrackedObjectName(index) { return "TrackedObject" + pad(index, 2); } - function getAvailableTrackedObjects() { var available = []; var NUM_TRACKED_OBJECTS = 16; @@ -92,83 +73,139 @@ function getAvailableTrackedObjects() { return available; } -function attach(obj) { - attachedEntity = Entities.addEntity({ - type: "Model", - name: "puck-attach-entity", - modelURL: obj.modelurl - }); - attachedObj = obj; - var localPos = {x: Number(obj.posx), y: Number(obj.posy), z: Number(obj.posz)}; - var localRot = Quat.fromVec3Degrees({x: Number(obj.rotx), y: Number(obj.roty), z: Number(obj.rotz)}); - attachedObj.localXform = new Xform(localRot, localPos); - var key = indexToTrackedObjectName(Number(attachedObj.puckno)); - attachedObj.key = key; - - // print("PUCK-ATTACH: attachedObj = " + JSON.stringify(attachedObj)); - - Script.update.connect(update); - update(); +function getRelativePosition(origin, rotation, offset) { + var relativeOffset = Vec3.multiplyQbyV(rotation, offset); + var worldPosition = Vec3.sum(origin, relativeOffset); + return worldPosition; +} +function getPropertyForEntity(entityID, propertyName) { + return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } -function remove() { - if (attachedEntity) { - Script.update.disconnect(update); - Entities.deleteEntity(attachedEntity); - attachedEntity = undefined; - } - attachedObj = undefined; -} +var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_NAME = "Tracked Puck"; -function pad(num, size) { - var tempString = "000000000" + num; - return tempString.substr(tempString.length - size); -} +var trackedPucks = { }; +var lastPuck = { }; -function update() { - if (attachedEntity && attachedObj && Controller.Standard) { - var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); - if (pose && pose.valid) { - Entities.editEntity(attachedEntity, { - position: finalXform.pos, - rotation: finalXform.rot - }); - } else { - if (pose) { - print("PUCK-ATTACH: WARNING: invalid pose for " + attachedObj.key); - } else { - print("PUCK-ATTACH: WARNING: could not find key " + attachedObj.key); +function createPuck(puck) { + // create a puck entity and add it to our list of pucks + var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + "name": "Tracked Puck", + "type": "Model", + "modelURL": VIVE_PUCK_MODEL, + "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, + "position": spawnPosition, + "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + trackedPucks[puck.puckno] = { + "puckEntityID": puckEntityID, + "trackedEntityID": "" + }; + lastPuck = trackedPucks[puck.puckno]; +} +function finalizePuck() { + // find nearest entity and change its parent to the puck + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); + var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); + + var foundEntity; + var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + + for (var i = 0; i < foundEntities.length; i++) { + var entity = foundEntities[i]; + + if (getPropertyForEntity(entity, "name") !== VIVE_PUCK_NAME) { + var entityPosition = getPropertyForEntity(entity, "position"); + var d = Vec3.distance(entityPosition, puckPosition); + + if (d < leastDistance) { + leastDistance = d; + foundEntity = entity; } } } + + if (foundEntity) { + lastPuck.trackedEntityID = foundEntity; + Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + } +} +function updatePucks() { + // for each puck, update its position and orientation + for (var puck in trackedPucks) { + var action = indexToTrackedObjectName(puck); + var pose = Controller.getPoseValue(Controller.Standard[action]); + if (pose && pose.valid) { + if (trackedPucks[puck].trackedEntityID) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + + Entities.editEntity(trackedPucks[puck].puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + } + } + } +} +function destroyPuck(puckName) { + // unparent entity and delete its parent + var puckEntityID = trackedPucks[puckName].puckEntityID; + var trackedEntityID = trackedPucks[puckName].trackedEntityID; + + Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); + Entities.deleteEntity(puckEntityID); +} +function destroyPucks() { + // remove all pucks and unparent entities + for (var puck in trackedPucks) { + destroyPuck(puck); + } } function onWebEventReceived(msg) { var obj = {}; - try { + + try { obj = JSON.parse(msg); - } catch (err) { - return; + } catch (err) { + return; } - if (obj.cmd === "attach") { - remove(); - attach(obj); - } else if (obj.cmd === "detach") { - remove(); + + switch (obj.cmd) { + case "create": + createPuck(obj); + break; + case "finalize": + finalizePuck(); + break; } } +Script.update.connect(updatePucks); Script.scriptEnding.connect(function () { - remove(); tablet.removeButton(tabletButton); + destroyPucks(); if (shown) { tablet.webEventReceived.disconnect(onWebEventReceived); tablet.gotoHomeScreen(); } tablet.screenChanged.disconnect(onScreenChanged); }); - -}()); // END LOCAL_SCOPE +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(TABLET_APP_URL); + } +}); +}()); // END LOCAL_SCOPE \ No newline at end of file From 961f748d8761332ba2939937b70cd6cac91d500b Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 13:08:24 -0700 Subject: [PATCH 70/85] Fixed a potential bug with puck-attach parenting --- scripts/developer/tests/puck-attach.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 25aeee54eb..3a5d8736f8 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -140,18 +140,25 @@ function finalizePuck() { } function updatePucks() { // for each puck, update its position and orientation - for (var puck in trackedPucks) { - var action = indexToTrackedObjectName(puck); + for (var puckName in trackedPucks) { + var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { - if (trackedPucks[puck].trackedEntityID) { + var puck = trackedPucks[puckName]; + if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); - Entities.editEntity(trackedPucks[puck].puckEntityID, { + Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, - rotation: finalXform.rot + rotation: finalXform.rot, + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID }); } } From 5a7a6bab7049b899b9b011fbb1bfda7f0a23639a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Aug 2017 13:57:27 -0700 Subject: [PATCH 71/85] Remove PDB files, eliminate sixense --- cmake/macros/GenerateInstallers.cmake | 18 +++----- cmake/macros/InstallBesideConsole.cmake | 5 ++- cmake/templates/FixupBundlePostBuild.cmake.in | 43 ++++++++----------- interface/CMakeLists.txt | 5 ++- plugins/hifiSixense/CMakeLists.txt | 14 +++--- 5 files changed, 40 insertions(+), 45 deletions(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..75190d044a 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -24,21 +24,12 @@ macro(GENERATE_INSTALLERS) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) + if (WIN32) - # include CMake module that will install compiler system libraries - # so that we have msvcr120 and msvcp120 installed with targets - set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) - - # as long as we're including sixense plugin with installer - # we need re-distributables for VS 2011 as well - # this should be removed if/when sixense support is pulled - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll" - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll" - ) - + # Do not install the Visual Studio C runtime libraries. The installer will do this automatically + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include(InstallRequiredSystemLibraries) - set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") # install and reference the Add/Remove icon @@ -90,3 +81,4 @@ macro(GENERATE_INSTALLERS) include(CPack) endmacro() + diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index d5777fff12..3c991acf86 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -22,9 +22,12 @@ macro(install_beside_console) else () # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${COMPONENT_INSTALL_DIR} COMPONENT ${SERVER_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) # on windows for PR and production builds, sign the executable diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 57d1fd787f..d4726884c2 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -11,34 +11,28 @@ include(BundleUtilities) -# replace copy_resolved_item_into_bundle -# -# The official version of copy_resolved_item_into_bundle will print out a "warning:" when -# the resolved item matches the resolved embedded item. This not not really an issue that -# should rise to the level of a "warning" so we replace this message with a "status:" -# -function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) - if (WIN32) - # ignore case on Windows - string(TOLOWER "${resolved_item}" resolved_item_compare) - string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare) - else() - set(resolved_item_compare "${resolved_item}") - set(resolved_embedded_item_compare "${resolved_embedded_item}") +function(gp_resolved_file_type_override resolved_file type_var) + if( file MATCHES ".*VCRUNTIME140.*" ) + set(type "system" PARENT_SCOPE) endif() - - if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") - # this is our only change from the original version - message(STATUS "status: resolved_item == resolved_embedded_item - not copying...") - else() - #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") - if(UNIX AND NOT APPLE) - file(RPATH_REMOVE FILE "${resolved_embedded_item}") - endif() + if( file MATCHES ".*concrt140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*msvcp140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*vcruntime140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-crt-conio.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-core-winrt.*" ) + set(type "system" PARENT_SCOPE) endif() endfunction() + message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}") @@ -52,3 +46,4 @@ endif() file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c97975e50a..81c8a44baf 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -309,9 +309,12 @@ else (APPLE) # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${INTERFACE_INSTALL_DIR} COMPONENT ${CLIENT_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}") diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt index 14676217db..58aeb6c88f 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -6,9 +6,11 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (NOT ANDROID) - set(TARGET_NAME hifiSixense) - setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) - target_sixense() -endif () +# FIXME if we want to re-enable this, we need to fix the mechanism for installing the +# dependency dlls `msvcr100` and `msvcp100` WITHOUT using CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS +#if (NOT ANDROID) +# set(TARGET_NAME hifiSixense) +# setup_hifi_plugin(Script Qml Widgets) +# link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) +# target_sixense() +#endif () From 82654c7cda887b19bc673ecb51f9c07bcb8652e1 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 15:28:26 -0700 Subject: [PATCH 72/85] Made requested changed to puck-attach.js --- scripts/developer/tests/puck-attach.js | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 3a5d8736f8..65adf35d00 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -83,7 +83,9 @@ function getPropertyForEntity(entityID, propertyName) { } var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; @@ -91,23 +93,28 @@ var lastPuck = { }; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); // should be an overlay var puckEntityProperties = { - "name": "Tracked Puck", - "type": "Model", - "modelURL": VIVE_PUCK_MODEL, - "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, - "position": spawnPosition, - "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' }; var puckEntityID = Entities.addEntity(puckEntityProperties); + + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + trackedPucks[puck.puckno] = { - "puckEntityID": puckEntityID, - "trackedEntityID": "" + puckEntityID: puckEntityID, + trackedEntityID: "" }; lastPuck = trackedPucks[puck.puckno]; } @@ -117,7 +124,7 @@ function finalizePuck() { var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); var foundEntity; - var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + var leastDistance = Number.MAX_VALUE; for (var i = 0; i < foundEntities.length; i++) { var entity = foundEntities[i]; @@ -141,6 +148,9 @@ function finalizePuck() { function updatePucks() { // for each puck, update its position and orientation for (var puckName in trackedPucks) { + if (!trackedPucks.hasOwnProperty(puckName)) { + continue; + } var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { @@ -148,7 +158,7 @@ function updatePucks() { if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + var finalXform = Xform.mul(avatarXform, puckXform); Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, @@ -169,13 +179,16 @@ function destroyPuck(puckName) { var puckEntityID = trackedPucks[puckName].puckEntityID; var trackedEntityID = trackedPucks[puckName].trackedEntityID; + // remove the puck as a parent entity and then delete the puck Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); Entities.deleteEntity(puckEntityID); } function destroyPucks() { // remove all pucks and unparent entities - for (var puck in trackedPucks) { - destroyPuck(puck); + for (var puckName in trackedPucks) { + if (trackedPucks.hasOwnProperty(puckName)) { + destroyPuck(puckName); + } } } From 24718e242405f422e2fcfc1a64a55be1725611c6 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 09:03:23 -0400 Subject: [PATCH 73/85] WL21423 Camera mode on startup is determined by user settings and HMD status --- interface/src/Application.cpp | 44 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ceded99f40..8debcbd538 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4146,6 +4146,7 @@ void Application::loadSettings() { //DependencyManager::get()->setAutomaticLODAdjust(false); Menu::getInstance()->loadSettings(); + // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. auto pluginManager = PluginManager::getInstance(); auto plugins = pluginManager->getPreferredDisplayPlugins(); @@ -4159,24 +4160,44 @@ void Application::loadSettings() { break; } } - } else { + } + + Setting::Handle firstRun { Settings::firstRun, true }; + bool isFirstPerson = false; + if (firstRun.get()) { // If this is our first run, and no preferred devices were set, default to // an HMD device if available. - Setting::Handle firstRun { Settings::firstRun, true }; - if (firstRun.get()) { - auto displayPlugins = pluginManager->getDisplayPlugins(); - for (auto& plugin : displayPlugins) { - if (plugin->isHmd()) { - if (auto action = menu->getActionForOption(plugin->getName())) { - action->setChecked(true); - action->trigger(); - break; - } + auto displayPlugins = pluginManager->getDisplayPlugins(); + for (auto& plugin : displayPlugins) { + if (plugin->isHmd()) { + if (auto action = menu->getActionForOption(plugin->getName())) { + action->setChecked(true); + action->trigger(); + break; } } } + + isFirstPerson = (qApp->isHMDMode()); + } else { + // if this is not the first run, the camera will be initialized differently depending on user settings + + if (qApp->isHMDMode()) { + // if the HMD is active, use first-person camera, unless the appropriate setting is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); + } else { + // if HMD is not active, only use first person if the menu option is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + } } + // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings + // dictated that we should be in first person + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + cameraMenuChanged(); + auto inputs = pluginManager->getInputPlugins(); for (auto plugin : inputs) { if (!plugin->isActive()) { @@ -4225,7 +4246,6 @@ void Application::init() { DependencyManager::get()->init(); DependencyManager::get()->init(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _timerStart.start(); _lastTimeUpdated.start(); From 4b7779f0f55381a1ea10f1b41eb6688ec7ecab69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 3 Aug 2017 07:57:21 -0700 Subject: [PATCH 74/85] add flag to outline render flags, fix pal.js/edit.js interaction --- interface/src/Application.cpp | 11 ++++++++--- .../src/RenderableModelEntityItem.cpp | 5 ++++- libraries/render/src/render/Args.h | 3 ++- scripts/system/pal.js | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 571f206cb5..d50348d9b7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1489,7 +1489,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["atp_mapping_requests"] = atpMappingRequests; properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; - + QJsonObject bytesDownloaded; bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt(); bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt(); @@ -5420,8 +5420,13 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; - if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + auto contextOverlayInterface = DependencyManager::get(); + if (contextOverlayInterface->getEnabled()) { + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE; + } else { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + } } renderArgs->_outlineFlags = renderOutlineFlags; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a03360a0e5..911fdf4184 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,7 +372,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + // this simple logic should say we set showingEntityHighlight to true whenever we are in marketplace mode and we have a marketplace id, or + // whenever we are not set to none and shouldHighlight is true. + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE) && getMarketplaceID().length() != 0) || + (args->_outlineFlags != RenderArgs::RENDER_OUTLINE_NONE && getShouldHighlight()); if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index f53454cf75..d5b5440c32 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -66,7 +66,8 @@ namespace render { enum OutlineFlags { RENDER_OUTLINE_NONE = 0, RENDER_OUTLINE_WIREFRAMES = 1, - RENDER_OUTLINE_SHADER = 2 + RENDER_OUTLINE_MARKETPLACE_MODE = 2, + RENDER_OUTLINE_SHADER = 4 }; enum DebugFlags { RENDER_DEBUG_NONE = 0, diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 58a54ef859..2a8a89ae7d 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -710,6 +710,7 @@ function off() { Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); tablet.tabletShownChanged.disconnect(tabletVisibilityChanged); isWired = false; + ContextOverlay.enabled = true } if (audioTimer) { Script.clearInterval(audioTimer); @@ -718,7 +719,6 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; - ContextOverlay.enabled = true; } function tabletVisibilityChanged() { From 6ea4ed80cd7a5ff0bd49aacd8ddb0790aed97e02 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 3 Aug 2017 09:19:08 -0700 Subject: [PATCH 75/85] Suppress initial PacketType::SilentAudioFrame on audio gate startup --- assignment-client/src/Agent.h | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index a549df5fb3..da60a07367 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -109,7 +109,7 @@ private: QHash _outgoingScriptAudioSequenceNumbers; AudioGate _audioGate; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; bool _isNoiseGateEnabled { false }; CodecPluginPointer _codec; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 31e36671c7..1c26b1bb08 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -364,7 +364,7 @@ private: AudioIOStats _stats; AudioGate* _audioGate { nullptr }; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; From 2279e174e6dd9b16564e7035a85c900576a9d6bf Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Thu, 3 Aug 2017 09:41:41 -0700 Subject: [PATCH 76/85] Switching to completely silent VC++ redistributable installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 2445933b52..5af51ff8c9 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,7 +853,7 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" - ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" From a439cc18372164e81290faeb325061ba7ab69afd Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 3 Aug 2017 20:58:44 +0200 Subject: [PATCH 77/85] Make connection to tablet shown signal more consistent, eliminates missed signals --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++----------- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 007754ed78..0fd32b42e6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -187,6 +187,7 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent) if (QThread::currentThread() != qApp->thread()) { qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name; } + connect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } TabletProxy::~TabletProxy() { @@ -194,6 +195,7 @@ TabletProxy::~TabletProxy() { if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; } + disconnect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } void TabletProxy::setToolbarMode(bool toolbarMode) { @@ -235,8 +237,7 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } else { loadHomeScreen(true); } - //check if running scripts opened and save it for reopen in Tablet - qDebug() << __FUNCTION__ << offscreenUi->isVisible("RunningScripts"); + //check if running scripts window opened and save it for reopen in Tablet if (offscreenUi->isVisible("RunningScripts")) { offscreenUi->hide("RunningScripts"); _showRunningScripts = true; @@ -322,6 +323,13 @@ void TabletProxy::emitWebEvent(const QVariant& msg) { emit webEventReceived(msg); } +void TabletProxy::onTabletShown() { + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } +} + bool TabletProxy::isPathLoaded(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; @@ -377,18 +385,7 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { _initialScreen = false; } - qDebug() << __FUNCTION__ << _showRunningScripts; if (_showRunningScripts) { - _showRunningScripts = false; - //connect to Tablet shown signal to open running scripts - //we cant just call pushOnStack here since RunningScripts draws incorrectly - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); //not needed anymore, disconnect - if (_tabletShown) { - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); //show Tablet. Make sure, setShown implemented in TabletRoot.qml QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); } @@ -491,7 +488,6 @@ void TabletProxy::loadQMLSource(const QVariant& path) { if (root) { removeButtonsFromHomeScreen(); //works only in Tablet - qDebug() << __FUNCTION__ << path << _currentPathLoaded << (int)_state; QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; if (path != _currentPathLoaded) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 82b7ad52b6..af38cb9351 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ protected slots: void addButtonsToHomeScreen(); void desktopWindowClosed(); void emitWebEvent(const QVariant& msg); + void onTabletShown(); protected: void removeButtonsFromHomeScreen(); void loadHomeScreen(bool forceOntoHomeScreen); From dff4e6e32c2d6af0834139cc26150ffb715240c8 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 15:40:38 -0400 Subject: [PATCH 78/85] WL21423 Changing order of camera setup steps --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8debcbd538..7728015d70 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4193,9 +4193,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); From 498ac5d0ab65209aa40230435c10572ecde04f9d Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 15:08:06 -0700 Subject: [PATCH 79/85] Updated version of puck-attach.js --- scripts/developer/tests/puck-attach.js | 173 +++++++++++++++++-------- 1 file changed, 121 insertions(+), 52 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 65adf35d00..823308e3d5 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -17,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; +var TABLET_APP_URL = "https://hifi-content.s3.amazonaws.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -33,12 +33,6 @@ function onScreenChanged(type, url) { if (!shown) { // hook up to event bridge tablet.webEventReceived.connect(onWebEventReceived); - - Script.setTimeout(function () { - // send available tracked objects to the html running in the tablet. - var availableTrackedObjects = getAvailableTrackedObjects(); - tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -72,6 +66,12 @@ function getAvailableTrackedObjects() { } return available; } +function sendAvailableTrackedObjects() { + tablet.emitScriptEvent(JSON.stringify({ + pucks: getAvailableTrackedObjects(), + selectedPuck: ((lastPuck === undefined) ? -1 : lastPuck.name) + })); +} function getRelativePosition(origin, rotation, offset) { var relativeOffset = Vec3.multiplyQbyV(rotation, offset); @@ -81,45 +81,74 @@ function getRelativePosition(origin, rotation, offset) { function getPropertyForEntity(entityID, propertyName) { return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } +function entityExists(entityID) { + return Object.keys(Entities.getEntityProperties(entityID)).length > 0; +} var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres -var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 0.5; // metres +var VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE = 10.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; -var lastPuck = { }; +var lastPuck; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); - var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); - - // should be an overlay - var puckEntityProperties = { - name: "Tracked Puck", - type: "Model", - modelURL: VIVE_PUCK_MODEL, - dimensions: VIVE_PUCK_DIMENSIONS, - position: spawnPosition, - userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' - }; - - var puckEntityID = Entities.addEntity(puckEntityProperties); + var action = indexToTrackedObjectName(puck.puckno); + var pose = Controller.getPoseValue(Controller.Standard[action]); - if (trackedPucks.hasOwnProperty(puck.puckno)) { - destroyPuck(puck.puckno); + if (pose && pose.valid) { + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + + // if we've already created this puck, destroy it + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + // if we had an unfinalized puck, destroy it + if (lastPuck !== undefined) { + destroyPuck(lastPuck.name); + } + // create our new unfinalized puck + trackedPucks[puck.puckno] = { + puckEntityID: puckEntityID, + trackedEntityID: "" + }; + lastPuck = trackedPucks[puck.puckno]; + lastPuck.name = Number(puck.puckno); } - - trackedPucks[puck.puckno] = { - puckEntityID: puckEntityID, - trackedEntityID: "" - }; - lastPuck = trackedPucks[puck.puckno]; } -function finalizePuck() { +function finalizePuck(puckName) { // find nearest entity and change its parent to the puck + + if (!trackedPucks.hasOwnProperty(puckName)) { + print('2'); + return; + } + if (lastPuck === undefined) { + print('3'); + return; + } + if (lastPuck.name !== Number(puckName)) { + print('1'); + return; + } + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); @@ -142,7 +171,16 @@ function finalizePuck() { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + // remember the userdata for the tracked entity since we're about to + // remove it and make it ungrabbable + lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + // update properties of the tracked entity + Entities.editEntity(lastPuck.trackedEntityID, { + "parentID": lastPuck.puckEntityID, + "userData": '{ "grabbableKey": { "grabbable": false } }' + }); + // remove reference to puck since it is now calibrated and finalized + lastPuck = undefined; } } function updatePucks() { @@ -156,32 +194,57 @@ function updatePucks() { if (pose && pose.valid) { var puck = trackedPucks[puckName]; if (puck.trackedEntityID) { - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, puckXform); + if (entityExists(puck.trackedEntityID)) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, puckXform); + + var d = Vec3.distance(MyAvatar.position, finalXform.pos); + if (d > VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE) { + print('tried to move tracked object too far away: ' + d); + return; + } - Entities.editEntity(puck.puckEntityID, { - position: finalXform.pos, - rotation: finalXform.rot, - }); - - // in case someone grabbed both entities and destroyed the - // child/parent relationship - Entities.editEntity(puck.trackedEntityID, { - parentID: puck.puckEntityID - }); + Entities.editEntity(puck.puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID + }); + } else { + destroyPuck(puckName); + } } } } } function destroyPuck(puckName) { // unparent entity and delete its parent - var puckEntityID = trackedPucks[puckName].puckEntityID; - var trackedEntityID = trackedPucks[puckName].trackedEntityID; + if (!trackedPucks.hasOwnProperty(puckName)) { + return; + } + + var puck = trackedPucks[puckName]; + var puckEntityID = puck.puckEntityID; + var trackedEntityID = puck.trackedEntityID; - // remove the puck as a parent entity and then delete the puck - Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); - Entities.deleteEntity(puckEntityID); + // remove the puck as a parent entity and restore the tracked entities + // former userdata + Entities.editEntity(trackedEntityID, { + "parentID": "{00000000-0000-0000-0000-000000000000}", + "userData": puck.trackedEntityUserData + }); + + delete trackedPucks[puckName]; + + Script.setTimeout(function() { + // delete the puck + Entities.deleteEntity(puckEntityID); + }, 100); } function destroyPucks() { // remove all pucks and unparent entities @@ -202,11 +265,17 @@ function onWebEventReceived(msg) { } switch (obj.cmd) { + case "ready": + sendAvailableTrackedObjects(); + break; case "create": createPuck(obj); break; case "finalize": - finalizePuck(); + finalizePuck(obj.puckno); + break; + case "destroy": + destroyPuck(obj.puckno); break; } } From 67b59210796bb2e10b08b9904b4e42a95d1b006f Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 16:01:12 -0700 Subject: [PATCH 80/85] Changed puck-attach to make tracked entities collisionless --- scripts/developer/tests/puck-attach.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 823308e3d5..04d5db5710 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -171,17 +171,19 @@ function finalizePuck(puckName) { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - // remember the userdata for the tracked entity since we're about to - // remove it and make it ungrabbable + // remember the userdata and collisionless flag for the tracked entity since + // we're about to remove it and make it ungrabbable and collisionless lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + lastPuck.trackedEntityCollisionFlag = getPropertyForEntity(foundEntity, "collisionless"); // update properties of the tracked entity Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID, - "userData": '{ "grabbableKey": { "grabbable": false } }' + "userData": '{ "grabbableKey": { "grabbable": false } }', + "collisionless": 1 }); // remove reference to puck since it is now calibrated and finalized lastPuck = undefined; - } + } } function updatePucks() { // for each puck, update its position and orientation @@ -233,14 +235,19 @@ function destroyPuck(puckName) { var trackedEntityID = puck.trackedEntityID; // remove the puck as a parent entity and restore the tracked entities - // former userdata + // former userdata and collision flag Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}", - "userData": puck.trackedEntityUserData + "userData": puck.trackedEntityUserData, + "collisionless": puck.trackedEntityCollisionFlag }); delete trackedPucks[puckName]; + // in some cases, the entity deletion may occur before the parent change + // has been processed, resulting in both the puck and the tracked entity + // to be deleted so we wait 100ms before deleting the puck, assuming + // that the parent change has occured Script.setTimeout(function() { // delete the puck Entities.deleteEntity(puckEntityID); From 9058a8e2ca73481570ff0198ed90747d2cb82f85 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 4 Aug 2017 16:36:00 +0200 Subject: [PATCH 81/85] Cleanup top menu on menu reinstantiation --- interface/resources/qml/hifi/tablet/TabletMenuStack.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 2fd33e9cbc..9076cd6c48 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -114,6 +114,7 @@ Item { } function clearMenus() { + topMenu = null d.clear() } From d05e1677c3a58272b14c655bbd940a9d73200edd Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 4 Aug 2017 23:04:15 +0300 Subject: [PATCH 82/85] 6677: Change "Desktop" App to Say "Exit VR" --- scripts/system/hmd.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 3598b461a4..c95d85ebeb 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,17 +43,20 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; +var switchToVR = "Enter VR"; +var switchToDesktop = "Exit VR"; + function onHmdChanged(isHmd) { HMD.closeTablet(); if (isHmd) { button.editProperties({ icon: "icons/tablet-icons/switch-desk-i.svg", - text: "DESKTOP" + text: switchToDesktop }); } else { button.editProperties({ icon: "icons/tablet-icons/switch-vr-i.svg", - text: "VR" + text: switchToVR }); } desktopOnlyViews.forEach(function (view) { @@ -70,7 +73,7 @@ function onClicked() { if (headset) { button = tablet.addButton({ icon: HMD.active ? "icons/tablet-icons/switch-desk-i.svg" : "icons/tablet-icons/switch-vr-i.svg", - text: HMD.active ? "DESKTOP" : "VR", + text: HMD.active ? switchToDesktop : switchToVR, sortOrder: 2 }); onHmdChanged(HMD.active); From 86e348916724fc8dd290569acd9fd44ee0c37e02 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 09:42:42 -0700 Subject: [PATCH 83/85] Updates from Threaded Rendering project --- interface/src/Application.cpp | 49 +++++------- interface/src/Application.h | 9 ++- interface/src/scripting/AudioDevices.cpp | 1 + interface/src/ui/ResourceImageItem.cpp | 3 +- interface/src/ui/SnapshotAnimated.cpp | 3 +- .../src/RenderableWebEntityItem.cpp | 7 +- .../src/RenderableWebEntityItem.h | 2 +- .../plugins/src/plugins/DisplayPlugin.cpp | 17 +++- libraries/plugins/src/plugins/DisplayPlugin.h | 11 ++- libraries/render/src/render/Args.h | 3 - libraries/shared/src/ThreadHelpers.cpp | 77 ++++++++++++++----- libraries/shared/src/ThreadHelpers.h | 13 +++- libraries/shared/src/shared/QtHelpers.cpp | 29 ++++++- libraries/shared/src/shared/QtHelpers.h | 1 + tests/render-perf/src/main.cpp | 2 +- 15 files changed, 155 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d016c9d020..549e5338a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,7 +2256,7 @@ void Application::paintGL() { QMutexLocker viewLocker(&_viewMutex); _viewFrustum.calculate(); } - renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), + renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); { @@ -2772,7 +2772,12 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } -bool _renderRequested { false }; +void Application::onPresent(quint32 frameCount) { + if (shouldPaint()) { + postEvent(this, new QEvent(static_cast(Idle)), Qt::HighEventPriority); + postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); + } +} bool Application::event(QEvent* event) { if (!Menu::getInstance()) { @@ -2788,23 +2793,9 @@ bool Application::event(QEvent* event) { // Explicit idle keeps the idle running at a lower interval, but without any rendering // see (windowMinimizedChanged) case Event::Idle: - { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - _lastTimeUpdated.start(); - idle(nsecsElapsed); - } - return true; - - case Event::Present: - if (!_renderRequested) { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - if (shouldPaint(nsecsElapsed)) { - _renderRequested = true; - _lastTimeUpdated.start(); - idle(nsecsElapsed); - postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); - } - } + idle(); + // Clear the event queue of pending idle calls + removePostedEvents(this, Idle); return true; case Event::Paint: @@ -2812,9 +2803,8 @@ bool Application::event(QEvent* event) { // or AvatarInputs will mysteriously move to the bottom-right AvatarInputs::getInstance()->update(); paintGL(); - // wait for the next present event before starting idle / paint again - removePostedEvents(this, Present); - _renderRequested = false; + // Clear the event queue of pending paint calls + removePostedEvents(this, Paint); return true; default: @@ -3633,7 +3623,7 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; -bool Application::shouldPaint(float nsecsElapsed) { +bool Application::shouldPaint() { if (_aboutToQuit) { return false; } @@ -3653,11 +3643,9 @@ bool Application::shouldPaint(float nsecsElapsed) { (float)paintDelaySamples / paintDelayUsecs << "us"; } #endif - - float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC; - + // Throttle if requested - if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { + if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { return false; } @@ -3874,7 +3862,7 @@ void setupCpuMonitorThread() { #endif -void Application::idle(float nsecsElapsed) { +void Application::idle() { PerformanceTimer perfTimer("idle"); // Update the deadlock watchdog @@ -3931,7 +3919,8 @@ void Application::idle(float nsecsElapsed) { steamClient->runCallbacks(); } - float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; + float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND; + _lastTimeUpdated.start(); // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { @@ -7106,6 +7095,7 @@ void Application::updateDisplayMode() { auto oldDisplayPlugin = _displayPlugin; if (_displayPlugin) { + disconnect(_displayPluginPresentConnection); _displayPlugin->deactivate(); } @@ -7146,6 +7136,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; + _displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 123aa85e2e..300bd4ac02 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -129,8 +129,7 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; enum Event { - Present = DisplayPlugin::Present, - Paint, + Paint = QEvent::User + 1, Idle, Lambda }; @@ -409,6 +408,7 @@ private slots: void clearDomainOctreeDetails(); void clearDomainAvatars(); void onAboutToQuit(); + void onPresent(quint32 frameCount); void resettingDomain(); @@ -455,8 +455,8 @@ private: void cleanupBeforeQuit(); - bool shouldPaint(float nsecsElapsed); - void idle(float nsecsElapsed); + bool shouldPaint(); + void idle(); void update(float deltaTime); // Various helper functions called during update() @@ -518,6 +518,7 @@ private: OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; + QMetaObject::Connection _displayPluginPresentConnection; mutable std::mutex _displayPluginLock; InputPluginList _activeInputPlugins; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index d02f4d8fcf..f2e6dbf4d7 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "AudioDevices.h" diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 7b9592fa4c..5b7c1896fe 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include "Application.h" #include "ResourceImageItem.h" #include @@ -16,6 +15,8 @@ #include #include +#include + ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() { auto textureCache = DependencyManager::get(); connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update())); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 70767b007d..3c00be8358 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -13,8 +13,9 @@ #include #include #include +#include -#include +#include #include "SnapshotAnimated.h" QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9a898b4071..ba8e0c18e7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -69,7 +69,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { } } -bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { +bool RenderableWebEntityItem::buildWebSurface() { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { qWarning() << "Too many concurrent web views to create new view"; return false; @@ -132,6 +132,8 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer handlePointerEvent(event); } }; + + auto renderer = DependencyManager::get(); _mousePressConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent); _mouseReleaseConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent); _mouseMoveConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent); @@ -185,8 +187,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - auto renderer = qSharedPointerCast(args->_renderData); - if (!buildWebSurface(renderer)) { + if (!buildWebSurface()) { return; } _fadeStartTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0f5d307766..a2318081b6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -57,7 +57,7 @@ public: virtual QObject* getRootItem() override; private: - bool buildWebSurface(QSharedPointer renderer); + bool buildWebSurface(); void destroyWebSurface(); glm::vec2 getWindowSize() const; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 747c72c08e..20c72159c4 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -18,6 +18,19 @@ void DisplayPlugin::incrementPresentCount() { ++_presentedFrameIndex; - // Alert the app that it needs to paint a new presentation frame - qApp->postEvent(qApp, new QEvent(static_cast(Present)), Qt::HighEventPriority); + { + QMutexLocker locker(&_presentMutex); + _presentCondition.wakeAll(); + } + + emit presented(_presentedFrameIndex); } + +void DisplayPlugin::waitForPresent() { + QMutexLocker locker(&_presentMutex); + while (isActive()) { + if (_presentCondition.wait(&_presentMutex, MSECS_PER_SECOND)) { + break; + } + } +} \ No newline at end of file diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 481a2609fc..9e18ee534d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -134,10 +136,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: - enum Event { - Present = QEvent::User + 1 - }; - virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -221,12 +219,15 @@ public: virtual void cycleDebugOutput() {} + void waitForPresent(); + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); + void presented(quint32 frame); protected: void incrementPresentCount(); @@ -234,6 +235,8 @@ protected: gpu::ContextPointer _gpuContext; private: + QMutex _presentMutex; + QWaitCondition _presentCondition; std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; QElapsedTimer _paintDelayTimer; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d5b5440c32..6a91081c95 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -77,7 +77,6 @@ namespace render { Args() {} Args(const gpu::ContextPointer& context, - QSharedPointer renderData = QSharedPointer(nullptr), float sizeScale = 1.0f, int boundaryLevelAdjust = 0, RenderMode renderMode = DEFAULT_RENDER_MODE, @@ -85,7 +84,6 @@ namespace render { DebugFlags debugFlags = RENDER_DEBUG_NONE, gpu::Batch* batch = nullptr) : _context(context), - _renderData(renderData), _sizeScale(sizeScale), _boundaryLevelAdjust(boundaryLevelAdjust), _renderMode(renderMode), @@ -110,7 +108,6 @@ namespace render { std::shared_ptr _context; std::shared_ptr _blitFramebuffer; std::shared_ptr _shapePipeline; - QSharedPointer _renderData; std::stack _viewFrustums; glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f }; glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f }; diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8f3d16a577..8cf8b85284 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -10,29 +10,66 @@ #include +// Support for viewing the thread name in the debugger. +// Note, Qt actually does this for you but only in debug builds +// Code from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx +// and matches logic in `qt_set_thread_name` in qthread_win.cpp +#ifdef Q_OS_WIN +#include +#pragma pack(push,8) +struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) +#endif + +void setThreadName(const std::string& name) { +#ifdef Q_OS_WIN + static const DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{ 0x1000, name.c_str(), (DWORD)-1, 0 }; + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { } +#endif +} + +void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { + Q_ASSERT(QThread::currentThread() == object->thread()); + // setup a thread for the NodeList and its PacketReceiver + QThread* thread = new QThread(); + thread->setObjectName(name); + + // Execute any additional work to do before the thread starts (like moving members to the target thread + preStartCallback(thread); + + // Link the in-thread initialization code + QObject::connect(thread, &QThread::started, [name, startCallback] { + if (!name.isEmpty()) { + // Make it easy to spot our thread processes inside the debugger + setThreadName("Hifi_" + name.toStdString()); + } + startCallback(); + }); + + // Make sure the thread will be destroyed and cleaned up + QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); + QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // put the object on the thread + object->moveToThread(thread); + thread->start(); + if (priority != QThread::InheritPriority) { + thread->setPriority(priority); + } +} void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority) { - Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver - QThread* thread = new QThread(); - thread->setObjectName(name); - - QString tempName = name; - QObject::connect(thread, &QThread::started, [startCallback] { - startCallback(); - }); - // Make sure the thread will be destroyed and cleaned up - QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); - QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); - - // put the object on the thread - object->moveToThread(thread); - thread->start(); - if (priority != QThread::InheritPriority) { - thread->setPriority(priority); - } + moveToNewNamedThread(object, name, [](QThread*){}, startCallback, priority); } void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { - moveToNewNamedThread(object, name, [] {}, priority); + moveToNewNamedThread(object, name, [](QThread*){}, []{}, priority); } diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index 6e024f787a..d236344dc5 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -32,8 +32,17 @@ void withLock(QMutex& lock, F function) { function(); } -void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority = QThread::InheritPriority); -void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority); +void moveToNewNamedThread(QObject* object, const QString& name, + std::function preStartCallback, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + QThread::Priority priority = QThread::InheritPriority); class ConditionalGuard { public: diff --git a/libraries/shared/src/shared/QtHelpers.cpp b/libraries/shared/src/shared/QtHelpers.cpp index 1ce1c3e07c..3e8c6d57ed 100644 --- a/libraries/shared/src/shared/QtHelpers.cpp +++ b/libraries/shared/src/shared/QtHelpers.cpp @@ -11,11 +11,24 @@ #include #include #include +#include +#include "../Profile.h" Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety") namespace hifi { namespace qt { +static QHash threadHash; +static QReadWriteLock threadHashLock; + +void addBlockingForbiddenThread(const QString& name, QThread* thread) { + if (!thread) { + thread = QThread::currentThread(); + } + QWriteLocker locker(&threadHashLock); + threadHash[thread] = name; +} + bool blockingInvokeMethod( const char* function, QObject *obj, const char *member, @@ -30,9 +43,23 @@ bool blockingInvokeMethod( QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { - if (QThread::currentThread() == qApp->thread()) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function; + return QMetaObject::invokeMethod(obj, member, + Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + + { + QReadLocker locker(&threadHashLock); + for (const auto& thread : threadHash.keys()) { + if (currentThread == thread) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << threadHash[thread]; + } + } } + + PROFILE_RANGE(app, function); return QMetaObject::invokeMethod(obj, member, Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 5da65a378f..2133119324 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -14,6 +14,7 @@ namespace hifi { namespace qt { +void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr); bool blockingInvokeMethod( const char* function, diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ce47a896aa..dbb315a9ae 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -681,7 +681,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); - RenderArgs renderArgs(_renderThread._gpuContext, _octree, DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); From 0e508432c4c745d08d890502d7279c062fc7b105 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 4 Aug 2017 15:27:33 -0700 Subject: [PATCH 84/85] PR comments --- libraries/shared/src/ThreadHelpers.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8cf8b85284..654b8e0252 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -38,11 +38,14 @@ void setThreadName(const std::string& name) { void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver + + // Create the target thread QThread* thread = new QThread(); thread->setObjectName(name); - // Execute any additional work to do before the thread starts (like moving members to the target thread + // Execute any additional work to do before the thread starts like moving members to the target thread. + // This is required as QObject::moveToThread isn't virutal, so we can't override it on objects that contain + // an OpenGLContext and ensure that the context moves to the target thread as well. preStartCallback(thread); // Link the in-thread initialization code @@ -54,8 +57,10 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function Date: Fri, 4 Aug 2017 17:19:26 -0700 Subject: [PATCH 85/85] Capitalize "ENTER VR" --- scripts/system/hmd.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c95d85ebeb..c9c3dbe493 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,8 +43,8 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; -var switchToVR = "Enter VR"; -var switchToDesktop = "Exit VR"; +var switchToVR = "ENTER VR"; +var switchToDesktop = "EXIT VR"; function onHmdChanged(isHmd) { HMD.closeTablet();