mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 18:15:49 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into addNoLogin
This commit is contained in:
commit
c9f9679cf7
130 changed files with 1127 additions and 792 deletions
CMakeLists.txt
android
assignment-client/src
domain-server
interface
resources
html/img
meshes/controller/touch
touch_l_bumper.fbxtouch_l_button_x.fbxtouch_l_button_y.fbxtouch_l_joystick.fbxtouch_l_trigger.fbxtouch_r_bumper.fbxtouch_r_button_a.fbxtouch_r_button_b.fbxtouch_r_joystick.fbxtouch_r_trigger.fbx
qml/hifi/commerce
common
purchases
wallet
src
libraries
animation/src
avatars-renderer/src/avatars-renderer
avatars/src
controllers/src/controllers
entities-renderer/src
RenderableMaterialEntityItem.cppRenderableMaterialEntityItem.hRenderableModelEntityItem.cppRenderableModelEntityItem.h
entities/src
EntityItem.cppEntityItem.hEntityItemProperties.cppEntityItemProperties.hEntityScriptingInterface.cppModelEntityItem.cppModelEntityItem.h
gpu-gl-common/src/gpu/gl
gpu/src/gpu
graphics/src/graphics
input-plugins/src/input-plugins
model-networking/src/model-networking
networking/src
BandwidthRecorder.cppBandwidthRecorder.hDomainHandler.cppNetworkPeer.cppNetworkPeer.hNodeList.cpp
udt
physics/src
plugins/src/plugins
pointers/src
render-utils/src
|
@ -7,6 +7,11 @@ else()
|
|||
cmake_minimum_required(VERSION 3.2)
|
||||
endif()
|
||||
|
||||
# squelch the Policy CMP0074 warning without requiring an update to cmake 3.12.
|
||||
if ((${CMAKE_MAJOR_VERSION} EQUAL 3 AND ${CMAKE_MINOR_VERSION} GREATER 11) OR ${CMAKE_MAJOR_VERSION} GREATER 3)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
|
||||
project(hifi)
|
||||
|
||||
include("cmake/init.cmake")
|
||||
|
|
1
android/gradle.properties
Normal file
1
android/gradle.properties
Normal file
|
@ -0,0 +1 @@
|
|||
org.gradle.jvmargs=-Xms2g -Xmx4g
|
|
@ -368,11 +368,7 @@ void Agent::executeScript() {
|
|||
|
||||
// setup an Avatar for the script to use
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
|
||||
scriptedAvatar->setID(getSessionUUID());
|
||||
|
||||
connect(_scriptEngine.data(), SIGNAL(update(float)),
|
||||
scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
|
||||
scriptedAvatar->setForceFaceTrackerConnected(true);
|
||||
|
||||
// call model URL setters with empty URLs so our avatar, if user, will have the default models
|
||||
|
@ -504,8 +500,6 @@ void Agent::executeScript() {
|
|||
|
||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||
|
||||
_avatarAudioTimer.start();
|
||||
|
||||
// Agents should run at 45hz
|
||||
static const int AVATAR_DATA_HZ = 45;
|
||||
static const int AVATAR_DATA_IN_MSECS = MSECS_PER_SECOND / AVATAR_DATA_HZ;
|
||||
|
@ -530,7 +524,8 @@ void Agent::executeScript() {
|
|||
}
|
||||
|
||||
avatarDataTimer->stop();
|
||||
_avatarAudioTimer.stop();
|
||||
|
||||
setIsAvatar(false); // will stop timers for sending identity packets
|
||||
}
|
||||
|
||||
setFinished(true);
|
||||
|
@ -582,28 +577,33 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
}
|
||||
_isAvatar = isAvatar;
|
||||
|
||||
if (_isAvatar && !_avatarIdentityTimer) {
|
||||
// set up the avatar timers
|
||||
_avatarIdentityTimer = new QTimer(this);
|
||||
_avatarQueryTimer = new QTimer(this);
|
||||
auto scriptableAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
if (_isAvatar) {
|
||||
if (!_avatarIdentityTimer) {
|
||||
// set up the avatar timers
|
||||
_avatarIdentityTimer = new QTimer(this);
|
||||
_avatarQueryTimer = new QTimer(this);
|
||||
|
||||
// connect our slot
|
||||
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
|
||||
connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
|
||||
// connect our slot
|
||||
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
|
||||
connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
|
||||
|
||||
static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
|
||||
// start the timers
|
||||
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
|
||||
_avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
|
||||
// start the timers
|
||||
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
|
||||
_avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
|
||||
|
||||
// tell the avatarAudioTimer to start ticking
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
|
||||
}
|
||||
connect(_scriptEngine.data(), &ScriptEngine::update,
|
||||
scriptableAvatar.data(), &ScriptableAvatar::update, Qt::QueuedConnection);
|
||||
|
||||
if (!_isAvatar) {
|
||||
// tell the avatarAudioTimer to start ticking
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
|
||||
}
|
||||
|
||||
_entityEditSender.setMyAvatar(scriptableAvatar.data());
|
||||
} else {
|
||||
if (_avatarIdentityTimer) {
|
||||
_avatarIdentityTimer->stop();
|
||||
delete _avatarIdentityTimer;
|
||||
|
@ -630,14 +630,14 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
packet->writePrimitive(KillAvatarReason::NoReason);
|
||||
nodeList->sendPacket(std::move(packet), *node);
|
||||
});
|
||||
|
||||
disconnect(_scriptEngine.data(), &ScriptEngine::update,
|
||||
scriptableAvatar.data(), &ScriptableAvatar::update);
|
||||
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
|
||||
|
||||
_entityEditSender.setMyAvatar(nullptr);
|
||||
} else {
|
||||
auto scriptableAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
_entityEditSender.setMyAvatar(scriptableAvatar.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,7 +788,7 @@ void Agent::processAgentAvatarAudio() {
|
|||
// seek past the sequence number, will be packed when destination node is known
|
||||
audioPacket->seek(sizeof(quint16));
|
||||
|
||||
if (silentFrame) {
|
||||
if (silentFrame && !_flushEncoder) {
|
||||
|
||||
if (!_isListeningToAudioStream) {
|
||||
// if we have a silent frame and we're not listening then just send nothing and break out of here
|
||||
|
@ -810,7 +810,7 @@ void Agent::processAgentAvatarAudio() {
|
|||
|
||||
// no matter what, the loudness should be set to 0
|
||||
computeLoudness(nullptr, scriptedAvatar);
|
||||
} else if (nextSoundOutput) {
|
||||
} else if (nextSoundOutput || _flushEncoder) {
|
||||
|
||||
// write the codec
|
||||
audioPacket->writeString(_selectedCodecName);
|
||||
|
@ -864,8 +864,6 @@ void Agent::processAgentAvatarAudio() {
|
|||
}
|
||||
|
||||
void Agent::aboutToFinish() {
|
||||
setIsAvatar(false);// will stop timers for sending identity packets
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
|
|
|
@ -497,7 +497,7 @@ void AudioMixerClientData::processStreamPacket(ReceivedMessage& message, Concurr
|
|||
|
||||
if (newStream) {
|
||||
// whenever a stream is added, push it to the concurrent vector of streams added this frame
|
||||
addedStreams.emplace_back(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get());
|
||||
addedStreams.push_back(AddedStream(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -380,6 +380,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
||||
++numAvatarsHeldBack;
|
||||
shouldIgnore = true;
|
||||
} else if (lastSeqFromSender == 0) {
|
||||
// We have have not yet recieved any data about this avatar. Ignore it for now
|
||||
// This is important for Agent scripts that are not avatar
|
||||
// so that they don't appear to be an avatar at the origin
|
||||
shouldIgnore = true;
|
||||
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
|
||||
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
|
||||
++numAvatarsWithSkippedFrames;
|
||||
|
|
|
@ -164,7 +164,7 @@ public:
|
|||
void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement);
|
||||
bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); }
|
||||
|
||||
private slots:
|
||||
public slots:
|
||||
void update(float deltatime);
|
||||
|
||||
private:
|
||||
|
|
|
@ -59,6 +59,7 @@ $(document).ready(function(){
|
|||
$.ajax({
|
||||
url: '/content/upload',
|
||||
type: 'POST',
|
||||
timeout: 3600000, // Set timeout to 1h
|
||||
cache: false,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
|
|
|
@ -2906,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction)
|
|||
// collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and
|
||||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
nodeList->eachNode([&direction, &replicationNodesInSettings, &replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) {
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 76 KiB After ![]() (image error) Size: 82 KiB ![]() ![]() |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before ![]() (image error) Size: 72 KiB After ![]() (image error) Size: 164 KiB ![]() ![]() |
Binary file not shown.
Before ![]() (image error) Size: 81 KiB After ![]() (image error) Size: 175 KiB ![]() ![]() |
|
@ -39,6 +39,7 @@ Item {
|
|||
property string sendingPubliclyEffectImage;
|
||||
property var http;
|
||||
property var listModelName;
|
||||
property var keyboardContainer: nil;
|
||||
|
||||
// This object is always used in a popup or full-screen Wallet section.
|
||||
// This MouseArea is used to prevent a user from being
|
||||
|
@ -1125,8 +1126,7 @@ Item {
|
|||
checked: Settings.getValue("sendAssetsNearbyPublicly", true);
|
||||
text: "Show Effect"
|
||||
// Anchors
|
||||
anchors.top: messageContainer.bottom;
|
||||
anchors.topMargin: 16;
|
||||
anchors.verticalCenter: bottomBarContainer.verticalCenter;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
width: 130;
|
||||
|
@ -1168,6 +1168,9 @@ Item {
|
|||
lightboxPopup.visible = false;
|
||||
}
|
||||
lightboxPopup.visible = true;
|
||||
if (keyboardContainer) {
|
||||
keyboardContainer.keyboardRaised = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1178,8 +1181,8 @@ Item {
|
|||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.bottomMargin: 20;
|
||||
anchors.top: messageContainer.bottom;
|
||||
anchors.topMargin: 20;
|
||||
height: 60;
|
||||
|
||||
// "CANCEL" button
|
||||
|
@ -1187,11 +1190,11 @@ Item {
|
|||
id: cancelButton_sendAssetStep;
|
||||
color: root.assetName === "" ? hifi.buttons.noneBorderlessWhite : hifi.buttons.noneBorderlessGray;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 24;
|
||||
anchors.right: sendButton.left;
|
||||
anchors.rightMargin: 24;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: 40;
|
||||
width: 150;
|
||||
width: 100;
|
||||
text: "CANCEL";
|
||||
onClicked: {
|
||||
resetSendAssetData();
|
||||
|
@ -1205,10 +1208,10 @@ Item {
|
|||
color: hifi.buttons.blue;
|
||||
colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 24;
|
||||
anchors.rightMargin: 0;
|
||||
anchors.verticalCenter: parent.verticalCenter;
|
||||
height: 40;
|
||||
width: 150;
|
||||
width: 100;
|
||||
text: "SUBMIT";
|
||||
onClicked: {
|
||||
if (root.assetName === "" && parseInt(amountTextField.text) > parseInt(balanceText.text)) {
|
||||
|
|
|
@ -158,6 +158,7 @@ Rectangle {
|
|||
listModelName: "Gift Connections";
|
||||
z: 998;
|
||||
visible: root.activeView === "giftAsset";
|
||||
keyboardContainer: root;
|
||||
anchors.fill: parent;
|
||||
parentAppTitleBarHeight: 70;
|
||||
parentAppNavBarHeight: 0;
|
||||
|
@ -585,7 +586,7 @@ Rectangle {
|
|||
visible: purchasesModel.count !== 0;
|
||||
clip: true;
|
||||
model: purchasesModel;
|
||||
snapMode: ListView.SnapToItem;
|
||||
snapMode: ListView.NoSnap;
|
||||
// Anchors
|
||||
anchors.top: separator.bottom;
|
||||
anchors.left: parent.left;
|
||||
|
|
|
@ -354,6 +354,7 @@ Rectangle {
|
|||
listModelName: "Send Money Connections";
|
||||
z: 997;
|
||||
visible: root.activeView === "sendMoney";
|
||||
keyboardContainer: root;
|
||||
anchors.fill: parent;
|
||||
parentAppTitleBarHeight: titleBarContainer.height;
|
||||
parentAppNavBarHeight: tabButtonsContainer.height;
|
||||
|
|
|
@ -3540,6 +3540,7 @@ void Application::setIsInterstitialMode(bool interstitialMode) {
|
|||
if (enableInterstitial) {
|
||||
if (_interstitialMode != interstitialMode) {
|
||||
_interstitialMode = interstitialMode;
|
||||
emit interstitialModeChanged(_interstitialMode);
|
||||
|
||||
DependencyManager::get<AudioClient>()->setAudioPaused(_interstitialMode);
|
||||
DependencyManager::get<AvatarManager>()->setMyAvatarDataPacketsPaused(_interstitialMode);
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include <RunningMarker.h>
|
||||
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "FancyCamera.h"
|
||||
#include "ConnectionMonitor.h"
|
||||
#include "CursorManager.h"
|
||||
|
@ -335,6 +334,8 @@ signals:
|
|||
|
||||
void uploadRequest(QString path);
|
||||
|
||||
void interstitialModeChanged(bool isInInterstitialMode);
|
||||
|
||||
void loginDialogPoppedUp();
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -78,8 +78,10 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
|||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// since we're creating this object we will immediately volunteer to own its simulation
|
||||
entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
if (entityProperties.getDynamic()) {
|
||||
// since we're creating a dynamic object we volunteer immediately to own its simulation
|
||||
entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
}
|
||||
entityProperties.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";
|
||||
|
@ -108,6 +110,9 @@ AvatarBookmarks::AvatarBookmarks() {
|
|||
|
||||
if (!QFile::copy(defaultBookmarksFilename, _bookmarksFilename)) {
|
||||
qDebug() << "failed to copy" << defaultBookmarksFilename << "to" << _bookmarksFilename;
|
||||
} else {
|
||||
QFile bookmarksFile(_bookmarksFilename);
|
||||
bookmarksFile.setPermissions(bookmarksFile.permissions() | QFile::WriteUser);
|
||||
}
|
||||
}
|
||||
readFromFile();
|
||||
|
|
|
@ -55,15 +55,7 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND /
|
|||
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
|
||||
const QUuid MY_AVATAR_KEY; // NULL key
|
||||
|
||||
namespace {
|
||||
// For an unknown avatar-data packet, wait this long before requesting the identity.
|
||||
constexpr std::chrono::milliseconds REQUEST_UNKNOWN_IDENTITY_DELAY { 5 * 1000 };
|
||||
constexpr int REQUEST_UNKNOWN_IDENTITY_TRANSMITS = 3;
|
||||
}
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_avatarsToFade(),
|
||||
_myAvatar(new MyAvatar(qApp->thread()), [](MyAvatar* ptr) { ptr->deleteLater(); })
|
||||
{
|
||||
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||
|
@ -322,28 +314,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
||||
// Check on avatars with pending identities:
|
||||
steady_clock::time_point now = steady_clock::now();
|
||||
QWriteLocker writeLock(&_hashLock);
|
||||
for (auto pendingAvatar = _pendingAvatars.begin(); pendingAvatar != _pendingAvatars.end(); ++pendingAvatar) {
|
||||
if (now - pendingAvatar->creationTime >= REQUEST_UNKNOWN_IDENTITY_DELAY) {
|
||||
// Too long without an ID
|
||||
sendIdentityRequest(pendingAvatar->avatar->getID());
|
||||
if (++pendingAvatar->transmits >= REQUEST_UNKNOWN_IDENTITY_TRANSMITS) {
|
||||
qCDebug(avatars) << "Requesting identity for unknown avatar (final request)" <<
|
||||
pendingAvatar->avatar->getID().toString();
|
||||
|
||||
pendingAvatar = _pendingAvatars.erase(pendingAvatar);
|
||||
if (pendingAvatar == _pendingAvatars.end()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pendingAvatar->creationTime = now;
|
||||
qCDebug(avatars) << "Requesting identity for unknown avatar" << pendingAvatar->avatar->getID().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
|||
* @borrows AvatarList.sessionUUIDChanged as sessionUUIDChanged
|
||||
* @borrows AvatarList.processAvatarDataPacket as processAvatarDataPacket
|
||||
* @borrows AvatarList.processAvatarIdentityPacket as processAvatarIdentityPacket
|
||||
* @borrows AvatarList.processBulkAvatarTraits as processBulkAvatarTraits
|
||||
* @borrows AvatarList.processKillAvatar as processKillAvatar
|
||||
*/
|
||||
|
||||
|
@ -152,6 +153,13 @@ public:
|
|||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findParabolaIntersectionVector
|
||||
* @param {PickParabola} pick
|
||||
* @param {Uuid[]} avatarsToInclude
|
||||
* @param {Uuid[]} avatarsToDiscard
|
||||
* @returns {ParabolaToAvatarIntersectionResult}
|
||||
*/
|
||||
Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
|
@ -176,7 +184,7 @@ public:
|
|||
* than iterating over each avatar and obtaining data about them in JavaScript, as that method
|
||||
* locks and unlocks each avatar's data structure potentially hundreds of times per update tick.
|
||||
* @function AvatarManager.getPalData
|
||||
* @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about
|
||||
* @param {string[]} [specificAvatarIdentifiers] - A list of specific Avatar Identifiers about
|
||||
* which you want to get PAL data
|
||||
* @returns {object}
|
||||
*/
|
||||
|
|
|
@ -122,7 +122,6 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_goToOrientation(),
|
||||
_prevShouldDrawHead(true),
|
||||
_audioListenerMode(FROM_HEAD),
|
||||
_hmdAtRestDetector(glm::vec3(0), glm::quat()),
|
||||
_dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),
|
||||
_headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f),
|
||||
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
|
||||
|
@ -702,6 +701,46 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// before we perform rig animations and IK.
|
||||
updateSensorToWorldMatrix();
|
||||
|
||||
// if we detect the hand controller is at rest, i.e. lying on the table, or the hand is too far away from the hmd
|
||||
// disable the associated hand controller input.
|
||||
{
|
||||
// NOTE: all poses are in sensor space.
|
||||
auto leftHandIter = _controllerPoseMap.find(controller::Action::LEFT_HAND);
|
||||
if (leftHandIter != _controllerPoseMap.end() && leftHandIter->second.isValid()) {
|
||||
_leftHandAtRestDetector.update(leftHandIter->second.getTranslation(), leftHandIter->second.getRotation());
|
||||
if (_leftHandAtRestDetector.isAtRest()) {
|
||||
leftHandIter->second.valid = false;
|
||||
}
|
||||
} else {
|
||||
_leftHandAtRestDetector.invalidate();
|
||||
}
|
||||
|
||||
auto rightHandIter = _controllerPoseMap.find(controller::Action::RIGHT_HAND);
|
||||
if (rightHandIter != _controllerPoseMap.end() && rightHandIter->second.isValid()) {
|
||||
_rightHandAtRestDetector.update(rightHandIter->second.getTranslation(), rightHandIter->second.getRotation());
|
||||
if (_rightHandAtRestDetector.isAtRest()) {
|
||||
rightHandIter->second.valid = false;
|
||||
}
|
||||
} else {
|
||||
_rightHandAtRestDetector.invalidate();
|
||||
}
|
||||
|
||||
auto headIter = _controllerPoseMap.find(controller::Action::HEAD);
|
||||
|
||||
// The 99th percentile man has a spine to fingertip to height ratio of 0.45. Lets increase that by about 10% to 0.5
|
||||
// then measure the distance the center of the eyes to the finger tips. To come up with this ratio.
|
||||
// From "The Measure of Man and Woman: Human Factors in Design, Revised Edition" by Alvin R. Tilley, Henry Dreyfuss Associates
|
||||
const float MAX_HEAD_TO_HAND_DISTANCE_RATIO = 0.52f;
|
||||
|
||||
float maxHeadHandDistance = getUserHeight() * MAX_HEAD_TO_HAND_DISTANCE_RATIO;
|
||||
if (glm::length(headIter->second.getTranslation() - leftHandIter->second.getTranslation()) > maxHeadHandDistance) {
|
||||
leftHandIter->second.valid = false;
|
||||
}
|
||||
if (glm::length(headIter->second.getTranslation() - rightHandIter->second.getTranslation()) > maxHeadHandDistance) {
|
||||
rightHandIter->second.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("skeleton");
|
||||
|
||||
|
|
|
@ -629,8 +629,6 @@ public:
|
|||
|
||||
const MyHead* getMyHead() const;
|
||||
|
||||
Q_INVOKABLE void toggleSmoothPoleVectors() { _skeletonModel->getRig().toggleSmoothPoleVectors(); };
|
||||
|
||||
/**jsdoc
|
||||
* Get the current position of the avatar's "Head" joint.
|
||||
* @function MyAvatar.getHeadPosition
|
||||
|
@ -951,50 +949,72 @@ public:
|
|||
void removeWearableAvatarEntities();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether your avatar is flying or not.
|
||||
* @function MyAvatar.isFlying
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if your avatar is flying and not taking off or falling, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool isFlying();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether your avatar is in the air or not.
|
||||
* @function MyAvatar.isInAir
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if your avatar is taking off, flying, or falling, otherwise <code>false</code>
|
||||
* because your avatar is on the ground.
|
||||
*/
|
||||
Q_INVOKABLE bool isInAir();
|
||||
|
||||
/**jsdoc
|
||||
* Set your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends
|
||||
* on whether the domain you're in allows you to fly.
|
||||
* @function MyAvatar.setFlyingEnabled
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} enabled - Set <code>true</code> if you want to enable flying in your current desktop or HMD display
|
||||
* mode, otherwise set <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE void setFlyingEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Get your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends
|
||||
* on whether the domain you're in allows you to fly.
|
||||
* @function MyAvatar.getFlyingEnabled
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if your preference is to enable flying in your current desktop or HMD display mode,
|
||||
* otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool getFlyingEnabled();
|
||||
|
||||
/**jsdoc
|
||||
* Set your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain
|
||||
* you're in allows you to fly.
|
||||
* @function MyAvatar.setFlyingDesktopPref
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} enabled - Set <code>true</code> if you want to enable flying in desktop display mode, otherwise set
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE void setFlyingDesktopPref(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Get your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain
|
||||
* you're in allows you to fly.
|
||||
* @function MyAvatar.getFlyingDesktopPref
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if your preference is to enable flying in desktop display mode, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool getFlyingDesktopPref();
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setFlyingDesktopPref
|
||||
* @param {boolean} enabled
|
||||
* Set your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain
|
||||
* you're in allows you to fly.
|
||||
* @function MyAvatar.setFlyingHMDPref
|
||||
* @param {boolean} enabled - Set <code>true</code> if you want to enable flying in HMD display mode, otherwise set
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE void setFlyingHMDPref(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getFlyingDesktopPref
|
||||
* @returns {boolean}
|
||||
* Get your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain
|
||||
* you're in allows you to fly.
|
||||
* @function MyAvatar.getFlyingHMDPref
|
||||
* @returns {boolean} <code>true</code> if your preference is to enable flying in HMD display mode, otherwise
|
||||
* <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool getFlyingHMDPref();
|
||||
|
||||
|
@ -1766,8 +1786,8 @@ private:
|
|||
glm::vec3 _customListenPosition;
|
||||
glm::quat _customListenOrientation;
|
||||
|
||||
AtRestDetector _hmdAtRestDetector;
|
||||
bool _lastIsMoving { false };
|
||||
AtRestDetector _leftHandAtRestDetector;
|
||||
AtRestDetector _rightHandAtRestDetector;
|
||||
|
||||
// all poses are in sensor-frame
|
||||
std::map<controller::Action, controller::Pose> _controllerPoseMap;
|
||||
|
|
|
@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
|||
}
|
||||
|
||||
glm::mat4 hipsMat;
|
||||
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState())) {
|
||||
if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && myAvatar->getHMDLeanRecenterEnabled()) {
|
||||
// then we use center of gravity model
|
||||
hipsMat = myAvatar->deriveBodyUsingCgModel();
|
||||
} else {
|
||||
|
|
|
@ -76,4 +76,4 @@ protected:
|
|||
bool _includeNormals;
|
||||
};
|
||||
|
||||
#endif // hifi_CollisionPick_h
|
||||
#endif // hifi_CollisionPick_h
|
||||
|
|
|
@ -27,7 +27,7 @@ class StartEndRenderState {
|
|||
public:
|
||||
StartEndRenderState() {}
|
||||
StartEndRenderState(const OverlayID& startID, const OverlayID& endID);
|
||||
virtual ~StartEndRenderState() {}
|
||||
virtual ~StartEndRenderState() = default;
|
||||
|
||||
const OverlayID& getStartID() const { return _startID; }
|
||||
const OverlayID& getEndID() const { return _endID; }
|
||||
|
|
|
@ -54,6 +54,9 @@ WindowScriptingInterface::WindowScriptingInterface() {
|
|||
});
|
||||
|
||||
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &WindowScriptingInterface::onWindowGeometryChanged);
|
||||
connect(qApp, &Application::interstitialModeChanged, [this] (bool interstitialMode) {
|
||||
emit interstitialModeChanged(interstitialMode);
|
||||
});
|
||||
}
|
||||
|
||||
WindowScriptingInterface::~WindowScriptingInterface() {
|
||||
|
|
|
@ -619,6 +619,14 @@ signals:
|
|||
*/
|
||||
void redirectErrorStateChanged(bool isInErrorState);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when interstitial mode changes.
|
||||
* @function Window.interstitialModeChanged
|
||||
* @param {bool} interstitialMode - The mode of the interstitial is changed to.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void interstitialModeChanged(bool interstitialMode);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
|
||||
* <code>includeAnimated = false</code> or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}.
|
||||
|
|
|
@ -169,7 +169,8 @@ std::map<QString, QString> AnimVariantMap::toDebugMap() const {
|
|||
break;
|
||||
*/
|
||||
default:
|
||||
assert(("invalid AnimVariant::Type", false));
|
||||
// invalid AnimVariant::Type
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
class Animation;
|
||||
|
||||
typedef QSharedPointer<Animation> AnimationPointer;
|
||||
using AnimationPointer = QSharedPointer<Animation>;
|
||||
|
||||
class AnimationCache : public ResourceCache, public Dependency {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -1253,7 +1253,6 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||
|
||||
const bool ENABLE_POLE_VECTORS = true;
|
||||
const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
if (leftHandEnabled) {
|
||||
|
||||
|
@ -1279,33 +1278,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector);
|
||||
if (usePoleVector) {
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
if (_smoothPoleVectors) {
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
if (!_prevLeftHandPoleVectorValid) {
|
||||
_prevLeftHandPoleVectorValid = true;
|
||||
_prevLeftHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector;
|
||||
} else {
|
||||
_prevLeftHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
_animVars.set("leftHandPoleVectorEnabled", true);
|
||||
_animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X);
|
||||
_animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector));
|
||||
_animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector));
|
||||
} else {
|
||||
_prevLeftHandPoleVectorValid = false;
|
||||
_animVars.set("leftHandPoleVectorEnabled", false);
|
||||
}
|
||||
|
||||
} else {
|
||||
_prevLeftHandPoleVectorValid = false;
|
||||
_animVars.set("leftHandPoleVectorEnabled", false);
|
||||
}
|
||||
} else {
|
||||
_prevLeftHandPoleVectorValid = false;
|
||||
_animVars.set("leftHandPoleVectorEnabled", false);
|
||||
|
||||
_animVars.unset("leftHandPosition");
|
||||
|
@ -1344,33 +1326,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector);
|
||||
if (usePoleVector) {
|
||||
glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector);
|
||||
|
||||
if (_smoothPoleVectors) {
|
||||
// smooth toward desired pole vector from previous pole vector... to reduce jitter
|
||||
if (!_prevRightHandPoleVectorValid) {
|
||||
_prevRightHandPoleVectorValid = true;
|
||||
_prevRightHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector);
|
||||
glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR);
|
||||
_prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector;
|
||||
} else {
|
||||
_prevRightHandPoleVector = sensorPoleVector;
|
||||
}
|
||||
|
||||
_animVars.set("rightHandPoleVectorEnabled", true);
|
||||
_animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X);
|
||||
_animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector));
|
||||
_animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector));
|
||||
} else {
|
||||
_prevRightHandPoleVectorValid = false;
|
||||
_animVars.set("rightHandPoleVectorEnabled", false);
|
||||
}
|
||||
} else {
|
||||
_prevRightHandPoleVectorValid = false;
|
||||
_animVars.set("rightHandPoleVectorEnabled", false);
|
||||
}
|
||||
} else {
|
||||
_prevRightHandPoleVectorValid = false;
|
||||
_animVars.set("rightHandPoleVectorEnabled", false);
|
||||
|
||||
_animVars.unset("rightHandPosition");
|
||||
|
|
|
@ -227,7 +227,6 @@ public:
|
|||
const AnimVariantMap& getAnimVars() const { return _lastAnimVars; }
|
||||
const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); }
|
||||
|
||||
void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; };
|
||||
signals:
|
||||
void onLoadComplete();
|
||||
|
||||
|
@ -381,14 +380,6 @@ protected:
|
|||
glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevLeftFootPoleVectorValid { false };
|
||||
|
||||
glm::vec3 _prevRightHandPoleVector{ -Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevRightHandPoleVectorValid{ false };
|
||||
|
||||
glm::vec3 _prevLeftHandPoleVector{ -Vectors::UNIT_Z }; // sensor space
|
||||
bool _prevLeftHandPoleVectorValid{ false };
|
||||
|
||||
bool _smoothPoleVectors { false };
|
||||
|
||||
int _rigId;
|
||||
bool _headEnabled { false };
|
||||
|
||||
|
|
|
@ -173,7 +173,6 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) {
|
|||
Status status = Status::IDLE;
|
||||
if (_isTransiting) {
|
||||
float nextTime = _currentTime + deltaTime;
|
||||
glm::vec3 newPosition;
|
||||
if (nextTime >= _totalTime) {
|
||||
_currentPosition = _endPosition;
|
||||
_isTransiting = false;
|
||||
|
@ -704,6 +703,19 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
|||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
void Avatar::metaBlendshapeOperator(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes,
|
||||
const render::ItemIDs& subItemIDs) {
|
||||
render::Transaction transaction;
|
||||
transaction.updateItem<AvatarData>(_renderItemID, [blendshapeNumber, blendshapeOffsets, blendedMeshSizes,
|
||||
subItemIDs](AvatarData& avatar) {
|
||||
auto avatarPtr = dynamic_cast<Avatar*>(&avatar);
|
||||
if (avatarPtr) {
|
||||
avatarPtr->setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs);
|
||||
}
|
||||
});
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||
auto avatarPayloadPointer = std::shared_ptr<render::Payload<AvatarData>>(avatarPayload);
|
||||
|
@ -713,7 +725,8 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc
|
|||
// INitialize the _render bound as we are creating the avatar render item
|
||||
_renderBound = getBounds();
|
||||
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
||||
_skeletonModel->addToScene(scene, transaction);
|
||||
using namespace std::placeholders;
|
||||
_skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4));
|
||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
_skeletonModel->setGroupCulled(true);
|
||||
_skeletonModel->setCanCastShadow(true);
|
||||
|
@ -792,7 +805,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) {
|
|||
avatarPtr->_renderBound = renderBound;
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -936,7 +949,8 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
|||
render::Transaction transaction;
|
||||
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
|
||||
_skeletonModel->removeFromScene(scene, transaction);
|
||||
_skeletonModel->addToScene(scene, transaction);
|
||||
using namespace std::placeholders;
|
||||
_skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4));
|
||||
|
||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
_skeletonModel->setGroupCulled(true);
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "Rig.h"
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include "MetaModelPayload.h"
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar);
|
||||
|
@ -108,7 +110,7 @@ private:
|
|||
float _scale { 1.0f };
|
||||
};
|
||||
|
||||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
||||
class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaModelPayload {
|
||||
Q_OBJECT
|
||||
|
||||
// This property has JSDoc in MyAvatar.h.
|
||||
|
@ -620,6 +622,9 @@ protected:
|
|||
static const float ATTACHMENT_LOADING_PRIORITY;
|
||||
|
||||
LoadingStatus _loadingStatus { LoadingStatus::NoModel };
|
||||
|
||||
void metaBlendshapeOperator(int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes,
|
||||
const render::ItemIDs& subItemIDs);
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -258,7 +258,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
|
|||
|
||||
if (isNewAvatar) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
|
||||
avatar->setIsNewAvatar(true);
|
||||
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
|
||||
for (auto replicaID : replicaIDs) {
|
||||
|
@ -300,7 +299,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
|
||||
{
|
||||
QReadLocker locker(&_hashLock);
|
||||
_pendingAvatars.remove(identityUUID);
|
||||
auto me = _avatarHash.find(EMPTY);
|
||||
if ((me != _avatarHash.end()) && (identityUUID == me.value()->getSessionUUID())) {
|
||||
// We add MyAvatar to _avatarHash with an empty UUID. Code relies on this. In order to correctly handle an
|
||||
|
@ -419,7 +417,6 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo
|
|||
}
|
||||
}
|
||||
|
||||
_pendingAvatars.remove(sessionUUID);
|
||||
auto removedAvatar = _avatarHash.take(sessionUUID);
|
||||
|
||||
if (removedAvatar) {
|
||||
|
|
|
@ -161,6 +161,11 @@ protected slots:
|
|||
*/
|
||||
void processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarList.processBulkAvatarTraits
|
||||
* @param {} message
|
||||
* @param {} sendingNode
|
||||
*/
|
||||
void processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
/**jsdoc
|
||||
|
@ -183,15 +188,8 @@ protected:
|
|||
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason);
|
||||
|
||||
AvatarHash _avatarHash;
|
||||
struct PendingAvatar {
|
||||
std::chrono::steady_clock::time_point creationTime;
|
||||
int transmits;
|
||||
AvatarSharedPointer avatar;
|
||||
};
|
||||
using AvatarPendingHash = QHash<QUuid, PendingAvatar>;
|
||||
AvatarPendingHash _pendingAvatars;
|
||||
mutable QReadWriteLock _hashLock;
|
||||
AvatarHash _avatarHash;
|
||||
|
||||
std::unordered_map<QUuid, AvatarTraits::TraitVersions> _processedTraitVersions;
|
||||
AvatarReplicas _replicas;
|
||||
|
|
|
@ -98,7 +98,7 @@ enum Hand {
|
|||
class InputDevice {
|
||||
public:
|
||||
InputDevice(const QString& name) : _name(name) {}
|
||||
virtual ~InputDevice() {}
|
||||
virtual ~InputDevice() = default;
|
||||
|
||||
using Pointer = std::shared_ptr<InputDevice>;
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace controller {
|
|||
using Factory = hifi::SimpleFactory<Conditional, QString>;
|
||||
using Lambda = std::function<bool()>;
|
||||
|
||||
virtual ~Conditional() = default;
|
||||
|
||||
virtual bool satisfied() = 0;
|
||||
virtual bool parseParameters(const QJsonValue& parameters) { return true; }
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ namespace controller {
|
|||
using Lambda = std::function<float(float)>;
|
||||
using Factory = hifi::SimpleFactory<Filter, QString>;
|
||||
|
||||
virtual ~Filter() = default;
|
||||
|
||||
virtual float apply(float value) const = 0;
|
||||
virtual Pose apply(Pose value) const = 0;
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@ public:
|
|||
AndConditional(Conditional::Pointer& first, Conditional::Pointer& second)
|
||||
: _children({ first, second }) {}
|
||||
|
||||
virtual ~AndConditional() {}
|
||||
|
||||
virtual bool satisfied() override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -18,7 +18,6 @@ namespace controller {
|
|||
class EndpointConditional : public Conditional {
|
||||
public:
|
||||
EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
|
||||
virtual ~EndpointConditional() {}
|
||||
virtual bool satisfied() override { return _endpoint && _endpoint->peek() != 0.0f; }
|
||||
private:
|
||||
Endpoint::Pointer _endpoint;
|
||||
|
|
|
@ -19,7 +19,6 @@ namespace controller {
|
|||
using Pointer = std::shared_ptr<NotConditional>;
|
||||
|
||||
NotConditional(Conditional::Pointer operand) : _operand(operand) { }
|
||||
virtual ~NotConditional() {}
|
||||
|
||||
virtual bool satisfied() override;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ class ClampFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(ClampFilter);
|
||||
public:
|
||||
ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {};
|
||||
virtual ~ClampFilter() {}
|
||||
virtual float apply(float value) const override {
|
||||
return glm::clamp(value, _min, _max);
|
||||
}
|
||||
|
|
|
@ -17,16 +17,13 @@ namespace controller {
|
|||
class ConstrainToIntegerFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(ConstrainToIntegerFilter);
|
||||
public:
|
||||
ConstrainToIntegerFilter() {};
|
||||
virtual ~ConstrainToIntegerFilter() {}
|
||||
ConstrainToIntegerFilter() = default;
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
return glm::sign(value);
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,16 +17,13 @@ namespace controller {
|
|||
class ConstrainToPositiveIntegerFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter);
|
||||
public:
|
||||
ConstrainToPositiveIntegerFilter() {};
|
||||
virtual ~ConstrainToPositiveIntegerFilter() {};
|
||||
ConstrainToPositiveIntegerFilter() = default;
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
return (value <= 0.0f) ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ class DeadZoneFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(DeadZoneFilter);
|
||||
public:
|
||||
DeadZoneFilter(float min = 0.0) : _min(min) {};
|
||||
virtual ~DeadZoneFilter() {}
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ namespace controller {
|
|||
ExponentialSmoothingFilter() {}
|
||||
ExponentialSmoothingFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
virtual ~ExponentialSmoothingFilter() {}
|
||||
|
||||
float apply(float value) const override { return value; }
|
||||
Pose apply(Pose value) const override;
|
||||
|
|
|
@ -18,7 +18,6 @@ class HysteresisFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(HysteresisFilter);
|
||||
public:
|
||||
HysteresisFilter(float min = 0.25, float max = 0.75);
|
||||
virtual ~HysteresisFilter() {}
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
|
|
@ -19,11 +19,8 @@ class InvertFilter : public ScaleFilter {
|
|||
public:
|
||||
using ScaleFilter::parseParameters;
|
||||
InvertFilter() : ScaleFilter(-1.0f) {}
|
||||
virtual ~InvertFilter() {}
|
||||
|
||||
virtual bool parseParameters(const QJsonArray& parameters) { return true; }
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,10 +17,9 @@ namespace controller {
|
|||
REGISTER_FILTER_CLASS(LowVelocityFilter);
|
||||
|
||||
public:
|
||||
LowVelocityFilter() {}
|
||||
LowVelocityFilter() = default;
|
||||
LowVelocityFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
virtual ~LowVelocityFilter() {}
|
||||
|
||||
float apply(float value) const override { return value; }
|
||||
Pose apply(Pose newPose) const override;
|
||||
|
|
|
@ -10,7 +10,6 @@ class NotFilter : public Filter {
|
|||
REGISTER_FILTER_CLASS(NotFilter);
|
||||
public:
|
||||
NotFilter();
|
||||
virtual ~NotFilter() {}
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace controller {
|
|||
class PostTransformFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(PostTransformFilter);
|
||||
public:
|
||||
PostTransformFilter() { }
|
||||
PostTransformFilter() = default;
|
||||
PostTransformFilter(glm::mat4 transform) : _transform(transform) {}
|
||||
virtual ~PostTransformFilter() {}
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.postTransform(_transform); }
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
|
||||
|
|
|
@ -18,9 +18,8 @@ namespace controller {
|
|||
class PulseFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(PulseFilter);
|
||||
public:
|
||||
PulseFilter() {}
|
||||
PulseFilter() = default;
|
||||
PulseFilter(float interval) : _interval(interval) {}
|
||||
virtual ~PulseFilter() {}
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace controller {
|
|||
class RotateFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(RotateFilter);
|
||||
public:
|
||||
RotateFilter() { }
|
||||
RotateFilter() = default;
|
||||
RotateFilter(glm::quat rotation) : _rotation(rotation) {}
|
||||
virtual ~RotateFilter() {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace controller {
|
|||
class ScaleFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(ScaleFilter);
|
||||
public:
|
||||
ScaleFilter() {}
|
||||
ScaleFilter() = default;
|
||||
ScaleFilter(float scale) : _scale(scale) {}
|
||||
virtual ~ScaleFilter() {}
|
||||
|
||||
virtual float apply(float value) const override {
|
||||
return value * _scale;
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace controller {
|
|||
class TransformFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(TransformFilter);
|
||||
public:
|
||||
TransformFilter() { }
|
||||
TransformFilter() = default;
|
||||
TransformFilter(glm::mat4 transform) : _transform(transform) {}
|
||||
virtual ~TransformFilter() {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.transform(_transform); }
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace controller {
|
|||
class TranslateFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(TranslateFilter);
|
||||
public:
|
||||
TranslateFilter() { }
|
||||
TranslateFilter() = default;
|
||||
TranslateFilter(glm::vec3 translate) : _translate(translate) {}
|
||||
virtual ~TranslateFilter() {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); }
|
||||
|
|
|
@ -24,12 +24,18 @@ bool MaterialEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP
|
|||
if (entity->getMaterialMappingPos() != _materialMappingPos || entity->getMaterialMappingScale() != _materialMappingScale || entity->getMaterialMappingRot() != _materialMappingRot) {
|
||||
return true;
|
||||
}
|
||||
if (!_texturesLoaded) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||
withWriteLock([&] {
|
||||
_drawMaterial = entity->getMaterial();
|
||||
if (_drawMaterial != entity->getMaterial()) {
|
||||
_texturesLoaded = false;
|
||||
_drawMaterial = entity->getMaterial();
|
||||
}
|
||||
_parentID = entity->getParentID();
|
||||
_materialMappingPos = entity->getMaterialMappingPos();
|
||||
_materialMappingScale = entity->getMaterialMappingScale();
|
||||
|
@ -38,6 +44,12 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
const float MATERIAL_ENTITY_SCALE = 0.5f;
|
||||
_renderTransform.postScale(MATERIAL_ENTITY_SCALE);
|
||||
_renderTransform.postScale(ENTITY_ITEM_DEFAULT_DIMENSIONS);
|
||||
|
||||
bool newTexturesLoaded = _drawMaterial ? !_drawMaterial->isMissingTexture() : false;
|
||||
if (!_texturesLoaded && newTexturesLoaded) {
|
||||
_drawMaterial->checkResetOpacityMap();
|
||||
}
|
||||
_texturesLoaded = newTexturesLoaded;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ private:
|
|||
glm::vec2 _materialMappingPos;
|
||||
glm::vec2 _materialMappingScale;
|
||||
float _materialMappingRot;
|
||||
bool _texturesLoaded { false };
|
||||
|
||||
std::shared_ptr<NetworkMaterial> _drawMaterial;
|
||||
};
|
||||
|
|
|
@ -975,14 +975,21 @@ QStringList RenderableModelEntityItem::getJointNames() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
// FIXME: deprecated; remove >= RC67
|
||||
bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) {
|
||||
auto model = getModel();
|
||||
if (!model || !model->isLoaded()) {
|
||||
return false;
|
||||
void RenderableModelEntityItem::setAnimationURL(const QString& url) {
|
||||
QString oldURL = getAnimationURL();
|
||||
ModelEntityItem::setAnimationURL(url);
|
||||
if (oldURL != getAnimationURL()) {
|
||||
_needsAnimationReset = true;
|
||||
}
|
||||
BLOCKING_INVOKE_METHOD(model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result));
|
||||
return !result.isEmpty();
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::needsAnimationReset() const {
|
||||
return _needsAnimationReset;
|
||||
}
|
||||
|
||||
QString RenderableModelEntityItem::getAnimationURLAndReset() {
|
||||
_needsAnimationReset = false;
|
||||
return getAnimationURL();
|
||||
}
|
||||
|
||||
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() {
|
||||
|
@ -1068,6 +1075,13 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
|||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::readyToAnimate() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
float firstFrame = _animationProperties.getFirstFrame();
|
||||
return (firstFrame >= 0.0f) && (firstFrame <= _animationProperties.getLastFrame());
|
||||
});
|
||||
}
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
|
@ -1155,7 +1169,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
|
||||
const QVector<glm::quat>& rotations = frames[_lastKnownCurrentFrame].rotations;
|
||||
const QVector<glm::vec3>& translations = frames[_lastKnownCurrentFrame].translations;
|
||||
|
||||
|
||||
jointsData.resize(_jointMapping.size());
|
||||
for (int j = 0; j < _jointMapping.size(); j++) {
|
||||
int index = _jointMapping[j];
|
||||
|
@ -1169,13 +1183,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
|||
}
|
||||
} else if (index < animationJointNames.size()) {
|
||||
QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation
|
||||
|
||||
if (originalFbxIndices.contains(jointName)) {
|
||||
// Making sure the joint names exist in the original model the animation is trying to apply onto. If they do, then remap and get it's translation.
|
||||
int remappedIndex = originalFbxIndices[jointName] - 1; // JointIndeces seem to always start from 1 and the found index is always 1 higher than actual.
|
||||
translationMat = glm::translate(originalFbxJoints[remappedIndex].translation);
|
||||
}
|
||||
}
|
||||
}
|
||||
glm::mat4 rotationMat;
|
||||
if (index < rotations.size()) {
|
||||
rotationMat = glm::mat4_cast(fbxJoints[index].preRotation * rotations[index] * fbxJoints[index].postRotation);
|
||||
|
@ -1477,14 +1490,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
if (_animating) {
|
||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||
|
||||
if (!jointsMapped()) {
|
||||
mapJoints(entity, model->getJointNames());
|
||||
//else the joint have been mapped before but we have a new animation to load
|
||||
} else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) {
|
||||
if (_animation && entity->needsAnimationReset()) {
|
||||
//(_animation->getURL().toString() != entity->getAnimationURL())) { // bad check
|
||||
// the joints have been mapped before but we have a new animation to load
|
||||
_animation.reset();
|
||||
_jointMappingCompleted = false;
|
||||
mapJoints(entity, model->getJointNames());
|
||||
}
|
||||
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
|
||||
|
||||
if (!_jointMappingCompleted) {
|
||||
mapJoints(entity, model);
|
||||
}
|
||||
if (entity->readyToAnimate()) {
|
||||
animate(entity);
|
||||
}
|
||||
emit requestRenderUpdate();
|
||||
|
@ -1518,19 +1534,20 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) {
|
||||
void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const ModelPointer& model) {
|
||||
// if we don't have animation, or we're already joint mapped then bail early
|
||||
if (!entity->hasAnimation() || jointsMapped()) {
|
||||
if (!entity->hasAnimation()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_animation || _animation->getURL().toString() != entity->getAnimationURL()) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURL());
|
||||
if (!_animation) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURLAndReset());
|
||||
}
|
||||
|
||||
if (_animation && _animation->isLoaded()) {
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
auto modelJointNames = model->getJointNames();
|
||||
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
|
||||
_jointMapping.resize(modelJointNames.size());
|
||||
for (int i = 0; i < modelJointNames.size(); i++) {
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
|
||||
|
||||
class Model;
|
||||
class EntityTreeRenderer;
|
||||
|
||||
|
@ -114,21 +112,25 @@ public:
|
|||
virtual int getJointIndex(const QString& name) const override;
|
||||
virtual QStringList getJointNames() const override;
|
||||
|
||||
bool getMeshes(MeshProxyList& result) override; // deprecated
|
||||
void setAnimationURL(const QString& url) override;
|
||||
bool needsAnimationReset() const;
|
||||
QString getAnimationURLAndReset();
|
||||
|
||||
private:
|
||||
bool needsUpdateModelBounds() const;
|
||||
void autoResizeJointArrays();
|
||||
void copyAnimationJointDataToModel();
|
||||
|
||||
bool readyToAnimate() const;
|
||||
void getCollisionGeometryResource();
|
||||
|
||||
GeometryResource::Pointer _compoundShapeResource;
|
||||
bool _jointMapCompleted { false };
|
||||
bool _originalTexturesRead { false };
|
||||
std::vector<int> _jointMap;
|
||||
QVariantMap _originalTextures;
|
||||
bool _jointMapCompleted { false };
|
||||
bool _originalTexturesRead { false };
|
||||
bool _dimensionsInitialized { true };
|
||||
bool _needsJointSimulation { false };
|
||||
bool _needsAnimationReset { false };
|
||||
};
|
||||
|
||||
namespace render { namespace entities {
|
||||
|
@ -169,8 +171,7 @@ protected:
|
|||
|
||||
private:
|
||||
void animate(const TypedEntityPointer& entity);
|
||||
void mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames);
|
||||
bool jointsMapped() const { return _jointMappingCompleted; }
|
||||
void mapJoints(const TypedEntityPointer& entity, const ModelPointer& model);
|
||||
|
||||
// Transparency is handled in ModelMeshPartPayload
|
||||
virtual bool isTransparent() const override { return false; }
|
||||
|
|
|
@ -673,7 +673,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||
|
||||
// pack SimulationOwner and terse update properties near each other
|
||||
// pack SimulationOwner, transform, and velocity properties near each other
|
||||
// NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data
|
||||
// even when we would otherwise ignore the rest of the packet.
|
||||
|
||||
|
@ -1358,8 +1358,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire
|
|||
return properties;
|
||||
}
|
||||
|
||||
void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const {
|
||||
// a TerseUpdate includes the transform and its derivatives
|
||||
void EntityItem::getTransformAndVelocityProperties(EntityItemProperties& properties) const {
|
||||
if (!properties._positionChanged) {
|
||||
properties._position = getLocalPosition();
|
||||
}
|
||||
|
@ -1383,8 +1382,11 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c
|
|||
properties._accelerationChanged = true;
|
||||
}
|
||||
|
||||
void EntityItem::setScriptSimulationPriority(uint8_t priority) {
|
||||
uint8_t newPriority = stillHasGrabActions() ? glm::max(priority, SCRIPT_GRAB_SIMULATION_PRIORITY) : priority;
|
||||
void EntityItem::upgradeScriptSimulationPriority(uint8_t priority) {
|
||||
uint8_t newPriority = glm::max(priority, _scriptSimulationPriority);
|
||||
if (newPriority < SCRIPT_GRAB_SIMULATION_PRIORITY && stillHasGrabActions()) {
|
||||
newPriority = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
}
|
||||
if (newPriority != _scriptSimulationPriority) {
|
||||
// set the dirty flag to trigger a bid or ownership update
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||
|
@ -1419,7 +1421,7 @@ bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const {
|
|||
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
// these affect TerseUpdate properties
|
||||
// these affect transform and velocity properties
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
|
||||
|
@ -3256,3 +3258,26 @@ void EntityItem::setScriptHasFinishedPreload(bool value) {
|
|||
bool EntityItem::isScriptPreloadFinished() {
|
||||
return _scriptPreloadFinished;
|
||||
}
|
||||
|
||||
void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority) {
|
||||
if (dynamicDataNeedsTransmit()) {
|
||||
setDynamicDataNeedsTransmit(false);
|
||||
properties.setActionData(getDynamicData());
|
||||
}
|
||||
|
||||
if (updateQueryAACube()) {
|
||||
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
||||
properties.setQueryAACube(getQueryAACube());
|
||||
}
|
||||
|
||||
// set the LastEdited of the properties but NOT the entity itself
|
||||
properties.setLastEdited(now);
|
||||
|
||||
clearScriptSimulationPriority();
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), priority);
|
||||
setPendingOwnershipPriority(priority);
|
||||
|
||||
properties.setClientOnly(getClientOnly());
|
||||
properties.setOwningAvatarID(getOwningAvatarID());
|
||||
setLastBroadcast(now); // for debug/physics status icons
|
||||
}
|
||||
|
|
|
@ -325,7 +325,7 @@ public:
|
|||
// TODO: move this "ScriptSimulationPriority" and "PendingOwnership" stuff into EntityMotionState
|
||||
// but first would need to do some other cleanup. In the meantime these live here as "scratch space"
|
||||
// to allow libs that don't know about each other to communicate.
|
||||
void setScriptSimulationPriority(uint8_t priority);
|
||||
void upgradeScriptSimulationPriority(uint8_t priority);
|
||||
void clearScriptSimulationPriority();
|
||||
uint8_t getScriptSimulationPriority() const { return _scriptSimulationPriority; }
|
||||
void setPendingOwnershipPriority(uint8_t priority);
|
||||
|
@ -420,7 +420,7 @@ public:
|
|||
quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; }
|
||||
void updateLastEditedFromRemote() { _lastEditedFromRemote = usecTimestampNow(); }
|
||||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
void getTransformAndVelocityProperties(EntityItemProperties& properties) const;
|
||||
|
||||
void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
|
@ -535,6 +535,8 @@ public:
|
|||
|
||||
const GrabPropertyGroup& getGrabProperties() const { return _grabProperties; }
|
||||
|
||||
void prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority);
|
||||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
void spaceUpdate(std::pair<int32_t, glm::vec4> data);
|
||||
|
|
|
@ -2387,8 +2387,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Light) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_INTENSITY, properties.getIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, properties.getFalloffRadius());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, properties.getExponent());
|
||||
|
@ -3244,9 +3244,12 @@ AABox EntityItemProperties::getAABox() const {
|
|||
return AABox(rotatedExtentsRelativeToRegistrationPoint);
|
||||
}
|
||||
|
||||
bool EntityItemProperties::hasTerseUpdateChanges() const {
|
||||
// a TerseUpdate includes the transform and its derivatives
|
||||
return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged;
|
||||
bool EntityItemProperties::hasTransformOrVelocityChanges() const {
|
||||
return _positionChanged ||_localPositionChanged
|
||||
|| _rotationChanged || _localRotationChanged
|
||||
|| _velocityChanged || _localVelocityChanged
|
||||
|| _angularVelocityChanged || _localAngularVelocityChanged
|
||||
|| _accelerationChanged;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::hasMiscPhysicsChanges() const {
|
||||
|
@ -3255,6 +3258,56 @@ bool EntityItemProperties::hasMiscPhysicsChanges() const {
|
|||
_compoundShapeURLChanged || _dynamicChanged || _collisionlessChanged || _collisionMaskChanged;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::hasSimulationRestrictedChanges() const {
|
||||
return _positionChanged || _localPositionChanged
|
||||
|| _rotationChanged || _localRotationChanged
|
||||
|| _velocityChanged || _localVelocityChanged
|
||||
|| _angularVelocityChanged || _localAngularVelocityChanged
|
||||
|| _accelerationChanged
|
||||
|| _parentIDChanged || _parentJointIndexChanged;
|
||||
}
|
||||
|
||||
void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPointer& entity) {
|
||||
if (!_parentIDChanged) {
|
||||
setParentID(entity->getParentID());
|
||||
}
|
||||
if (!_parentJointIndexChanged) {
|
||||
setParentJointIndex(entity->getParentJointIndex());
|
||||
}
|
||||
if (!_localPositionChanged && !_positionChanged) {
|
||||
setPosition(entity->getWorldPosition());
|
||||
}
|
||||
if (!_localRotationChanged && !_rotationChanged) {
|
||||
setRotation(entity->getWorldOrientation());
|
||||
}
|
||||
if (!_localVelocityChanged && !_velocityChanged) {
|
||||
setVelocity(entity->getWorldVelocity());
|
||||
}
|
||||
if (!_localAngularVelocityChanged && !_angularVelocityChanged) {
|
||||
setAngularVelocity(entity->getWorldAngularVelocity());
|
||||
}
|
||||
if (!_accelerationChanged) {
|
||||
setAcceleration(entity->getAcceleration());
|
||||
}
|
||||
if (!_localDimensionsChanged && !_dimensionsChanged) {
|
||||
setDimensions(entity->getScaledDimensions());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::clearSimulationRestrictedProperties() {
|
||||
_positionChanged = false;
|
||||
_localPositionChanged = false;
|
||||
_rotationChanged = false;
|
||||
_localRotationChanged = false;
|
||||
_velocityChanged = false;
|
||||
_localVelocityChanged = false;
|
||||
_angularVelocityChanged = false;
|
||||
_localAngularVelocityChanged = false;
|
||||
_accelerationChanged = false;
|
||||
_parentIDChanged = false;
|
||||
_parentJointIndexChanged = false;
|
||||
}
|
||||
|
||||
void EntityItemProperties::clearSimulationOwner() {
|
||||
_simulationOwner.clear();
|
||||
_simulationOwnerChanged = true;
|
||||
|
@ -3273,6 +3326,20 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t EntityItemProperties::computeSimulationBidPriority() const {
|
||||
uint8_t priority = 0;
|
||||
if (_parentIDChanged || _parentJointIndexChanged) {
|
||||
// we need higher simulation ownership priority to chang parenting info
|
||||
priority = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
} else if ( _positionChanged || _localPositionChanged
|
||||
|| _rotationChanged || _localRotationChanged
|
||||
|| _velocityChanged || _localVelocityChanged
|
||||
|| _angularVelocityChanged || _localAngularVelocityChanged) {
|
||||
priority = SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
QList<QString> EntityItemProperties::listChangedProperties() {
|
||||
QList<QString> out;
|
||||
if (containsPositionChange()) {
|
||||
|
|
|
@ -349,13 +349,18 @@ public:
|
|||
|
||||
void setCreated(QDateTime& v);
|
||||
|
||||
bool hasTerseUpdateChanges() const;
|
||||
bool hasTransformOrVelocityChanges() const;
|
||||
bool hasMiscPhysicsChanges() const;
|
||||
|
||||
bool hasSimulationRestrictedChanges() const;
|
||||
void copySimulationRestrictedProperties(const EntityItemPointer& entity);
|
||||
void clearSimulationRestrictedProperties();
|
||||
|
||||
void clearSimulationOwner();
|
||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||
void setSimulationOwner(const QByteArray& data);
|
||||
void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); }
|
||||
uint8_t computeSimulationBidPriority() const;
|
||||
|
||||
void setActionDataDirty() { _actionDataChanged = true; }
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@ EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperti
|
|||
}
|
||||
|
||||
|
||||
// TODO: this method looks expensive and should take properties by reference, update it, and return void
|
||||
EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties,
|
||||
bool scalesWithParent) {
|
||||
// convert position and rotation properties from world-space to local, unless localPosition and localRotation
|
||||
|
@ -242,13 +243,12 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
_activityTracking.addedEntityCount++;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto sessionID = nodeList->getSessionUUID();
|
||||
const auto sessionID = nodeList->getSessionUUID();
|
||||
|
||||
EntityItemProperties propertiesWithSimID = properties;
|
||||
if (clientOnly) {
|
||||
const QUuid myNodeID = sessionID;
|
||||
propertiesWithSimID.setClientOnly(clientOnly);
|
||||
propertiesWithSimID.setOwningAvatarID(myNodeID);
|
||||
propertiesWithSimID.setOwningAvatarID(sessionID);
|
||||
}
|
||||
|
||||
propertiesWithSimID.setLastEditedBy(sessionID);
|
||||
|
@ -290,7 +290,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti
|
|||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// since we're creating this object we will immediately volunteer to own its simulation
|
||||
entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
properties.setLastEdited(entity->getLastEdited());
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
|
@ -530,54 +530,86 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
|
||||
_activityTracking.editedEntityCount++;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto sessionID = nodeList->getSessionUUID();
|
||||
const auto sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
|
||||
|
||||
EntityItemProperties properties = scriptSideProperties;
|
||||
properties.setLastEditedBy(sessionID);
|
||||
|
||||
EntityItemID entityID(id);
|
||||
if (!_entityTree) {
|
||||
properties.setLastEditedBy(sessionID);
|
||||
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
||||
return id;
|
||||
}
|
||||
// If we have a local entity tree set, then also update it.
|
||||
|
||||
bool updatedEntity = false;
|
||||
_entityTree->withWriteLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
EntityItemPointer entity(nullptr);
|
||||
SimulationOwner simulationOwner;
|
||||
_entityTree->withReadLock([&] {
|
||||
// make a copy of entity for local logic outside of tree lock
|
||||
entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) {
|
||||
if (entity->getClientOnly() && entity->getOwningAvatarID() != sessionID) {
|
||||
// don't edit other avatar's avatarEntities
|
||||
properties = EntityItemProperties();
|
||||
return;
|
||||
}
|
||||
// make a copy of simulationOwner for local logic outside of tree lock
|
||||
simulationOwner = entity->getSimulationOwner();
|
||||
});
|
||||
|
||||
if (scriptSideProperties.parentRelatedPropertyChanged()) {
|
||||
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
|
||||
// If any of these changed, pull any missing properties from the entity.
|
||||
if (entity) {
|
||||
if (properties.hasSimulationRestrictedChanges()) {
|
||||
if (_bidOnSimulationOwnership) {
|
||||
// flag for simulation ownership, or upgrade existing ownership priority
|
||||
// (actual bids for simulation ownership are sent by the PhysicalEntitySimulation)
|
||||
entity->upgradeScriptSimulationPriority(properties.computeSimulationBidPriority());
|
||||
if (simulationOwner.getID() == sessionID) {
|
||||
// we own the simulation --> copy ALL restricted properties
|
||||
properties.copySimulationRestrictedProperties(entity);
|
||||
} else {
|
||||
// we don't own the simulation but think we would like to
|
||||
|
||||
if (!scriptSideProperties.parentIDChanged()) {
|
||||
properties.setParentID(entity->getParentID());
|
||||
}
|
||||
if (!scriptSideProperties.parentJointIndexChanged()) {
|
||||
properties.setParentJointIndex(entity->getParentJointIndex());
|
||||
}
|
||||
if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) {
|
||||
properties.setPosition(entity->getWorldPosition());
|
||||
}
|
||||
if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) {
|
||||
properties.setRotation(entity->getWorldOrientation());
|
||||
}
|
||||
if (!scriptSideProperties.localDimensionsChanged() && !scriptSideProperties.dimensionsChanged()) {
|
||||
properties.setDimensions(entity->getScaledDimensions());
|
||||
uint8_t desiredPriority = entity->getScriptSimulationPriority();
|
||||
if (desiredPriority < simulationOwner.getPriority()) {
|
||||
// the priority at which we'd like to own it is not high enough
|
||||
// --> assume failure and clear all restricted property changes
|
||||
properties.clearSimulationRestrictedProperties();
|
||||
} else {
|
||||
// the priority at which we'd like to own it is high enough to win.
|
||||
// --> assume success and copy ALL restricted properties
|
||||
properties.copySimulationRestrictedProperties(entity);
|
||||
}
|
||||
}
|
||||
} else if (!simulationOwner.getID().isNull()) {
|
||||
// someone owns this but not us
|
||||
// clear restricted properties
|
||||
properties.clearSimulationRestrictedProperties();
|
||||
}
|
||||
// clear the cached simulationPriority level
|
||||
entity->upgradeScriptSimulationPriority(0);
|
||||
}
|
||||
|
||||
// set these to make EntityItemProperties::getScalesWithParent() work correctly
|
||||
properties.setClientOnly(entity->getClientOnly());
|
||||
properties.setOwningAvatarID(entity->getOwningAvatarID());
|
||||
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
|
||||
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
properties.setType(entity->getType());
|
||||
} else if (_bidOnSimulationOwnership) {
|
||||
// bail when simulation participants don't know about entity
|
||||
return QUuid();
|
||||
}
|
||||
// TODO: it is possible there is no remaining useful changes in properties and we should bail early.
|
||||
// How to check for this cheaply?
|
||||
|
||||
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
|
||||
properties.setLastEditedBy(sessionID);
|
||||
|
||||
// done reading and modifying properties --> start write
|
||||
bool updatedEntity = false;
|
||||
_entityTree->withWriteLock([&] {
|
||||
updatedEntity = _entityTree->updateEntity(entityID, properties);
|
||||
});
|
||||
|
||||
|
@ -590,63 +622,37 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
// return QUuid();
|
||||
// }
|
||||
|
||||
bool entityFound { false };
|
||||
bool hasQueryAACubeRelatedChanges = properties.queryAACubeRelatedPropertyChanged();
|
||||
// done writing, send update
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
// find the entity again: maybe it was removed since we last found it
|
||||
entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
entityFound = true;
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
properties.setType(entity->getType());
|
||||
bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
|
||||
bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;
|
||||
if (_bidOnSimulationOwnership && hasPhysicsChanges) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
uint64_t now = usecTimestampNow();
|
||||
entity->setLastBroadcast(now);
|
||||
|
||||
if (entity->getSimulatorID() == myNodeID) {
|
||||
// we think we already own the simulation, so make sure to send ALL TerseUpdate properties
|
||||
if (hasTerseUpdateChanges) {
|
||||
entity->getAllTerseUpdateProperties(properties);
|
||||
}
|
||||
// TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object
|
||||
// is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update
|
||||
// and instead let the physics simulation decide when to send a terse update. This would remove
|
||||
// the "slide-no-rotate" glitch (and typical double-update) that we see during the "poke rolling
|
||||
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
|
||||
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
|
||||
// simulation.
|
||||
|
||||
if (entity->getSimulationPriority() < SCRIPT_POKE_SIMULATION_PRIORITY) {
|
||||
// we re-assert our simulation ownership at a higher priority
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
} else {
|
||||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
if (properties.queryAACubeRelatedPropertyChanged()) {
|
||||
if (hasQueryAACubeRelatedChanges) {
|
||||
properties.setQueryAACube(entity->getQueryAACube());
|
||||
}
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
properties.setLastEdited(entity->getLastEdited());
|
||||
|
||||
// if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
|
||||
// if they've changed.
|
||||
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
||||
if (descendant->getNestableType() == NestableType::Entity) {
|
||||
if (descendant->updateQueryAACube()) {
|
||||
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
|
||||
EntityItemProperties newQueryCubeProperties;
|
||||
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
|
||||
newQueryCubeProperties.setLastEdited(properties.getLastEdited());
|
||||
queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties);
|
||||
entityDescendant->setLastBroadcast(usecTimestampNow());
|
||||
// if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
|
||||
// if they've changed.
|
||||
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
|
||||
if (descendant->getNestableType() == NestableType::Entity) {
|
||||
if (descendant->updateQueryAACube()) {
|
||||
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
|
||||
EntityItemProperties newQueryCubeProperties;
|
||||
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
|
||||
newQueryCubeProperties.setLastEdited(properties.getLastEdited());
|
||||
queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties);
|
||||
entityDescendant->setLastBroadcast(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!entity) {
|
||||
if (hasQueryAACubeRelatedChanges) {
|
||||
// Sometimes ESS don't have the entity they are trying to edit in their local tree. In this case,
|
||||
// convertPropertiesFromScriptSemantics doesn't get called and local* edits will get dropped.
|
||||
// This is because, on the script side, "position" is in world frame, but in the network
|
||||
|
@ -668,8 +674,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
properties.setDimensions(properties.getLocalDimensions());
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!entityFound) {
|
||||
// we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity,
|
||||
// print a warning and don't send an edit packet to the entity-server.
|
||||
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
|
||||
|
@ -1449,7 +1453,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
}
|
||||
action->setIsMine(true);
|
||||
success = entity->addAction(simulation, action);
|
||||
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
if (success) {
|
||||
|
@ -1465,7 +1469,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||
}
|
||||
return success;
|
||||
});
|
||||
|
@ -1479,7 +1483,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
|||
success = entity->removeAction(simulation, actionID);
|
||||
if (success) {
|
||||
// reduce from grab to poke
|
||||
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->upgradeScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
|
|
|
@ -629,30 +629,6 @@ bool ModelEntityItem::getAnimationHold() const {
|
|||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationFirstFrame(float firstFrame) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setFirstFrame(firstFrame);
|
||||
});
|
||||
}
|
||||
|
||||
float ModelEntityItem::getAnimationFirstFrame() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _animationProperties.getFirstFrame();
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setAnimationLastFrame(float lastFrame) {
|
||||
withWriteLock([&] {
|
||||
_animationProperties.setLastFrame(lastFrame);
|
||||
});
|
||||
}
|
||||
|
||||
float ModelEntityItem::getAnimationLastFrame() const {
|
||||
return resultWithReadLock<float>([&] {
|
||||
return _animationProperties.getLastFrame();
|
||||
});
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getAnimationIsPlaying() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _animationProperties.getRunning();
|
||||
|
|
|
@ -79,9 +79,10 @@ public:
|
|||
// Animation related items...
|
||||
AnimationPropertyGroup getAnimationProperties() const;
|
||||
|
||||
// TODO: audit and remove unused Animation accessors
|
||||
bool hasAnimation() const;
|
||||
QString getAnimationURL() const;
|
||||
void setAnimationURL(const QString& url);
|
||||
virtual void setAnimationURL(const QString& url);
|
||||
|
||||
void setAnimationCurrentFrame(float value);
|
||||
void setAnimationIsPlaying(bool value);
|
||||
|
@ -99,12 +100,6 @@ public:
|
|||
void setRelayParentJoints(bool relayJoints);
|
||||
bool getRelayParentJoints() const;
|
||||
|
||||
void setAnimationFirstFrame(float firstFrame);
|
||||
float getAnimationFirstFrame() const;
|
||||
|
||||
void setAnimationLastFrame(float lastFrame);
|
||||
float getAnimationLastFrame() const;
|
||||
|
||||
bool getAnimationIsPlaying() const;
|
||||
float getAnimationCurrentFrame() const;
|
||||
float getAnimationFPS() const;
|
||||
|
|
|
@ -19,7 +19,11 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
|
|||
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
|
||||
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail)
|
||||
|
||||
#if defined(__clang__)
|
||||
#define BUFFER_OFFSET(bytes) (reinterpret_cast<GLvoid*>(bytes))
|
||||
#else
|
||||
#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes))
|
||||
#endif
|
||||
|
||||
namespace gpu { namespace gl {
|
||||
|
||||
|
|
|
@ -25,8 +25,10 @@ struct GLFilterMode {
|
|||
|
||||
class GLTextureTransferEngine {
|
||||
public:
|
||||
virtual ~GLTextureTransferEngine() {}
|
||||
using Pointer = std::shared_ptr<GLTextureTransferEngine>;
|
||||
|
||||
virtual ~GLTextureTransferEngine() = default;
|
||||
|
||||
/// Called once per frame to perform any require memory management or transfer work
|
||||
virtual void manageMemory() = 0;
|
||||
virtual void shutdown() = 0;
|
||||
|
|
|
@ -118,11 +118,13 @@ public:
|
|||
uint8 _function = LESS;
|
||||
uint8 _writeMask = true;
|
||||
uint8 _enabled = false;
|
||||
uint8 _spare = 0; // _spare is here to to affect alignment
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
uint8 _spare = 0; // Padding
|
||||
public:
|
||||
DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) :
|
||||
_function(func), _writeMask(writeMask), _enabled(enabled) {
|
||||
(void)_spare; // to avoid unusued variable warning
|
||||
}
|
||||
|
||||
bool isEnabled() const { return _enabled != 0; }
|
||||
|
|
|
@ -320,7 +320,10 @@ public:
|
|||
|
||||
float _scattering{ 0.0f }; // Scattering info
|
||||
|
||||
glm::vec2 _spare{ 0.0f };
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
glm::vec2 _spare{ 0.0f }; // Padding
|
||||
|
||||
uint32_t _key{ 0 }; // a copy of the materialKey
|
||||
|
||||
|
|
|
@ -96,7 +96,6 @@ protected:
|
|||
class InputDevice : public controller::InputDevice {
|
||||
public:
|
||||
InputDevice() : controller::InputDevice("Keyboard") {}
|
||||
virtual ~InputDevice() {}
|
||||
private:
|
||||
// Device functions
|
||||
virtual controller::Input::NamedVector getAvailableInputs() const override;
|
||||
|
|
|
@ -414,33 +414,13 @@ bool Geometry::areTexturesLoaded() const {
|
|||
if (!_areTexturesLoaded) {
|
||||
for (auto& material : _materials) {
|
||||
// Check if material textures are loaded
|
||||
bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(),
|
||||
[](const NetworkMaterial::Textures::value_type& it) {
|
||||
auto texture = it.texture;
|
||||
if (!texture) {
|
||||
return false;
|
||||
}
|
||||
// Failed texture downloads need to be considered as 'loaded'
|
||||
// or the object will never fade in
|
||||
bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
|
||||
if (!finished) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
bool materialMissingTexture = material->isMissingTexture();
|
||||
|
||||
if (materialMissingTexture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If material textures are loaded, check the material translucency
|
||||
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
|
||||
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
|
||||
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
|
||||
const auto albedoTexture = material->_textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
||||
if (albedoTexture.texture) {
|
||||
material->resetOpacityMap();
|
||||
}
|
||||
material->checkResetOpacityMap();
|
||||
}
|
||||
|
||||
_areTexturesLoaded = true;
|
||||
|
@ -783,4 +763,31 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) {
|
|||
}
|
||||
}
|
||||
|
||||
bool NetworkMaterial::isMissingTexture() {
|
||||
for (auto& networkTexture : _textures) {
|
||||
auto& texture = networkTexture.texture;
|
||||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
// Failed texture downloads need to be considered as 'loaded'
|
||||
// or the object will never fade in
|
||||
bool finished = texture->isFailed() || (texture->isLoaded() && texture->getGPUTexture() && texture->getGPUTexture()->isDefined());
|
||||
if (!finished) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetworkMaterial::checkResetOpacityMap() {
|
||||
// If material textures are loaded, check the material translucency
|
||||
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
|
||||
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
|
||||
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
|
||||
const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
|
||||
if (albedoTexture.texture) {
|
||||
resetOpacityMap();
|
||||
}
|
||||
}
|
||||
|
||||
#include "ModelCache.moc"
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
|
||||
Geometry() = default;
|
||||
Geometry(const Geometry& geometry);
|
||||
virtual ~Geometry() {}
|
||||
virtual ~Geometry() = default;
|
||||
|
||||
// Immutable over lifetime
|
||||
using GeometryMeshes = std::vector<std::shared_ptr<const graphics::Mesh>>;
|
||||
|
@ -177,6 +177,9 @@ public:
|
|||
void setScatteringMap(const QUrl& url);
|
||||
void setLightmapMap(const QUrl& url);
|
||||
|
||||
bool isMissingTexture();
|
||||
void checkResetOpacityMap();
|
||||
|
||||
protected:
|
||||
friend class Geometry;
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
BandwidthRecorder::Channel::Channel() {
|
||||
}
|
||||
|
||||
float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() {
|
||||
float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() const {
|
||||
float averageTimeBetweenPackets = _input.getEventDeltaAverage();
|
||||
if (averageTimeBetweenPackets > 0.0f) {
|
||||
return (1.0f / averageTimeBetweenPackets);
|
||||
|
@ -26,7 +26,7 @@ float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() {
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() {
|
||||
float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() const {
|
||||
float averageTimeBetweenPackets = _output.getEventDeltaAverage();
|
||||
if (averageTimeBetweenPackets > 0.0f) {
|
||||
return (1.0f / averageTimeBetweenPackets);
|
||||
|
@ -34,11 +34,11 @@ float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() {
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() {
|
||||
float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() const {
|
||||
return (_input.getAverageSampleValuePerSecond() * (8.0f / 1000));
|
||||
}
|
||||
|
||||
float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() {
|
||||
float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() const {
|
||||
return (_output.getAverageSampleValuePerSecond() * (8.0f / 1000));
|
||||
}
|
||||
|
||||
|
@ -77,35 +77,35 @@ void BandwidthRecorder::updateOutboundData(const quint8 channelType, const int s
|
|||
_channels[channelType]->updateOutputAverage(sample);
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) {
|
||||
float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) const {
|
||||
if (! _channels[channelType]) {
|
||||
return 0.0f;
|
||||
}
|
||||
return _channels[channelType]->getAverageInputPacketsPerSecond();
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) {
|
||||
float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) const {
|
||||
if (! _channels[channelType]) {
|
||||
return 0.0f;
|
||||
}
|
||||
return _channels[channelType]->getAverageOutputPacketsPerSecond();
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) {
|
||||
float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) const {
|
||||
if (! _channels[channelType]) {
|
||||
return 0.0f;
|
||||
}
|
||||
return _channels[channelType]->getAverageInputKilobitsPerSecond();
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) {
|
||||
float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) const {
|
||||
if (! _channels[channelType]) {
|
||||
return 0.0f;
|
||||
}
|
||||
return _channels[channelType]->getAverageOutputKilobitsPerSecond();
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() {
|
||||
float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() const {
|
||||
float result = 0.0f;
|
||||
for (uint i=0; i<CHANNEL_COUNT; i++) {
|
||||
if (_channels[i]) {
|
||||
|
@ -115,7 +115,7 @@ float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() {
|
|||
return result;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() {
|
||||
float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() const {
|
||||
float result = 0.0f;
|
||||
for (uint i=0; i<CHANNEL_COUNT; i++) {
|
||||
if (_channels[i]) {
|
||||
|
@ -125,7 +125,7 @@ float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() {
|
|||
return result;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond(){
|
||||
float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond() const {
|
||||
float result = 0.0f;
|
||||
for (uint i=0; i<CHANNEL_COUNT; i++) {
|
||||
if (_channels[i]) {
|
||||
|
@ -135,7 +135,7 @@ float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond(){
|
|||
return result;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond(){
|
||||
float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond() const {
|
||||
float result = 0.0f;
|
||||
for (uint i=0; i<CHANNEL_COUNT; i++) {
|
||||
if (_channels[i]) {
|
||||
|
@ -145,7 +145,7 @@ float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond(){
|
|||
return result;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getCachedTotalAverageInputPacketsPerSecond() {
|
||||
float BandwidthRecorder::getCachedTotalAverageInputPacketsPerSecond() const {
|
||||
static qint64 lastCalculated = 0;
|
||||
static float cachedValue = 0.0f;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
@ -156,7 +156,7 @@ float BandwidthRecorder::getCachedTotalAverageInputPacketsPerSecond() {
|
|||
return cachedValue;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getCachedTotalAverageOutputPacketsPerSecond() {
|
||||
float BandwidthRecorder::getCachedTotalAverageOutputPacketsPerSecond() const {
|
||||
static qint64 lastCalculated = 0;
|
||||
static float cachedValue = 0.0f;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
@ -167,7 +167,7 @@ float BandwidthRecorder::getCachedTotalAverageOutputPacketsPerSecond() {
|
|||
return cachedValue;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getCachedTotalAverageInputKilobitsPerSecond() {
|
||||
float BandwidthRecorder::getCachedTotalAverageInputKilobitsPerSecond() const {
|
||||
static qint64 lastCalculated = 0;
|
||||
static float cachedValue = 0.0f;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
@ -178,7 +178,7 @@ float BandwidthRecorder::getCachedTotalAverageInputKilobitsPerSecond() {
|
|||
return cachedValue;
|
||||
}
|
||||
|
||||
float BandwidthRecorder::getCachedTotalAverageOutputKilobitsPerSecond() {
|
||||
float BandwidthRecorder::getCachedTotalAverageOutputKilobitsPerSecond() const {
|
||||
static qint64 lastCalculated = 0;
|
||||
static float cachedValue = 0.0f;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
|
|
@ -32,33 +32,33 @@ public:
|
|||
class Channel {
|
||||
public:
|
||||
Channel();
|
||||
float getAverageInputPacketsPerSecond();
|
||||
float getAverageOutputPacketsPerSecond();
|
||||
float getAverageInputKilobitsPerSecond();
|
||||
float getAverageOutputKilobitsPerSecond();
|
||||
float getAverageInputPacketsPerSecond() const;
|
||||
float getAverageOutputPacketsPerSecond() const;
|
||||
float getAverageInputKilobitsPerSecond() const;
|
||||
float getAverageOutputKilobitsPerSecond() const;
|
||||
|
||||
void updateInputAverage(const float sample);
|
||||
void updateOutputAverage(const float sample);
|
||||
|
||||
private:
|
||||
SimpleMovingAverage _input = SimpleMovingAverage();
|
||||
SimpleMovingAverage _output = SimpleMovingAverage();
|
||||
SimpleMovingAverage _input;
|
||||
SimpleMovingAverage _output;
|
||||
};
|
||||
|
||||
float getAverageInputPacketsPerSecond(const quint8 channelType);
|
||||
float getAverageOutputPacketsPerSecond(const quint8 channelType);
|
||||
float getAverageInputKilobitsPerSecond(const quint8 channelType);
|
||||
float getAverageOutputKilobitsPerSecond(const quint8 channelType);
|
||||
float getAverageInputPacketsPerSecond(const quint8 channelType) const;
|
||||
float getAverageOutputPacketsPerSecond(const quint8 channelType) const;
|
||||
float getAverageInputKilobitsPerSecond(const quint8 channelType) const;
|
||||
float getAverageOutputKilobitsPerSecond(const quint8 channelType) const;
|
||||
|
||||
float getTotalAverageInputPacketsPerSecond();
|
||||
float getTotalAverageOutputPacketsPerSecond();
|
||||
float getTotalAverageInputKilobitsPerSecond();
|
||||
float getTotalAverageOutputKilobitsPerSecond();
|
||||
float getTotalAverageInputPacketsPerSecond() const;
|
||||
float getTotalAverageOutputPacketsPerSecond() const;
|
||||
float getTotalAverageInputKilobitsPerSecond() const;
|
||||
float getTotalAverageOutputKilobitsPerSecond() const;
|
||||
|
||||
float getCachedTotalAverageInputPacketsPerSecond();
|
||||
float getCachedTotalAverageOutputPacketsPerSecond();
|
||||
float getCachedTotalAverageInputKilobitsPerSecond();
|
||||
float getCachedTotalAverageOutputKilobitsPerSecond();
|
||||
float getCachedTotalAverageInputPacketsPerSecond() const;
|
||||
float getCachedTotalAverageOutputPacketsPerSecond() const;
|
||||
float getCachedTotalAverageInputKilobitsPerSecond() const;
|
||||
float getCachedTotalAverageOutputKilobitsPerSecond() const;
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -140,8 +140,10 @@ void DomainHandler::hardReset() {
|
|||
}
|
||||
|
||||
bool DomainHandler::isHardRefusal(int reasonCode) {
|
||||
return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || reasonCode == (int)ConnectionRefusedReason::NotAuthorized ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TimedOut);
|
||||
return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TooManyUsers ||
|
||||
reasonCode == (int)ConnectionRefusedReason::NotAuthorized ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TimedOut);
|
||||
}
|
||||
|
||||
bool DomainHandler::getInterstitialModeEnabled() const {
|
||||
|
@ -473,7 +475,7 @@ bool DomainHandler::reasonSuggestsLogin(ConnectionRefusedReason reasonCode) {
|
|||
case ConnectionRefusedReason::LoginError:
|
||||
case ConnectionRefusedReason::NotAuthorized:
|
||||
return true;
|
||||
|
||||
|
||||
default:
|
||||
case ConnectionRefusedReason::Unknown:
|
||||
case ConnectionRefusedReason::ProtocolMismatch:
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include <Trace.h>
|
||||
#include "NodeType.h"
|
||||
|
@ -230,35 +229,18 @@ QDebug operator<<(QDebug debug, const NetworkPeer &peer) {
|
|||
return debug;
|
||||
}
|
||||
|
||||
|
||||
// FIXME this is a temporary implementation to determine if this is the right approach.
|
||||
// If so, migrate the BandwidthRecorder into the NetworkPeer class
|
||||
using BandwidthRecorderPtr = QSharedPointer<BandwidthRecorder>;
|
||||
static QHash<QUuid, BandwidthRecorderPtr> PEER_BANDWIDTH;
|
||||
|
||||
BandwidthRecorder& getBandwidthRecorder(const QUuid & uuid) {
|
||||
if (!PEER_BANDWIDTH.count(uuid)) {
|
||||
PEER_BANDWIDTH.insert(uuid, QSharedPointer<BandwidthRecorder>::create());
|
||||
}
|
||||
return *PEER_BANDWIDTH[uuid].data();
|
||||
}
|
||||
|
||||
void NetworkPeer::recordBytesSent(int count) const {
|
||||
auto& bw = getBandwidthRecorder(_uuid);
|
||||
bw.updateOutboundData(0, count);
|
||||
_bandwidthRecorder.updateOutboundData(0, count);
|
||||
}
|
||||
|
||||
void NetworkPeer::recordBytesReceived(int count) const {
|
||||
auto& bw = getBandwidthRecorder(_uuid);
|
||||
bw.updateInboundData(0, count);
|
||||
_bandwidthRecorder.updateInboundData(0, count);
|
||||
}
|
||||
|
||||
float NetworkPeer::getOutboundBandwidth() const {
|
||||
auto& bw = getBandwidthRecorder(_uuid);
|
||||
return bw.getAverageOutputKilobitsPerSecond(0);
|
||||
return _bandwidthRecorder.getAverageOutputKilobitsPerSecond(0);
|
||||
}
|
||||
|
||||
float NetworkPeer::getInboundBandwidth() const {
|
||||
auto& bw = getBandwidthRecorder(_uuid);
|
||||
return bw.getAverageInputKilobitsPerSecond(0);
|
||||
return _bandwidthRecorder.getAverageInputKilobitsPerSecond(0);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "UUID.h"
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "UUID.h"
|
||||
|
||||
const QString ICE_SERVER_HOSTNAME = "localhost";
|
||||
const quint16 ICE_SERVER_DEFAULT_PORT = 7337;
|
||||
|
@ -113,6 +114,8 @@ protected:
|
|||
HifiSockAddr _symmetricSocket;
|
||||
HifiSockAddr* _activeSocket;
|
||||
|
||||
mutable BandwidthRecorder _bandwidthRecorder;
|
||||
|
||||
quint64 _wakeTimestamp;
|
||||
std::atomic_ullong _lastHeardMicrostamp;
|
||||
|
||||
|
|
|
@ -427,8 +427,10 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
|
||||
|
||||
// Send duplicate check-ins in the exponentially increasing sequence 1, 1, 2, 4, ...
|
||||
static const int MAX_CHECKINS_TOGETHER = 20;
|
||||
int outstandingCheckins = _domainHandler.getCheckInPacketsSinceLastReply();
|
||||
int checkinCount = outstandingCheckins > 1 ? std::pow(2, outstandingCheckins - 2) : 1;
|
||||
checkinCount = std::min(checkinCount, MAX_CHECKINS_TOGETHER);
|
||||
for (int i = 1; i < checkinCount; ++i) {
|
||||
auto packetCopy = domainPacket->createCopy(*domainPacket);
|
||||
sendPacket(std::move(packetCopy), _domainHandler.getSockAddr());
|
||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::ScriptGlmVectors);
|
||||
return static_cast<PacketVersion>(EntityVersion::FixedLightSerialization);
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
|
|
|
@ -244,7 +244,8 @@ enum class EntityVersion : PacketVersion {
|
|||
ParticleSpin,
|
||||
BloomEffect,
|
||||
GrabProperties,
|
||||
ScriptGlmVectors
|
||||
ScriptGlmVectors,
|
||||
FixedLightSerialization
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
|
@ -37,7 +37,6 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
|
|||
_shouldChangeSocketOptions(shouldChangeSocketOptions)
|
||||
{
|
||||
connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams);
|
||||
connect(this, &Socket::pendingDatagrams, this, &Socket::processPendingDatagrams, Qt::QueuedConnection);
|
||||
|
||||
// make sure we hear about errors and state changes from the underlying socket
|
||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
|
@ -316,85 +315,64 @@ void Socket::checkForReadyReadBackup() {
|
|||
}
|
||||
|
||||
void Socket::readPendingDatagrams() {
|
||||
int packetsRead = 0;
|
||||
|
||||
using namespace std::chrono;
|
||||
static const auto MAX_PROCESS_TIME { 100ms };
|
||||
const auto abortTime = system_clock::now() + MAX_PROCESS_TIME;
|
||||
int packetSizeWithHeader = -1;
|
||||
// Max datagrams to read before processing:
|
||||
static const int MAX_DATAGRAMS_CONSECUTIVELY = 10000;
|
||||
while (_udpSocket.hasPendingDatagrams()
|
||||
&& (packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1
|
||||
&& packetsRead <= MAX_DATAGRAMS_CONSECUTIVELY) {
|
||||
// grab a time point we can mark as the receive time of this packet
|
||||
auto receiveTime = p_high_resolution_clock::now();
|
||||
|
||||
|
||||
// setup a buffer to read the packet into
|
||||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
|
||||
// pull the datagram
|
||||
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||
&senderAddress, &senderPort);
|
||||
|
||||
// we either didn't pull anything for this packet or there was an error reading (this seems to trigger
|
||||
// on windows even if there's not a packet available)
|
||||
if (sizeRead < 0) {
|
||||
continue;
|
||||
while (_udpSocket.hasPendingDatagrams() &&
|
||||
(packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
|
||||
if (system_clock::now() > abortTime) {
|
||||
// We've been running for too long, stop processing packets for now
|
||||
// Once we've processed the event queue, we'll come back to packet processing
|
||||
break;
|
||||
}
|
||||
|
||||
_incomingDatagrams.push_back({ senderAddress, senderPort, packetSizeWithHeader,
|
||||
std::move(buffer), receiveTime });
|
||||
++packetsRead;
|
||||
|
||||
}
|
||||
|
||||
if (packetsRead > _maxDatagramsRead) {
|
||||
_maxDatagramsRead = packetsRead;
|
||||
qCDebug(networking) << "readPendingDatagrams: Datagrams read:" << packetsRead;
|
||||
}
|
||||
emit pendingDatagrams(packetsRead);
|
||||
}
|
||||
|
||||
void Socket::processPendingDatagrams(int) {
|
||||
// setup a HifiSockAddr to read into
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
while (!_incomingDatagrams.empty()) {
|
||||
auto& datagram = _incomingDatagrams.front();
|
||||
senderSockAddr.setAddress(datagram._senderAddress);
|
||||
senderSockAddr.setPort(datagram._senderPort);
|
||||
int datagramSize = datagram._datagramLength;
|
||||
auto receiveTime = datagram._receiveTime;
|
||||
|
||||
// we're reading a packet so re-start the readyRead backup timer
|
||||
_readyReadBackupTimer->start();
|
||||
|
||||
// grab a time point we can mark as the receive time of this packet
|
||||
auto receiveTime = p_high_resolution_clock::now();
|
||||
|
||||
// setup a HifiSockAddr to read into
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
// setup a buffer to read the packet into
|
||||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||
|
||||
// pull the datagram
|
||||
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||
|
||||
// save information for this packet, in case it is the one that sticks readyRead
|
||||
_lastPacketSizeRead = datagramSize;
|
||||
_lastPacketSizeRead = sizeRead;
|
||||
_lastPacketSockAddr = senderSockAddr;
|
||||
|
||||
// Process unfiltered packets first.
|
||||
if (sizeRead <= 0) {
|
||||
// we either didn't pull anything for this packet or there was an error reading (this seems to trigger
|
||||
// on windows even if there's not a packet available)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = _unfilteredHandlers.find(senderSockAddr);
|
||||
|
||||
if (it != _unfilteredHandlers.end()) {
|
||||
// we have a registered unfiltered handler for this HifiSockAddr (eg. STUN packet) - call that and return
|
||||
// we have a registered unfiltered handler for this HifiSockAddr - call that and return
|
||||
if (it->second) {
|
||||
auto basePacket = BasePacket::fromReceivedPacket(std::move(datagram._datagram),
|
||||
datagramSize, senderSockAddr);
|
||||
auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||
basePacket->setReceiveTime(receiveTime);
|
||||
it->second(std::move(basePacket));
|
||||
}
|
||||
_incomingDatagrams.pop_front();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if this was a control packet or a data packet
|
||||
bool isControlPacket = *reinterpret_cast<uint32_t*>(datagram._datagram.get()) & CONTROL_BIT_MASK;
|
||||
bool isControlPacket = *reinterpret_cast<uint32_t*>(buffer.get()) & CONTROL_BIT_MASK;
|
||||
|
||||
if (isControlPacket) {
|
||||
// setup a control packet from the data we just read
|
||||
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr);
|
||||
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||
controlPacket->setReceiveTime(receiveTime);
|
||||
|
||||
// move this control packet to the matching connection, if there is one
|
||||
|
@ -406,13 +384,13 @@ void Socket::processPendingDatagrams(int) {
|
|||
|
||||
} else {
|
||||
// setup a Packet from the data we just read
|
||||
auto packet = Packet::fromReceivedPacket(std::move(datagram._datagram), datagramSize, senderSockAddr);
|
||||
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||
packet->setReceiveTime(receiveTime);
|
||||
|
||||
// save the sequence number in case this is the packet that sticks readyRead
|
||||
_lastReceivedSequenceNumber = packet->getSequenceNumber();
|
||||
|
||||
// call our hash verification operator to see if this packet is verified
|
||||
// call our verification operator to see if this packet is verified
|
||||
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
|
||||
if (packet->isReliable()) {
|
||||
// if this was a reliable packet then signal the matching connection with the sequence number
|
||||
|
@ -426,7 +404,6 @@ void Socket::processPendingDatagrams(int) {
|
|||
qCDebug(networking) << "Can't process packet: version" << (unsigned int)NLPacket::versionInHeader(*packet)
|
||||
<< ", type" << NLPacket::typeInHeader(*packet);
|
||||
#endif
|
||||
_incomingDatagrams.pop_front();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -442,8 +419,6 @@ void Socket::processPendingDatagrams(int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
_incomingDatagrams.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,6 @@ public:
|
|||
|
||||
signals:
|
||||
void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
||||
void pendingDatagrams(int datagramCount);
|
||||
|
||||
public slots:
|
||||
void cleanupConnection(HifiSockAddr sockAddr);
|
||||
|
@ -103,7 +102,6 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
void processPendingDatagrams(int datagramCount);
|
||||
void checkForReadyReadBackup();
|
||||
|
||||
void handleSocketError(QAbstractSocket::SocketError socketError);
|
||||
|
@ -147,17 +145,6 @@ private:
|
|||
int _lastPacketSizeRead { 0 };
|
||||
SequenceNumber _lastReceivedSequenceNumber;
|
||||
HifiSockAddr _lastPacketSockAddr;
|
||||
|
||||
struct Datagram {
|
||||
QHostAddress _senderAddress;
|
||||
int _senderPort;
|
||||
int _datagramLength;
|
||||
std::unique_ptr<char[]> _datagram;
|
||||
p_high_resolution_clock::time_point _receiveTime;
|
||||
};
|
||||
|
||||
std::list<Datagram> _incomingDatagrams;
|
||||
int _maxDatagramsRead { 0 };
|
||||
|
||||
friend UDTTest;
|
||||
};
|
||||
|
|
|
@ -502,36 +502,18 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
|||
properties.setVelocity(linearVelocity);
|
||||
properties.setAcceleration(_entity->getAcceleration());
|
||||
properties.setAngularVelocity(angularVelocity);
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_entity->setDynamicDataNeedsTransmit(false);
|
||||
properties.setActionData(_entity->getDynamicData());
|
||||
}
|
||||
|
||||
if (_entity->updateQueryAACube()) {
|
||||
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
|
||||
properties.setQueryAACube(_entity->getQueryAACube());
|
||||
}
|
||||
|
||||
// set the LastEdited of the properties but NOT the entity itself
|
||||
quint64 now = usecTimestampNow();
|
||||
properties.setLastEdited(now);
|
||||
|
||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||
quint64 now = usecTimestampNow();
|
||||
uint8_t finalBidPriority = computeFinalBidPriority();
|
||||
_entity->clearScriptSimulationPriority();
|
||||
properties.setSimulationOwner(Physics::getSessionUUID(), finalBidPriority);
|
||||
_entity->setPendingOwnershipPriority(finalBidPriority);
|
||||
_entity->prepareForSimulationOwnershipBid(properties, now, finalBidPriority);
|
||||
|
||||
EntityTreeElementPointer element = _entity->getElement();
|
||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||
|
||||
properties.setClientOnly(_entity->getClientOnly());
|
||||
properties.setOwningAvatarID(_entity->getOwningAvatarID());
|
||||
|
||||
EntityItemID id(_entity->getID());
|
||||
EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);
|
||||
entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties);
|
||||
_entity->setLastBroadcast(now); // for debug/physics status icons
|
||||
|
||||
// NOTE: we don't descend to children for ownership bid. Instead, if we win ownership of the parent
|
||||
// then in sendUpdate() we'll walk descendents and send updates for their QueryAACubes if necessary.
|
||||
|
|
|
@ -19,7 +19,6 @@ namespace controller {
|
|||
|
||||
class InputPlugin : public Plugin {
|
||||
public:
|
||||
virtual ~InputPlugin() {}
|
||||
virtual void pluginFocusOutEvent() = 0;
|
||||
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0;
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ class PickResult {
|
|||
public:
|
||||
PickResult() {}
|
||||
PickResult(const QVariantMap& pickVariant) : pickVariant(pickVariant) {}
|
||||
virtual ~PickResult() {}
|
||||
virtual ~PickResult() = default;
|
||||
|
||||
virtual QVariantMap toVariantMap() const {
|
||||
return pickVariant;
|
||||
|
@ -135,6 +135,7 @@ class PickQuery : protected ReadWriteLockable {
|
|||
Q_GADGET
|
||||
public:
|
||||
PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled);
|
||||
virtual ~PickQuery() = default;
|
||||
|
||||
/**jsdoc
|
||||
* Enum for different types of Picks and Pointers.
|
||||
|
@ -145,9 +146,11 @@ public:
|
|||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
*
|
||||
* @property {number} Ray Ray Picks intersect a ray with the nearest object in front of them, along a given direction.
|
||||
* @property {number} Stylus Stylus Picks provide "tapping" functionality on/into flat surfaces.
|
||||
* @property {number} Parabola Parabola Picks intersect a parabola with the nearest object in front of them, with a given initial velocity and acceleration.
|
||||
* @property {number} Ray Ray picks intersect a ray with the nearest object in front of them, along a given direction.
|
||||
* @property {number} Stylus Stylus picks provide "tapping" functionality on/into flat surfaces.
|
||||
* @property {number} Parabola Parabola picks intersect a parabola with the nearest object in front of them, with a given
|
||||
* initial velocity and acceleration.
|
||||
* @property {number} Collision Collision picks intersect a collision volume with avatars and entities that have collisions.
|
||||
*/
|
||||
/**jsdoc
|
||||
* <table>
|
||||
|
@ -158,6 +161,7 @@ public:
|
|||
* <tr><td><code>{@link PickType(0)|PickType.Ray}</code></td><td></td></tr>
|
||||
* <tr><td><code>{@link PickType(0)|PickType.Stylus}</code></td><td></td></tr>
|
||||
* <tr><td><code>{@link PickType(0)|PickType.Parabola}</code></td><td></td></tr>
|
||||
* <tr><td><code>{@link PickType(0)|PickType.Collision}</code></td><td></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} PickType
|
||||
|
@ -245,7 +249,6 @@ template<typename T>
|
|||
class Pick : public PickQuery {
|
||||
public:
|
||||
Pick(const T& mathPick, const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled), _mathPick(mathPick) {}
|
||||
virtual ~Pick() {}
|
||||
|
||||
virtual T getMathematicalPick() const = 0;
|
||||
virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0;
|
||||
|
|
|
@ -38,11 +38,11 @@ std::shared_ptr<PickQuery> PickManager::findPick(unsigned int uid) const {
|
|||
|
||||
void PickManager::removePick(unsigned int uid) {
|
||||
withWriteLock([&] {
|
||||
auto type = _typeMap.find(uid);
|
||||
if (type != _typeMap.end()) {
|
||||
_picks[type->second].erase(uid);
|
||||
_typeMap.erase(uid);
|
||||
_totalPickCounts[type->second]--;
|
||||
auto typeIt = _typeMap.find(uid);
|
||||
if (typeIt != _typeMap.end()) {
|
||||
_picks[typeIt->second].erase(uid);
|
||||
_totalPickCounts[typeIt->second]--;
|
||||
_typeMap.erase(typeIt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ void CauterizedModel::createRenderItemSet() {
|
|||
shapeID++;
|
||||
}
|
||||
}
|
||||
_blendshapeBuffersInitialized = true;
|
||||
_blendshapeOffsetsInitialized = true;
|
||||
} else {
|
||||
Model::createRenderItemSet();
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||
if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||
modelBlender->noteRequiresBlend(getThisPointer());
|
||||
}
|
||||
|
|
|
@ -2175,7 +2175,10 @@ public:
|
|||
bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); }
|
||||
|
||||
Flags _flags = 0;
|
||||
short _spare = 0;
|
||||
#if defined(__clang__)
|
||||
__attribute__((unused))
|
||||
#endif
|
||||
short _spare = 0; // Padding
|
||||
|
||||
int getRaw() const { return *reinterpret_cast<const int*>(this); }
|
||||
|
||||
|
|
|
@ -39,10 +39,10 @@ namespace gr {
|
|||
|
||||
extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state);
|
||||
|
||||
HighlightRessources::HighlightRessources() {
|
||||
HighlightResources::HighlightResources() {
|
||||
}
|
||||
|
||||
void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
void HighlightResources::update(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize());
|
||||
|
||||
// If the buffer size changed, we need to delete our FBOs and recreate them at the
|
||||
|
@ -58,32 +58,37 @@ void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuff
|
|||
if (!_colorFrameBuffer) {
|
||||
allocateColorBuffer(primaryFrameBuffer);
|
||||
}
|
||||
|
||||
// The primaryFrameBuffer render buffer can change
|
||||
if (_colorFrameBuffer->getRenderBuffer(0) != primaryFrameBuffer->getRenderBuffer(0)) {
|
||||
_colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
void HighlightResources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
_colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil"));
|
||||
_colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0));
|
||||
_colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat());
|
||||
}
|
||||
|
||||
void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
void HighlightResources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) {
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL);
|
||||
_depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y));
|
||||
_depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth"));
|
||||
_depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() {
|
||||
gpu::FramebufferPointer HighlightResources::getDepthFramebuffer() {
|
||||
assert(_depthFrameBuffer);
|
||||
return _depthFrameBuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer HighlightRessources::getDepthTexture() {
|
||||
gpu::TexturePointer HighlightResources::getDepthTexture() {
|
||||
return _depthStencilTexture;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() {
|
||||
gpu::FramebufferPointer HighlightResources::getColorFramebuffer() {
|
||||
assert(_colorFrameBuffer);
|
||||
return _colorFrameBuffer;
|
||||
}
|
||||
|
@ -97,25 +102,21 @@ float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle&
|
|||
}
|
||||
|
||||
PrepareDrawHighlight::PrepareDrawHighlight() {
|
||||
_ressources = std::make_shared<HighlightRessources>();
|
||||
_resources = std::make_shared<HighlightResources>();
|
||||
}
|
||||
|
||||
void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
auto destinationFrameBuffer = inputs;
|
||||
|
||||
_ressources->update(destinationFrameBuffer);
|
||||
outputs = _ressources;
|
||||
_resources->update(destinationFrameBuffer);
|
||||
outputs = _resources;
|
||||
}
|
||||
|
||||
gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline;
|
||||
gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline;
|
||||
|
||||
DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex,
|
||||
render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) :
|
||||
_highlightPassIndex{ highlightIndex },
|
||||
_shapePlumber { shapePlumber },
|
||||
_sharedParameters{ parameters } {
|
||||
}
|
||||
DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber,
|
||||
HighlightSharedParametersPointer parameters) : _highlightPassIndex(highlightIndex), _shapePlumber(shapePlumber), _sharedParameters(parameters) {}
|
||||
|
||||
void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
|
@ -126,13 +127,13 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
const int PARAMETERS_SLOT = 0;
|
||||
|
||||
if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
gpu::StatePointer state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE));
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
|
||||
state->setColorWriteMask(false, false, false, false);
|
||||
state->setCullMode(gpu::State::CULL_FRONT);
|
||||
|
||||
gpu::StatePointer fillState = gpu::StatePointer(new gpu::State());
|
||||
gpu::StatePointer fillState = std::make_shared<gpu::State>();
|
||||
fillState->setDepthTest(false, false, gpu::LESS_EQUAL);
|
||||
fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
|
||||
fillState->setColorWriteMask(false, false, false, false);
|
||||
|
@ -151,7 +152,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
|
||||
|
||||
if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) {
|
||||
auto ressources = inputs.get1();
|
||||
auto resources = inputs.get1();
|
||||
auto& highlight = highlightStage->getHighlight(highlightId);
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
@ -165,15 +166,11 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
// while stereo is enabled triggers a warning
|
||||
gpu::doInBatch("DrawHighlightMask::run::begin", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setFramebuffer(ressources->getDepthFramebuffer());
|
||||
batch.setFramebuffer(resources->getDepthFramebuffer());
|
||||
batch.clearDepthStencilFramebuffer(1.0f, 0);
|
||||
});
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
const auto jitter = inputs.get2();
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
|
||||
render::ItemBounds itemBounds;
|
||||
|
||||
|
@ -185,6 +182,10 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
auto maskDeformedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed().withDualQuatSkinned());
|
||||
|
||||
// Setup camera, projection and viewport for all items
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(projMat);
|
||||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setProjectionJitter(jitter.x, jitter.y);
|
||||
|
@ -233,7 +234,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
|
||||
const auto securityMargin = 2.0f;
|
||||
const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w);
|
||||
const auto framebufferSize = ressources->getSourceFrameSize();
|
||||
const auto framebufferSize = resources->getSourceFrameSize();
|
||||
const glm::vec2 highlightWidth = { blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y };
|
||||
|
||||
if (highlightWidth != _outlineWidth.get()) {
|
||||
|
@ -241,11 +242,6 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
|
|||
}
|
||||
|
||||
gpu::doInBatch("DrawHighlightMask::run::end", args->_context, [&](gpu::Batch& batch) {
|
||||
// Setup camera, projection and viewport for all items
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
||||
// Draw stencil mask with object bounding boxes
|
||||
auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline;
|
||||
batch.setPipeline(stencilPipeline);
|
||||
|
@ -264,15 +260,14 @@ gpu::PipelinePointer DrawHighlight::_pipeline;
|
|||
gpu::PipelinePointer DrawHighlight::_pipelineFilled;
|
||||
|
||||
DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) :
|
||||
_highlightPassIndex{ highlightIndex },
|
||||
_sharedParameters{ parameters } {
|
||||
_highlightPassIndex(highlightIndex), _sharedParameters(parameters) {
|
||||
}
|
||||
|
||||
void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
auto highlightFrameBuffer = inputs.get1();
|
||||
auto highlightRect = inputs.get3();
|
||||
|
||||
if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) {
|
||||
if (highlightFrameBuffer && highlightRect.z > 0 && highlightRect.w > 0) {
|
||||
auto sceneDepthBuffer = inputs.get2();
|
||||
const auto frameTransform = inputs.get0();
|
||||
auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture();
|
||||
|
@ -334,10 +329,11 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const
|
|||
|
||||
const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) {
|
||||
if (!_pipeline) {
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
gpu::StatePointer state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||
state->setColorWriteMask(true, true, true, true);
|
||||
|
||||
auto program = gpu::Shader::createProgram(shader::render_utils::program::highlight);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
|
@ -364,10 +360,10 @@ void DebugHighlight::configure(const Config& config) {
|
|||
}
|
||||
|
||||
void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) {
|
||||
const auto highlightRessources = input.get0();
|
||||
const auto highlightResources = input.get0();
|
||||
const auto highlightRect = input.get1();
|
||||
|
||||
if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) {
|
||||
if (_isDisplayEnabled && highlightResources && highlightRect.z > 0 && highlightRect.w > 0) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
@ -376,7 +372,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons
|
|||
auto primaryFramebuffer = input.get3();
|
||||
gpu::doInBatch("DebugHighlight::run", args->_context, [&](gpu::Batch& batch) {
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setFramebuffer(highlightRessources->getColorFramebuffer());
|
||||
batch.setFramebuffer(highlightResources->getColorFramebuffer());
|
||||
|
||||
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
|
||||
|
||||
|
@ -386,13 +382,13 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons
|
|||
args->getViewFrustum().evalViewTransform(viewMat);
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setProjectionJitter(jitter.x, jitter.y);
|
||||
batch.setViewTransform(viewMat, true);
|
||||
batch.setViewTransform(viewMat);
|
||||
batch.setModelTransform(Transform());
|
||||
|
||||
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
batch.setPipeline(getDepthPipeline());
|
||||
batch.setResourceTexture(0, highlightRessources->getDepthTexture());
|
||||
batch.setResourceTexture(0, highlightResources->getDepthTexture());
|
||||
const glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
const glm::vec2 topRight(1.0f, 1.0f);
|
||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId);
|
||||
|
@ -415,6 +411,7 @@ void DebugHighlight::initializePipelines() {
|
|||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL));
|
||||
state->setColorWriteMask(true, true, true, true);
|
||||
|
||||
const auto vs = gpu::Shader::createVertex(shader::render_utils::vertex::debug_deferred_buffer);
|
||||
|
||||
|
@ -512,7 +509,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
|
|||
const auto highlightSelectionNames = task.addJob<SelectionToHighlight>("SelectionToHighlight", sharedParameters);
|
||||
|
||||
// Prepare for highlight group rendering.
|
||||
const auto highlightRessources = task.addJob<PrepareDrawHighlight>("PrepareHighlight", primaryFramebuffer);
|
||||
const auto highlightResources = task.addJob<PrepareDrawHighlight>("PrepareHighlight", primaryFramebuffer);
|
||||
render::Varying highlight0Rect;
|
||||
|
||||
for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) {
|
||||
|
@ -532,7 +529,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
|
|||
stream << "HighlightMask" << i;
|
||||
name = stream.str();
|
||||
}
|
||||
const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources, jitter).asVarying();
|
||||
const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightResources, jitter).asVarying();
|
||||
const auto highlightedRect = task.addJob<DrawHighlightMask>(name, drawMaskInputs, i, shapePlumber, sharedParameters);
|
||||
if (i == 0) {
|
||||
highlight0Rect = highlightedRect;
|
||||
|
@ -544,12 +541,12 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren
|
|||
stream << "HighlightEffect" << i;
|
||||
name = stream.str();
|
||||
}
|
||||
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying();
|
||||
const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightResources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying();
|
||||
task.addJob<DrawHighlight>(name, drawHighlightInputs, i, sharedParameters);
|
||||
}
|
||||
|
||||
// Debug highlight
|
||||
const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast<const render::Varying&>(highlight0Rect), jitter, primaryFramebuffer).asVarying();
|
||||
const auto debugInputs = DebugHighlight::Inputs(highlightResources, const_cast<const render::Varying&>(highlight0Rect), jitter, primaryFramebuffer).asVarying();
|
||||
task.addJob<DebugHighlight>("HighlightDebug", debugInputs);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue