mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 00:47:33 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into queueDepthDebug
This commit is contained in:
commit
0319e8cc4d
151 changed files with 2477 additions and 1224 deletions
|
@ -62,7 +62,7 @@ endif()
|
|||
# Use default time server if none defined in environment
|
||||
set_from_env(TIMESERVER_URL TIMESERVER_URL "http://sha256timestamp.ws.symantec.com/sha256/timestamp")
|
||||
|
||||
set(HIFI_USE_OPTIMIZED_IK OFF)
|
||||
set(HIFI_USE_OPTIMIZED_IK_OPTION OFF)
|
||||
set(BUILD_CLIENT_OPTION ON)
|
||||
set(BUILD_SERVER_OPTION ON)
|
||||
set(BUILD_TESTS_OPTION OFF)
|
||||
|
@ -126,7 +126,7 @@ if (USE_GLES AND (NOT ANDROID))
|
|||
set(DISABLE_QML_OPTION ON)
|
||||
endif()
|
||||
|
||||
option(HIFI_USE_OPTIMIZED_IK "USE OPTIMIZED IK" ${HIFI_USE_OPTIMIZED_IK_OPTION})
|
||||
option(HIFI_USE_OPTIMIZED_IK "Use optimized IK" ${HIFI_USE_OPTIMIZED_IK_OPTION})
|
||||
option(BUILD_CLIENT "Build client components" ${BUILD_CLIENT_OPTION})
|
||||
option(BUILD_SERVER "Build server components" ${BUILD_SERVER_OPTION})
|
||||
option(BUILD_TESTS "Build tests" ${BUILD_TESTS_OPTION})
|
||||
|
@ -157,7 +157,7 @@ foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS})
|
|||
list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}")
|
||||
endforeach()
|
||||
|
||||
MESSAGE(STATUS "USE OPTIMIZED IK: " ${HIFI_USE_OPTIMIZED_IK})
|
||||
MESSAGE(STATUS "Use optimized IK: " ${HIFI_USE_OPTIMIZED_IK})
|
||||
MESSAGE(STATUS "Build server: " ${BUILD_SERVER})
|
||||
MESSAGE(STATUS "Build client: " ${BUILD_CLIENT})
|
||||
MESSAGE(STATUS "Build tests: " ${BUILD_TESTS})
|
||||
|
|
|
@ -307,7 +307,7 @@ void AssignmentClient::assignmentCompleted() {
|
|||
|
||||
// reset our NodeList by switching back to unassigned and clearing the list
|
||||
nodeList->setOwnerType(NodeType::Unassigned);
|
||||
nodeList->reset();
|
||||
nodeList->reset("Assignment completed");
|
||||
nodeList->resetNodeInterestSet();
|
||||
|
||||
_isAssigned = false;
|
||||
|
|
|
@ -45,6 +45,7 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
|
|||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
// restart the codec
|
||||
if (_codec) {
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
if (_decoder) {
|
||||
_codec->releaseDecoder(_decoder);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ void EntityTreeHeadlessViewer::update() {
|
|||
if (_tree) {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
tree->withTryWriteLock([&] {
|
||||
tree->preUpdate();
|
||||
tree->update();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -811,26 +811,23 @@ void DomainGatekeeper::processICEPeerInformationPacket(QSharedPointer<ReceivedMe
|
|||
// any peer we don't have we add to the hash, otherwise we update
|
||||
QDataStream iceResponseStream(message->getMessage());
|
||||
|
||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||
auto receivedPeer = SharedNetworkPeer::create();
|
||||
iceResponseStream >> *receivedPeer;
|
||||
|
||||
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
||||
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
||||
_icePeers[receivedPeer->getUUID()] = newPeer;
|
||||
qCDebug(domain_server_ice) << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||
_icePeers[receivedPeer->getUUID()] = receivedPeer;
|
||||
|
||||
// make sure we know when we should ping this peer
|
||||
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout);
|
||||
connect(receivedPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainGatekeeper::handlePeerPingTimeout);
|
||||
|
||||
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
||||
newPeer->startPingTimer();
|
||||
receivedPeer->startPingTimer();
|
||||
|
||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< newPeer->getUUID();
|
||||
qCDebug(domain_server_ice) << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< receivedPeer->getUUID();
|
||||
|
||||
pingPunchForConnectingPeer(newPeer);
|
||||
} else {
|
||||
delete receivedPeer;
|
||||
pingPunchForConnectingPeer(receivedPeer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include <OctreeDataUtils.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(domain_server, "hifi.domain_server")
|
||||
Q_LOGGING_CATEGORY(domain_server_ice, "hifi.domain_server.ice")
|
||||
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
const QString DomainServer::REPLACEMENT_FILE_EXTENSION = ".replace";
|
||||
|
@ -374,7 +375,7 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
qCWarning(domain_server_ice) << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -1570,12 +1571,8 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
|
|||
callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
|
||||
callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate";
|
||||
|
||||
static bool printedIceServerMessage = false;
|
||||
if (!printedIceServerMessage) {
|
||||
printedIceServerMessage = true;
|
||||
qDebug() << "Updating ice-server address in High Fidelity Metaverse API to"
|
||||
<< (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString());
|
||||
}
|
||||
qCDebug(domain_server_ice) << "Updating ice-server address in High Fidelity Metaverse API to"
|
||||
<< (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString());
|
||||
|
||||
static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address";
|
||||
|
||||
|
@ -1589,11 +1586,11 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
|
|||
void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply* requestReply) {
|
||||
_sendICEServerAddressToMetaverseAPIInProgress = false;
|
||||
if (_sendICEServerAddressToMetaverseAPIRedo) {
|
||||
qDebug() << "ice-server address updated with metaverse, but has since changed. redoing update...";
|
||||
qCDebug(domain_server_ice) << "ice-server address (" << _iceServerSocket << ") updated with metaverse, but has since changed. redoing update...";
|
||||
_sendICEServerAddressToMetaverseAPIRedo = false;
|
||||
sendICEServerAddressToMetaverseAPI();
|
||||
} else {
|
||||
qDebug() << "ice-server address updated with metaverse.";
|
||||
qCDebug(domain_server_ice) << "ice-server address (" << _iceServerSocket << ") updated with metaverse.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1606,9 +1603,9 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestRepl
|
|||
} else {
|
||||
const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000;
|
||||
|
||||
qWarning() << "Failed to update ice-server address with High Fidelity Metaverse - error was"
|
||||
qCWarning(domain_server_ice) << "Failed to update ice-server address (" << _iceServerSocket << ") with High Fidelity Metaverse - error was"
|
||||
<< requestReply->errorString();
|
||||
qWarning() << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
|
||||
qCWarning(domain_server_ice) << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
|
||||
|
||||
QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI()));
|
||||
}
|
||||
|
@ -1621,26 +1618,26 @@ void DomainServer::sendHeartbeatToIceServer() {
|
|||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
if (!accountManager->getAccountInfo().hasPrivateKey()) {
|
||||
qWarning() << "Cannot send an ice-server heartbeat without a private key for signature.";
|
||||
qWarning() << "Waiting for keypair generation to complete before sending ICE heartbeat.";
|
||||
qCWarning(domain_server_ice) << "Cannot send an ice-server heartbeat without a private key for signature.";
|
||||
qCWarning(domain_server_ice) << "Waiting for keypair generation to complete before sending ICE heartbeat.";
|
||||
|
||||
if (!limitedNodeList->getSessionUUID().isNull()) {
|
||||
accountManager->generateNewDomainKeypair(limitedNodeList->getSessionUUID());
|
||||
} else {
|
||||
qWarning() << "Attempting to send ICE server heartbeat with no domain ID. This is not supported";
|
||||
qCWarning(domain_server_ice) << "Attempting to send ICE server heartbeat with no domain ID. This is not supported";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int FAILOVER_NO_REPLY_ICE_HEARTBEATS { 3 };
|
||||
const int FAILOVER_NO_REPLY_ICE_HEARTBEATS { 6 };
|
||||
|
||||
// increase the count of no reply ICE heartbeats and check the current value
|
||||
++_noReplyICEHeartbeats;
|
||||
|
||||
if (_noReplyICEHeartbeats > FAILOVER_NO_REPLY_ICE_HEARTBEATS) {
|
||||
qWarning() << "There have been" << _noReplyICEHeartbeats - 1 << "heartbeats sent with no reply from the ice-server";
|
||||
qWarning() << "Clearing the current ice-server socket and selecting a new candidate ice-server";
|
||||
qCWarning(domain_server_ice) << "There have been" << _noReplyICEHeartbeats - 1 << "heartbeats sent with no reply from the ice-server";
|
||||
qCWarning(domain_server_ice) << "Clearing the current ice-server socket and selecting a new candidate ice-server";
|
||||
|
||||
// add the current address to our list of failed addresses
|
||||
_failedIceServerAddresses << _iceServerSocket.getAddress();
|
||||
|
@ -1713,8 +1710,8 @@ void DomainServer::sendHeartbeatToIceServer() {
|
|||
limitedNodeList->sendUnreliablePacket(*_iceServerHeartbeatPacket, _iceServerSocket);
|
||||
|
||||
} else {
|
||||
qDebug() << "Not sending ice-server heartbeat since there is no selected ice-server.";
|
||||
qDebug() << "Waiting for" << _iceServerAddr << "host lookup response";
|
||||
qCDebug(domain_server_ice) << "Not sending ice-server heartbeat since there is no selected ice-server.";
|
||||
qCDebug(domain_server_ice) << "Waiting for" << _iceServerAddr << "host lookup response";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3294,7 +3291,7 @@ void DomainServer::processICEServerHeartbeatDenialPacket(QSharedPointer<Received
|
|||
static const int NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN = 3;
|
||||
|
||||
if (++_numHeartbeatDenials > NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN) {
|
||||
qDebug() << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server"
|
||||
qCDebug(domain_server_ice) << "Received" << NUM_HEARTBEAT_DENIALS_FOR_KEYPAIR_REGEN << "heartbeat denials from ice-server"
|
||||
<< "- re-generating keypair now";
|
||||
|
||||
// we've hit our threshold of heartbeat denials, trigger a keypair re-generation
|
||||
|
@ -3316,7 +3313,7 @@ void DomainServer::processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage>
|
|||
if (!_connectedToICEServer) {
|
||||
_connectedToICEServer = true;
|
||||
sendICEServerAddressToMetaverseAPI();
|
||||
qInfo() << "Connected to ice-server at" << _iceServerSocket;
|
||||
qCInfo(domain_server_ice) << "Connected to ice-server at" << _iceServerSocket;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3347,7 +3344,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
}
|
||||
|
||||
if (hostInfo.error() != QHostInfo::NoError || sanitizedAddresses.empty()) {
|
||||
qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
|
||||
qCWarning(domain_server_ice) << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
|
||||
|
||||
// if we don't have an ICE server to use yet, trigger a retry
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -3362,7 +3359,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
_iceServerAddresses = sanitizedAddresses;
|
||||
|
||||
if (countBefore == 0) {
|
||||
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;
|
||||
qCInfo(domain_server_ice) << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;
|
||||
}
|
||||
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -3396,7 +3393,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
// we ended up with an empty list since everything we've tried has failed
|
||||
// so clear the set of failed addresses and start going through them again
|
||||
|
||||
qWarning() << "All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
qCWarning(domain_server_ice) << "All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
<< _iceServerAddr;
|
||||
|
||||
_failedIceServerAddresses.clear();
|
||||
|
@ -3416,7 +3413,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
}
|
||||
|
||||
_iceServerSocket = HifiSockAddr { candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT };
|
||||
qInfo() << "Set candidate ice-server socket to" << _iceServerSocket;
|
||||
qCInfo(domain_server_ice) << "Set candidate ice-server socket to" << _iceServerSocket;
|
||||
|
||||
// clear our number of hearbeat denials, this should be re-set on ice-server change
|
||||
_numHeartbeatDenials = 0;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(domain_server)
|
||||
Q_DECLARE_LOGGING_CATEGORY(domain_server_ice)
|
||||
|
||||
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
|
||||
typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
|
||||
|
|
|
@ -245,11 +245,8 @@
|
|||
#include "webbrowser/WebBrowserSuggestionsEngine.h"
|
||||
#include <DesktopPreviewProvider.h>
|
||||
|
||||
|
||||
#include "AboutUtil.h"
|
||||
|
||||
#include <DisableDeferred.h>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
|
@ -320,6 +317,7 @@ static const unsigned int THROTTLED_SIM_FRAMERATE = 15;
|
|||
static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE;
|
||||
static const int ENTITY_SERVER_ADDED_TIMEOUT = 5000;
|
||||
static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000;
|
||||
static const int WATCHDOG_TIMER_TIMEOUT = 100;
|
||||
|
||||
static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled
|
||||
|
||||
|
@ -976,6 +974,7 @@ const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false;
|
|||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
|
||||
const QString DEFAULT_CURSOR_NAME = "DEFAULT";
|
||||
const bool DEFAULT_MINI_TABLET_ENABLED = true;
|
||||
const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true;
|
||||
|
||||
QSharedPointer<OffscreenUi> getOffscreenUI() {
|
||||
#if !defined(DISABLE_QML)
|
||||
|
@ -1006,6 +1005,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER),
|
||||
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
|
||||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED),
|
||||
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),
|
||||
_miniTabletEnabledSetting("miniTabletEnabled", DEFAULT_MINI_TABLET_ENABLED),
|
||||
_scaleMirror(1.0f),
|
||||
|
@ -1127,6 +1127,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
auto deadlockWatchdogThread = new DeadlockWatchdogThread();
|
||||
deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId());
|
||||
deadlockWatchdogThread->start();
|
||||
|
||||
|
||||
// Main thread timer to keep the watchdog updated
|
||||
QTimer* watchdogUpdateTimer = new QTimer(this);
|
||||
connect(watchdogUpdateTimer, &QTimer::timeout, [this] { updateHeartbeat(); });
|
||||
connect(this, &QCoreApplication::aboutToQuit, [watchdogUpdateTimer] {
|
||||
watchdogUpdateTimer->stop();
|
||||
watchdogUpdateTimer->deleteLater();
|
||||
});
|
||||
watchdogUpdateTimer->setSingleShot(false);
|
||||
watchdogUpdateTimer->setInterval(WATCHDOG_TIMER_TIMEOUT); // 100ms, Qt::CoarseTimer acceptable
|
||||
watchdogUpdateTimer->start();
|
||||
}
|
||||
|
||||
// Set File Logger Session UUID
|
||||
|
@ -2685,7 +2697,7 @@ void Application::cleanupBeforeQuit() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->getDomainHandler().disconnect("Quitting");
|
||||
nodeList->setIsShuttingDown(true);
|
||||
|
||||
// tell the packet receiver we're shutting down, so it can drop packets
|
||||
|
@ -2779,21 +2791,15 @@ Application::~Application() {
|
|||
// remove avatars from physics engine
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
avatarManager->clearOtherAvatars();
|
||||
auto myCharacterController = getMyAvatar()->getCharacterController();
|
||||
myCharacterController->clearDetailedMotionStates();
|
||||
|
||||
PhysicsEngine::Transaction transaction;
|
||||
avatarManager->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
avatarManager->deleteAllAvatars();
|
||||
|
||||
auto myCharacterController = getMyAvatar()->getCharacterController();
|
||||
myCharacterController->clearDetailedMotionStates();
|
||||
|
||||
myCharacterController->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
myCharacterController->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// the _shapeManager should have zero references
|
||||
|
@ -3012,7 +3018,7 @@ void Application::initializeDisplayPlugins() {
|
|||
void Application::initializeRenderEngine() {
|
||||
// FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
|
||||
DeadlockWatchdogThread::withPause([&] {
|
||||
_graphicsEngine.initializeRender(DISABLE_DEFERRED);
|
||||
_graphicsEngine.initializeRender();
|
||||
DependencyManager::get<Keyboard>()->registerKeyboardHighlighting();
|
||||
});
|
||||
}
|
||||
|
@ -3627,6 +3633,11 @@ void Application::setSettingConstrainToolbarPosition(bool setting) {
|
|||
getOffscreenUI()->setConstrainToolbarToCenterX(setting);
|
||||
}
|
||||
|
||||
void Application::setAwayStateWhenFocusLostInVREnabled(bool enabled) {
|
||||
_awayStateWhenFocusLostInVREnabled.set(enabled);
|
||||
emit awayStateWhenFocusLostInVRChanged(enabled);
|
||||
}
|
||||
|
||||
void Application::setMiniTabletEnabled(bool enabled) {
|
||||
_miniTabletEnabledSetting.set(enabled);
|
||||
emit miniTabletEnabledChanged(enabled);
|
||||
|
@ -4967,9 +4978,6 @@ void setupCpuMonitorThread() {
|
|||
void Application::idle() {
|
||||
PerformanceTimer perfTimer("idle");
|
||||
|
||||
// Update the deadlock watchdog
|
||||
updateHeartbeat();
|
||||
|
||||
#if !defined(DISABLE_QML)
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
|
||||
|
@ -5516,7 +5524,7 @@ void Application::pauseUntilLoginDetermined() {
|
|||
cameraModeChanged();
|
||||
|
||||
// disconnect domain handler.
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->getDomainHandler().disconnect("Pause until login determined");
|
||||
|
||||
// From now on, it's permissible to call resumeAfterLoginDialogActionTaken()
|
||||
_resumeAfterLoginDialogActionTaken_SafeToRun = true;
|
||||
|
@ -5896,7 +5904,7 @@ void Application::reloadResourceCaches() {
|
|||
DependencyManager::get<TextureCache>()->refreshAll();
|
||||
DependencyManager::get<recording::ClipCache>()->refreshAll();
|
||||
|
||||
DependencyManager::get<NodeList>()->reset(); // Force redownload of .fst models
|
||||
DependencyManager::get<NodeList>()->reset("Reloading resources"); // Force redownload of .fst models
|
||||
|
||||
DependencyManager::get<ScriptEngines>()->reloadAllScripts();
|
||||
getOffscreenUI()->clearCache();
|
||||
|
@ -6385,64 +6393,42 @@ void Application::update(float deltaTime) {
|
|||
PROFILE_RANGE(simulation_physics, "Simulation");
|
||||
PerformanceTimer perfTimer("simulation");
|
||||
|
||||
if (_physicsEnabled) {
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
auto t1 = t0;
|
||||
getEntities()->preUpdate();
|
||||
|
||||
auto t0 = std::chrono::high_resolution_clock::now();
|
||||
auto t1 = t0;
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
||||
PerformanceTimer perfTimer("prePhysics)");
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
||||
PerformanceTimer perfTimer("prePhysics)");
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "RemoveEntities");
|
||||
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
}
|
||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||
}
|
||||
PROFILE_RANGE(simulation_physics, "Entities");
|
||||
PhysicsEngine::Transaction transaction;
|
||||
_entitySimulation->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
_entitySimulation->handleProcessedPhysicsTransaction(transaction);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "AddEntities");
|
||||
VectorOfMotionStates motionStates;
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
||||
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
});
|
||||
}
|
||||
{
|
||||
VectorOfMotionStates motionStates;
|
||||
PROFILE_RANGE(simulation_physics, "ChangeEntities");
|
||||
getEntities()->getTree()->withReadLock([&] {
|
||||
_entitySimulation->getObjectsToChange(motionStates);
|
||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||
});
|
||||
}
|
||||
t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Avatars");
|
||||
PhysicsEngine::Transaction transaction;
|
||||
avatarManager->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
||||
}
|
||||
}
|
||||
|
||||
if (_physicsEnabled) {
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PrepareActions");
|
||||
_entitySimulation->applyDynamicChanges();
|
||||
|
||||
t1 = std::chrono::high_resolution_clock::now();
|
||||
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "Avatars");
|
||||
PhysicsEngine::Transaction transaction;
|
||||
avatarManager->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||
myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(simulation_physics, "PrepareActions");
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
});
|
||||
}
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
{
|
||||
|
@ -6696,7 +6682,7 @@ void Application::updateRenderArgs(float deltaTime) {
|
|||
}
|
||||
appRenderArgs._renderArgs = RenderArgs(_graphicsEngine.getGPUContext(), lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
RenderArgs::MONO, RenderArgs::DEFERRED, RenderArgs::RENDER_DEBUG_NONE);
|
||||
appRenderArgs._renderArgs._scene = getMain3DScene();
|
||||
|
||||
{
|
||||
|
@ -9369,7 +9355,7 @@ void Application::showUrlHandler(const QUrl& url) {
|
|||
void Application::beforeEnterBackground() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->setSendDomainServerCheckInEnabled(false);
|
||||
nodeList->reset(true);
|
||||
nodeList->reset("Entering background", true);
|
||||
clearDomainOctreeDetails();
|
||||
}
|
||||
|
||||
|
|
|
@ -211,6 +211,8 @@ public:
|
|||
float getNumCollisionObjects() const;
|
||||
float getTargetRenderFrameRate() const; // frames/second
|
||||
|
||||
static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties);
|
||||
|
||||
float getFieldOfView() { return _fieldOfView.get(); }
|
||||
void setFieldOfView(float fov);
|
||||
|
||||
|
@ -239,6 +241,9 @@ public:
|
|||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
void setSettingConstrainToolbarPosition(bool setting);
|
||||
|
||||
float getAwayStateWhenFocusLostInVREnabled() { return _awayStateWhenFocusLostInVREnabled.get(); }
|
||||
void setAwayStateWhenFocusLostInVREnabled(bool setting);
|
||||
|
||||
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int stabilityCount) { _minimumGPUTextureMemSizeStabilityCount = stabilityCount; }
|
||||
|
||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||
|
@ -369,6 +374,7 @@ signals:
|
|||
void loginDialogFocusDisabled();
|
||||
|
||||
void miniTabletEnabledChanged(bool enabled);
|
||||
void awayStateWhenFocusLostInVRChanged(bool enabled);
|
||||
|
||||
public slots:
|
||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||
|
@ -604,7 +610,6 @@ private:
|
|||
void maybeToggleMenuVisible(QMouseEvent* event) const;
|
||||
void toggleTabletUI(bool shouldOpen = false) const;
|
||||
|
||||
static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties);
|
||||
void userKickConfirmation(const QUuid& nodeID);
|
||||
|
||||
MainWindow* _window;
|
||||
|
@ -672,6 +677,7 @@ private:
|
|||
Setting::Handle<bool> _preferStylusOverLaserSetting;
|
||||
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
|
||||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
Setting::Handle<bool> _awayStateWhenFocusLostInVREnabled;
|
||||
Setting::Handle<QString> _preferredCursor;
|
||||
Setting::Handle<bool> _miniTabletEnabledSetting;
|
||||
Setting::Handle<bool> _keepLogWindowOnTop { "keepLogWindowOnTop", false };
|
||||
|
|
|
@ -20,6 +20,7 @@ class FancyCamera : public Camera {
|
|||
|
||||
/**jsdoc
|
||||
* The <code>Camera</code> API provides access to the "camera" that defines your view in desktop and HMD display modes.
|
||||
* The High Fidelity camera has axes <code>x</code> = right, <code>y</code> = up, </code>-z</code> = forward.
|
||||
*
|
||||
* @namespace Camera
|
||||
*
|
||||
|
|
|
@ -272,10 +272,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) {
|
||||
const auto cachedArg = task.addJob<SecondaryCameraJob>("SecondaryCamera");
|
||||
|
||||
task.addJob<RenderViewTask>("RenderSecondView", cullFunctor, isDeferred, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
task.addJob<RenderViewTask>("RenderSecondView", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
|
||||
task.addJob<EndSecondaryCameraFrame>("EndSecondaryCamera", cachedArg);
|
||||
}
|
|
@ -65,7 +65,7 @@ public:
|
|||
using JobModel = render::Task::Model<SecondaryCameraRenderTask, Config>;
|
||||
SecondaryCameraRenderTask() {}
|
||||
void configure(const Config& config) {}
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -101,7 +101,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
}
|
||||
|
||||
AvatarManager::~AvatarManager() {
|
||||
assert(_avatarsToChangeInPhysics.empty());
|
||||
assert(_otherAvatarsToChangeInPhysics.empty());
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
|
@ -295,7 +295,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
render::Transaction renderTransaction;
|
||||
workload::Transaction workloadTransaction;
|
||||
|
||||
|
||||
for (int p = kHero; p < NumVariants; p++) {
|
||||
auto& priorityQueue = avatarPriorityQueues[p];
|
||||
// Sorting the current queue HERE as part of the measured timing.
|
||||
|
@ -314,7 +314,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// remove the orb if it is there
|
||||
avatar->removeOrb();
|
||||
if (avatar->needsPhysicsUpdate()) {
|
||||
_avatarsToChangeInPhysics.insert(avatar);
|
||||
_otherAvatarsToChangeInPhysics.insert(avatar);
|
||||
}
|
||||
} else {
|
||||
avatar->updateOrbPosition();
|
||||
|
@ -419,69 +419,111 @@ AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
|
|||
}
|
||||
|
||||
void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) {
|
||||
_avatarsToChangeInPhysics.insert(avatar);
|
||||
_otherAvatarsToChangeInPhysics.insert(avatar);
|
||||
}
|
||||
|
||||
DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex) {
|
||||
bool isBound = false;
|
||||
std::vector<int32_t> boundJoints;
|
||||
const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints);
|
||||
if (shape) {
|
||||
DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);
|
||||
motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok
|
||||
motionState->setIsBound(isBound, boundJoints);
|
||||
return motionState;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar) {
|
||||
if (!avatar->_motionState) {
|
||||
avatar->_motionState = new AvatarMotionState(avatar, nullptr);
|
||||
}
|
||||
AvatarMotionState* motionState = avatar->_motionState;
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
assert(shape);
|
||||
motionState->setShape(shape);
|
||||
motionState->setMass(avatar->computeMass());
|
||||
if (motionState->getRigidBody()) {
|
||||
transaction.objectsToReinsert.push_back(motionState);
|
||||
} else {
|
||||
transaction.objectsToAdd.push_back(motionState);
|
||||
}
|
||||
motionState->clearIncomingDirtyFlags();
|
||||
|
||||
// Rather than reconcile numbers of joints after change to model or LOD
|
||||
// we blow away old detailedMotionStates and create anew all around.
|
||||
|
||||
// delete old detailedMotionStates
|
||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||
if (detailedMotionStates.size() != 0) {
|
||||
for (auto& detailedMotionState : detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(detailedMotionState);
|
||||
}
|
||||
avatar->resetDetailedMotionStates();
|
||||
}
|
||||
|
||||
// build new detailedMotionStates
|
||||
OtherAvatar::BodyLOD lod = avatar->getBodyLOD();
|
||||
if (lod == OtherAvatar::BodyLOD::Sphere) {
|
||||
auto dMotionState = createDetailedMotionState(avatar, -1);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
} else {
|
||||
int32_t numJoints = avatar->getJointCount();
|
||||
for (int32_t i = 0; i < numJoints; i++) {
|
||||
auto dMotionState = createDetailedMotionState(avatar, i);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
}
|
||||
avatar->_needsReinsertion = false;
|
||||
}
|
||||
|
||||
void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
SetOfOtherAvatars failedShapeBuilds;
|
||||
for (auto avatar : _avatarsToChangeInPhysics) {
|
||||
_myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
|
||||
for (auto avatar : _otherAvatarsToChangeInPhysics) {
|
||||
bool isInPhysics = avatar->isInPhysicsSimulation();
|
||||
if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) {
|
||||
if (isInPhysics) {
|
||||
transaction.objectsToRemove.push_back(avatar->_motionState);
|
||||
avatar->_motionState = nullptr;
|
||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||
for (auto& mState : detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(mState);
|
||||
for (auto& motionState : detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(motionState);
|
||||
}
|
||||
avatar->resetDetailedMotionStates();
|
||||
} else {
|
||||
if (avatar->getDetailedMotionStates().size() == 0) {
|
||||
avatar->createDetailedMotionStates(avatar);
|
||||
for (auto dMotionState : avatar->getDetailedMotionStates()) {
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
if (avatar->getDetailedMotionStates().size() > 0) {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
motionState->setMass(avatar->computeMass());
|
||||
avatar->_motionState = motionState;
|
||||
transaction.objectsToAdd.push_back(motionState);
|
||||
} else {
|
||||
failedShapeBuilds.insert(avatar);
|
||||
}
|
||||
} else {
|
||||
failedShapeBuilds.insert(avatar);
|
||||
}
|
||||
rebuildAvatarPhysics(transaction, avatar);
|
||||
}
|
||||
} else if (isInPhysics) {
|
||||
transaction.objectsToChange.push_back(avatar->_motionState);
|
||||
|
||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||
for (auto& mState : detailedMotionStates) {
|
||||
if (mState) {
|
||||
transaction.objectsToChange.push_back(mState);
|
||||
}
|
||||
AvatarMotionState* motionState = avatar->_motionState;
|
||||
uint32_t flags = motionState->getIncomingDirtyFlags();
|
||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
motionState->handleEasyChanges(flags);
|
||||
}
|
||||
// NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC
|
||||
// and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active.
|
||||
|
||||
if (motionState->needsNewShape()) {
|
||||
rebuildAvatarPhysics(transaction, avatar);
|
||||
} else {
|
||||
if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) {
|
||||
transaction.objectsToReinsert.push_back(motionState);
|
||||
}
|
||||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_avatarsToChangeInPhysics.swap(failedShapeBuilds);
|
||||
}
|
||||
|
||||
void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
// things on objectsToChange correspond to failed changes
|
||||
// so we push them back onto _avatarsToChangeInPhysics
|
||||
for (auto object : transaction.objectsToChange) {
|
||||
AvatarMotionState* motionState = static_cast<AvatarMotionState*>(object);
|
||||
assert(motionState);
|
||||
assert(motionState->_avatar);
|
||||
_avatarsToChangeInPhysics.insert(motionState->_avatar);
|
||||
}
|
||||
// things on objectsToRemove are ready for delete
|
||||
for (auto object : transaction.objectsToRemove) {
|
||||
delete object;
|
||||
|
@ -536,14 +578,20 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
|
||||
workload::SpacePointer space = _space;
|
||||
transaction.transitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() {
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
if (avatar->getLastFadeRequested() != render::Transition::Type::USER_LEAVE_DOMAIN) {
|
||||
// The avatar is using another transition besides the fade-out transition, which means it is still in use.
|
||||
// Deleting the avatar now could cause state issues, so abort deletion and show message.
|
||||
qCWarning(interfaceapp) << "An ending fade-out transition wants to delete an avatar, but the avatar is still in use. Avatar deletion has aborted. (avatar ID: " << avatar->getSessionUUID() << ")";
|
||||
} else {
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
||||
workload::Transaction workloadTransaction;
|
||||
workloadTransaction.remove(avatar->getSpaceIndex());
|
||||
space->enqueueTransaction(workloadTransaction);
|
||||
workload::Transaction workloadTransaction;
|
||||
workloadTransaction.remove(avatar->getSpaceIndex());
|
||||
space->enqueueTransaction(workloadTransaction);
|
||||
}
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
@ -570,7 +618,7 @@ void AvatarManager::clearOtherAvatars() {
|
|||
++avatarIterator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& av : removedAvatars) {
|
||||
handleRemovedAvatar(av);
|
||||
|
@ -578,7 +626,7 @@ void AvatarManager::clearOtherAvatars() {
|
|||
}
|
||||
|
||||
void AvatarManager::deleteAllAvatars() {
|
||||
assert(_avatarsToChangeInPhysics.empty());
|
||||
assert(_otherAvatarsToChangeInPhysics.empty());
|
||||
QReadLocker locker(&_hashLock);
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
|
@ -588,7 +636,7 @@ void AvatarManager::deleteAllAvatars() {
|
|||
if (avatar != _myAvatar) {
|
||||
auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
|
||||
assert(!otherAvatar->_motionState);
|
||||
assert(otherAvatar->getDetailedMotionStates().size() == 0);
|
||||
assert(otherAvatar->getDetailedMotionStates().size() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -273,6 +273,8 @@ public slots:
|
|||
|
||||
protected:
|
||||
AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
||||
DetailedMotionState* createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex);
|
||||
void rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar);
|
||||
|
||||
private:
|
||||
explicit AvatarManager(QObject* parent = 0);
|
||||
|
@ -288,7 +290,7 @@ private:
|
|||
void handleTransitAnimations(AvatarTransit::Status status);
|
||||
|
||||
using SetOfOtherAvatars = std::set<OtherAvatarPointer>;
|
||||
SetOfOtherAvatars _avatarsToChangeInPhysics;
|
||||
SetOfOtherAvatars _otherAvatarsToChangeInPhysics;
|
||||
|
||||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
|
|
@ -29,23 +29,19 @@ void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
}
|
||||
}
|
||||
|
||||
bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
AvatarMotionState::~AvatarMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
|
||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() const {
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void AvatarMotionState::clearIncomingDirtyFlags() {
|
||||
void AvatarMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
_dirtyFlags &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,13 +50,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
|||
return MOTION_TYPE_DYNAMIC;
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool AvatarMotionState::isMoving() const {
|
||||
return false;
|
||||
|
|
|
@ -23,44 +23,44 @@ class AvatarMotionState : public ObjectMotionState {
|
|||
public:
|
||||
AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape);
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
||||
void handleEasyChanges(uint32_t& flags) override;
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
uint32_t getIncomingDirtyFlags() const override;
|
||||
void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
||||
PhysicsMotionType computePhysicsMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const override;
|
||||
bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
// pure virtual overrides from ObjectMotionState
|
||||
virtual float getObjectRestitution() const override;
|
||||
virtual float getObjectFriction() const override;
|
||||
virtual float getObjectLinearDamping() const override;
|
||||
virtual float getObjectAngularDamping() const override;
|
||||
float getObjectRestitution() const override;
|
||||
float getObjectFriction() const override;
|
||||
float getObjectLinearDamping() const override;
|
||||
float getObjectAngularDamping() const override;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const override;
|
||||
virtual glm::quat getObjectRotation() const override;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
||||
virtual glm::vec3 getObjectGravity() const override;
|
||||
glm::vec3 getObjectPosition() const override;
|
||||
glm::quat getObjectRotation() const override;
|
||||
glm::vec3 getObjectLinearVelocity() const override;
|
||||
glm::vec3 getObjectAngularVelocity() const override;
|
||||
glm::vec3 getObjectGravity() const override;
|
||||
|
||||
virtual const QUuid getObjectID() const override;
|
||||
const QUuid getObjectID() const override;
|
||||
|
||||
virtual QString getName() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
QString getName() const override;
|
||||
ShapeType getShapeType() const override { return SHAPE_TYPE_CAPSULE_Y; }
|
||||
QUuid getSimulatorID() const override;
|
||||
|
||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||
|
||||
|
@ -69,9 +69,9 @@ public:
|
|||
void setCollisionGroup(int32_t group) { _collisionGroup = group; }
|
||||
int32_t getCollisionGroup() { return _collisionGroup; }
|
||||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
virtual float getMass() const override;
|
||||
float getMass() const override;
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
@ -85,9 +85,6 @@ protected:
|
|||
// ever called by the Avatar class dtor.
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
OtherAvatarPointer _avatar;
|
||||
float _diameter { 0.0f };
|
||||
int32_t _collisionGroup;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "MyAvatar.h"
|
||||
|
||||
|
||||
DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex) :
|
||||
DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int32_t jointIndex) :
|
||||
ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) {
|
||||
assert(_avatar);
|
||||
if (!_avatar->isMyAvatar()) {
|
||||
|
@ -33,47 +33,26 @@ void DetailedMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
}
|
||||
}
|
||||
|
||||
bool DetailedMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
DetailedMotionState::~DetailedMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t DetailedMotionState::getIncomingDirtyFlags() {
|
||||
uint32_t DetailedMotionState::getIncomingDirtyFlags() const {
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void DetailedMotionState::clearIncomingDirtyFlags() {
|
||||
void DetailedMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
_dirtyFlags &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const {
|
||||
// TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting)
|
||||
return MOTION_TYPE_KINEMATIC;
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
const btCollisionShape* DetailedMotionState::computeNewShape() {
|
||||
btCollisionShape* shape = nullptr;
|
||||
if (!_avatar->isMyAvatar()) {
|
||||
if (_otherAvatar != nullptr) {
|
||||
shape = _otherAvatar->createCollisionShape(_jointIndex, _isBound, _boundJoints);
|
||||
}
|
||||
} else {
|
||||
std::shared_ptr<MyAvatar> myAvatar = std::static_pointer_cast<MyAvatar>(_avatar);
|
||||
if (myAvatar) {
|
||||
shape = myAvatar->getCharacterController()->createDetailedCollisionShapeForJoint(_jointIndex);
|
||||
}
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool DetailedMotionState::isMoving() const {
|
||||
return false;
|
||||
|
@ -178,11 +157,23 @@ void DetailedMotionState::setRigidBody(btRigidBody* body) {
|
|||
}
|
||||
|
||||
void DetailedMotionState::setShape(const btCollisionShape* shape) {
|
||||
ObjectMotionState::setShape(shape);
|
||||
if (_shape != shape) {
|
||||
if (_shape) {
|
||||
getShapeManager()->releaseShape(_shape);
|
||||
}
|
||||
_shape = shape;
|
||||
if (_body) {
|
||||
assert(_shape);
|
||||
_body->setCollisionShape(const_cast<btCollisionShape*>(_shape));
|
||||
}
|
||||
} else if (shape) {
|
||||
// we need to release unused reference to shape
|
||||
getShapeManager()->releaseShape(shape);
|
||||
}
|
||||
}
|
||||
|
||||
void DetailedMotionState::forceActive() {
|
||||
if (_body && !_body->isActive()) {
|
||||
_body->setActivationState(ACTIVE_TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,55 +23,55 @@ class DetailedMotionState : public ObjectMotionState {
|
|||
public:
|
||||
DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex);
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
||||
void handleEasyChanges(uint32_t& flags) override;
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
uint32_t getIncomingDirtyFlags() const override;
|
||||
void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
||||
PhysicsMotionType computePhysicsMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const override;
|
||||
bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
// pure virtual overrides from ObjectMotionState
|
||||
virtual float getObjectRestitution() const override;
|
||||
virtual float getObjectFriction() const override;
|
||||
virtual float getObjectLinearDamping() const override;
|
||||
virtual float getObjectAngularDamping() const override;
|
||||
float getObjectRestitution() const override;
|
||||
float getObjectFriction() const override;
|
||||
float getObjectLinearDamping() const override;
|
||||
float getObjectAngularDamping() const override;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const override;
|
||||
virtual glm::quat getObjectRotation() const override;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
||||
virtual glm::vec3 getObjectGravity() const override;
|
||||
glm::vec3 getObjectPosition() const override;
|
||||
glm::quat getObjectRotation() const override;
|
||||
glm::vec3 getObjectLinearVelocity() const override;
|
||||
glm::vec3 getObjectAngularVelocity() const override;
|
||||
glm::vec3 getObjectGravity() const override;
|
||||
|
||||
virtual const QUuid getObjectID() const override;
|
||||
const QUuid getObjectID() const override;
|
||||
|
||||
virtual QString getName() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
QString getName() const override;
|
||||
ShapeType getShapeType() const override { return SHAPE_TYPE_HULL; }
|
||||
QUuid getSimulatorID() const override;
|
||||
|
||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
virtual float getMass() const override;
|
||||
float getMass() const override;
|
||||
void forceActive();
|
||||
QUuid getAvatarID() const { return _avatar->getID(); }
|
||||
int getJointIndex() const { return _jointIndex; }
|
||||
void setIsBound(bool isBound, std::vector<int> boundJoints) { _isBound = isBound; _boundJoints = boundJoints; }
|
||||
bool getIsBound(std::vector<int>& boundJoints) const { boundJoints = _boundJoints; return _isBound; }
|
||||
int32_t getJointIndex() const { return _jointIndex; }
|
||||
void setIsBound(bool isBound, const std::vector<int32_t>& boundJoints) { _isBound = isBound; _boundJoints = boundJoints; }
|
||||
bool getIsBound(std::vector<int32_t>& boundJoints) const { boundJoints = _boundJoints; return _isBound; }
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
@ -84,17 +84,14 @@ protected:
|
|||
// ever called by the Avatar class dtor.
|
||||
~DetailedMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
AvatarPointer _avatar;
|
||||
float _diameter { 0.0f };
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
int _jointIndex { -1 };
|
||||
int32_t _jointIndex { -1 };
|
||||
OtherAvatarPointer _otherAvatar { nullptr };
|
||||
bool _isBound { false };
|
||||
std::vector<int> _boundJoints;
|
||||
std::vector<int32_t> _boundJoints;
|
||||
};
|
||||
|
||||
#endif // hifi_DetailedMotionState_h
|
||||
|
|
|
@ -3755,6 +3755,7 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings
|
|||
void MyAvatar::leaveDomain() {
|
||||
clearScaleRestriction();
|
||||
saveAvatarScale();
|
||||
prepareResetTraitInstances();
|
||||
}
|
||||
|
||||
void MyAvatar::saveAvatarScale() {
|
||||
|
|
|
@ -377,21 +377,18 @@ void MyCharacterController::updateMassProperties() {
|
|||
_rigidBody->setMassProps(mass, inertia);
|
||||
}
|
||||
|
||||
btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int jointIndex) {
|
||||
const btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int32_t jointIndex) {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
shape->setMargin(0.001f);
|
||||
}
|
||||
const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
return shape;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int jointIndex) {
|
||||
auto shape = createDetailedCollisionShapeForJoint(jointIndex);
|
||||
DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int32_t jointIndex) {
|
||||
const btCollisionShape* shape = createDetailedCollisionShapeForJoint(jointIndex);
|
||||
if (shape) {
|
||||
DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex);
|
||||
motionState->setMass(_avatar->computeMass());
|
||||
|
@ -423,25 +420,16 @@ void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction&
|
|||
}
|
||||
if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) {
|
||||
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||
for (int i = 0; i < _avatar->getJointCount(); i++) {
|
||||
for (int32_t i = 0; i < _avatar->getJointCount(); i++) {
|
||||
auto dMotionState = createDetailedMotionStateForJoint(i);
|
||||
if (dMotionState) {
|
||||
_detailedMotionStates.push_back(dMotionState);
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
// things on objectsToRemove are ready for delete
|
||||
for (auto object : transaction.objectsToRemove) {
|
||||
delete object;
|
||||
}
|
||||
transaction.clear();
|
||||
}
|
||||
|
||||
|
||||
class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback {
|
||||
public:
|
||||
DetailedRayResultCallback()
|
||||
|
@ -467,7 +455,7 @@ std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTe
|
|||
_dynamicsWorld->rayTest(origin, end, rayCallback);
|
||||
if (rayCallback.m_hitFractions.size() > 0) {
|
||||
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
||||
for (int i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
||||
for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
||||
auto object = rayCallback.m_collisionObjects[i];
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(object->getUserPointer());
|
||||
if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) {
|
||||
|
@ -493,4 +481,4 @@ std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTe
|
|||
}
|
||||
}
|
||||
return foundAvatars;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,27 +44,25 @@ public:
|
|||
|
||||
void setDensity(btScalar density) { _density = density; }
|
||||
|
||||
btCollisionShape* createDetailedCollisionShapeForJoint(int jointIndex);
|
||||
DetailedMotionState* createDetailedMotionStateForJoint(int jointIndex);
|
||||
const btCollisionShape* createDetailedCollisionShapeForJoint(int32_t jointIndex);
|
||||
DetailedMotionState* createDetailedMotionStateForJoint(int32_t jointIndex);
|
||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||
void clearDetailedMotionStates();
|
||||
void resetDetailedMotionStates();
|
||||
|
||||
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
|
||||
|
||||
struct RayAvatarResult {
|
||||
bool _intersect { false };
|
||||
bool _isBound { false };
|
||||
QUuid _intersectWithAvatar;
|
||||
int _intersectWithJoint { -1 };
|
||||
int32_t _intersectWithJoint { -1 };
|
||||
float _distance { 0.0f };
|
||||
float _maxDistance { 0.0f };
|
||||
QVariantMap _extraInfo;
|
||||
glm::vec3 _intersectionPoint;
|
||||
glm::vec3 _intersectionNormal;
|
||||
std::vector<int> _boundJoints;
|
||||
std::vector<int32_t> _boundJoints;
|
||||
};
|
||||
std::vector<RayAvatarResult> rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length,
|
||||
const QVector<uint>& jointsToExclude) const;
|
||||
|
|
|
@ -116,6 +116,8 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const {
|
|||
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
// NOTE: we activate _detailedMotionStates is because they are KINEMATIC
|
||||
// and Bullet will automagically call DetailedMotionState::getWorldTransform() when active.
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
}
|
||||
if (_moving && _motionState) {
|
||||
|
@ -124,11 +126,11 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints) {
|
||||
const btCollisionShape* OtherAvatar::createCollisionShape(int32_t jointIndex, bool& isBound, std::vector<int32_t>& boundJoints) {
|
||||
ShapeInfo shapeInfo;
|
||||
isBound = false;
|
||||
QString jointName = "";
|
||||
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
||||
QString jointName = "";
|
||||
if (jointIndex > -1 && jointIndex < (int32_t)_multiSphereShapes.size()) {
|
||||
jointName = _multiSphereShapes[jointIndex].getJointName();
|
||||
}
|
||||
switch (_bodyLOD) {
|
||||
|
@ -163,39 +165,21 @@ btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBoun
|
|||
}
|
||||
break;
|
||||
}
|
||||
// Note: MultiSphereLow case really means: "skip fingers and use spheres for hands,
|
||||
// else fall through to MultiSphereHigh case"
|
||||
case BodyLOD::MultiSphereHigh:
|
||||
computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||
break;
|
||||
default:
|
||||
assert(false); // should never reach here
|
||||
break;
|
||||
}
|
||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
||||
auto shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
shape->setMargin(0.001f);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DetailedMotionState* OtherAvatar::createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex) {
|
||||
bool isBound = false;
|
||||
std::vector<int> boundJoints;
|
||||
btCollisionShape* shape = createCollisionShape(jointIndex, isBound, boundJoints);
|
||||
if (shape) {
|
||||
DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);
|
||||
motionState->setMass(computeMass());
|
||||
motionState->setIsBound(isBound, boundJoints);
|
||||
return motionState;
|
||||
}
|
||||
return nullptr;
|
||||
return ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
void OtherAvatar::resetDetailedMotionStates() {
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i] = nullptr;
|
||||
}
|
||||
// NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove
|
||||
// See AvatarManager::handleProcessedPhysicsTransaction()
|
||||
_detailedMotionStates.clear();
|
||||
}
|
||||
|
||||
|
@ -231,11 +215,11 @@ void OtherAvatar::computeShapeLOD() {
|
|||
}
|
||||
|
||||
bool OtherAvatar::isInPhysicsSimulation() const {
|
||||
return _motionState != nullptr && _detailedMotionStates.size() > 0;
|
||||
return _motionState && _motionState->getRigidBody();
|
||||
}
|
||||
|
||||
bool OtherAvatar::shouldBeInPhysicsSimulation() const {
|
||||
return !isDead() && !(isInPhysicsSimulation() && _needsReinsertion);
|
||||
return !isDead() && _workloadRegion < workload::Region::R3;
|
||||
}
|
||||
|
||||
bool OtherAvatar::needsPhysicsUpdate() const {
|
||||
|
@ -245,12 +229,9 @@ bool OtherAvatar::needsPhysicsUpdate() const {
|
|||
|
||||
void OtherAvatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
// do not actually rebuild here, instead flag for later
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
}
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
if (_detailedMotionStates[i]) {
|
||||
_detailedMotionStates[i]->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
}
|
||||
_needsReinsertion = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,25 +241,6 @@ void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar) {
|
||||
auto& detailedMotionStates = getDetailedMotionStates();
|
||||
assert(detailedMotionStates.empty());
|
||||
if (_bodyLOD == BodyLOD::Sphere) {
|
||||
auto dMotionState = createMotionState(avatar, -1);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < getJointCount(); i++) {
|
||||
auto dMotionState = createMotionState(avatar, i);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
}
|
||||
_needsReinsertion = false;
|
||||
}
|
||||
|
||||
void OtherAvatar::simulate(float deltaTime, bool inView) {
|
||||
PROFILE_RANGE(simulation, "simulate");
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@ public:
|
|||
bool shouldBeInPhysicsSimulation() const;
|
||||
bool needsPhysicsUpdate() const;
|
||||
|
||||
btCollisionShape* createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints);
|
||||
DetailedMotionState* createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex);
|
||||
void createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar);
|
||||
const btCollisionShape* createCollisionShape(int32_t jointIndex, bool& isBound, std::vector<int32_t>& boundJoints);
|
||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||
void resetDetailedMotionStates();
|
||||
BodyLOD getBodyLOD() { return _bodyLOD; }
|
||||
|
|
|
@ -65,15 +65,15 @@ void GraphicsEngine::initializeGPU(GLWidget* glwidget) {
|
|||
DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext);
|
||||
}
|
||||
|
||||
void GraphicsEngine::initializeRender(bool disableDeferred) {
|
||||
void GraphicsEngine::initializeRender() {
|
||||
|
||||
// Set up the render engine
|
||||
render::CullFunctor cullFunctor = LODManager::shouldRender;
|
||||
_renderEngine->addJob<UpdateSceneTask>("UpdateScene");
|
||||
#ifndef Q_OS_ANDROID
|
||||
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, !disableDeferred);
|
||||
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor);
|
||||
#endif
|
||||
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, !disableDeferred, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0);
|
||||
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0);
|
||||
_renderEngine->load();
|
||||
_renderEngine->registerScene(_renderScene);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
~GraphicsEngine();
|
||||
|
||||
void initializeGPU(GLWidget*);
|
||||
void initializeRender(bool disableDeferred);
|
||||
void initializeRender();
|
||||
void startup();
|
||||
void shutdown();
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace render {
|
|||
PerformanceTimer perfTimer("worldBox");
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, false, false, true, args->_renderMethod == Args::RenderMethod::FORWARD);
|
||||
WorldBoxRenderData::renderWorldBox(args, batch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,15 @@ int main(int argc, const char* argv[]) {
|
|||
// https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// Check the minimum version of
|
||||
if (gl::getAvailableVersion() < gl::getRequiredVersion()) {
|
||||
MessageBoxA(nullptr, "Interface requires OpenGL 4.1 or higher", "Unsupported", MB_OK);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
setupHifiApplication(BuildInfo::INTERFACE_NAME);
|
||||
|
||||
QStringList arguments;
|
||||
|
|
|
@ -168,7 +168,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
|
|||
bool hasAABox;
|
||||
entity->getAABox(hasAABox);
|
||||
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
||||
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad());
|
||||
return (!entity->shouldBePhysical() || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ const float ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_W
|
|||
const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA { false };
|
||||
const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_DRAWINFRONT { false };
|
||||
|
||||
gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_parabolaPipeline { nullptr };
|
||||
gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_transparentParabolaPipeline { nullptr };
|
||||
std::map<std::pair<bool, bool>, gpu::PipelinePointer> ParabolaPointer::RenderState::ParabolaRenderItem::_parabolaPipelines;
|
||||
|
||||
ParabolaPointer::ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
|
||||
const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd,
|
||||
|
@ -401,33 +400,34 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() {
|
|||
_bound = AABox(min, max - min);
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() {
|
||||
if (!_parabolaPipeline || !_transparentParabolaPipeline) {
|
||||
{
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline(bool forward) const {
|
||||
if (_parabolaPipelines.empty()) {
|
||||
using namespace shader::render_utils::program;
|
||||
|
||||
static const std::vector<std::tuple<bool, bool, uint32_t>> keys = {
|
||||
std::make_tuple(false, false, parabola), std::make_tuple(false, true, forward_parabola), std::make_tuple(true, false, parabola_translucent)/*, std::make_tuple(true, true, forward_parabola_translucent)*/
|
||||
};
|
||||
|
||||
for (auto& key : keys) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(false,
|
||||
if (std::get<0>(key)) {
|
||||
PrepareStencil::testMask(*state);
|
||||
} else {
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
}
|
||||
state->setBlendFunction(std::get<0>(key),
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
PrepareStencil::testMaskDrawShape(*state);
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
_parabolaPipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
_parabolaPipelines[{std::get<0>(key), std::get<1>(key)}] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state);
|
||||
}
|
||||
|
||||
{
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola_translucent);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
_transparentParabolaPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
// The forward opaque/translucent pipelines are the same for now
|
||||
_parabolaPipelines[{ true, true }] = _parabolaPipelines[{ false, true}];
|
||||
}
|
||||
return (_parabolaData.color.a < 1.0f ? _transparentParabolaPipeline : _parabolaPipeline);
|
||||
return _parabolaPipelines[{ _parabolaData.color.a < 1.0f, forward }];
|
||||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args) {
|
||||
|
@ -441,7 +441,7 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args)
|
|||
transform.setTranslation(_origin);
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
batch.setPipeline(getParabolaPipeline());
|
||||
batch.setPipeline(getParabolaPipeline(args->_renderMethod == render::Args::RenderMethod::FORWARD));
|
||||
|
||||
const int MAX_SECTIONS = 100;
|
||||
if (glm::length2(_parabolaData.acceleration) < EPSILON) {
|
||||
|
|
|
@ -26,9 +26,8 @@ public:
|
|||
bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled);
|
||||
~ParabolaRenderItem() {}
|
||||
|
||||
static gpu::PipelinePointer _parabolaPipeline;
|
||||
static gpu::PipelinePointer _transparentParabolaPipeline;
|
||||
const gpu::PipelinePointer getParabolaPipeline();
|
||||
static std::map<std::pair<bool, bool>, gpu::PipelinePointer> _parabolaPipelines;
|
||||
gpu::PipelinePointer getParabolaPipeline(bool forward) const;
|
||||
|
||||
void render(RenderArgs* args);
|
||||
render::Item::Bound& editBound() { return _bound; }
|
||||
|
|
|
@ -366,8 +366,10 @@ void Audio::onContextChanged() {
|
|||
void Audio::handlePushedToTalk(bool enabled) {
|
||||
if (getPTT()) {
|
||||
if (enabled) {
|
||||
DependencyManager::get<AudioClient>()->setOutputGain(0.1f); // duck the output by 20dB
|
||||
setMuted(false);
|
||||
} else {
|
||||
DependencyManager::get<AudioClient>()->setOutputGain(1.0f);
|
||||
setMuted(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ HMDScriptingInterface::HMDScriptingInterface() {
|
|||
connect(qApp, &Application::miniTabletEnabledChanged, [this](bool enabled) {
|
||||
emit miniTabletEnabledChanged(enabled);
|
||||
});
|
||||
connect(qApp, &Application::awayStateWhenFocusLostInVRChanged, [this](bool enabled) {
|
||||
emit awayStateWhenFocusLostInVRChanged(enabled);
|
||||
});
|
||||
}
|
||||
|
||||
glm::vec3 HMDScriptingInterface::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction) const {
|
||||
|
@ -137,6 +140,14 @@ bool HMDScriptingInterface::getMiniTabletEnabled() {
|
|||
return qApp->getMiniTabletEnabled();
|
||||
}
|
||||
|
||||
void HMDScriptingInterface::setAwayStateWhenFocusLostInVREnabled(bool enabled) {
|
||||
qApp->setAwayStateWhenFocusLostInVREnabled(enabled);
|
||||
}
|
||||
|
||||
bool HMDScriptingInterface::getAwayStateWhenFocusLostInVREnabled() {
|
||||
return qApp->getAwayStateWhenFocusLostInVREnabled();
|
||||
}
|
||||
|
||||
|
||||
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
||||
glm::vec3 hudIntersection;
|
||||
|
|
|
@ -375,6 +375,14 @@ signals:
|
|||
*/
|
||||
bool miniTabletEnabledChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the altering the mode for going into an away state when the interface focus is lost in VR.
|
||||
* @function HMD.awayStateWhenFocusLostInVRChanged
|
||||
* @param {boolean} enabled - <code>true</code> if the setting to go into an away state in VR when the interface focus is lost is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
bool awayStateWhenFocusLostInVRChanged(bool enabled);
|
||||
|
||||
public:
|
||||
HMDScriptingInterface();
|
||||
|
||||
|
@ -423,6 +431,9 @@ public:
|
|||
void setMiniTabletEnabled(bool enabled);
|
||||
bool getMiniTabletEnabled();
|
||||
|
||||
void setAwayStateWhenFocusLostInVREnabled(bool enabled);
|
||||
bool getAwayStateWhenFocusLostInVREnabled();
|
||||
|
||||
QVariant getPlayAreaRect();
|
||||
QVector<glm::vec3> getSensorPositions();
|
||||
|
||||
|
|
|
@ -92,6 +92,8 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
|
||||
auto mainWindow = qApp->getWindow();
|
||||
_dockWidget = std::shared_ptr<DockWidget>(new DockWidget(title, mainWindow), dockWidgetDeleter);
|
||||
auto quickView = _dockWidget->getQuickView();
|
||||
Application::setupQmlSurface(quickView->rootContext(), true);
|
||||
|
||||
if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) {
|
||||
DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt();
|
||||
|
@ -119,7 +121,6 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
}
|
||||
}
|
||||
|
||||
auto quickView = _dockWidget->getQuickView();
|
||||
QObject::connect(quickView.get(), &QQuickView::statusChanged, [&, this] (QQuickView::Status status) {
|
||||
if (status == QQuickView::Ready) {
|
||||
QQuickItem* rootItem = _dockWidget->getRootItem();
|
||||
|
|
|
@ -111,6 +111,12 @@ void setupPreferences() {
|
|||
auto setter = [](bool value) { qApp->setSettingConstrainToolbarPosition(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Constrain Toolbar Position to Horizontal Center", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getAwayStateWhenFocusLostInVREnabled(); };
|
||||
auto setter = [](bool value) { qApp->setAwayStateWhenFocusLostInVREnabled(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Go into away state when interface window loses focus in VR", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->float { return qApp->getDesktopTabletScale(); };
|
||||
|
|
|
@ -145,6 +145,7 @@ void GameWorkloadRenderItem::setAllViews(const workload::Views& views) {
|
|||
}
|
||||
|
||||
const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
|
||||
// FIXME: this needs a forward pipeline, or to only write to one output
|
||||
if (!_drawAllProxiesPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadProxy);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
@ -162,6 +163,7 @@ const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
|
|||
|
||||
|
||||
const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() {
|
||||
// FIXME: this needs a forward pipeline, or to only write to one output
|
||||
if (!_drawAllViewsPipeline) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadView);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
|
|
@ -221,6 +221,35 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels,
|
|||
return (float)loudness * scale;
|
||||
}
|
||||
|
||||
template <int NUM_CHANNELS>
|
||||
static void applyGainSmoothing(float* buffer, int numFrames, float gain0, float gain1) {
|
||||
|
||||
// fast path for unity gain
|
||||
if (gain0 == 1.0f && gain1 == 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cubic poly from gain0 to gain1
|
||||
float c3 = -2.0f * (gain1 - gain0);
|
||||
float c2 = 3.0f * (gain1 - gain0);
|
||||
float c0 = gain0;
|
||||
|
||||
float t = 0.0f;
|
||||
float tStep = 1.0f / numFrames;
|
||||
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
||||
// evaluate poly over t=[0,1)
|
||||
float gain = (c3 * t + c2) * t * t + c0;
|
||||
t += tStep;
|
||||
|
||||
// apply gain to all channels
|
||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||
buffer[NUM_CHANNELS*i + ch] *= gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline float convertToFloat(int16_t sample) {
|
||||
return (float)sample * (1 / 32768.0f);
|
||||
}
|
||||
|
@ -2109,6 +2138,14 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
int framesPopped = samplesPopped / AudioConstants::STEREO;
|
||||
int bytesWritten;
|
||||
if (samplesPopped > 0) {
|
||||
|
||||
// apply output gain
|
||||
float newGain = _audio->_outputGain.load(std::memory_order_acquire);
|
||||
float oldGain = _audio->_lastOutputGain;
|
||||
_audio->_lastOutputGain = newGain;
|
||||
|
||||
applyGainSmoothing<OUTPUT_CHANNEL_COUNT>(mixBuffer, framesPopped, oldGain, newGain);
|
||||
|
||||
if (deviceChannelCount == OUTPUT_CHANNEL_COUNT) {
|
||||
// limit the audio
|
||||
_audio->_audioLimiter.render(mixBuffer, (int16_t*)data, framesPopped);
|
||||
|
|
|
@ -241,8 +241,10 @@ public slots:
|
|||
void setInputVolume(float volume, bool emitSignal = true);
|
||||
void setReverb(bool reverb);
|
||||
void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
void setLocalInjectorGain(float gain) { _localInjectorGain = gain; };
|
||||
void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; };
|
||||
void setOutputGain(float gain) { _outputGain = gain; };
|
||||
|
||||
void outputNotify();
|
||||
|
||||
|
@ -395,6 +397,8 @@ private:
|
|||
int _outputPeriod { 0 };
|
||||
float* _outputMixBuffer { NULL };
|
||||
int16_t* _outputScratchBuffer { NULL };
|
||||
std::atomic<float> _outputGain { 1.0f };
|
||||
float _lastOutputGain { 1.0f };
|
||||
|
||||
// for local audio (used by audio injectors thread)
|
||||
std::atomic<float> _localInjectorGain { 1.0f };
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include "InboundAudioStream.h"
|
||||
#include "TryLocker.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -215,7 +216,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
if (framesAvailable > _desiredJitterBufferFrames + MAX_FRAMES_OVER_DESIRED) {
|
||||
int framesToDrop = framesAvailable - (_desiredJitterBufferFrames + DESIRED_JITTER_BUFFER_FRAMES_PADDING);
|
||||
_ringBuffer.shiftReadPosition(framesToDrop * _ringBuffer.getNumFrameSamples());
|
||||
|
||||
|
||||
_framesAvailableStat.reset();
|
||||
_currentJitterBufferFrames = 0;
|
||||
|
||||
|
@ -247,10 +248,18 @@ int InboundAudioStream::lostAudioData(int numPackets) {
|
|||
QByteArray decodedBuffer;
|
||||
|
||||
while (numPackets--) {
|
||||
MutexTryLocker lock(_decoderMutex);
|
||||
if (!lock.isLocked()) {
|
||||
// an incoming packet is being processed,
|
||||
// and will likely be on the ring buffer shortly,
|
||||
// so don't bother generating more data
|
||||
qCInfo(audiostream, "Packet currently being unpacked or lost frame already being generated. Not generating lost frame.");
|
||||
return 0;
|
||||
}
|
||||
if (_decoder) {
|
||||
_decoder->lostFrame(decodedBuffer);
|
||||
} else {
|
||||
decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO);
|
||||
decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * _numChannels);
|
||||
memset(decodedBuffer.data(), 0, decodedBuffer.size());
|
||||
}
|
||||
_ringBuffer.writeData(decodedBuffer.data(), decodedBuffer.size());
|
||||
|
@ -260,6 +269,12 @@ int InboundAudioStream::lostAudioData(int numPackets) {
|
|||
|
||||
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
// parseAudioData is only called by the packet processing
|
||||
// thread which, while high performance, is not as sensitive to
|
||||
// delays as the real-time thread.
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
if (_decoder) {
|
||||
_decoder->decode(packetAfterStreamProperties, decodedBuffer);
|
||||
} else {
|
||||
|
@ -278,16 +293,23 @@ int InboundAudioStream::writeDroppableSilentFrames(int silentFrames) {
|
|||
// case we will call the decoder's lostFrame() method, which indicates
|
||||
// that it should interpolate from its last known state down toward
|
||||
// silence.
|
||||
if (_decoder) {
|
||||
// FIXME - We could potentially use the output from the codec, in which
|
||||
// case we might get a cleaner fade toward silence. NOTE: The below logic
|
||||
// attempts to catch up in the event that the jitter buffers have grown.
|
||||
// The better long term fix is to use the output from the decode, detect
|
||||
// when it actually reaches silence, and then delete the silent portions
|
||||
// of the jitter buffers. Or petentially do a cross fade from the decode
|
||||
// output to silence.
|
||||
QByteArray decodedBuffer;
|
||||
_decoder->lostFrame(decodedBuffer);
|
||||
{
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
// writeDroppableSilentFrames is only called by the packet processing
|
||||
// thread which, while high performance, is not as sensitive to
|
||||
// delays as the real-time thread.
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
if (_decoder) {
|
||||
// FIXME - We could potentially use the output from the codec, in which
|
||||
// case we might get a cleaner fade toward silence. NOTE: The below logic
|
||||
// attempts to catch up in the event that the jitter buffers have grown.
|
||||
// The better long term fix is to use the output from the decode, detect
|
||||
// when it actually reaches silence, and then delete the silent portions
|
||||
// of the jitter buffers. Or petentially do a cross fade from the decode
|
||||
// output to silence.
|
||||
QByteArray decodedBuffer;
|
||||
_decoder->lostFrame(decodedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate how many silent frames we should drop.
|
||||
|
@ -338,10 +360,23 @@ int InboundAudioStream::popSamples(int maxSamples, bool allOrNothing) {
|
|||
popSamplesNoCheck(samplesAvailable);
|
||||
samplesPopped = samplesAvailable;
|
||||
} else {
|
||||
// we can't pop any samples, set this stream to starved
|
||||
// we can't pop any samples, set this stream to starved for jitter
|
||||
// buffer calculations.
|
||||
setToStarved();
|
||||
_consecutiveNotMixedCount++;
|
||||
_lastPopSucceeded = false;
|
||||
//Kick PLC to generate a filler frame, reducing 'click'
|
||||
lostAudioData(allOrNothing ? (maxSamples - samplesAvailable) / _ringBuffer.getNumFrameSamples() : 1);
|
||||
samplesPopped = _ringBuffer.samplesAvailable();
|
||||
if (samplesPopped) {
|
||||
popSamplesNoCheck(samplesPopped);
|
||||
} else {
|
||||
// No samples available means a packet is currently being
|
||||
// processed, so we don't generate lost audio data, and instead
|
||||
// just wait for the packet to come in. This prevents locking
|
||||
// the real-time audio thread at the cost of a potential (but rare)
|
||||
// 'click'
|
||||
_lastPopSucceeded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return samplesPopped;
|
||||
|
@ -528,6 +563,7 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod
|
|||
_codec = codec;
|
||||
_selectedCodecName = codecName;
|
||||
if (_codec) {
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
_decoder = codec->createDecoder(AudioConstants::SAMPLE_RATE, numChannels);
|
||||
}
|
||||
}
|
||||
|
@ -535,6 +571,7 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod
|
|||
void InboundAudioStream::cleanupCodec() {
|
||||
// release any old codec encoder/decoder first...
|
||||
if (_codec) {
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
if (_decoder) {
|
||||
_codec->releaseDecoder(_decoder);
|
||||
_decoder = nullptr;
|
||||
|
|
|
@ -187,6 +187,7 @@ protected:
|
|||
|
||||
CodecPluginPointer _codec;
|
||||
QString _selectedCodecName;
|
||||
QMutex _decoderMutex;
|
||||
Decoder* _decoder { nullptr };
|
||||
int _mismatchedAudioCodecCount { 0 };
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "MixedProcessedAudioStream.h"
|
||||
#include "AudioLogging.h"
|
||||
#include "TryLocker.h"
|
||||
|
||||
MixedProcessedAudioStream::MixedProcessedAudioStream(int numFramesCapacity, int numStaticJitterFrames)
|
||||
: InboundAudioStream(AudioConstants::STEREO, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL,
|
||||
|
@ -36,13 +37,20 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) {
|
|||
QByteArray outputBuffer;
|
||||
|
||||
while (numPackets--) {
|
||||
MutexTryLocker lock(_decoderMutex);
|
||||
if (!lock.isLocked()) {
|
||||
// an incoming packet is being processed,
|
||||
// and will likely be on the ring buffer shortly,
|
||||
// so don't bother generating more data
|
||||
qCInfo(audiostream, "Packet currently being unpacked or lost frame already being generated. Not generating lost frame.");
|
||||
return 0;
|
||||
}
|
||||
if (_decoder) {
|
||||
_decoder->lostFrame(decodedBuffer);
|
||||
} else {
|
||||
decodedBuffer.resize(AudioConstants::NETWORK_FRAME_BYTES_STEREO);
|
||||
memset(decodedBuffer.data(), 0, decodedBuffer.size());
|
||||
}
|
||||
|
||||
emit addedStereoSamples(decodedBuffer);
|
||||
|
||||
emit processSamples(decodedBuffer, outputBuffer);
|
||||
|
@ -55,6 +63,12 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) {
|
|||
|
||||
int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
// parseAudioData is only called by the packet processing
|
||||
// thread which, while high performance, is not as sensitive to
|
||||
// delays as the real-time thread.
|
||||
QMutexLocker lock(&_decoderMutex);
|
||||
if (_decoder) {
|
||||
_decoder->decode(packetAfterStreamProperties, decodedBuffer);
|
||||
} else {
|
||||
|
|
|
@ -690,6 +690,11 @@ void Avatar::fade(render::Transaction& transaction, render::Transition::Type typ
|
|||
transaction.addTransitionToItem(itemId, type, _renderItemID);
|
||||
}
|
||||
}
|
||||
_lastFadeRequested = type;
|
||||
}
|
||||
|
||||
render::Transition::Type Avatar::getLastFadeRequested() const {
|
||||
return _lastFadeRequested;
|
||||
}
|
||||
|
||||
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
|
@ -779,7 +784,7 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
pointerTransform.setTranslation(position);
|
||||
pointerTransform.setRotation(rotation);
|
||||
batch.setModelTransform(pointerTransform);
|
||||
geometryCache->bindSimpleProgram(batch);
|
||||
geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _leftPointerGeometryID);
|
||||
}
|
||||
}
|
||||
|
@ -803,7 +808,7 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
pointerTransform.setTranslation(position);
|
||||
pointerTransform.setRotation(rotation);
|
||||
batch.setModelTransform(pointerTransform);
|
||||
geometryCache->bindSimpleProgram(batch);
|
||||
geometryCache->bindSimpleProgram(batch, false, false, true, false, false, true, renderArgs->_renderMethod == render::Args::FORWARD);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor, _rightPointerGeometryID);
|
||||
}
|
||||
}
|
||||
|
@ -829,7 +834,7 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
auto& frustum = renderArgs->getViewFrustum();
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
if (frustum.pointIntersectsFrustum(textPosition)) {
|
||||
renderDisplayName(batch, frustum, textPosition);
|
||||
renderDisplayName(batch, frustum, textPosition, renderArgs->_renderMethod == render::Args::FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1034,7 +1039,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const g
|
|||
return result;
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const {
|
||||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition, bool forward) const {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
|
||||
|
||||
bool shouldShowReceiveStats = showReceiveStats && !isMyAvatar();
|
||||
|
@ -1090,7 +1095,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|
|||
|
||||
{
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true);
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true, true, forward);
|
||||
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
|
||||
bevelDistance, backgroundColor, _nameRectGeometryID);
|
||||
}
|
||||
|
@ -1103,7 +1108,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
|
|||
batch.setModelTransform(textTransform);
|
||||
{
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), forward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -523,6 +523,7 @@ public:
|
|||
|
||||
void fadeIn(render::ScenePointer scene);
|
||||
void fadeOut(render::Transaction& transaction, KillAvatarReason reason);
|
||||
render::Transition::Type getLastFadeRequested() const;
|
||||
|
||||
// JSDoc is in AvatarData.h.
|
||||
Q_INVOKABLE virtual float getEyeHeight() const override;
|
||||
|
@ -694,13 +695,14 @@ protected:
|
|||
glm::vec3 getDisplayNamePosition() const;
|
||||
|
||||
Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition, bool forward) const;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
|
||||
virtual void fixupModelsInScene(const render::ScenePointer& scene);
|
||||
|
||||
virtual void updatePalms();
|
||||
|
||||
render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID };
|
||||
render::Transition::Type _lastFadeRequested { render::Transition::Type::NONE }; // Used for sanity checking
|
||||
|
||||
ThreadSafeValueCache<glm::vec3> _leftPalmPositionCache { glm::vec3() };
|
||||
ThreadSafeValueCache<glm::quat> _leftPalmRotationCache { glm::quat() };
|
||||
|
|
|
@ -481,6 +481,12 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::preUpdate() {
|
||||
if (_tree && !_shuttingDown) {
|
||||
_tree->preUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::update(bool simulate) {
|
||||
PROFILE_RANGE(simulation_physics, "ETR::update");
|
||||
PerformanceTimer perfTimer("ETRupdate");
|
||||
|
@ -585,7 +591,7 @@ void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet<EntityItemI
|
|||
if (contains) {
|
||||
// if this entity is a zone and visible, add it to our layered zones
|
||||
if (isZone && entity->getVisible() && renderableIdForEntity(entity) != render::Item::INVALID_ITEM_ID) {
|
||||
_layeredZones.emplace(std::dynamic_pointer_cast<ZoneEntityItem>(entity));
|
||||
_layeredZones.emplace_back(std::dynamic_pointer_cast<ZoneEntityItem>(entity));
|
||||
}
|
||||
|
||||
if ((!hasScript && isZone) || scriptHasLoaded) {
|
||||
|
@ -594,6 +600,7 @@ void EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QSet<EntityItemI
|
|||
}
|
||||
}
|
||||
|
||||
_layeredZones.sort();
|
||||
if (!_layeredZones.equals(oldLayeredZones)) {
|
||||
applyLayeredZones();
|
||||
}
|
||||
|
@ -1181,66 +1188,73 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
|
|||
|
||||
void EntityTreeRenderer::updateZone(const EntityItemID& id) {
|
||||
if (auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(getTree()->findEntityByEntityItemID(id))) {
|
||||
_layeredZones.update(zone, _avatarPosition, this);
|
||||
applyLayeredZones();
|
||||
if (_layeredZones.update(zone, _avatarPosition, this)) {
|
||||
applyLayeredZones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::LayeredZones::clearDomainAndNonOwnedZones(const QUuid& sessionUUID) {
|
||||
bool zonesChanged = false;
|
||||
|
||||
auto it = c.begin();
|
||||
while (it != c.end()) {
|
||||
auto it = begin();
|
||||
while (it != end()) {
|
||||
auto zone = it->zone.lock();
|
||||
if (!zone || !(zone->isLocalEntity() || (zone->isAvatarEntity() && zone->getOwningAvatarID() == sessionUUID))) {
|
||||
zonesChanged = true;
|
||||
it = c.erase(it);
|
||||
it = erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
if (zonesChanged) {
|
||||
std::make_heap(c.begin(), c.end(), comp);
|
||||
sort();
|
||||
}
|
||||
return zonesChanged;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> EntityTreeRenderer::LayeredZones::getZoneInteractionProperties() const {
|
||||
auto it = c.cbegin();
|
||||
while (it != c.cend()) {
|
||||
for (auto it = cbegin(); it != cend(); it++) {
|
||||
auto zone = it->zone.lock();
|
||||
if (zone && zone->isDomainEntity()) {
|
||||
return { zone->getFlyingAllowed(), zone->getGhostingAllowed() };
|
||||
}
|
||||
it++;
|
||||
}
|
||||
return { true, true };
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::LayeredZones::remove(const std::shared_ptr<ZoneEntityItem>& zone) {
|
||||
auto it = c.begin();
|
||||
while (it != c.end()) {
|
||||
if (it->zone.lock() == zone) {
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (it != c.end()) {
|
||||
c.erase(it);
|
||||
std::make_heap(c.begin(), c.end(), comp);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) {
|
||||
bool EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer) {
|
||||
// When a zone's position or visibility changes, we call this method
|
||||
// In order to resort our zones, we first remove the changed zone, and then re-insert it if necessary
|
||||
remove(zone);
|
||||
|
||||
bool needsResort = false;
|
||||
|
||||
{
|
||||
auto it = begin();
|
||||
while (it != end()) {
|
||||
if (it->zone.lock() == zone) {
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (it != end()) {
|
||||
erase(it);
|
||||
needsResort = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only call contains if the zone is rendering
|
||||
if (zone->isVisible() && entityTreeRenderer->renderableIdForEntity(zone) != render::Item::INVALID_ITEM_ID && zone->contains(position)) {
|
||||
emplace(zone);
|
||||
emplace_back(zone);
|
||||
needsResort = true;
|
||||
}
|
||||
|
||||
if (needsResort) {
|
||||
sort();
|
||||
}
|
||||
|
||||
return needsResort;
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const {
|
||||
|
@ -1248,9 +1262,9 @@ bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
auto it = c.cbegin();
|
||||
auto otherIt = other.c.cbegin();
|
||||
while (it != c.cend()) {
|
||||
auto it = cbegin();
|
||||
auto otherIt = other.cbegin();
|
||||
while (it != cend()) {
|
||||
if (*it != *otherIt) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1262,15 +1276,13 @@ bool EntityTreeRenderer::LayeredZones::equals(const LayeredZones& other) const {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::LayeredZones::appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const {
|
||||
auto it = c.cbegin();
|
||||
while (it != c.cend()) {
|
||||
for (auto it = cbegin(); it != cend(); it++) {
|
||||
if (it->zone.lock()) {
|
||||
auto id = entityTreeRenderer->renderableIdForEntityId(it->id);
|
||||
if (id != render::Item::INVALID_ITEM_ID) {
|
||||
list.push_back(id);
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
void setSetPrecisionPickingOperator(std::function<void(unsigned int, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
||||
|
||||
void shutdown();
|
||||
void preUpdate();
|
||||
void update(bool simulate);
|
||||
|
||||
EntityTreePointer getTree() { return std::static_pointer_cast<EntityTree>(_tree); }
|
||||
|
@ -212,24 +213,24 @@ private:
|
|||
public:
|
||||
LayeredZone(std::shared_ptr<ZoneEntityItem> zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {}
|
||||
|
||||
bool operator>(const LayeredZone& r) const { return volume > r.volume; }
|
||||
bool operator==(const LayeredZone& r) const { return zone.lock() == r.zone.lock(); }
|
||||
// We need to sort on volume AND id so that different clients sort zones with identical volumes the same way
|
||||
bool operator<(const LayeredZone& r) const { return volume < r.volume || (volume == r.volume && id < r.id); }
|
||||
bool operator==(const LayeredZone& r) const { return zone.lock() && zone.lock() == r.zone.lock(); }
|
||||
bool operator!=(const LayeredZone& r) const { return !(*this == r); }
|
||||
bool operator>=(const LayeredZone& r) const { return (*this > r) || (*this == r); }
|
||||
bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); }
|
||||
|
||||
std::weak_ptr<ZoneEntityItem> zone;
|
||||
QUuid id;
|
||||
float volume;
|
||||
};
|
||||
|
||||
class LayeredZones : public std::priority_queue<LayeredZone, std::vector<LayeredZone>, std::greater<LayeredZone>> {
|
||||
class LayeredZones : public std::vector<LayeredZone> {
|
||||
public:
|
||||
void clear() { *this = LayeredZones(); }
|
||||
bool clearDomainAndNonOwnedZones(const QUuid& sessionUUID);
|
||||
|
||||
void sort() { std::sort(begin(), end(), std::less<LayeredZone>()); }
|
||||
bool equals(const LayeredZones& other) const;
|
||||
void remove(const std::shared_ptr<ZoneEntityItem>& zone);
|
||||
void update(std::shared_ptr<ZoneEntityItem> zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer);
|
||||
bool update(std::shared_ptr<ZoneEntityItem> zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer);
|
||||
|
||||
void appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const;
|
||||
std::pair<bool, bool> getZoneInteractionProperties() const;
|
||||
|
|
|
@ -261,15 +261,17 @@ void GizmoEntityRenderer::doRender(RenderArgs* args) {
|
|||
Transform transform;
|
||||
bool hasTickMarks;
|
||||
glm::vec4 tickProperties;
|
||||
bool forward;
|
||||
withReadLock([&] {
|
||||
transform = _renderTransform;
|
||||
hasTickMarks = _ringProperties.getHasTickMarks();
|
||||
tickProperties = glm::vec4(_ringProperties.getMajorTickMarksAngle(), _ringProperties.getMajorTickMarksLength(),
|
||||
_ringProperties.getMinorTickMarksAngle(), _ringProperties.getMinorTickMarksLength());
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD;
|
||||
});
|
||||
|
||||
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
|
||||
geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, _renderLayer != RenderLayer::WORLD);
|
||||
geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, forward);
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
|
|
|
@ -113,11 +113,13 @@ void GridEntityRenderer::doRender(RenderArgs* args) {
|
|||
glm::vec4 color;
|
||||
glm::vec3 dimensions;
|
||||
Transform renderTransform;
|
||||
bool forward;
|
||||
withReadLock([&] {
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
renderTransform = _renderTransform;
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD;
|
||||
});
|
||||
|
||||
if (!_visible) {
|
||||
|
@ -153,5 +155,5 @@ void GridEntityRenderer::doRender(RenderArgs* args) {
|
|||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
|
||||
minorGridRowDivisions, minorGridColDivisions, MINOR_GRID_EDGE,
|
||||
majorGridRowDivisions, majorGridColDivisions, MAJOR_GRID_EDGE,
|
||||
color, _geometryId);
|
||||
color, forward, _geometryId);
|
||||
}
|
|
@ -55,7 +55,8 @@ void LineEntityRenderer::doRender(RenderArgs* args) {
|
|||
transform.setRotation(modelTransform.getRotation());
|
||||
batch.setModelTransform(transform);
|
||||
if (_linePoints.size() > 1) {
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, false, false, true,
|
||||
_renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD);
|
||||
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -735,13 +735,15 @@ bool RenderableModelEntityItem::shouldBePhysical() const {
|
|||
auto model = getModel();
|
||||
// If we have a model, make sure it hasn't failed to download.
|
||||
// If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready.
|
||||
if (model && (getShapeType() == SHAPE_TYPE_COMPOUND || getShapeType() == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) {
|
||||
return false;
|
||||
} else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) {
|
||||
return false;
|
||||
} else {
|
||||
return ModelEntityItem::shouldBePhysical();
|
||||
ShapeType shapeType = getShapeType();
|
||||
if (model) {
|
||||
if ((shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) {
|
||||
return false;
|
||||
} else if (shapeType != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !isDead() && shapeType != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid();
|
||||
}
|
||||
|
||||
int RenderableModelEntityItem::getJointParent(int index) const {
|
||||
|
@ -1520,7 +1522,7 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
|
|||
model = _model;
|
||||
});
|
||||
if (model) {
|
||||
model->renderDebugMeshBoxes(batch);
|
||||
model->renderDebugMeshBoxes(batch, args->_renderMethod == Args::RenderMethod::FORWARD);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -19,15 +19,12 @@
|
|||
#include <PerfStat.h>
|
||||
#include <shaders/Shaders.h>
|
||||
|
||||
#include <DisableDeferred.h>
|
||||
|
||||
#include "paintStroke_Shared.slh"
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
gpu::PipelinePointer PolyLineEntityRenderer::_pipeline = nullptr;
|
||||
gpu::PipelinePointer PolyLineEntityRenderer::_glowPipeline = nullptr;
|
||||
std::map<std::pair<render::Args::RenderMethod, bool>, gpu::PipelinePointer> PolyLineEntityRenderer::_pipelines;
|
||||
|
||||
static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png");
|
||||
|
||||
|
@ -44,29 +41,24 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
|
|||
}
|
||||
}
|
||||
|
||||
void PolyLineEntityRenderer::buildPipeline() {
|
||||
// FIXME: opaque pipeline
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(DISABLE_DEFERRED ? shader::entities_renderer::program::paintStroke_forward : shader::entities_renderer::program::paintStroke);
|
||||
void PolyLineEntityRenderer::buildPipelines() {
|
||||
// FIXME: opaque pipelines
|
||||
|
||||
static const std::vector<std::pair<render::Args::RenderMethod, bool>> keys = {
|
||||
{ render::Args::DEFERRED, false }, { render::Args::DEFERRED, true }, { render::Args::FORWARD, false }, { render::Args::FORWARD, true },
|
||||
};
|
||||
|
||||
for (auto& key : keys) {
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(key.first == render::Args::DEFERRED ? shader::entities_renderer::program::paintStroke : shader::entities_renderer::program::paintStroke_forward);
|
||||
|
||||
{
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setCullMode(gpu::State::CullMode::CULL_NONE);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setDepthTest(true, !key.second, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
{
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setCullMode(gpu::State::CullMode::CULL_NONE);
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
PrepareStencil::testMask(*state);
|
||||
state->setBlendFunction(true,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_glowPipeline = gpu::Pipeline::create(program, state);
|
||||
_pipelines[key] = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,11 +291,11 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_pipeline) {
|
||||
buildPipeline();
|
||||
if (_pipelines.empty()) {
|
||||
buildPipelines();
|
||||
}
|
||||
|
||||
batch.setPipeline(_glow ? _glowPipeline : _pipeline);
|
||||
batch.setPipeline(_pipelines[{args->_renderMethod, _glow}]);
|
||||
batch.setModelTransform(transform);
|
||||
batch.setResourceTexture(0, texture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0);
|
||||
|
|
|
@ -37,7 +37,7 @@ protected:
|
|||
virtual ShapeKey getShapeKey() override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
void buildPipeline();
|
||||
static void buildPipelines();
|
||||
void updateGeometry();
|
||||
void updateData();
|
||||
|
||||
|
@ -58,8 +58,7 @@ protected:
|
|||
size_t _numVertices;
|
||||
gpu::BufferPointer _polylineDataBuffer;
|
||||
gpu::BufferPointer _polylineGeometryBuffer;
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
static gpu::PipelinePointer _glowPipeline;
|
||||
static std::map<std::pair<render::Args::RenderMethod, bool>, gpu::PipelinePointer> _pipelines;
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
|
|
@ -72,7 +72,6 @@ public:
|
|||
glm::mat4 localToVoxelMatrix() const;
|
||||
|
||||
virtual ShapeType getShapeType() const override;
|
||||
virtual bool shouldBePhysical() const override { return !isDead(); }
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
render::ShapePipelinePointer pipeline;
|
||||
if (renderLayer == RenderLayer::WORLD) {
|
||||
if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) {
|
||||
pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||
} else {
|
||||
pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
|
||||
|
|
|
@ -163,7 +163,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
Transform modelTransform;
|
||||
glm::vec3 dimensions;
|
||||
BillboardMode billboardMode;
|
||||
bool layered;
|
||||
bool forward;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
dimensions = _dimensions;
|
||||
|
@ -174,7 +174,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created);
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
layered = _renderLayer != RenderLayer::WORLD;
|
||||
forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD;
|
||||
});
|
||||
|
||||
// Render background
|
||||
|
@ -187,7 +187,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
// FIXME: we need to find a better way of rendering text so we don't have to do this
|
||||
if (layered) {
|
||||
if (forward) {
|
||||
DependencyManager::get<DeferredLightingEffect>()->setupKeyLightBatch(args, batch);
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
if (backgroundColor.a > 0.0f) {
|
||||
batch.setModelTransform(transformToTopLeft);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, layered);
|
||||
geometryCache->bindSimpleProgram(batch, false, backgroundColor.a < 1.0f, false, false, false, true, forward);
|
||||
geometryCache->renderQuad(batch, minCorner, maxCorner, backgroundColor, _geometryID);
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
|
|||
batch.setModelTransform(transformToTopLeft);
|
||||
|
||||
glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin));
|
||||
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, layered);
|
||||
_textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, forward);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1828,42 +1828,42 @@ void EntityItem::setParentID(const QUuid& value) {
|
|||
if (!value.isNull() && tree) {
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(value);
|
||||
if (entity) {
|
||||
newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldParentID.isNull() && tree) {
|
||||
EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID);
|
||||
if (entity) {
|
||||
oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) {
|
||||
newParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
newParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
}
|
||||
|
||||
if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) {
|
||||
oldParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
oldParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
}
|
||||
|
||||
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
|
||||
if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
||||
markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||
markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
if (object->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||
if (object->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2102,7 +2102,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask
|
|||
}
|
||||
}
|
||||
|
||||
if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
||||
if ((bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
|
||||
}
|
||||
mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask);
|
||||
|
@ -2173,8 +2173,8 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin
|
|||
}
|
||||
|
||||
void EntityItem::enableNoBootstrap() {
|
||||
if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
||||
_flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
if (!(bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||
_flags |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
|
||||
// NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here
|
||||
|
@ -2186,7 +2186,7 @@ void EntityItem::enableNoBootstrap() {
|
|||
if (child->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2194,7 +2194,7 @@ void EntityItem::enableNoBootstrap() {
|
|||
|
||||
void EntityItem::disableNoBootstrap() {
|
||||
if (!stillHasMyGrabAction()) {
|
||||
_flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
||||
_flags &= ~Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||
|
||||
EntityTreePointer entityTree = getTree();
|
||||
|
@ -2207,7 +2207,7 @@ void EntityItem::disableNoBootstrap() {
|
|||
if (child->getNestableType() == NestableType::Entity) {
|
||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||
simulation->changeEntity(entity);
|
||||
}
|
||||
});
|
||||
|
@ -2326,7 +2326,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) {
|
||||
disableNoBootstrap();
|
||||
} else {
|
||||
// NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct
|
||||
// NO-OP: we assume SPECIAL_FLAG_NO_BOOTSTRAPPING bits and collision group are correct
|
||||
// because they should have been set correctly when the action was added
|
||||
// and/or when children were linked
|
||||
}
|
||||
|
@ -3154,21 +3154,21 @@ DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVer
|
|||
uint32_t EntityItem::getDirtyFlags() const {
|
||||
uint32_t result;
|
||||
withReadLock([&] {
|
||||
result = _flags & Simulation::DIRTY_FLAGS;
|
||||
result = _flags & Simulation::DIRTY_FLAGS_MASK;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::markDirtyFlags(uint32_t mask) {
|
||||
withWriteLock([&] {
|
||||
mask &= Simulation::DIRTY_FLAGS;
|
||||
mask &= Simulation::DIRTY_FLAGS_MASK;
|
||||
_flags |= mask;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::clearDirtyFlags(uint32_t mask) {
|
||||
withWriteLock([&] {
|
||||
mask &= Simulation::DIRTY_FLAGS;
|
||||
mask &= Simulation::DIRTY_FLAGS_MASK;
|
||||
_flags &= ~mask;
|
||||
});
|
||||
}
|
||||
|
@ -3176,21 +3176,21 @@ void EntityItem::clearDirtyFlags(uint32_t mask) {
|
|||
uint32_t EntityItem::getSpecialFlags() const {
|
||||
uint32_t result;
|
||||
withReadLock([&] {
|
||||
result = _flags & Simulation::SPECIAL_FLAGS;
|
||||
result = _flags & Simulation::SPECIAL_FLAGS_MASK;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::markSpecialFlags(uint32_t mask) {
|
||||
withWriteLock([&] {
|
||||
mask &= Simulation::SPECIAL_FLAGS;
|
||||
mask &= Simulation::SPECIAL_FLAGS_MASK;
|
||||
_flags |= mask;
|
||||
});
|
||||
}
|
||||
|
||||
void EntityItem::clearSpecialFlags(uint32_t mask) {
|
||||
withWriteLock([&] {
|
||||
mask &= Simulation::SPECIAL_FLAGS;
|
||||
mask &= Simulation::SPECIAL_FLAGS_MASK;
|
||||
_flags &= ~mask;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||
#include <OctreePacketData.h>
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <SimulationFlags.h>
|
||||
#include <ShapeInfo.h>
|
||||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
|
@ -33,11 +34,11 @@
|
|||
#include "EntityPropertyFlags.h"
|
||||
#include "EntityTypes.h"
|
||||
#include "SimulationOwner.h"
|
||||
#include "SimulationFlags.h"
|
||||
#include "EntityDynamicInterface.h"
|
||||
#include "GrabPropertyGroup.h"
|
||||
|
||||
class EntitySimulation;
|
||||
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
|
||||
class EntityTreeElement;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
class EntityDynamicInterface;
|
||||
|
@ -322,7 +323,7 @@ public:
|
|||
bool getDynamic() const;
|
||||
void setDynamic(bool value);
|
||||
|
||||
virtual bool shouldBePhysical() const { return false; }
|
||||
virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE; }
|
||||
bool isVisuallyReady() const { return _visuallyReady; }
|
||||
|
||||
bool getLocked() const;
|
||||
|
@ -423,8 +424,9 @@ public:
|
|||
|
||||
bool isSimulated() const { return _simulated; }
|
||||
|
||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
bool isInPhysicsSimulation() const { return (bool)(_flags & Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); }
|
||||
|
||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
|
||||
EntityTreeElementPointer getElement() const { return _element; }
|
||||
|
|
|
@ -176,47 +176,44 @@ void EntitySimulation::addEntity(EntityItemPointer entity) {
|
|||
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
if (!entity->isSimulated()) {
|
||||
// This entity was either never added to the simulation or has been removed
|
||||
// (probably for pending delete), so we don't want to keep a pointer to it
|
||||
// on any internal lists.
|
||||
return;
|
||||
}
|
||||
_changedEntities.insert(entity);
|
||||
}
|
||||
|
||||
// Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes
|
||||
// it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence
|
||||
// we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
|
||||
void EntitySimulation::processChangedEntities() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
PROFILE_RANGE_EX(simulation_physics, "processChangedEntities", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||
for (auto& entity : _changedEntities) {
|
||||
if (entity->isSimulated()) {
|
||||
processChangedEntity(entity);
|
||||
}
|
||||
}
|
||||
_changedEntities.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||
if (dirtyFlags & Simulation::DIRTY_POSITION) {
|
||||
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
|
||||
bool success;
|
||||
AACube newCube = entity->getQueryAACube(success);
|
||||
if (success && !domainBounds.touches(newCube)) {
|
||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
||||
entity->die();
|
||||
prepareEntityForDelete(entity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
uint64_t expiry = entity->getExpiry();
|
||||
if (expiry < _nextExpiry) {
|
||||
_nextExpiry = expiry;
|
||||
if (dirtyFlags & (Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE)) {
|
||||
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
uint64_t expiry = entity->getExpiry();
|
||||
if (expiry < _nextExpiry) {
|
||||
_nextExpiry = expiry;
|
||||
}
|
||||
} else {
|
||||
_mortalEntities.remove(entity);
|
||||
}
|
||||
} else {
|
||||
_mortalEntities.remove(entity);
|
||||
}
|
||||
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME);
|
||||
if (dirtyFlags & Simulation::DIRTY_UPDATEABLE) {
|
||||
if (entity->needsToCallUpdate()) {
|
||||
_entitiesToUpdate.insert(entity);
|
||||
} else {
|
||||
_entitiesToUpdate.remove(entity);
|
||||
}
|
||||
}
|
||||
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE);
|
||||
}
|
||||
if (entity->needsToCallUpdate()) {
|
||||
_entitiesToUpdate.insert(entity);
|
||||
} else {
|
||||
_entitiesToUpdate.remove(entity);
|
||||
}
|
||||
changeEntityInternal(entity);
|
||||
}
|
||||
|
||||
void EntitySimulation::clearEntities() {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_EntitySimulation_h
|
||||
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QSet>
|
||||
|
@ -82,13 +83,15 @@ public:
|
|||
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
||||
|
||||
void processChangedEntities();
|
||||
|
||||
protected:
|
||||
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
||||
// calls them in the right places.
|
||||
virtual void updateEntitiesInternal(uint64_t now) = 0;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity);
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void processChangedEntity(const EntityItemPointer& entity);
|
||||
virtual void clearEntitiesInternal() = 0;
|
||||
|
||||
void expireMortalEntities(uint64_t now);
|
||||
|
@ -114,11 +117,11 @@ private:
|
|||
|
||||
// We maintain multiple lists, each for its distinct purpose.
|
||||
// An entity may be in more than one list.
|
||||
std::unordered_set<EntityItemPointer> _changedEntities; // all changes this frame
|
||||
SetOfEntities _allEntities; // tracks all entities added the simulation
|
||||
SetOfEntities _mortalEntities; // entities that have an expiry
|
||||
uint64_t _nextExpiry;
|
||||
|
||||
|
||||
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
||||
};
|
||||
|
||||
|
|
|
@ -2079,7 +2079,6 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
|
|||
}
|
||||
|
||||
void EntityTree::fixupNeedsParentFixups() {
|
||||
PROFILE_RANGE(simulation_physics, "FixupParents");
|
||||
MovingEntitiesOperator moveOperator;
|
||||
QVector<EntityItemWeakPointer> entitiesToFixup;
|
||||
{
|
||||
|
@ -2189,11 +2188,19 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
|||
_needsParentFixup.append(entity);
|
||||
}
|
||||
|
||||
void EntityTree::preUpdate() {
|
||||
withWriteLock([&] {
|
||||
fixupNeedsParentFixups();
|
||||
if (_simulation) {
|
||||
_simulation->processChangedEntities();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EntityTree::update(bool simulate) {
|
||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||
PerformanceTimer perfTimer("updateTree");
|
||||
withWriteLock([&] {
|
||||
fixupNeedsParentFixups();
|
||||
if (simulate && _simulation) {
|
||||
_simulation->updateEntities();
|
||||
{
|
||||
|
|
|
@ -109,9 +109,10 @@ public:
|
|||
|
||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;
|
||||
|
||||
virtual void update() override { update(true); }
|
||||
|
||||
void update(bool simulate);
|
||||
// Why preUpdate() and update()?
|
||||
// Because sometimes we need to do stuff between the two.
|
||||
void preUpdate() override;
|
||||
void update(bool simulate = true) override;
|
||||
|
||||
// The newer API...
|
||||
void postAddEntity(EntityItemPointer entityItem);
|
||||
|
|
|
@ -421,11 +421,6 @@ void ModelEntityItem::setAnimationFPS(float value) {
|
|||
});
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool ModelEntityItem::shouldBePhysical() const {
|
||||
return !isDead() && getShapeType() != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid();
|
||||
}
|
||||
|
||||
void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||
if (newSize < 0) {
|
||||
return;
|
||||
|
|
|
@ -118,8 +118,6 @@ public:
|
|||
const QString getTextures() const;
|
||||
void setTextures(const QString& textures);
|
||||
|
||||
virtual bool shouldBePhysical() const override;
|
||||
|
||||
virtual void setJointRotations(const QVector<glm::quat>& rotations);
|
||||
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet);
|
||||
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||
|
|
|
@ -231,6 +231,8 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
bool shouldBePhysical() const override { return false; }
|
||||
|
||||
void setColor(const glm::u8vec3& value);
|
||||
glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; }
|
||||
|
||||
|
|
|
@ -164,7 +164,6 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
glm::vec3 getSurfacePositionAdjustment() const;
|
||||
|
||||
virtual ShapeType getShapeType() const override;
|
||||
virtual bool shouldBePhysical() const override { return !isDead(); }
|
||||
|
||||
bool isEdged() const;
|
||||
|
||||
|
|
|
@ -84,8 +84,6 @@ public:
|
|||
|
||||
void setUnscaledDimensions(const glm::vec3& value) override;
|
||||
|
||||
bool shouldBePhysical() const override { return !isDead(); }
|
||||
|
||||
bool supportsDetailedIntersection() const override;
|
||||
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
|
|
|
@ -85,7 +85,9 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
|||
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||
}
|
||||
|
||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||
EntitySimulation::processChangedEntity(entity);
|
||||
|
||||
uint32_t flags = entity->getDirtyFlags();
|
||||
if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) {
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
|||
void updateEntitiesInternal(uint64_t now) override;
|
||||
void addEntityInternal(EntityItemPointer entity) override;
|
||||
void removeEntityInternal(EntityItemPointer entity) override;
|
||||
void changeEntityInternal(EntityItemPointer entity) override;
|
||||
void processChangedEntity(const EntityItemPointer& entity) override;
|
||||
void clearEntitiesInternal() override;
|
||||
|
||||
void sortEntitiesThatMoved() override;
|
||||
|
|
|
@ -89,6 +89,8 @@
|
|||
// (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may
|
||||
// bid for it immediately at VOLUNTEER.
|
||||
//
|
||||
/* These declarations temporarily moved to SimulationFlags.h while we unravel some spaghetti dependencies.
|
||||
* The intent is to move them back here once the dust settles.
|
||||
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||
const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||
|
@ -101,6 +103,7 @@ const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY
|
|||
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
|
||||
*/
|
||||
|
||||
|
||||
class SimulationOwner {
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
virtual bool isReadyToComputeShape() const override { return false; }
|
||||
virtual void setShapeType(ShapeType type) override;
|
||||
virtual ShapeType getShapeType() const override;
|
||||
bool shouldBePhysical() const override { return false; }
|
||||
|
||||
QString getCompoundShapeURL() const;
|
||||
virtual void setCompoundShapeURL(const QString& url);
|
||||
|
|
|
@ -71,19 +71,159 @@ void gl::globalRelease(bool finish) {}
|
|||
#endif
|
||||
|
||||
|
||||
void gl::getTargetVersion(int& major, int& minor) {
|
||||
uint16_t gl::getTargetVersion() {
|
||||
uint8_t major = 0, minor = 0;
|
||||
|
||||
#if defined(USE_GLES)
|
||||
major = 3;
|
||||
minor = 2;
|
||||
#else
|
||||
#if defined(Q_OS_MAC)
|
||||
#elif defined(Q_OS_MAC)
|
||||
major = 4;
|
||||
minor = 1;
|
||||
#else
|
||||
major = 4;
|
||||
minor = disableGl45() ? 1 : 5;
|
||||
#endif
|
||||
return GL_MAKE_VERSION(major, minor);
|
||||
}
|
||||
|
||||
uint16_t gl::getRequiredVersion() {
|
||||
uint8_t major = 0, minor = 0;
|
||||
#if defined(USE_GLES)
|
||||
major = 3;
|
||||
minor = 2;
|
||||
#else
|
||||
major = 4;
|
||||
minor = 1;
|
||||
#endif
|
||||
return GL_MAKE_VERSION(major, minor);
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
|
||||
typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList);
|
||||
GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
|
||||
GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
|
||||
|
||||
static bool setupPixelFormatSimple(HDC hdc) {
|
||||
// FIXME build the PFD based on the
|
||||
static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be
|
||||
{
|
||||
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
||||
1, // Version Number
|
||||
PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
||||
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
||||
PFD_DOUBLEBUFFER, // Must Support Double Buffering
|
||||
PFD_TYPE_RGBA, // Request An RGBA Format
|
||||
24, // Select Our Color Depth
|
||||
0, 0, 0, 0, 0, 0, // Color Bits Ignored
|
||||
1, // Alpha Buffer
|
||||
0, // Shift Bit Ignored
|
||||
0, // No Accumulation Buffer
|
||||
0, 0, 0, 0, // Accumulation Bits Ignored
|
||||
24, // 24 Bit Z-Buffer (Depth Buffer)
|
||||
8, // 8 Bit Stencil Buffer
|
||||
0, // No Auxiliary Buffer
|
||||
PFD_MAIN_PLANE, // Main Drawing Layer
|
||||
0, // Reserved
|
||||
0, 0, 0 // Layer Masks Ignored
|
||||
};
|
||||
auto pixelFormat = ChoosePixelFormat(hdc, &pfd);
|
||||
if (pixelFormat == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint16_t gl::getAvailableVersion() {
|
||||
static uint8_t major = 0, minor = 0;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
#if defined(USE_GLES)
|
||||
// FIXME do runtime detection of the available GL version
|
||||
major = 3;
|
||||
minor = 2;
|
||||
#elif defined(Q_OS_MAC)
|
||||
// Damn it Apple.
|
||||
major = 4;
|
||||
minor = 1;
|
||||
#elif defined(Q_OS_WIN)
|
||||
//
|
||||
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||
const auto windowClassName = "OpenGLVersionCheck";
|
||||
WNDCLASS wc = { };
|
||||
wc.lpfnWndProc = DefWindowProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = windowClassName;
|
||||
RegisterClass(&wc);
|
||||
|
||||
using Handle = std::shared_ptr<void>;
|
||||
HWND rawHwnd = CreateWindowEx(
|
||||
WS_EX_APPWINDOW, // extended style
|
||||
windowClassName, // class name
|
||||
windowClassName, // title
|
||||
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CS_OWNDC | WS_POPUP, // style
|
||||
0, 0, 10, 10, // position and size
|
||||
NULL, NULL, hInstance, NULL);
|
||||
auto WindowDestroyer = [](void* handle) {
|
||||
DestroyWindow((HWND)handle);
|
||||
};
|
||||
Handle hWnd = Handle(rawHwnd, WindowDestroyer);
|
||||
if (!hWnd) {
|
||||
return;
|
||||
}
|
||||
HDC rawDC = GetDC(rawHwnd);
|
||||
auto DCDestroyer = [=](void* handle) {
|
||||
ReleaseDC(rawHwnd, (HDC)handle);
|
||||
};
|
||||
if (!rawDC) {
|
||||
return;
|
||||
}
|
||||
Handle hDC = Handle(rawDC, DCDestroyer);
|
||||
if (!setupPixelFormatSimple(rawDC)) {
|
||||
return;
|
||||
}
|
||||
auto GLRCDestroyer = [](void* handle) {
|
||||
wglDeleteContext((HGLRC)handle);
|
||||
};
|
||||
auto rawglrc = wglCreateContext(rawDC);
|
||||
if (!rawglrc) {
|
||||
return;
|
||||
}
|
||||
Handle hGLRC = Handle(rawglrc, GLRCDestroyer);
|
||||
if (!wglMakeCurrent(rawDC, rawglrc)) {
|
||||
return;
|
||||
}
|
||||
gl::initModuleGl();
|
||||
wglMakeCurrent(0, 0);
|
||||
hGLRC.reset();
|
||||
if (!wglChoosePixelFormatARB || !wglCreateContextAttribsARB) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The only two versions we care about on Windows
|
||||
// are 4.5 and 4.1
|
||||
if (GLAD_GL_VERSION_4_5) {
|
||||
major = 4;
|
||||
minor = disableGl45() ? 1 : 5;
|
||||
} else if (GLAD_GL_VERSION_4_1) {
|
||||
major = 4;
|
||||
minor = 1;
|
||||
}
|
||||
#else
|
||||
// FIXME do runtime detection of GL version on non-Mac/Windows/Mobile platforms
|
||||
major = 4;
|
||||
minor = disableGl45() ? 1 : 5;
|
||||
#endif
|
||||
});
|
||||
return GL_MAKE_VERSION(major, minor);
|
||||
}
|
||||
|
||||
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
||||
|
@ -105,10 +245,9 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
|
||||
format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS);
|
||||
format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS);
|
||||
int major, minor;
|
||||
::gl::getTargetVersion(major, minor);
|
||||
format.setMajorVersion(major);
|
||||
format.setMinorVersion(minor);
|
||||
auto glversion = ::gl::getTargetVersion();
|
||||
format.setMajorVersion(GL_GET_MAJOR_VERSION(glversion));
|
||||
format.setMinorVersion(GL_GET_MINOR_VERSION(glversion));
|
||||
});
|
||||
return format;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ int glVersionToInteger(QString glVersion);
|
|||
|
||||
bool isRenderThread();
|
||||
|
||||
#define GL_MAKE_VERSION(major, minor) ((major << 8) | minor)
|
||||
#define GL_GET_MINOR_VERSION(glversion) (glversion & 0x00FF)
|
||||
#define GL_GET_MAJOR_VERSION(glversion) ((glversion & 0xFF00) >> 8)
|
||||
|
||||
namespace gl {
|
||||
void globalLock();
|
||||
void globalRelease(bool finish = true);
|
||||
|
@ -52,7 +56,11 @@ namespace gl {
|
|||
|
||||
bool disableGl45();
|
||||
|
||||
void getTargetVersion(int& major, int& minor);
|
||||
uint16_t getTargetVersion();
|
||||
|
||||
uint16_t getAvailableVersion();
|
||||
|
||||
uint16_t getRequiredVersion();
|
||||
} // namespace gl
|
||||
|
||||
#define CHECK_GL_ERROR() ::gl::checkGLErrorDebug(__FUNCTION__)
|
||||
|
|
|
@ -61,7 +61,7 @@ void CalculateBlendshapeNormalsTask::run(const baker::BakeContextPointer& contex
|
|||
outVertex = blendshape.vertices[lookupIndex];
|
||||
} else {
|
||||
// Index isn't in the blendshape, so return vertex from mesh
|
||||
outVertex = mesh.vertices[lookupIndex];
|
||||
outVertex = baker::safeGet(mesh.vertices, lookupIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ void CalculateMeshNormalsTask::run(const baker::BakeContextPointer& context, con
|
|||
return &normalsOut[normalIndex];
|
||||
},
|
||||
[&mesh](int vertexIndex, glm::vec3& outVertex) /* VertexSetter */ {
|
||||
outVertex = mesh.vertices[vertexIndex];
|
||||
outVertex = baker::safeGet(mesh.vertices, vertexIndex);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,17 @@ namespace baker {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& safeGet(const QVector<T>& data, int i) {
|
||||
static T t;
|
||||
|
||||
if (i >= 0 && data.size() > i) {
|
||||
return data[i];
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reference to the normal at the specified index, or nullptr if it cannot be accessed
|
||||
using NormalAccessor = std::function<glm::vec3*(int index)>;
|
||||
|
||||
|
|
|
@ -542,5 +542,3 @@ void GeometryResourceWatcher::resourceRefreshed() {
|
|||
// FIXME: Model is not set up to handle a refresh
|
||||
// _instance.reset();
|
||||
}
|
||||
|
||||
#include "ModelCache.moc"
|
||||
|
|
|
@ -482,7 +482,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
} else {
|
||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||
|
||||
qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID
|
||||
qCDebug(networking_ice) << "Possible domain change required to connect to domain with ID" << domainID
|
||||
<< "via ice-server at" << iceServerAddress;
|
||||
|
||||
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
||||
|
|
|
@ -64,7 +64,7 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop);
|
||||
}
|
||||
|
||||
void DomainHandler::disconnect() {
|
||||
void DomainHandler::disconnect(QString reason) {
|
||||
// if we're currently connected to a domain, send a disconnect packet on our way out
|
||||
if (_isConnected) {
|
||||
sendDisconnectPacket();
|
||||
|
@ -81,6 +81,8 @@ void DomainHandler::disconnect() {
|
|||
_sockAddr.clear();
|
||||
}
|
||||
|
||||
qCDebug(networking_ice) << "Disconnecting from domain server.";
|
||||
qCDebug(networking_ice) << "REASON:" << reason;
|
||||
setIsConnected(false);
|
||||
}
|
||||
|
||||
|
@ -100,9 +102,9 @@ void DomainHandler::clearSettings() {
|
|||
_settingsObject = QJsonObject();
|
||||
}
|
||||
|
||||
void DomainHandler::softReset() {
|
||||
void DomainHandler::softReset(QString reason) {
|
||||
qCDebug(networking) << "Resetting current domain connection information.";
|
||||
disconnect();
|
||||
disconnect(reason);
|
||||
|
||||
clearSettings();
|
||||
|
||||
|
@ -118,10 +120,10 @@ void DomainHandler::softReset() {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainHandler::hardReset() {
|
||||
void DomainHandler::hardReset(QString reason) {
|
||||
emit resetting();
|
||||
|
||||
softReset();
|
||||
softReset(reason);
|
||||
_isInErrorState = false;
|
||||
emit redirectErrorStateChanged(_isInErrorState);
|
||||
|
||||
|
@ -166,7 +168,7 @@ void DomainHandler::setErrorDomainURL(const QUrl& url) {
|
|||
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
|
||||
if (_sockAddr != sockAddr) {
|
||||
// we should reset on a sockAddr change
|
||||
hardReset();
|
||||
hardReset("Changing domain sockAddr");
|
||||
// change the sockAddr
|
||||
_sockAddr = sockAddr;
|
||||
}
|
||||
|
@ -209,7 +211,7 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
|
|||
// if it's in the error state, reset and try again.
|
||||
if ((_domainURL != domainURL || _sockAddr.getPort() != domainPort) || _isInErrorState) {
|
||||
// re-set the domain info so that auth information is reloaded
|
||||
hardReset();
|
||||
hardReset("Changing domain URL");
|
||||
|
||||
QString previousHost = _domainURL.host();
|
||||
_domainURL = domainURL;
|
||||
|
@ -242,10 +244,24 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
|
|||
|
||||
void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) {
|
||||
|
||||
auto newIceServer = _iceServerSockAddr.getAddress().toString() != iceServerHostname;
|
||||
auto newDomainID = id != _pendingDomainID;
|
||||
|
||||
// if it's in the error state, reset and try again.
|
||||
if ((_iceServerSockAddr.getAddress().toString() != iceServerHostname || id != _pendingDomainID) || _isInErrorState) {
|
||||
if (newIceServer || newDomainID || _isInErrorState) {
|
||||
QString reason;
|
||||
if (newIceServer) {
|
||||
reason += "New ICE server;";
|
||||
}
|
||||
if (newDomainID) {
|
||||
reason += "New domain ID;";
|
||||
}
|
||||
if (_isInErrorState) {
|
||||
reason += "Domain in error state;";
|
||||
}
|
||||
|
||||
// re-set the domain info to connect to new domain
|
||||
hardReset();
|
||||
hardReset(reason);
|
||||
|
||||
// refresh our ICE client UUID to something new
|
||||
_iceClientID = QUuid::createUuid();
|
||||
|
@ -268,7 +284,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
|||
completedIceServerHostnameLookup();
|
||||
}
|
||||
|
||||
qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname;
|
||||
qCDebug(networking_ice) << "ICE required to connect to domain via ice server at" << iceServerHostname;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,7 +338,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
|||
}
|
||||
|
||||
void DomainHandler::completedIceServerHostnameLookup() {
|
||||
qCDebug(networking) << "ICE server socket is at" << _iceServerSockAddr;
|
||||
qCDebug(networking_ice) << "ICE server socket is at" << _iceServerSockAddr;
|
||||
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerSocket);
|
||||
|
||||
|
@ -409,7 +425,7 @@ void DomainHandler::processSettingsPacketList(QSharedPointer<ReceivedMessage> pa
|
|||
|
||||
void DomainHandler::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
const HifiSockAddr& senderSockAddr = message->getSenderSockAddr();
|
||||
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
|
||||
qCDebug(networking_ice) << "Received reply from domain-server on" << senderSockAddr;
|
||||
|
||||
if (getIP().isNull()) {
|
||||
// we're hearing back from this domain-server, no need to refresh API information
|
||||
|
@ -417,13 +433,13 @@ void DomainHandler::processICEPingReplyPacket(QSharedPointer<ReceivedMessage> me
|
|||
|
||||
// for now we're unsafely assuming this came back from the domain
|
||||
if (senderSockAddr == _icePeer.getLocalSocket()) {
|
||||
qCDebug(networking) << "Connecting to domain using local socket";
|
||||
qCDebug(networking_ice) << "Connecting to domain using local socket";
|
||||
activateICELocalSocket();
|
||||
} else if (senderSockAddr == _icePeer.getPublicSocket()) {
|
||||
qCDebug(networking) << "Conecting to domain using public socket";
|
||||
qCDebug(networking_ice) << "Conecting to domain using public socket";
|
||||
activateICEPublicSocket();
|
||||
} else {
|
||||
qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect.";
|
||||
qCDebug(networking_ice) << "Reply does not match either local or public socket for domain. Will not connect.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,7 +458,7 @@ void DomainHandler::processDTLSRequirementPacket(QSharedPointer<ReceivedMessage>
|
|||
|
||||
void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (_icePeer.hasSockets()) {
|
||||
qCDebug(networking) << "Received an ICE peer packet for domain-server but we already have sockets. Not processing.";
|
||||
qCDebug(networking_ice) << "Received an ICE peer packet for domain-server but we already have sockets. Not processing.";
|
||||
// bail on processing this packet if our ice peer already has sockets
|
||||
return;
|
||||
}
|
||||
|
@ -457,10 +473,10 @@ void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> mes
|
|||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation);
|
||||
|
||||
if (_icePeer.getUUID() != _pendingDomainID) {
|
||||
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||
qCDebug(networking_ice) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||
_icePeer.reset();
|
||||
} else {
|
||||
qCDebug(networking) << "Received network peer object for domain -" << _icePeer;
|
||||
qCDebug(networking_ice) << "Received network peer object for domain -" << _icePeer;
|
||||
|
||||
// ask the peer object to start its ping timer
|
||||
_icePeer.startPingTimer();
|
||||
|
@ -546,18 +562,24 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
bool DomainHandler::checkInPacketTimeout() {
|
||||
++_checkInPacketsSinceLastReply;
|
||||
|
||||
if (_checkInPacketsSinceLastReply > 1) {
|
||||
qCDebug(networking_ice) << "Silent domain checkins:" << _checkInPacketsSinceLastReply;
|
||||
}
|
||||
|
||||
if (_checkInPacketsSinceLastReply > MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||
// so emit our signal that says that
|
||||
|
||||
#ifdef DEBUG_EVENT_QUEUE
|
||||
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(nodeList->thread());
|
||||
qCDebug(networking) << "Limit of silent domain checkins reached (network qt queue: " << nodeListQueueSize << ")";
|
||||
#else // DEBUG_EVENT_QUEUE
|
||||
qCDebug(networking) << "Limit of silent domain checkins reached";
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
||||
emit limitOfSilentDomainCheckInsReached();
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -42,7 +42,7 @@ class DomainHandler : public QObject {
|
|||
public:
|
||||
DomainHandler(QObject* parent = 0);
|
||||
|
||||
void disconnect();
|
||||
void disconnect(QString reason);
|
||||
void clearSettings();
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
|
@ -105,7 +105,7 @@ public:
|
|||
|
||||
bool isSocketKnown() const { return !_sockAddr.getAddress().isNull(); }
|
||||
|
||||
void softReset();
|
||||
void softReset(QString reason);
|
||||
|
||||
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
|
||||
bool checkInPacketTimeout();
|
||||
|
@ -210,7 +210,7 @@ signals:
|
|||
private:
|
||||
bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode);
|
||||
void sendDisconnectPacket();
|
||||
void hardReset();
|
||||
void hardReset(QString reason);
|
||||
|
||||
bool isHardRefusal(int reasonCode);
|
||||
|
||||
|
|
|
@ -885,6 +885,7 @@ void LimitedNodeList::removeSilentNodes() {
|
|||
});
|
||||
|
||||
foreach(const SharedNodePointer& killedNode, killedNodes) {
|
||||
qCDebug(networking_ice) << "Removing silent node" << killedNode;
|
||||
handleNodeKill(killedNode);
|
||||
}
|
||||
}
|
||||
|
@ -1265,7 +1266,7 @@ void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSoc
|
|||
|
||||
iceDataStream << peerID;
|
||||
|
||||
qCDebug(networking) << "Sending packet to ICE server to request connection info for peer with ID"
|
||||
qCDebug(networking_ice) << "Sending packet to ICE server to request connection info for peer with ID"
|
||||
<< uuidStringWithoutCurlyBraces(peerID);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "NetworkLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(networking, "hifi.networking")
|
||||
Q_LOGGING_CATEGORY(networking_ice, "hifi.networking.ice")
|
||||
Q_LOGGING_CATEGORY(resourceLog, "hifi.networking.resource")
|
||||
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")
|
||||
Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client")
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
Q_DECLARE_LOGGING_CATEGORY(resourceLog)
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking)
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking_ice)
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
const QString ICE_SERVER_HOSTNAME = "localhost";
|
||||
const quint16 ICE_SERVER_DEFAULT_PORT = 7337;
|
||||
const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000;
|
||||
const int ICE_HEARBEAT_INTERVAL_MSECS = 1 * 1000;
|
||||
const int MAX_ICE_CONNECTION_ATTEMPTS = 5;
|
||||
|
||||
const int UDP_PUNCH_PING_INTERVAL_MS = 250;
|
||||
|
|
|
@ -243,9 +243,11 @@ void NodeList::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
|
|||
sendPacket(std::move(replyPacket), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void NodeList::reset(bool skipDomainHandlerReset) {
|
||||
void NodeList::reset(QString reason, bool skipDomainHandlerReset) {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, skipDomainHandlerReset));
|
||||
QMetaObject::invokeMethod(this, "reset",
|
||||
Q_ARG(QString, reason),
|
||||
Q_ARG(bool, skipDomainHandlerReset));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -267,7 +269,7 @@ void NodeList::reset(bool skipDomainHandlerReset) {
|
|||
|
||||
if (!skipDomainHandlerReset) {
|
||||
// clear the domain connection information, unless they're the ones that asked us to reset
|
||||
_domainHandler.softReset();
|
||||
_domainHandler.softReset(reason);
|
||||
}
|
||||
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
|
@ -297,12 +299,12 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
// may be called by multiple threads.
|
||||
|
||||
if (!_sendDomainServerCheckInEnabled) {
|
||||
qCDebug(networking) << "Refusing to send a domain-server check in while it is disabled.";
|
||||
qCDebug(networking_ice) << "Refusing to send a domain-server check in while it is disabled.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isShuttingDown) {
|
||||
qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
|
||||
qCDebug(networking_ice) << "Refusing to send a domain-server check in while shutting down.";
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -311,9 +313,9 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
if (publicSockAddr.isNull()) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
|
||||
qCDebug(networking_ice) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
|
||||
} else if (domainHandlerIp.isNull() && _domainHandler.requiresICE()) {
|
||||
qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
|
||||
qCDebug(networking_ice) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
|
||||
handleICEConnectionToDomainServer();
|
||||
// let the domain handler know we are due to send a checkin packet
|
||||
} else if (!domainHandlerIp.isNull() && !_domainHandler.checkInPacketTimeout()) {
|
||||
|
@ -324,7 +326,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
if (!domainIsConnected) {
|
||||
auto hostname = _domainHandler.getHostname();
|
||||
qCDebug(networking) << "Sending connect request to domain-server at" << hostname;
|
||||
qCDebug(networking_ice) << "Sending connect request to domain-server at" << hostname;
|
||||
|
||||
// is this our localhost domain-server?
|
||||
// if so we need to make sure we have an up-to-date local port in case it restarted
|
||||
|
@ -334,7 +336,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, domainPort);
|
||||
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
|
||||
qCDebug(networking_ice) << "Local domain-server port read from shared memory (or default) is" << domainPort;
|
||||
_domainHandler.setPort(domainPort);
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +348,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
bool requiresUsernameSignature = !domainIsConnected && !connectionToken.isNull();
|
||||
|
||||
if (requiresUsernameSignature && !accountManager->getAccountInfo().hasPrivateKey()) {
|
||||
qWarning() << "A keypair is required to present a username signature to the domain-server"
|
||||
qCWarning(networking_ice) << "A keypair is required to present a username signature to the domain-server"
|
||||
<< "but no keypair is present. Waiting for keypair generation to complete.";
|
||||
accountManager->generateNewUserKeypair();
|
||||
|
||||
|
@ -574,12 +576,12 @@ void NodeList::pingPunchForDomainServer() {
|
|||
const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
||||
|
||||
if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) {
|
||||
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||
qCDebug(networking_ice) << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getPendingDomainID());
|
||||
} else {
|
||||
if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) {
|
||||
// if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat
|
||||
qCDebug(networking) << "No ping replies received from domain-server with ID"
|
||||
qCDebug(networking_ice) << "No ping replies received from domain-server with ID"
|
||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEClientID()) << "-" << "re-sending ICE query.";
|
||||
|
||||
_domainHandler.getICEPeer().softReset();
|
||||
|
@ -657,14 +659,14 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
|
|||
if (_domainHandler.isConnected() &&
|
||||
((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) ||
|
||||
(!currentSessionID.isNull() && newUUID != currentSessionID))) {
|
||||
qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset";
|
||||
|
||||
// reset the nodelist, but don't do a domain handler reset since we're about to process a good domain list
|
||||
reset(true);
|
||||
reset("Local ID or Session ID changed while connected to domain - forcing NodeList reset", true);
|
||||
|
||||
// tell the domain handler that we're no longer connected so that below
|
||||
// it can re-perform actions as if we just connected
|
||||
_domainHandler.setIsConnected(false);
|
||||
// Clear any reliable connections using old ID.
|
||||
_nodeSocket.clearConnections();
|
||||
}
|
||||
|
||||
setSessionLocalID(newLocalID);
|
||||
|
|
|
@ -103,8 +103,8 @@ public:
|
|||
virtual HifiSockAddr getDomainSockAddr() const override { return _domainHandler.getSockAddr(); }
|
||||
|
||||
public slots:
|
||||
void reset(bool skipDomainHandlerReset = false);
|
||||
void resetFromDomainHandler() { reset(true); }
|
||||
void reset(QString reason, bool skipDomainHandlerReset = false);
|
||||
void resetFromDomainHandler() { reset("Reset from Domain Handler", true); }
|
||||
|
||||
void sendDomainServerCheckIn();
|
||||
void handleDSPathQuery(const QString& newPath);
|
||||
|
|
|
@ -59,7 +59,7 @@ void ThreadedAssignment::setFinished(bool isFinished) {
|
|||
packetReceiver.setShouldDropPackets(true);
|
||||
|
||||
// send a disconnect packet to the domain
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->getDomainHandler().disconnect("Finished");
|
||||
|
||||
// stop our owned timers
|
||||
_domainServerTimer.stop();
|
||||
|
|
|
@ -145,7 +145,10 @@ public:
|
|||
virtual bool rootElementHasData() const { return false; }
|
||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
|
||||
|
||||
virtual void update() { } // nothing to do by default
|
||||
// Why preUpdate() and update()?
|
||||
// Because EntityTree needs them.
|
||||
virtual void preUpdate() { }
|
||||
virtual void update(bool simulate = true) { }
|
||||
|
||||
OctreeElementPointer getRoot() { return _rootElement; }
|
||||
|
||||
|
|
|
@ -242,6 +242,7 @@ bool OctreePersistThread::backupCurrentFile() {
|
|||
}
|
||||
|
||||
void OctreePersistThread::process() {
|
||||
_tree->preUpdate();
|
||||
_tree->update();
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
|
|
@ -25,23 +25,6 @@
|
|||
#include "PhysicsHelpers.h"
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
#include "EntityTree.h"
|
||||
|
||||
bool EntityMotionState::entityTreeIsLocked() const {
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
if (!tree) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool entityTreeIsLocked() {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
|
||||
const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
||||
|
||||
|
@ -74,7 +57,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
setMass(_entity->computeMass());
|
||||
// we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
|
||||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||
|
@ -143,7 +125,6 @@ void EntityMotionState::handleDeactivation() {
|
|||
|
||||
// virtual
|
||||
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
assert(entityTreeIsLocked());
|
||||
updateServerPhysicsVariables();
|
||||
ObjectMotionState::handleEasyChanges(flags);
|
||||
|
||||
|
@ -191,17 +172,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
}
|
||||
|
||||
|
||||
// virtual
|
||||
bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
updateServerPhysicsVariables();
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
||||
if (!_entity) {
|
||||
return MOTION_TYPE_STATIC;
|
||||
}
|
||||
assert(entityTreeIsLocked());
|
||||
|
||||
if (_entity->getLocked()) {
|
||||
if (_entity->isMoving()) {
|
||||
|
@ -226,7 +200,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
|||
}
|
||||
|
||||
bool EntityMotionState::isMoving() const {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity && _entity->isMovingRelativeToParent();
|
||||
}
|
||||
|
||||
|
@ -240,7 +213,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
if (!_entity) {
|
||||
return;
|
||||
}
|
||||
assert(entityTreeIsLocked());
|
||||
if (_motionType == MOTION_TYPE_KINEMATIC) {
|
||||
BT_PROFILE("kinematicIntegration");
|
||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||
|
@ -271,7 +243,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
// This callback is invoked by the physics simulation at the end of each simulation step...
|
||||
// iff the corresponding RigidBody is DYNAMIC and ACTIVE.
|
||||
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
assert(entityTreeIsLocked());
|
||||
measureBodyAcceleration();
|
||||
|
||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||
|
@ -309,19 +280,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
}
|
||||
|
||||
|
||||
// virtual and protected
|
||||
bool EntityMotionState::isReadyToComputeShape() const {
|
||||
return _entity->isReadyToComputeShape();
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
const btCollisionShape* EntityMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
|
@ -439,7 +397,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||
assert(entityTreeIsLocked());
|
||||
|
||||
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
||||
assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
||||
|
@ -505,7 +462,6 @@ void EntityMotionState::updateSendVelocities() {
|
|||
|
||||
void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Bid");
|
||||
assert(entityTreeIsLocked());
|
||||
|
||||
updateSendVelocities();
|
||||
|
||||
|
@ -546,7 +502,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
|||
|
||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
||||
assert(entityTreeIsLocked());
|
||||
assert(isLocallyOwned());
|
||||
|
||||
updateSendVelocities();
|
||||
|
@ -645,8 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
_bumpedPriority = 0;
|
||||
}
|
||||
|
||||
uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
||||
assert(entityTreeIsLocked());
|
||||
uint32_t EntityMotionState::getIncomingDirtyFlags() const {
|
||||
uint32_t dirtyFlags = 0;
|
||||
if (_body && _entity) {
|
||||
dirtyFlags = _entity->getDirtyFlags();
|
||||
|
@ -676,10 +630,9 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
|||
return dirtyFlags;
|
||||
}
|
||||
|
||||
void EntityMotionState::clearIncomingDirtyFlags() {
|
||||
assert(entityTreeIsLocked());
|
||||
void EntityMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||
if (_body && _entity) {
|
||||
_entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS);
|
||||
_entity->clearDirtyFlags(mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -694,7 +647,6 @@ void EntityMotionState::slaveBidPriority() {
|
|||
|
||||
// virtual
|
||||
QUuid EntityMotionState::getSimulatorID() const {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getSimulatorID();
|
||||
}
|
||||
|
||||
|
@ -762,6 +714,10 @@ glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const {
|
|||
return _measuredAcceleration * _measuredDeltaTime;
|
||||
}
|
||||
|
||||
bool EntityMotionState::shouldBeInPhysicsSimulation() const {
|
||||
return _region < workload::Region::R3 && _entity->shouldBePhysical();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||
ObjectMotionState::setMotionType(motionType);
|
||||
|
@ -770,7 +726,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
|||
|
||||
// virtual
|
||||
QString EntityMotionState::getName() const {
|
||||
assert(entityTreeIsLocked());
|
||||
return _entity->getName();
|
||||
}
|
||||
|
||||
|
@ -788,6 +743,15 @@ bool EntityMotionState::shouldSendBid() const {
|
|||
&& !_entity->getLocked();
|
||||
}
|
||||
|
||||
void EntityMotionState::setRigidBody(btRigidBody* body) {
|
||||
ObjectMotionState::setRigidBody(body);
|
||||
if (_body) {
|
||||
_entity->markSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION);
|
||||
} else {
|
||||
_entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
||||
return (_region == workload::Region::R1) ?
|
||||
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef hifi_EntityMotionState_h
|
||||
#define hifi_EntityMotionState_h
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <EntityTypes.h>
|
||||
#include <AACube.h>
|
||||
#include <workload/Region.h>
|
||||
|
@ -38,7 +39,6 @@ public:
|
|||
|
||||
void handleDeactivation();
|
||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
||||
|
||||
/// \return PhysicsMotionType based on params set in EntityItem
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
||||
|
@ -55,8 +55,8 @@ public:
|
|||
void sendBid(OctreeEditPacketSender* packetSender, uint32_t step);
|
||||
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
virtual uint32_t getIncomingDirtyFlags() const override;
|
||||
virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||
|
||||
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
||||
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
||||
|
@ -84,6 +84,7 @@ public:
|
|||
void measureBodyAcceleration();
|
||||
|
||||
virtual QString getName() const override;
|
||||
ShapeType getShapeType() const override { return _entity->getShapeType(); }
|
||||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
|
@ -99,6 +100,8 @@ public:
|
|||
void saveKinematicState(btScalar timeStep) override;
|
||||
|
||||
protected:
|
||||
void setRigidBody(btRigidBody* body) override;
|
||||
|
||||
uint8_t computeFinalBidPriority() const;
|
||||
void updateSendVelocities();
|
||||
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
||||
|
@ -112,12 +115,8 @@ protected:
|
|||
|
||||
void clearObjectVelocities() const;
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool entityTreeIsLocked() const;
|
||||
#endif
|
||||
|
||||
bool isReadyToComputeShape() const override;
|
||||
const btCollisionShape* computeNewShape() override;
|
||||
bool isInPhysicsSimulation() const { return _body != nullptr; }
|
||||
bool shouldBeInPhysicsSimulation() const;
|
||||
void setMotionType(PhysicsMotionType motionType) override;
|
||||
|
||||
// EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
#include "ObjectActionTractor.h"
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
#include <EntityItem.h>
|
||||
#include <QVariantGLM.h>
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
#include <EntityItem.h>
|
||||
#include <QVariantGLM.h>
|
||||
|
||||
#include "PhysicsLogging.h"
|
||||
|
||||
const uint16_t ObjectActionTravelOriented::actionVersion = 1;
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
|
||||
#include "ObjectMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
|
|
|
@ -198,9 +198,14 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) {
|
|||
getShapeManager()->releaseShape(_shape);
|
||||
}
|
||||
_shape = shape;
|
||||
if (_body && _type != MOTIONSTATE_TYPE_DETAILED) {
|
||||
if (_body) {
|
||||
assert(_shape);
|
||||
_body->setCollisionShape(const_cast<btCollisionShape*>(_shape));
|
||||
updateCCDConfiguration();
|
||||
}
|
||||
} else if (shape) {
|
||||
// we need to release unused reference to shape
|
||||
getShapeManager()->releaseShape(shape);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,50 +290,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
}
|
||||
}
|
||||
|
||||
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
assert(_body && _shape);
|
||||
if (flags & Simulation::DIRTY_SHAPE) {
|
||||
// make sure the new shape is valid
|
||||
if (!isReadyToComputeShape()) {
|
||||
return false;
|
||||
}
|
||||
const btCollisionShape* newShape = computeNewShape();
|
||||
if (!newShape) {
|
||||
qCDebug(physics) << "Warning: failed to generate new shape!";
|
||||
// failed to generate new shape! --> keep old shape and remove shape-change flag
|
||||
flags &= ~Simulation::DIRTY_SHAPE;
|
||||
// TODO: force this object out of PhysicsEngine rather than just use the old shape
|
||||
if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) {
|
||||
// no HARD flags remain, so do any EASY changes
|
||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
handleEasyChanges(flags);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (_shape == newShape) {
|
||||
// the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
||||
flags &= ~Simulation::DIRTY_SHAPE;
|
||||
// and clear the reference we just created
|
||||
getShapeManager()->releaseShape(_shape);
|
||||
} else {
|
||||
_body->setCollisionShape(const_cast<btCollisionShape*>(newShape));
|
||||
setShape(newShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
handleEasyChanges(flags);
|
||||
}
|
||||
// it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed)
|
||||
// so we check again before we reinsert:
|
||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
||||
engine->reinsertObject(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectMotionState::updateBodyMaterialProperties() {
|
||||
_body->setRestitution(getObjectRestitution());
|
||||
_body->setFriction(getObjectFriction());
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <SimulationFlags.h>
|
||||
|
||||
#include "ContactInfo.h"
|
||||
#include "ShapeManager.h"
|
||||
|
@ -100,7 +100,6 @@ public:
|
|||
virtual ~ObjectMotionState();
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags);
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
|
||||
|
||||
void updateBodyMaterialProperties();
|
||||
void updateBodyVelocities();
|
||||
|
@ -123,11 +122,12 @@ public:
|
|||
glm::vec3 getBodyAngularVelocity() const;
|
||||
virtual glm::vec3 getObjectLinearVelocityChange() const;
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() = 0;
|
||||
virtual void clearIncomingDirtyFlags() = 0;
|
||||
virtual uint32_t getIncomingDirtyFlags() const = 0;
|
||||
virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) = 0;
|
||||
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const = 0;
|
||||
|
||||
virtual bool needsNewShape() const { return _shape == nullptr || getIncomingDirtyFlags() & Simulation::DIRTY_SHAPE; }
|
||||
const btCollisionShape* getShape() const { return _shape; }
|
||||
btRigidBody* getRigidBody() const { return _body; }
|
||||
|
||||
|
@ -154,6 +154,7 @@ public:
|
|||
virtual void bump(uint8_t priority) {}
|
||||
|
||||
virtual QString getName() const { return ""; }
|
||||
virtual ShapeType getShapeType() const = 0;
|
||||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0;
|
||||
|
||||
|
@ -172,8 +173,6 @@ public:
|
|||
friend class PhysicsEngine;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() const = 0;
|
||||
virtual const btCollisionShape* computeNewShape() = 0;
|
||||
virtual void setMotionType(PhysicsMotionType motionType);
|
||||
void updateCCDConfiguration();
|
||||
|
||||
|
@ -187,7 +186,7 @@ protected:
|
|||
btRigidBody* _body { nullptr };
|
||||
float _density { 1.0f };
|
||||
|
||||
// ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These date members allow an Action
|
||||
// ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These data members allow an Action
|
||||
// to operate on a kinematic object without screwing up our default kinematic integration
|
||||
// which is done in the MotionState::getWorldTransform().
|
||||
mutable uint32_t _lastKinematicStep;
|
||||
|
|
|
@ -131,10 +131,10 @@ void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntitie
|
|||
_deadAvatarEntities.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||
EntitySimulation::processChangedEntity(entity);
|
||||
|
||||
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
||||
QMutexLocker lock(&_mutex);
|
||||
assert(entity);
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||
|
@ -156,7 +156,6 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
|||
|
||||
// remove from the physical simulation
|
||||
_incomingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
removeOwnershipData(motionState);
|
||||
_entitiesToRemoveFromPhysics.insert(entity);
|
||||
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||
|
@ -227,44 +226,68 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity)
|
|||
}
|
||||
// end EntitySimulation overrides
|
||||
|
||||
const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
assert(motionState);
|
||||
// TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen
|
||||
if (motionState) {
|
||||
_entitiesToAddToPhysics.remove(entity);
|
||||
if (entity->isDead() && entity->getElement()) {
|
||||
_deadEntities.insert(entity);
|
||||
void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() {
|
||||
// this lambda for when we decide to actually build the motionState
|
||||
auto buildMotionState = [&](btCollisionShape* shape, EntityItemPointer entity) {
|
||||
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||
_physicalObjects.insert(motionState);
|
||||
_incomingChanges.insert(motionState);
|
||||
};
|
||||
|
||||
uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount();
|
||||
if (deliveryCount != _lastWorkDeliveryCount) {
|
||||
// new off-thread shapes have arrived --> find adds whose shapes have arrived
|
||||
_lastWorkDeliveryCount = deliveryCount;
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.begin();
|
||||
while (requestItr != _shapeRequests.end()) {
|
||||
EntityItemPointer entity = requestItr->entity;
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (!motionState) {
|
||||
// this is an ADD because motionState doesn't exist yet
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash));
|
||||
if (shape) {
|
||||
// shape is ready at last!
|
||||
// But the entity's desired shape might have changed since last requested
|
||||
// --> rebuild the ShapeInfo to verify hash
|
||||
// TODO? is there a better way to do this?
|
||||
ShapeInfo shapeInfo;
|
||||
entity->computeShapeInfo(shapeInfo);
|
||||
if (shapeInfo.getHash() != requestItr->shapeHash) {
|
||||
// bummer, the hashes are different and we no longer want the shape we've received
|
||||
ObjectMotionState::getShapeManager()->releaseShape(shape);
|
||||
// try again
|
||||
shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
buildMotionState(shape, entity);
|
||||
requestItr = _shapeRequests.erase(requestItr);
|
||||
} else {
|
||||
requestItr->shapeHash = shapeInfo.getHash();
|
||||
++requestItr;
|
||||
}
|
||||
} else {
|
||||
buildMotionState(shape, entity);
|
||||
requestItr = _shapeRequests.erase(requestItr);
|
||||
}
|
||||
} else {
|
||||
// shape not ready
|
||||
++requestItr;
|
||||
}
|
||||
} else {
|
||||
// this is a CHANGE because motionState already exists
|
||||
if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) {
|
||||
entity->markDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||
_incomingChanges.insert(motionState);
|
||||
requestItr = _shapeRequests.erase(requestItr);
|
||||
} else {
|
||||
// shape not ready
|
||||
++requestItr;
|
||||
}
|
||||
}
|
||||
|
||||
_incomingChanges.remove(motionState);
|
||||
removeOwnershipData(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
|
||||
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
||||
_objectsToDelete.push_back(motionState);
|
||||
}
|
||||
}
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
return _objectsToDelete;
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto motionState : _objectsToDelete) {
|
||||
// someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo
|
||||
// until then we must do it here
|
||||
// NOTE: a reference to the EntityItemPointer is released in the EntityMotionState::dtor
|
||||
delete motionState;
|
||||
}
|
||||
_objectsToDelete.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();
|
||||
while (entityItr != _entitiesToAddToPhysics.end()) {
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
|
@ -273,7 +296,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
|||
prepareEntityForDelete(entity);
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
} else if (!entity->shouldBePhysical()) {
|
||||
// this entity should no longer be on the internal _entitiesToAddToPhysics
|
||||
// this entity should no longer be on _entitiesToAddToPhysics
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
if (entity->isMovingRelativeToParent()) {
|
||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||
|
@ -282,53 +305,149 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
|||
}
|
||||
}
|
||||
} else if (entity->isReadyToComputeShape()) {
|
||||
ShapeInfo shapeInfo;
|
||||
entity->computeShapeInfo(shapeInfo);
|
||||
int numPoints = shapeInfo.getLargestSubshapePointCount();
|
||||
if (shapeInfo.getType() == SHAPE_TYPE_COMPOUND) {
|
||||
if (numPoints > MAX_HULL_POINTS) {
|
||||
qWarning() << "convex hull with" << numPoints
|
||||
<< "points for entity" << entity->getName()
|
||||
<< "at" << entity->getWorldPosition() << " will be reduced";
|
||||
ShapeRequest shapeRequest(entity);
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||
if (requestItr == _shapeRequests.end()) {
|
||||
// not waiting for a shape (yet)
|
||||
ShapeInfo shapeInfo;
|
||||
entity->computeShapeInfo(shapeInfo);
|
||||
uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount();
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (!motionState) {
|
||||
buildMotionState(shape, entity);
|
||||
} else {
|
||||
// Is it possible to fall in here?
|
||||
// entity shouldn't be on _entitiesToAddToPhysics list if it already has a motionState.
|
||||
// but just in case...
|
||||
motionState->setShape(shape);
|
||||
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||
_physicalObjects.insert(motionState);
|
||||
_incomingChanges.insert(motionState);
|
||||
}
|
||||
} else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) {
|
||||
// shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait
|
||||
shapeRequest.shapeHash = shapeInfo.getHash();
|
||||
_shapeRequests.insert(shapeRequest);
|
||||
} else {
|
||||
// failed to build shape --> will not be added
|
||||
}
|
||||
}
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||
_physicalObjects.insert(motionState);
|
||||
result.push_back(motionState);
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
|
||||
// make sure the motionState's region is up-to-date before it is actually added to physics
|
||||
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||
} else {
|
||||
//qWarning() << "Failed to generate new shape for entity." << entity->getName();
|
||||
++entityItr;
|
||||
}
|
||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||
} else {
|
||||
++entityItr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::setObjectsToChange(const VectorOfMotionStates& objectsToChange) {
|
||||
void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto object : objectsToChange) {
|
||||
_incomingChanges.insert(static_cast<EntityMotionState*>(object));
|
||||
// entities being removed
|
||||
for (auto entity : _entitiesToRemoveFromPhysics) {
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState) {
|
||||
transaction.objectsToRemove.push_back(motionState);
|
||||
_incomingChanges.remove(motionState);
|
||||
}
|
||||
if (_shapeRequests.size() > 0) {
|
||||
ShapeRequest shapeRequest(entity);
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||
if (requestItr != _shapeRequests.end()) {
|
||||
_shapeRequests.erase(requestItr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_entitiesToRemoveFromPhysics.clear();
|
||||
|
||||
void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
QMutexLocker lock(&_mutex);
|
||||
for (auto stateItr : _incomingChanges) {
|
||||
EntityMotionState* motionState = &(*stateItr);
|
||||
result.push_back(motionState);
|
||||
// entities to add
|
||||
buildMotionStatesForEntitiesThatNeedThem();
|
||||
|
||||
// motionStates with changed entities: delete, add, or change
|
||||
for (auto& object : _incomingChanges) {
|
||||
uint32_t unhandledFlags = object->getIncomingDirtyFlags();
|
||||
|
||||
uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS;
|
||||
bool isInPhysicsSimulation = object->isInPhysicsSimulation();
|
||||
bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation();
|
||||
if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) {
|
||||
transaction.objectsToRemove.push_back(object);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool needsNewShape = object->needsNewShape();
|
||||
if (needsNewShape) {
|
||||
ShapeType shapeType = object->getShapeType();
|
||||
if (shapeType == SHAPE_TYPE_STATIC_MESH) {
|
||||
ShapeRequest shapeRequest(object->_entity);
|
||||
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||
if (requestItr == _shapeRequests.end()) {
|
||||
ShapeInfo shapeInfo;
|
||||
object->_entity->computeShapeInfo(shapeInfo);
|
||||
uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount();
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
object->setShape(shape);
|
||||
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||
needsNewShape = false;
|
||||
} else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) {
|
||||
// shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait
|
||||
shapeRequest.shapeHash = shapeInfo.getHash();
|
||||
_shapeRequests.insert(shapeRequest);
|
||||
} else {
|
||||
// failed to build shape --> will not be added/updated
|
||||
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||
}
|
||||
} else {
|
||||
// continue waiting for shape request
|
||||
}
|
||||
} else {
|
||||
ShapeInfo shapeInfo;
|
||||
object->_entity->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
object->setShape(shape);
|
||||
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||
needsNewShape = false;
|
||||
} else {
|
||||
// failed to build shape --> will not be added
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isInPhysicsSimulation) {
|
||||
if (needsNewShape) {
|
||||
// skip it
|
||||
continue;
|
||||
} else {
|
||||
transaction.objectsToAdd.push_back(object);
|
||||
handledFlags = DIRTY_PHYSICS_FLAGS;
|
||||
unhandledFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
object->handleEasyChanges(unhandledFlags);
|
||||
}
|
||||
if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) {
|
||||
transaction.objectsToReinsert.push_back(object);
|
||||
handledFlags |= HARD_DIRTY_PHYSICS_FLAGS;
|
||||
} else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) {
|
||||
transaction.activeStaticObjects.push_back(object);
|
||||
}
|
||||
object->clearIncomingDirtyFlags(handledFlags);
|
||||
}
|
||||
_incomingChanges.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
// things on objectsToRemove are ready for delete
|
||||
for (auto object : transaction.objectsToRemove) {
|
||||
_physicalObjects.remove(object);
|
||||
delete object;
|
||||
}
|
||||
transaction.clear();
|
||||
}
|
||||
|
||||
void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||
bool serverlessMode = getEntityTree()->isServerlessMode();
|
||||
for (auto stateItr : motionStates) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#define hifi_PhysicalEntitySimulation_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
|
@ -70,7 +72,7 @@ protected: // only called by EntitySimulation
|
|||
virtual void updateEntitiesInternal(uint64_t now) override;
|
||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
||||
void processChangedEntity(const EntityItemPointer& entity) override;
|
||||
virtual void clearEntitiesInternal() override;
|
||||
|
||||
void removeOwnershipData(EntityMotionState* motionState);
|
||||
|
@ -79,12 +81,8 @@ protected: // only called by EntitySimulation
|
|||
public:
|
||||
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
||||
|
||||
const VectorOfMotionStates& getObjectsToRemoveFromPhysics();
|
||||
void deleteObjectsRemovedFromPhysics();
|
||||
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
||||
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
||||
void getObjectsToChange(VectorOfMotionStates& result);
|
||||
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
|
||||
void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates);
|
||||
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
|
||||
|
@ -98,16 +96,25 @@ public:
|
|||
void sendOwnedUpdates(uint32_t numSubsteps);
|
||||
|
||||
private:
|
||||
SetOfEntities _entitiesToAddToPhysics;
|
||||
void buildMotionStatesForEntitiesThatNeedThem();
|
||||
|
||||
class ShapeRequest {
|
||||
public:
|
||||
ShapeRequest() { }
|
||||
ShapeRequest(const EntityItemPointer& e) : entity(e) { }
|
||||
bool operator<(const ShapeRequest& other) const { return entity.get() < other.entity.get(); }
|
||||
bool operator==(const ShapeRequest& other) const { return entity.get() == other.entity.get(); }
|
||||
EntityItemPointer entity { nullptr };
|
||||
mutable uint64_t shapeHash { 0 };
|
||||
};
|
||||
SetOfEntities _entitiesToAddToPhysics; // we could also call this: _entitiesThatNeedMotionStates
|
||||
SetOfEntities _entitiesToRemoveFromPhysics;
|
||||
|
||||
VectorOfMotionStates _objectsToDelete;
|
||||
|
||||
SetOfEntityMotionStates _incomingChanges; // EntityMotionStates that have changed from external sources
|
||||
// and need their RigidBodies updated
|
||||
|
||||
SetOfEntityMotionStates _incomingChanges; // EntityMotionStates changed by external events
|
||||
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
||||
|
||||
using ShapeRequests = std::set<ShapeRequest>;
|
||||
ShapeRequests _shapeRequests;
|
||||
|
||||
PhysicsEnginePointer _physicsEngine = nullptr;
|
||||
EntityEditPacketSender* _entityPacketSender = nullptr;
|
||||
|
||||
|
@ -117,6 +124,7 @@ private:
|
|||
workload::SpacePointer _space;
|
||||
uint64_t _nextBidExpiry;
|
||||
uint32_t _lastStepSendPackets { 0 };
|
||||
uint32_t _lastWorkDeliveryCount { 0 };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -178,8 +178,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
int32_t group, mask;
|
||||
motionState->computeCollisionGroupAndMask(group, mask);
|
||||
_dynamicsWorld->addRigidBody(body, group, mask);
|
||||
|
||||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
QList<EntityDynamicPointer> PhysicsEngine::removeDynamicsForBody(btRigidBody* body) {
|
||||
|
@ -252,6 +250,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) {
|
|||
}
|
||||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
_activeStaticBodies.clear();
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
||||
|
@ -260,35 +259,6 @@ void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
|||
}
|
||||
}
|
||||
|
||||
VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& objects) {
|
||||
VectorOfMotionStates stillNeedChange;
|
||||
for (auto object : objects) {
|
||||
uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
|
||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
||||
if (object->handleHardAndEasyChanges(flags, this)) {
|
||||
object->clearIncomingDirtyFlags();
|
||||
} else {
|
||||
stillNeedChange.push_back(object);
|
||||
}
|
||||
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
object->handleEasyChanges(flags);
|
||||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
|
||||
_activeStaticBodies.insert(object->getRigidBody());
|
||||
}
|
||||
}
|
||||
// active static bodies have changed (in an Easy way) and need their Aabbs updated
|
||||
// but we've configured Bullet to NOT update them automatically (for improved performance)
|
||||
// so we must do it ourselves
|
||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
||||
while (itr != _activeStaticBodies.end()) {
|
||||
_dynamicsWorld->updateSingleAabb(*itr);
|
||||
++itr;
|
||||
}
|
||||
return stillNeedChange;
|
||||
}
|
||||
|
||||
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||
// remove object from DynamicsWorld
|
||||
bumpAndPruneContacts(object);
|
||||
|
@ -320,7 +290,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction)
|
|||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
}
|
||||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
// adds
|
||||
|
@ -328,34 +297,16 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction)
|
|||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
||||
// changes
|
||||
std::vector<ObjectMotionState*> failedChanges;
|
||||
for (auto object : transaction.objectsToChange) {
|
||||
uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
|
||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
||||
if (object->handleHardAndEasyChanges(flags, this)) {
|
||||
object->clearIncomingDirtyFlags();
|
||||
} else {
|
||||
failedChanges.push_back(object);
|
||||
}
|
||||
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
object->handleEasyChanges(flags);
|
||||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
|
||||
_activeStaticBodies.insert(object->getRigidBody());
|
||||
}
|
||||
// reinserts
|
||||
for (auto object : transaction.objectsToReinsert) {
|
||||
reinsertObject(object);
|
||||
}
|
||||
// activeStaticBodies have changed (in an Easy way) and need their Aabbs updated
|
||||
// but we've configured Bullet to NOT update them automatically (for improved performance)
|
||||
// so we must do it ourselves
|
||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
||||
while (itr != _activeStaticBodies.end()) {
|
||||
_dynamicsWorld->updateSingleAabb(*itr);
|
||||
++itr;
|
||||
|
||||
for (auto object : transaction.activeStaticObjects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
_dynamicsWorld->updateSingleAabb(body);
|
||||
_activeStaticBodies.insert(body);
|
||||
}
|
||||
// we replace objectsToChange with any that failed
|
||||
transaction.objectsToChange.swap(failedChanges);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeContacts(ObjectMotionState* motionState) {
|
||||
|
|
|
@ -79,11 +79,13 @@ public:
|
|||
void clear() {
|
||||
objectsToRemove.clear();
|
||||
objectsToAdd.clear();
|
||||
objectsToChange.clear();
|
||||
objectsToReinsert.clear();
|
||||
activeStaticObjects.clear();
|
||||
}
|
||||
std::vector<ObjectMotionState*> objectsToRemove;
|
||||
std::vector<ObjectMotionState*> objectsToAdd;
|
||||
std::vector<ObjectMotionState*> objectsToChange;
|
||||
std::vector<ObjectMotionState*> objectsToReinsert;
|
||||
std::vector<ObjectMotionState*> activeStaticObjects;
|
||||
};
|
||||
|
||||
PhysicsEngine(const glm::vec3& offset);
|
||||
|
@ -97,7 +99,7 @@ public:
|
|||
void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||
|
||||
void addObjects(const VectorOfMotionStates& objects);
|
||||
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
|
||||
void changeObjects(const VectorOfMotionStates& objects);
|
||||
void reinsertObject(ObjectMotionState* object);
|
||||
|
||||
void processTransaction(Transaction& transaction);
|
||||
|
|
|
@ -293,6 +293,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
|||
radiuses.push_back(sphereData.w);
|
||||
}
|
||||
shape = new btMultiSphereShape(positions.data(), radiuses.data(), (int)positions.size());
|
||||
const float MULTI_SPHERE_MARGIN = 0.001f;
|
||||
shape->setMargin(MULTI_SPHERE_MARGIN);
|
||||
}
|
||||
break;
|
||||
case SHAPE_TYPE_ELLIPSOID: {
|
||||
|
@ -433,6 +435,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
|||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (shape) {
|
||||
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||
|
@ -457,6 +461,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
|||
shape = compound;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: warn about this case
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
@ -481,3 +487,8 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
|
|||
}
|
||||
delete nonConstShape;
|
||||
}
|
||||
|
||||
void ShapeFactory::Worker::run() {
|
||||
shape = ShapeFactory::createShapeFromInfo(shapeInfo);
|
||||
emit submitWork(this);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <QObject>
|
||||
#include <QtCore/QRunnable>
|
||||
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
|
@ -22,6 +24,17 @@
|
|||
namespace ShapeFactory {
|
||||
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
void deleteShape(const btCollisionShape* shape);
|
||||
|
||||
class Worker : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Worker(const ShapeInfo& info) : shapeInfo(info), shape(nullptr) {}
|
||||
void run() override;
|
||||
ShapeInfo shapeInfo;
|
||||
const btCollisionShape* shape;
|
||||
signals:
|
||||
void submitWork(Worker*);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeFactory_h
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue