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

This commit is contained in:
Sam Gateau 2015-09-18 00:03:19 -07:00
commit d0f7ddd3a6
64 changed files with 956 additions and 690 deletions

View file

@ -153,11 +153,11 @@ void Agent::run() {
qDebug() << "Downloaded script:" << scriptContents;
_scriptEngine = new ScriptEngine(scriptContents, _payload);
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(scriptContents, _payload));
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
// setup an Avatar for the script to use
ScriptableAvatar scriptedAvatar(_scriptEngine);
ScriptableAvatar scriptedAvatar(_scriptEngine.get());
scriptedAvatar.setForceFaceTrackerConnected(true);
// call model URL setters with empty URLs so our avatar, if user, will have the default models
@ -191,22 +191,21 @@ void Agent::run() {
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
// we need to make sure that init has been called for our EntityScriptingInterface
// so that it actually has a jurisdiction listener when we ask it for it next
entityScriptingInterface->init();
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
_entityViewer.init();
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
// wire up our additional agent related processing to the update signal
QObject::connect(_scriptEngine, &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
_scriptEngine->run();
setFinished(true);
// kill the avatar identity timer
delete _avatarIdentityTimer;
// delete the script engine
delete _scriptEngine;
}
void Agent::setIsAvatar(bool isAvatar) {
@ -227,10 +226,17 @@ void Agent::setIsAvatar(bool isAvatar) {
}
if (!_isAvatar) {
if (_avatarIdentityTimer) {
_avatarIdentityTimer->stop();
delete _avatarIdentityTimer;
_avatarIdentityTimer = NULL;
delete _avatarBillboardTimer;
_avatarBillboardTimer = NULL;
_avatarIdentityTimer = nullptr;
}
if (_avatarBillboardTimer) {
_avatarIdentityTimer->stop();
delete _avatarIdentityTimer;
_avatarBillboardTimer = nullptr;
}
}
}

View file

@ -12,6 +12,7 @@
#ifndef hifi_Agent_h
#define hifi_Agent_h
#include <memory>
#include <vector>
#include <QtScript/QScriptEngine>
@ -61,7 +62,7 @@ private slots:
void processAgentAvatarAndAudio(float deltaTime);
private:
ScriptEngine* _scriptEngine;
std::unique_ptr<ScriptEngine> _scriptEngine;
EntityEditPacketSender _entityEditSender;
EntityTreeHeadlessViewer _entityViewer;
QTimer* _pingTimer;

View file

@ -1,7 +1,8 @@
(function() {
// Script.include("../libraries/utils.js");
//Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
Script.include("https://hifi-public.s3.amazonaws.com/scripts/libraries/utils.js");
Script.include("../libraries/utils.js");
GRAB_FRAME_USER_DATA_KEY = "grabFrame";
this.userData = {};
@ -56,26 +57,21 @@
timeSinceLastMoved = 0;
}
if (self.userData.grabKey && self.userData.grabKey.activated === true) {
//Only activate for the user who grabbed the object
if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId == MyAvatar.sessionUUID) {
if (self.activated !== true) {
//We were just grabbed, so create a particle system
self.grab();
Entities.editEntity(self.paintStream, {
animationSettings: startSetting
});
}
//Move emitter to where entity is always when its activated
self.sprayStream();
} else if (self.userData.grabKey && self.userData.grabKey.activated === false && self.activated) {
Entities.editEntity(self.paintStream, {
animationSettings: stopSetting
});
self.activated = false;
self.letGo();
}
}
this.grab = function() {
self.activated = true;
this.activated = true;
var animationSettings = JSON.stringify({
fps: 30,
loop: true,
@ -92,9 +88,9 @@
emitVelocity: ZERO_VEC,
emitAcceleration: ZERO_VEC,
velocitySpread: {
x: .02,
y: .02,
z: 0.02
x: .1,
y: .1,
z: 0.1
},
emitRate: 100,
particleRadius: 0.01,
@ -103,14 +99,14 @@
green: 20,
blue: 150
},
lifetime: 500, //probably wont be holding longer than this straight
lifetime: 50, //probably wont be holding longer than this straight
});
}
this.letGo = function() {
self.activated = false;
this.activated = false;
Entities.deleteEntity(this.paintStream);
this.paintStream = null;
}
this.reset = function() {
@ -123,8 +119,7 @@
}
this.sprayStream = function() {
var forwardVec = Quat.getFront(self.properties.rotation);
forwardVec = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 90, 0), forwardVec);
var forwardVec = Quat.getFront(Quat.multiply(self.properties.rotation , Quat.fromPitchYawRollDegrees(0, 90, 0)));
forwardVec = Vec3.normalize(forwardVec);
var upVec = Quat.getUp(self.properties.rotation);
@ -132,11 +127,10 @@
position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
Entities.editEntity(self.paintStream, {
position: position,
emitVelocity: Vec3.multiply(forwardVec, 4)
emitVelocity: Vec3.multiply(5, forwardVec)
});
//Now check for an intersection with an entity
//move forward so ray doesnt intersect with gun
var origin = Vec3.sum(position, forwardVec);
var pickRay = {
@ -216,6 +210,8 @@
this.entityId = entityId;
this.properties = Entities.getEntityProperties(self.entityId);
this.getUserData();
//Only activate for the avatar who is grabbing the can!
if (this.userData.grabKey && this.userData.grabKey.activated) {
this.activated = true;
}
@ -235,7 +231,9 @@
this.unload = function() {
Script.update.disconnect(this.update);
if(this.paintStream) {
Entities.deleteEntity(this.paintStream);
}
this.strokes.forEach(function(stroke) {
Entities.deleteEntity(stroke);
});
@ -244,6 +242,7 @@
});
function randFloat(min, max) {
return Math.random() * (max - min) + min;
}

View file

@ -9,9 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//Just temporarily using my own bucket here so others can test the entity. Once PR is tested and merged, then the entity script will appear in its proper place in S3, and I wil switch it
var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v1";
// var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v6 ";
var scriptURL = Script.resolvePath("entityScripts/sprayPaintCan.js?v2");
var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx";
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
var sprayCan = Entities.addEntity({
type: "Model",
@ -32,7 +32,10 @@ var sprayCan = Entities.addEntity({
});
function cleanup() {
Entities.deleteEntity(sprayCan);
// Uncomment the below line to delete sprayCan on script reload- for faster iteration during development
// Entities.deleteEntity(sprayCan);
}
Script.scriptEnding.connect(cleanup);

View file

@ -182,9 +182,6 @@ public:
using namespace std;
// Starfield information
const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB
static QTimer* locationUpdateTimer = NULL;
static QTimer* balanceUpdateTimer = NULL;
static QTimer* identityPacketTimer = NULL;
@ -370,6 +367,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastFaceTrackerUpdate(0),
_applicationOverlay()
{
thread()->setObjectName("Main Thread");
setInstance(this);
_entityClipboard->createRootElement();
@ -460,13 +459,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
audioThread->start();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
// Setup AssetClient
auto assetClient = DependencyManager::get<AssetClient>();
QThread* assetThread = new QThread;
assetThread->setObjectName("Asset Thread");
assetClient->moveToThread(assetThread);
connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init);
assetThread->start();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
@ -855,8 +853,7 @@ void Application::cleanupBeforeQuit() {
}
void Application::emptyLocalCache() {
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
if (cache) {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}
@ -4350,6 +4347,8 @@ void Application::stopScript(const QString &scriptName, bool restart) {
}
void Application::reloadAllScripts() {
DependencyManager::get<ScriptCache>()->clearCache();
getEntities()->reloadEntityScripts();
stopAllScripts(true);
}

View file

@ -1302,7 +1302,7 @@ void MyAvatar::initAnimGraph() {
// or run a local web-server
// python -m SimpleHTTPServer&
//auto graphUrl = QUrl("http://localhost:8000/avatar.json");
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/5f2a4e268d35147c83d44881e268f83a6296e89b/ik-avatar-hands.json");
auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/04a02c47eb56d8bfaebb/raw/72517b231f606b724c5169e02642e401f9af5a54/ik-avatar-hands.json");
_rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry());
}

View file

@ -140,8 +140,7 @@ void DiskCacheEditor::clear() {
"You are about to erase all the content of the disk cache,"
"are you sure you want to do that?");
if (buttonClicked == QMessageBox::Yes) {
QNetworkDiskCache* cache = qobject_cast<QNetworkDiskCache*>(NetworkAccessManager::getInstance().cache());
if (cache) {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
qDebug() << "DiskCacheEditor::clear(): Clearing disk cache.";
cache->clear();
}

View file

@ -13,7 +13,7 @@
#include "AnimationLogging.h"
#include "AnimUtil.h"
AnimBlendLinear::AnimBlendLinear(const std::string& id, float alpha) :
AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) :
AnimNode(AnimNode::Type::BlendLinear, id),
_alpha(alpha) {

View file

@ -27,12 +27,12 @@ class AnimBlendLinear : public AnimNode {
public:
friend class AnimTests;
AnimBlendLinear(const std::string& id, float alpha);
AnimBlendLinear(const QString& id, float alpha);
virtual ~AnimBlendLinear() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
protected:
// for AnimDebugDraw rendering
@ -42,7 +42,7 @@ protected:
float _alpha;
std::string _alphaVar;
QString _alphaVar;
// no copies
AnimBlendLinear(const AnimBlendLinear&) = delete;

View file

@ -13,7 +13,7 @@
#include "AnimationLogging.h"
#include "AnimUtil.h"
AnimClip::AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) :
AnimNode(AnimNode::Type::Clip, id),
_startFrame(startFrame),
_endFrame(endFrame),
@ -68,9 +68,9 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt,
return _poses;
}
void AnimClip::loadURL(const std::string& url) {
void AnimClip::loadURL(const QString& url) {
auto animCache = DependencyManager::get<AnimationCache>();
_networkAnim = animCache->getAnimation(QString::fromStdString(url));
_networkAnim = animCache->getAnimation(url);
_url = url;
}
@ -127,7 +127,7 @@ void AnimClip::copyFromNetworkAnim() {
for (int i = 0; i < animJointCount; i++) {
int skeletonJoint = _skeleton->nameToJointIndex(animJoints.at(i).name);
if (skeletonJoint == -1) {
qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url.c_str();
qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url;
}
jointMap.push_back(skeletonJoint);
}

View file

@ -25,19 +25,19 @@ class AnimClip : public AnimNode {
public:
friend class AnimTests;
AnimClip(const std::string& id, const std::string& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag);
virtual ~AnimClip() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setStartFrameVar(const std::string& startFrameVar) { _startFrameVar = startFrameVar; }
void setEndFrameVar(const std::string& endFrameVar) { _endFrameVar = endFrameVar; }
void setTimeScaleVar(const std::string& timeScaleVar) { _timeScaleVar = timeScaleVar; }
void setLoopFlagVar(const std::string& loopFlagVar) { _loopFlagVar = loopFlagVar; }
void setFrameVar(const std::string& frameVar) { _frameVar = frameVar; }
void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; }
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; }
void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; }
void setFrameVar(const QString& frameVar) { _frameVar = frameVar; }
protected:
void loadURL(const std::string& url);
void loadURL(const QString& url);
virtual void setCurrentFrameInternal(float frame) override;
@ -53,18 +53,18 @@ protected:
// _anim[frame][joint]
std::vector<AnimPoseVec> _anim;
std::string _url;
QString _url;
float _startFrame;
float _endFrame;
float _timeScale;
bool _loopFlag;
float _frame;
std::string _startFrameVar;
std::string _endFrameVar;
std::string _timeScaleVar;
std::string _loopFlagVar;
std::string _frameVar;
QString _startFrameVar;
QString _endFrameVar;
QString _timeScaleVar;
QString _loopFlagVar;
QString _frameVar;
// no copies
AnimClip(const AnimClip&) = delete;

View file

@ -9,6 +9,7 @@
#include "AnimInverseKinematics.h"
#include <GLMHelpers.h>
#include <NumericalConstants.h>
#include <SharedUtil.h>
@ -16,7 +17,7 @@
#include "SwingTwistConstraint.h"
#include "AnimationLogging.h"
AnimInverseKinematics::AnimInverseKinematics(const std::string& id) : AnimNode(AnimNode::Type::InverseKinematics, id) {
AnimInverseKinematics::AnimInverseKinematics(const QString& id) : AnimNode(AnimNode::Type::InverseKinematics, id) {
}
AnimInverseKinematics::~AnimInverseKinematics() {
@ -54,7 +55,20 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) {
// if there are dups, last one wins.
_targetVarVec.push_back(IKTargetVar(jointName, positionVar.toStdString(), rotationVar.toStdString()));
bool found = false;
for (auto& targetVar: _targetVarVec) {
if (targetVar.jointName == jointName) {
// update existing targetVar
targetVar.positionVar = positionVar;
targetVar.rotationVar = rotationVar;
found = true;
break;
}
}
if (!found) {
// create a new entry
_targetVarVec.push_back(IKTargetVar(jointName, positionVar, rotationVar));
}
}
static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) {
@ -68,6 +82,12 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
return rootIndex;
}
struct IKTarget {
AnimPose pose;
int index;
int rootIndex;
};
//virtual
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
@ -76,43 +96,53 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
return _relativePoses;
}
// evaluate target vars
// build a list of targets from _targetVarVec
std::vector<IKTarget> targets;
bool removeUnfoundJoints = false;
for (auto& targetVar : _targetVarVec) {
// lazy look up of jointIndices and insertion into _absoluteTargets map
if (!targetVar.hasPerformedJointLookup) {
targetVar.jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
if (targetVar.jointIndex >= 0) {
// insert into _absoluteTargets map
IKTarget target;
target.pose = AnimPose::identity;
target.rootIndex = findRootJointInSkeleton(_skeleton, targetVar.jointIndex);
_absoluteTargets[targetVar.jointIndex] = target;
if (targetVar.jointIndex > _maxTargetIndex) {
_maxTargetIndex = targetVar.jointIndex;
}
if (targetVar.jointIndex == -1) {
// this targetVar hasn't been validated yet...
int jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
if (jointIndex >= 0) {
// this targetVar has a valid joint --> cache the indices
targetVar.jointIndex = jointIndex;
targetVar.rootIndex = findRootJointInSkeleton(_skeleton, jointIndex);
} else {
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
removeUnfoundJoints = true;
}
targetVar.hasPerformedJointLookup = true;
}
if (targetVar.jointIndex >= 0) {
// update pose in _absoluteTargets map
auto iter = _absoluteTargets.find(targetVar.jointIndex);
if (iter != _absoluteTargets.end()) {
} else {
// TODO: get this done without a double-lookup of each var in animVars
if (animVars.hasKey(targetVar.positionVar) || animVars.hasKey(targetVar.rotationVar)) {
IKTarget target;
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
iter->second.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
iter->second.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.rootIndex = targetVar.rootIndex;
target.index = targetVar.jointIndex;
targets.push_back(target);
}
}
}
// RELAX! Don't do it.
// relaxTowardDefaults(dt);
if (removeUnfoundJoints) {
int numVars = _targetVarVec.size();
int i = 0;
while (i < numVars) {
if (_targetVarVec[i].jointIndex == -1) {
if (numVars > 1) {
// swap i for last element
_targetVarVec[i] = _targetVarVec[numVars - 1];
}
_targetVarVec.pop_back();
--numVars;
} else {
++i;
}
}
}
if (_absoluteTargets.empty()) {
if (targets.empty()) {
// no IK targets but still need to enforce constraints
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
while (constraintItr != _constraints.end()) {
@ -135,11 +165,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
quint64 expiry = usecTimestampNow() + MAX_IK_TIME;
do {
largestError = 0.0f;
for (auto& targetPair: _absoluteTargets) {
for (auto& target: targets) {
int lowestMovedIndex = _relativePoses.size() - 1;
int tipIndex = targetPair.first;
AnimPose targetPose = targetPair.second.pose;
int rootIndex = targetPair.second.rootIndex;
int tipIndex = target.index;
AnimPose targetPose = target.pose;
int rootIndex = target.rootIndex;
if (rootIndex != -1) {
// transform targetPose into skeleton's absolute frame
AnimPose& rootPose = _relativePoses[rootIndex];
@ -150,11 +180,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
glm::vec3 tip = absolutePoses[tipIndex].trans;
float error = glm::length(targetPose.trans - tip);
// descend toward root, rotating each joint to get tip closer to target
int index = _skeleton->getParentIndex(tipIndex);
while (index != -1 && error > ACCEPTABLE_RELATIVE_ERROR) {
// descend toward root, pivoting each joint to get tip closer to target
int pivotIndex = _skeleton->getParentIndex(tipIndex);
while (pivotIndex != -1 && error > ACCEPTABLE_RELATIVE_ERROR) {
// compute the two lines that should be aligned
glm::vec3 jointPosition = absolutePoses[index].trans;
glm::vec3 jointPosition = absolutePoses[pivotIndex].trans;
glm::vec3 leverArm = tip - jointPosition;
glm::vec3 targetLine = targetPose.trans - jointPosition;
@ -173,29 +203,34 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
angle = 0.5f * angle;
glm::quat deltaRotation = glm::angleAxis(angle, axis);
int parentIndex = _skeleton->getParentIndex(index);
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[index].rot);
RotationConstraint* constraint = getConstraint(index);
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[index].rot);
deltaRotation = absolutePoses[parentIndex].rot *
newRot *
glm::inverse(absolutePoses[pivotIndex].rot);
}
}
_relativePoses[index].rot = newRot;
_relativePoses[pivotIndex].rot = newRot;
}
// this joint has been changed so we check to see if it has the lowest index
if (index < lowestMovedIndex) {
lowestMovedIndex = index;
if (pivotIndex < lowestMovedIndex) {
lowestMovedIndex = pivotIndex;
}
// keep track of tip's new position as we descend towards root
@ -203,7 +238,7 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
error = glm::length(targetPose.trans - tip);
}
}
index = _skeleton->getParentIndex(index);
pivotIndex = _skeleton->getParentIndex(pivotIndex);
}
if (largestError < error) {
largestError = error;
@ -248,12 +283,16 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
loadPoses(underPoses);
} else {
// relax toward underpose
const float RELAXATION_TIMESCALE = 0.125f;
const float alpha = glm::clamp(dt / RELAXATION_TIMESCALE, 0.0f, 1.0f);
// HACK: this relaxation needs to be constant per-frame rather than per-realtime
// in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts
// of this relaxation will be FPS dependent (low FPS will make the limbs align slower
// in real-time), however most people will not notice this and this problem is less
// annoying than the flutter.
const float blend = (1.0f / 60.0f) / (0.25f); // effectively: dt / RELAXATION_TIMESCALE
int numJoints = (int)_relativePoses.size();
for (int i = 0; i < numJoints; ++i) {
float dotSign = copysignf(1.0f, glm::dot(_relativePoses[i].rot, underPoses[i].rot));
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, alpha));
_relativePoses[i].rot = glm::normalize(glm::lerp(_relativePoses[i].rot, dotSign * underPoses[i].rot, blend));
}
}
return evaluate(animVars, dt, triggersOut);
@ -277,10 +316,6 @@ void AnimInverseKinematics::clearConstraints() {
_constraints.clear();
}
const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
const glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
void AnimInverseKinematics::initConstraints() {
if (!_skeleton) {
return;
@ -306,7 +341,7 @@ void AnimInverseKinematics::initConstraints() {
// y |
// | |
// | O---O---O RightUpLeg
// z | | |
// z | | Hips2 |
// \ | | |
// \| | |
// x -----+ O O RightLeg
@ -334,7 +369,7 @@ void AnimInverseKinematics::initConstraints() {
_constraints.clear();
for (int i = 0; i < numJoints; ++i) {
// compute the joint's baseName and remember if it was Left or not
// compute the joint's baseName and remember whether its prefix was "Left" or not
QString baseName = _skeleton->getJointName(i);
bool isLeft = baseName.startsWith("Left", Qt::CaseInsensitive);
float mirror = isLeft ? -1.0f : 1.0f;
@ -467,6 +502,18 @@ void AnimInverseKinematics::initConstraints() {
minDots.push_back(cosf(MAX_SPINE_SWING));
stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (baseName.startsWith("Hips2", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot);
const float MAX_SPINE_TWIST = PI / 8.0f;
stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST);
std::vector<float> minDots;
const float MAX_SPINE_SWING = PI / 14.0f;
minDots.push_back(cosf(MAX_SPINE_SWING));
stConstraint->setSwingLimits(minDots);
constraint = static_cast<RotationConstraint*>(stConstraint);
} else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) {
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
@ -488,18 +535,18 @@ void AnimInverseKinematics::initConstraints() {
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment
glm::vec3 hingeAxis = - mirror * zAxis;
glm::vec3 hingeAxis = - mirror * Vectors::UNIT_Z;
const float MIN_ELBOW_ANGLE = 0.05f;
const float MAX_ELBOW_ANGLE = 11.0f * PI / 12.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * yAxis;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * yAxis;
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_ELBOW_ANGLE, hingeAxis) * Vectors::UNIT_Y;
// for the rest of the math we rotate hingeAxis into the child frame
hingeAxis = referenceRotation * hingeAxis;
eConstraint->setHingeAxis(hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis);
float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis));
if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) {
minAngle = - minAngle;
@ -516,21 +563,21 @@ void AnimInverseKinematics::initConstraints() {
ElbowConstraint* eConstraint = new ElbowConstraint();
glm::quat referenceRotation = _defaultRelativePoses[i].rot;
eConstraint->setReferenceRotation(referenceRotation);
glm::vec3 hingeAxis = -1.0f * xAxis;
glm::vec3 hingeAxis = -1.0f * Vectors::UNIT_X;
// we determine the max/min angles by rotating the swing limit lines from parent- to child-frame
// then measure the angles to swing the yAxis into alignment
const float MIN_KNEE_ANGLE = 0.0f;
const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f;
glm::quat invReferenceRotation = glm::inverse(referenceRotation);
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * yAxis;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * yAxis;
glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y;
// for the rest of the math we rotate hingeAxis into the child frame
hingeAxis = referenceRotation * hingeAxis;
eConstraint->setHingeAxis(hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(yAxis - glm::dot(yAxis, hingeAxis) * hingeAxis);
glm::vec3 projectedYAxis = glm::normalize(Vectors::UNIT_Y - glm::dot(Vectors::UNIT_Y, hingeAxis) * hingeAxis);
float minAngle = acosf(glm::dot(projectedYAxis, minSwingAxis));
if (glm::dot(hingeAxis, glm::cross(projectedYAxis, minSwingAxis)) < 0.0f) {
minAngle = - minAngle;
@ -550,8 +597,8 @@ void AnimInverseKinematics::initConstraints() {
// these directions are approximate swing limits in parent-frame
// NOTE: they don't need to be normalized
std::vector<glm::vec3> swungDirections;
swungDirections.push_back(yAxis);
swungDirections.push_back(xAxis);
swungDirections.push_back(Vectors::UNIT_Y);
swungDirections.push_back(Vectors::UNIT_X);
swungDirections.push_back(glm::vec3(1.0f, 1.0f, 1.0f));
swungDirections.push_back(glm::vec3(1.0f, 1.0f, -1.0f));
@ -575,13 +622,10 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
AnimNode::setSkeletonInternal(skeleton);
// invalidate all targetVars
for (auto& targetVar : _targetVarVec) {
targetVar.hasPerformedJointLookup = false;
for (auto& targetVar: _targetVarVec) {
targetVar.jointIndex = -1;
}
// invalidate all targets
_absoluteTargets.clear();
_maxTargetIndex = 0;
if (skeleton) {

View file

@ -19,7 +19,7 @@ class RotationConstraint;
class AnimInverseKinematics : public AnimNode {
public:
AnimInverseKinematics(const std::string& id);
AnimInverseKinematics(const QString& id);
virtual ~AnimInverseKinematics() override;
void loadDefaultPoses(const AnimPoseVec& poses);
@ -45,28 +45,22 @@ protected:
void initConstraints();
struct IKTargetVar {
IKTargetVar(const QString& jointNameIn, const std::string& positionVarIn, const std::string& rotationVarIn) :
IKTargetVar(const QString& jointNameIn, const QString& positionVarIn, const QString& rotationVarIn) :
positionVar(positionVarIn),
rotationVar(rotationVarIn),
jointName(jointNameIn),
jointIndex(-1),
hasPerformedJointLookup(false) {}
rootIndex(-1) {}
std::string positionVar;
std::string rotationVar;
QString positionVar;
QString rotationVar;
QString jointName;
int jointIndex; // cached joint index
bool hasPerformedJointLookup = false;
};
struct IKTarget {
AnimPose pose;
int rootIndex;
int rootIndex; // cached root index
};
std::map<int, RotationConstraint*> _constraints;
std::vector<IKTargetVar> _targetVarVec;
std::map<int, IKTarget> _absoluteTargets; // IK targets of end-points
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
AnimPoseVec _relativePoses; // current relative poses

View file

@ -1,5 +1,5 @@
//
// AnimController.cpp
// AnimManipulator.cpp
//
// Created by Anthony J. Thibault on 9/8/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
@ -8,77 +8,81 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimController.h"
#include "AnimManipulator.h"
#include "AnimUtil.h"
#include "AnimationLogging.h"
AnimController::AnimController(const std::string& id, float alpha) :
AnimNode(AnimNode::Type::Controller, id),
AnimManipulator::AnimManipulator(const QString& id, float alpha) :
AnimNode(AnimNode::Type::Manipulator, id),
_alpha(alpha) {
}
AnimController::~AnimController() {
AnimManipulator::~AnimManipulator() {
}
const AnimPoseVec& AnimController::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses());
}
const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
_alpha = animVars.lookup(_alphaVar, _alpha);
for (auto& jointVar : _jointVars) {
if (!jointVar.hasPerformedJointLookup) {
QString qJointName = QString::fromStdString(jointVar.jointName);
jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName);
jointVar.jointIndex = _skeleton->nameToJointIndex(jointVar.jointName);
if (jointVar.jointIndex < 0) {
qCWarning(animation) << "AnimController could not find jointName" << qJointName << "in skeleton";
qCWarning(animation) << "AnimManipulator could not find jointName" << jointVar.jointName << "in skeleton";
}
jointVar.hasPerformedJointLookup = true;
}
if (jointVar.jointIndex >= 0) {
AnimPose defaultPose;
glm::quat absRot;
glm::quat parentAbsRot;
AnimPose defaultAbsPose;
AnimPose defaultRelPose;
AnimPose parentAbsPose = AnimPose::identity;
if (jointVar.jointIndex <= (int)underPoses.size()) {
// jointVar is an absolute rotation, if it is not set we will use the underPose as our default value
defaultPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses);
absRot = animVars.lookup(jointVar.var, defaultPose.rot);
defaultRelPose = underPoses[jointVar.jointIndex];
defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses);
defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot);
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose.
int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex);
if (parentIndex >= 0) {
parentAbsRot = _skeleton->getAbsolutePose(parentIndex, underPoses).rot;
parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses);
}
} else {
// jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value
defaultPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex);
absRot = animVars.lookup(jointVar.var, defaultPose.rot);
defaultRelPose = AnimPose::identity;
defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex);
defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot);
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose
// here we use the bind pose
int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex);
if (parentIndex >= 0) {
parentAbsRot = _skeleton->getAbsoluteBindPose(parentIndex).rot;
parentAbsPose = _skeleton->getAbsoluteBindPose(parentIndex);
}
}
// convert from absolute to relative
glm::quat relRot = glm::inverse(parentAbsRot) * absRot;
_poses[jointVar.jointIndex] = AnimPose(defaultPose.scale, relRot, defaultPose.trans);
AnimPose relPose = parentAbsPose.inverse() * defaultAbsPose;
// blend with underPose
::blend(1, &defaultRelPose, &relPose, _alpha, &_poses[jointVar.jointIndex]);
}
}
return _poses;
}
void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
void AnimManipulator::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
AnimNode::setSkeletonInternal(skeleton);
// invalidate all jointVar indices
@ -97,10 +101,10 @@ void AnimController::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
}
// for AnimDebugDraw rendering
const AnimPoseVec& AnimController::getPosesInternal() const {
const AnimPoseVec& AnimManipulator::getPosesInternal() const {
return _poses;
}
void AnimController::addJointVar(const JointVar& jointVar) {
void AnimManipulator::addJointVar(const JointVar& jointVar) {
_jointVars.push_back(jointVar);
}

View file

@ -1,5 +1,5 @@
//
// AnimController.h
// AnimManipulator.h
//
// Created by Anthony J. Thibault on 9/8/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
@ -8,31 +8,31 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AnimController_h
#define hifi_AnimController_h
#ifndef hifi_AnimManipulator_h
#define hifi_AnimManipulator_h
#include "AnimNode.h"
// Allows procedural control over a set of joints.
class AnimController : public AnimNode {
class AnimManipulator : public AnimNode {
public:
friend class AnimTests;
AnimController(const std::string& id, float alpha);
virtual ~AnimController() override;
AnimManipulator(const QString& id, float alpha);
virtual ~AnimManipulator() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
struct JointVar {
JointVar(const std::string& varIn, const std::string& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {}
std::string var = "";
std::string jointName = "";
JointVar(const QString& varIn, const QString& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {}
QString var = "";
QString jointName = "";
int jointIndex = -1;
bool hasPerformedJointLookup = false;
};
@ -45,14 +45,14 @@ protected:
AnimPoseVec _poses;
float _alpha;
std::string _alphaVar;
QString _alphaVar;
std::vector<JointVar> _jointVars;
// no copies
AnimController(const AnimController&) = delete;
AnimController& operator=(const AnimController&) = delete;
AnimManipulator(const AnimManipulator&) = delete;
AnimManipulator& operator=(const AnimManipulator&) = delete;
};
#endif // hifi_AnimController_h
#endif // hifi_AnimManipulator_h

View file

@ -40,22 +40,22 @@ public:
BlendLinear,
Overlay,
StateMachine,
Controller,
Manipulator,
InverseKinematics,
NumTypes
};
using Pointer = std::shared_ptr<AnimNode>;
using ConstPointer = std::shared_ptr<const AnimNode>;
using Triggers = std::vector<std::string>;
using Triggers = std::vector<QString>;
friend class AnimDebugDraw;
friend void buildChildMap(std::map<std::string, Pointer>& map, Pointer node);
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
friend class AnimStateMachine;
AnimNode(Type type, const std::string& id) : _type(type), _id(id) {}
AnimNode(Type type, const QString& id) : _type(type), _id(id) {}
virtual ~AnimNode() {}
const std::string& getID() const { return _id; }
const QString& getID() const { return _id; }
Type getType() const { return _type; }
// hierarchy accessors
@ -105,7 +105,7 @@ protected:
virtual const AnimPoseVec& getPosesInternal() const = 0;
Type _type;
std::string _id;
QString _id;
std::vector<AnimNode::Pointer> _children;
AnimSkeleton::ConstPointer _skeleton;

View file

@ -20,7 +20,7 @@
#include "AnimOverlay.h"
#include "AnimNodeLoader.h"
#include "AnimStateMachine.h"
#include "AnimController.h"
#include "AnimManipulator.h"
#include "AnimInverseKinematics.h"
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
@ -31,7 +31,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
// called after children have been loaded
@ -40,7 +40,7 @@ static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
static bool processControllerNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
static const char* animNodeTypeToString(AnimNode::Type type) {
@ -49,7 +49,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return "blendLinear";
case AnimNode::Type::Overlay: return "overlay";
case AnimNode::Type::StateMachine: return "stateMachine";
case AnimNode::Type::Controller: return "controller";
case AnimNode::Type::Manipulator: return "manipulator";
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
case AnimNode::Type::NumTypes: return nullptr;
};
@ -62,7 +62,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return loadBlendLinearNode;
case AnimNode::Type::Overlay: return loadOverlayNode;
case AnimNode::Type::StateMachine: return loadStateMachineNode;
case AnimNode::Type::Controller: return loadControllerNode;
case AnimNode::Type::Manipulator: return loadManipulatorNode;
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
case AnimNode::Type::NumTypes: return nullptr;
};
@ -75,7 +75,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
case AnimNode::Type::BlendLinear: return processBlendLinearNode;
case AnimNode::Type::Overlay: return processOverlayNode;
case AnimNode::Type::StateMachine: return processStateMachineNode;
case AnimNode::Type::Controller: return processControllerNode;
case AnimNode::Type::Manipulator: return processManipulatorNode;
case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode;
case AnimNode::Type::NumTypes: return nullptr;
};
@ -122,7 +122,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
static AnimNode::Type stringToEnum(const QString& str) {
// O(n), move to map when number of types becomes large.
const int NUM_TYPES = static_cast<int>(AnimNode::Type::NumTypes);
for (int i = 0; i < NUM_TYPES; i++ ) {
for (int i = 0; i < NUM_TYPES; i++) {
AnimNode::Type type = static_cast<AnimNode::Type>(i);
if (str == animNodeTypeToString(type)) {
return type;
@ -200,19 +200,19 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString&
READ_OPTIONAL_STRING(timeScaleVar, jsonObj);
READ_OPTIONAL_STRING(loopFlagVar, jsonObj);
auto node = std::make_shared<AnimClip>(id.toStdString(), url.toStdString(), startFrame, endFrame, timeScale, loopFlag);
auto node = std::make_shared<AnimClip>(id, url, startFrame, endFrame, timeScale, loopFlag);
if (!startFrameVar.isEmpty()) {
node->setStartFrameVar(startFrameVar.toStdString());
node->setStartFrameVar(startFrameVar);
}
if (!endFrameVar.isEmpty()) {
node->setEndFrameVar(endFrameVar.toStdString());
node->setEndFrameVar(endFrameVar);
}
if (!timeScaleVar.isEmpty()) {
node->setTimeScaleVar(timeScaleVar.toStdString());
node->setTimeScaleVar(timeScaleVar);
}
if (!loopFlagVar.isEmpty()) {
node->setLoopFlagVar(loopFlagVar.toStdString());
node->setLoopFlagVar(loopFlagVar);
}
return node;
@ -224,10 +224,10 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
READ_OPTIONAL_STRING(alphaVar, jsonObj);
auto node = std::make_shared<AnimBlendLinear>(id.toStdString(), alpha);
auto node = std::make_shared<AnimBlendLinear>(id, alpha);
if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar.toStdString());
node->setAlphaVar(alphaVar);
}
return node;
@ -271,31 +271,31 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri
READ_OPTIONAL_STRING(boneSetVar, jsonObj);
READ_OPTIONAL_STRING(alphaVar, jsonObj);
auto node = std::make_shared<AnimOverlay>(id.toStdString(), boneSetEnum, alpha);
auto node = std::make_shared<AnimOverlay>(id, boneSetEnum, alpha);
if (!boneSetVar.isEmpty()) {
node->setBoneSetVar(boneSetVar.toStdString());
node->setBoneSetVar(boneSetVar);
}
if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar.toStdString());
node->setAlphaVar(alphaVar);
}
return node;
}
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
auto node = std::make_shared<AnimStateMachine>(id.toStdString());
auto node = std::make_shared<AnimStateMachine>(id);
return node;
}
static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
auto node = std::make_shared<AnimController>(id.toStdString(), alpha);
auto node = std::make_shared<AnimManipulator>(id, alpha);
READ_OPTIONAL_STRING(alphaVar, jsonObj);
if (!alphaVar.isEmpty()) {
node->setAlphaVar(alphaVar.toStdString());
node->setAlphaVar(alphaVar);
}
auto jointsValue = jsonObj.value("joints");
@ -315,7 +315,7 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS
READ_STRING(var, jointObj, id, jsonUrl, nullptr);
READ_STRING(jointName, jointObj, id, jsonUrl, nullptr);
AnimController::JointVar jointVar(var.toStdString(), jointName.toStdString());
AnimManipulator::JointVar jointVar(var, jointName);
node->addJointVar(jointVar);
};
@ -323,7 +323,7 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS
}
AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
auto node = std::make_shared<AnimInverseKinematics>(id.toStdString());
auto node = std::make_shared<AnimInverseKinematics>(id);
auto targetsValue = jsonObj.value("targets");
if (!targetsValue.isArray()) {
@ -349,9 +349,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
return node;
}
void buildChildMap(std::map<std::string, AnimNode::Pointer>& map, AnimNode::Pointer node) {
void buildChildMap(std::map<QString, AnimNode::Pointer>& map, AnimNode::Pointer node) {
for ( auto child : node->_children ) {
map.insert(std::pair<std::string, AnimNode::Pointer>(child->_id, child));
map.insert(std::pair<QString, AnimNode::Pointer>(child->_id, child));
}
}
@ -368,15 +368,15 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
}
// build a map for all children by name.
std::map<std::string, AnimNode::Pointer> childMap;
std::map<QString, AnimNode::Pointer> childMap;
buildChildMap(childMap, node);
// first pass parse all the states and build up the state and transition map.
using StringPair = std::pair<std::string, std::string>;
using StringPair = std::pair<QString, QString>;
using TransitionMap = std::multimap<AnimStateMachine::State::Pointer, StringPair>;
TransitionMap transitionMap;
using StateMap = std::map<std::string, AnimStateMachine::State::Pointer>;
using StateMap = std::map<QString, AnimStateMachine::State::Pointer>;
StateMap stateMap;
auto statesArray = statesValue.toArray();
@ -394,22 +394,20 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
auto stdId = id.toStdString();
auto iter = childMap.find(stdId);
auto iter = childMap.find(id);
if (iter == childMap.end()) {
qCCritical(animation) << "AnimNodeLoader, could not find stateMachine child (state) with nodeId =" << nodeId << "stateId =" << id << "url =" << jsonUrl.toDisplayString();
return false;
}
auto statePtr = std::make_shared<AnimStateMachine::State>(stdId, iter->second, interpTarget, interpDuration);
auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration);
assert(statePtr);
if (!interpTargetVar.isEmpty()) {
statePtr->setInterpTargetVar(interpTargetVar.toStdString());
statePtr->setInterpTargetVar(interpTargetVar);
}
if (!interpDurationVar.isEmpty()) {
statePtr->setInterpDurationVar(interpDurationVar.toStdString());
statePtr->setInterpDurationVar(interpDurationVar);
}
smNode->addState(statePtr);
@ -432,7 +430,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
READ_STRING(var, transitionObj, nodeId, jsonUrl, false);
READ_STRING(state, transitionObj, nodeId, jsonUrl, false);
transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var.toStdString(), state.toStdString())));
transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var, state)));
}
}
@ -443,12 +441,12 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
if (iter != stateMap.end()) {
srcState->addTransition(AnimStateMachine::State::Transition(transition.second.first, iter->second));
} else {
qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id.c_str() << "dstState =" << transition.second.second.c_str() << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString();
qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id << "dstState =" << transition.second.second << "nodeId =" << nodeId << "url = " << jsonUrl.toDisplayString();
return false;
}
}
auto iter = stateMap.find(currentState.toStdString());
auto iter = stateMap.find(currentState);
if (iter == stateMap.end()) {
qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId << "url = " << jsonUrl.toDisplayString();
}

