mirror of
https://github.com/overte-org/overte.git
synced 2025-04-15 12:28:51 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into fix_import
This commit is contained in:
commit
710a960a35
24 changed files with 466 additions and 216 deletions
|
@ -48,6 +48,8 @@
|
|||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamAttributes>
|
||||
#include <QMediaPlayer>
|
||||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <Logging.h>
|
||||
|
@ -199,7 +201,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
connect(audioThread, SIGNAL(started()), &_audio, SLOT(start()));
|
||||
|
||||
audioThread->start();
|
||||
|
||||
|
||||
connect(nodeList, SIGNAL(domainChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
|
@ -1415,6 +1417,32 @@ void Application::wheelEvent(QWheelEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::dropEvent(QDropEvent *event) {
|
||||
QString snapshotPath;
|
||||
const QMimeData *mimeData = event->mimeData();
|
||||
foreach (QUrl url, mimeData->urls()) {
|
||||
if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) {
|
||||
snapshotPath = url.url().remove("file://");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
|
||||
if (snapshotData != NULL) {
|
||||
if (!snapshotData->getDomain().isEmpty()) {
|
||||
Menu::getInstance()->goToDomain(snapshotData->getDomain());
|
||||
}
|
||||
|
||||
_myAvatar->setPosition(snapshotData->getLocation());
|
||||
_myAvatar->setOrientation(snapshotData->getOrientation());
|
||||
} else {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("No location details were found in this JPG, try dragging in an authentic Hifi snapshot.");
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::sendPingPackets() {
|
||||
QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
|
||||
controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
|
||||
|
@ -3830,7 +3858,7 @@ void Application::updateWindowTitle(){
|
|||
|
||||
QString title = QString() + _profile.getUsername() + " " + nodeList->getSessionUUID().toString()
|
||||
+ " @ " + nodeList->getDomainHostname() + buildVersion;
|
||||
|
||||
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
@ -4234,6 +4262,6 @@ void Application::takeSnapshot() {
|
|||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar->getPosition());
|
||||
Snapshot::saveSnapshot(_glWidget, &_profile, _myAvatar);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ static const float NODE_KILLED_RED = 1.0f;
|
|||
static const float NODE_KILLED_GREEN = 0.0f;
|
||||
static const float NODE_KILLED_BLUE = 0.0f;
|
||||
|
||||
static const QString SNAPSHOT_EXTENSION = ".jpg";
|
||||
|
||||
class Application : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -127,6 +129,7 @@ public:
|
|||
void touchUpdateEvent(QTouchEvent* event);
|
||||
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
|
||||
bool event(QEvent* event);
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "Application.h"
|
||||
|
||||
#include "GLCanvas.h"
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
|
||||
GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuffer)) {
|
||||
}
|
||||
|
@ -16,6 +18,7 @@ GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuf
|
|||
void GLCanvas::initializeGL() {
|
||||
Application::getInstance()->initializeGL();
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void GLCanvas::paintGL() {
|
||||
|
@ -67,4 +70,18 @@ bool GLCanvas::event(QEvent* event) {
|
|||
|
||||
void GLCanvas::wheelEvent(QWheelEvent* event) {
|
||||
Application::getInstance()->wheelEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
|
||||
const QMimeData *mimeData = event->mimeData();
|
||||
foreach (QUrl url, mimeData->urls()) {
|
||||
if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) {
|
||||
event->acceptProposedAction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas::dropEvent(QDropEvent* event) {
|
||||
Application::getInstance()->dropEvent(event);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ protected:
|
|||
virtual bool event(QEvent* event);
|
||||
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event);
|
||||
virtual void dropEvent(QDropEvent* event);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__GLCanvas__) */
|
||||
|
|
|
@ -112,7 +112,7 @@ Menu::Menu() :
|
|||
MenuOption::GoToDomain,
|
||||
Qt::CTRL | Qt::Key_D,
|
||||
this,
|
||||
SLOT(goToDomain()));
|
||||
SLOT(goToDomainDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToLocation,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
|
@ -169,14 +169,7 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly);
|
||||
|
||||
QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options");
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
// TODO: make this option work
|
||||
//addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, 0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
|
||||
|
@ -345,6 +338,8 @@ Menu::Menu() :
|
|||
SLOT(setTCPEnabled(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
|
||||
addAvatarCollisionSubMenu(avatarOptionsMenu);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu,
|
||||
|
@ -519,6 +514,11 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
Application::getInstance()->getProfile()->loadData(settings);
|
||||
Application::getInstance()->updateWindowTitle();
|
||||
NodeList::getInstance()->loadData(settings);
|
||||
|
||||
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
|
||||
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
myAvatar->updateCollisionFlags();
|
||||
}
|
||||
|
||||
void Menu::saveSettings(QSettings* settings) {
|
||||
|
@ -897,7 +897,7 @@ void Menu::goToDomain(const QString newDomain) {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::goToDomain() {
|
||||
void Menu::goToDomainDialog() {
|
||||
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
|
||||
|
||||
|
@ -1232,6 +1232,22 @@ void Menu::updateFrustumRenderModeAction() {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
|
||||
// add avatar collisions subMenu to overMenu
|
||||
QMenu* subMenu = overMenu->addMenu("Collision Options");
|
||||
|
||||
Application* appInstance = Application::getInstance();
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
}
|
||||
|
||||
QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) {
|
||||
int lastIndex;
|
||||
lastIndex = string.lastIndexOf(search);
|
||||
|
@ -1242,4 +1258,3 @@ QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string)
|
|||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ private slots:
|
|||
void aboutApp();
|
||||
void login();
|
||||
void editPreferences();
|
||||
void goToDomain();
|
||||
void goToDomainDialog();
|
||||
void goToLocation();
|
||||
void bandwidthDetailsClosed();
|
||||
void voxelStatsDetailsClosed();
|
||||
|
@ -152,6 +152,8 @@ private:
|
|||
|
||||
void updateFrustumRenderModeAction();
|
||||
|
||||
void addAvatarCollisionSubMenu(QMenu* overMenu);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
||||
BandwidthDialog* _bandwidthDialog;
|
||||
|
|
|
@ -273,27 +273,19 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
}
|
||||
|
||||
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex) {
|
||||
bool didPenetrate = false;
|
||||
glm::vec3 skeletonPenetration;
|
||||
ModelCollisionInfo collisionInfo;
|
||||
/* Temporarily disabling collisions against the skeleton because the collision proxies up
|
||||
* near the neck are bad and prevent the hand from hitting the face.
|
||||
if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) {
|
||||
collisionInfo._model = &_skeletonModel;
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
*/
|
||||
if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) {
|
||||
collisionInfo._model = &(_head.getFaceModel());
|
||||
collisions.push_back(collisionInfo);
|
||||
didPenetrate = true;
|
||||
}
|
||||
return didPenetrate;
|
||||
CollisionList& collisions, int skeletonSkipIndex) {
|
||||
// Temporarily disabling collisions against the skeleton because the collision proxies up
|
||||
// near the neck are bad and prevent the hand from hitting the face.
|
||||
//return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, 1.0f, skeletonSkipIndex);
|
||||
return _head.getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
if (_collisionFlags & COLLISION_GROUP_PARTICLES) {
|
||||
return false;
|
||||
}
|
||||
bool collided = false;
|
||||
// first do the hand collisions
|
||||
const HandData* handData = getHandData();
|
||||
if (handData) {
|
||||
for (int i = 0; i < NUM_HANDS; i++) {
|
||||
|
@ -311,41 +303,55 @@ bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float s
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int jointIndex = -1;
|
||||
glm::vec3 handPosition;
|
||||
if (i == 0) {
|
||||
_skeletonModel.getLeftHandPosition(handPosition);
|
||||
jointIndex = _skeletonModel.getLeftHandJointIndex();
|
||||
}
|
||||
else {
|
||||
_skeletonModel.getRightHandPosition(handPosition);
|
||||
jointIndex = _skeletonModel.getRightHandJointIndex();
|
||||
}
|
||||
glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis;
|
||||
glm::vec3 diskNormal = palm->getNormal();
|
||||
float diskThickness = 0.08f;
|
||||
const float DISK_THICKNESS = 0.08f;
|
||||
|
||||
// collide against the disk
|
||||
if (findSphereDiskPenetration(sphereCenter, sphereRadius,
|
||||
diskCenter, HAND_PADDLE_RADIUS, diskThickness, diskNormal,
|
||||
collision._penetration)) {
|
||||
collision._addedVelocity = palm->getVelocity();
|
||||
return true;
|
||||
glm::vec3 penetration;
|
||||
if (findSphereDiskPenetration(particleCenter, particleRadius,
|
||||
diskCenter, HAND_PADDLE_RADIUS, DISK_THICKNESS, diskNormal,
|
||||
penetration)) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = PADDLE_HAND_COLLISION;
|
||||
collision->_flags = jointIndex;
|
||||
collision->_penetration = penetration;
|
||||
collision->_addedVelocity = palm->getVelocity();
|
||||
collided = true;
|
||||
} else {
|
||||
// collisions are full, so we might as well bail now
|
||||
return collided;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* adebug TODO: make this work again
|
||||
bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
int jointIndex = _skeletonModel.findSphereCollision(sphereCenter, sphereRadius, collision._penetration);
|
||||
if (jointIndex != -1) {
|
||||
collision._penetration /= (float)(TREE_SCALE);
|
||||
collision._addedVelocity = getVelocity();
|
||||
return true;
|
||||
// then collide against the models
|
||||
int preNumCollisions = collisions.size();
|
||||
if (_skeletonModel.findSphereCollisions(particleCenter, particleRadius, collisions)) {
|
||||
// the Model doesn't have velocity info, so we have to set it for each new collision
|
||||
int postNumCollisions = collisions.size();
|
||||
for (int i = preNumCollisions; i < postNumCollisions; ++i) {
|
||||
CollisionInfo* collision = collisions.getCollision(i);
|
||||
collision->_penetration /= (float)(TREE_SCALE);
|
||||
collision->_addedVelocity = getVelocity();
|
||||
}
|
||||
collided = true;
|
||||
}
|
||||
return false;
|
||||
return collided;
|
||||
}
|
||||
*/
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl &faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
|
@ -430,9 +436,9 @@ void Avatar::updateCollisionFlags() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
|
||||
_collisionFlags |= COLLISION_GROUP_VOXELS;
|
||||
}
|
||||
//if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
// _collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
//}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
_collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setScale(float scale) {
|
||||
|
@ -449,34 +455,34 @@ float Avatar::getHeight() const {
|
|||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const {
|
||||
// ATM only the Skeleton is pokeable
|
||||
// TODO: make poke affect head
|
||||
if (!collision._model) {
|
||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return false;
|
||||
}
|
||||
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
int jointIndex = collision._flags;
|
||||
|
||||
if (model == &(_skeletonModel) && jointIndex != -1) {
|
||||
// collision response of skeleton is temporarily disabled
|
||||
return false;
|
||||
//return _skeletonModel.collisionHitsMoveableJoint(collision);
|
||||
}
|
||||
if (collision._model == &(_head.getFaceModel())) {
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
// ATM we always handle MODEL_COLLISIONS against the face.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Avatar::applyCollision(ModelCollisionInfo& collision) {
|
||||
if (!collision._model) {
|
||||
void Avatar::applyCollision(CollisionInfo& collision) {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return;
|
||||
}
|
||||
if (collision._model == &(_head.getFaceModel())) {
|
||||
// TODO: make skeleton also respond to collisions
|
||||
Model* model = static_cast<Model*>(collision._data);
|
||||
if (model == &(_head.getFaceModel())) {
|
||||
_head.applyCollision(collision);
|
||||
}
|
||||
// TODO: make skeleton respond to collisions
|
||||
//if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
// _skeletonModel.applyCollision(collision);
|
||||
//}
|
||||
}
|
||||
|
||||
float Avatar::getPelvisFloatingHeight() const {
|
||||
|
|
|
@ -57,8 +57,6 @@ enum ScreenTintLayer {
|
|||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
typedef QVector<ModelCollisionInfo> ModelCollisionList;
|
||||
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found)
|
||||
// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by
|
||||
// Grayson as he's building a street around here for demo dinner 2
|
||||
|
@ -97,26 +95,19 @@ public:
|
|||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param collisions[out] a list of collisions
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionList& collisions, int skeletonSkipIndex = -1);
|
||||
CollisionList& collisions, int skeletonSkipIndex = -1);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar's (paddle) hands.
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
/// Checks for collision between the a spherical particle and the avatar (including paddle hands)
|
||||
/// \param collisionCenter the center of particle's bounding sphere
|
||||
/// \param collisionRadius the radius of particle's bounding sphere
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the particle collided
|
||||
bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions);
|
||||
|
||||
/// Checks for collision between the a sphere and the avatar's skeleton (including hand capsules).
|
||||
/// \param collisionCenter the center of the penetration test sphere
|
||||
/// \param collisionRadius the radius of the penetration test sphere
|
||||
/// \param collision[out] the details of the collision point
|
||||
/// \return whether or not the sphere collided
|
||||
//bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision);
|
||||
|
||||
virtual bool isMyAvatar() { return false; }
|
||||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
|
@ -126,13 +117,13 @@ public:
|
|||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool collisionWouldMoveAvatar(ModelCollisionInfo& collision) const;
|
||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
void applyCollision(ModelCollisionInfo& collision);
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
float getBoundingRadius() const { return 0.5f * getHeight(); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
@ -164,6 +155,7 @@ protected:
|
|||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
|
||||
float getHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
float getPelvisToHeadLength() const;
|
||||
|
||||
|
|
|
@ -125,6 +125,10 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
// We create a static CollisionList that is recycled for each collision test.
|
||||
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
||||
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
||||
|
||||
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
||||
if (!avatar || avatar == _owningAvatar) {
|
||||
// don't collide with our own hands (that is done elsewhere)
|
||||
|
@ -137,7 +141,6 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
continue;
|
||||
}
|
||||
glm::vec3 totalPenetration;
|
||||
ModelCollisionList collisions;
|
||||
if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) {
|
||||
// Check for palm collisions
|
||||
glm::vec3 myPalmPosition = palm.getPosition();
|
||||
|
@ -171,20 +174,22 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
handCollisions.clear();
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) {
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
if (isMyHand) {
|
||||
if (!avatar->collisionWouldMoveAvatar(collisions[j])) {
|
||||
if (!avatar->collisionWouldMoveAvatar(*collision)) {
|
||||
// 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, collisions[j]._penetration);
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
} else {
|
||||
// when !isMyHand then avatar is MyAvatar and we apply the collision
|
||||
// which might not do anything (hand hit unmovable part of MyAvatar) however
|
||||
// we don't resolve the hand's penetration because we expect the remote
|
||||
// simulation to do the right thing.
|
||||
avatar->applyCollision(collisions[j]);
|
||||
avatar->applyCollision(*collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +205,6 @@ void Hand::collideAgainstOurself() {
|
|||
return;
|
||||
}
|
||||
|
||||
ModelCollisionList collisions;
|
||||
int leftPalmIndex, rightPalmIndex;
|
||||
getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
|
||||
float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale();
|
||||
|
@ -210,16 +214,18 @@ void Hand::collideAgainstOurself() {
|
|||
if (!palm.isActive()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 totalPenetration;
|
||||
// and the current avatar (ignoring everything below the parent of the parent of the last free joint)
|
||||
collisions.clear();
|
||||
const Model& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
// ignoring everything below the parent of the parent of the last free joint
|
||||
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
|
||||
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
|
||||
handCollisions.clear();
|
||||
glm::vec3 totalPenetration;
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) {
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
|
||||
}
|
||||
}
|
||||
// resolve penetration
|
||||
|
|
|
@ -219,7 +219,7 @@ float Head::getTweakedRoll() const {
|
|||
return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
|
||||
}
|
||||
|
||||
void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
||||
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.
|
||||
|
@ -229,7 +229,7 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
|||
// 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(collisionInfo._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
||||
if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) {
|
||||
// compute lean angles
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
|
||||
glm::quat bodyRotation = owningAvatar->getOrientation();
|
||||
|
@ -239,8 +239,8 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) {
|
|||
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(collisionInfo._penetration, zAxis) / neckLength;
|
||||
float sideways = - glm::dot(collisionInfo._penetration, xAxis) / neckLength;
|
||||
float forward = glm::dot(collision._penetration, zAxis) / neckLength;
|
||||
float sideways = - glm::dot(collision._penetration, xAxis) / neckLength;
|
||||
addLean(sideways, forward);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
float getTweakedYaw() const;
|
||||
float getTweakedRoll() const;
|
||||
|
||||
void applyCollision(ModelCollisionInfo& collisionInfo);
|
||||
void applyCollision(CollisionInfo& collisionInfo);
|
||||
|
||||
private:
|
||||
// disallow copies of the Head, copy of owning Avatar is disallowed too
|
||||
|
|
|
@ -955,7 +955,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
float myBoundingRadius = 0.5f * getHeight();
|
||||
float myBoundingRadius = getBoundingRadius();
|
||||
|
||||
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||
// TODO: make the collision work without assuming avatar orientation
|
||||
|
@ -975,7 +975,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
if (_distanceToNearestAvatar > distance) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
float theirBoundingRadius = 0.5f * avatar->getHeight();
|
||||
float theirBoundingRadius = avatar->getBoundingRadius();
|
||||
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||
|
|
|
@ -45,5 +45,6 @@ private:
|
|||
TouchState _touchState;
|
||||
timeval* _lastReceivedPacket;
|
||||
|
||||
#endif /* defined(__hifi__Transmitter__) */
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Transmitter__) */
|
||||
|
|
|
@ -457,9 +457,9 @@ bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collisionInfo, float boneScale, int skipIndex) const {
|
||||
int jointIndex = -1;
|
||||
bool Model::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
CollisionList& collisions, float boneScale, int skipIndex) const {
|
||||
bool collided = false;
|
||||
const glm::vec3 relativeCenter = penetratorCenter - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::vec3 totalPenetration;
|
||||
|
@ -488,22 +488,22 @@ bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetra
|
|||
if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end,
|
||||
startRadius, endRadius, bonePenetration)) {
|
||||
totalPenetration = addPenetrations(totalPenetration, bonePenetration);
|
||||
// BUG: we currently overwrite the jointIndex with the last one found
|
||||
// which can cause incorrect collisions when colliding against more than
|
||||
// one joint.
|
||||
// TODO: fix this.
|
||||
jointIndex = i;
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
collision->_type = MODEL_COLLISION;
|
||||
collision->_data = (void*)(this);
|
||||
collision->_flags = i;
|
||||
collision->_contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration);
|
||||
collision->_penetration = totalPenetration;
|
||||
collided = true;
|
||||
} else {
|
||||
// collisions are full, so we might as well break
|
||||
break;
|
||||
}
|
||||
}
|
||||
outerContinue: ;
|
||||
}
|
||||
if (jointIndex != -1) {
|
||||
// don't store collisionInfo._model at this stage, let the outer context do that
|
||||
collisionInfo._penetration = totalPenetration;
|
||||
collisionInfo._jointIndex = jointIndex;
|
||||
collisionInfo._contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return collided;
|
||||
}
|
||||
|
||||
void Model::updateJointState(int index) {
|
||||
|
@ -736,24 +736,30 @@ void Model::renderCollisionProxies(float alpha) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool Model::collisionHitsMoveableJoint(ModelCollisionInfo& collision) const {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
|
||||
if (collision._type == MODEL_COLLISION) {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._flags];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int jointIndex = collision._flags;
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(collision._jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::applyCollision(ModelCollisionInfo& collision) {
|
||||
// This needs work. At the moment it can wiggle joints that are free to move (such as arms)
|
||||
// but unmovable joints (such as torso) cannot be influenced at all.
|
||||
void Model::applyCollision(CollisionInfo& collision) {
|
||||
if (collision._type != MODEL_COLLISION) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 jointPosition(0.f);
|
||||
if (getJointPosition(collision._jointIndex, jointPosition)) {
|
||||
int jointIndex = collision._jointIndex;
|
||||
int jointIndex = collision._flags;
|
||||
if (getJointPosition(jointIndex, jointPosition)) {
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
if (joint.parentIndex != -1) {
|
||||
// compute the approximate distance (travel) that the joint needs to move
|
||||
|
|
|
@ -17,16 +17,6 @@
|
|||
#include "ProgramObject.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
class Model;
|
||||
|
||||
// TODO: Andrew to move this into its own file
|
||||
class ModelCollisionInfo : public CollisionInfo {
|
||||
public:
|
||||
ModelCollisionInfo() : CollisionInfo(), _model(NULL), _jointIndex(-1) {}
|
||||
Model* _model;
|
||||
int _jointIndex;
|
||||
};
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -162,17 +152,18 @@ public:
|
|||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
bool findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
ModelCollisionInfo& collision, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
CollisionList& collisions, float boneScale = 1.0f, int skipIndex = -1) const;
|
||||
|
||||
void renderCollisionProxies(float alpha);
|
||||
|
||||
/// \param collision details about the collisions
|
||||
/// \return true if the collision is against a moveable joint
|
||||
bool collisionHitsMoveableJoint(ModelCollisionInfo& collision) const;
|
||||
bool collisionHitsMoveableJoint(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collisionInfo info about the collision
|
||||
/// Use the collisionInfo to affect the model
|
||||
void applyCollision(ModelCollisionInfo& collisionInfo);
|
||||
/// \param collision details about the collision
|
||||
/// Use the collision to affect the model
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
|
||||
// %1 <= username, %2 <= date and time, %3 <= current location
|
||||
|
@ -21,18 +20,69 @@ const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg";
|
|||
const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
|
||||
const QString SNAPSHOTS_DIRECTORY = "Snapshots";
|
||||
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location) {
|
||||
QImage shot = widget->grabFrameBuffer();
|
||||
const QString LOCATION_X = "location-x";
|
||||
const QString LOCATION_Y = "location-y";
|
||||
const QString LOCATION_Z = "location-z";
|
||||
|
||||
const QString ORIENTATION_X = "orientation-x";
|
||||
const QString ORIENTATION_Y = "orientation-y";
|
||||
const QString ORIENTATION_Z = "orientation-z";
|
||||
const QString ORIENTATION_W = "orientation-w";
|
||||
|
||||
const QString DOMAIN_KEY = "domain";
|
||||
|
||||
|
||||
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
||||
|
||||
if (!QFile(snapshotPath).exists()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QImage shot(snapshotPath);
|
||||
|
||||
// no location data stored
|
||||
if (shot.text(LOCATION_X).isEmpty() || shot.text(LOCATION_Y).isEmpty() || shot.text(LOCATION_Z).isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SnapshotMetaData* data = new SnapshotMetaData();
|
||||
data->setLocation(glm::vec3(shot.text(LOCATION_X).toFloat(),
|
||||
shot.text(LOCATION_Y).toFloat(),
|
||||
shot.text(LOCATION_Z).toFloat()));
|
||||
|
||||
data->setOrientation(glm::quat(shot.text(ORIENTATION_W).toFloat(),
|
||||
shot.text(ORIENTATION_X).toFloat(),
|
||||
shot.text(ORIENTATION_Y).toFloat(),
|
||||
shot.text(ORIENTATION_Z).toFloat()));
|
||||
|
||||
data->setDomain(shot.text(DOMAIN_KEY));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar) {
|
||||
QImage shot = widget->grabFrameBuffer();
|
||||
|
||||
glm::vec3 location = avatar->getPosition();
|
||||
glm::quat orientation = avatar->getHead().getOrientation();
|
||||
|
||||
// add metadata
|
||||
shot.setText("location-x", QString::number(location.x));
|
||||
shot.setText("location-y", QString::number(location.y));
|
||||
shot.setText("location-z", QString::number(location.z));
|
||||
shot.setText(LOCATION_X, QString::number(location.x));
|
||||
shot.setText(LOCATION_Y, QString::number(location.y));
|
||||
shot.setText(LOCATION_Z, QString::number(location.z));
|
||||
|
||||
shot.setText(ORIENTATION_X, QString::number(orientation.x));
|
||||
shot.setText(ORIENTATION_Y, QString::number(orientation.y));
|
||||
shot.setText(ORIENTATION_Z, QString::number(orientation.z));
|
||||
shot.setText(ORIENTATION_W, QString::number(orientation.w));
|
||||
|
||||
shot.setText(DOMAIN_KEY, profile->getLastDomain());
|
||||
|
||||
QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z);
|
||||
// replace decimal . with '-'
|
||||
formattedLocation.replace('.', '-');
|
||||
|
||||
QString username = profile->getUsername();
|
||||
// normalize username, replace all non alphanumeric with '-'
|
||||
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
|
||||
|
||||
|
|
|
@ -9,19 +9,38 @@
|
|||
#ifndef __hifi__Snapshot__
|
||||
#define __hifi__Snapshot__
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QGLWidget>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/Profile.h"
|
||||
|
||||
class SnapshotMetaData {
|
||||
public:
|
||||
|
||||
QString getDomain() { return _domain; }
|
||||
void setDomain(QString domain) { _domain = domain; }
|
||||
|
||||
glm::vec3 getLocation() { return _location; }
|
||||
void setLocation(glm::vec3 location) { _location = location; }
|
||||
|
||||
glm::quat getOrientation() { return _orientation; }
|
||||
void setOrientation(glm::quat orientation) { _orientation = orientation; }
|
||||
|
||||
private:
|
||||
QString _domain;
|
||||
glm::vec3 _location;
|
||||
glm::quat _orientation;;
|
||||
};
|
||||
|
||||
class Snapshot {
|
||||
|
||||
public:
|
||||
static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location);
|
||||
|
||||
private:
|
||||
QString _username;
|
||||
static void saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar);
|
||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Snapshot__) */
|
||||
|
|
|
@ -135,14 +135,10 @@ public:
|
|||
|
||||
virtual const glm::vec3& getVelocity() const { return vec3Zero; }
|
||||
|
||||
virtual bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasIdentityChangedAfterParsing(const QByteArray& packet);
|
||||
QByteArray identityByteArray();
|
||||
|
||||
|
@ -150,6 +146,8 @@ public:
|
|||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
|
||||
protected:
|
||||
glm::vec3 _position;
|
||||
|
|
|
@ -791,7 +791,6 @@ const char* OctreeSceneStats::getItemValue(Item item) {
|
|||
break;
|
||||
}
|
||||
default:
|
||||
sprintf(_itemValueBuffer, "");
|
||||
break;
|
||||
}
|
||||
return _itemValueBuffer;
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
#include "ParticleEditPacketSender.h"
|
||||
#include "ParticleTree.h"
|
||||
|
||||
const int MAX_COLLISIONS_PER_PARTICLE = 16;
|
||||
|
||||
ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender,
|
||||
ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio,
|
||||
AvatarHashMap* avatars) {
|
||||
AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_PARTICLE) {
|
||||
init(packetSender, particles, voxels, audio, avatars);
|
||||
}
|
||||
|
||||
|
@ -181,39 +183,53 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
const float COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
|
||||
_collisions.clear();
|
||||
foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
|
||||
AvatarData* avatar = avatarPointer.data();
|
||||
CollisionInfo collisionInfo;
|
||||
collisionInfo._damping = DAMPING;
|
||||
collisionInfo._elasticity = ELASTICITY;
|
||||
if (avatar->findSphereCollisionWithHands(center, radius, collisionInfo)) {
|
||||
// TODO: Andrew to resurrect particles-vs-avatar body collisions
|
||||
//avatar->findSphereCollisionWithSkeleton(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
// use a very generous bounding radius since the arms can stretch
|
||||
float totalRadius = 2.f * avatar->getBoundingRadius() + radius;
|
||||
glm::vec3 relativePosition = center - avatar->getPosition();
|
||||
if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (avatar->findParticleCollisions(center, radius, _collisions)) {
|
||||
int numCollisions = _collisions.size();
|
||||
for (int i = 0; i < numCollisions; ++i) {
|
||||
CollisionInfo* collision = _collisions.getCollision(i);
|
||||
collision->_damping = DAMPING;
|
||||
collision->_elasticity = ELASTICITY;
|
||||
|
||||
collision->_addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collision->_addedVelocity - particle->getVelocity();
|
||||
|
||||
if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) {
|
||||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them.
|
||||
if (collision->_type == PADDLE_HAND_COLLISION) {
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float attenuationFactor = glm::length(collision->_addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collision->_addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
}
|
||||
// HACK END
|
||||
|
||||
updateCollisionSound(particle, collision->_penetration, COLLISION_FREQUENCY);
|
||||
collision->_penetration /= (float)(TREE_SCALE);
|
||||
particle->applyHardCollision(*collision);
|
||||
queueParticlePropertiesUpdate(particle);
|
||||
}
|
||||
// HACK END
|
||||
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
particle->applyHardCollision(collisionInfo);
|
||||
queueParticlePropertiesUpdate(particle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ private:
|
|||
VoxelTree* _voxels;
|
||||
AbstractAudioInterface* _audio;
|
||||
AvatarHashMap* _avatars;
|
||||
CollisionList _collisions;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleCollisionSystem__) */
|
||||
|
|
42
libraries/shared/src/CollisionInfo.cpp
Normal file
42
libraries/shared/src/CollisionInfo.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// CollisionInfo.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Andrew Meadows on 2014.02.14
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CollisionInfo.h"
|
||||
|
||||
CollisionList::CollisionList(int maxSize) :
|
||||
_maxSize(maxSize),
|
||||
_size(0) {
|
||||
_collisions.resize(_maxSize);
|
||||
}
|
||||
|
||||
CollisionInfo* CollisionList::getNewCollision() {
|
||||
// return pointer to existing CollisionInfo, or NULL of list is full
|
||||
return (_size < _maxSize) ? &(_collisions[++_size]) : NULL;
|
||||
}
|
||||
|
||||
CollisionInfo* CollisionList::getCollision(int index) {
|
||||
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
|
||||
}
|
||||
|
||||
void CollisionList::clear() {
|
||||
for (int i = 0; i < _size; ++i) {
|
||||
// we only clear the important stuff
|
||||
CollisionInfo& collision = _collisions[i];
|
||||
collision._type = BASE_COLLISION;
|
||||
collision._data = NULL; // CollisionInfo does not own whatever this points to.
|
||||
collision._flags = 0;
|
||||
// we rely on the consumer to properly overwrite these fields when the collision is "created"
|
||||
//collision._damping;
|
||||
//collision._elasticity;
|
||||
//collision._contactPoint;
|
||||
//collision._penetration;
|
||||
//collision._addedVelocity;
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
|
@ -11,15 +11,42 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
const uint32_t COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
const uint32_t COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const uint32_t COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const uint32_t COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
#include <QVector>
|
||||
|
||||
enum CollisionType {
|
||||
BASE_COLLISION = 0,
|
||||
PADDLE_HAND_COLLISION,
|
||||
MODEL_COLLISION,
|
||||
};
|
||||
|
||||
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
||||
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const quint32 COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
|
||||
// CollisionInfo contains details about the collision between two things: BodyA and BodyB.
|
||||
// The assumption is that the context that analyzes the collision knows about BodyA but
|
||||
// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo
|
||||
// is expected to be relative to BodyA (for example the penetration points from A into B).
|
||||
|
||||
class CollisionInfo {
|
||||
public:
|
||||
CollisionInfo()
|
||||
: _damping(0.f),
|
||||
: _type(0),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
_penetration(0.f),
|
||||
_addedVelocity(0.f) {
|
||||
}
|
||||
|
||||
CollisionInfo(qint32 type)
|
||||
: _type(type),
|
||||
_data(NULL),
|
||||
_flags(0),
|
||||
_damping(0.f),
|
||||
_elasticity(1.f),
|
||||
_contactPoint(0.f),
|
||||
_penetration(0.f),
|
||||
|
@ -28,13 +55,40 @@ public:
|
|||
|
||||
~CollisionInfo() {}
|
||||
|
||||
//glm::vec3 _normal;
|
||||
float _damping;
|
||||
float _elasticity;
|
||||
glm::vec3 _contactPoint; // world-frame point on bodyA that is deepest into bodyB
|
||||
glm::vec3 _penetration; // depth that bodyA penetrates into bodyB
|
||||
glm::vec3 _addedVelocity;
|
||||
qint32 _type; // type of Collision (will determine what is supposed to be in _data and _flags)
|
||||
void* _data; // pointer to user supplied data
|
||||
quint32 _flags; // 32 bits for whatever
|
||||
|
||||
float _damping; // range [0,1] of friction coeficient
|
||||
float _elasticity; // range [0,1] of energy conservation
|
||||
glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB
|
||||
glm::vec3 _penetration; // depth that BodyA penetrates into BodyB
|
||||
glm::vec3 _addedVelocity; // velocity of BodyB
|
||||
};
|
||||
|
||||
// CollisionList is intended to be a recycled container. Fill the CollisionInfo's,
|
||||
// use them, and then clear them for the next frame or context.
|
||||
|
||||
class CollisionList {
|
||||
public:
|
||||
CollisionList(int maxSize);
|
||||
|
||||
/// \return pointer to next collision. NULL if list is full.
|
||||
CollisionInfo* getNewCollision();
|
||||
|
||||
/// \return pointer to collision by index. NULL if index out of bounds.
|
||||
CollisionInfo* getCollision(int index);
|
||||
|
||||
/// \return number of valid collisions
|
||||
int size() const { return _size; }
|
||||
|
||||
/// Clear valid collisions.
|
||||
void clear();
|
||||
|
||||
private:
|
||||
int _maxSize;
|
||||
int _size;
|
||||
QVector<CollisionInfo> _collisions;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__CollisionInfo__) */
|
||||
|
|
|
@ -807,7 +807,8 @@ void NodeList::loadData(QSettings *settings) {
|
|||
} else {
|
||||
_domainHostname = DEFAULT_DOMAIN_HOSTNAME;
|
||||
}
|
||||
|
||||
|
||||
emit domainChanged(_domainHostname);
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue