mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 12:24:26 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into orange
This commit is contained in:
commit
d0f7ddd3a6
64 changed files with 956 additions and 690 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -18,6 +18,7 @@ QThreadStorage<QNetworkAccessManager*> networkAccessManagers;
|
|||
QNetworkAccessManager& NetworkAccessManager::getInstance() {
|
||||
if (!networkAccessManagers.hasLocalData()) {
|
||||
networkAccessManagers.setLocalData(new QNetworkAccessManager());
|
||||
|
||||
}
|
||||
|
||||
return *networkAccessManagers.localData();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -12,3 +12,4 @@
|
|||
#include "NetworkLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(networking, "hifi.networking")
|
||||
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking)
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||
|
||||
#endif // hifi_NetworkLogging_h
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -54,6 +54,7 @@ void OctreeScriptingInterface::init() {
|
|||
if (_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_jurisdictionListener) {
|
||||
_managedJurisdictionListener = false;
|
||||
} else {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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": [
|
||||
|
|
Loading…
Reference in a new issue