View file

@ -12,7 +12,7 @@
#include "AnimUtil.h"
#include <queue>
AnimOverlay::AnimOverlay(const std::string& id, BoneSet boneSet, float alpha) :
AnimOverlay::AnimOverlay(const QString& id, BoneSet boneSet, float alpha) :
AnimNode(AnimNode::Type::Overlay, id), _boneSet(boneSet), _alpha(alpha) {
}
@ -173,8 +173,8 @@ void AnimOverlay::buildEmptyBoneSet() {
void AnimOverlay::buildLeftHandBoneSet() {
assert(_skeleton);
buildEmptyBoneSet();
int headJoint = _skeleton->nameToJointIndex("LeftHand");
for_each_child_joint(_skeleton, headJoint, [&](int i) {
int handJoint = _skeleton->nameToJointIndex("LeftHand");
for_each_child_joint(_skeleton, handJoint, [&](int i) {
_boneSetVec[i] = 1.0f;
});
}
@ -182,8 +182,8 @@ void AnimOverlay::buildLeftHandBoneSet() {
void AnimOverlay::buildRightHandBoneSet() {
assert(_skeleton);
buildEmptyBoneSet();
int headJoint = _skeleton->nameToJointIndex("RightHand");
for_each_child_joint(_skeleton, headJoint, [&](int i) {
int handJoint = _skeleton->nameToJointIndex("RightHand");
for_each_child_joint(_skeleton, handJoint, [&](int i) {
_boneSetVec[i] = 1.0f;
});
}

View file

@ -40,13 +40,13 @@ public:
NumBoneSets
};
AnimOverlay(const std::string& id, BoneSet boneSet, float alpha);
AnimOverlay(const QString& id, BoneSet boneSet, float alpha);
virtual ~AnimOverlay() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setBoneSetVar(const std::string& boneSetVar) { _boneSetVar = boneSetVar; }
void setAlphaVar(const std::string& alphaVar) { _alphaVar = alphaVar; }
void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; }
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
protected:
void buildBoneSet(BoneSet boneSet);
@ -60,8 +60,8 @@ public:
float _alpha;
std::vector<float> _boneSetVec;
std::string _boneSetVar;
std::string _alphaVar;
QString _boneSetVar;
QString _alphaVar;
void buildFullBodyBoneSet();
void buildUpperBodyBoneSet();

View file

@ -12,7 +12,7 @@
#include "AnimUtil.h"
#include "AnimationLogging.h"
AnimStateMachine::AnimStateMachine(const std::string& id) :
AnimStateMachine::AnimStateMachine(const QString& id) :
AnimNode(AnimNode::Type::StateMachine, id) {
}
@ -23,7 +23,7 @@ AnimStateMachine::~AnimStateMachine() {
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
std::string desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
if (_currentState->getID() != desiredStateID) {
// switch states
bool foundState = false;
@ -35,7 +35,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl
}
}
if (!foundState) {
qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID.c_str() << ", referenced by _currentStateVar =" << _currentStateVar.c_str();
qCCritical(animation) << "AnimStateMachine could not find state =" << desiredStateID << ", referenced by _currentStateVar =" << _currentStateVar;
}
}
@ -77,7 +77,7 @@ void AnimStateMachine::addState(State::Pointer state) {
void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) {
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID().c_str() << "->" << desiredState->getID().c_str();
qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID();
const float FRAMES_PER_SECOND = 30.0f;

View file

@ -49,23 +49,23 @@ protected:
class Transition {
public:
friend AnimStateMachine;
Transition(const std::string& var, State::Pointer state) : _var(var), _state(state) {}
Transition(const QString& var, State::Pointer state) : _var(var), _state(state) {}
protected:
std::string _var;
QString _var;
State::Pointer _state;
};
State(const std::string& id, AnimNode::Pointer node, float interpTarget, float interpDuration) :
State(const QString& id, AnimNode::Pointer node, float interpTarget, float interpDuration) :
_id(id),
_node(node),
_interpTarget(interpTarget),
_interpDuration(interpDuration) {}
void setInterpTargetVar(const std::string& interpTargetVar) { _interpTargetVar = interpTargetVar; }
void setInterpDurationVar(const std::string& interpDurationVar) { _interpDurationVar = interpDurationVar; }
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
AnimNode::Pointer getNode() const { return _node; }
const std::string& getID() const { return _id; }
const QString& getID() const { return _id; }
protected:
@ -74,13 +74,13 @@ protected:
void addTransition(const Transition& transition) { _transitions.push_back(transition); }
std::string _id;
QString _id;
AnimNode::Pointer _node;
float _interpTarget; // frames
float _interpDuration; // frames
std::string _interpTargetVar;
std::string _interpDurationVar;
QString _interpTargetVar;
QString _interpDurationVar;
std::vector<Transition> _transitions;
@ -92,12 +92,12 @@ protected:
public:
AnimStateMachine(const std::string& id);
AnimStateMachine(const QString& id);
virtual ~AnimStateMachine() override;
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override;
void setCurrentStateVar(std::string& currentStateVar) { _currentStateVar = currentStateVar; }
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
protected:
@ -123,7 +123,7 @@ protected:
State::Pointer _currentState;
std::vector<State::Pointer> _states;
std::string _currentStateVar;
QString _currentStateVar;
private:
// no copies

View file

@ -37,7 +37,7 @@ public:
AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast<glm::vec3*>(&_val) = value; }
AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast<glm::quat*>(&_val) = value; }
AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast<glm::mat4*>(&_val) = value; }
AnimVariant(const std::string& value) : _type(Type::String) { _stringVal = value; }
AnimVariant(const QString& value) : _type(Type::String) { _stringVal = value; }
bool isBool() const { return _type == Type::Bool; }
bool isInt() const { return _type == Type::Int; }
@ -53,7 +53,7 @@ public:
void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast<glm::vec3*>(&_val) = value; }
void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast<glm::quat*>(&_val) = value; }
void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast<glm::mat4*>(&_val) = value; }
void setString(const std::string& value) { assert(_type == Type::String); _stringVal = value; }
void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
int getInt() const { assert(_type == Type::Int); return _val.intVal; }
@ -61,11 +61,11 @@ public:
const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
const std::string& getString() const { assert(_type == Type::String); return _stringVal; }
const QString& getString() const { assert(_type == Type::String); return _stringVal; }
protected:
Type _type;
std::string _stringVal;
QString _stringVal;
union {
bool boolVal;
int intVal;
@ -76,9 +76,9 @@ protected:
class AnimVariantMap {
public:
bool lookup(const std::string& key, bool defaultValue) const {
bool lookup(const QString& key, bool defaultValue) const {
// check triggers first, then map
if (key.empty()) {
if (key.isEmpty()) {
return defaultValue;
} else if (_triggers.find(key) != _triggers.end()) {
return true;
@ -88,8 +88,8 @@ public:
}
}
int lookup(const std::string& key, int defaultValue) const {
if (key.empty()) {
int lookup(const QString& key, int defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -97,8 +97,8 @@ public:
}
}
float lookup(const std::string& key, float defaultValue) const {
if (key.empty()) {
float lookup(const QString& key, float defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -106,8 +106,8 @@ public:
}
}
const glm::vec3& lookup(const std::string& key, const glm::vec3& defaultValue) const {
if (key.empty()) {
const glm::vec3& lookup(const QString& key, const glm::vec3& defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -115,8 +115,8 @@ public:
}
}
const glm::quat& lookup(const std::string& key, const glm::quat& defaultValue) const {
if (key.empty()) {
const glm::quat& lookup(const QString& key, const glm::quat& defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -124,8 +124,8 @@ public:
}
}
const glm::mat4& lookup(const std::string& key, const glm::mat4& defaultValue) const {
if (key.empty()) {
const glm::mat4& lookup(const QString& key, const glm::mat4& defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -133,8 +133,8 @@ public:
}
}
const std::string& lookup(const std::string& key, const std::string& defaultValue) const {
if (key.empty()) {
const QString& lookup(const QString& key, const QString& defaultValue) const {
if (key.isEmpty()) {
return defaultValue;
} else {
auto iter = _map.find(key);
@ -142,21 +142,23 @@ public:
}
}
void set(const std::string& key, bool value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, int value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, float value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const glm::vec3& value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); }
void unset(const std::string& key) { _map.erase(key); }
void set(const QString& key, bool value) { _map[key] = AnimVariant(value); }
void set(const QString& key, int value) { _map[key] = AnimVariant(value); }
void set(const QString& key, float value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const glm::mat4& value) { _map[key] = AnimVariant(value); }
void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); }
void unset(const QString& key) { _map.erase(key); }
void setTrigger(const std::string& key) { _triggers.insert(key); }
void setTrigger(const QString& key) { _triggers.insert(key); }
void clearTriggers() { _triggers.clear(); }
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
protected:
std::map<std::string, AnimVariant> _map;
std::set<std::string> _triggers;
std::map<QString, AnimVariant> _map;
std::set<QString> _triggers;
};
#endif // hifi_AnimVariant_h

View file

@ -735,21 +735,12 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
return;
}
if (freeLineage.isEmpty()) {
if (_enableAnimGraph && _animSkeleton) {
// the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit
return;
}
int numFree = freeLineage.size();
if (_enableAnimGraph && _animSkeleton) {
if (endIndex == _leftHandJointIndex) {
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
_animVars.set("leftHandPosition", targetPosition + rootTrans);
_animVars.set("leftHandRotation", targetRotation);
} else if (endIndex == _rightHandJointIndex) {
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
_animVars.set("rightHandPosition", targetPosition + rootTrans);
_animVars.set("rightHandRotation", targetRotation);
}
if (freeLineage.isEmpty()) {
return;
}
@ -768,6 +759,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
// relax toward default rotation
// NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax
int numFree = freeLineage.size();
for (int j = 0; j < numFree; j++) {
int nextIndex = freeLineage.at(j);
JointState& nextState = _jointStates[nextIndex];
@ -995,13 +987,9 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
_animVars.set("headRotation", realLocalHeadOrientation);
auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans;
if (params.isInHMD) {
_animVars.set("headPosition", params.localHeadPosition + rootTrans);
} else {
_animVars.unset("headPosition");
}
// There's a theory that when not in hmd, we should _animVars.unset("headPosition").
// However, until that works well, let's always request head be positioned where requested by hmd, camera, or default.
_animVars.set("headPosition", params.localHeadPosition);
} else if (!_enableAnimGraph) {
auto& state = _jointStates[index];
@ -1052,6 +1040,23 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
if (_enableAnimGraph && _animSkeleton) {
// TODO: figure out how to obtain the yFlip from where it is actually stored
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
if (params.isLeftEnabled) {
_animVars.set("leftHandPosition", yFlipHACK * params.leftPosition);
_animVars.set("leftHandRotation", yFlipHACK * params.leftOrientation);
} else {
_animVars.unset("leftHandPosition");
_animVars.unset("leftHandRotation");
}
if (params.isRightEnabled) {
_animVars.set("rightHandPosition", yFlipHACK * params.rightPosition);
_animVars.set("rightHandRotation", yFlipHACK * params.rightOrientation);
} else {
_animVars.unset("rightHandPosition");
_animVars.unset("rightHandRotation");
}
// set leftHand grab vars
_animVars.set("isLeftHandIdle", false);
_animVars.set("isLeftHandPoint", false);

View file

@ -239,7 +239,7 @@ inline void AudioFrameBuffer< T >::copyFrames(uint32_t channelCount, const uint3
if(typeid(T) == typeid(float32_t) &&
typeid(S) == typeid(int16_t)) { // source and destination aare not the same, convert from float32_t to int16_t and copy out
const int scale = (2 << ((8 * sizeof(S)) - 1));
const int scale = (1 << ((8 * sizeof(S)) - 1));
if (frameAlignment16 && (_channelCount == 1 || _channelCount == 2)) {
@ -382,7 +382,7 @@ inline void AudioFrameBuffer< T >::copyFrames(uint32_t channelCount, const uint3
if(typeid(T) == typeid(float32_t) &&
typeid(S) == typeid(int16_t)) { // source and destination aare not the same, convert from int16_t to float32_t and copy in
const int scale = (2 << ((8 * sizeof(S)) - 1));
const int scale = (1 << ((8 * sizeof(S)) - 1));
if (frameAlignment16 && (_channelCount == 1 || _channelCount == 2)) {

View file

@ -16,6 +16,7 @@
#include "openvr/OpenVrDisplayPlugin.h"
#include "oculus/OculusDisplayPlugin.h"
#include "oculus/OculusDebugDisplayPlugin.h"
#include "oculus/OculusLegacyDisplayPlugin.h"
const QString& DisplayPlugin::MENU_PATH() {
@ -42,6 +43,10 @@ DisplayPluginList getDisplayPlugins() {
// Windows Oculus SDK
new OculusDisplayPlugin(),
// Windows Oculus Simulator... uses head tracking and the same rendering
// as the connected hardware, but without using the SDK to display to the
// Rift. Useful for debugging Rift performance with nSight.
new OculusDebugDisplayPlugin(),
// Mac/Linux Oculus SDK (0.5)
new OculusLegacyDisplayPlugin(),
#ifdef Q_OS_WIN

View file

@ -0,0 +1,151 @@
//
// Created by Bradley Austin Davis on 2014/04/13.
// 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 "OculusBaseDisplayPlugin.h"
#include <ViewFrustum.h>
#include "OculusHelpers.h"
uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize;
}
void OculusBaseDisplayPlugin::preRender() {
#if (OVR_MAJOR_VERSION >= 6)
ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex);
_trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds);
ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses);
#endif
}
glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye];
}
void OculusBaseDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION >= 6)
ovr_RecenterPose(_hmd);
#endif
}
glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const {
return toGlm(_eyePoses[eye]);
}
glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const {
return toGlm(_trackingState.HeadPose.ThePose);
}
bool OculusBaseDisplayPlugin::isSupported() const {
#if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
return false;
}
bool result = false;
if (ovrHmd_Detect() > 0) {
result = true;
}
ovr_Shutdown();
return result;
#else
return false;
#endif
}
void OculusBaseDisplayPlugin::init() {
}
void OculusBaseDisplayPlugin::deinit() {
}
void OculusBaseDisplayPlugin::activate() {
#if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
qFatal("Could not init OVR");
}
#if (OVR_MAJOR_VERSION == 6)
if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) {
#elif (OVR_MAJOR_VERSION == 7)
if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) {
#endif
Q_ASSERT(false);
qFatal("Failed to acquire HMD");
}
_hmdDesc = ovr_GetHmdDesc(_hmd);
_ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
glm::uvec2 eyeSizes[2];
ovr_for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
});
ovrFovPort combined = _eyeFovs[Left];
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
_desiredFramebufferSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
// Parent class relies on our _hmd intialization, so it must come after that.
memset(&_sceneLayer, 0, sizeof(ovrLayerEyeFov));
_sceneLayer.Header.Type = ovrLayerType_EyeFov;
_sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
ovr_for_each_eye([&](ovrEyeType eye) {
ovrFovPort & fov = _sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
ovrSizei & size = _sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f);
_sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
});
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
#endif
WindowOpenGLDisplayPlugin::activate();
}
void OculusBaseDisplayPlugin::deactivate() {
WindowOpenGLDisplayPlugin::deactivate();
#if (OVR_MAJOR_VERSION >= 6)
ovr_Destroy(_hmd);
_hmd = nullptr;
ovr_Shutdown();
#endif
}
void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
++_frameIndex;
}

View file

@ -0,0 +1,79 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// 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
//
#pragma once
#include "../WindowOpenGLDisplayPlugin.h"
#include <QTimer>
#include <OVR_CAPI_GL.h>
class OculusBaseDisplayPlugin : public WindowOpenGLDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual void init() override final;
virtual void deinit() override final;
virtual void activate() override;
virtual void deactivate() override;
// Stereo specific methods
virtual bool isHmd() const override final { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::uvec2 getRecommendedRenderSize() const override final;
virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); }
virtual void resetSensors() override final;
virtual glm::mat4 getEyePose(Eye eye) const override final;
virtual glm::mat4 getHeadPose() const override final;
protected:
virtual void preRender() override final;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
protected:
ovrPosef _eyePoses[2];
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState;
unsigned int _frameIndex{ 0 };
#if (OVR_MAJOR_VERSION >= 6)
ovrHmd _hmd;
float _ipd{ OVR_DEFAULT_IPD };
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
ovrHmdDesc _hmdDesc;
ovrLayerEyeFov _sceneLayer;
#endif
#if (OVR_MAJOR_VERSION == 7)
ovrGraphicsLuid _luid;
#endif
};
#if (OVR_MAJOR_VERSION == 6)
#define ovr_Create ovrHmd_Create
#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL
#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL
#define ovr_Destroy ovrHmd_Destroy
#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet
#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture
#define ovr_GetFloat ovrHmd_GetFloat
#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize
#define ovr_GetFrameTiming ovrHmd_GetFrameTiming
#define ovr_GetTrackingState ovrHmd_GetTrackingState
#define ovr_GetRenderDesc ovrHmd_GetRenderDesc
#define ovr_RecenterPose ovrHmd_RecenterPose
#define ovr_SubmitFrame ovrHmd_SubmitFrame
#define ovr_ConfigureTracking ovrHmd_ConfigureTracking
#define ovr_GetHmdDesc(X) *X
#endif

View file

@ -0,0 +1,40 @@
//
// Created by Bradley Austin Davis on 2014/04/13.
// 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 "OculusDebugDisplayPlugin.h"
#include <QtCore/QProcessEnvironment>
const QString OculusDebugDisplayPlugin::NAME("Oculus Rift (Simulator)");
const QString & OculusDebugDisplayPlugin::getName() const {
return NAME;
}
static const QString DEBUG_FLAG("HIFI_DEBUG_OCULUS");
static bool enableDebugOculus = QProcessEnvironment::systemEnvironment().contains("HIFI_DEBUG_OCULUS");
bool OculusDebugDisplayPlugin::isSupported() const {
if (!enableDebugOculus) {
return false;
}
return OculusBaseDisplayPlugin::isSupported();
}
void OculusDebugDisplayPlugin::customizeContext() {
WindowOpenGLDisplayPlugin::customizeContext();
enableVsync(false);
}
void OculusDebugDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize);
OculusBaseDisplayPlugin::display(finalTexture, sceneSize);
}
void OculusDebugDisplayPlugin::finishFrame() {
swapBuffers();
doneCurrent();
};

