Merge branch 'master' into tony/gangsta-lean

This commit is contained in:
Anthony J. Thibault 2015-09-25 11:36:58 -07:00
commit fc9b270951
33 changed files with 705 additions and 442 deletions

View file

@ -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.

View file

@ -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=<INSTALL_DIR>
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 ()

View file

@ -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)

View file

@ -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);

View file

@ -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();
});

View file

@ -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();

View file

@ -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();
});

View file

@ -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();

View file

@ -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();
})
});

View file

@ -31,7 +31,8 @@
{
"jointName": "Head",
"positionVar": "headPosition",
"rotationVar": "headRotation"
"rotationVar": "headRotation",
"typeVar": "headType"
}
]
},

View file

@ -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<FramebufferCache>()->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<FramebufferCache>()->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<FramebufferCache>();
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() {

View file

@ -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) {

View file

@ -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<GeometryCache>();
@ -278,7 +278,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
auto geometryCache = DependencyManager::get<GeometryCache>();
doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
gpu::doInBatch(renderArgs->_context, [=](gpu::Batch& batch) {
geometryCache->useSimpleDrawPipeline(batch);
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));

View file

@ -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

View file

@ -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<IKTarget>& 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<IKTarget>& targets) {
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& 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::vector<IKTarge
accumulator.clearAndClean();
}
float largestError = 0.0f;
const float ACCEPTABLE_RELATIVE_ERROR = 1.0e-3f;
int numLoops = 0;
const int MAX_IK_LOOPS = 16;
const quint64 MAX_IK_TIME = 10 * USECS_PER_MSEC;
quint64 expiry = usecTimestampNow() + MAX_IK_TIME;
const int MAX_IK_LOOPS = 4;
do {
largestError = 0.0f;
int lowestMovedIndex = _relativePoses.size();
for (auto& target: targets) {
int tipIndex = target.index;
if (target.type == IKTarget::Type::RotationOnly) {
// the final rotation will be enforced after the iterations
continue;
}
AnimPose targetPose = target.pose;
glm::vec3 tip = absolutePoses[tipIndex].trans;
float error = glm::length(targetPose.trans - tip);
// descend toward root, pivoting each joint to get tip closer to target
int pivotIndex = _skeleton->getParentIndex(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<IKTarge
// compute the axis of the rotation that would align them
glm::vec3 axis = glm::cross(leverArm, targetLine);
float axisLength = glm::length(axis);
if (axisLength > 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<IKTarge
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
}
}
} while (largestError > 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 {

View file

@ -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<IKTarget>& targets);
void solveWithCyclicCoordinateDescent(std::vector<IKTarget>& targets);
void solveWithCyclicCoordinateDescent(const std::vector<IKTarget>& 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

View file

@ -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;

View file

@ -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());

View file

@ -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");
}

View file

@ -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:

View file

@ -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)

View file

@ -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)

View file

@ -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.

View file

@ -215,8 +215,6 @@ protected:
};
typedef std::shared_ptr<Context> ContextPointer;
};
template<typename F>
void doInBatch(std::shared_ptr<gpu::Context> context, F f) {
static gpu::Batch::CacheState cacheState;
@ -226,4 +224,7 @@ void doInBatch(std::shared_ptr<gpu::Context> context, F f) {
cacheState = batch.getCacheState();
}
};
#endif

View file

@ -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>();
geometryCache->useSimpleDrawPipeline(batch);
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, true);

View file

@ -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<FramebufferCache>();
QSize framebufferSize = framebufferCache->getFrameBufferSize();
float fbWidth = framebufferSize.width();

View file

@ -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<FramebufferCache>();

View file

@ -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<FramebufferCache>();
doInBatch(args->_context, [=](gpu::Batch& batch) {
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
batch.enableStereo(false);
QSize framebufferSize = framebufferCache->getFrameBufferSize();

View file

@ -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;

View file

@ -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<DeferredLightingEffect>()->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);

View file

@ -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<FramebufferCache>()->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<TextureCache>()->getWhiteTexture();

View file

@ -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);

View file

@ -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);
});