Merge branch 'master' of https://github.com/highfidelity/hifi into controllers

This commit is contained in:
samcake 2015-12-02 09:49:03 -08:00
commit c7b9d7d26e
20 changed files with 241 additions and 122 deletions

View file

@ -12,7 +12,6 @@
#include <QThread> #include <QThread>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include "ScriptableAvatar.h" #include "ScriptableAvatar.h"
// hold and priority unused but kept so that client side JS can run. // hold and priority unused but kept so that client side JS can run.
@ -48,14 +47,12 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() {
} }
void ScriptableAvatar::update(float deltatime) { void ScriptableAvatar::update(float deltatime) {
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
}
// Run animation // Run animation
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0) { if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && _bind->isLoaded()) {
QStringList modelJoints = getJointNames();
QStringList animationJoints = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
@ -63,19 +60,29 @@ void ScriptableAvatar::update(float deltatime) {
currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
} }
_animationDetails.currentFrame = currentFrame; _animationDetails.currentFrame = currentFrame;
const QVector<FBXJoint>& modelJoints = _bind->getGeometry().joints;
QStringList animationJointNames = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
const int frameCount = _animation->getFrames().size(); const int frameCount = _animation->getFrames().size();
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
const float frameFraction = glm::fract(currentFrame); const float frameFraction = glm::fract(currentFrame);
for (int i = 0; i < animationJoints.size(); i++) { for (int i = 0; i < animationJointNames.size(); i++) {
const QString& name = animationJoints[i]; const QString& name = animationJointNames[i];
int mapping = getJointIndex(name); // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
int mapping = _bind->getGeometry().getJointIndex(name);
if (mapping != -1 && !_maskedJoints.contains(name)) { if (mapping != -1 && !_maskedJoints.contains(name)) {
JointData& data = _jointData[mapping]; JointData& data = _jointData[mapping];
auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); auto newRotation = modelJoints[mapping].preRotation *
safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
// We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in), // We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in),
// but we don't do so for MyAvatar yet, so let's not be different here. // but we don't do so for MyAvatar yet, so let's not be different here.
if (data.rotation != newRotation) { if (data.rotation != newRotation) {

View file

@ -33,6 +33,7 @@ private:
AnimationPointer _animation; AnimationPointer _animation;
AnimationDetails _animationDetails; AnimationDetails _animationDetails;
QStringList _maskedJoints; QStringList _maskedJoints;
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
}; };
#endif // hifi_ScriptableAvatar_h #endif // hifi_ScriptableAvatar_h

View file

@ -43,6 +43,7 @@ void OctreeInboundPacketProcessor::resetStats() {
_totalPackets = 0; _totalPackets = 0;
_lastNackTime = usecTimestampNow(); _lastNackTime = usecTimestampNow();
QWriteLocker locker(&_senderStatsLock);
_singleSenderStats.clear(); _singleSenderStats.clear();
} }
@ -220,6 +221,8 @@ void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, uns
_totalElementsInPacket += editsInPacket; _totalElementsInPacket += editsInPacket;
_totalPackets++; _totalPackets++;
QWriteLocker locker(&_senderStatsLock);
// find the individual senders stats and track them there too... // find the individual senders stats and track them there too...
// see if this is the first we've heard of this node... // see if this is the first we've heard of this node...
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) { if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
@ -242,6 +245,8 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
int packetsSent = 0; int packetsSent = 0;
int totalBytesSent = 0; int totalBytesSent = 0;
QWriteLocker locker(&_senderStatsLock);
NodeToSenderStatsMapIterator i = _singleSenderStats.begin(); NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
while (i != _singleSenderStats.end()) { while (i != _singleSenderStats.end()) {
@ -262,10 +267,9 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
} }
const SharedNodePointer& destinationNode = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID); const SharedNodePointer& destinationNode = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
// If the node no longer exists, wait until the ReceivedPacketProcessor has cleaned up the node // if the node no longer exists, remove its stats
// to remove it from our stats list.
// FIXME Is it safe to clean it up here before ReceivedPacketProcess has?
if (!destinationNode) { if (!destinationNode) {
i = _singleSenderStats.erase(i);
continue; continue;
} }

View file

@ -72,7 +72,7 @@ public:
void resetStats(); void resetStats();
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; }
virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
@ -94,15 +94,16 @@ private:
OctreeServer* _myServer; OctreeServer* _myServer;
int _receivedPacketCount; int _receivedPacketCount;
quint64 _totalTransitTime; std::atomic<uint64_t> _totalTransitTime;
quint64 _totalProcessTime; std::atomic<uint64_t> _totalProcessTime;
quint64 _totalLockWaitTime; std::atomic<uint64_t> _totalLockWaitTime;
quint64 _totalElementsInPacket; std::atomic<uint64_t> _totalElementsInPacket;
quint64 _totalPackets; std::atomic<uint64_t> _totalPackets;
NodeToSenderStatsMap _singleSenderStats; NodeToSenderStatsMap _singleSenderStats;
QReadWriteLock _senderStatsLock;
quint64 _lastNackTime; std::atomic<uint64_t> _lastNackTime;
bool _shuttingDown; bool _shuttingDown;
}; };
#endif // hifi_OctreeInboundPacketProcessor_h #endif // hifi_OctreeInboundPacketProcessor_h