View file

@ -0,0 +1,26 @@
//
// Created by Bradley Austin Davis on 2015/05/29
// 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
//
#pragma once
#include "OculusBaseDisplayPlugin.h"
class OculusDebugDisplayPlugin : public OculusBaseDisplayPlugin {
public:
virtual const QString & getName() const override;
virtual bool isSupported() const override;
protected:
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override;
// Do not perform swap in finish
virtual void finishFrame() override;
private:
static const QString NAME;
};

View file

@ -7,59 +7,10 @@
//
#include "OculusDisplayPlugin.h"
#include <memory>
#include <QMainWindow>
#include <QGLWidget>
#include <GLMHelpers.h>
#include <GlWindow.h>
#include <QEvent>
#include <QResizeEvent>
#include <QThread>
#include <OglplusHelpers.h>
#include <oglplus/opt/list_init.hpp>
#include <oglplus/shapes/vector.hpp>
#include <oglplus/opt/list_init.hpp>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdouble-promotion"
#endif
#include <oglplus/shapes/obj_mesh.hpp>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#include <PerfStat.h>
#include <plugins/PluginContainer.h>
#include <ViewFrustum.h>
#include "OculusHelpers.h"
#if (OVR_MAJOR_VERSION == 6)
#define ovr_Create ovrHmd_Create
#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL
#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL
#define ovr_Destroy ovrHmd_Destroy
#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet
#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture
#define ovr_GetFloat ovrHmd_GetFloat
#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize
#define ovr_GetFrameTiming ovrHmd_GetFrameTiming
#define ovr_GetTrackingState ovrHmd_GetTrackingState
#define ovr_GetRenderDesc ovrHmd_GetRenderDesc
#define ovr_RecenterPose ovrHmd_RecenterPose
#define ovr_SubmitFrame ovrHmd_SubmitFrame
#define ovr_ConfigureTracking ovrHmd_ConfigureTracking
#define ovr_GetHmdDesc(X) *X
#endif
#if (OVR_MAJOR_VERSION >= 6)
// A base class for FBO wrappers that need to use the Oculus C
@ -180,160 +131,21 @@ private:
const QString OculusDisplayPlugin::NAME("Oculus Rift");
uvec2 OculusDisplayPlugin::getRecommendedRenderSize() const {
return _desiredFramebufferSize;
}
void OculusDisplayPlugin::preRender() {
#if (OVR_MAJOR_VERSION >= 6)
ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex);
_trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds);
ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses);
#endif
}
glm::mat4 OculusDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
return _eyeProjections[eye];
}
void OculusDisplayPlugin::resetSensors() {
#if (OVR_MAJOR_VERSION >= 6)
ovr_RecenterPose(_hmd);
#endif
}
glm::mat4 OculusDisplayPlugin::getEyePose(Eye eye) const {
return toGlm(_eyePoses[eye]);
}
glm::mat4 OculusDisplayPlugin::getHeadPose() const {
return toGlm(_trackingState.HeadPose.ThePose);
}
const QString & OculusDisplayPlugin::getName() const {
return NAME;
}
bool OculusDisplayPlugin::isSupported() const {
#if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
return false;
}
bool result = false;
if (ovrHmd_Detect() > 0) {
result = true;
}
ovr_Shutdown();
return result;
#else
return false;
#endif
}
void OculusDisplayPlugin::init() {
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
qFatal("Could not init OVR");
}
}
void OculusDisplayPlugin::deinit() {
ovr_Shutdown();
}
#if (OVR_MAJOR_VERSION >= 6)
ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() {
return _sceneLayer;
}
#endif
//static gpu::TexturePointer _texture;
void OculusDisplayPlugin::activate() {
#if (OVR_MAJOR_VERSION >= 6)
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
Q_ASSERT(false);
qFatal("Failed to Initialize SDK");
}
// CONTAINER->getPrimarySurface()->makeCurrent();
#if (OVR_MAJOR_VERSION == 6)
if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) {
#elif (OVR_MAJOR_VERSION == 7)
if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) {
#endif
Q_ASSERT(false);
qFatal("Failed to acquire HMD");
}
_hmdDesc = ovr_GetHmdDesc(_hmd);
_ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
glm::uvec2 eyeSizes[2];
ovr_for_each_eye([&](ovrEyeType eye) {
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
ovrPerspectiveProjection =
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
});
ovrFovPort combined = _eyeFovs[Left];
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
ovrMatrix4f ovrPerspectiveProjection =
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
_desiredFramebufferSize = uvec2(
eyeSizes[0].x + eyeSizes[1].x,
std::max(eyeSizes[0].y, eyeSizes[1].y));
_frameIndex = 0;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
WindowOpenGLDisplayPlugin::activate();
// Parent class relies on our _hmd intialization, so it must come after that.
ovrLayerEyeFov& sceneLayer = getSceneLayer();
memset(&sceneLayer, 0, sizeof(ovrLayerEyeFov));
sceneLayer.Header.Type = ovrLayerType_EyeFov;
sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
ovr_for_each_eye([&](ovrEyeType eye) {
ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f);
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
});
// We're rendering both eyes to the same texture, so only one of the
// pointers is populated
sceneLayer.ColorTexture[0] = _sceneFbo->color;
// not needed since the structure was zeroed on init, but explicit
sceneLayer.ColorTexture[1] = nullptr;
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
qFatal("Could not attach to sensor device");
}
#endif
}
void OculusDisplayPlugin::customizeContext() {
WindowOpenGLDisplayPlugin::customizeContext();
#if (OVR_MAJOR_VERSION >= 6)
_sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd));
_sceneFbo->Init(getRecommendedRenderSize());
// We're rendering both eyes to the same texture, so only one of the
// pointers is populated
_sceneLayer.ColorTexture[0] = _sceneFbo->color;
// not needed since the structure was zeroed on init, but explicit
_sceneLayer.ColorTexture[1] = nullptr;
#endif
enableVsync(false);
// Only enable mirroring if we know vsync is disabled
@ -345,13 +157,9 @@ void OculusDisplayPlugin::deactivate() {
makeCurrent();
_sceneFbo.reset();
doneCurrent();
WindowOpenGLDisplayPlugin::deactivate();
ovr_Destroy(_hmd);
_hmd = nullptr;
ovr_Shutdown();
#endif
OculusBaseDisplayPlugin::deactivate();
}
void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
@ -379,9 +187,8 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
drawUnitQuad();
});
ovrLayerEyeFov& sceneLayer = getSceneLayer();
ovr_for_each_eye([&](ovrEyeType eye) {
sceneLayer.RenderPose[eye] = _eyePoses[eye];
_sceneLayer.RenderPose[eye] = _eyePoses[eye];
});
auto windowSize = toGlm(_window->size());
@ -391,7 +198,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1];
ovrLayerHeader* layers = &sceneLayer.Header;
ovrLayerHeader* layers = &_sceneLayer.Header;
ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1);
if (!OVR_SUCCESS(result)) {
qDebug() << result;
@ -403,11 +210,6 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi
#endif
}
// Pass input events on to the application
bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
}
/*
The swapbuffer call here is only required if we want to mirror the content to the screen.
However, it should only be done if we can reliably disable v-sync on the mirror surface,
@ -419,36 +221,3 @@ void OculusDisplayPlugin::finishFrame() {
}
doneCurrent();
};
#if 0
/*
An alternative way to render the UI is to pass it specifically as a composition layer to
the Oculus SDK which should technically result in higher quality. However, the SDK doesn't
have a mechanism to present the image as a sphere section, which is our desired look.
*/
ovrLayerQuad& uiLayer = getUiLayer();
if (nullptr == uiLayer.ColorTexture || overlaySize != _uiFbo->size) {
_uiFbo->Resize(overlaySize);
uiLayer.ColorTexture = _uiFbo->color;
uiLayer.Viewport.Size.w = overlaySize.x;
uiLayer.Viewport.Size.h = overlaySize.y;
float overlayAspect = aspect(overlaySize);
uiLayer.QuadSize.x = 1.0f;
uiLayer.QuadSize.y = 1.0f / overlayAspect;
}
_uiFbo->Bound([&] {
Q_ASSERT(0 == glGetError());
using namespace oglplus;
Context::Viewport(_uiFbo->size.x, _uiFbo->size.y);
glClearColor(0, 0, 0, 0);
Context::Clear().ColorBuffer();
_program->Bind();
glBindTexture(GL_TEXTURE_2D, overlayTexture);
_plane->Use();
_plane->Draw();
Q_ASSERT(0 == glGetError());
});
#endif

