mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 18:41:10 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into emojiAppAdd
This commit is contained in:
commit
330b8ceb91
21 changed files with 419 additions and 200 deletions
|
@ -225,10 +225,6 @@ StackView {
|
||||||
verticalCenter: addressLineContainer.verticalCenter;
|
verticalCenter: addressLineContainer.verticalCenter;
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocusChanged: {
|
|
||||||
addressBarDialog.raised = focus;
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
updateLocationText(text.length > 0);
|
updateLocationText(text.length > 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4235,12 +4235,43 @@ bool Application::event(QEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||||
|
auto eventType = event->type();
|
||||||
|
|
||||||
if (_aboutToQuit && event->type() != QEvent::DeferredDelete && event->type() != QEvent::Destroy) {
|
if (_aboutToQuit && eventType != QEvent::DeferredDelete && eventType != QEvent::Destroy) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto eventType = event->type();
|
#if defined(Q_OS_MAC)
|
||||||
|
// On Mac OS, Cmd+LeftClick is treated as a RightClick (more specifically, it seems to
|
||||||
|
// be Cmd+RightClick without the modifier being dropped). Starting in Qt 5.12, only
|
||||||
|
// on Mac, the MouseButtonRelease event for these mouse presses is sent to the top
|
||||||
|
// level QWidgetWindow, but are not propagated further. This means that the Application
|
||||||
|
// will see a MouseButtonPress, but no MouseButtonRelease, causing the client to get
|
||||||
|
// stuck in "mouse-look." The cause of the problem is in the way QWidgetWindow processes
|
||||||
|
// events where QMouseEvent::button() is not equal to QMouseEvent::buttons(). In this case
|
||||||
|
// QMouseEvent::button() is Qt::RightButton, while QMouseEvent::buttons() is (correctly?)
|
||||||
|
// Qt::LeftButton.
|
||||||
|
//
|
||||||
|
// The change here gets around this problem by capturing these
|
||||||
|
// pseudo-RightClicks, and re-emitting them as "pure" RightClicks, where
|
||||||
|
// QMouseEvent::button() == QMouseEvent::buttons() == Qt::RightButton.
|
||||||
|
//
|
||||||
|
if (eventType == QEvent::MouseButtonPress) {
|
||||||
|
auto mouseEvent = static_cast<QMouseEvent*>(event);
|
||||||
|
if (mouseEvent->button() == Qt::RightButton
|
||||||
|
&& mouseEvent->buttons() == Qt::LeftButton
|
||||||
|
&& mouseEvent->modifiers() == Qt::MetaModifier) {
|
||||||
|
|
||||||
|
QMouseEvent* newEvent = new QMouseEvent(
|
||||||
|
QEvent::MouseButtonPress, mouseEvent->localPos(), mouseEvent->windowPos(),
|
||||||
|
mouseEvent->screenPos(), Qt::RightButton, Qt::MouseButtons(Qt::RightButton),
|
||||||
|
mouseEvent->modifiers());
|
||||||
|
QApplication::postEvent(object, newEvent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease || eventType == QEvent::MouseMove) {
|
if (eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease || eventType == QEvent::MouseMove) {
|
||||||
getRefreshRateManager().resetInactiveTimer();
|
getRefreshRateManager().resetInactiveTimer();
|
||||||
}
|
}
|
||||||
|
@ -6251,11 +6282,13 @@ void Application::tryToEnablePhysics() {
|
||||||
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
||||||
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
||||||
// scene is ready to compute its collision shape.
|
// scene is ready to compute its collision shape.
|
||||||
if (getMyAvatar()->isReadyForPhysics()) {
|
auto myAvatar = getMyAvatar();
|
||||||
|
if (myAvatar->isReadyForPhysics()) {
|
||||||
|
myAvatar->getCharacterController()->setPhysicsEngine(_physicsEngine);
|
||||||
_octreeProcessor.resetSafeLanding();
|
_octreeProcessor.resetSafeLanding();
|
||||||
_physicsEnabled = true;
|
_physicsEnabled = true;
|
||||||
setIsInterstitialMode(false);
|
setIsInterstitialMode(false);
|
||||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
myAvatar->updateMotionBehaviorFromMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6546,7 +6579,7 @@ void Application::update(float deltaTime) {
|
||||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||||
|
|
||||||
myAvatar->prepareForPhysicsSimulation();
|
myAvatar->prepareForPhysicsSimulation();
|
||||||
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
myAvatar->getCharacterController()->preSimulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6599,6 +6632,7 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "MyAvatar");
|
PROFILE_RANGE(simulation_physics, "MyAvatar");
|
||||||
|
myAvatar->getCharacterController()->postSimulation();
|
||||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -733,7 +733,7 @@ void MyAvatar::update(float deltaTime) {
|
||||||
// When needed and ready, arrange to check and fix.
|
// When needed and ready, arrange to check and fix.
|
||||||
_physicsSafetyPending = false;
|
_physicsSafetyPending = false;
|
||||||
if (_goToSafe) {
|
if (_goToSafe) {
|
||||||
safeLanding(_goToPosition); // no-op if already safe
|
safeLanding(_goToPosition); // no-op if safeLanding logic determines already safe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2733,24 +2733,22 @@ void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::quat orientation;
|
glm::quat orientation;
|
||||||
if (_characterController.isEnabledAndReady()) {
|
if (_characterController.isEnabledAndReady() && !(_characterController.needsSafeLandingSupport() || _goToPending)) {
|
||||||
_characterController.getPositionAndOrientation(position, orientation);
|
_characterController.getPositionAndOrientation(position, orientation);
|
||||||
|
setWorldVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
||||||
} else {
|
} else {
|
||||||
position = getWorldPosition();
|
position = getWorldPosition();
|
||||||
orientation = getWorldOrientation();
|
orientation = getWorldOrientation();
|
||||||
|
if (_characterController.needsSafeLandingSupport() && !_goToPending) {
|
||||||
|
_characterController.resetStuckCounter();
|
||||||
|
_physicsSafetyPending = true;
|
||||||
|
_goToSafe = true;
|
||||||
|
_goToPosition = position;
|
||||||
|
}
|
||||||
|
setWorldVelocity(getWorldVelocity() + _characterController.getFollowVelocity());
|
||||||
}
|
}
|
||||||
nextAttitude(position, orientation);
|
nextAttitude(position, orientation);
|
||||||
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||||
|
|
||||||
if (_characterController.isEnabledAndReady()) {
|
|
||||||
setWorldVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
|
||||||
if (_characterController.isStuck()) {
|
|
||||||
_physicsSafetyPending = true;
|
|
||||||
_goToPosition = getWorldPosition();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setWorldVelocity(getWorldVelocity() + _characterController.getFollowVelocity());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MyAvatar::getScriptedMotorFrame() const {
|
QString MyAvatar::getScriptedMotorFrame() const {
|
||||||
|
|
|
@ -36,10 +36,10 @@ MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar) {
|
||||||
MyCharacterController::~MyCharacterController() {
|
MyCharacterController::~MyCharacterController() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
void MyCharacterController::addToWorld() {
|
||||||
CharacterController::setDynamicsWorld(world);
|
CharacterController::addToWorld();
|
||||||
if (world && _rigidBody) {
|
if (_rigidBody) {
|
||||||
initRayShotgun(world);
|
initRayShotgun(_physicsEngine->getDynamicsWorld());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t MyCharacterController::computeCollisionMask() const {
|
int32_t MyCharacterController::computeCollisionMask() const {
|
||||||
int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR;
|
int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR;
|
||||||
if (_collisionless && _collisionlessAllowed) {
|
if (_collisionless && _collisionlessAllowed) {
|
||||||
collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS;
|
collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||||
} else if (!_collideWithOtherAvatars) {
|
} else if (!_collideWithOtherAvatars) {
|
||||||
|
@ -216,16 +216,17 @@ int32_t MyCharacterController::computeCollisionMask() const {
|
||||||
void MyCharacterController::handleChangedCollisionMask() {
|
void MyCharacterController::handleChangedCollisionMask() {
|
||||||
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) {
|
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) {
|
||||||
// ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody
|
// ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody
|
||||||
if (_dynamicsWorld) {
|
// but we don't do it here. Instead we set some flags to remind us to do it later.
|
||||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
_pendingFlags |= (PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_ADD_TO_SIMULATION);
|
||||||
int32_t collisionMask = computeCollisionMask();
|
|
||||||
_dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, collisionMask);
|
|
||||||
}
|
|
||||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK;
|
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||||
updateCurrentGravity();
|
updateCurrentGravity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MyCharacterController::needsSafeLandingSupport() const {
|
||||||
|
return _isStuck && _numStuckSubsteps >= NUM_SUBSTEPS_FOR_SAFE_LANDING_RETRY;
|
||||||
|
}
|
||||||
|
|
||||||
btConvexHullShape* MyCharacterController::computeShape() const {
|
btConvexHullShape* MyCharacterController::computeShape() const {
|
||||||
// HACK: the avatar collides using convex hull with a collision margin equal to
|
// HACK: the avatar collides using convex hull with a collision margin equal to
|
||||||
// the old capsule radius. Two points define a capsule and additional points are
|
// the old capsule radius. Two points define a capsule and additional points are
|
||||||
|
@ -447,12 +448,12 @@ public:
|
||||||
std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTest(const btVector3& origin, const btVector3& direction,
|
std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTest(const btVector3& origin, const btVector3& direction,
|
||||||
const btScalar& length, const QVector<uint>& jointsToExclude) const {
|
const btScalar& length, const QVector<uint>& jointsToExclude) const {
|
||||||
std::vector<RayAvatarResult> foundAvatars;
|
std::vector<RayAvatarResult> foundAvatars;
|
||||||
if (_dynamicsWorld) {
|
if (_physicsEngine) {
|
||||||
btVector3 end = origin + length * direction;
|
btVector3 end = origin + length * direction;
|
||||||
DetailedRayResultCallback rayCallback = DetailedRayResultCallback();
|
DetailedRayResultCallback rayCallback = DetailedRayResultCallback();
|
||||||
rayCallback.m_flags |= btTriangleRaycastCallback::kF_KeepUnflippedNormal;
|
rayCallback.m_flags |= btTriangleRaycastCallback::kF_KeepUnflippedNormal;
|
||||||
rayCallback.m_flags |= btTriangleRaycastCallback::kF_UseSubSimplexConvexCastRaytest;
|
rayCallback.m_flags |= btTriangleRaycastCallback::kF_UseSubSimplexConvexCastRaytest;
|
||||||
_dynamicsWorld->rayTest(origin, end, rayCallback);
|
_physicsEngine->getDynamicsWorld()->rayTest(origin, end, rayCallback);
|
||||||
if (rayCallback.m_hitFractions.size() > 0) {
|
if (rayCallback.m_hitFractions.size() > 0) {
|
||||||
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
||||||
for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar);
|
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar);
|
||||||
~MyCharacterController ();
|
~MyCharacterController ();
|
||||||
|
|
||||||
void setDynamicsWorld(btDynamicsWorld* world) override;
|
void addToWorld() override;
|
||||||
void updateShapeIfNecessary() override;
|
void updateShapeIfNecessary() override;
|
||||||
|
|
||||||
// Sweeping a convex shape through the physics simulation can be expensive when the obstacles are too
|
// Sweeping a convex shape through the physics simulation can be expensive when the obstacles are too
|
||||||
|
@ -69,9 +69,10 @@ public:
|
||||||
int32_t computeCollisionMask() const override;
|
int32_t computeCollisionMask() const override;
|
||||||
void handleChangedCollisionMask() override;
|
void handleChangedCollisionMask() override;
|
||||||
|
|
||||||
bool _collideWithOtherAvatars{ true };
|
|
||||||
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
|
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
|
||||||
|
|
||||||
|
bool needsSafeLandingSupport() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initRayShotgun(const btCollisionWorld* world);
|
void initRayShotgun(const btCollisionWorld* world);
|
||||||
void updateMassProperties() override;
|
void updateMassProperties() override;
|
||||||
|
@ -88,6 +89,7 @@ protected:
|
||||||
btScalar _density { 1.0f };
|
btScalar _density { 1.0f };
|
||||||
|
|
||||||
std::vector<DetailedMotionState*> _detailedMotionStates;
|
std::vector<DetailedMotionState*> _detailedMotionStates;
|
||||||
|
bool _collideWithOtherAvatars { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_MyCharacterController_h
|
#endif // hifi_MyCharacterController_h
|
||||||
|
|
|
@ -89,8 +89,14 @@ if ("${LAUNCHER_HMAC_SECRET}" STREQUAL "")
|
||||||
message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
|
message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Development environments don't set BUILD_VERSION. Let 0 mean a development version.
|
||||||
|
if(NOT BUILD_VERSION)
|
||||||
|
set(BUILD_VERSION 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
|
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_BUILD_VERSION="${BUILD_VERSION}")
|
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_BUILD_VERSION="${BUILD_VERSION}")
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE USER_AGENT_STRING="HQLauncher/${BUILD_VERSION}")
|
||||||
|
|
||||||
file(GLOB NIB_FILES "nib/*.xib")
|
file(GLOB NIB_FILES "nib/*.xib")
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||||
[request setURL:[NSURL URLWithString:@"https://metaverse.highfidelity.com/oauth/token"]];
|
[request setURL:[NSURL URLWithString:@"https://metaverse.highfidelity.com/oauth/token"]];
|
||||||
[request setHTTPMethod:@"POST"];
|
[request setHTTPMethod:@"POST"];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
||||||
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
||||||
[request setHTTPBody:postData];
|
[request setHTTPBody:postData];
|
||||||
|
|
|
@ -12,9 +12,10 @@
|
||||||
{
|
{
|
||||||
self.progressPercentage = 0.0;
|
self.progressPercentage = 0.0;
|
||||||
self.taskProgressPercentage = 0.0;
|
self.taskProgressPercentage = 0.0;
|
||||||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:domainContentUrl]
|
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:domainContentUrl]
|
||||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||||
timeoutInterval:60.0];
|
timeoutInterval:60.0];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
|
|
||||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
{
|
{
|
||||||
self.progressPercentage = 0.0;
|
self.progressPercentage = 0.0;
|
||||||
self.taskProgressPercentage = 0.0;
|
self.taskProgressPercentage = 0.0;
|
||||||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadUrl]
|
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:downloadUrl]
|
||||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||||
timeoutInterval:60.0];
|
timeoutInterval:60.0];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
|
|
||||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
@implementation DownloadLauncher
|
@implementation DownloadLauncher
|
||||||
|
|
||||||
- (void) downloadLauncher:(NSString*)launcherUrl {
|
- (void) downloadLauncher:(NSString*)launcherUrl {
|
||||||
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:launcherUrl]
|
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:launcherUrl]
|
||||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||||
timeoutInterval:60.0];
|
timeoutInterval:60.0];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
|
|
||||||
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
|
|
||||||
- (void) downloadScripts:(NSString*) scriptsUrl
|
- (void) downloadScripts:(NSString*) scriptsUrl
|
||||||
{
|
{
|
||||||
/*NSURLRequest* theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:scriptsUrl]
|
/*NSMutableURLRequest* theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:scriptsUrl]
|
||||||
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
cachePolicy:NSURLRequestUseProtocolCachePolicy
|
||||||
timeoutInterval:6000.0];
|
timeoutInterval:6000.0];
|
||||||
|
[theRequest setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
|
|
||||||
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self];
|
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self];
|
||||||
|
|
||||||
if (!theDownload) {
|
if (!theDownload) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
NSMutableURLRequest* request = [NSMutableURLRequest new];
|
NSMutableURLRequest* request = [NSMutableURLRequest new];
|
||||||
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
|
||||||
[request setHTTPMethod:@"GET"];
|
[request setHTTPMethod:@"GET"];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||||
|
|
||||||
// We're using an ephermeral session here to ensure the tags api response is never cached.
|
// We're using an ephermeral session here to ensure the tags api response is never cached.
|
||||||
|
|
|
@ -32,6 +32,7 @@ static NSString* const organizationURL = @"https://orgs.highfidelity.com/organiz
|
||||||
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
NSMutableURLRequest *request = [NSMutableURLRequest new];
|
||||||
[request setURL:[NSURL URLWithString:[organizationURL stringByAppendingString:jsonFile]]];
|
[request setURL:[NSURL URLWithString:[organizationURL stringByAppendingString:jsonFile]]];
|
||||||
[request setHTTPMethod:@"GET"];
|
[request setHTTPMethod:@"GET"];
|
||||||
|
[request setValue:@USER_AGENT_STRING forHTTPHeaderField:@"User-Agent"];
|
||||||
[request setValue:@"" forHTTPHeaderField:@"Content-Type"];
|
[request setValue:@"" forHTTPHeaderField:@"Content-Type"];
|
||||||
|
|
||||||
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
NSURLSession * session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: [NSOperationQueue mainQueue]];
|
||||||
|
|
|
@ -425,7 +425,15 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
packetStream << FingerprintUtils::getMachineFingerprint();
|
packetStream << FingerprintUtils::getMachineFingerprint();
|
||||||
|
|
||||||
auto desc = platform::getAll();
|
platform::json all = platform::getAll();
|
||||||
|
platform::json desc;
|
||||||
|
// only pull out those items that will fit within a packet
|
||||||
|
desc[platform::keys::COMPUTER] = all[platform::keys::COMPUTER];
|
||||||
|
desc[platform::keys::MEMORY] = all[platform::keys::MEMORY];
|
||||||
|
desc[platform::keys::CPUS] = all[platform::keys::CPUS];
|
||||||
|
desc[platform::keys::GPUS] = all[platform::keys::GPUS];
|
||||||
|
desc[platform::keys::DISPLAYS] = all[platform::keys::DISPLAYS];
|
||||||
|
desc[platform::keys::NICS] = all[platform::keys::NICS];
|
||||||
|
|
||||||
QByteArray systemInfo(desc.dump().c_str());
|
QByteArray systemInfo(desc.dump().c_str());
|
||||||
QByteArray compressedSystemInfo = qCompress(systemInfo);
|
QByteArray compressedSystemInfo = qCompress(systemInfo);
|
||||||
|
|
|
@ -11,14 +11,59 @@
|
||||||
|
|
||||||
#include "CharacterController.h"
|
#include "CharacterController.h"
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
|
||||||
#include <AvatarConstants.h>
|
#include <AvatarConstants.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
#include "TemporaryPairwiseCollisionFilter.h"
|
||||||
|
|
||||||
|
const float STUCK_PENETRATION = -0.05f; // always negative into the object.
|
||||||
|
const float STUCK_IMPULSE = 500.0f;
|
||||||
|
|
||||||
|
|
||||||
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
||||||
|
static bool _appliedStuckRecoveryStrategy = false;
|
||||||
|
|
||||||
|
static TemporaryPairwiseCollisionFilter _pairwiseFilter;
|
||||||
|
|
||||||
|
// Note: applyPairwiseFilter is registered as a sub-callback to Bullet's gContactAddedCallback feature
|
||||||
|
// when we detect MyAvatar is "stuck". It will disable new ManifoldPoints between MyAvatar and mesh objects with
|
||||||
|
// which it has deep penetration, and will continue disabling new contact until new contacts stop happening
|
||||||
|
// (no overlap). If MyAvatar is not trying to move its velocity is defaulted to "up", to help it escape overlap.
|
||||||
|
bool applyPairwiseFilter(btManifoldPoint& cp,
|
||||||
|
const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
|
||||||
|
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) {
|
||||||
|
static int32_t numCalls = 0;
|
||||||
|
++numCalls;
|
||||||
|
// This callback is ONLY called on objects with btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag
|
||||||
|
// and the flagged object will always be sorted to Obj0. Hence the "other" is always Obj1.
|
||||||
|
const btCollisionObject* other = colObj1Wrap->m_collisionObject;
|
||||||
|
|
||||||
|
if (_pairwiseFilter.isFiltered(other)) {
|
||||||
|
_pairwiseFilter.incrementEntry(other);
|
||||||
|
// disable contact point by setting distance too large and normal to zero
|
||||||
|
cp.setDistance(1.0e6f);
|
||||||
|
cp.m_normalWorldOnB.setValue(0.0f, 0.0f, 0.0f);
|
||||||
|
_appliedStuckRecoveryStrategy = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE) {
|
||||||
|
if ( cp.getDistance() < 2.0f * STUCK_PENETRATION ||
|
||||||
|
cp.getAppliedImpulse() > 2.0f * STUCK_IMPULSE ||
|
||||||
|
(cp.getDistance() < STUCK_PENETRATION && cp.getAppliedImpulse() > STUCK_IMPULSE)) {
|
||||||
|
_pairwiseFilter.incrementEntry(other);
|
||||||
|
// disable contact point by setting distance too large and normal to zero
|
||||||
|
cp.setDistance(1.0e6f);
|
||||||
|
cp.m_normalWorldOnB.setValue(0.0f, 0.0f, 0.0f);
|
||||||
|
_appliedStuckRecoveryStrategy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_STATE_CHANGE
|
#ifdef DEBUG_STATE_CHANGE
|
||||||
#define SET_STATE(desiredState, reason) setState(desiredState, reason)
|
#define SET_STATE(desiredState, reason) setState(desiredState, reason)
|
||||||
|
@ -60,6 +105,8 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t _numCharacterControllers { 0 };
|
||||||
|
|
||||||
CharacterController::CharacterController() {
|
CharacterController::CharacterController() {
|
||||||
_floorDistance = _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
_floorDistance = _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||||
|
|
||||||
|
@ -78,6 +125,11 @@ CharacterController::CharacterController() {
|
||||||
_hasSupport = false;
|
_hasSupport = false;
|
||||||
|
|
||||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||||
|
|
||||||
|
// ATM CharacterController is a singleton. When we want more we'll have to
|
||||||
|
// overhaul the applyPairwiseFilter() logic to handle multiple instances.
|
||||||
|
++_numCharacterControllers;
|
||||||
|
assert(numCharacterControllers == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterController::~CharacterController() {
|
CharacterController::~CharacterController() {
|
||||||
|
@ -92,64 +144,70 @@ CharacterController::~CharacterController() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::needsRemoval() const {
|
bool CharacterController::needsRemoval() const {
|
||||||
return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
return (_physicsEngine && (_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::needsAddition() const {
|
bool CharacterController::needsAddition() const {
|
||||||
return ((_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION);
|
return (_physicsEngine && (_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
void CharacterController::removeFromWorld() {
|
||||||
if (_dynamicsWorld != world) {
|
if (_inWorld) {
|
||||||
// remove from old world
|
|
||||||
if (_dynamicsWorld) {
|
|
||||||
if (_rigidBody) {
|
|
||||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
|
||||||
_dynamicsWorld->removeAction(this);
|
|
||||||
}
|
|
||||||
_dynamicsWorld = nullptr;
|
|
||||||
}
|
|
||||||
int32_t collisionMask = computeCollisionMask();
|
|
||||||
int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
|
|
||||||
if (_rigidBody) {
|
if (_rigidBody) {
|
||||||
updateMassProperties();
|
_physicsEngine->getDynamicsWorld()->removeRigidBody(_rigidBody);
|
||||||
}
|
_physicsEngine->getDynamicsWorld()->removeAction(this);
|
||||||
if (world && _rigidBody) {
|
|
||||||
// add to new world
|
|
||||||
_dynamicsWorld = world;
|
|
||||||
_pendingFlags &= ~PENDING_FLAG_JUMP;
|
|
||||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask);
|
|
||||||
_dynamicsWorld->addAction(this);
|
|
||||||
// restore gravity settings because adding an object to the world overwrites its gravity setting
|
|
||||||
_rigidBody->setGravity(_currentGravity * _currentUp);
|
|
||||||
// set flag to enable custom contactAddedCallback
|
|
||||||
_rigidBody->setCollisionFlags(btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
|
|
||||||
|
|
||||||
// enable CCD
|
|
||||||
_rigidBody->setCcdSweptSphereRadius(_radius);
|
|
||||||
_rigidBody->setCcdMotionThreshold(_radius);
|
|
||||||
|
|
||||||
btCollisionShape* shape = _rigidBody->getCollisionShape();
|
|
||||||
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
|
|
||||||
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
|
|
||||||
}
|
|
||||||
_ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup));
|
|
||||||
_ghost.setCollisionWorld(_dynamicsWorld);
|
|
||||||
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
|
||||||
if (_rigidBody) {
|
|
||||||
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
|
||||||
}
|
}
|
||||||
|
_inWorld = false;
|
||||||
}
|
}
|
||||||
if (_dynamicsWorld) {
|
_pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
||||||
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
}
|
||||||
// shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set
|
|
||||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION |
|
void CharacterController::addToWorld() {
|
||||||
PENDING_FLAG_ADD_DETAILED_TO_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
if (!_rigidBody) {
|
||||||
} else {
|
return;
|
||||||
_pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION;
|
}
|
||||||
}
|
if (_inWorld) {
|
||||||
|
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
btDiscreteDynamicsWorld* world = _physicsEngine->getDynamicsWorld();
|
||||||
|
int32_t collisionMask = computeCollisionMask();
|
||||||
|
int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||||
|
|
||||||
|
updateMassProperties();
|
||||||
|
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||||
|
|
||||||
|
// add to new world
|
||||||
|
_pendingFlags &= ~PENDING_FLAG_JUMP;
|
||||||
|
world->addRigidBody(_rigidBody, collisionGroup, collisionMask);
|
||||||
|
world->addAction(this);
|
||||||
|
_inWorld = true;
|
||||||
|
|
||||||
|
// restore gravity settings because adding an object to the world overwrites its gravity setting
|
||||||
|
_rigidBody->setGravity(_currentGravity * _currentUp);
|
||||||
|
// set flag to enable custom contactAddedCallback
|
||||||
|
_rigidBody->setCollisionFlags(btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
|
||||||
|
|
||||||
|
// enable CCD
|
||||||
|
_rigidBody->setCcdSweptSphereRadius(_radius);
|
||||||
|
_rigidBody->setCcdMotionThreshold(_radius);
|
||||||
|
|
||||||
|
btCollisionShape* shape = _rigidBody->getCollisionShape();
|
||||||
|
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
|
||||||
|
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
|
||||||
|
|
||||||
|
_ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup));
|
||||||
|
_ghost.setCollisionWorld(world);
|
||||||
|
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
||||||
|
if (_rigidBody) {
|
||||||
|
_ghost.setWorldTransform(_rigidBody->getWorldTransform());
|
||||||
|
}
|
||||||
|
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
||||||
|
// shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set
|
||||||
|
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION |
|
||||||
|
PENDING_FLAG_ADD_DETAILED_TO_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||||
} else {
|
} else {
|
||||||
_pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION;
|
_pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,17 +217,21 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
||||||
btDispatcher* dispatcher = collisionWorld->getDispatcher();
|
btDispatcher* dispatcher = collisionWorld->getDispatcher();
|
||||||
int numManifolds = dispatcher->getNumManifolds();
|
int numManifolds = dispatcher->getNumManifolds();
|
||||||
bool hasFloor = false;
|
bool hasFloor = false;
|
||||||
bool isStuck = false;
|
bool probablyStuck = _isStuck && _appliedStuckRecoveryStrategy;
|
||||||
|
|
||||||
btTransform rotation = _rigidBody->getWorldTransform();
|
btTransform rotation = _rigidBody->getWorldTransform();
|
||||||
rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part
|
rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part
|
||||||
|
|
||||||
|
float deepestDistance = 0.0f;
|
||||||
|
float strongestImpulse = 0.0f;
|
||||||
|
|
||||||
for (int i = 0; i < numManifolds; i++) {
|
for (int i = 0; i < numManifolds; i++) {
|
||||||
btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i);
|
btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i);
|
||||||
if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) {
|
if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) {
|
||||||
bool characterIsFirst = _rigidBody == contactManifold->getBody0();
|
bool characterIsFirst = _rigidBody == contactManifold->getBody0();
|
||||||
int numContacts = contactManifold->getNumContacts();
|
int numContacts = contactManifold->getNumContacts();
|
||||||
int stepContactIndex = -1;
|
int stepContactIndex = -1;
|
||||||
|
bool stepValid = true;
|
||||||
float highestStep = _minStepHeight;
|
float highestStep = _minStepHeight;
|
||||||
for (int j = 0; j < numContacts; j++) {
|
for (int j = 0; j < numContacts; j++) {
|
||||||
// check for "floor"
|
// check for "floor"
|
||||||
|
@ -177,28 +239,24 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
||||||
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
||||||
btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character
|
btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character
|
||||||
btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);
|
btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp);
|
||||||
// If there's non-trivial penetration with a big impulse for several steps, we're probably stuck.
|
|
||||||
// Note it here in the controller, and let MyAvatar figure out what to do about it.
|
float distance = contact.getDistance();
|
||||||
const float STUCK_PENETRATION = -0.05f; // always negative into the object.
|
if (distance < deepestDistance) {
|
||||||
const float STUCK_IMPULSE = 500.0f;
|
deepestDistance = distance;
|
||||||
const int STUCK_LIFETIME = 3;
|
|
||||||
if ((contact.getDistance() < STUCK_PENETRATION) && (contact.getAppliedImpulse() > STUCK_IMPULSE) && (contact.getLifeTime() > STUCK_LIFETIME)) {
|
|
||||||
isStuck = true; // latch on
|
|
||||||
}
|
}
|
||||||
|
float impulse = contact.getAppliedImpulse();
|
||||||
|
if (impulse > strongestImpulse) {
|
||||||
|
strongestImpulse = impulse;
|
||||||
|
}
|
||||||
|
|
||||||
if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) {
|
if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) {
|
||||||
hasFloor = true;
|
hasFloor = true;
|
||||||
if (!pushing && isStuck) {
|
|
||||||
// we're not pushing against anything and we're stuck so we can early exit
|
|
||||||
// (all we need to know is that there is a floor)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (pushing && _targetVelocity.dot(normal) < 0.0f) {
|
if (stepValid && pushing && _targetVelocity.dot(normal) < 0.0f) {
|
||||||
// remember highest step obstacle
|
// remember highest step obstacle
|
||||||
if (!_stepUpEnabled || hitHeight > _maxStepHeight) {
|
if (!_stepUpEnabled || hitHeight > _maxStepHeight) {
|
||||||
// this manifold is invalidated by point that is too high
|
// this manifold is invalidated by point that is too high
|
||||||
stepContactIndex = -1;
|
stepValid = false;
|
||||||
break;
|
|
||||||
} else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) {
|
} else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) {
|
||||||
highestStep = hitHeight;
|
highestStep = hitHeight;
|
||||||
stepContactIndex = j;
|
stepContactIndex = j;
|
||||||
|
@ -206,7 +264,7 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stepContactIndex > -1 && highestStep > _stepHeight) {
|
if (stepValid && stepContactIndex > -1 && highestStep > _stepHeight) {
|
||||||
// remember step info for later
|
// remember step info for later
|
||||||
btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex);
|
btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex);
|
||||||
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame
|
||||||
|
@ -214,13 +272,44 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) {
|
||||||
_stepHeight = highestStep;
|
_stepHeight = highestStep;
|
||||||
_stepPoint = rotation * pointOnCharacter; // rotate into world-frame
|
_stepPoint = rotation * pointOnCharacter; // rotate into world-frame
|
||||||
}
|
}
|
||||||
if (hasFloor && isStuck && !(pushing && _stepUpEnabled)) {
|
|
||||||
// early exit since all we need to know is that we're on a floor
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_isStuck = isStuck;
|
|
||||||
|
// If there's deep penetration and big impulse we're probably stuck.
|
||||||
|
probablyStuck = probablyStuck
|
||||||
|
|| deepestDistance < 2.0f * STUCK_PENETRATION
|
||||||
|
|| strongestImpulse > 2.0f * STUCK_IMPULSE
|
||||||
|
|| (deepestDistance < STUCK_PENETRATION && strongestImpulse > STUCK_IMPULSE);
|
||||||
|
|
||||||
|
if (_isStuck != probablyStuck) {
|
||||||
|
++_stuckTransitionCount;
|
||||||
|
if (_stuckTransitionCount > NUM_SUBSTEPS_FOR_STUCK_TRANSITION) {
|
||||||
|
// we've been in this "probablyStuck" state for several consecutive substeps
|
||||||
|
// --> make it official by changing state
|
||||||
|
qCDebug(physics) << "CharacterController::_isStuck :" << _isStuck << "-->" << probablyStuck;
|
||||||
|
_isStuck = probablyStuck;
|
||||||
|
// init _numStuckSubsteps at NUM_SUBSTEPS_FOR_SAFE_LANDING_RETRY so SafeLanding tries to help immediately
|
||||||
|
_numStuckSubsteps = NUM_SUBSTEPS_FOR_SAFE_LANDING_RETRY;
|
||||||
|
_stuckTransitionCount = 0;
|
||||||
|
if (_isStuck) {
|
||||||
|
// enable pairwise filter
|
||||||
|
_physicsEngine->setContactAddedCallback(applyPairwiseFilter);
|
||||||
|
_pairwiseFilter.incrementStepCount();
|
||||||
|
} else {
|
||||||
|
// disable pairwise filter
|
||||||
|
_physicsEngine->setContactAddedCallback(nullptr);
|
||||||
|
_appliedStuckRecoveryStrategy = false;
|
||||||
|
_pairwiseFilter.clearAllEntries();
|
||||||
|
}
|
||||||
|
updateCurrentGravity();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_stuckTransitionCount = 0;
|
||||||
|
if (_isStuck) {
|
||||||
|
++_numStuckSubsteps;
|
||||||
|
_appliedStuckRecoveryStrategy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return hasFloor;
|
return hasFloor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +483,7 @@ static const char* stateToStr(CharacterController::State state) {
|
||||||
|
|
||||||
void CharacterController::updateCurrentGravity() {
|
void CharacterController::updateCurrentGravity() {
|
||||||
int32_t collisionMask = computeCollisionMask();
|
int32_t collisionMask = computeCollisionMask();
|
||||||
if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS || _isStuck) {
|
||||||
_currentGravity = 0.0f;
|
_currentGravity = 0.0f;
|
||||||
} else {
|
} else {
|
||||||
_currentGravity = _gravity;
|
_currentGravity = _gravity;
|
||||||
|
@ -452,7 +541,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
||||||
_minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
_minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
||||||
_maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
_maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
||||||
|
|
||||||
if (_dynamicsWorld) {
|
if (_physicsEngine) {
|
||||||
// must REMOVE from world prior to shape update
|
// must REMOVE from world prior to shape update
|
||||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||||
}
|
}
|
||||||
|
@ -470,6 +559,15 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterController::setPhysicsEngine(const PhysicsEnginePointer& engine) {
|
||||||
|
if (!_physicsEngine && engine) {
|
||||||
|
// ATM there is only one PhysicsEngine: it is a singleton, and we are taking advantage
|
||||||
|
// of that assumption here. If we ever introduce more and allow for this backpointer
|
||||||
|
// to change then we'll have to overhaul this method.
|
||||||
|
_physicsEngine = engine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CharacterController::setCollisionless(bool collisionless) {
|
void CharacterController::setCollisionless(bool collisionless) {
|
||||||
if (collisionless != _collisionless) {
|
if (collisionless != _collisionless) {
|
||||||
_collisionless = collisionless;
|
_collisionless = collisionless;
|
||||||
|
@ -634,6 +732,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) {
|
void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) {
|
||||||
|
btVector3 currentVelocity = velocity;
|
||||||
|
|
||||||
if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||||
velocity = btVector3(0.0f, 0.0f, 0.0f);
|
velocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
@ -665,6 +765,12 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) {
|
||||||
if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||||
velocity = btVector3(0.0f, 0.0f, 0.0f);
|
velocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
if (_isStuck && _targetVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||||
|
// we're stuck, but not trying to move --> move UP by default
|
||||||
|
// in the hopes we'll get unstuck
|
||||||
|
const float STUCK_EXTRACTION_SPEED = 1.0f;
|
||||||
|
velocity = STUCK_EXTRACTION_SPEED * _currentUp;
|
||||||
|
}
|
||||||
|
|
||||||
// 'thrust' is applied at the very end
|
// 'thrust' is applied at the very end
|
||||||
_targetVelocity += dt * _linearAcceleration;
|
_targetVelocity += dt * _linearAcceleration;
|
||||||
|
@ -672,6 +778,14 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) {
|
||||||
// Note the differences between these two variables:
|
// Note the differences between these two variables:
|
||||||
// _targetVelocity = ideal final velocity according to input
|
// _targetVelocity = ideal final velocity according to input
|
||||||
// velocity = real final velocity after motors are applied to current velocity
|
// velocity = real final velocity after motors are applied to current velocity
|
||||||
|
|
||||||
|
bool gettingStuck = !_isStuck && _stuckTransitionCount > 1 && _state == State::Hover;
|
||||||
|
if (gettingStuck && velocity.length2() > currentVelocity.length2()) {
|
||||||
|
// we are probably trying to fly fast into a mesh obstacle
|
||||||
|
// which is causing us to tickle the "stuck" detection code
|
||||||
|
// so we average our new velocity with currentVeocity to prevent a "safe landing" response
|
||||||
|
velocity = 0.5f * (velocity + currentVelocity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) {
|
void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) {
|
||||||
|
@ -681,7 +795,7 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::updateState() {
|
void CharacterController::updateState() {
|
||||||
if (!_dynamicsWorld) {
|
if (!_physicsEngine) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) {
|
if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) {
|
||||||
|
@ -712,7 +826,7 @@ void CharacterController::updateState() {
|
||||||
|
|
||||||
ClosestNotMe rayCallback(_rigidBody);
|
ClosestNotMe rayCallback(_rigidBody);
|
||||||
rayCallback.m_closestHitFraction = 1.0f;
|
rayCallback.m_closestHitFraction = 1.0f;
|
||||||
_dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback);
|
_physicsEngine->getDynamicsWorld()->rayTest(rayStart, rayEnd, rayCallback);
|
||||||
bool rayHasHit = rayCallback.hasHit();
|
bool rayHasHit = rayCallback.hasHit();
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
if (rayHasHit) {
|
if (rayHasHit) {
|
||||||
|
@ -829,6 +943,21 @@ void CharacterController::updateState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::preSimulation() {
|
void CharacterController::preSimulation() {
|
||||||
|
if (needsRemoval()) {
|
||||||
|
removeFromWorld();
|
||||||
|
|
||||||
|
// We must remove any existing contacts for the avatar so that any new contacts will have
|
||||||
|
// valid data. MyAvatar's RigidBody is the ONLY one in the simulation that does not yet
|
||||||
|
// have a MotionState so we pass nullptr to removeContacts().
|
||||||
|
if (_physicsEngine) {
|
||||||
|
_physicsEngine->removeContacts(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateShapeIfNecessary();
|
||||||
|
if (needsAddition()) {
|
||||||
|
addToWorld();
|
||||||
|
}
|
||||||
|
|
||||||
if (_rigidBody) {
|
if (_rigidBody) {
|
||||||
// slam body transform and remember velocity
|
// slam body transform and remember velocity
|
||||||
_rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position)));
|
_rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position)));
|
||||||
|
@ -843,6 +972,11 @@ void CharacterController::preSimulation() {
|
||||||
_followTime = 0.0f;
|
_followTime = 0.0f;
|
||||||
_followLinearDisplacement = btVector3(0, 0, 0);
|
_followLinearDisplacement = btVector3(0, 0, 0);
|
||||||
_followAngularDisplacement = btQuaternion::getIdentity();
|
_followAngularDisplacement = btQuaternion::getIdentity();
|
||||||
|
|
||||||
|
if (_isStuck) {
|
||||||
|
_pairwiseFilter.expireOldEntries();
|
||||||
|
_pairwiseFilter.incrementStepCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::postSimulation() {
|
void CharacterController::postSimulation() {
|
||||||
|
|
|
@ -20,11 +20,12 @@
|
||||||
|
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <PhysicsCollisionGroups.h>
|
|
||||||
|
|
||||||
|
#include "AvatarConstants.h"
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "CharacterGhostObject.h"
|
#include "CharacterGhostObject.h"
|
||||||
#include "AvatarConstants.h"
|
#include "PhysicsEngine.h"
|
||||||
|
#include "PhysicsHelpers.h"
|
||||||
|
|
||||||
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
||||||
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
||||||
|
@ -37,6 +38,9 @@ const uint32_t PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION = 1U << 7;
|
||||||
|
|
||||||
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
|
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
|
||||||
|
|
||||||
|
const uint32_t NUM_SUBSTEPS_FOR_STUCK_TRANSITION = 6; // physics substeps
|
||||||
|
const uint32_t NUM_SUBSTEPS_FOR_SAFE_LANDING_RETRY = NUM_SUBSTEPS_PER_SECOND / 2; // retry every half second
|
||||||
|
|
||||||
class btRigidBody;
|
class btRigidBody;
|
||||||
class btCollisionWorld;
|
class btCollisionWorld;
|
||||||
class btDynamicsWorld;
|
class btDynamicsWorld;
|
||||||
|
@ -53,7 +57,8 @@ public:
|
||||||
virtual ~CharacterController();
|
virtual ~CharacterController();
|
||||||
bool needsRemoval() const;
|
bool needsRemoval() const;
|
||||||
bool needsAddition() const;
|
bool needsAddition() const;
|
||||||
virtual void setDynamicsWorld(btDynamicsWorld* world);
|
virtual void addToWorld();
|
||||||
|
void removeFromWorld();
|
||||||
btCollisionObject* getCollisionObject() { return _rigidBody; }
|
btCollisionObject* getCollisionObject() { return _rigidBody; }
|
||||||
|
|
||||||
void setGravity(float gravity);
|
void setGravity(float gravity);
|
||||||
|
@ -120,7 +125,8 @@ public:
|
||||||
|
|
||||||
void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale);
|
void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale);
|
||||||
|
|
||||||
bool isEnabledAndReady() const { return _dynamicsWorld; }
|
void setPhysicsEngine(const PhysicsEnginePointer& engine);
|
||||||
|
bool isEnabledAndReady() const { return (bool)_physicsEngine; }
|
||||||
bool isStuck() const { return _isStuck; }
|
bool isStuck() const { return _isStuck; }
|
||||||
|
|
||||||
void setCollisionless(bool collisionless);
|
void setCollisionless(bool collisionless);
|
||||||
|
@ -139,6 +145,8 @@ public:
|
||||||
void setSeated(bool isSeated) { _isSeated = isSeated; }
|
void setSeated(bool isSeated) { _isSeated = isSeated; }
|
||||||
bool getSeated() { return _isSeated; }
|
bool getSeated() { return _isSeated; }
|
||||||
|
|
||||||
|
void resetStuckCounter() { _numStuckSubsteps = 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef DEBUG_STATE_CHANGE
|
#ifdef DEBUG_STATE_CHANGE
|
||||||
void setState(State state, const char* reason);
|
void setState(State state, const char* reason);
|
||||||
|
@ -187,7 +195,6 @@ protected:
|
||||||
// data for walking up steps
|
// data for walking up steps
|
||||||
btVector3 _stepPoint { 0.0f, 0.0f, 0.0f };
|
btVector3 _stepPoint { 0.0f, 0.0f, 0.0f };
|
||||||
btVector3 _stepNormal { 0.0f, 0.0f, 0.0f };
|
btVector3 _stepNormal { 0.0f, 0.0f, 0.0f };
|
||||||
bool _steppingUp { false };
|
|
||||||
btScalar _stepHeight { 0.0f };
|
btScalar _stepHeight { 0.0f };
|
||||||
btScalar _minStepHeight { 0.0f };
|
btScalar _minStepHeight { 0.0f };
|
||||||
btScalar _maxStepHeight { 0.0f };
|
btScalar _maxStepHeight { 0.0f };
|
||||||
|
@ -197,6 +204,7 @@ protected:
|
||||||
btScalar _radius { 0.0f };
|
btScalar _radius { 0.0f };
|
||||||
|
|
||||||
btScalar _floorDistance;
|
btScalar _floorDistance;
|
||||||
|
bool _steppingUp { false };
|
||||||
bool _stepUpEnabled { true };
|
bool _stepUpEnabled { true };
|
||||||
bool _hasSupport;
|
bool _hasSupport;
|
||||||
|
|
||||||
|
@ -213,11 +221,14 @@ protected:
|
||||||
bool _isStuck { false };
|
bool _isStuck { false };
|
||||||
bool _isSeated { false };
|
bool _isSeated { false };
|
||||||
|
|
||||||
btDynamicsWorld* _dynamicsWorld { nullptr };
|
PhysicsEnginePointer _physicsEngine { nullptr };
|
||||||
btRigidBody* _rigidBody { nullptr };
|
btRigidBody* _rigidBody { nullptr };
|
||||||
uint32_t _pendingFlags { 0 };
|
uint32_t _pendingFlags { 0 };
|
||||||
uint32_t _previousFlags { 0 };
|
uint32_t _previousFlags { 0 };
|
||||||
|
uint32_t _stuckTransitionCount { 0 };
|
||||||
|
uint32_t _numStuckSubsteps { 0 };
|
||||||
|
|
||||||
|
bool _inWorld { false };
|
||||||
bool _zoneFlyingAllowed { true };
|
bool _zoneFlyingAllowed { true };
|
||||||
bool _comfortFlyingAllowed { true };
|
bool _comfortFlyingAllowed { true };
|
||||||
bool _hoverWhenUnsupported{ true };
|
bool _hoverWhenUnsupported{ true };
|
||||||
|
|
|
@ -27,33 +27,13 @@
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
static bool flipNormalsMyAvatarVsBackfacingTriangles(btManifoldPoint& cp,
|
|
||||||
const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
|
|
||||||
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) {
|
|
||||||
if (colObj1Wrap->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE) {
|
|
||||||
auto triShape = static_cast<const btTriangleShape*>(colObj1Wrap->getCollisionShape());
|
|
||||||
const btVector3* v = triShape->m_vertices1;
|
|
||||||
btVector3 faceNormal = colObj1Wrap->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]);
|
|
||||||
float nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
|
|
||||||
if (nDotF <= 0.0f && faceNormal.length2() > EPSILON) {
|
|
||||||
faceNormal.normalize();
|
|
||||||
// flip the contact normal to be aligned with the face normal
|
|
||||||
cp.m_normalWorldOnB += -2.0f * nDotF * faceNormal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// return value is currently ignored but to be future-proof: return false when not modifying friction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
PhysicsEngine::PhysicsEngine(const glm::vec3& offset) :
|
||||||
_originOffset(offset),
|
_originOffset(offset),
|
||||||
_myAvatarController(nullptr) {
|
_myAvatarController(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsEngine::~PhysicsEngine() {
|
PhysicsEngine::~PhysicsEngine() {
|
||||||
if (_myAvatarController) {
|
_myAvatarController = nullptr;
|
||||||
_myAvatarController->setDynamicsWorld(nullptr);
|
|
||||||
}
|
|
||||||
delete _collisionConfig;
|
delete _collisionConfig;
|
||||||
delete _collisionDispatcher;
|
delete _collisionDispatcher;
|
||||||
delete _broadphaseFilter;
|
delete _broadphaseFilter;
|
||||||
|
@ -335,27 +315,6 @@ void PhysicsEngine::stepSimulation() {
|
||||||
_clock.reset();
|
_clock.reset();
|
||||||
float timeStep = btMin(dt, MAX_TIMESTEP);
|
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||||
|
|
||||||
if (_myAvatarController) {
|
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "avatarController");
|
|
||||||
BT_PROFILE("avatarController");
|
|
||||||
// TODO: move this stuff outside and in front of stepSimulation, because
|
|
||||||
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
|
||||||
// be done on the main thread during the pre-simulation stuff
|
|
||||||
if (_myAvatarController->needsRemoval()) {
|
|
||||||
_myAvatarController->setDynamicsWorld(nullptr);
|
|
||||||
|
|
||||||
// We must remove any existing contacts for the avatar so that any new contacts will have
|
|
||||||
// valid data. MyAvatar's RigidBody is the ONLY one in the simulation that does not yet
|
|
||||||
// have a MotionState so we pass nullptr to removeContacts().
|
|
||||||
removeContacts(nullptr);
|
|
||||||
}
|
|
||||||
_myAvatarController->updateShapeIfNecessary();
|
|
||||||
if (_myAvatarController->needsAddition()) {
|
|
||||||
_myAvatarController->setDynamicsWorld(_dynamicsWorld);
|
|
||||||
}
|
|
||||||
_myAvatarController->preSimulation();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto onSubStep = [this]() {
|
auto onSubStep = [this]() {
|
||||||
this->updateContactMap();
|
this->updateContactMap();
|
||||||
this->doOwnershipInfectionForConstraints();
|
this->doOwnershipInfectionForConstraints();
|
||||||
|
@ -364,15 +323,11 @@ void PhysicsEngine::stepSimulation() {
|
||||||
int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS,
|
int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS,
|
||||||
PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep);
|
PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep);
|
||||||
if (numSubsteps > 0) {
|
if (numSubsteps > 0) {
|
||||||
BT_PROFILE("postSimulation");
|
|
||||||
if (_myAvatarController) {
|
|
||||||
_myAvatarController->postSimulation();
|
|
||||||
}
|
|
||||||
_hasOutgoingChanges = true;
|
_hasOutgoingChanges = true;
|
||||||
}
|
if (_physicsDebugDraw->getDebugMode()) {
|
||||||
|
BT_PROFILE("debugDrawWorld");
|
||||||
if (_physicsDebugDraw->getDebugMode()) {
|
_dynamicsWorld->debugDrawWorld();
|
||||||
_dynamicsWorld->debugDrawWorld();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,15 +689,7 @@ void PhysicsEngine::bumpAndPruneContacts(ObjectMotionState* motionState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::setCharacterController(CharacterController* character) {
|
void PhysicsEngine::setCharacterController(CharacterController* character) {
|
||||||
if (_myAvatarController != character) {
|
_myAvatarController = character;
|
||||||
if (_myAvatarController) {
|
|
||||||
// remove the character from the DynamicsWorld immediately
|
|
||||||
_myAvatarController->setDynamicsWorld(nullptr);
|
|
||||||
_myAvatarController = nullptr;
|
|
||||||
}
|
|
||||||
// the character will be added to the DynamicsWorld later
|
|
||||||
_myAvatarController = character;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityDynamicPointer PhysicsEngine::getDynamicByID(const QUuid& dynamicID) const {
|
EntityDynamicPointer PhysicsEngine::getDynamicByID(const QUuid& dynamicID) const {
|
||||||
|
@ -872,14 +819,11 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::enableGlobalContactAddedCallback(bool enabled) {
|
void PhysicsEngine::setContactAddedCallback(PhysicsEngine::ContactAddedCallback newCb) {
|
||||||
if (enabled) {
|
// gContactAddedCallback is a special feature hook in Bullet
|
||||||
// register contact filter to help MyAvatar pass through backfacing triangles
|
// if non-null AND one of the colliding objects has btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK flag set
|
||||||
gContactAddedCallback = flipNormalsMyAvatarVsBackfacingTriangles;
|
// then it is called whenever a new candidate contact point is created
|
||||||
} else {
|
gContactAddedCallback = newCb;
|
||||||
// deregister contact filter
|
|
||||||
gContactAddedCallback = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||||
|
|
|
@ -74,6 +74,10 @@ using CollisionEvents = std::vector<Collision>;
|
||||||
|
|
||||||
class PhysicsEngine {
|
class PhysicsEngine {
|
||||||
public:
|
public:
|
||||||
|
using ContactAddedCallback = bool (*)(btManifoldPoint& cp,
|
||||||
|
const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
|
||||||
|
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1);
|
||||||
|
|
||||||
class Transaction {
|
class Transaction {
|
||||||
public:
|
public:
|
||||||
void clear() {
|
void clear() {
|
||||||
|
@ -150,7 +154,10 @@ public:
|
||||||
// See PhysicsCollisionGroups.h for mask flags.
|
// See PhysicsCollisionGroups.h for mask flags.
|
||||||
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const;
|
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const;
|
||||||
|
|
||||||
void enableGlobalContactAddedCallback(bool enabled);
|
void setContactAddedCallback(ContactAddedCallback cb);
|
||||||
|
|
||||||
|
btDiscreteDynamicsWorld* getDynamicsWorld() const { return _dynamicsWorld; }
|
||||||
|
void removeContacts(ObjectMotionState* motionState);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||||
|
@ -159,8 +166,6 @@ private:
|
||||||
/// \brief bump any objects that touch this one, then remove contact info
|
/// \brief bump any objects that touch this one, then remove contact info
|
||||||
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
||||||
|
|
||||||
void removeContacts(ObjectMotionState* motionState);
|
|
||||||
|
|
||||||
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
|
void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB);
|
||||||
|
|
||||||
btClock _clock;
|
btClock _clock;
|
||||||
|
|
36
libraries/physics/src/TemporaryPairwiseCollisionFilter.cpp
Executable file
36
libraries/physics/src/TemporaryPairwiseCollisionFilter.cpp
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// TemporaryPairwiseCollisionFilter.cpp
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2019.08.12
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TemporaryPairwiseCollisionFilter.h"
|
||||||
|
|
||||||
|
bool TemporaryPairwiseCollisionFilter::isFiltered(const btCollisionObject* object) const {
|
||||||
|
return _filteredContacts.find(object) != _filteredContacts.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemporaryPairwiseCollisionFilter::incrementEntry(const btCollisionObject* object) {
|
||||||
|
LastContactMap::iterator itr = _filteredContacts.find(object);
|
||||||
|
if (itr == _filteredContacts.end()) {
|
||||||
|
_filteredContacts.emplace(std::make_pair(object, _stepCount));
|
||||||
|
} else {
|
||||||
|
itr->second = _stepCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemporaryPairwiseCollisionFilter::expireOldEntries() {
|
||||||
|
LastContactMap::iterator itr = _filteredContacts.begin();
|
||||||
|
while (itr != _filteredContacts.end()) {
|
||||||
|
if (itr->second < _stepCount) {
|
||||||
|
itr = _filteredContacts.erase(itr);
|
||||||
|
} else {
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
libraries/physics/src/TemporaryPairwiseCollisionFilter.h
Executable file
35
libraries/physics/src/TemporaryPairwiseCollisionFilter.h
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// TemporaryPairwiseCollisionFilter.h
|
||||||
|
// libraries/physics/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2019.08.12
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_TemporaryPairwiseCollisionFilter_h
|
||||||
|
#define hifi_TemporaryPairwiseCollisionFilter_h
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
|
||||||
|
class TemporaryPairwiseCollisionFilter {
|
||||||
|
public:
|
||||||
|
using LastContactMap = std::unordered_map<const btCollisionObject*, uint32_t>;
|
||||||
|
|
||||||
|
TemporaryPairwiseCollisionFilter() { }
|
||||||
|
|
||||||
|
bool isFiltered(const btCollisionObject* object) const;
|
||||||
|
void incrementEntry(const btCollisionObject* object);
|
||||||
|
void expireOldEntries();
|
||||||
|
void clearAllEntries() { _filteredContacts.clear(); _stepCount = 0; }
|
||||||
|
void incrementStepCount() { ++_stepCount; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LastContactMap _filteredContacts;
|
||||||
|
uint32_t _stepCount { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_TemporaryPairwiseCollisionFilter_h
|
|
@ -19,8 +19,9 @@
|
||||||
// TODO: move everything in here to the physics library after the physics/entities library
|
// TODO: move everything in here to the physics library after the physics/entities library
|
||||||
// dependency order is swapped.
|
// dependency order is swapped.
|
||||||
|
|
||||||
const int PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
|
const int32_t PHYSICS_ENGINE_MAX_NUM_SUBSTEPS = 6; // Bullet will start to "lose time" at 10 FPS.
|
||||||
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 90.0f;
|
const uint32_t NUM_SUBSTEPS_PER_SECOND = 90;
|
||||||
|
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / (float)NUM_SUBSTEPS_PER_SECOND;
|
||||||
|
|
||||||
const float DYNAMIC_LINEAR_SPEED_THRESHOLD = 0.05f; // 5 cm/sec
|
const float DYNAMIC_LINEAR_SPEED_THRESHOLD = 0.05f; // 5 cm/sec
|
||||||
const float DYNAMIC_ANGULAR_SPEED_THRESHOLD = 0.087266f; // ~5 deg/sec
|
const float DYNAMIC_ANGULAR_SPEED_THRESHOLD = 0.087266f; // ~5 deg/sec
|
||||||
|
|
Loading…
Reference in a new issue