View file

@ -711,7 +711,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
int senderNumber = 0; int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats(); NodeToSenderStatsMap allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) { for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++; senderNumber++;
QUuid senderID = i.key(); QUuid senderID = i.key();

View file

@ -1,6 +1,6 @@
"use strict"; "use strict";
/*jslint vars: true, plusplus: true*/ /*jslint vars: true, plusplus: true*/
/*global Agent, Avatar, Script, Entities, Vec3, print*/ /*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/
// //
// animatedAvatar.js // animatedAvatar.js
// examples/acScripts // examples/acScripts
@ -16,15 +16,62 @@
var origin = {x: 500, y: 500, z: 500}; var origin = {x: 500, y: 500, z: 500};
var spread = 20; // meters var spread = 20; // meters
var turnSpread = 90; // How many degrees should turn from front range over.
var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35};
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy var models = [ // Commented-out avatars do not animate properly on AC's. Presumably because ScriptableAvatar doesn't use model pre-rotations.
Avatar.displayName = "'Bot"; "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/alan/alan.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/andrew/andrew.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/austin/austin.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/birarda/birarda.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/brad/brad.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/chris/chris2.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/clement/clement.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/emily/emily.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ewing/ewing.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/howard/howard.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/huffman/huffman.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/james/james.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/philip/philip.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ryan/ryan.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/sam/sam.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/tony/tony.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/1e57c395-612e-4acd-9561-e79dbda0bc49/faafb83c63a3e5e265884d270fc29f8b.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/615ca447-06ee-4215-8dd1-a609c2fcd0cd/c7af6d4224c501fdd9cb54e0101ff281.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/731c39d7-559a-4ce2-947c-3e2768f5471c/8d5eba2fd5bf068259556aec1861c5dd.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/8bdaa1ec-99df-4a29-a249-6941c7fd1930/37351a18a3dea468088fc3d822aaf29c.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/0909d7b7-c67e-45fb-acd9-a07380dc6b9c/da76b8c59dbc680bdda90df4b9a46faa.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ad0dffd7-f811-487b-a20a-2509235491ef/29106da1f7e6a42c7907603421fd7df5.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/3ebe5c84-8b88-4d91-86ac-f104f3620fe3/3534b032d079514aa8960a316500ce13.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ff060580-2fc7-4b6c-8e12-f023d05363cf/dadef29b1e60f23b413d1850d7e0dd4a.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/b55d3baf-4eb3-4cac-af4c-0fb66d0c907b/ad2c9157f3924ab1f7f6ea87a8cce6cc.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73/4618d52e711fbb34df442b414da767bb.fst"
];
var nameMap = {
faafb83c63a3e5e265884d270fc29f8b: 'albert',
c7af6d4224c501fdd9cb54e0101ff281: 'boss',
'8d5eba2fd5bf068259556aec1861c5dd': 'dougland',
'37351a18a3dea468088fc3d822aaf29c': 'fightbot blue',
da76b8c59dbc680bdda90df4b9a46faa: 'judd',
'29106da1f7e6a42c7907603421fd7df5': 'kate',
'3534b032d079514aa8960a316500ce13': 'lenny',
'98baa90b3b66803c5d7bd4537fca6993': 'lovejoy',
dadef29b1e60f23b413d1850d7e0dd4a: 'mery', // eyes no good
ad2c9157f3924ab1f7f6ea87a8cce6cc: 'owen',
'4618d52e711fbb34df442b414da767bb': 'will'
};
Avatar.skeletonModelURL = models[Math.round(Math.random() * (models.length - 1))]; // pick one
Avatar.displayName = Avatar.skeletonModelURL.match(/\/(\w*).fst/)[1]; // grab the file basename
Avatar.displayName = nameMap[Avatar.displayName] || Avatar.displayName;
var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start.
Agent.isAvatar = true; Agent.isAvatar = true;
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2. function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
Script.setTimeout(function () { Script.setTimeout(function () {
Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()}); Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()});
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, turnSpread * (Math.random() - 0.5), 0);
print("Starting at", JSON.stringify(Avatar.position)); print("Starting at", JSON.stringify(Avatar.position));
Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame);
}, millisecondsToWaitBeforeStarting); }, millisecondsToWaitBeforeStarting);

View file

@ -200,6 +200,7 @@ function entityIsGrabbedByOther(entityID) {
return false; return false;
} }
function MyController(hand) { function MyController(hand) {
this.hand = hand; this.hand = hand;
if (this.hand === RIGHT_HAND) { if (this.hand === RIGHT_HAND) {
@ -223,6 +224,8 @@ function MyController(hand) {
this.rawTriggerValue = 0; this.rawTriggerValue = 0;
this.rawBumperValue = 0; this.rawBumperValue = 0;
this.overlayLine = null;
this.offsetPosition = { this.offsetPosition = {
x: 0.0, x: 0.0,
y: 0.0, y: 0.0,
@ -318,6 +321,33 @@ function MyController(hand) {
}); });
} }
this.overlayLineOn = function(closePoint, farPoint, color) {
if (this.overlayLine === null) {
var lineProperties = {
lineWidth: 5,
start: closePoint,
end: farPoint,
color: color,
ignoreRayIntersection: true, // always ignore this
visible: true,
alpha: 1
};
this.overlayLine = Overlays.addOverlay("line3d", lineProperties);
} else {
var success = Overlays.editOverlay(this.overlayLine, {
lineWidth: 5,
start: closePoint,
end: farPoint,
color: color,
visible: true,
ignoreRayIntersection: true, // always ignore this
alpha: 1
});
}
}
this.lineOn = function(closePoint, farPoint, color) { this.lineOn = function(closePoint, farPoint, color) {
// draw a line // draw a line
if (this.pointer === null) { if (this.pointer === null) {
@ -356,6 +386,13 @@ function MyController(hand) {
this.pointer = null; this.pointer = null;
}; };
this.overlayLineOff = function() {
if (this.overlayLine !== null) {
Overlays.deleteOverlay(this.overlayLine);
}
this.overlayLine = null;
};
this.triggerPress = function(value) { this.triggerPress = function(value) {
_this.rawTriggerValue = value; _this.rawTriggerValue = value;
}; };
@ -604,7 +641,8 @@ function MyController(hand) {
} }
} }
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR);
}; };
this.distanceHolding = function() { this.distanceHolding = function() {
@ -650,6 +688,9 @@ function MyController(hand) {
this.currentAvatarPosition = MyAvatar.position; this.currentAvatarPosition = MyAvatar.position;
this.currentAvatarOrientation = MyAvatar.orientation; this.currentAvatarOrientation = MyAvatar.orientation;
this.overlayLineOff();
}; };
this.continueDistanceHolding = function() { this.continueDistanceHolding = function() {
@ -757,6 +798,7 @@ function MyController(hand) {
} }
this.lineOff(); this.lineOff();
this.overlayLineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
this.activateEntity(this.grabbedEntity, grabbedProperties); this.activateEntity(this.grabbedEntity, grabbedProperties);
@ -898,6 +940,7 @@ function MyController(hand) {
this.pullTowardEquipPosition = function() { this.pullTowardEquipPosition = function() {
this.lineOff(); this.lineOff();
this.overlayLineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
@ -1101,7 +1144,7 @@ function MyController(hand) {
this.release = function() { this.release = function() {
this.lineOff(); this.lineOff();
this.overlayLineOff();
if (this.grabbedEntity !== null) { if (this.grabbedEntity !== null) {
if (this.actionID !== null) { if (this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID); Entities.deleteAction(this.grabbedEntity, this.actionID);
@ -1223,10 +1266,10 @@ Controller.enableMapping(MAPPING_NAME);
var handToDisable = 'none'; var handToDisable = 'none';
function update() { function update() {
if (handToDisable !== LEFT_HAND) { if (handToDisable !== LEFT_HAND && handToDisable!=='both') {
leftController.update(); leftController.update();
} }
if (handToDisable !== RIGHT_HAND) { if (handToDisable !== RIGHT_HAND && handToDisable!=='both') {
rightController.update(); rightController.update();
} }
} }
@ -1234,15 +1277,20 @@ function update() {
Messages.subscribe('Hifi-Hand-Disabler'); Messages.subscribe('Hifi-Hand-Disabler');
handleHandDisablerMessages = function(channel, message, sender) { handleHandDisablerMessages = function(channel, message, sender) {
if (sender === MyAvatar.sessionUUID) { if (sender === MyAvatar.sessionUUID) {
handToDisable = message;
if (message === 'left') { if (message === 'left') {
handToDisable = LEFT_HAND; handToDisable = LEFT_HAND;
} }
if (message === 'right') { if (message === 'right') {
handToDisable = RIGHT_HAND; handToDisable = RIGHT_HAND;
} }
if(message==='both'){
handToDisable='both';
}
if(message==='none'){
handToDisable='none';
}
} }
} }

View file

@ -22,8 +22,10 @@ var laserPointer = (function () {
]; ];
function isHandPointing(hand) { function isHandPointing(hand) {
var MINIMUM_TRIGGER_PULL = 0.9; var MINIMUM_TRIGGER_PULL = 0.9,
return Controller.getTriggerValue(hand) > MINIMUM_TRIGGER_PULL; controller;
controller = hand === 0 ? Controller.Standard.LT : Controller.Standard.RT;
return Controller.getValue(controller) > MINIMUM_TRIGGER_PULL;
} }
function isFingerPointing(hand) { function isFingerPointing(hand) {

View file

@ -193,13 +193,12 @@ var leapHands = (function () {
} }
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration // Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0)); MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0)); MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
// Wait for arms to assume their positions before calculating // Wait for arms to assume their positions before calculating
Script.setTimeout(finishCalibration, CALIBRATION_TIME); Script.setTimeout(finishCalibration, CALIBRATION_TIME);
@ -382,23 +381,14 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ... // Hand rotation in camera coordinates ...
handRotation = { handRotation = {
x: handRotation.z, x: -handRotation.x,
y: handRotation.y, y: -handRotation.z,
z: handRotation.x, z: -handRotation.y,
w: handRotation.w w: handRotation.w
}; };
// Hand rotation in avatar coordinates ... // Hand rotation in avatar coordinates ...
if (h === 0) { handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
}
cameraOrientation.x = -cameraOrientation.x; cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z; cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation); handRotation = Quat.multiply(cameraOrientation, handRotation);
@ -421,22 +411,14 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ... // Hand rotation in camera coordinates ...
handRotation = { handRotation = {
x: handRotation.z, x: -handRotation.x,
y: handRotation.y, y: -handRotation.z,
z: handRotation.x, z: -handRotation.y,
w: handRotation.w w: handRotation.w
}; };
// Hand rotation in avatar coordinates ... // Hand rotation in avatar coordinates ...
if (h === 0) { handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
handRotation);
}
} }
// Set hand position and orientation ... // Set hand position and orientation ...
@ -462,7 +444,7 @@ var leapHands = (function () {
w: locRotation.w w: locRotation.w
}; };
} }
MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation); MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation);
} }
} }
} }

View file

@ -18,6 +18,7 @@ Script.include("../../../libraries/constants.js");
var GUN_FORCE =20; var GUN_FORCE =20;
Messages.sendMessage('Hifi-Hand-Disabler', "both");
var gameName = "Kill All The Rats!" var gameName = "Kill All The Rats!"
// var HOST = "localhost:5000" // var HOST = "localhost:5000"
@ -194,6 +195,7 @@ function fire(side, value) {
function scriptEnding() { function scriptEnding() {
Messages.sendMessage('Hifi-Hand-Disabler', 'none');
mapping.disable(); mapping.disable();
for (var i = 0; i < pointers.length; ++i) { for (var i = 0; i < pointers.length; ++i) {
Overlays.deleteOverlay(pointers[i]); Overlays.deleteOverlay(pointers[i]);

View file

@ -36,14 +36,10 @@
Entities.editEntity(this.entityID, { Entities.editEntity(this.entityID, {
animation: { animation: {
url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx", url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
currentFrame: 0 running: true
} }
}); });
Entities.editEntity(_this.entityID, {
animationIsPlaying: true
});
var position = Entities.getEntityProperties(this.entityID, "position").position; var position = Entities.getEntityProperties(this.entityID, "position").position;
this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], { this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
position: position, position: position,
@ -67,8 +63,10 @@
this.audioInjector.stop(); this.audioInjector.stop();
Entities.editEntity(this.entityID, { Entities.editEntity(this.entityID, {
animation: { animation: {
url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", // Providing actual model fbx for animation used to work, now contorts doll into a weird ball
currentFrame: 0 // See bug: https://app.asana.com/0/26225263936266/70097355490098
// url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
running: false,
} }
}); });

View file

@ -22,6 +22,8 @@
Controller.Standard.LT, Controller.Standard.LT,
Controller.Standard.RT, Controller.Standard.RT,
]; ];
var RELOAD_THRESHOLD = 0.95;
Pistol = function() { Pistol = function() {
_this = this; _this = this;
this.equipped = false; this.equipped = false;
@ -36,7 +38,7 @@
this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw"); this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw");
this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav"); this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav");
this.playRichochetSoundChance = 0.1; this.playRichochetSoundChance = 0.1;
this.fireVolume = 0.5; this.fireVolume = 0.2;
this.bulletForce = 10; this.bulletForce = 10;
@ -45,6 +47,7 @@
}; };
Pistol.prototype = { Pistol.prototype = {
canShoot: false,
startEquip: function(id, params) { startEquip: function(id, params) {
this.equipped = true; this.equipped = true;
@ -62,6 +65,17 @@
}, },
toggleWithTriggerPressure: function() { toggleWithTriggerPressure: function() {
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]); this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
if (this.triggerValue < RELOAD_THRESHOLD) {
// print('RELOAD');
this.canShoot = true;
}
if (this.canShoot === true && this.triggerValue === 1) {
// print('SHOOT');
this.fire();
this.canShoot = false;
}
if (this.triggerValue < DISABLE_LASER_THRESHOLD && this.showLaser === true) { if (this.triggerValue < DISABLE_LASER_THRESHOLD && this.showLaser === true) {
this.showLaser = false; this.showLaser = false;
Overlays.editOverlay(this.laser, { Overlays.editOverlay(this.laser, {
@ -74,6 +88,7 @@
}); });
} }
}, },
updateLaser: function() { updateLaser: function() {
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
@ -101,7 +116,7 @@
preload: function(entityID) { preload: function(entityID) {
this.entityID = entityID; this.entityID = entityID;
this.initControllerMapping(); // this.initControllerMapping();
this.laser = Overlays.addOverlay("line3d", { this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR, start: ZERO_VECTOR,
end: ZERO_VECTOR, end: ZERO_VECTOR,
@ -150,22 +165,7 @@
} }
}, },
initControllerMapping: function() {
this.mapping = Controller.newMapping();
this.mapping.from(Controller.Standard.LT).hysteresis(0.0, 0.75).to(function(value) {
_this.triggerPress(0, value);
});
this.mapping.from(Controller.Standard.RT).hysteresis(0.0, 0.75).to(function(value) {
_this.triggerPress(1, value);
});
this.mapping.enable();
},
unload: function() { unload: function() {
this.mapping.disable();
Overlays.deleteOverlay(this.laser); Overlays.deleteOverlay(this.laser);
}, },

View file

@ -2856,6 +2856,8 @@ void Application::update(float deltaTime) {
} }
} }
_controllerScriptingInterface->updateInputControllers();
// Transfer the user inputs to the driveKeys // Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly? // FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys(); myAvatar->clearDriveKeys();

View file

@ -84,13 +84,12 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
return qApp->getUiSize(); return qApp->getUiSize();
} }
controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retreive the Device Tracker category and then the sub tracker within it // This is where we retrieve the Device Tracker category and then the sub tracker within it
auto icIt = _inputControllers.find(0); auto icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) { if (icIt != _inputControllers.end()) {
return (*icIt).second; return (*icIt).second.get();
} }
// Look for device // Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
@ -110,18 +109,24 @@ controller::InputController::Pointer ControllerScriptingInterface::createInputCo
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this); controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
controller::InputController::Key key = inputController->getKey(); controller::InputController::Key key = inputController->getKey();
_inputControllers.insert(InputControllerMap::value_type(key, inputController)); _inputControllers.insert(InputControllerMap::value_type(key, inputController));
return inputController; return inputController.get();
} }
} }
} }
return controller::InputController::Pointer(); return nullptr;
} }
void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) { void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
_inputControllers.erase(input->getKey()); _inputControllers.erase(input->getKey());
} }
void ControllerScriptingInterface::updateInputControllers() {
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
_deviceTrackerId(deviceTrackerId), _deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId), _subTrackerId(subTrackerId),

View file

@ -85,6 +85,8 @@ public:
bool isKeyCaptured(const KeyEvent& event) const; bool isKeyCaptured(const KeyEvent& event) const;
bool isJoystickCaptured(int joystickIndex) const; bool isJoystickCaptured(int joystickIndex) const;
void updateInputControllers();
public slots: public slots:
virtual void captureKeyEvents(const KeyEvent& event); virtual void captureKeyEvents(const KeyEvent& event);
@ -96,8 +98,8 @@ public slots:
virtual glm::vec2 getViewportDimensions() const; virtual glm::vec2 getViewportDimensions() const;
/// Factory to create an InputController /// Factory to create an InputController
virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker); virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController::Pointer input); virtual void releaseInputController(controller::InputController* input);
signals: signals:
void keyPressEvent(const KeyEvent& event); void keyPressEvent(const KeyEvent& event);

View file

@ -116,7 +116,7 @@ void AvatarData::setOrientation(const glm::quat& orientation, bool overideRefere
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation)); glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation));
_bodyPitch = eulerAngles.x; _bodyPitch = eulerAngles.x;
_bodyYaw = eulerAngles.y; _bodyYaw = eulerAngles.y;
_bodyRoll = eulerAngles.z; _bodyRoll = eulerAngles.z;
} }
} }
@ -1212,7 +1212,14 @@ void AvatarData::setJointMappingsFromNetworkReply() {
QByteArray line; QByteArray line;
while (!(line = networkReply->readLine()).isEmpty()) { while (!(line = networkReply->readLine()).isEmpty()) {
if (!(line = line.trimmed()).startsWith("jointIndex")) { line = line.trimmed();
if (line.startsWith("filename")) {
int filenameIndex = line.indexOf('=') + 1;
if (filenameIndex > 0) {
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
}
}
if (!line.startsWith("jointIndex")) {
continue; continue;
} }
int jointNameIndex = line.indexOf('=') + 1; int jointNameIndex = line.indexOf('=') + 1;
@ -1522,6 +1529,17 @@ QJsonObject AvatarData::toJson() const {
} }
void AvatarData::fromJson(const QJsonObject& json) { void AvatarData::fromJson(const QJsonObject& json) {
// The head setOrientation likes to overwrite the avatar orientation,
// so lets do the head first
// Most head data is relative to the avatar, and needs no basis correction,
// but the lookat vector does need correction
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
if (json.contains(JSON_AVATAR_HEAD_MODEL)) { if (json.contains(JSON_AVATAR_HEAD_MODEL)) {
auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString(); auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString();
if (faceModelURL != getFaceModelURL().toString()) { if (faceModelURL != getFaceModelURL().toString()) {
@ -1593,15 +1611,6 @@ void AvatarData::fromJson(const QJsonObject& json) {
} }
setRawJointData(jointArray); setRawJointData(jointArray);
} }
// Most head data is relative to the avatar, and needs no basis correction,
// but the lookat vector does need correction
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
} }
// Every frame will store both a basis for the recording and a relative transform // Every frame will store both a basis for the recording and a relative transform

View file

@ -392,6 +392,7 @@ protected:
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonFBXURL;
QVector<AttachmentData> _attachmentData; QVector<AttachmentData> _attachmentData;
QString _displayName; QString _displayName;

View file

@ -81,6 +81,9 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) {
out = qobject_cast<AvatarData*>(object.toQObject()); out = qobject_cast<AvatarData*>(object.toQObject());
} }
Q_DECLARE_METATYPE(controller::InputController*)
static int inputControllerPointerId = qRegisterMetaType<controller::InputController*>();
QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) {
return engine->newQObject(in); return engine->newQObject(in);
} }
@ -89,8 +92,6 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu
out = qobject_cast<controller::InputController*>(object.toQObject()); out = qobject_cast<controller::InputController*>(object.toQObject());
} }
static bool hasCorrectSyntax(const QScriptProgram& program) { static bool hasCorrectSyntax(const QScriptProgram& program) {
const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {

View file

@ -132,11 +132,12 @@
createLights(); createLights();
createCat({ createCat({
x: 551.09, x: 551.0,
y: 494.98, y: 495.3,
z: 503.49 z: 503.3
}); });
createSprayCan({ createSprayCan({
x: 549.7, x: 549.7,
y: 495.6, y: 495.6,
@ -240,6 +241,7 @@
gravity: BOW_GRAVITY, gravity: BOW_GRAVITY,
shapeType: 'compound', shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL, compoundShapeURL: COLLISION_HULL_URL,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav",
script: bowScriptURL, script: bowScriptURL,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
@ -648,6 +650,7 @@
z: 0.08 z: 0.08
}, },
collisionsWillMove: true, collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav",
gravity: { gravity: {
x: 0, x: 0,
y: -3.5, y: -3.5,
@ -1184,10 +1187,11 @@
z: 0.07 z: 0.07
}, },
collisionsWillMove: true, collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav",
shapeType: 'box', shapeType: 'box',
gravity: { gravity: {
x: 0, x: 0,
y: -0.5, y: -3.0,
z: 0 z: 0
}, },
velocity: { velocity: {

View file

@ -110,9 +110,9 @@ MasterReset = function() {
createCat({ createCat({
x: 551.09, x: 551.0,
y: 494.98, y: 495.3,
z: 503.49 z: 503.3
}); });
createSprayCan({ createSprayCan({
@ -220,6 +220,7 @@ MasterReset = function() {
gravity: BOW_GRAVITY, gravity: BOW_GRAVITY,
shapeType: 'compound', shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL, compoundShapeURL: COLLISION_HULL_URL,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav",
script: bowScriptURL, script: bowScriptURL,
userData: JSON.stringify({ userData: JSON.stringify({
resetMe: { resetMe: {
@ -629,6 +630,7 @@ MasterReset = function() {
z: 0.08 z: 0.08
}, },
collisionsWillMove: true, collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav",
gravity: { gravity: {
x: 0, x: 0,
y: -3.5, y: -3.5,
@ -1165,10 +1167,11 @@ MasterReset = function() {
z: 0.07 z: 0.07
}, },
collisionsWillMove: true, collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav",
shapeType: 'box', shapeType: 'box',
gravity: { gravity: {
x: 0, x: 0,
y: -0.5, y: -3.0,
z: 0 z: 0
}, },
velocity: { velocity: {