View file

@ -7,43 +7,17 @@
//
#pragma once
#include "../WindowOpenGLDisplayPlugin.h"
#include "OculusBaseDisplayPlugin.h"
#include <QTimer>
#include <OVR_CAPI.h>
class OffscreenGlCanvas;
struct SwapFramebufferWrapper;
struct MirrorFramebufferWrapper;
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
using MirrorFboPtr = QSharedPointer<MirrorFramebufferWrapper>;
class OculusDisplayPlugin : public WindowOpenGLDisplayPlugin {
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
public:
virtual bool isSupported() const override;
virtual void deactivate() override;
virtual const QString & getName() const override;
virtual void init() override;
virtual void deinit() override;
virtual void activate() override;
virtual void deactivate() override;
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
// Stereo specific methods
virtual bool isHmd() const override { return true; }
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
virtual glm::uvec2 getRecommendedRenderSize() const override;
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
virtual void resetSensors() override;
virtual glm::mat4 getEyePose(Eye eye) const override;
virtual glm::mat4 getHeadPose() const override;
protected:
virtual void preRender() override;
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
virtual void customizeContext() override;
// Do not perform swap in finish
@ -51,30 +25,10 @@ protected:
private:
static const QString NAME;
ovrPosef _eyePoses[2];
mat4 _eyeProjections[3];
mat4 _compositeEyeProjections[2];
uvec2 _desiredFramebufferSize;
ovrTrackingState _trackingState;
bool _enableMirror{ false };
#if (OVR_MAJOR_VERSION >= 6)
ovrHmd _hmd;
float _ipd{ OVR_DEFAULT_IPD };
unsigned int _frameIndex;
ovrEyeRenderDesc _eyeRenderDescs[2];
ovrVector3f _eyeOffsets[2];
ovrFovPort _eyeFovs[2];
ovrLayerEyeFov& getSceneLayer();
ovrHmdDesc _hmdDesc;
SwapFboPtr _sceneFbo;
ovrLayerEyeFov _sceneLayer;
#endif
#if (OVR_MAJOR_VERSION == 7)
ovrGraphicsLuid _luid;
#endif
};

