diff --git a/BUILD.md b/BUILD.md index ae4a67c2ee..e2f310bb1f 100644 --- a/BUILD.md +++ b/BUILD.md @@ -13,7 +13,6 @@ * [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4 * [gverb](https://github.com/highfidelity/gverb) -* [Soxr](http://sourceforge.net/projects/soxr/) ~> 0.1.1 The following external projects are optional dependencies. You can indicate to CMake that you would like to include them by passing -DGET_$NAME=1 when running a clean CMake build. For example, to get CMake to download and compile SDL2 you would pass -DGET_SDL2=1. diff --git a/cmake/externals/soxr/CMakeLists.txt b/cmake/externals/soxr/CMakeLists.txt deleted file mode 100644 index 69d2276ab9..0000000000 --- a/cmake/externals/soxr/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -set(EXTERNAL_NAME soxr) - -if (ANDROID) - set(PLATFORM_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19" "-DHAVE_WORDS_BIGENDIAN_EXITCODE=1") -endif () - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/soxr-0.1.1.zip - URL_MD5 349b5b2f323a7380bc12186d98c77d1d - CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DBUILD_SHARED_LIBS=1 -DBUILD_TESTS=0 -DCMAKE_INSTALL_PREFIX:PATH= - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of soxr include directories") - -if (WIN32) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/soxr.lib CACHE FILEPATH "List of soxr libraries") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE PATH "Path to soxr dll") -elseif (APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libsoxr.dylib CACHE FILEPATH "List of soxr libraries") -else () - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libsoxr.so CACHE FILEPATH "List of soxr libraries") -endif () \ No newline at end of file diff --git a/cmake/modules/FindSoxr.cmake b/cmake/modules/FindSoxr.cmake deleted file mode 100644 index df6ad8050e..0000000000 --- a/cmake/modules/FindSoxr.cmake +++ /dev/null @@ -1,43 +0,0 @@ -# -# FindSoxr.cmake -# -# Try to find the libsoxr resampling library -# -# You can provide a LIBSOXR_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# SOXR_FOUND - system found libsoxr -# SOXR_INCLUDE_DIRS - the libsoxr include directory -# SOXR_LIBRARIES - link to this to use libsoxr -# -# Created on 1/22/2015 by Stephen Birarda -# Copyright 2015 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("soxr") - -find_path(SOXR_INCLUDE_DIRS soxr.h PATH_SUFFIXES include HINTS ${SOXR_SEARCH_DIRS}) -find_library(SOXR_LIBRARIES NAMES soxr PATH_SUFFIXES lib HINTS ${SOXR_SEARCH_DIRS}) - -if (WIN32) - find_path(SOXR_DLL_PATH soxr.dll PATH_SUFFIXES bin HINTS ${SOXR_SEARCH_DIRS}) -endif() - -set(SOXR_REQUIREMENTS SOXR_INCLUDE_DIRS SOXR_LIBRARIES) -if (WIN32) - list(APPEND SOXR_REQUIREMENTS SOXR_DLL_PATH) -endif () - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Soxr DEFAULT_MSG ${SOXR_REQUIREMENTS}) - -if (WIN32) - add_paths_to_fixup_libs(${SOXR_DLL_PATH}) -endif () - -mark_as_advanced(SOXR_INCLUDE_DIRS SOXR_LIBRARIES SOXR_SEARCH_DIRS) \ No newline at end of file diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f57e79e974..a13f1de86d 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -2,17 +2,17 @@ // examples // // Created by Eric Levin on 9/2/15 +// Additions by James B. Pollack @imgntn on 9/24/2015 // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ Script.include("../libraries/utils.js"); - ///////////////////////////////////////////////////////////////// // // these tune time-averaging and "on" value for analog trigger @@ -29,9 +29,9 @@ var TRIGGER_ON_VALUE = 0.2; var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did -var NO_INTERSECT_COLOR = {red: 10, green: 10, blue: 255}; // line color when pick misses -var INTERSECT_COLOR = {red: 250, green: 10, blue: 10}; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = {x: 1000, y: 1000, z: 1000}; +var NO_INTERSECT_COLOR = { red: 10, green: 10, blue: 255}; // line color when pick misses +var INTERSECT_COLOR = { red: 250, green: 10, blue: 10}; // line color when pick hits +var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000, z: 1000}; var LINE_LENGTH = 500; @@ -54,7 +54,7 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var RIGHT_HAND = 1; var LEFT_HAND = 0; -var ZERO_VEC = {x: 0, y: 0, z: 0}; +var ZERO_VEC = { x: 0, y: 0, z: 0}; var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var MSEC_PER_SEC = 1000.0; @@ -68,11 +68,13 @@ var STATE_DISTANCE_HOLDING = 1; var STATE_CONTINUE_DISTANCE_HOLDING = 2; var STATE_NEAR_GRABBING = 3; var STATE_CONTINUE_NEAR_GRABBING = 4; -var STATE_RELEASE = 5; +var STATE_NEAR_GRABBING_NON_COLLIDING = 5; +var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 6; +var STATE_RELEASE = 7; + var GRAB_USER_DATA_KEY = "grabKey"; - -function controller(hand, triggerAction) { +function MyController(hand, triggerAction) { this.hand = hand; if (this.hand === RIGHT_HAND) { this.getHandPosition = MyAvatar.getRightPalmPosition; @@ -81,6 +83,7 @@ function controller(hand, triggerAction) { this.getHandPosition = MyAvatar.getLeftPalmPosition; this.getHandRotation = MyAvatar.getLeftPalmRotation; } + this.triggerAction = triggerAction; this.palm = 2 * hand; // this.tip = 2 * hand + 1; // unused, but I'm leaving this here for fear it will be needed @@ -91,11 +94,13 @@ function controller(hand, triggerAction) { this.state = 0; this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value + var _this = this; this.update = function() { - switch(this.state) { + switch (this.state) { case STATE_SEARCHING: this.search(); + this.touchTest(); break; case STATE_DISTANCE_HOLDING: this.distanceHolding(); @@ -109,44 +114,48 @@ function controller(hand, triggerAction) { case STATE_CONTINUE_NEAR_GRABBING: this.continueNearGrabbing(); break; + case STATE_NEAR_GRABBING_NON_COLLIDING: + this.nearGrabbingNonColliding(); + break; + case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING: + this.continueNearGrabbingNonColliding(); + break; case STATE_RELEASE: this.release(); break; } - } - + }; this.lineOn = function(closePoint, farPoint, color) { // draw a line - if (this.pointer == null) { + if (this.pointer === null) { this.pointer = Entities.addEntity({ type: "Line", name: "pointer", dimensions: LINE_ENTITY_DIMENSIONS, visible: true, position: closePoint, - linePoints: [ ZERO_VEC, farPoint ], + linePoints: [ZERO_VEC, farPoint], color: color, lifetime: LIFETIME }); } else { Entities.editEntity(this.pointer, { position: closePoint, - linePoints: [ ZERO_VEC, farPoint ], + linePoints: [ZERO_VEC, farPoint], color: color, lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME }); } - } + }; this.lineOff = function() { - if (this.pointer != null) { + if (this.pointer !== null) { Entities.deleteEntity(this.pointer); } this.pointer = null; - } - + }; this.triggerSmoothedSqueezed = function() { var triggerValue = Controller.getActionValue(this.triggerAction); @@ -154,14 +163,12 @@ function controller(hand, triggerAction) { this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); return this.triggerValue > TRIGGER_ON_VALUE; - } - + }; this.triggerSqueezed = function() { var triggerValue = Controller.getActionValue(this.triggerAction); return triggerValue > TRIGGER_ON_VALUE; - } - + }; this.search = function() { if (!this.triggerSmoothedSqueezed()) { @@ -171,7 +178,10 @@ function controller(hand, triggerAction) { // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); - var pickRay = {origin: handPosition, direction: Quat.getUp(this.getHandRotation())}; + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects && intersection.properties.collisionsWillMove === 1 && @@ -192,30 +202,31 @@ function controller(hand, triggerAction) { // forward ray test failed, try sphere test. var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); var minDistance = GRAB_RADIUS; - var grabbedEntity = null; - for (var i = 0; i < nearbyEntities.length; i++) { - var props = Entities.getEntityProperties(nearbyEntities[i]); - var distance = Vec3.distance(props.position, handPosition); - if (distance < minDistance && props.name !== "pointer" && - props.collisionsWillMove === 1 && - props.locked === 0) { + var i, props, distance; + for (i = 0; i < nearbyEntities.length; i++) { + props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); + distance = Vec3.distance(props.position, handPosition); + if (distance < minDistance && props.name !== "pointer") { this.grabbedEntity = nearbyEntities[i]; minDistance = distance; } } if (this.grabbedEntity === null) { this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } else { + } else if (props.locked === 0 && props.collisionsWillMove === 1) { this.state = STATE_NEAR_GRABBING; + } else if (props.collisionsWillMove === 0) { + // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event + this.state = STATE_NEAR_GRABBING_NON_COLLIDING; } } - } + }; this.distanceHolding = function() { var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); // add the action and initialize some variables this.currentObjectPosition = grabbedProperties.position; @@ -230,23 +241,22 @@ function controller(hand, triggerAction) { targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME }); - if (this.actionID == NULL_ACTION_ID) { + if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } - if (this.actionID != null) { + if (this.actionID !== null) { this.state = STATE_CONTINUE_DISTANCE_HOLDING; this.activateEntity(this.grabbedEntity); - Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); - if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); } - } + }; this.continueDistanceHolding = function() { if (!this.triggerSmoothedSqueezed()) { @@ -262,9 +272,7 @@ function controller(hand, triggerAction) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, - handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, - DISTANCE_HOLDING_RADIUS_FACTOR); + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); this.handPreviousPosition = handControllerPosition; @@ -280,20 +288,19 @@ function controller(hand, triggerAction) { this.currentObjectTime = now; // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation)); this.handPreviousRotation = handRotation; this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME }); - } - + }; this.nearGrabbing = function() { if (!this.triggerSmoothedSqueezed()) { @@ -313,32 +320,32 @@ function controller(hand, triggerAction) { var objectRotation = grabbedProperties.rotation; var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - currentObjectPosition = grabbedProperties.position; + var currentObjectPosition = grabbedProperties.position; var offset = Vec3.subtract(currentObjectPosition, handPosition); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = Entities.addAction("hold", this.grabbedEntity, { - hand: this.hand == RIGHT_HAND ? "right" : "left", + hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, relativeRotation: offsetRotation }); - if (this.actionID == NULL_ACTION_ID) { + if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } else { this.state = STATE_CONTINUE_NEAR_GRABBING; - Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + } this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); this.currentObjectTime = Date.now(); - } - + }; this.continueNearGrabbing = function() { if (!this.triggerSmoothedSqueezed()) { @@ -357,8 +364,94 @@ function controller(hand, triggerAction) { this.currentHandControllerPosition = handControllerPosition; this.currentObjectTime = now; Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - } + }; + this.nearGrabbingNonColliding = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; + } + Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding"); + this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING; + }; + + this.continueNearGrabbingNonColliding = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; + } + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); + }; + + _this.allTouchedIDs={}; + this.touchTest = function() { + var maxDistance = 0.05; + var leftHandPosition = MyAvatar.getLeftPalmPosition(); + var rightHandPosition = MyAvatar.getRightPalmPosition(); + var leftEntities = Entities.findEntities(leftHandPosition, maxDistance); + var rightEntities = Entities.findEntities(rightHandPosition, maxDistance); + var ids = []; + + if (leftEntities.length !== 0) { + leftEntities.forEach(function(entity) { + ids.push(entity); + }); + + } + + if (rightEntities.length !== 0) { + rightEntities.forEach(function(entity) { + ids.push(entity); + }); + } + + ids.forEach(function(id) { + + var props = Entities.getEntityProperties(id, ["boundingBox", "name"]); + if (props.name === 'pointer') { + return; + } else { + var entityMinPoint = props.boundingBox.brn; + var entityMaxPoint = props.boundingBox.tfl; + var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint); + var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint); + + if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) { + // we haven't been touched before, but either right or left is touching us now + _this.allTouchedIDs[id] = true; + _this.startTouch(id); + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) { + // we have been touched before and are still being touched + // continue touch + _this.continueTouch(id); + } else if (_this.allTouchedIDs[id] === true) { + delete _this.allTouchedIDs[id]; + _this.stopTouch(id); + + } else { + //we are in another state + return; + } + } + + }); + + }; + + this.startTouch = function(entityID) { + // print('START TOUCH' + entityID); + Entities.callEntityMethod(entityID, "startTouch"); + }; + + this.continueTouch = function(entityID) { + // print('CONTINUE TOUCH' + entityID); + Entities.callEntityMethod(entityID, "continueTouch"); + }; + + this.stopTouch = function(entityID) { + // print('STOP TOUCH' + entityID); + Entities.callEntityMethod(entityID, "stopTouch"); + }; this.computeReleaseVelocity = function(deltaPosition, deltaTime, useMultiplier) { if (deltaTime > 0.0 && !vec3equal(deltaPosition, ZERO_VEC)) { @@ -367,75 +460,70 @@ function controller(hand, triggerAction) { // value would otherwise give the held object time to slow down. if (this.triggerSqueezed()) { this.grabbedVelocity = - Vec3.sum(Vec3.multiply(this.grabbedVelocity, - (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)), - Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)); + Vec3.sum(Vec3.multiply(this.grabbedVelocity, (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)), + Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)); } if (useMultiplier) { this.grabbedVelocity = Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER); } } - } - + }; this.release = function() { this.lineOff(); - if (this.grabbedEntity != null && this.actionID != null) { + if (this.grabbedEntity !== null && this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. - Entities.editEntity(this.grabbedEntity, {velocity: this.grabbedVelocity}); + Entities.editEntity(this.grabbedEntity, { + velocity: this.grabbedVelocity + }); this.deactivateEntity(this.grabbedEntity); this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; this.actionID = null; this.state = STATE_SEARCHING; - } - + }; this.cleanup = function() { - release(); - } + this.release(); + }; - this.activateEntity = function(entity) { + this.activateEntity = function() { var data = { activated: true, avatarId: MyAvatar.sessionUUID }; setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); - } + }; - this.deactivateEntity = function(entity) { + this.deactivateEntity = function() { var data = { activated: false, avatarId: null }; setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); - } + }; } - -var rightController = new controller(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); -var leftController = new controller(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); - +var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); +var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); function update() { rightController.update(); leftController.update(); } - function cleanup() { rightController.cleanup(); leftController.cleanup(); } - Script.scriptEnding.connect(cleanup); -Script.update.connect(update) +Script.update.connect(update); \ No newline at end of file diff --git a/examples/entityScripts/changeColorOnTouch.js b/examples/entityScripts/changeColorOnTouch.js new file mode 100644 index 0000000000..3a8ebae956 --- /dev/null +++ b/examples/entityScripts/changeColorOnTouch.js @@ -0,0 +1,71 @@ +// +// changeColorOnTouch.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Additions by James B. Pollack @imgntn on 9/23/2015 +// Copyright 2014 High Fidelity, Inc. +// +// ATTENTION: Requires you to run handControllerGrab.js +// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will +// change the color of the entity when you touch it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + + +(function() { + + function ChangeColorOnTouch () { + this.oldColor = {}; + this.oldColorKnown = false; + } + + ChangeColorOnTouch.prototype = { + + storeOldColor: function(entityID) { + var oldProperties = Entities.getEntityProperties(entityID); + this.oldColor = oldProperties.color; + this.oldColorKnown = true; + + print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + }, + + preload: function(entityID) { + print("preload"); + + this.entityID = entityID; + this.storeOldColor(entityID); + }, + + startTouch: function() { + print("startTouch"); + + if (!this.oldColorKnown) { + this.storeOldColor(this.entityID); + } + + Entities.editEntity(this.entityID, {color: { red: 0, green: 255, blue: 255 }}); + }, + + continueTouch: function() { + //unused here + return; + }, + + stopTouch: function() { + print("stopTouch"); + + if (this.oldColorKnown) { + print("leave restoring old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + Entities.editEntity(this.entityID, {color: this.oldColor}); + } + } + + + }; + + return new ChangeColorOnTouch(); +}); \ No newline at end of file diff --git a/examples/toys/doll/createDoll.js b/examples/toys/doll/createDoll.js new file mode 100644 index 0000000000..ffd840f4ea --- /dev/null +++ b/examples/toys/doll/createDoll.js @@ -0,0 +1,47 @@ +// createDoll.js +// +// Script Type: Entity +// Created by James B. Pollack @imgntn 9/23/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Creates a doll entity in front of you. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ + +function createDoll() { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; + + var scriptURL = Script.resolvePath("doll.js"); + + var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); + + var naturalDimensions = { x: 1.63, y: 1.67, z: 0.26}; + + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + + var doll = Entities.addEntity({ + type: "Model", + name: "doll", + modelURL: modelURL, + script: scriptURL, + position: center, + shapeType: 'box', + dimensions: desiredDimensions, + gravity: { + x: 0, + y: 0, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + collisionsWillMove: true + }); + return doll; +} + +createDoll(); \ No newline at end of file diff --git a/examples/toys/doll/doll.js b/examples/toys/doll/doll.js new file mode 100644 index 0000000000..f97b6de378 --- /dev/null +++ b/examples/toys/doll/doll.js @@ -0,0 +1,92 @@ +// doll.js +// +// Script Type: Entity +// Created by Eric Levin on 9/21/15. +// Additions by James B. Pollack @imgntn on 9/24/15 +// Copyright 2015 High Fidelity, Inc. +// +// This entity script plays an animation and a sound while you hold it. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Known issues: If you pass the doll between hands, animation can get into a weird state. We want to prevent the animation from starting again when you switch hands, but when you switch mid-animation your hand at release is still the first hand and not the current hand. +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ + +(function() { + Script.include("../../utilities.js"); + Script.include("../../libraries/utils.js"); + + // this is the "constructor" for the entity as a JS object we don't do much here + var Doll = function() { + this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")]; + }; + + Doll.prototype = { + startAnimationSetting: JSON.stringify({ + running: true, + fps: 30, + startFrame: 0, + lastFrame: 128, + startAutomatically: true + }), + stopAnimationSetting: JSON.stringify({running: false}), + audioInjector: null, + isGrabbed: false, + setLeftHand: function() { + this.hand = 'left'; + }, + + setRightHand: function() { + this.hand = 'right'; + }, + + startNearGrab: function() { + if (this.isGrabbed === false) { + + Entities.editEntity(this.entityID, { + animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx", + animationSettings: this.startAnimationSetting + }); + + var position = Entities.getEntityProperties(this.entityID, "position").position; + this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], { + position: position, + volume: 0.1 + }); + + this.isGrabbed = true; + this.initialHand = this.hand; + } + }, + + continueNearGrab: function() { + var props = Entities.getEntityProperties(this.entityID, "position"); + var audioOptions = { + position: props.position + }; + this.audioInjector.options = audioOptions; + }, + + releaseGrab: function() { + if (this.isGrabbed === true && this.hand === this.initialHand) { + + this.audioInjector.stop(); + + Entities.editEntity(this.entityID, { + animationSettings: this.stopAnimationSetting, + animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", + }); + this.isGrabbed = false; + } + }, + + preload: function(entityID) { + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + this.entityID = entityID; + }, + }; + // entity scripts always need to return a newly constructed object of our type + return new Doll(); +}); \ No newline at end of file diff --git a/examples/toys/flashlight/createFlashlight.js b/examples/toys/flashlight/createFlashlight.js index 53de3bd623..73b10d3189 100644 --- a/examples/toys/flashlight/createFlashlight.js +++ b/examples/toys/flashlight/createFlashlight.js @@ -11,7 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js"); @@ -19,34 +19,14 @@ var scriptURL = Script.resolvePath('flashlight.js?123123'); var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; -var center = Vec3.sum(Vec3.sum(MyAvatar.position, { - x: 0, - y: 0.5, - z: 0 -}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var flashlight = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: center, - dimensions: { - x: 0.08, - y: 0.30, - z: 0.08 - }, - collisionsWillMove: true, - shapeType: 'box', - script: scriptURL + type: "Model", + modelURL: modelURL, + position: center, + dimensions: { x: 0.08, y: 0.30, z: 0.08}, + collisionsWillMove: true, + shapeType: 'box', + script: scriptURL }); - - -function cleanup() { - //commenting out the line below makes this persistent. to delete at cleanup, uncomment - //Entities.deleteEntity(flashlight); -} - - -Script.scriptEnding.connect(cleanup); - -// Close script after running once to prevent a flashlight from being created each time you start Interface -Script.stop(); diff --git a/examples/toys/flashlight/flashlight.js b/examples/toys/flashlight/flashlight.js index 0465266b1c..9836579afd 100644 --- a/examples/toys/flashlight/flashlight.js +++ b/examples/toys/flashlight/flashlight.js @@ -14,39 +14,28 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ (function() { Script.include("../../libraries/utils.js"); - var _this; + var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav'; + var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav'; // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) - Flashlight = function() { - _this = this; - }; + function Flashlight() { + return; + } //if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will var DISABLE_LIGHT_THRESHOLD = 0.7; // These constants define the Spotlight position and orientation relative to the model - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); + var MODEL_LIGHT_POSITION = { x: 0, y: -0.3, z: 0 }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }); - var GLOW_LIGHT_POSITION = { - x: 0, - y: -0.1, - z: 0 - } + var GLOW_LIGHT_POSITION = { x: 0, y: -0.1, z: 0}; // Evaluate the world light entity positions and orientations from the model ones function evalLightWorldTransform(modelPos, modelRot) { @@ -55,15 +44,14 @@ p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) }; - }; + } function glowLightWorldTransform(modelPos, modelRot) { return { p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)), q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) }; - }; - + } Flashlight.prototype = { lightOn: false, @@ -74,11 +62,13 @@ setRightHand: function() { this.hand = 'RIGHT'; }, + setLeftHand: function() { this.hand = 'LEFT'; }, + startNearGrab: function() { - if (!_this.hasSpotlight) { + if (!this.hasSpotlight) { //this light casts the beam this.spotlight = Entities.addEntity({ @@ -117,29 +107,16 @@ cutoff: 90, // in degrees }); - this.debugBox = Entities.addEntity({ - type: 'Box', - color: { - red: 255, - blue: 0, - green: 0 - }, - dimensions: { - x: 0.25, - y: 0.25, - z: 0.25 - }, - ignoreForCollisions:true - }) - this.hasSpotlight = true; } }, + setWhichHand: function() { this.whichHand = this.hand; }, + continueNearGrab: function() { if (this.whichHand === null) { //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten @@ -149,6 +126,7 @@ this.changeLightWithTriggerPressure(this.whichHand); } }, + releaseGrab: function() { //delete the lights and reset state if (this.hasSpotlight) { @@ -158,9 +136,10 @@ this.glowLight = null; this.spotlight = null; this.whichHand = null; - + this.lightOn = false; } }, + updateLightPositions: function() { var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); @@ -168,27 +147,20 @@ var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation); - //move them with the entity model Entities.editEntity(this.spotlight, { position: lightTransform.p, rotation: lightTransform.q, - }) - + }); Entities.editEntity(this.glowLight, { position: glowLightTransform.p, rotation: glowLightTransform.q, - }) - - // Entities.editEntity(this.debugBox, { - // position: lightTransform.p, - // rotation: lightTransform.q, - // }) + }); }, - changeLightWithTriggerPressure: function(flashLightHand) { + changeLightWithTriggerPressure: function(flashLightHand) { var handClickString = flashLightHand + "_HAND_CLICK"; var handClick = Controller.findAction(handClickString); @@ -200,37 +172,62 @@ } else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) { this.turnLightOn(); } - return + return; }, + turnLightOff: function() { + this.playSoundAtCurrentPosition(false); Entities.editEntity(this.spotlight, { intensity: 0 }); Entities.editEntity(this.glowLight, { intensity: 0 }); - this.lightOn = false + this.lightOn = false; }, + turnLightOn: function() { + this.playSoundAtCurrentPosition(true); + Entities.editEntity(this.glowLight, { intensity: 2 }); Entities.editEntity(this.spotlight, { intensity: 2 }); - this.lightOn = true + this.lightOn = true; }, - // preload() will be called when the entity has become visible (or known) to the interface - // it gives us a chance to set our local JavaScript object up. In this case it means: - // * remembering our entityID, so we can access it in cases where we're called without an entityID + + playSoundAtCurrentPosition: function(playOnSound) { + var position = Entities.getEntityProperties(this.entityID, "position").position; + + var audioProperties = { + volume: 0.25, + position: position + }; + + if (playOnSound) { + Audio.playSound(this.ON_SOUND, audioProperties); + } else { + Audio.playSound(this.OFF_SOUND, audioProperties); + } + }, + preload: function(entityID) { + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * preloading sounds this.entityID = entityID; + this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL); + this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL); + }, - // unload() will be called when our entity is no longer available. It may be because we were deleted, - // or because we've left the domain or quit the application. - unload: function(entityID) { - + unload: function() { + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. if (this.hasSpotlight) { Entities.deleteEntity(this.spotlight); Entities.deleteEntity(this.glowLight); @@ -238,12 +235,13 @@ this.glowLight = null; this.spotlight = null; this.whichHand = null; + this.lightOn = false; } - }, + }; // entity scripts always need to return a newly constructed object of our type return new Flashlight(); -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 03c5a103ff..72eefaf7e8 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -31,7 +31,8 @@ { "jointName": "Head", "positionVar": "headPosition", - "rotationVar": "headRotation" + "rotationVar": "headRotation", + "typeVar": "headType" } ] }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e41a375144..4b4383ac09 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1036,6 +1036,8 @@ void Application::initializeUi() { void Application::paintGL() { PROFILE_RANGE(__FUNCTION__); + PerformanceTimer perfTimer("paintGL"); + if (nullptr == _displayPlugin) { return; } @@ -1060,18 +1062,19 @@ void Application::paintGL() { lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); - PerformanceTimer perfTimer("paintGL"); - - PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened - renderArgs._context->syncCache(); + { + PerformanceTimer perfTimer("syncCache"); + renderArgs._context->syncCache(); + } if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + PerformanceTimer perfTimer("Mirror"); auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; @@ -1085,7 +1088,7 @@ void Application::paintGL() { auto mirrorRectDest = glm::ivec4(mirrorRect.z, mirrorRect.y, mirrorRect.x, mirrorRect.w); auto selfieFbo = DependencyManager::get()->getSelfieFramebuffer(); - doInBatch(renderArgs._context, [=](gpu::Batch& batch) { + gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { batch.setFramebuffer(selfieFbo); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); batch.blit(primaryFbo, mirrorRect, selfieFbo, mirrorRectDest); @@ -1103,81 +1106,86 @@ void Application::paintGL() { _applicationOverlay.renderOverlay(&renderArgs); } - _myAvatar->startCapture(); - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); - Application::getInstance()->cameraMenuChanged(); - } + { + PerformanceTimer perfTimer("CameraUpdates"); - // The render mode is default or mirror if the camera is in mirror mode, assigned further below - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - // Always use the default eye position, not the actual head eye position. - // Using the latter will cause the camera to wobble with idle animations, - // or with changes from the face tracker - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { - if (isHMDMode()) { - mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setRotation(glm::quat_cast(camMat)); - } else { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + _myAvatar->startCapture(); + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); + Application::getInstance()->cameraMenuChanged(); } - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * hmdRotation); - // Ignore MenuOption::CenterPlayerInView in HMD view - glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + _myAvatar->getOrientation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); - } else { - _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + _myCamera.getRotation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; + + // Always use the default eye position, not the actual head eye position. + // Using the latter will cause the camera to wobble with idle animations, + // or with changes from the face tracker + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { + if (isHMDMode()) { + mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setRotation(glm::quat_cast(camMat)); } else { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + } + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * hmdRotation); + // Ignore MenuOption::CenterPlayerInView in HMD view + glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + _myAvatar->getOrientation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); + } else { + _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myCamera.getRotation() + * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + } else { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myAvatar->getOrientation() + * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + } } + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (isHMDMode()) { + glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); + } else { + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) + + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + } + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } - } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation); - glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); - } else { - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + // Update camera position + if (!isHMDMode()) { + _myCamera.update(1.0f / _fps); } - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + _myAvatar->endCapture(); } - // Update camera position - if (!isHMDMode()) { - _myCamera.update(1.0f / _fps); - } - _myAvatar->endCapture(); // Primary rendering pass auto framebufferCache = DependencyManager::get(); const QSize size = framebufferCache->getFrameBufferSize(); { PROFILE_RANGE(__FUNCTION__ "/mainRender"); + PerformanceTimer perfTimer("mainRender"); // Viewport is assigned to the size of the framebuffer renderArgs._viewport = ivec4(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { @@ -1214,7 +1222,7 @@ void Application::paintGL() { } displaySide(&renderArgs, _myCamera); renderArgs._context->enableStereo(false); - doInBatch(renderArgs._context, [](gpu::Batch& batch) { + gpu::doInBatch(renderArgs._context, [](gpu::Batch& batch) { batch.setFramebuffer(nullptr); }); } @@ -1222,6 +1230,7 @@ void Application::paintGL() { // Overlay Composition, needs to occur after screen space effects have completed { PROFILE_RANGE(__FUNCTION__ "/compositor"); + PerformanceTimer perfTimer("compositor"); auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo)); if (displayPlugin->isStereo()) { @@ -1246,6 +1255,7 @@ void Application::paintGL() { // deliver final composited scene to the display plugin { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); + PerformanceTimer perfTimer("pluginOutput"); auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0)); // Ensure the rendering context commands are completed when rendering @@ -1263,24 +1273,29 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); + PerformanceTimer perfTimer("pluginDisplay"); displayPlugin->display(finalTexture, toGlm(size)); } { PROFILE_RANGE(__FUNCTION__ "/bufferSwap"); + PerformanceTimer perfTimer("bufferSwap"); displayPlugin->finishFrame(); } } - _offscreenContext->makeCurrent(); - _frameCount++; - Stats::getInstance()->setRenderDetails(renderArgs._details); + { + PerformanceTimer perfTimer("makeCurrent"); + _offscreenContext->makeCurrent(); + _frameCount++; + Stats::getInstance()->setRenderDetails(renderArgs._details); - // Reset the gpu::Context Stages - // Back to the default framebuffer; - doInBatch(renderArgs._context, [=](gpu::Batch& batch) { - batch.resetStages(); - }); + // Reset the gpu::Context Stages + // Back to the default framebuffer; + gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) { + batch.resetStages(); + }); + } } void Application::runTests() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8e517991c0..4cf1b69ce4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -150,6 +150,25 @@ void MyAvatar::reset() { eulers.x = 0.0f; eulers.z = 0.0f; setOrientation(glm::quat(eulers)); + + // This should be simpler when we have only graph animations always on. + bool isRig = _rig->getEnableRig(); + bool isGraph = _rig->getEnableAnimGraph(); + qApp->setRawAvatarUpdateThreading(false); + _rig->disableHands = true; + setEnableRigAnimations(true); + _skeletonModel.simulate(0.1f); // non-zero + setEnableRigAnimations(false); + _skeletonModel.simulate(0.1f); + if (isRig) { + setEnableRigAnimations(true); + Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true); + } else if (isGraph) { + setEnableAnimGraph(true); + Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true); + } + _rig->disableHands = false; + qApp->setRawAvatarUpdateThreading(); } void MyAvatar::update(float deltaTime) { diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index ec93137d3a..497a94bb86 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -207,7 +207,7 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { updateTooltips(); //Handle fading and deactivation/activation of UI - doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); @@ -278,7 +278,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int auto geometryCache = DependencyManager::get(); - doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { geometryCache->useSimpleDrawPipeline(batch); batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0)); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 7254295c2f..03d6432104 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -70,29 +70,28 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { } // Execute the batch into our framebuffer - gpu::Batch batch; - renderArgs->_batch = &batch; + doInBatch(renderArgs->_context, [=](gpu::Batch& batch) { + renderArgs->_batch = &batch; - int width = _overlayFramebuffer->getWidth(); - int height = _overlayFramebuffer->getHeight(); + int width = _overlayFramebuffer->getWidth(); + int height = _overlayFramebuffer->getHeight(); - batch.setViewportTransform(glm::ivec4(0, 0, width, height)); - batch.setFramebuffer(_overlayFramebuffer); + batch.setViewportTransform(glm::ivec4(0, 0, width, height)); + batch.setFramebuffer(_overlayFramebuffer); - glm::vec4 color { 0.0f, 0.0f, 0.0f, 0.0f }; - float depth = 1.0f; - int stencil = 0; - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, color, depth, stencil); + glm::vec4 color { 0.0f, 0.0f, 0.0f, 0.0f }; + float depth = 1.0f; + int stencil = 0; + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, color, depth, stencil); - // Now render the overlay components together into a single texture - renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line - renderAudioScope(renderArgs); // audio scope in the very back - renderRearView(renderArgs); // renders the mirror view selfie - renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts - renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope - renderStatsAndLogs(renderArgs); // currently renders nothing - - renderArgs->_context->render(batch); + // Now render the overlay components together into a single texture + renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line + renderAudioScope(renderArgs); // audio scope in the very back + renderRearView(renderArgs); // renders the mirror view selfie + renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts + renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope + renderStatsAndLogs(renderArgs); // currently renders nothing + }); renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index e31f7795f3..7f32d8a2fa 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -42,9 +42,8 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) { void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) const { int numJoints = (int)_relativePoses.size(); - absolutePoses.clear(); - absolutePoses.resize(numJoints); assert(numJoints <= _skeleton->getNumJoints()); + assert(numJoints == (int)absolutePoses.size()); for (int i = 0; i < numJoints; ++i) { int parentIndex = _skeleton->getParentIndex(i); if (parentIndex < 0) { @@ -55,7 +54,11 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con } } -void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) { +void AnimInverseKinematics::setTargetVars( + const QString& jointName, + const QString& positionVar, + const QString& rotationVar, + const QString& typeVar) { // if there are dups, last one wins. bool found = false; for (auto& targetVar: _targetVarVec) { @@ -63,13 +66,14 @@ void AnimInverseKinematics::setTargetVars(const QString& jointName, const QStrin // update existing targetVar targetVar.positionVar = positionVar; targetVar.rotationVar = rotationVar; + targetVar.typeVar = typeVar; found = true; break; } } if (!found) { // create a new entry - _targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar)); + _targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar, typeVar)); } } @@ -86,6 +90,7 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets) { // build a list of valid targets from _targetVarVec and animVars + _maxTargetIndex = -1; bool removeUnfoundJoints = false; for (auto& targetVar : _targetVarVec) { if (targetVar.jointIndex == -1) { @@ -100,14 +105,17 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: removeUnfoundJoints = true; } } else { - // TODO: get this done without a double-lookup of each var in animVars IKTarget target; AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses); target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); + target.setType(animVars.lookup(targetVar.typeVar, QString(""))); target.rootIndex = targetVar.rootIndex; target.index = targetVar.jointIndex; targets.push_back(target); + if (target.index > _maxTargetIndex) { + _maxTargetIndex = target.index; + } } } @@ -129,9 +137,10 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } -void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector& targets) { +void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets) { // compute absolute poses that correspond to relative target poses AnimPoseVec absolutePoses; + absolutePoses.resize(_relativePoses.size()); computeAbsolutePoses(absolutePoses); // clear the accumulators before we start the IK solver @@ -139,25 +148,23 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vectorgetParentIndex(tipIndex); - while (pivotIndex != -1 && error > ACCEPTABLE_RELATIVE_ERROR) { + float fractionDenominator = 1.0f; + while (pivotIndex != -1) { // compute the two lines that should be aligned glm::vec3 jointPosition = absolutePoses[pivotIndex].trans; glm::vec3 leverArm = tip - jointPosition; @@ -166,59 +173,60 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector EPSILON) { + glm::quat deltaRotation; + const float MIN_AXIS_LENGTH = 1.0e-4f; + if (axisLength > MIN_AXIS_LENGTH) { // compute deltaRotation for alignment (brings tip closer to target) axis /= axisLength; float angle = acosf(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine))); // NOTE: even when axisLength is not zero (e.g. lever-arm and pivot-arm are not quite aligned) it is // still possible for the angle to be zero so we also check that to avoid unnecessary calculations. - if (angle > EPSILON) { + const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f; + if (angle > MIN_ADJUSTMENT_ANGLE) { // reduce angle by half: slows convergence but adds stability to IK solution - angle = 0.5f * angle; - glm::quat deltaRotation = glm::angleAxis(angle, axis); - - int parentIndex = _skeleton->getParentIndex(pivotIndex); - if (parentIndex == -1) { - // TODO? apply constraints to root? - // TODO? harvest the root's transform as movement of entire skeleton? - } else { - // compute joint's new parent-relative rotation - // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q - glm::quat newRot = glm::normalize(glm::inverse( - absolutePoses[parentIndex].rot) * - deltaRotation * - absolutePoses[pivotIndex].rot); - RotationConstraint* constraint = getConstraint(pivotIndex); - if (constraint) { - bool constrained = constraint->apply(newRot); - if (constrained) { - // the constraint will modify the movement of the tip so we have to compute the modified - // model-frame deltaRotation - // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ - deltaRotation = absolutePoses[parentIndex].rot * - newRot * - glm::inverse(absolutePoses[pivotIndex].rot); - } - } - // store the rotation change in the accumulator - _accumulators[pivotIndex].add(newRot); - } - // this joint has been changed so we check to see if it has the lowest index - if (pivotIndex < lowestMovedIndex) { - lowestMovedIndex = pivotIndex; - } - - // keep track of tip's new position as we descend towards root - tip = jointPosition + deltaRotation * leverArm; - error = glm::length(targetPose.trans - tip); + angle /= fractionDenominator; + deltaRotation = glm::angleAxis(angle, axis); } } + fractionDenominator++; + + int parentIndex = _skeleton->getParentIndex(pivotIndex); + if (parentIndex == -1) { + // TODO? apply constraints to root? + // TODO? harvest the root's transform as movement of entire skeleton? + } else { + // compute joint's new parent-relative rotation + // Q' = dQ * Q and Q = Qp * q --> q' = Qp^ * dQ * Q + glm::quat newRot = glm::normalize(glm::inverse( + absolutePoses[parentIndex].rot) * + deltaRotation * + absolutePoses[pivotIndex].rot); + RotationConstraint* constraint = getConstraint(pivotIndex); + if (constraint) { + bool constrained = constraint->apply(newRot); + if (constrained) { + // the constraint will modify the movement of the tip so we have to compute the modified + // model-frame deltaRotation + // Q' = Qp^ * dQ * Q --> dQ = Qp * Q' * Q^ + deltaRotation = absolutePoses[parentIndex].rot * + newRot * + glm::inverse(absolutePoses[pivotIndex].rot); + } + } + // store the rotation change in the accumulator + _accumulators[pivotIndex].add(newRot); + } + // this joint has been changed so we check to see if it has the lowest index + if (pivotIndex < lowestMovedIndex) { + lowestMovedIndex = pivotIndex; + } + + // keep track of tip's new position as we descend towards root + tip = jointPosition + deltaRotation * leverArm; + pivotIndex = _skeleton->getParentIndex(pivotIndex); } - if (largestError < error) { - largestError = error; - } } ++numLoops; @@ -238,7 +246,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector ACCEPTABLE_RELATIVE_ERROR && numLoops < MAX_IK_LOOPS && usecTimestampNow() < expiry); + } while (numLoops < MAX_IK_LOOPS); // finally set the relative rotation of each tip to agree with absolute target rotation for (auto& target: targets) { @@ -380,7 +388,7 @@ void AnimInverseKinematics::initConstraints() { } } - _constraints.clear(); + clearConstraints(); for (int i = 0; i < numJoints; ++i) { // compute the joint's baseName and remember whether its prefix was "Left" or not QString baseName = _skeleton->getJointName(i); @@ -639,9 +647,12 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele targetVar.jointIndex = -1; } - _maxTargetIndex = 0; + _maxTargetIndex = -1; + + for (auto& accumulator: _accumulators) { + accumulator.clearAndClean(); + } - _accumulators.clear(); if (skeleton) { initConstraints(); } else { diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 70808f5919..121b35bd9d 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -31,20 +31,27 @@ public: void loadPoses(const AnimPoseVec& poses); void computeAbsolutePoses(AnimPoseVec& absolutePoses) const; - void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar); + void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, const QString& typeVar); virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override; virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; protected: struct IKTarget { + enum class Type { + RotationAndPosition, + RotationOnly + }; AnimPose pose; int index; int rootIndex; + Type type = Type::RotationAndPosition; + + void setType(const QString& typeVar) { type = ((typeVar == "RotationOnly") ? Type::RotationOnly : Type::RotationAndPosition); } }; void computeTargets(const AnimVariantMap& animVars, std::vector& targets); - void solveWithCyclicCoordinateDescent(std::vector& targets); + void solveWithCyclicCoordinateDescent(const std::vector& targets); virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton); // for AnimDebugDraw rendering @@ -55,15 +62,20 @@ protected: void initConstraints(); struct IKTargetVar { - IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn) : + IKTargetVar(const QString& jointNameIn, + const QString& positionVarIn, + const QString& rotationVarIn, + const QString& typeVarIn) : positionVar(positionVarIn), rotationVar(rotationVarIn), + typeVar(typeVarIn), jointName(jointNameIn), jointIndex(-1), rootIndex(-1) {} QString positionVar; QString rotationVar; + QString typeVar; QString jointName; int jointIndex; // cached joint index int rootIndex; // cached root index diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index b2afae4728..147025f1cf 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -342,8 +342,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS READ_STRING(jointName, targetObj, id, jsonUrl, nullptr); READ_STRING(positionVar, targetObj, id, jsonUrl, nullptr); READ_STRING(rotationVar, targetObj, id, jsonUrl, nullptr); + READ_OPTIONAL_STRING(typeVar, targetObj); - node->setTargetVars(jointName, positionVar, rotationVar); + node->setTargetVars(jointName, positionVar, rotationVar, typeVar); }; return node; diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index 08c4304b08..8f60b972ce 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -51,8 +51,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float d _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() >= 2) { - auto underPoses = _children[1]->evaluate(animVars, dt, triggersOut); - auto overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses); + auto& underPoses = _children[1]->evaluate(animVars, dt, triggersOut); + auto& overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses); if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index f1e81df64e..1874939b3d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -738,7 +738,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q return; } - if (_enableAnimGraph && _animSkeleton) { + if (disableHands || (_enableAnimGraph && _animSkeleton)) { // the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit return; } @@ -1065,6 +1065,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); + _animVars.set("headType", QString("RotationAndPosition")); _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); @@ -1077,6 +1078,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("headPosition"); _animVars.set("headRotation", realLocalHeadOrientation); + _animVars.set("headType", QString("RotationOnly")); _animVars.unset("neckPosition"); _animVars.unset("neckRotation"); } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 37e1d51a0a..90082ca38b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -193,6 +193,7 @@ public: AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } + bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) protected: diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index 76f4cb4a0f..f631ec6387 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -9,18 +9,13 @@ link_hifi_libraries(audio) target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src") # have CMake grab externals for us -add_dependency_external_projects(gverb soxr) +add_dependency_external_projects(gverb) find_package(Gverb REQUIRED) target_link_libraries(${TARGET_NAME} ${GVERB_LIBRARIES}) target_include_directories(${TARGET_NAME} PRIVATE ${GVERB_INCLUDE_DIRS}) -# we use libsoxr for resampling -find_package(Soxr REQUIRED) -target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES}) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS}) - if (APPLE) find_library(CoreAudio CoreAudio) find_library(CoreFoundation CoreFoundation) diff --git a/libraries/audio/CMakeLists.txt b/libraries/audio/CMakeLists.txt index c03f588d94..5134ccda36 100644 --- a/libraries/audio/CMakeLists.txt +++ b/libraries/audio/CMakeLists.txt @@ -7,10 +7,4 @@ add_dependency_external_projects(glm) find_package(GLM REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) -# we use libsoxr for resampling -add_dependency_external_projects(soxr) -find_package(Soxr REQUIRED) -target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES}) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS}) - link_hifi_libraries(networking shared) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 68567372e2..005252672d 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -211,9 +211,12 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { _vertices.clear(); // build vertices from particle positions and radiuses - const glm::vec3 up = frustum->getUp(); - const glm::vec3 right = frustum->getRight(); + glm::vec3 frustumPosition = frustum->getPosition(); for (auto&& particle : particleDetails) { + glm::vec3 particleDirection = particle.position - frustumPosition; + glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), particleDirection)); + glm::vec3 up = glm::normalize(glm::cross(right, particleDirection)); + glm::vec3 upOffset = up * particle.radius; glm::vec3 rightOffset = right * particle.radius; // generate corners of quad aligned to face the camera. diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 286decfad4..ad21d71427 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -215,8 +215,6 @@ protected: }; typedef std::shared_ptr ContextPointer; -}; - template void doInBatch(std::shared_ptr context, F f) { static gpu::Batch::CacheState cacheState; @@ -226,4 +224,7 @@ void doInBatch(std::shared_ptr context, F f) { cacheState = batch.getCacheState(); } +}; + + #endif diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 3d6f2e1656..bb8267b616 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -173,7 +173,7 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()]; UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()]; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); DependencyManager::get()->bindSimpleProgram(batch, true); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 73c316648b..456a6430a7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -179,7 +179,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto framebufferCache = DependencyManager::get(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); float fbWidth = framebufferSize.width(); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 9af3f90a4e..aaef67542f 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -101,7 +101,7 @@ void Antialiasing::run(const render::SceneContextPointer& sceneContext, const re } RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); auto framebufferCache = DependencyManager::get(); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 5ace0d4e13..a9d83aa9d6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -340,7 +340,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu } void DeferredLightingEffect::prepare(RenderArgs* args) { - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setStateScissorRect(args->_viewport); @@ -357,7 +357,7 @@ void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::FramebufferPointer _copyFBO; void DeferredLightingEffect::render(RenderArgs* args) { - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { // Allocate the parameters buffer used by all the deferred shaders if (!_deferredTransformBuffer[0]._buffer) { @@ -685,7 +685,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { void DeferredLightingEffect::copyBack(RenderArgs* args) { auto framebufferCache = DependencyManager::get(); - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); QSize framebufferSize = framebufferCache->getFrameBufferSize(); diff --git a/libraries/render-utils/src/HitEffect.cpp b/libraries/render-utils/src/HitEffect.cpp index b3915aa072..06bd07b456 100644 --- a/libraries/render-utils/src/HitEffect.cpp +++ b/libraries/render-utils/src/HitEffect.cpp @@ -63,7 +63,7 @@ void HitEffect::run(const render::SceneContextPointer& sceneContext, const rende assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ff27c3ab62..7aee46b108 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1272,6 +1272,8 @@ void Model::simulateInternal(float deltaTime) { updateRig(deltaTime, parentTransform); } void Model::updateClusterMatrices() { + PerformanceTimer perfTimer("Model::updateClusterMatrices"); + if (!_needsUpdateClusterMatrices) { return; } @@ -1528,6 +1530,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, int shape { if (!_showTrueJointTransforms) { + PerformanceTimer perfTimer("_rig->updateVisibleJointStates()"); _rig->updateVisibleJointStates(); } // else no need to update visible transforms } @@ -1692,6 +1695,8 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, int shape // TODO: We should be able to do that just in the renderTransparentJob if (translucentMesh && locations->lightBufferUnit >= 0) { + PerformanceTimer perfTimer("DLE->setupTransparent()"); + DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); } @@ -1702,8 +1707,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, int shape } } - batch.setIndexBuffer(gpu::UINT32, part.getMergedTriangles(), 0); - batch.drawIndexed(gpu::TRIANGLES, part.mergedTrianglesIndicesCount, 0); + { + PerformanceTimer perfTimer("batch.drawIndexed()"); + batch.setIndexBuffer(gpu::UINT32, part.getMergedTriangles(), 0); + batch.drawIndexed(gpu::TRIANGLES, part.mergedTrianglesIndicesCount, 0); + } if (args) { const int INDICES_PER_TRIANGLE = 3; @@ -1743,6 +1751,9 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, Locations*& locations) { + PerformanceTimer perfTimer("Model::pickPrograms"); + + RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); if (mode == RenderArgs::MIRROR_RENDER_MODE) { key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 35ac2e4af2..65f77689c3 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -32,7 +32,7 @@ using namespace render; void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); @@ -166,7 +166,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; @@ -195,7 +195,7 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; @@ -267,7 +267,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } // Render the items - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 66067f3ff2..32cd21d0bc 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -126,7 +126,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex } // Allright, something to render let's do it - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 644b9b0adf..e2c4c5f747 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -236,7 +236,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext cullItems(sceneContext, renderContext, inItems, culledItems); RenderArgs* args = renderContext->args; - doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; renderItems(sceneContext, renderContext, culledItems); });