From 7bcd9f9f9544256638657ca15c78671c4b7e47d6 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 12:22:11 -0400 Subject: [PATCH 001/114] * 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 002/114] 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 003/114] 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 004/114] 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 005/114] 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 006/114] 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 007/114] 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 008/114] 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 009/114] 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 010/114] 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 011/114] 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 012/114] 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 013/114] 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 014/114] 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 015/114] 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 016/114] 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 017/114] 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 018/114] 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 019/114] 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 020/114] 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 021/114] 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 022/114] 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 023/114] 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 024/114] 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 025/114] 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 026/114] 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 027/114] 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 028/114] 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 029/114] 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 030/114] 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 031/114] 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 032/114] 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 033/114] 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 034/114] 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 035/114] 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 036/114] 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 037/114] 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 038/114] 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 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 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 039/114] 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 040/114] 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 041/114] 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 042/114] 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 043/114] 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 f0871c6878ff614b5b2f3d775ea161e0edb8ae91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:06:15 -0700 Subject: [PATCH 044/114] avoid unnecessary and expensive gets --- interface/src/avatar/MyAvatar.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b32ef4024e..7910df90ec 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1643,10 +1643,13 @@ void MyAvatar::prepareForPhysicsSimulation() { } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { - glm::vec3 position = getPosition(); - glm::quat orientation = getOrientation(); + glm::vec3 position; + glm::quat orientation; if (_characterController.isEnabledAndReady()) { _characterController.getPositionAndOrientation(position, orientation); + } else { + position = getPosition(); + orientation = getOrientation(); } nextAttitude(position, orientation); _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); From 29be9aee65a14e314905dead6dc8229f6667b1f2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:34:44 -0700 Subject: [PATCH 045/114] move nextAttitude() from AvatarData to MyAvatar --- interface/src/avatar/MyAvatar.cpp | 17 +++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + .../src/avatars-renderer/Avatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 17 ----------------- libraries/avatars/src/AvatarData.h | 3 --- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7910df90ec..2e3b9a584e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1642,6 +1642,23 @@ void MyAvatar::prepareForPhysicsSimulation() { _prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix); } +// There are a number of possible strategies for this set of tools through endRender, below. +void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { + bool success; + Transform trans = getTransform(success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + return; + } + trans.setTranslation(position); + trans.setRotation(orientation); + SpatiallyNestable::setTransform(trans, success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + } + updateAttitude(); +} + void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { glm::vec3 position; glm::quat orientation; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 648a5b5f29..86321137d4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -432,6 +432,7 @@ public: void updateMotors(); void prepareForPhysicsSimulation(); + void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 89db519abc..db9d36be7a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -184,7 +184,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() override { _skeletonModel->updateAttitude(); } + virtual void updateAttitude() { _skeletonModel->updateAttitude(); } // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6fdb4d1ef6..85228af941 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -110,23 +110,6 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() { return _defaultFullAvatarModelUrl; } -// There are a number of possible strategies for this set of tools through endRender, below. -void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { - bool success; - Transform trans = getTransform(success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - return; - } - trans.setTranslation(position); - trans.setRotation(orientation); - SpatiallyNestable::setTransform(trans, success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - } - updateAttitude(); -} - void AvatarData::setTargetScale(float targetScale) { auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); if (_targetScale != newValue) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 16768ec62a..1cf7d7dd91 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -445,9 +445,6 @@ public: using SpatiallyNestable::setOrientation; virtual void setOrientation(const glm::quat& orientation) override; - void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. - virtual void updateAttitude() {} // Tell skeleton mesh about changes - glm::quat getHeadOrientation() const { lazyInitHeadData(); return _headData->getOrientation(); From f598b1f1f581761010f45ca0f7907a71943c484f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:56:05 -0700 Subject: [PATCH 046/114] fix spelling typo in comment --- interface/src/avatar/MyHead.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 9f2d080cd6..7fc6b9fa26 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -29,7 +29,7 @@ MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) { glm::quat MyHead::getHeadOrientation() const { // NOTE: Head::getHeadOrientation() is not used for orienting the camera "view" while in Oculus mode, so // you may wonder why this code is here. This method will be called while in Oculus mode to determine how - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // to change the driving direction while in Oculus mode. It is used to support driving toward where your // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not // always the same. @@ -39,7 +39,7 @@ glm::quat MyHead::getHeadOrientation() const { return headPose.rotation * Quaternions::Y_180; } - return myAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); + return myAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); } void MyHead::simulate(float deltaTime) { From 49942832474dd2638100d4114ac7d0ab68466f9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:57:18 -0700 Subject: [PATCH 047/114] remove unnecessary state check --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index bd4d1201c7..9a7abc4e98 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -445,7 +445,7 @@ void CharacterController::handleChangedCollisionGroup() { void CharacterController::updateUpAxis(const glm::quat& rotation) { _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (_state != State::Hover && _rigidBody) { + if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); } } From 8c55476c65feb3762141403cc6225bd9f8b85699 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:58:39 -0700 Subject: [PATCH 048/114] fix motor direction when walking upside down also maintain worldUp and remove unnecessary cruft --- interface/src/Application.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 6 +-- interface/src/avatar/MyAvatar.h | 1 - .../src/avatars-renderer/Avatar.cpp | 38 ++++++------------- .../src/avatars-renderer/Avatar.h | 13 ++----- .../src/avatars-renderer/SkeletonModel.cpp | 6 +-- .../src/avatars-renderer/SkeletonModel.h | 2 +- libraries/avatars/src/AvatarData.cpp | 14 +++---- libraries/avatars/src/AvatarData.h | 12 +++--- libraries/shared/src/shared/Camera.h | 2 +- 10 files changed, 38 insertions(+), 60 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dbb94cfdae..166b4fee72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2314,7 +2314,7 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); + auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); // Mirror HMD yaw and roll @@ -2336,7 +2336,7 @@ void Application::paintGL() { + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); } else { - _myCamera.setOrientation(myAvatar->getWorldAlignedOrientation() + _myCamera.setOrientation(myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2e3b9a584e..b644defde2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1291,7 +1291,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() { } glm::vec3 MyAvatar::getDefaultEyePosition() const { - return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); + return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = 1.0f + 1.0f; @@ -1588,7 +1588,7 @@ void MyAvatar::updateMotors() { // non-hovering = walking: follow camera twist about vertical but not lift // so we decompose camera's rotation and store the twist part in motorRotation glm::quat liftRotation; - swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation); + motorRotation = getOrientation(); } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; @@ -1656,7 +1656,7 @@ void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { if (!success) { qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; } - updateAttitude(); + updateAttitude(orientation); } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 86321137d4..2f8f838cf9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -552,7 +552,6 @@ public: Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; - public slots: void increaseSize(); void decreaseSize(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4016592d0a..06814c2707 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -151,11 +151,6 @@ glm::vec3 Avatar::getNeckPosition() const { return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition(); } - -glm::quat Avatar::getWorldAlignedOrientation () const { - return computeRotationFromBodyToWorldUp() * getOrientation(); -} - AABox Avatar::getBounds() const { if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) { // approximately 2m tall, scaled to user request. @@ -436,6 +431,11 @@ void Avatar::slamPosition(const glm::vec3& newPosition) { _lastVelocity = glm::vec3(0.0f); } +void Avatar::updateAttitude(const glm::quat& orientation) { + _skeletonModel->updateAttitude(orientation); + _worldUpDirection = orientation * Vectors::UNIT_Y; +} + void Avatar::applyPositionDelta(const glm::vec3& delta) { setPosition(getPosition() + delta); _positionDeltaAccumulator += delta; @@ -628,22 +628,6 @@ void Avatar::render(RenderArgs* renderArgs) { } } -glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { - glm::quat orientation = getOrientation(); - glm::vec3 currentUp = orientation * IDENTITY_UP; - float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)); - if (angle < EPSILON) { - return glm::quat(); - } - glm::vec3 axis; - if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis - axis = orientation * IDENTITY_RIGHT; - } else { - axis = glm::normalize(glm::cross(currentUp, _worldUpDirection)); - } - return glm::angleAxis(angle * proportion, axis); -} - void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _attachmentsToDelete.clear(); @@ -1401,14 +1385,14 @@ glm::quat Avatar::getUncachedRightPalmRotation() const { return rightPalmRotation; } -void Avatar::setPosition(const glm::vec3& position) { - AvatarData::setPosition(position); - updateAttitude(); +void Avatar::setPositionViaScript(const glm::vec3& position) { + setPosition(position); + updateAttitude(getOrientation()); } -void Avatar::setOrientation(const glm::quat& orientation) { - AvatarData::setOrientation(orientation); - updateAttitude(); +void Avatar::setOrientationViaScript(const glm::quat& orientation) { + setOrientation(orientation); + updateAttitude(orientation); } void Avatar::updatePalms() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index db9d36be7a..2c75012209 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -112,8 +112,6 @@ public: const Head* getHead() const { return static_cast(_headData); } Head* getHead() { return static_cast(_headData); } - glm::quat getWorldAlignedOrientation() const; - AABox getBounds() const; /// Returns the distance to use as a LOD parameter. @@ -184,7 +182,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() { _skeletonModel->updateAttitude(); } + virtual void updateAttitude(const glm::quat& orientation) override; // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity @@ -197,10 +195,8 @@ public: void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); float computeMass(); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + void setPositionViaScript(const glm::vec3& position) override; + void setOrientationViaScript(const glm::quat& orientation) override; // these call through to the SpatiallyNestable versions, but they are here to expose these to javascript. Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } @@ -240,7 +236,7 @@ public: bool hasNewJointData() const { return _hasNewJointData; } float getBoundingRadius() const; - + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); bool isInScene() const { return render::Item::isValidID(_renderItemID); } @@ -303,7 +299,6 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); float getSkeletonHeight() const; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 9651951b46..c0d5fc07d7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -118,16 +118,16 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig.updateFromEyeParameters(eyeParams); } -void SkeletonModel::updateAttitude() { +void SkeletonModel::updateAttitude(const glm::quat& orientation) { setTranslation(_owningAvatar->getSkeletonPosition()); - setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180); + setRotation(orientation * Quaternions::Y_180); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); } // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), // but just before head has been simulated. void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { - updateAttitude(); + updateAttitude(_owningAvatar->getOrientation()); if (fullUpdate) { setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients()); diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index e48884c581..919e82825c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -35,7 +35,7 @@ public: void simulate(float deltaTime, bool fullUpdate = true) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateAttitude(); + void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 85228af941..9570056353 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -91,9 +91,6 @@ AvatarData::AvatarData() : _targetVelocity(0.0f), _density(DEFAULT_AVATAR_DENSITY) { - setBodyPitch(0.0f); - setBodyYaw(-90.0f); - setBodyRoll(0.0f); } AvatarData::~AvatarData() { @@ -2083,6 +2080,7 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { currentBasis = std::make_shared(Transform::fromJson(json[JSON_AVATAR_BASIS])); } + glm::quat orientation; if (json.contains(JSON_AVATAR_RELATIVE)) { // During playback you can either have the recording basis set to the avatar current state // meaning that all playback is relative to this avatars starting position, or @@ -2094,12 +2092,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]); auto worldTransform = currentBasis->worldTransform(relativeTransform); setPosition(worldTransform.getTranslation()); - setOrientation(worldTransform.getRotation()); + orientation = worldTransform.getRotation(); } else { // We still set the position in the case that there is no movement. setPosition(currentBasis->getTranslation()); - setOrientation(currentBasis->getRotation()); + orientation = currentBasis->getRotation(); } + setOrientation(orientation); + updateAttitude(orientation); // Do after avatar orientation because head look-at needs avatar orientation. if (json.contains(JSON_AVATAR_HEAD)) { @@ -2217,11 +2217,11 @@ void AvatarData::setBodyRoll(float bodyRoll) { setOrientation(glm::quat(glm::radians(eulerAngles))); } -void AvatarData::setPosition(const glm::vec3& position) { +void AvatarData::setPositionViaScript(const glm::vec3& position) { SpatiallyNestable::setPosition(position); } -void AvatarData::setOrientation(const glm::quat& orientation) { +void AvatarData::setOrientationViaScript(const glm::quat& orientation) { SpatiallyNestable::setOrientation(orientation); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1cf7d7dd91..0e936c49e0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -351,14 +351,14 @@ public: class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale) Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientationViaScript) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) @@ -440,10 +440,10 @@ public: float getBodyRoll() const; void setBodyRoll(float bodyRoll); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + virtual void setPositionViaScript(const glm::vec3& position); + virtual void setOrientationViaScript(const glm::quat& orientation); + + virtual void updateAttitude(const glm::quat& orientation) {} glm::quat getHeadOrientation() const { lazyInitHeadData(); diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 5f2162ff6e..c7b943f0dd 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -127,7 +127,7 @@ private: glm::mat4 _projection; // derived - glm::vec3 _position; + glm::vec3 _position { 0.0f, 0.0f, 0.0f }; glm::quat _orientation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; From c22798a8e64adfbd0682b5be512b8c06a5b7dd25 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 17:16:07 -0700 Subject: [PATCH 049/114] 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 050/114] 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 051/114] 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 5bc38bd7f07869f3392a8babbda49b75a63cc773 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Fri, 7 Jul 2017 11:15:42 -0400 Subject: [PATCH 052/114] [WL21389] Collision Shapes need to be updated (details below). Revised approach involves creating a helper function within ShapeFactory to aid in devising the ShapeType to be used by an ShapeEntityItem for collision. The ShapeFactory is currently doing this for creating the actual Bullet Library collision shapes. ShapeEntityItem overrides its virtually inherited computeShapeInfo which in turn calls the new ShapeFactory helper function. ShapeEntityItem has a new memvar _collisionShapeType to cache its actual ShapeType used by the physics system. This memvar is returned via the getShapeType accessor which is expected to return an object's ShapeType. Note(s): This is similar to the original approach save translation between entity::Shape and ShapeType isn't tied to the EntityItemProperties shapeTypeNames or shapeType. This approach more directly solves the issue of getting the actual ShapeType used by the time it's needed to determine the bullet collision object type created when initializing the physic information. Translation of the ShapeEntityItem's entity::Shape to its ShapeType is handled by ShapeFactory which handles creating the bullet collision objects when setting up physics on the ShapeEntityItems. Known Issue(s): This doesn't compile. It appears that the Entity Library needs to know about the Physics Library. The naive attempt at providing that link failed to resolve all compilation issues. Current Error: C1083: Cannot open include file: btBulletDynamicsCommon.h: No such file or directory (C:\projects\cusack\libraries\entities\src\ShapeEntityItem.cpp) C:\projects\cusack\libraries\physics\src\ShapeFactory.h 15 1 entities modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/physics/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js new file: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 10 +- libraries/entities/CMakeLists.txt | 3 +- libraries/entities/src/ShapeEntityItem.cpp | 54 ++++- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 203 ++++++++++++++++-- libraries/physics/src/ShapeFactory.h | 3 + libraries/shared/src/ShapeInfo.cpp | 5 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- .../tests/basicEntityTest/shapeSpawner.js | 33 +++ 9 files changed, 286 insertions(+), 33 deletions(-) create mode 100644 scripts/developer/tests/basicEntityTest/shapeSpawner.js diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 62ab3377a8..b197e0b9e6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -48,18 +48,22 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Cube); + result->setShape(entity::Shape::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Sphere); + result->setShape(entity::Shape::Sphere); return result; } diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 19341ec3e2..90de43b5f8 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars) +link_hifi_libraries(shared networking octree avatars physics) + diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 018d8c568a..d9c48f8593 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -58,7 +59,11 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -101,6 +106,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } +//TODO_CUSACK: Move back to header prior to PN +void ShapeEntityItem::setShape( const QString &shape ) { + setShape(entity::shapeFromString(shape)); +} + bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -160,10 +170,46 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } +void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} + + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + { + //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + return SHAPE_TYPE_ELLIPSOID; + } + + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { @@ -223,10 +269,12 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " shape:" << stringFromShape(_shape); + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 96f69deb0c..f021fcf957 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } + void setShape(const QString& shape); float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -84,6 +84,7 @@ public: QColor getQColor() const; void setColor(const QColor& value); + void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } @@ -100,6 +101,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index d209667966..a9bc8b2e05 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -17,7 +17,7 @@ #include "BulletUtil.h" // These are the same normalized directions used by the btShapeHull class. -// 12 points for the face centers of a duodecohedron plus another 30 points +// 12 points for the face centers of a dodecahedron plus another 30 points // for the midpoints the edges, for a total of 42. const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42; static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = { @@ -247,6 +247,124 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +{ + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + outCollisionShape = new btSphereShape(radius); + } + + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + outCollisionShape = createConvexHull(points); + } + + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShape(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x > halfExtents.z) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeX(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeZ(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } + + //Got here, then you are what you are along with outCollisionShape + return type; +} + +ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { + if ( shape == entity::Shape::NUM_SHAPES ) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } + + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } + + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } + + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { + + //TODO_CUSACK: Implement the hedrons + return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); + } + + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { + + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } + +} + const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -255,30 +373,33 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SHAPE_TYPE_SPHERE: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - shape = new btSphereShape(radius); - } - break; + //case SHAPE_TYPE_SPHERE: { + // glm::vec3 halfExtents = info.getHalfExtents(); + // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + // shape = new btSphereShape(radius); + //} + //break; + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - shape = new btSphereShape(radius); - } else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - shape = createConvexHull(points); - } + //float radius = halfExtents.x; + //const float MIN_RADIUS = 0.001f; + //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + //if (radius > MIN_RADIUS + // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // // close enough to true sphere + // shape = new btSphereShape(radius); + //} else { + // ShapeInfo::PointList points; + // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + // } + // shape = createConvexHull(points); + //} + + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); } break; case SHAPE_TYPE_CAPSULE_Y: { @@ -288,6 +409,42 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); + } + break; case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..52b448ee1d 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,11 +15,14 @@ #include #include +#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { + + ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 496e94f8bd..e40d91379c 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -136,7 +136,10 @@ float ShapeInfo::computeVolume() const { } case SHAPE_TYPE_CAPSULE_Y: { float radius = _halfExtents.x; - volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f); + // Need to offset halfExtents.y by x to account for the system treating + // the y extent of the capsule as the cylindrical height + spherical radius. + float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x); + volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f); break; } default: diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index 538e9145f5..fa5c9291cb 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,7 +8,9 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - type: "Sphere", + name: "Cusack_Testing", + type: "Shape", + shapeType: "Cylinder", color: { red: 200, green: 10, diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js new file mode 100644 index 0000000000..f93072a582 --- /dev/null +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -0,0 +1,33 @@ + var orientation = Camera.getOrientation(); + orientation = Quat.safeEulerAngles(orientation); + orientation.x = 0; + orientation = Quat.fromVec3Degrees(orientation); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); + + // Math.random ensures no caching of script + var SCRIPT_URL = Script.resolvePath("myEntityScript.js") + + var myEntity = Entities.addEntity({ + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + color: { + red: 200, + green: 10, + blue: 200 + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + script: SCRIPT_URL + }) + + + function cleanup() { + // Entities.deleteEntity(myEntity); + } + + Script.scriptEnding.connect(cleanup); From 75403124b621835b1bb2c4b9a060980324ab7e1b Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 18 Jul 2017 17:15:02 -0400 Subject: [PATCH 053/114] [WL21389] Addresses physics library dependency and has some other fixes (details below). * Addresses physics library dependency by moving computeShapeInfo override from ShapeEntityItem (which is within Entities Library) to RenderableShapeEntityItem (which is in Entities-Renderer Library). ** Entities-Renderer library already links against the physic library. ** Per discussion with Andrew Meadows: In order to ShapeEntityItem to be utilized the library dependency between the Entity and Physics library would need to be resolved to avoid the cyclical reliance which isn't in the scope of this ticket. * Updates shapeSpawner test script from the default clone of basicEntityTest\entitySpawner.js ** Objects now have a finite lifetime ** Script now cleans up the objects created when the script ends ** Also moved some adjustable properties out into var aliases at the top of the file for easier/less error prone tweaking. Should probably add one for the shapeType. * Fixes some issues with validateShapeType helper function * Removed naive attempt at including physics library within entities library. * Transferred some todos from notes * Fixed some formatting NOTE(s): This compiles and runs. Cylinder is spawned and treated as CYLINDER_Y. TODO(s): * Add tweakable var for shapeType within shapeSpawner.js * Vet and verify other shapes. * Add in edge case handling. * Add in support for other shapes to ShapeInfo infrastructure. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 25 +- .../src/RenderableShapeEntityItem.h | 2 + libraries/entities/CMakeLists.txt | 2 +- libraries/entities/src/ShapeEntityItem.cpp | 61 ++-- libraries/entities/src/ShapeEntityItem.h | 1 - libraries/physics/src/ShapeFactory.cpp | 277 +++++++++--------- libraries/shared/src/ShapeInfo.cpp | 14 +- .../tests/basicEntityTest/shapeSpawner.js | 53 ++-- 8 files changed, 226 insertions(+), 209 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b197e0b9e6..f071518b42 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -48,11 +49,12 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -86,6 +88,23 @@ bool RenderableShapeEntityItem::isTransparent() { } } +void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 0cc6a54f81..96f450f523 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,6 +28,8 @@ public: bool isTransparent() override; + void computeShapeInfo(ShapeInfo& info); + private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 90de43b5f8..84906a48df 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars physics) +link_hifi_libraries(shared networking octree avatars) diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index d9c48f8593..854be7df1e 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,7 +12,6 @@ #include #include -#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -59,11 +58,12 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -96,9 +96,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; + _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -170,46 +172,29 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - - if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); - - //--EARLY EXIT--( allow default handling to process ) - return; - } - - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); - } - - return EntityItem::computeShapeInfo(info); -} - // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - { - //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - return SHAPE_TYPE_ELLIPSOID; - } + //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + //{ + // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + // return SHAPE_TYPE_ELLIPSOID; + //} - return _collisionShapeType; + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index f021fcf957..435d22a6b2 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index a9bc8b2e05..74aff45e55 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,121 +247,120 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) { - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - outCollisionShape = new btSphereShape(radius); - } + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + (*outCollisionShape) = new btSphereShape(radius); + } - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - outCollisionShape = createConvexHull(points); - } + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + (*outCollisionShape) = createConvexHull(points); + } - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShape(btHalfExtents); - } + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + // TODO_CUSACK: Should allow for minor variance along axes? + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShape(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x > halfExtents.z) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeX(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x >= halfExtents.z) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeZ(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } - //Got here, then you are what you are along with outCollisionShape - return type; + //Got here, then you are what you are along with outCollisionShape + return type; } ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if ( shape == entity::Shape::NUM_SHAPES ) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } + if (shape == entity::Shape::NUM_SHAPES) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } + //TODO_CUSACK: Implement the hedrons + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { - //TODO_CUSACK: Implement the hedrons - return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } } @@ -379,7 +378,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = new btSphereShape(radius); //} //break; - case SHAPE_TYPE_SPHERE: + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); //float radius = halfExtents.x; @@ -399,9 +398,11 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = createConvexHull(points); //} - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); } break; + //TODO_CUSACK: Add Capsules to vetting/validation process for + // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -409,42 +410,44 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; - case SHAPE_TYPE_CAPSULE_X: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.y; - float height = 2.0f * halfExtents.x; - shape = new btCapsuleShapeX(radius, height); - } - break; - case SHAPE_TYPE_CAPSULE_Z: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - float height = 2.0f * halfExtents.z; - shape = new btCapsuleShapeZ(radius, height); - } - break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. - const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); - } - break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + } + break; + //TODO_CUSACK: Add compound and simple hull to vetting/validation + // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index e40d91379c..57e927eaf3 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { @@ -55,6 +56,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -62,6 +65,8 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -70,12 +75,17 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { + //TODO_CUSACK: Should this have protection against inadvertant clearance and type + // resetting? If for some reason this was called and point list was and is emtpy + // would we still wish to clear out everything? _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -117,6 +127,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { + //TODO_CUSACK: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -150,6 +161,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { + //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js index f93072a582..9fcaf3a3db 100644 --- a/scripts/developer/tests/basicEntityTest/shapeSpawner.js +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -1,33 +1,30 @@ - var orientation = Camera.getOrientation(); - orientation = Quat.safeEulerAngles(orientation); - orientation.x = 0; - orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); +// compute a position to create the object relative to avatar +var forwardOffset = Vec3.multiply(2.0, Quat.getFront(MyAvatar.orientation)); +var objectPosition = Vec3.sum(MyAvatar.position, forwardOffset); - // Math.random ensures no caching of script - var SCRIPT_URL = Script.resolvePath("myEntityScript.js") +var LIFETIME = 1800; //seconds +var DIM_HEIGHT = 1, DIM_WIDTH = 1, DIM_DEPTH = 1; +var COLOR_R = 100, COLOR_G = 10, COLOR_B = 200; - var myEntity = Entities.addEntity({ - name: "ShapeSpawnTest", - type: "Shape", - shape: "Cylinder", - color: { - red: 200, - green: 10, - blue: 200 - }, - position: center, - dimensions: { - x: 1, - y: 1, - z: 1 - }, - script: SCRIPT_URL - }) +var properties = { + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + dimensions: {x: DIM_WIDTH, y: DIM_HEIGHT, z: DIM_DEPTH}, + color: {red: COLOR_R, green: COLOR_G, blue: COLOR_B}, + position: objectPosition, + lifetime: LIFETIME, +}; + +// create the object +var entityId = Entities.addEntity(properties); + +function cleanup() { + Entities.deleteEntity(entityId); +} + +// delete the object when this script is stopped +Script.scriptEnding.connect(cleanup); - function cleanup() { - // Entities.deleteEntity(myEntity); - } - Script.scriptEnding.connect(cleanup); From d155c0264030c3eec0c231d7f80072b6ab63662b Mon Sep 17 00:00:00 2001 From: Leander Hasty <1p-cusack@1stplayable.com> Date: Mon, 24 Jul 2017 17:02:40 -0400 Subject: [PATCH 054/114] [WL21389] wip and modifications based on comments https://github.com/highfidelity/hifi/pull/11024#pullrequestreview-51611518 Cleans up tabs, moves new functionality out of ShapeFactory directly to RenderableShapeEntityItem's computeShapeInfo override, begins to break down where we will need pointlists. Still need to determine how rotation is handled for pointlists, and check for axis alignment on cylinders before deciding on a shape. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js --- .../src/RenderableShapeEntityItem.cpp | 92 ++++++-- .../src/RenderableShapeEntityItem.h | 2 +- libraries/entities/CMakeLists.txt | 1 - libraries/entities/src/ShapeEntityItem.cpp | 37 +--- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 202 ++++-------------- libraries/physics/src/ShapeFactory.h | 3 - libraries/shared/src/ShapeInfo.cpp | 4 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- 9 files changed, 122 insertions(+), 227 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index f071518b42..a305b201d6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -49,23 +49,18 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Cube); + result->setShape(entity::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Sphere); + result->setShape(entity::Sphere); return result; } @@ -90,19 +85,84 @@ bool RenderableShapeEntityItem::isTransparent() { void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); + // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) + // is set. - //--EARLY EXIT--( allow default handling to process ) - return; + const glm::vec3 entityDimensions = getDimensions(); + + switch (_shape){ + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; + } + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO_CUSACK: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); +#if 0 + // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y + // TODO_CUSACK: Should allow for minor variance along axes? + if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { + } + else if (entityDimensions.x >= entityDimensions.z) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_X; + } + else if (entityDimensions.z >= entityDimensions.x) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a hull + { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } +#endif + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_NONE; + //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + } + break; + default:{ + //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. + } + break; } - return EntityItem::computeShapeInfo(info); + EntityItem::computeShapeInfo(info); } void RenderableShapeEntityItem::render(RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 96f450f523..2a841df57e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info); private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 84906a48df..19341ec3e2 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,3 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) link_hifi_libraries(shared networking octree avatars) - diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 854be7df1e..58799de807 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -58,12 +58,7 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -93,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { + switch (_shape) { // TODO_CUSACK fill out? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? break; default: _type = EntityTypes::Shape; @@ -108,11 +103,6 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } -//TODO_CUSACK: Move back to header prior to PN -void ShapeEntityItem::setShape( const QString &shape ) { - setShape(entity::shapeFromString(shape)); -} - bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -175,25 +165,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} - - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - - //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - //{ - // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - // return SHAPE_TYPE_ELLIPSOID; - //} - return _collisionShapeType; } @@ -254,7 +225,7 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 435d22a6b2..42a92f7bd7 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape); + void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -100,7 +100,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; + ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 74aff45e55..87580c6726 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,123 +247,6 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) -{ - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - (*outCollisionShape) = new btSphereShape(radius); - } - - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - (*outCollisionShape) = createConvexHull(points); - } - - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - // TODO_CUSACK: Should allow for minor variance along axes? - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShape(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x >= halfExtents.z) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } - - //Got here, then you are what you are along with outCollisionShape - return type; -} - -ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if (shape == entity::Shape::NUM_SHAPES) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } - - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } - - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } - - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { - - //TODO_CUSACK: Implement the hedrons - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } - -} - const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -372,37 +255,32 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - //case SHAPE_TYPE_SPHERE: { - // glm::vec3 halfExtents = info.getHalfExtents(); - // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - // shape = new btSphereShape(radius); - //} - //break; - case SHAPE_TYPE_SPHERE: - case SHAPE_TYPE_ELLIPSOID: { + case SHAPE_TYPE_SPHERE: { glm::vec3 halfExtents = info.getHalfExtents(); - //float radius = halfExtents.x; - //const float MIN_RADIUS = 0.001f; - //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - //if (radius > MIN_RADIUS - // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // // close enough to true sphere - // shape = new btSphereShape(radius); - //} else { - // ShapeInfo::PointList points; - // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - // } - // shape = createConvexHull(points); - //} - - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); + float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + shape = new btSphereShape(radius); + } + break; + case SHAPE_TYPE_ELLIPSOID: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + shape = new btSphereShape(radius); + } else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + shape = createConvexHull(points); + } } break; - //TODO_CUSACK: Add Capsules to vetting/validation process for - // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -424,30 +302,22 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShapeZ(radius, height); } break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. + case SHAPE_TYPE_CYLINDER_X: { const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeX(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Z: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeZ(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Y: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShape(btHalfExtents); } break; - //TODO_CUSACK: Add compound and simple hull to vetting/validation - // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 52b448ee1d..2bf79f390c 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,14 +15,11 @@ #include #include -#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { - - ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 57e927eaf3..5d6b4ece7a 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,8 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index fa5c9291cb..538e9145f5 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,9 +8,7 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - name: "Cusack_Testing", - type: "Shape", - shapeType: "Cylinder", + type: "Sphere", color: { red: 200, green: 10, From ef1e426273b3412e63ee511495f8013375250245 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 25 Jul 2017 14:09:46 -0400 Subject: [PATCH 055/114] [WL21389] Some code and todo cleanup in prep for PR1. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 29 +++++-------------- libraries/entities/src/ShapeEntityItem.cpp | 7 ++--- libraries/physics/src/ShapeFactory.cpp | 2 ++ libraries/shared/src/ShapeInfo.cpp | 20 ++++++------- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a305b201d6..5fa2354bd5 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -114,26 +114,13 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { break; case entity::Shape::Cylinder: { _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO_CUSACK: determine if rotation is axis-aligned + // TODO WL21389: determine if rotation is axis-aligned //const Transform::Quat & rot = _transform.getRotation(); -#if 0 - // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y - // TODO_CUSACK: Should allow for minor variance along axes? - if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { - } - else if (entityDimensions.x >= entityDimensions.z) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_X; - } - else if (entityDimensions.z >= entityDimensions.x) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a hull - { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection - } -#endif + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + } break; case entity::Shape::Triangle: @@ -145,15 +132,15 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Dodecahedron: case entity::Shape::Icosahedron: case entity::Shape::Cone: { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; } break; case entity::Shape::Torus: { // Not in GeometryCache::buildShapes, unsupported. _collisionShapeType = SHAPE_TYPE_NONE; - //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) } break; default:{ diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 58799de807..157e3afab3 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO_CUSACK fill out? + switch (_shape) { // TODO WL21389: fill out with other shapes? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,8 +162,7 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -// For now, all polys will act as spheres +// This value specifes how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 87580c6726..97174b2216 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -307,11 +307,13 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeX(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Z: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeZ(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Y: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5d6b4ece7a..5930880859 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,7 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? + //TODO WL21389: Does this need additional cases and handling added? _url = ""; _type = type; setHalfExtents(halfExtents); @@ -56,8 +56,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { - //TODO_CUSACK: Should this pointlist clearance added in case + //TODO WL21389: Should this pointlist clearance added in case // this is a re-purposed instance? + // See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491 _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -65,8 +66,7 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -75,17 +75,14 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { - //TODO_CUSACK: Should this have protection against inadvertant clearance and type - // resetting? If for some reason this was called and point list was and is emtpy - // would we still wish to clear out everything? + //TODO WL21389: May need to skip resetting type here. _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -127,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO_CUSACK: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -161,7 +158,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { - //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. + //TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; @@ -206,6 +203,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const { } const DoubleHashKey& ShapeInfo::getHash() const { + //TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; From 6b5cabf00af08e4278828da0ac3fceec45a6f717 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Tue, 25 Jul 2017 18:12:07 -0400 Subject: [PATCH 056/114] [WL21389] Minor: Mark RenderableShapeEntityItem::computeShapeInfo as an override. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h --- libraries/entities-renderer/src/RenderableShapeEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 2a841df57e..f93e4b991e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - virtual void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info) override; private: std::unique_ptr _procedural { nullptr }; From 7702bfceaa49cc555d39b1800ba7f9adea593a27 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 16:12:32 -0700 Subject: [PATCH 057/114] 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 058/114] 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 33ff5917be70f26b4b5472c52661fcfd8ddfc9c5 Mon Sep 17 00:00:00 2001 From: "scromie@turnmeupgames.com" Date: Wed, 26 Jul 2017 19:34:20 +0300 Subject: [PATCH 059/114] fix --- scripts/system/html/js/entityProperties.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 777ef54085..7ef1a6974b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -475,6 +475,15 @@ function unbindAllInputs() { } } +function clearSelection() { + if(document.selection && document.selection.empty) { + document.selection.empty(); + } else if(window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + } +} + function loaded() { openEventBridge(function() { @@ -1051,6 +1060,7 @@ function loaded() { activeElement.select(); } } + clearSelection(); } }); } From 9147bc21d0a3dadebf61fcf4220899fe45511c49 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 11:41:27 -0700 Subject: [PATCH 060/114] 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 061/114] 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 062/114] 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 063/114] 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 5bd6dac66c87a3369e2e2360d689ec5ce46aa6ff Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:24:56 -0700 Subject: [PATCH 064/114] wrap text if longer than 1m text entity --- scripts/system/chat.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 58a1849f1f..65c7567b30 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -43,6 +43,7 @@ var speechBubbleOffset = {x: 0, y: 0.3, z: 0.0}; // The offset from the joint to whic the speech bubble is attached. var speechBubbleJointName = 'Head'; // The name of the joint to which the speech bubble is attached. var speechBubbleLineHeight = 0.05; // The height of a line of text in the speech bubble. + var SPEECH_BUBBLE_MAX_WIDTH = 1; // meters // Load the persistent variables from the Settings, with defaults. function loadSettings() { @@ -645,8 +646,16 @@ //print("updateSpeechBubble:", "speechBubbleMessage", speechBubbleMessage, "textSize", textSize.width, textSize.height); var fudge = 0.02; + var width = textSize.width + fudge; - var height = textSize.height + fudge; + var height = speechBubbleLineHeight + fudge; + + if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + var numLines = Math.ceil(width); + height = speechBubbleLineHeight * numLines + fudge; + width = SPEECH_BUBBLE_MAX_WIDTH; + }; + dimensions = { x: width, y: height, @@ -672,6 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); + position.y += height / 2; // offset based on wrapped height of bubble speechBubbleParams.position = position; if (!speechBubbleTextID) { From 256853f79b4d15370540950c3e2041c961ab5d1e Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:28:52 -0700 Subject: [PATCH 065/114] syntax and clarification on magic number 2 as half height --- scripts/system/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 65c7567b30..85de9fd0ac 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -650,7 +650,7 @@ var width = textSize.width + fudge; var height = speechBubbleLineHeight + fudge; - if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + if (textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; @@ -681,7 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); - position.y += height / 2; // offset based on wrapped height of bubble + position.y += height / 2; // offset based on half of bubble height speechBubbleParams.position = position; if (!speechBubbleTextID) { From 3a05219c59ae3de69f7fe81e5883d2073f63ff97 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 14:10:41 -0700 Subject: [PATCH 066/114] 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 067/114] 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 068/114] 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 069/114] 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 070/114] 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 dd76c30de5b703a5250fddd4978e869b517922ce Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Thu, 27 Jul 2017 17:31:30 +0700 Subject: [PATCH 071/114] Make Xylophone mallets equipable, make the mallets provide haptic feedback, fix a closing bracket bug stopping the xylophone from working and clean up code according to coding standards --- .../marketplace/xylophone/pUtils.js | 34 ----- .../marketplace/xylophone/xylophoneKey.js | 48 +++++-- .../marketplace/xylophone/xylophoneRezzer.js | 117 +++++++++++------- 3 files changed, 111 insertions(+), 88 deletions(-) delete mode 100644 unpublishedScripts/marketplace/xylophone/pUtils.js diff --git a/unpublishedScripts/marketplace/xylophone/pUtils.js b/unpublishedScripts/marketplace/xylophone/pUtils.js deleted file mode 100644 index 2cafbc1f50..0000000000 --- a/unpublishedScripts/marketplace/xylophone/pUtils.js +++ /dev/null @@ -1,34 +0,0 @@ -// -// pUtils.js -// -// Created by Patrick Gosch on 03/28/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 -// - -getEntityTextures = function(id) { - var results = null; - var properties = Entities.getEntityProperties(id, "textures"); - if (properties.textures) { - try { - results = JSON.parse(properties.textures); - } catch (err) { - logDebug(err); - logDebug(properties.textures); - } - } - return results ? results : {}; -}; - -setEntityTextures = function(id, textureList) { - var json = JSON.stringify(textureList); - Entities.editEntity(id, {textures: json}); -}; - -editEntityTextures = function(id, textureName, textureURL) { - var textureList = getEntityTextures(id); - textureList[textureName] = textureURL; - setEntityTextures(id, textureList); -}; diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index afba7e8075..38b8552b6e 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -11,8 +11,9 @@ (function() { Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; - var TEXGRAY = Script.resolvePath("xylotex_bar_gray.png"); - var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); + var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); + var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); + var IS_DEBUG = false; var _this; function XylophoneKey() { @@ -22,7 +23,7 @@ XylophoneKey.prototype = { sound: null, isWaiting: false, - homePos: null, + homePosition: null, injector: null, preload: function(entityID) { @@ -45,20 +46,51 @@ hit: function() { if (!_this.isWaiting) { _this.isWaiting = true; - _this.homePos = Entities.getEntityProperties(_this.entityID, ["position"]).position; - _this.injector = Audio.playSound(_this.sound, {position: _this.homePos, volume: 1}); - editEntityTextures(_this.entityID, "file5", TEXGRAY); + _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; + _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; + var HAPTIC_DURATION = 20; + var HAPTIC_HAND = 2; // Both hands + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); _this.timeout(); } }, timeout: function() { Script.setTimeout(function() { - editEntityTextures(_this.entityID, "file5", TEXBLACK); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_BLACK); _this.isWaiting = false; }, TIMEOUT); + }, + + getEntityTextures: function(id) { + var results = null; + var properties = Entities.getEntityProperties(id, "textures"); + if (properties.textures) { + try { + results = JSON.parse(properties.textures); + } catch (err) { + if (IS_DEBUG) { + print(err); + print(properties.textures); + } + } + } + return results ? results : {}; + }, + + setEntityTextures: function(id, textureList) { + var json = JSON.stringify(textureList); + Entities.editEntity(id, {textures: json}); + }, + + editEntityTextures: function(id, textureName, textureURL) { + var textureList = _this.getEntityTextures(id); + textureList[textureName] = textureURL; + _this.setEntityTextures(id, textureList); + } }; return new XylophoneKey(); - }); diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index 6416f81037..e99341bb19 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -8,65 +8,69 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var soundFiles = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; -var keyModelURL = Script.resolvePath("xyloKey_2_a_e.fbx"); -var keyScriptURL = Script.resolvePath("xylophoneKey.js"); -var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); -var malletModelURL = Script.resolvePath("Mallet3-2pc.fbx"); -var malletModelColliderURL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; +var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); +var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); +var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); +var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var FORWARD = { x: 0, y: 0, z: -1 }; var center = MyAvatar.position; -var fwd = {x:0, y:0, z:-1}; -var xyloFramePos = Vec3.sum(center, Vec3.multiply(fwd, 0.8)); -var xyloFrameID = Entities.addEntity( { +var XYLOPHONE_FORWARD_OFFSET = 0.8; +var xylophoneFramePosition = Vec3.sum(center, Vec3.multiply(FORWARD, XYLOPHONE_FORWARD_OFFSET)); +var xylophoneFrameID = Entities.addEntity({ name: "Xylophone", type: "Model", modelURL: Script.resolvePath("xylophoneFrameWithWave.fbx"), - position: xyloFramePos, - rotation: Quat.fromVec3Radians({x:0, y:Math.PI, z:0}), + position: xylophoneFramePosition, + rotation: Quat.fromVec3Radians({ x: 0, y: Math.PI, z: 0 }), shapeType: "static-mesh" }); -center.y += (0.45); // key Y offset from frame -var keyPos, keyRot, ud, td, keyID; -for (var i = 1; i <= soundFiles.length; i++) { +var KEY_Y_OFFSET = 0.45; +center.y += KEY_Y_OFFSET; +var keyPosition, keyRotation, userData, textureData, keyID; +var ROTATION_START = 0.9; +var ROTATION_DELTA = 0.2; +for (var i = 1; i <= SOUND_FILES.length; i++) { + + keyRotation = Quat.fromVec3Radians({ x: 0, y: ROTATION_START - (i*ROTATION_DELTA), z: 0 }); + keyPosition = Vec3.sum(center, Vec3.multiplyQbyV(keyRotation, FORWARD)); - keyRot = Quat.fromVec3Radians({x:0, y:(0.9 - (i*0.2)), z:0}); - keyPos = Vec3.sum(center, Vec3.multiplyQbyV(keyRot, fwd)); - - ud = { - soundFile: soundFiles[i-1] + userData = { + soundFile: SOUND_FILES[i-1] }; - td = { + textureData = { "file4": Script.resolvePath("xylotex_bar" + i + ".png"), - "file5": TEXBLACK + "file5": TEXTURE_BLACK }; - keyID = Entities.addEntity( { + keyID = Entities.addEntity({ name: ("XyloKey" + i), type: "Model", - modelURL: keyModelURL, - position: keyPos, - rotation: keyRot, + modelURL: KEY_MODEL_URL, + position: keyPosition, + rotation: keyRotation, shapeType: "static-mesh", - script: keyScriptURL, - textures: JSON.stringify(td), - userData: JSON.stringify(ud), - parentID: xyloFrameID - } ); + script: KEY_SCRIPT_URL, + textures: JSON.stringify(textureData), + userData: JSON.stringify(userData), + parentID: xylophoneFrameID + }); } // if rezzed on/above something, wait until after model has loaded so you can read its dimensions then move object on to that surface. -var pickRay = {origin: center, direction: {x:0, y:-1, z:0}}; +var pickRay = {origin: center, direction: {x: 0, y: -1, z: 0}}; var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects && (intersection.distance < 10)) { var surfaceY = intersection.intersection.y; Script.setTimeout( function() { // should add loop to check for fbx loaded instead of delay - var xyloDimensions = Entities.getEntityProperties(xyloFrameID, ["dimensions"]).dimensions; - xyloFramePos.y = surfaceY + (xyloDimensions.y/2); - Entities.editEntity(xyloFrameID, {position: xyloFramePos}); + var xylophoneDimensions = Entities.getEntityProperties(xylophoneFrameID, ["dimensions"]).dimensions; + xylophoneFramePosition.y = surfaceY + (xylophoneDimensions.y/2); + Entities.editEntity(xylophoneFrameID, {position: xylophoneFramePosition}); rezMallets(); }, 2000); } else { @@ -75,28 +79,49 @@ if (intersection.intersects && (intersection.distance < 10)) { } function rezMallets() { - var malletProps = { + var malletProperties = { name: "Xylophone Mallet", type: "Model", - modelURL: malletModelURL, - compoundShapeURL: malletModelColliderURL, - collidesWith: "static,dynamic,kinematic,", + modelURL: MALLET_MODEL_URL, + compoundShapeURL: MALLET_MODEL_COLLIDER_URL, + collidesWith: "static,dynamic,kinematic", collisionMask: 7, collisionsWillMove: 1, dynamic: 1, damping: 1, angularDamping: 1, shapeType: "compound", - userData: "{\"grabbableKey\":{\"grabbable\":true}}", - dimensions: {"x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911} // not being set from fbx for some reason. + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + LeftHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ], + RightHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ] + } + } + }), + dimensions: { "x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911 } // not being set from fbx for some reason. }; - malletProps.position = Vec3.sum(xyloFramePos, {x: 0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI - 0.1, z:0}); - Entities.addEntity(malletProps); + var LEFT_MALLET_POSITION = { x: 0.1, y: 0.55, z: 0 }; + var LEFT_MALLET_ROTATION = { x: 0, y: Math.PI - 0.1, z: 0 }; + var RIGHT_MALLET_POSITION = { x: -0.1, y: 0.55, z: 0 }; + var RIGHT_MALLET_ROTATION = { x: 0, y: Math.PI + 0.1, z: 0 }; - malletProps.position = Vec3.sum(xyloFramePos, {x: -0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI + 0.1, z:0}); - Entities.addEntity(malletProps); + malletProperties.position = Vec3.sum(xylophoneFramePosition, LEFT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(LEFT_MALLET_ROTATION); + Entities.addEntity(malletProperties); + + malletProperties.position = Vec3.sum(xylophoneFramePosition, RIGHT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(RIGHT_MALLET_ROTATION); + Entities.addEntity(malletProperties); Script.stop(); } \ No newline at end of file From 8b7a29c3f3250ebfd777bf93f8d0153981feb3b2 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Fri, 28 Jul 2017 06:22:29 +0700 Subject: [PATCH 072/114] remove putils reference --- unpublishedScripts/marketplace/xylophone/xylophoneKey.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 38b8552b6e..5cff66e557 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,7 +9,6 @@ // (function() { - Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); From 45934cb53e2b1622ef3a0366f6703b68f563ca33 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 28 Jul 2017 18:04:29 +0200 Subject: [PATCH 073/114] Fix Landscape mode, when menu pushed in Create mode. Fix pages gets white in Create mode --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 ++ libraries/ui/src/ui/TabletScriptingInterface.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..080c72f3bb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -205,6 +205,8 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); + _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); // mark the TabletProxy object as cpp ownership. diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6ff5e46cea..b74d9fcb5c 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -483,6 +483,11 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { return result; } + //set landscape off when pushing menu items while in Create mode + if (_landscape) { + setLandscape(false); + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; From 3ec9640ea1e8bdf201dd56e8043432bf9b4262e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Jul 2017 09:22:47 -0700 Subject: [PATCH 074/114] fix calculation of walk motor for HMD --- interface/src/avatar/MyAvatar.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b644defde2..83d170300b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1586,9 +1586,14 @@ void MyAvatar::updateMotors() { motorRotation = getMyHead()->getHeadOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift - // so we decompose camera's rotation and store the twist part in motorRotation + // we decompose camera's rotation and store the twist part in motorRotation + // however, we need to perform the decomposition in the avatar-frame + // using the local UP axis and then transform back into world-frame + glm::quat orientation = getOrientation(); + glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame glm::quat liftRotation; - motorRotation = getOrientation(); + swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation); + motorRotation = orientation * motorRotation; } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; From cc4fbc97cdab663701315f823e928e9fd2429646 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Fri, 28 Jul 2017 15:59:17 -0400 Subject: [PATCH 075/114] [WL21389] PR1 Update based on code review discussion & feedback (details below). * Removed some left overs from prior approach. * Moved _collisionShapeType & getShapeType override from ShapeEntityItem to RenderableShapeEntityItem (see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r130154903) * Switched _collisionShapeType default from SHAPE_TYPE_NONE to SHAPE_TYPE_ELLIPSOID ** see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r129982909 Note(s): * Retested and the cylinder behaves as expected along with the Box & Sphere shapes save from the previously mentioned caveats in the PR notes (https://github.com/highfidelity/hifi/pull/11048) * Confirmed that currently unsupported shapes (hedrons, polygons, & cone) fallback to ellipsoid behavior given default change. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 112 +++++++++--------- .../src/RenderableShapeEntityItem.h | 7 ++ libraries/entities/src/ShapeEntityItem.cpp | 9 +- libraries/entities/src/ShapeEntityItem.h | 2 - libraries/shared/src/ShapeInfo.cpp | 2 +- 5 files changed, 67 insertions(+), 65 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5fa2354bd5..c853335915 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -91,67 +90,72 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { const glm::vec3 entityDimensions = getDimensions(); switch (_shape){ - case entity::Shape::Quad: - case entity::Shape::Cube: { - _collisionShapeType = SHAPE_TYPE_BOX; - } - break; - case entity::Shape::Sphere: { - - float diameter = entityDimensions.x; - const float MIN_DIAMETER = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (diameter > MIN_DIAMETER - && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { - - _collisionShapeType = SHAPE_TYPE_SPHERE; + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; } - else { + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; + } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO WL21389: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); + + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) + } + break; + default:{ _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - } - break; - case entity::Shape::Cylinder: { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO WL21389: determine if rotation is axis-aligned - //const Transform::Quat & rot = _transform.getRotation(); - - // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and - // hull ( or dimensions, need circular cross section) - // Should allow for minor variance along axes? - - } - break; - case entity::Shape::Triangle: - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: - case entity::Shape::Cone: { - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - } - break; - case entity::Shape::Torus: - { - // Not in GeometryCache::buildShapes, unsupported. - _collisionShapeType = SHAPE_TYPE_NONE; - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) - } - break; - default:{ - //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. - } - break; + break; } EntityItem::computeShapeInfo(info); } +// This value specifes how the shape should be treated by physics calculations. +ShapeType RenderableShapeEntityItem::getShapeType() const { + return _collisionShapeType; +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index f93e4b991e..d603cdedef 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -29,10 +29,17 @@ public: bool isTransparent() override; virtual void computeShapeInfo(ShapeInfo& info) override; + ShapeType getShapeType() const override; + private: std::unique_ptr _procedural { nullptr }; + //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain + //! prior functionality where new or unsupported shapes are treated as + //! ellipsoids. + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; + SIMPLE_RENDERABLE(); }; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 157e3afab3..eb1bed503c 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,12 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO WL21389: fill out with other shapes? + switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; - _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,11 +160,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -ShapeType ShapeEntityItem::getShapeType() const { - return _collisionShapeType; -} - void ShapeEntityItem::setColor(const rgbColor& value) { memcpy(_color, value, sizeof(rgbColor)); } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 42a92f7bd7..60fcfd628a 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } bool supportsDetailedRayIntersection() const override; @@ -100,7 +99,6 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5930880859..a556548b25 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -124,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO WL21389: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc). const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { From 3c9ab36416a187251a22947358a522dfdc4e6ace Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 29 Jul 2017 12:00:41 +0200 Subject: [PATCH 076/114] Remove already already implemented fix --- interface/src/ui/overlays/Web3DOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ffb4faf83e..2bcb247df8 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -192,7 +192,6 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); From 734330dc063311990e8308bed080582b8655fbc7 Mon Sep 17 00:00:00 2001 From: Menithal Date: Sat, 29 Jul 2017 15:48:31 +0300 Subject: [PATCH 077/114] Fixed Particle Editor Update event Particle Editor was agressively refreshing when ever a particle was be ing up dated Added a flag check to make sure the particle was always using the latest version --- scripts/system/edit.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..02cc35e645 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/"; @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function () { +selectionManager.addEventListener(function (test) { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); @@ -275,7 +275,8 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); } entityID = Entities.addEntity(properties); - if (properties.type == "ParticleEffect") { + + if (properties.type === "ParticleEffect") { selectParticleEntity(entityID); } @@ -2229,10 +2230,9 @@ var particleExplorerTool = new ParticleExplorerTool(); var selectedParticleEntity = 0; var selectedParticleEntityID = null; - function selectParticleEntity(entityID) { var properties = Entities.getEntityProperties(entityID); - + selectedParticleEntityID = entityID; if (properties.emitOrientation) { properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); } @@ -2274,7 +2274,6 @@ entityListTool.webView.webEventReceived.connect(function (data) { return; } // Destroy the old particles web view first - selectParticleEntity(ids[0]); } else { selectedParticleEntity = 0; particleExplorerTool.destroyWebView(); From bb7110d81b47957a6757b801ccf3af39c6f2fc2a Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 30 Jul 2017 19:22:54 +0200 Subject: [PATCH 078/114] 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 079/114] 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 5fab6c37a3daec1beb0302af43fff7ce2f46f05a Mon Sep 17 00:00:00 2001 From: Matti Lahtinen Date: Mon, 31 Jul 2017 08:56:04 +0300 Subject: [PATCH 080/114] Removed stray variable test --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 02cc35e645..300db5d50a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function (test) { +selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); From 1f6b92a5190966886712a5356ecef593215eb75b Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Mon, 31 Jul 2017 17:27:49 +0700 Subject: [PATCH 081/114] Vibrate only the controller that hits the key and decrease timeout so that the xylophone can be played faster --- .../marketplace/xylophone/xylophoneKey.js | 22 +++++++++++-------- .../marketplace/xylophone/xylophoneRezzer.js | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 5cff66e557..3d131252c8 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,12 +9,12 @@ // (function() { - var TIMEOUT = 150; + var TIMEOUT = 50; // at 30 ms, the key's color sometimes fails to switch when hit var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var IS_DEBUG = false; var _this; - + function XylophoneKey() { _this = this; } @@ -34,24 +34,28 @@ collisionWithEntity: function(thisEntity, otherEntity, collision) { if (collision.type === 0) { - _this.hit(); + _this.hit(otherEntity); } }, - clickDownOnEntity: function() { - _this.hit(); + clickDownOnEntity: function(otherEntity) { + _this.hit(otherEntity); }, - hit: function() { + hit: function(otherEntity) { if (!_this.isWaiting) { _this.isWaiting = true; _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; - var HAPTIC_DURATION = 20; - var HAPTIC_HAND = 2; // Both hands - Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); + var HAPTIC_DURATION = 20; + var userData = JSON.parse(Entities.getEntityProperties(otherEntity, 'userData').userData); + if (userData.hasOwnProperty('hand')){ + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, userData.hand); + } + _this.timeout(); } }, diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index e99341bb19..6adf8710a7 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -11,6 +11,7 @@ var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var MALLET_SCRIPT_URL = Script.resolvePath("xylophoneMallet.js"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); @@ -91,6 +92,7 @@ function rezMallets() { damping: 1, angularDamping: 1, shapeType: "compound", + script: MALLET_SCRIPT_URL, userData: JSON.stringify({ grabbableKey: { invertSolidWhileHeld: true From e30e0b1a5f43f92c6ef0cc7e6117da209a6f3812 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 31 Jul 2017 16:45:21 +0200 Subject: [PATCH 082/114] 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 ed9ea17917d2f2d49630d8a8329dab97ccbe0730 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 31 Jul 2017 08:02:18 -0700 Subject: [PATCH 083/114] remove semi-colon --- scripts/system/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 85de9fd0ac..fa997e20cc 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -654,7 +654,7 @@ var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; - }; + } dimensions = { x: width, From 60201548670ca5549691dbab2091b93e14e4042d Mon Sep 17 00:00:00 2001 From: Menithal Date: Mon, 31 Jul 2017 22:49:11 +0300 Subject: [PATCH 084/114] Removed some whitespaces to trigger new build --- scripts/system/edit.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 300db5d50a..b5e54e7a57 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -26,11 +26,8 @@ Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", "libraries/progressDialog.js", - "libraries/entitySelectionTool.js", - "libraries/ToolTip.js", - "libraries/entityCameraTool.js", "libraries/gridTool.js", "libraries/entityList.js", From 43c8079f0383c8f92ca44d1d21773d87a6c14221 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 13:55:43 -0700 Subject: [PATCH 085/114] 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 0b79809f547c9ec90c3b611a02972feabdaa3584 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Mon, 31 Jul 2017 17:08:30 -0400 Subject: [PATCH 086/114] [WL21389] PR1 Feedback: Small change missed last commit (details below). Until they're properly implemented, default the hedrons and polygons to SHAPE_TYPE_ELLIPSOID within RenderableShapeEntity::computeShapeInfo. Changes committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp --- libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c853335915..3f9497741f 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -132,7 +132,7 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Icosahedron: case entity::Shape::Cone: { //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } break; case entity::Shape::Torus: From b371c875c47bf53a24857064c2dc5c3907fdbbc6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 14:46:26 -0700 Subject: [PATCH 087/114] 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 088/114] 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 46966b11321254fd96e5b14d0a052f85b68e0857 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:19:34 -0700 Subject: [PATCH 089/114] switching from LZMA (default) to BZIP2 for NSIS compression --- cmake/macros/GenerateInstallers.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..0296ad9901 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,6 +22,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From ae31df426c32094f780abecbed1c09ec70e9f648 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:52:38 -0700 Subject: [PATCH 090/114] 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 6855dcbacf253d630e15c1d82340434da1a4c9e0 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 17:17:11 -0700 Subject: [PATCH 091/114] use bzip2 compression only for PR builds (so that we internally benefit from faster builds) --- cmake/macros/GenerateInstallers.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0296ad9901..aa681e27b4 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,7 +22,9 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) - set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + if (PR_BUILD) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + endif () set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From 9b28d546e037f5dc1a9b1a4cd729ea26f4deb73d Mon Sep 17 00:00:00 2001 From: "Scromie@turnmeupgames.com" Date: Tue, 1 Aug 2017 12:32:27 +0300 Subject: [PATCH 092/114] Test plan --- Test Plan 2.docx | Bin 0 -> 7305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Test Plan 2.docx diff --git a/Test Plan 2.docx b/Test Plan 2.docx new file mode 100644 index 0000000000000000000000000000000000000000..da60821b53e86ea9383e166da7f1d63dba588740 GIT binary patch literal 7305 zcmaKRWmp_ruhxxxyQt50eftRLb;_hGa=-*>-7q`i6obI~u09 zS^X?VV`Ygmjg1v#rM3{s0L((Ne{TIbVH89$@6 zt%i_E@gJCFYZ+(==A^6h_ℜHr^16?r6-Yt^lIBVxkg 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 I;wQ-e1EA-uc>n+a literal 0 HcmV?d00001 From f1d7ef76e7f28cce1bee01f47af6229903327bd9 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Tue, 1 Aug 2017 23:41:16 +0700 Subject: [PATCH 093/114] Add the mallet js file --- .../marketplace/xylophone/xylophoneMallet.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 unpublishedScripts/marketplace/xylophone/xylophoneMallet.js diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js new file mode 100644 index 0000000000..061d1303bb --- /dev/null +++ b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js @@ -0,0 +1,25 @@ +// +// xylophoneMallet.js +// +// Created by Johnathan Franck on 07/30/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 + +(function() { + function XylophoneMallet() { + } + + XylophoneMallet.prototype = { + startEquip: function(entityID, args) { + var LEFT_HAND = 0; + var RIGHT_HAND = 1; + var userData = JSON.parse(Entities.getEntityProperties(entityID, 'userData').userData); + userData.hand = args[0] === "left" ? LEFT_HAND : RIGHT_HAND; + Entities.editEntity(entityID, {userData: JSON.stringify(userData)}); + } + }; + + return new XylophoneMallet(); +}); From 3a9b8c02a6532f4c414897bd8d28ee5d271db482 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 1 Aug 2017 13:41:28 -0400 Subject: [PATCH 094/114] * disable Avatar "follow" / recentering behavior when using HMD + "View > Mirror" * restore access to "View > Mirror" from HMD mode --- interface/src/Application.cpp | 6 ++---- interface/src/avatar/MyAvatar.cpp | 3 ++- scripts/system/hmd.js | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 94d9d6e885..69359660af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4483,11 +4483,9 @@ void Application::cameraModeChanged() { void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { - if (isHMDMode()) { - menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - } else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { + if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); + getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers } } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 490f22ed8b..160235d77e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2849,7 +2849,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - if (myAvatar.getHMDLeanRecenterEnabled()) { + if (myAvatar.getHMDLeanRecenterEnabled() && + qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); } diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c545e6bcee..3598b461a4 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -40,9 +40,8 @@ function updateControllerDisplay() { var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. -// Disable them in hmd. -var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; +// Independent and Entity mode make people sick; disable them in hmd. +var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { HMD.closeTablet(); From c22e08f3e8ac3e4a550f0d7f7e4316bd090109dc Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 19:58:23 +0100 Subject: [PATCH 095/114] loacked script engine in EntityEditPacketSender --- libraries/entities/src/EntityEditPacketSender.cpp | 1 + libraries/entities/src/EntityEditPacketSender.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 7845b0d5e3..ee0fcf8218 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -54,6 +54,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, EntityItemProperties entityProperties = entity->getProperties(); entityProperties.merge(properties); + std::lock_guard lock(_mutex); QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); QVariant variantProperties = scriptProperties.toVariant(); QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 9190a8296a..81efaa865c 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -14,6 +14,8 @@ #include +#include + #include "EntityItem.h" #include "AvatarData.h" @@ -49,6 +51,7 @@ private: EntityItemID entityItemID, const EntityItemProperties& properties); private: + std::mutex _mutex; AvatarData* _myAvatar { nullptr }; QScriptEngine _scriptEngine; }; From cca29e849b50a8b982144439b3ca46d2cb2f73d9 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 23:18:42 +0100 Subject: [PATCH 096/114] fix issue of tablet begin blank when switching between HMD and Desktop mode --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 43c460fd23..e7b736d232 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -236,7 +236,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _desktopWindow = nullptr; } } - loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } From 762b4098143b4fa68cf10e16e232ca5229fe6a7f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:20:33 -0700 Subject: [PATCH 097/114] 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 098/114] 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 1b6feb63c8d901d484dfac389fbf7bb4d1ff46b5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Aug 2017 18:40:43 -0700 Subject: [PATCH 099/114] Fix for crash when attempting to teleport while avatar was loading The fix had two parts. * Make Avatar::getAbsoluteDefaultJointXXXInObjectFrame thread safe * Make teleport.js handle a zero foot offset more gracefully, to prevent the avatar from teleporting into the floor. --- .../src/avatars-renderer/Avatar.cpp | 33 ++++++++++++++----- scripts/system/controllers/teleport.js | 8 ++++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 06814c2707..d775fe05f0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -899,17 +899,34 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { } glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const { - glm::quat rotation; - glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot(); - return Quaternions::Y_180 * rot; + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + auto skeleton = model->getRig().getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // The rotation part of the geometry-to-rig transform is always identity so we can skip it. + // Y_180 is to convert from rig-frame into avatar-frame + return Quaternions::Y_180 * skeleton->getAbsoluteDefaultPose(index).rot(); + } + } + return Quaternions::Y_180; } glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const { - glm::vec3 translation; - const Rig& rig = _skeletonModel->getRig(); - glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); - glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans); + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + const Rig& rig = model->getRig(); + auto skeleton = rig.getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // trans is in geometry frame. + glm::vec3 trans = skeleton->getAbsoluteDefaultPose(index).trans(); + // Y_180 is to convert from rig-frame into avatar-frame + glm::mat4 geomToAvatarMat = Matrices::Y_180 * rig.getGeometryToRigTransform(); + return transformPoint(geomToAvatarMat, trans); + } + } + return Vectors::ZERO; } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index b058ec670f..15ba314a1d 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -381,7 +381,13 @@ function getAvatarFootOffset() { } if (footJointIndex != -1) { // default vertical offset from foot to avatar root. - return -MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex).y; + var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); + if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { + // if footPos is exactly zero, it's probably wrong because avatar is currently loading, fall back to default. + return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; + } else { + return -footPos.y; + } } else { return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; } From 4330e312122448354b9f892ec5e306812d54fbf4 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 16:32:09 +0200 Subject: [PATCH 100/114] Toolbar mode: 1. Fixed switching between different apps. 2. Fixed switching from Create --- interface/resources/qml/hifi/audio/Audio.qml | 2 +- libraries/ui/src/ui/TabletScriptingInterface.cpp | 13 ++++++------- scripts/system/edit.js | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 03d27e3831..93742e39a5 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -33,7 +33,7 @@ Rectangle { // only show the title if loaded through a "loader" function showTitle() { - return root.parent.objectName == "loader"; + return (root.parent !== null) && root.parent.objectName == "loader"; } Column { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..2bdad096f4 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -237,7 +237,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } } loadHomeScreen(true); - emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { @@ -463,14 +462,14 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } if (root) { - if (_state != State::QML) { - removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); - _state = State::QML; + removeButtonsFromHomeScreen(); //works only in Tablet + QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); + _state = State::QML; + if (path != _currentPathLoaded) { emit screenChanged(QVariant("QML"), path); - _currentPathLoaded = path; - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } + _currentPathLoaded = path; + QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } else { qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..046cd2e66a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -419,7 +419,7 @@ var toolBar = (function () { createButton = activeButton; tablet.screenChanged.connect(function (type, url) { if (isActive && (type !== "QML" || url !== "Edit.qml")) { - that.toggle(); + that.setActive(false) } }); tablet.fromQml.connect(fromQml); From 05f1caab01b8fa1a0e872c92898559a81c0148b4 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Aug 2017 16:35:58 +0100 Subject: [PATCH 101/114] better solution to help and blank tablet issues --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index e7b736d232..603b85c160 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -228,7 +228,12 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); } else { removeButtonsFromToolbar(); - addButtonsToHomeScreen(); + + if (_currentPathLoaded == TABLET_SOURCE_URL) { + addButtonsToHomeScreen(); + } else { + loadHomeScreen(true); + } // destroy desktop window if (_desktopWindow) { From 7d361c69c2493e6198c19a5fe750be87f60d3e4d Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 19:52:47 +0200 Subject: [PATCH 102/114] Audio mic bar show only rectangle on mouse over --- interface/resources/qml/hifi/audio/MicBar.qml | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 10e12551b7..e757d7cadf 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -27,7 +27,7 @@ Rectangle { color: "#00000000"; border { - width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0; + width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0; color: colors.border; } @@ -61,7 +61,7 @@ Rectangle { drag.target: dragTarget; } - Item { + QtObject { id: colors; readonly property string unmuted: "#FFF"; @@ -72,7 +72,7 @@ Rectangle { readonly property string red: colors.muted; readonly property string fill: "#55000000"; readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; - readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted; + readonly property string icon: Audio.muted ? muted : unmuted; } Item { @@ -92,10 +92,8 @@ Rectangle { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; - function exclusiveOr(a, b) { return (a || b) && !(a && b); } - id: image; - source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon; + source: Audio.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -118,9 +116,9 @@ Rectangle { Item { id: status; - readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted; + readonly property string color: Audio.muted ? colors.muted : colors.unmuted; - visible: Audio.muted || mouseArea.containsMouse; + visible: Audio.muted; anchors { left: parent.left; @@ -133,14 +131,14 @@ Rectangle { Text { anchors { - horizontalCenter: parent.horizontalCenter; + horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter; } color: parent.color; - text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE"; - font.pointSize: 12; + text: Audio.muted ? "MUTED" : "MUTE"; + font.pointSize: 12; } Rectangle { @@ -150,7 +148,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } @@ -161,7 +159,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } } From 106b0ad8bb7e1118d6abd2f0f7aa747588fedc3e Mon Sep 17 00:00:00 2001 From: seefo Date: Mon, 31 Jul 2017 14:37:40 -0700 Subject: [PATCH 103/114] 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 104/114] 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 105/114] 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 106/114] 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 107/114] 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 3c1607715ecaec1952d9aadfdfcf7d562b79f5f2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 2 Aug 2017 17:42:56 -0700 Subject: [PATCH 108/114] Updated WASAPI plugin --- cmake/externals/wasapi/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 1bf195fc84..4437024962 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi8.zip - URL_MD5 b01510437ea15527156bc25cdf733bd9 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip + URL_MD5 94f4765bdbcd53cd099f349ae031e769 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 4b7779f0f55381a1ea10f1b41eb6688ec7ecab69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 3 Aug 2017 07:57:21 -0700 Subject: [PATCH 109/114] 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 110/114] 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 111/114] 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 112/114] 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 498ac5d0ab65209aa40230435c10572ecde04f9d Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 15:08:06 -0700 Subject: [PATCH 113/114] 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 114/114] 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);