View file

@ -161,7 +161,7 @@ void OpenVrDisplayPlugin::resetSensors() {
}
glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const {
return _eyesData[eye]._eyeOffset * getHeadPose();
return getHeadPose() * _eyesData[eye]._eyeOffset;
}
glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {

View file

@ -93,6 +93,15 @@ void EntityTreeRenderer::clear() {
OctreeRenderer::clear();
}
void EntityTreeRenderer::reloadEntityScripts() {
_entitiesScriptEngine->unloadAllEntityScripts();
foreach(auto entity, _entitiesInScene) {
if (!entity->getScript().isEmpty()) {
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true);
}
}
}
void EntityTreeRenderer::init() {
OctreeRenderer::init();
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);

View file

@ -62,6 +62,9 @@ public:
/// clears the tree
virtual void clear();
/// reloads the entity scripts, calling unload and preload
void reloadEntityScripts();
/// if a renderable entity item needs a model, we will allocate it for them
Q_INVOKABLE Model* allocateModel(const QString& url, const QString& collisionUrl);

View file

@ -1,6 +1,6 @@
//
// EntityScriptingInterface.h
// libraries/models/src
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.

View file

@ -160,15 +160,22 @@ typedef std::vector< BufferPointer > Buffers;
class BufferView {
protected:
void initFromBuffer(const BufferPointer& buffer) {
_buffer = (buffer);
if (_buffer) {
_size = (buffer->getSize());
}
}
public:
typedef Resource::Size Size;
typedef int Index;
BufferPointer _buffer;
Size _offset;
Size _size;
Size _offset{ 0 };
Size _size{ 0 };
Element _element;
uint16 _stride;
uint16 _stride{ 1 };
BufferView() :
_buffer(NULL),
@ -188,19 +195,19 @@ public:
// create the BufferView and own the Buffer
BufferView(Buffer* newBuffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
_buffer(newBuffer),
_offset(0),
_size(newBuffer->getSize()),
_element(element),
_stride(uint16(element.getSize()))
{};
{
initFromBuffer(BufferPointer(newBuffer));
};
BufferView(const BufferPointer& buffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
_buffer(buffer),
_offset(0),
_size(buffer->getSize()),
_element(element),
_stride(uint16(element.getSize()))
{};
{
initFromBuffer(buffer);
};
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
_buffer(buffer),
_offset(offset),

View file

@ -11,16 +11,21 @@
#include "AssetClient.h"
#include <QBuffer>
#include <QThread>
#include <cstdint>
#include <QtCore/QBuffer>
#include <QtCore/QStandardPaths>
#include <QtCore/QThread>
#include <QtNetwork/QNetworkDiskCache>
#include "AssetRequest.h"
#include "AssetUpload.h"
#include "AssetUtils.h"
#include "NetworkAccessManager.h"
#include "NetworkLogging.h"
#include "NodeList.h"
#include "PacketReceiver.h"
#include "AssetUtils.h"
#include "ResourceCache.h"
MessageID AssetClient::_currentID = 0;
@ -40,9 +45,29 @@ AssetClient::AssetClient() {
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled);
}
void AssetClient::init() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection);
}
// Setup disk cache if not already
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
if (!networkAccessManager.cache()) {
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache";
QNetworkDiskCache* cache = new QNetworkDiskCache();
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(cachePath);
networkAccessManager.setCache(cache);
qCDebug(asset_client) << "AssetClient disk cache setup at" << cachePath
<< "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)";
}
}
AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) {
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
qDebug() << "Invalid hash size";
qCWarning(asset_client) << "Invalid hash size";
return nullptr;
}
@ -50,7 +75,7 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (!assetServer) {
qDebug().nospace() << "Could not request " << hash << "." << extension
qCWarning(asset_client).nospace() << "Could not request " << hash << "." << extension
<< " since you are not currently connected to an asset-server.";
return nullptr;
}
@ -68,7 +93,7 @@ AssetUpload* AssetClient::createUpload(const QString& filename) {
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (!assetServer) {
qDebug() << "Could not upload" << filename << "since you are not currently connected to an asset-server.";
qCWarning(asset_client) << "Could not upload" << filename << "since you are not currently connected to an asset-server.";
return nullptr;
}
@ -82,7 +107,7 @@ AssetUpload* AssetClient::createUpload(const QString& filename) {
bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end,
ReceivedAssetCallback callback) {
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
qDebug() << "Invalid hash size";
qCWarning(asset_client) << "Invalid hash size";
return false;
}
@ -97,7 +122,7 @@ bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOf
+ sizeof(start) + sizeof(end);
auto packet = NLPacket::create(PacketType::AssetGet, payloadSize, true);
qDebug() << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
qCDebug(asset_client) << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server.";
packet->writePrimitive(messageID);
@ -184,7 +209,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, S
packet.open(QIODevice::ReadOnly);
auto assetHash = packet.read(SHA256_HASH_LENGTH);
qDebug() << "Got reply for asset: " << assetHash.toHex();
qCDebug(asset_client) << "Got reply for asset: " << assetHash.toHex();
MessageID messageID;
packet.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
@ -198,7 +223,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, S
packet.read(reinterpret_cast<char*>(&length), sizeof(DataOffset));
data = packet.read(length);
} else {
qDebug() << "Failure getting asset: " << error;
qCWarning(asset_client) << "Failure getting asset: " << error;
}
// Check if we have any pending requests for this node
@ -254,15 +279,15 @@ void AssetClient::handleAssetUploadReply(QSharedPointer<NLPacket> packet, Shared
AssetServerError error;
packet->readPrimitive(&error);
QString hashString { "" };
QString hashString;
if (error) {
qDebug() << "Error uploading file to asset server";
qCWarning(asset_client) << "Error uploading file to asset server";
} else {
auto hash = packet->read(SHA256_HASH_LENGTH);
hashString = hash.toHex();
qDebug() << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString;
qCDebug(asset_client) << "Successfully uploaded asset to asset-server - SHA256 hash is " << hashString;
}
// Check if we have any pending requests for this node

View file

@ -41,6 +41,8 @@ class AssetClient : public QObject, public Dependency {
public:
AssetClient();
Q_INVOKABLE void init();
Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension);
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);

