mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-09 02:52:13 +02:00
merge upstream/master into enforce-standard
Conflicts: interface/src/avatar/Hand.cpp
This commit is contained in:
commit
c383b69b61
36 changed files with 383 additions and 251 deletions
|
@ -76,9 +76,9 @@ void OctreeQueryNode::deleteLater() {
|
|||
}
|
||||
|
||||
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) {
|
||||
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node) {
|
||||
// Create octree sending thread...
|
||||
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
|
||||
_octreeSendThread = new OctreeSendThread(octreeServer, node);
|
||||
_octreeSendThread->initialize(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
|
||||
OctreeSceneStats stats;
|
||||
|
||||
void initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID);
|
||||
void initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node);
|
||||
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
|
||||
|
||||
void dumpOutOfView();
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
quint64 startSceneSleepTime = 0;
|
||||
quint64 endSceneSleepTime = 0;
|
||||
|
||||
OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) :
|
||||
_nodeUUID(nodeUUID),
|
||||
OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) :
|
||||
_myServer(myServer),
|
||||
_node(node),
|
||||
_packetData(),
|
||||
_nodeMissingCount(0),
|
||||
_processLock(),
|
||||
_isShuttingDown(false)
|
||||
{
|
||||
|
@ -44,7 +43,8 @@ OctreeSendThread::~OctreeSendThread() {
|
|||
}
|
||||
qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected "
|
||||
"- ending sending thread [" << this << "]";
|
||||
OctreeServer::clientDisconnected();
|
||||
_node.clear();
|
||||
OctreeServer::clientDisconnected();
|
||||
}
|
||||
|
||||
void OctreeSendThread::setIsShuttingDown() {
|
||||
|
@ -68,35 +68,18 @@ bool OctreeSendThread::process() {
|
|||
return false; // exit early if we're shutting down
|
||||
}
|
||||
|
||||
const int MAX_NODE_MISSING_CHECKS = 10;
|
||||
if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) {
|
||||
qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount
|
||||
<< "times we checked, we are going to stop attempting to send.";
|
||||
return false; // stop processing and shutdown, our node no longer exists
|
||||
}
|
||||
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
// don't do any send processing until the initial load of the octree is complete...
|
||||
if (_myServer->isInitialLoadComplete()) {
|
||||
|
||||
// see if we can get access to our node, but don't wait on the lock, if the nodeList is busy
|
||||
// it might not return a node that is known, but that's ok we can handle that case.
|
||||
SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false);
|
||||
|
||||
if (node) {
|
||||
_nodeMissingCount = 0;
|
||||
OctreeQueryNode* nodeData = NULL;
|
||||
|
||||
nodeData = (OctreeQueryNode*) node->getLinkedData();
|
||||
if (!_node.isNull()) {
|
||||
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(_node->getLinkedData());
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData && !nodeData->isShuttingDown()) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
packetDistributor(node, nodeData, viewFrustumChanged);
|
||||
packetDistributor(_node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
} else {
|
||||
_nodeMissingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
class OctreeSendThread : public GenericThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer);
|
||||
OctreeSendThread(OctreeServer* myServer, SharedNodePointer node);
|
||||
virtual ~OctreeSendThread();
|
||||
|
||||
void setIsShuttingDown();
|
||||
|
@ -38,7 +38,7 @@ protected:
|
|||
virtual bool process();
|
||||
|
||||
private:
|
||||
QUuid _nodeUUID;
|
||||
SharedNodePointer _node;
|
||||
OctreeServer* _myServer;
|
||||
|
||||
int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
|
||||
|
@ -46,7 +46,6 @@ private:
|
|||
|
||||
OctreePacketData _packetData;
|
||||
|
||||
int _nodeMissingCount;
|
||||
QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing
|
||||
bool _isShuttingDown;
|
||||
};
|
||||
|
|
|
@ -835,7 +835,7 @@ void OctreeServer::readPendingDatagrams() {
|
|||
if (debug) {
|
||||
qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode;
|
||||
}
|
||||
nodeData->initializeOctreeSendThread(this, matchingNode->getUUID());
|
||||
nodeData->initializeOctreeSendThread(this, matchingNode);
|
||||
}
|
||||
}
|
||||
} else if (packetType == PacketTypeJurisdictionRequest) {
|
||||
|
@ -852,7 +852,9 @@ void OctreeServer::readPendingDatagrams() {
|
|||
|
||||
void OctreeServer::run() {
|
||||
_safeServerName = getMyServerName();
|
||||
|
||||
// Before we do anything else, create our tree...
|
||||
OctreeElement::resetPopulationStatistics();
|
||||
_tree = createTree();
|
||||
|
||||
// use common init to setup common timers and logging
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3597"/>
|
||||
<location filename="src/Application.cpp" line="3608"/>
|
||||
<source>Open Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3598"/>
|
||||
<location filename="src/Application.cpp" line="3609"/>
|
||||
<source>JavaScript Files (*.js)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -113,18 +113,18 @@
|
|||
<context>
|
||||
<name>Menu</name>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="457"/>
|
||||
<location filename="src/Menu.cpp" line="459"/>
|
||||
<source>Open .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="459"/>
|
||||
<location filename="src/Menu.cpp" line="471"/>
|
||||
<location filename="src/Menu.cpp" line="461"/>
|
||||
<location filename="src/Menu.cpp" line="473"/>
|
||||
<source>Text files (*.ini)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="469"/>
|
||||
<location filename="src/Menu.cpp" line="471"/>
|
||||
<source>Save .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
|
|
@ -2676,7 +2676,7 @@ void Application::displayStats() {
|
|||
|
||||
glm::vec3 avatarPos = _myAvatar->getPosition();
|
||||
|
||||
lines = _statsExpanded ? 4 : 3;
|
||||
lines = _statsExpanded ? 5 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
@ -2713,12 +2713,23 @@ void Application::displayStats() {
|
|||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT);
|
||||
|
||||
stringstream downloadStats;
|
||||
downloadStats << "Downloads: ";
|
||||
foreach (Resource* resource, ResourceCache::getLoadingRequests()) {
|
||||
const float MAXIMUM_PERCENTAGE = 100.0f;
|
||||
downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% ";
|
||||
}
|
||||
downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)";
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT);
|
||||
}
|
||||
|
||||
verticalOffset = 0;
|
||||
horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410);
|
||||
|
||||
lines = _statsExpanded ? 11 : 3;
|
||||
lines = _statsExpanded ? 12 : 3;
|
||||
displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
|
|
@ -143,6 +143,28 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
QString deviceName;
|
||||
if (mode == QAudio::AudioInput) {
|
||||
WAVEINCAPS wic;
|
||||
// first use WAVE_MAPPER to get the default devices manufacturer ID
|
||||
waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic));
|
||||
//Use the received manufacturer id to get the device's real name
|
||||
waveInGetDevCaps(wic.wMid, &wic, sizeof(wic));
|
||||
qDebug() << "input device:" << wic.szPname;
|
||||
deviceName = wic.szPname;
|
||||
} else {
|
||||
WAVEOUTCAPS woc;
|
||||
// first use WAVE_MAPPER to get the default devices manufacturer ID
|
||||
waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc));
|
||||
//Use the received manufacturer id to get the device's real name
|
||||
waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc));
|
||||
qDebug() << "output device:" << woc.szPname;
|
||||
deviceName = woc.szPname;
|
||||
}
|
||||
return getNamedAudioDeviceForMode(mode, deviceName);
|
||||
#endif
|
||||
|
||||
|
||||
// fallback for failed lookup is the default device
|
||||
return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice();
|
||||
|
@ -500,7 +522,7 @@ void Audio::handleAudioInput() {
|
|||
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||
MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead()->getOrientation();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead()->getTweakedOrientation();
|
||||
|
||||
// we need the amount of bytes in the buffer + 1 for type
|
||||
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
|
||||
|
@ -840,7 +862,6 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
|
|||
// cleanup any previously initialized device
|
||||
if (_audioOutput) {
|
||||
_audioOutput->stop();
|
||||
disconnect(_outputDevice, 0, 0, 0);
|
||||
_outputDevice = NULL;
|
||||
|
||||
delete _audioOutput;
|
||||
|
|
|
@ -62,7 +62,8 @@ Menu* Menu::getInstance() {
|
|||
const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f};
|
||||
const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
|
||||
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
|
||||
const int FIVE_SECONDS_OF_FRAMES = 5 * 60;
|
||||
const int ONE_SECOND_OF_FRAMES = 60;
|
||||
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
|
||||
|
||||
Menu::Menu() :
|
||||
_actionHash(),
|
||||
|
@ -82,6 +83,7 @@ Menu::Menu() :
|
|||
_lastAdjust(usecTimestampNow()),
|
||||
_lastAvatarDetailDrop(usecTimestampNow()),
|
||||
_fpsAverage(FIVE_SECONDS_OF_FRAMES),
|
||||
_fastFPSAverage(ONE_SECOND_OF_FRAMES),
|
||||
_loginAction(NULL)
|
||||
{
|
||||
Application *appInstance = Application::getInstance();
|
||||
|
@ -1192,19 +1194,21 @@ void Menu::autoAdjustLOD(float currentFPS) {
|
|||
currentFPS = ASSUMED_FPS;
|
||||
}
|
||||
_fpsAverage.updateAverage(currentFPS);
|
||||
_fastFPSAverage.updateAverage(currentFPS);
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
if (_fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS) {
|
||||
if (now - _lastAvatarDetailDrop > ADJUST_LOD_DOWN_DELAY) {
|
||||
const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000;
|
||||
if (_fastFPSAverage.getAverage() < ADJUST_LOD_DOWN_FPS) {
|
||||
if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) {
|
||||
// attempt to lower the detail in proportion to the fps difference
|
||||
float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f;
|
||||
_avatarLODDistanceMultiplier *= (targetFps / _fpsAverage.getAverage());
|
||||
_avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage());
|
||||
_lastAvatarDetailDrop = now;
|
||||
}
|
||||
} else if (_fpsAverage.getAverage() > ADJUST_LOD_UP_FPS) {
|
||||
} else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) {
|
||||
// let the detail level creep slowly upwards
|
||||
const float DISTANCE_DECREASE_RATE = 0.01f;
|
||||
const float DISTANCE_DECREASE_RATE = 0.02f;
|
||||
const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f;
|
||||
_avatarLODDistanceMultiplier = qMax(MINIMUM_DISTANCE_MULTIPLIER,
|
||||
_avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE);
|
||||
|
|
|
@ -66,8 +66,6 @@ public:
|
|||
static Menu* getInstance();
|
||||
~Menu();
|
||||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
|
@ -133,6 +131,8 @@ public slots:
|
|||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
private slots:
|
||||
void aboutApp();
|
||||
|
@ -211,6 +211,7 @@ private:
|
|||
quint64 _lastAdjust;
|
||||
quint64 _lastAvatarDetailDrop;
|
||||
SimpleMovingAverage _fpsAverage;
|
||||
SimpleMovingAverage _fastFPSAverage;
|
||||
QAction* _loginAction;
|
||||
QAction* _chatAction;
|
||||
};
|
||||
|
|
|
@ -499,13 +499,19 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
|
|||
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
||||
void Avatar::updateShapePositions() {
|
||||
_skeletonModel.updateShapePositions();
|
||||
bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
||||
|
||||
Model& headModel = getHead()->getFaceModel();
|
||||
headModel.updateShapePositions();
|
||||
collided = headModel.findCollisions(shapes, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
||||
// TODO: Andrew to fix: also collide against _skeleton
|
||||
//bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
||||
|
||||
Model& headModel = getHead()->getFaceModel();
|
||||
//collided = headModel.findCollisions(shapes, collisions) || collided;
|
||||
bool collided = headModel.findCollisions(shapes, collisions);
|
||||
return collided;
|
||||
}
|
||||
|
||||
|
@ -752,17 +758,30 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Avatar::applyCollision(CollisionInfo& collision) {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return;
|
||||
}
|
||||
// TODO: make skeleton also respond to collisions
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
if (model == &(getHead()->getFaceModel())) {
|
||||
getHead()->applyCollision(collision);
|
||||
void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) {
|
||||
// compute lean angles
|
||||
glm::vec3 leverAxis = contactPoint - getPosition();
|
||||
float leverLength = glm::length(leverAxis);
|
||||
if (leverLength > EPSILON) {
|
||||
glm::quat bodyRotation = getOrientation();
|
||||
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
|
||||
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
|
||||
|
||||
leverAxis = leverAxis / leverLength;
|
||||
glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis;
|
||||
// we use the small-angle approximation for sine below to compute the length of
|
||||
// the opposite side of a narrow right triangle
|
||||
float sideways = - glm::dot(effectivePenetration, xAxis) / leverLength;
|
||||
float forward = glm::dot(effectivePenetration, zAxis) / leverLength;
|
||||
getHead()->addLean(sideways, forward);
|
||||
}
|
||||
}
|
||||
|
||||
float Avatar::getBoundingRadius() const {
|
||||
// TODO: also use head model when computing the avatar's bounding radius
|
||||
return _skeletonModel.getBoundingRadius();
|
||||
}
|
||||
|
||||
float Avatar::getPelvisFloatingHeight() const {
|
||||
return -_skeletonModel.getBindExtents().minimum.y;
|
||||
}
|
||||
|
|
|
@ -145,10 +145,11 @@ public:
|
|||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration);
|
||||
|
||||
float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); }
|
||||
/// \return bounding radius of avatar
|
||||
virtual float getBoundingRadius() const;
|
||||
void updateShapePositions();
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <NodeList.h>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
|
@ -109,8 +110,6 @@ void Hand::collideAgainstAvatarOld(Avatar* avatar, bool isMyHand) {
|
|||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
if (isMyHand) {
|
||||
// we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is
|
||||
// not expected to respond to the collision (hand hit unmovable part of their Avatar)
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
}
|
||||
|
@ -128,55 +127,38 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
return;
|
||||
}
|
||||
|
||||
// 2 = NUM_HANDS
|
||||
int palmIndices[2];
|
||||
getLeftRightPalmIndices(*palmIndices, *(palmIndices + 1));
|
||||
|
||||
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
int jointIndices[2];
|
||||
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
|
||||
jointIndices[1] = skeletonModel.getRightHandJointIndex();
|
||||
|
||||
palmIndices[1] = -1; // adebug temporarily disable right hand
|
||||
jointIndices[1] = -1; // adebug temporarily disable right hand
|
||||
|
||||
for (size_t i = 0; i < 1; i++) {
|
||||
int palmIndex = palmIndices[i];
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
int jointIndex = jointIndices[i];
|
||||
if (palmIndex == -1 || jointIndex == -1) {
|
||||
if (jointIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
PalmData& palm = _palms[palmIndex];
|
||||
if (!palm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
playSlaps(palm, avatar);
|
||||
}
|
||||
|
||||
handCollisions.clear();
|
||||
QVector<const Shape*> shapes;
|
||||
skeletonModel.getHandShapes(jointIndex, shapes);
|
||||
bool collided = isMyHand ? avatar->findCollisions(shapes, handCollisions) : avatar->findCollisions(shapes, handCollisions);
|
||||
if (collided) {
|
||||
//if (avatar->findCollisions(shapes, handCollisions)) {
|
||||
glm::vec3 averagePenetration;
|
||||
|
||||
if (avatar->findCollisions(shapes, handCollisions)) {
|
||||
glm::vec3 totalPenetration(0.f);
|
||||
glm::vec3 averageContactPoint;
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
averagePenetration += collision->_penetration;
|
||||
totalPenetration += collision->_penetration;
|
||||
averageContactPoint += collision->_contactPoint;
|
||||
}
|
||||
averagePenetration /= (float)handCollisions.size();
|
||||
if (isMyHand) {
|
||||
// our hand against other avatar
|
||||
// for now we resolve it to test shapes/collisions
|
||||
// TODO: only partially resolve this penetration
|
||||
palm.addToPosition(-averagePenetration);
|
||||
// TODO: resolve this penetration when we don't think the other avatar will yield
|
||||
//palm.addToPenetration(averagePenetration);
|
||||
} else {
|
||||
// someone else's hand against MyAvatar
|
||||
// TODO: submit collision info to MyAvatar which should lean accordingly
|
||||
averageContactPoint /= (float)handCollisions.size();
|
||||
avatar->applyCollision(averageContactPoint, totalPenetration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +174,7 @@ void Hand::collideAgainstOurself() {
|
|||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
||||
|
||||
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
for (size_t i = 0; i < getNumPalms(); i++) {
|
||||
for (int i = 0; i < int(getNumPalms()); i++) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (!palm.isActive()) {
|
||||
continue;
|
||||
|
@ -210,11 +192,18 @@ void Hand::collideAgainstOurself() {
|
|||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
// resolve penetration
|
||||
palm.addToPosition(-totalPenetration);
|
||||
palm.addToPenetration(totalPenetration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::resolvePenetrations() {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
palm.resolvePenetrations();
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::calculateGeometry() {
|
||||
// generate finger tip balls....
|
||||
_leapFingerTipBalls.clear();
|
||||
|
@ -385,19 +374,3 @@ void Hand::renderLeapHands(bool isMine) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||
const std::vector<glm::vec3>& handNormals) {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
if (i < handPositions.size()) {
|
||||
palm.setActive(true);
|
||||
palm.setRawPosition(handPositions[i]);
|
||||
palm.setRawNormal(handNormals[i]);
|
||||
}
|
||||
else {
|
||||
palm.setActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
||||
void collideAgainstOurself();
|
||||
|
||||
void resolvePenetrations();
|
||||
|
||||
private:
|
||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
||||
Hand(const Hand&);
|
||||
|
@ -71,10 +73,6 @@ private:
|
|||
std::vector<HandBall> _leapFingerTipBalls;
|
||||
std::vector<HandBall> _leapFingerRootBalls;
|
||||
|
||||
// private methods
|
||||
void setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||
const std::vector<glm::vec3>& handNormals);
|
||||
|
||||
void renderLeapHands(bool isMine);
|
||||
void renderLeapFingerTrails();
|
||||
|
||||
|
@ -84,3 +82,4 @@ private:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -55,9 +55,6 @@ void Head::reset() {
|
|||
_faceModel.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
||||
|
||||
// Update audio trailing average for rendering facial animations
|
||||
|
@ -212,34 +209,6 @@ float Head::getTweakedRoll() const {
|
|||
return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
|
||||
}
|
||||
|
||||
void Head::applyCollision(CollisionInfo& collision) {
|
||||
// HACK: the collision proxies for the FaceModel are bad. As a temporary workaround
|
||||
// we collide against a hard coded collision proxy.
|
||||
// TODO: get a better collision proxy here.
|
||||
const float HEAD_RADIUS = 0.15f;
|
||||
const glm::vec3 HEAD_CENTER = _position;
|
||||
|
||||
// collide the contactPoint against the collision proxy to obtain a new penetration
|
||||
// NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere)
|
||||
glm::vec3 penetration;
|
||||
if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
||||
// compute lean angles
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||
glm::quat bodyRotation = owningAvatar->getOrientation();
|
||||
glm::vec3 neckPosition;
|
||||
if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
|
||||
glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f);
|
||||
glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f);
|
||||
float neckLength = glm::length(_position - neckPosition);
|
||||
if (neckLength > 0.f) {
|
||||
float forward = glm::dot(collision._penetration, zAxis) / neckLength;
|
||||
float sideways = - glm::dot(collision._penetration, xAxis) / neckLength;
|
||||
addLean(sideways, forward);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
|
||||
|
||||
Application::getInstance()->getGlowEffect()->begin();
|
||||
|
|
|
@ -82,8 +82,6 @@ public:
|
|||
virtual float getTweakedPitch() const;
|
||||
virtual float getTweakedYaw() const;
|
||||
virtual float getTweakedRoll() const;
|
||||
|
||||
void applyCollision(CollisionInfo& collisionInfo);
|
||||
|
||||
private:
|
||||
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||
|
|
|
@ -177,26 +177,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime);
|
||||
}
|
||||
|
||||
if (_collisionFlags != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cosf(0.5f * RADIANS_PER_DEGREE * myCamera->getFieldOfView()));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
|
||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// add thrust to velocity
|
||||
_velocity += _thrust * deltaTime;
|
||||
|
||||
|
@ -320,7 +300,28 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
|
||||
// now that we're done stepping the avatar forward in time, compute new collisions
|
||||
if (_collisionFlags != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
|
||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// consider updating our billboard
|
||||
maybeUpdateBillboard();
|
||||
}
|
||||
|
@ -361,7 +362,7 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
|||
}
|
||||
} else {
|
||||
// restore rotation, lean to neutral positions
|
||||
const float RESTORE_PERIOD = 1.f; // seconds
|
||||
const float RESTORE_PERIOD = 0.25f; // seconds
|
||||
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
|
||||
head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage));
|
||||
head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage));
|
||||
|
@ -881,35 +882,31 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
updateShapePositions();
|
||||
float myBoundingRadius = getBoundingRadius();
|
||||
|
||||
/* TODO: Andrew to fix Avatar-Avatar body collisions
|
||||
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||
// TODO: make the collision work without assuming avatar orientation
|
||||
Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
||||
float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||
float myCapsuleHeight = staticScale.y;
|
||||
*/
|
||||
|
||||
// TODO: these local variables are not used in the live code, only in the
|
||||
// commented-outTODO code below.
|
||||
//Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
||||
//glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
||||
//float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||
//float myCapsuleHeight = staticScale.y;
|
||||
|
||||
CollisionInfo collisionInfo;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (static_cast<Avatar*>(this) == avatar) {
|
||||
// don't collide with ourselves
|
||||
continue;
|
||||
}
|
||||
avatar->updateShapePositions();
|
||||
float distance = glm::length(_position - avatar->getPosition());
|
||||
if (_distanceToNearestAvatar > distance) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
float theirBoundingRadius = avatar->getBoundingRadius();
|
||||
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||
_skeletonModel.updateShapePositions();
|
||||
Model& headModel = getHead()->getFaceModel();
|
||||
headModel.updateShapePositions();
|
||||
|
||||
/* TODO: Andrew to fix Avatar-Avatar body collisions
|
||||
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||
|
@ -925,12 +922,16 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
*/
|
||||
|
||||
// collide our hands against them
|
||||
getHand()->collideAgainstAvatar(avatar, true);
|
||||
// TODO: make this work when we can figure out when the other avatar won't yeild
|
||||
// (for example, we're colling against their chest or leg)
|
||||
//getHand()->collideAgainstAvatar(avatar, true);
|
||||
|
||||
// collide their hands against us
|
||||
avatar->getHand()->collideAgainstAvatar(this, false);
|
||||
}
|
||||
}
|
||||
// TODO: uncomment this when we handle collisions that won't affect other avatar
|
||||
//getHand()->resolvePenetrations();
|
||||
}
|
||||
|
||||
class SortedAvatar {
|
||||
|
|
|
@ -63,13 +63,30 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
|
||||
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
|
||||
if (jointIndex == -1) {
|
||||
if (jointIndex < 0 || jointIndex >= int(_shapes.size())) {
|
||||
return;
|
||||
}
|
||||
if (jointIndex == getLeftHandJointIndex()
|
||||
|| jointIndex == getRightHandJointIndex()) {
|
||||
// TODO: also add fingers and other hand-parts
|
||||
shapes.push_back(_shapes[jointIndex]);
|
||||
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
const FBXJoint& joint = geometry.joints[i];
|
||||
if (i == jointIndex) {
|
||||
// this shape is the hand
|
||||
shapes.push_back(_shapes[i]);
|
||||
} else {
|
||||
int parentIndex = joint.parentIndex;
|
||||
while (parentIndex != -1) {
|
||||
if (parentIndex == jointIndex) {
|
||||
// this shape is a child of the hand
|
||||
shapes.push_back(_shapes[i]);
|
||||
break;
|
||||
}
|
||||
parentIndex = geometry.joints[parentIndex].parentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
/// \param jointIndex index of hand joint
|
||||
/// \param shapes[out] list in which is stored pointers to hand shapes
|
||||
void getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QThreadPool>
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
|
@ -32,7 +33,8 @@ Model::Model(QObject* parent) :
|
|||
_scale(1.0f, 1.0f, 1.0f),
|
||||
_shapesAreDirty(true),
|
||||
_lodDistance(0.0f),
|
||||
_pupilDilation(0.0f) {
|
||||
_pupilDilation(0.0f),
|
||||
_boundingRadius(0.f) {
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
@ -164,6 +166,7 @@ void Model::createCollisionShapes() {
|
|||
|
||||
void Model::updateShapePositions() {
|
||||
if (_shapesAreDirty && _shapes.size() == _jointStates.size()) {
|
||||
_boundingRadius = 0.f;
|
||||
float uniformScale = extractUniformScale(_scale);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
|
@ -173,7 +176,12 @@ void Model::updateShapePositions() {
|
|||
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
|
||||
_shapes[i]->setPosition(worldPosition);
|
||||
_shapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
|
||||
float distance2 = glm::distance2(worldPosition, _translation);
|
||||
if (distance2 > _boundingRadius) {
|
||||
_boundingRadius = distance2;
|
||||
}
|
||||
}
|
||||
_boundingRadius = sqrtf(_boundingRadius);
|
||||
_shapesAreDirty = false;
|
||||
}
|
||||
}
|
||||
|
@ -321,18 +329,10 @@ bool Model::getRightHandRotation(glm::quat& rotation) const {
|
|||
return getJointRotation(getRightHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::setLeftHandPosition(const glm::vec3& position) {
|
||||
return setJointPosition(getLeftHandJointIndex(), position);
|
||||
}
|
||||
|
||||
bool Model::restoreLeftHandPosition(float percent) {
|
||||
return restoreJointPosition(getLeftHandJointIndex(), percent);
|
||||
}
|
||||
|
||||
bool Model::setLeftHandRotation(const glm::quat& rotation) {
|
||||
return setJointRotation(getLeftHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::getLeftShoulderPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getLastFreeJointIndex(getLeftHandJointIndex()), position);
|
||||
}
|
||||
|
@ -341,18 +341,10 @@ float Model::getLeftArmLength() const {
|
|||
return getLimbLength(getLeftHandJointIndex());
|
||||
}
|
||||
|
||||
bool Model::setRightHandPosition(const glm::vec3& position) {
|
||||
return setJointPosition(getRightHandJointIndex(), position);
|
||||
}
|
||||
|
||||
bool Model::restoreRightHandPosition(float percent) {
|
||||
return restoreJointPosition(getRightHandJointIndex(), percent);
|
||||
}
|
||||
|
||||
bool Model::setRightHandRotation(const glm::quat& rotation) {
|
||||
return setJointRotation(getRightHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::getRightShoulderPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getLastFreeJointIndex(getRightHandJointIndex()), position);
|
||||
}
|
||||
|
|
|
@ -135,19 +135,11 @@ public:
|
|||
/// \return true whether or not the rotation was found
|
||||
bool getRightHandRotation(glm::quat& rotation) const;
|
||||
|
||||
/// Sets the position of the left hand using inverse kinematics.
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool setLeftHandPosition(const glm::vec3& position);
|
||||
|
||||
/// Restores some percentage of the default position of the left hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool restoreLeftHandPosition(float percent = 1.0f);
|
||||
|
||||
/// Sets the rotation of the left hand.
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool setLeftHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Gets the position of the left shoulder.
|
||||
/// \return whether or not the left shoulder joint was found
|
||||
bool getLeftShoulderPosition(glm::vec3& position) const;
|
||||
|
@ -155,19 +147,11 @@ public:
|
|||
/// Returns the extended length from the left hand to its last free ancestor.
|
||||
float getLeftArmLength() const;
|
||||
|
||||
/// Sets the position of the right hand using inverse kinematics.
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool setRightHandPosition(const glm::vec3& position);
|
||||
|
||||
/// Restores some percentage of the default position of the right hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool restoreRightHandPosition(float percent = 1.0f);
|
||||
|
||||
/// Sets the rotation of the right hand.
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool setRightHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Gets the position of the right shoulder.
|
||||
/// \return whether or not the right shoulder joint was found
|
||||
bool getRightShoulderPosition(glm::vec3& position) const;
|
||||
|
@ -195,6 +179,8 @@ public:
|
|||
/// Use the collision to affect the model
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
float getBoundingRadius() const { return _boundingRadius; }
|
||||
|
||||
/// Sets blended vertices computed in a separate thread.
|
||||
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
|
||||
|
@ -254,7 +240,7 @@ protected:
|
|||
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
|
||||
/// first free ancestor.
|
||||
float getLimbLength(int jointIndex) const;
|
||||
|
||||
|
||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
|
||||
|
||||
private:
|
||||
|
@ -280,6 +266,8 @@ private:
|
|||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||
|
||||
QVector<Model*> _attachments;
|
||||
|
||||
float _boundingRadius;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _normalMapProgram;
|
||||
|
|
|
@ -65,9 +65,15 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString&
|
|||
};
|
||||
|
||||
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||
return Menu::getInstance()->isOptionChecked(menuOption);
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, menuOption));
|
||||
return result;
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
return Menu::getInstance()->setIsOptionChecked(menuOption, isChecked);
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, menuOption),
|
||||
Q_ARG(bool, isChecked));
|
||||
}
|
||||
|
|
|
@ -61,11 +61,12 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex)
|
|||
}
|
||||
|
||||
PalmData::PalmData(HandData* owningHandData) :
|
||||
_rawRotation(0, 0, 0, 1),
|
||||
_rawPosition(0, 0, 0),
|
||||
_rawNormal(0, 1, 0),
|
||||
_rawVelocity(0, 0, 0),
|
||||
_rotationalVelocity(0, 0, 0),
|
||||
_rawRotation(0.f, 0.f, 0.f, 1.f),
|
||||
_rawPosition(0.f),
|
||||
_rawNormal(0.f, 1.f, 0.f),
|
||||
_rawVelocity(0.f),
|
||||
_rotationalVelocity(0.f),
|
||||
_totalPenetration(0.f),
|
||||
_controllerButtons(0),
|
||||
_isActive(false),
|
||||
_leapID(LEAPID_INVALID),
|
||||
|
|
|
@ -154,6 +154,9 @@ public:
|
|||
void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
|
||||
const glm::vec3& getRawVelocity() const { return _rawVelocity; }
|
||||
void addToPosition(const glm::vec3& delta);
|
||||
|
||||
void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
|
||||
void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.f); }
|
||||
|
||||
void setTipPosition(const glm::vec3& position) { _tipPosition = position; }
|
||||
const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); }
|
||||
|
@ -203,6 +206,7 @@ private:
|
|||
|
||||
glm::vec3 _tipPosition;
|
||||
glm::vec3 _tipVelocity;
|
||||
glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
|
||||
unsigned int _controllerButtons;
|
||||
unsigned int _lastControllerButtons;
|
||||
float _trigger;
|
||||
|
|
|
@ -29,6 +29,11 @@ quint64 OctreeElement::_externalChildrenMemoryUsage = 0;
|
|||
quint64 OctreeElement::_voxelNodeCount = 0;
|
||||
quint64 OctreeElement::_voxelNodeLeafCount = 0;
|
||||
|
||||
void OctreeElement::resetPopulationStatistics() {
|
||||
_voxelNodeCount = 0;
|
||||
_voxelNodeLeafCount = 0;
|
||||
}
|
||||
|
||||
OctreeElement::OctreeElement() {
|
||||
// Note: you must call init() from your subclass, otherwise the OctreeElement will not be properly
|
||||
// initialized. You will see DEADBEEF in your memory debugger if you have not properly called init()
|
||||
|
|
|
@ -152,6 +152,7 @@ public:
|
|||
static void addUpdateHook(OctreeElementUpdateHook* hook);
|
||||
static void removeUpdateHook(OctreeElementUpdateHook* hook);
|
||||
|
||||
static void resetPopulationStatistics();
|
||||
static unsigned long getNodeCount() { return _voxelNodeCount; }
|
||||
static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; }
|
||||
static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; }
|
||||
|
|
|
@ -463,7 +463,6 @@ void ScriptEngine::timerFired() {
|
|||
|
||||
if (!callingTimer->isActive()) {
|
||||
// this timer is done, we can kill it
|
||||
qDebug() << "Deleting a single shot timer";
|
||||
delete callingTimer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,18 +357,9 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) {
|
||||
SharedNodePointer node;
|
||||
// if caller wants us to block and guarantee the correct answer, then honor that request
|
||||
if (blockingLock) {
|
||||
// this will block till we can get access
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
} else if (_nodeHashMutex.tryLock()) { // some callers are willing to get wrong answers but not block
|
||||
node = _nodeHash.value(nodeUUID);
|
||||
_nodeHashMutex.unlock();
|
||||
}
|
||||
return node;
|
||||
SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) {
|
||||
QMutexLocker locker(&_nodeHashMutex);
|
||||
return _nodeHash.value(nodeUUID);
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) {
|
||||
|
|
|
@ -103,8 +103,7 @@ public:
|
|||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket);
|
||||
void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node);
|
||||
|
||||
/// passing false for blockingLock, will tryLock, and may return NULL when a node with the UUID actually does exist
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true);
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
|
||||
SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
|
||||
|
||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||
|
|
|
@ -63,10 +63,12 @@ void ResourceCache::attemptRequest(Resource* resource) {
|
|||
return;
|
||||
}
|
||||
_requestLimit--;
|
||||
_loadingRequests.append(resource);
|
||||
resource->makeRequest();
|
||||
}
|
||||
|
||||
void ResourceCache::requestCompleted() {
|
||||
void ResourceCache::requestCompleted(Resource* resource) {
|
||||
_loadingRequests.removeOne(resource);
|
||||
_requestLimit++;
|
||||
|
||||
// look for the highest priority pending request
|
||||
|
@ -96,6 +98,7 @@ const int DEFAULT_REQUEST_LIMIT = 10;
|
|||
int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
|
||||
|
||||
QList<QPointer<Resource> > ResourceCache::_pendingRequests;
|
||||
QList<Resource*> ResourceCache::_loadingRequests;
|
||||
|
||||
Resource::Resource(const QUrl& url, bool delayLoad) :
|
||||
_url(url),
|
||||
|
@ -121,7 +124,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) :
|
|||
|
||||
Resource::~Resource() {
|
||||
if (_reply) {
|
||||
ResourceCache::requestCompleted();
|
||||
ResourceCache::requestCompleted(this);
|
||||
delete _reply;
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +218,7 @@ void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
|||
_replyTimer->disconnect(this);
|
||||
_replyTimer->deleteLater();
|
||||
_replyTimer = NULL;
|
||||
ResourceCache::requestCompleted();
|
||||
ResourceCache::requestCompleted(this);
|
||||
|
||||
downloadFinished(reply);
|
||||
}
|
||||
|
@ -250,7 +253,7 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug)
|
|||
_replyTimer->disconnect(this);
|
||||
_replyTimer->deleteLater();
|
||||
_replyTimer = NULL;
|
||||
ResourceCache::requestCompleted();
|
||||
ResourceCache::requestCompleted(this);
|
||||
|
||||
// retry for certain types of failures
|
||||
switch (error) {
|
||||
|
|
|
@ -37,6 +37,10 @@ public:
|
|||
static void setRequestLimit(int limit) { _requestLimit = limit; }
|
||||
static int getRequestLimit() { return _requestLimit; }
|
||||
|
||||
static const QList<Resource*>& getLoadingRequests() { return _loadingRequests; }
|
||||
|
||||
static int getPendingRequestCount() { return _pendingRequests.size(); }
|
||||
|
||||
ResourceCache(QObject* parent = NULL);
|
||||
virtual ~ResourceCache();
|
||||
|
||||
|
@ -58,7 +62,7 @@ protected:
|
|||
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
|
||||
static void attemptRequest(Resource* resource);
|
||||
static void requestCompleted();
|
||||
static void requestCompleted(Resource* resource);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -70,6 +74,7 @@ private:
|
|||
static QNetworkAccessManager* _networkAccessManager;
|
||||
static int _requestLimit;
|
||||
static QList<QPointer<Resource> > _pendingRequests;
|
||||
static QList<Resource*> _loadingRequests;
|
||||
};
|
||||
|
||||
/// Base class for resources.
|
||||
|
@ -102,6 +107,15 @@ public:
|
|||
/// Checks whether the resource has loaded.
|
||||
bool isLoaded() const { return _loaded; }
|
||||
|
||||
/// For loading resources, returns the number of bytes received.
|
||||
qint64 getBytesReceived() const { return _bytesReceived; }
|
||||
|
||||
/// For loading resources, returns the number of total bytes (or zero if unknown).
|
||||
qint64 getBytesTotal() const { return _bytesTotal; }
|
||||
|
||||
/// For loading resources, returns the load progress.
|
||||
float getProgress() const { return (_bytesTotal == 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
||||
|
||||
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
|
||||
|
||||
void setCache(ResourceCache* cache) { _cache = cache; }
|
||||
|
@ -152,6 +166,7 @@ private:
|
|||
int _lruKey;
|
||||
QNetworkReply* _reply;
|
||||
QTimer* _replyTimer;
|
||||
int _index;
|
||||
qint64 _bytesReceived;
|
||||
qint64 _bytesTotal;
|
||||
int _attempts;
|
||||
|
|
|
@ -86,7 +86,8 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
|||
if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) {
|
||||
glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B
|
||||
float radialDistance2 = glm::length2(radialAxis);
|
||||
if (radialDistance2 > totalRadius * totalRadius) {
|
||||
float totalRadius2 = totalRadius * totalRadius;
|
||||
if (radialDistance2 > totalRadius2) {
|
||||
// sphere is too far from capsule axis
|
||||
return false;
|
||||
}
|
||||
|
@ -95,6 +96,9 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
|
|||
float sign = (axialDistance > 0.f) ? 1.f : -1.f;
|
||||
radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis;
|
||||
radialDistance2 = glm::length2(radialAxis);
|
||||
if (radialDistance2 > totalRadius2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (radialDistance2 > EPSILON * EPSILON) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
|
@ -147,7 +151,8 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
|||
if (absAxialDistance < totalRadius + capsuleA->getHalfHeight()) {
|
||||
glm::vec3 radialAxis = AB + axialDistance * capsuleAxis; // from sphereB to axis of capsuleA
|
||||
float radialDistance2 = glm::length2(radialAxis);
|
||||
if (radialDistance2 > totalRadius * totalRadius) {
|
||||
float totalRadius2 = totalRadius * totalRadius;
|
||||
if (radialDistance2 > totalRadius2) {
|
||||
// sphere is too far from capsule axis
|
||||
return false;
|
||||
}
|
||||
|
@ -162,6 +167,9 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
|
|||
closestApproach = capsuleA->getPosition() + (sign * capsuleA->getHalfHeight()) * capsuleAxis;
|
||||
radialAxis = closestApproach - sphereB->getPosition();
|
||||
radialDistance2 = glm::length2(radialAxis);
|
||||
if (radialDistance2 > totalRadius2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (radialDistance2 > EPSILON * EPSILON) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
|
|
75
libraries/shared/src/StreamUtils.cpp
Normal file
75
libraries/shared/src/StreamUtils.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// StreamUtils.cpp
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.02.21
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "StreamUtils.h"
|
||||
|
||||
const char* hex_digits = "0123456789abcdef";
|
||||
|
||||
void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) {
|
||||
int row_size = 32;
|
||||
int i = 0;
|
||||
while (i < buffer.size()) {
|
||||
for(int j = 0; i < buffer.size() && j < row_size; ++j) {
|
||||
char byte = buffer[i];
|
||||
s << hex_digits[(byte >> 4) & 0x0f] << hex_digits[byte & 0x0f] << " ";
|
||||
++i;
|
||||
}
|
||||
s << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::vec3& v) {
|
||||
s << "<" << v.x << " " << v.y << " " << v.z << ">";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::quat& q) {
|
||||
s << "<" << q.x << " " << q.y << " " << q.z << " " << q.w << ">";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::mat4& m) {
|
||||
s << "[";
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";";
|
||||
}
|
||||
s << " ]";
|
||||
return s;
|
||||
}
|
||||
|
||||
// less common utils can be enabled with DEBUG
|
||||
#ifdef DEBUG
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) {
|
||||
s << "{penetration=" << c._penetration
|
||||
<< ", contactPoint=" << c._contactPoint
|
||||
<< ", addedVelocity=" << c._addedVelocity
|
||||
<< "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) {
|
||||
s << "{type='sphere', center=" << sphere.getPosition()
|
||||
<< ", radius=" << sphere.getRadius()
|
||||
<< "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
|
||||
s << "{type='capsule', center=" << capsule.getPosition()
|
||||
<< ", radius=" << capsule.getRadius()
|
||||
<< ", length=" << (2.f * capsule.getHalfHeight())
|
||||
<< ", begin=" << capsule.getStartPoint()
|
||||
<< ", end=" << capsule.getEndPoint()
|
||||
<< "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
39
libraries/shared/src/StreamUtils.h
Normal file
39
libraries/shared/src/StreamUtils.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// StreamUtils.h
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.02.21
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __tests__StreamUtils__
|
||||
#define __tests__StreamUtils__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
|
||||
namespace StreamUtil {
|
||||
// dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace
|
||||
void dump(std::ostream& s, const QByteArray& buffer);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::vec3& v);
|
||||
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
|
||||
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);
|
||||
|
||||
// less common utils can be enabled with DEBUG
|
||||
#ifdef DEBUG
|
||||
#include "CollisionInfo.h"
|
||||
#include "SphereShape.h"
|
||||
#include "CapsuleShape.h"
|
||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
|
||||
std::ostream& operator<<(std::ostream& s, const SphereShape& shape);
|
||||
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
#endif // __tests__StreamUtils__
|
|
@ -13,12 +13,17 @@
|
|||
|
||||
#include <CollisionInfo.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "CollisionInfoTests.h"
|
||||
#include "PhysicsTestUtil.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
static glm::vec3 xAxis(1.f, 0.f, 0.f);
|
||||
static glm::vec3 xZxis(0.f, 1.f, 0.f);
|
||||
static glm::vec3 xYxis(0.f, 0.f, 1.f);
|
||||
|
||||
void CollisionInfoTests::rotateThenTranslate() {
|
||||
CollisionInfo collision;
|
||||
collision._penetration = xAxis;
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
#include <ShapeCollider.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <SphereShape.h>
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "PhysicsTestUtil.h"
|
||||
#include "ShapeColliderTests.h"
|
||||
|
||||
const glm::vec3 origin(0.f);
|
||||
static const glm::vec3 xAxis(1.f, 0.f, 0.f);
|
||||
static const glm::vec3 yAxis(0.f, 1.f, 0.f);
|
||||
static const glm::vec3 zAxis(0.f, 0.f, 1.f);
|
||||
|
||||
void ShapeColliderTests::sphereMissesSphere() {
|
||||
// non-overlapping spheres of unequal size
|
||||
|
|
Loading…
Reference in a new issue