View file

@ -13,18 +13,20 @@
#include <algorithm>
#include <QThread>
#include <QtCore/QThread>
#include <QtNetwork/QAbstractNetworkCache>
#include "AssetClient.h"
#include "NetworkAccessManager.h"
#include "NetworkLogging.h"
#include "NodeList.h"
#include "ResourceCache.h"
AssetRequest::AssetRequest(const QString& hash, const QString& extension) :
QObject(),
_hash(hash),
_extension(extension)
{
}
void AssetRequest::start() {
@ -34,7 +36,19 @@ void AssetRequest::start() {
}
if (_state != NotStarted) {
qCWarning(networking) << "AssetRequest already started.";
qCWarning(asset_client) << "AssetRequest already started.";
return;
}
// Try to load from cache
if (loadFromCache()) {
_info.hash = _hash;
_info.size = _data.size();
_error = NoError;
_state = Finished;
emit finished(this);
qCDebug(asset_client) << getUrl().toDisplayString() << "loaded from disk cache.";
return;
}
@ -58,8 +72,7 @@ void AssetRequest::start() {
}
if (_error != NoError) {
qCDebug(networking) << "Got error retrieving asset info for" << _hash;
qCWarning(asset_client) << "Got error retrieving asset info for" << _hash;
_state = Finished;
emit finished(this);
@ -69,7 +82,7 @@ void AssetRequest::start() {
_state = WaitingForData;
_data.resize(info.size);
qCDebug(networking) << "Got size of " << _hash << " : " << info.size << " bytes";
qCDebug(asset_client) << "Got size of " << _hash << " : " << info.size << " bytes";
int start = 0, end = _info.size;
@ -98,6 +111,10 @@ void AssetRequest::start() {
memcpy(_data.data() + start, data.constData(), data.size());
_totalReceived += data.size();
emit progress(_totalReceived, _info.size);
if (saveToCache(data)) {
qCDebug(asset_client) << getUrl().toDisplayString() << "saved to disk cache";
}
} else {
// hash doesn't match - we have an error
_error = HashVerificationFailed;
@ -106,7 +123,7 @@ void AssetRequest::start() {
}
if (_error != NoError) {
qCDebug(networking) << "Got error retrieving asset" << _hash << "- error code" << _error;
qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error;
}
_state = Finished;
@ -114,3 +131,51 @@ void AssetRequest::start() {
});
});
}
QUrl AssetRequest::getUrl() const {
if (!_extension.isEmpty()) {
return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, _hash, _extension));
} else {
return QUrl(QString("%1:%2").arg(URL_SCHEME_ATP, _hash));
}
}
bool AssetRequest::loadFromCache() {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
auto url = getUrl();
if (auto ioDevice = cache->data(url)) {
_data = ioDevice->readAll();
return true;
} else {
qCDebug(asset_client) << url.toDisplayString() << "not in disk cache";
}
} else {
qCWarning(asset_client) << "No disk cache to load assets from.";
}
return false;
}
bool AssetRequest::saveToCache(const QByteArray& file) const {
if (auto cache = NetworkAccessManager::getInstance().cache()) {
auto url = getUrl();
if (!cache->metaData(url).isValid()) {
QNetworkCacheMetaData metaData;
metaData.setUrl(url);
metaData.setSaveToDisk(true);
metaData.setLastModified(QDateTime::currentDateTime());
metaData.setExpirationDate(QDateTime()); // Never expires
if (auto ioDevice = cache->prepare(metaData)) {
ioDevice->write(file);
cache->insert(ioDevice);
return true;
}
qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache.";
}
} else {
qCWarning(asset_client) << "No disk cache to save assets to.";
}
return false;
}

View file

@ -46,12 +46,16 @@ public:
const QByteArray& getData() const { return _data; }
const State& getState() const { return _state; }
const Error& getError() const { return _error; }
QUrl getUrl() const;
signals:
void finished(AssetRequest* thisRequest);
void progress(qint64 totalReceived, qint64 total);
private:
bool loadFromCache();
bool saveToCache(const QByteArray& file) const;
State _state = NotStarted;
Error _error = NoError;
AssetInfo _info;

View file

@ -32,7 +32,6 @@ void AssetResourceRequest::doSend() {
_state = Finished;
emit finished();
return;
}
@ -43,12 +42,11 @@ void AssetResourceRequest::doSend() {
_state = Finished;
emit finished();
return;
}
connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress);
QObject::connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) mutable {
connect(_assetRequest, &AssetRequest::finished, [this](AssetRequest* req) {
Q_ASSERT(_state == InProgress);
Q_ASSERT(req == _assetRequest);
Q_ASSERT(req->getState() == AssetRequest::Finished);

View file

@ -15,6 +15,7 @@
#include <QtCore/QThread>
#include "AssetClient.h"
#include "NetworkLogging.h"
AssetUpload::AssetUpload(QObject* object, const QString& filename) :
_filename(filename)
@ -41,7 +42,7 @@ void AssetUpload::start() {
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback
auto assetClient = DependencyManager::get<AssetClient>();
qDebug() << "Attempting to upload" << _filename << "to asset-server.";
qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server.";
assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){
if (!responseReceived) {

View file

@ -29,8 +29,7 @@ HTTPResourceRequest::~HTTPResourceRequest() {
}
void HTTPResourceRequest::doSend() {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(_url);
QNetworkRequest networkRequest(_url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
if (_cacheEnabled) {
@ -39,7 +38,7 @@ void HTTPResourceRequest::doSend() {
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
}
_reply = networkAccessManager.get(networkRequest);
_reply = NetworkAccessManager::getInstance().get(networkRequest);
connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished);
connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);

View file

@ -18,6 +18,7 @@ QThreadStorage<QNetworkAccessManager*> networkAccessManagers;
QNetworkAccessManager& NetworkAccessManager::getInstance() {
if (!networkAccessManagers.hasLocalData()) {
networkAccessManagers.setLocalData(new QNetworkAccessManager());
}
return *networkAccessManagers.localData();

View file

@ -12,7 +12,7 @@
#ifndef hifi_NetworkAccessManager_h
#define hifi_NetworkAccessManager_h
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/QNetworkAccessManager>
/// Wrapper around QNetworkAccessManager to restrict at one instance by thread
class NetworkAccessManager : public QObject {

View file

@ -12,3 +12,4 @@
#include "NetworkLogging.h"
Q_LOGGING_CATEGORY(networking, "hifi.networking")
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")

View file

@ -15,5 +15,6 @@
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(networking)
Q_DECLARE_LOGGING_CATEGORY(asset_client)
#endif // hifi_NetworkLogging_h

View file

@ -12,8 +12,6 @@
#include <cfloat>
#include <cmath>
#include <QDebug>
#include <QNetworkDiskCache>
#include <QThread>
#include <QTimer>

View file

@ -35,6 +35,7 @@ class Resource;
static const qint64 BYTES_PER_MEGABYTES = 1024 * 1024;
static const qint64 BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES;
static const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES; // 10GB
// Windows can have troubles allocating that much memory in ram sometimes
// so default cache size at 100 MB on windows (1GB otherwise)

View file

@ -43,7 +43,9 @@ void ThreadedAssignment::setFinished(bool isFinished) {
packetReceiver.setShouldDropPackets(true);
if (_domainServerTimer) {
_domainServerTimer->stop();
// stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread
_domainServerTimer->deleteLater();
_domainServerTimer = nullptr;
}
if (_statsTimer) {
@ -65,10 +67,13 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy
auto nodeList = DependencyManager::get<NodeList>();
nodeList->setOwnerType(nodeType);
_domainServerTimer = new QTimer(this);
_domainServerTimer = new QTimer;
connect(_domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
_domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// move the domain server time to the NL so check-ins fire from there
_domainServerTimer->moveToThread(nodeList->thread());
if (shouldSendStats) {
// start sending stats packet once we connect to the domain
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &ThreadedAssignment::startSendingStats);

View file

@ -56,14 +56,15 @@ void Connection::stopSendQueue() {
// grab the send queue thread so we can wait on it
QThread* sendQueueThread = _sendQueue->thread();
// since we're stopping the send queue we should consider our handshake ACK not receieved
_hasReceivedHandshakeACK = false;
// tell the send queue to stop and be deleted
_sendQueue->stop();
_sendQueue->deleteLater();
_sendQueue.release();
// since we're stopping the send queue we should consider our handshake ACK not receieved
_hasReceivedHandshakeACK = false;
// wait on the send queue thread so we know the send queue is gone
sendQueueThread->quit();
sendQueueThread->wait();
@ -111,7 +112,7 @@ void Connection::queueInactive() {
qCDebug(networking) << "Connection SendQueue to" << _destination << "stopped and no data is being received - stopping connection.";
#endif
emit connectionInactive(_destination);
deactivate();
}
}
@ -170,7 +171,9 @@ void Connection::sync() {
qCDebug(networking) << "Connection to" << _destination << "no longer receiving any data and there is currently no send queue - stopping connection.";
#endif
emit connectionInactive(_destination);
deactivate();
return;
}
}
@ -207,7 +210,9 @@ void Connection::sync() {
<< CONNECTION_NOT_USED_EXPIRY_SECONDS << "seconds - stopping connection.";
#endif
emit connectionInactive(_destination);
deactivate();
return;
}
}
}
@ -728,11 +733,14 @@ void Connection::processHandshake(std::unique_ptr<ControlPacket> controlPacket)
}
void Connection::processHandshakeACK(std::unique_ptr<ControlPacket> controlPacket) {
// if we've decided to clean up the send queue then this handshake ACK should be ignored, it's useless
if (_sendQueue) {
// hand off this handshake ACK to the send queue so it knows it can start sending
getSendQueue().handshakeACK();
// indicate that handshake ACK was received
_hasReceivedHandshakeACK = true;
}
}
void Connection::processTimeoutNAK(std::unique_ptr<ControlPacket> controlPacket) {

View file

@ -72,6 +72,8 @@ public:
ConnectionStats::Stats sampleStats() { return _stats.sample(); }
bool isActive() const { return _isActive; }
signals:
void packetSent();
void connectionInactive(const HifiSockAddr& sockAddr);
@ -100,6 +102,8 @@ private:
void resetReceiveState();
void resetRTT();
void deactivate() { _isActive = false; emit connectionInactive(_destination); }
SendQueue& getSendQueue();
SequenceNumber nextACK() const;
void updateRTT(int rtt);
@ -123,6 +127,7 @@ private:
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
bool _isActive { true }; // flag used for inactivity of connection
LossList _lossList; // List of all missing packets
SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer

View file

@ -65,6 +65,7 @@ std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destin
// Move queue to private thread and start it
queue->moveToThread(thread);
thread->start();
return std::move(queue);
@ -89,7 +90,8 @@ void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets
_emptyCondition.notify_one();
}
if (!this->thread()->isRunning()) {
if (!this->thread()->isRunning() && _state == State::NotStarted) {
this->thread()->start();
}
}
@ -136,13 +138,14 @@ void SendQueue::queuePacketList(std::unique_ptr<PacketList> packetList) {
_emptyCondition.notify_one();
}
if (!this->thread()->isRunning()) {
if (!this->thread()->isRunning() && _state == State::NotStarted) {
this->thread()->start();
}
}
void SendQueue::stop() {
_isRunning = false;
_state = State::Stopped;
// in case we're waiting to send another handshake, release the condition_variable now so we cleanup sooner
_handshakeACKCondition.notify_one();
@ -268,9 +271,23 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
}
void SendQueue::run() {
_isRunning = true;
if (_state == State::Stopped) {
// we've already been asked to stop before we even got a chance to start
// don't start now
#ifdef UDT_CONNECTION_DEBUG
qDebug() << "SendQueue asked to run after being told to stop. Will not run.";
#endif
return;
} else if (_state == State::Running) {
#ifdef UDT_CONNECTION_DEBUG
qDebug() << "SendQueue asked to run but is already running (according to state). Will not re-run.";
#endif
return;
}
while (_isRunning) {
_state = State::Running;
while (_state == State::Running) {
// Record how long the loop takes to execute
auto loopStartTimestamp = p_high_resolution_clock::now();
@ -314,11 +331,11 @@ void SendQueue::run() {
}
// since we're a while loop, give the thread a chance to process events
QCoreApplication::processEvents();
QCoreApplication::sendPostedEvents(this, 0);
// we just processed events so check now if we were just told to stop
if (!_isRunning) {
break;
if (_state != State::Running) {
return;
}
if (_hasReceivedHandshakeACK && !sentAPacket) {
@ -525,5 +542,5 @@ void SendQueue::deactivate() {
// this queue is inactive - emit that signal and stop the while
emit queueInactive();
_isRunning = false;
_state = State::Stopped;
}

View file

@ -45,6 +45,12 @@ class SendQueue : public QObject {
Q_OBJECT
public:
enum class State {
NotStarted,
Running,
Stopped
};
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);
void queuePacket(std::unique_ptr<Packet> packet);
@ -106,7 +112,7 @@ private:
std::atomic<uint32_t> _atomicCurrentSequenceNumber { 0 };// Atomic for last sequence number sent out
std::atomic<int> _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC
std::atomic<bool> _isRunning { false };
std::atomic<State> _state { State::NotStarted };
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
std::atomic<int> _timeoutExpiryCount { 0 }; // The number of times the timeout has expired without response from client

View file

@ -183,8 +183,7 @@ Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, _ccFactory->create()));
// we queue the connection to cleanup connection in case it asks for it during its own rate control sync
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection,
Qt::QueuedConnection);
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection);
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Creating new connection to" << sockAddr;
@ -208,11 +207,13 @@ void Socket::clearConnections() {
}
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
auto numErased = _connectionsHash.erase(sockAddr);
if (numErased > 0) {
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Socket::cleanupConnection called for UDT connection to" << sockAddr;
#endif
_connectionsHash.erase(sockAddr);
}
}
void Socket::messageReceived(std::unique_ptr<PacketList> packetList) {
@ -297,8 +298,31 @@ void Socket::connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* r
void Socket::rateControlSync() {
// enumerate our list of connections and ask each of them to send off periodic ACK packet for rate control
// the way we do this is a little funny looking - we need to avoid the case where we call sync and
// (because of our Qt direct connection to the Connection's signal that it has been deactivated)
// an iterator on _connectionsHash would be invalidated by our own call to cleanupConnection
// collect the sockets for all connections in a vector
std::vector<HifiSockAddr> sockAddrVector;
sockAddrVector.reserve(_connectionsHash.size());
for (auto& connection : _connectionsHash) {
connection.second->sync();
sockAddrVector.emplace_back(connection.first);
}
// enumerate that vector of HifiSockAddr objects
for (auto& sockAddr : sockAddrVector) {
// pull out the respective connection via a quick find on the unordered_map
auto it = _connectionsHash.find(sockAddr);
if (it != _connectionsHash.end()) {
// if the connection is erased while calling sync since we are re-using the iterator that was invalidated
// we're good to go
auto& connection = _connectionsHash[sockAddr];
connection->sync();
}
}
if (_synTimer->interval() != _synInterval) {

View file

@ -23,9 +23,6 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() :
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
}
OctreeHeadlessViewer::~OctreeHeadlessViewer() {
}
void OctreeHeadlessViewer::init() {
OctreeRenderer::init();
setViewFrustum(&_viewFrustum);
@ -34,6 +31,7 @@ void OctreeHeadlessViewer::init() {
void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();
bool wantExtraDebugging = false;

View file

@ -29,7 +29,7 @@ class OctreeHeadlessViewer : public OctreeRenderer {
Q_OBJECT
public:
OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer();
virtual ~OctreeHeadlessViewer() {};
virtual void renderElement(OctreeElementPointer element, RenderArgs* args) { /* swallow these */ }
virtual void init();
@ -65,7 +65,7 @@ public slots:
private:
ViewFrustum _viewFrustum;
JurisdictionListener* _jurisdictionListener;
JurisdictionListener* _jurisdictionListener = nullptr;
OctreeQuery _octreeQuery;
float _voxelSizeScale;
int _boundaryLevelAdjust;

View file

@ -54,6 +54,7 @@ void OctreeScriptingInterface::init() {
if (_initialized) {
return;
}
if (_jurisdictionListener) {
_managedJurisdictionListener = false;
} else {

View file

@ -21,8 +21,7 @@
class OctreeScriptingInterface : public QObject {
Q_OBJECT
public:
OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL,
JurisdictionListener* jurisdictionListener = NULL);
OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, JurisdictionListener* jurisdictionListener = NULL);
~OctreeScriptingInterface();
@ -86,8 +85,8 @@ public slots:
protected:
/// attached OctreeEditPacketSender that handles queuing and sending of packets to VS
OctreeEditPacketSender* _packetSender;
JurisdictionListener* _jurisdictionListener;
OctreeEditPacketSender* _packetSender = nullptr;
JurisdictionListener* _jurisdictionListener = nullptr;
bool _managedPacketSender;
bool _managedJurisdictionListener;
bool _initialized;

View file

@ -373,7 +373,10 @@ void DeferredLightingEffect::render(RenderArgs* args) {
projMats[0] = monoProjMat;
deferredTransforms[0].projection = monoProjMat;
deferredTransforms[0].viewInverse = monoViewMat;
viewTransforms[0] = monoViewTransform;
deferredTransforms[0].stereoSide = 0.0f;
clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight);
@ -579,6 +582,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
batch.setResourceTexture(1, nullptr);
batch.setResourceTexture(2, nullptr);
batch.setResourceTexture(3, nullptr);
batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr);
args->_context->render(batch);

View file

@ -27,6 +27,10 @@ ScriptCache::ScriptCache(QObject* parent) {
// nothing to do here...
}
void ScriptCache::clearCache() {
_scriptCache.clear();
}
QString ScriptCache::getScript(const QUrl& unnormalizedURL, ScriptUser* scriptUser, bool& isPending, bool reload) {
QUrl url = ResourceManager::normalizeURL(unnormalizedURL);
QString scriptContents;

View file

@ -28,6 +28,7 @@ class ScriptCache : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
void clearCache();
void getScriptContents(const QString& scriptOrURL, contentAvailableCallback contentAvailable, bool forceDownload = false);

View file

@ -405,7 +405,7 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func
QScriptValue setterFunction = newFunction(setter, 1);
QScriptValue getterFunction = newFunction(getter);
if (!parent.isNull()) {
if (!parent.isNull() && !parent.isEmpty()) {
QScriptValue object = globalObject().property(parent);
if (object.isValid()) {
object.setProperty(name, setterFunction, QScriptValue::PropertySetter);
@ -559,6 +559,7 @@ void ScriptEngine::run() {
if (!_isInitialized) {
init();
}
_isRunning = true;
_isFinished = false;
if (_wantSignals) {

View file

@ -33,7 +33,7 @@
"children": []
},
{
"id": "controllerOverlay",
"id": "manipulatorOverlay",
"type": "overlay",
"data": {
"alpha": 1.0,
@ -42,7 +42,7 @@
"children": [
{
"id": "spineLean",
"type": "controller",
"type": "manipulator",
"data": {
"alpha": 1.0,
"joints": [