mirror of
https://github.com/overte-org/overte.git
synced 2025-08-05 00:59:56 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into black-bis
This commit is contained in:
commit
1ad556a075
59 changed files with 935 additions and 455 deletions
1
android/gradle.properties
Normal file
1
android/gradle.properties
Normal file
|
@ -0,0 +1 @@
|
|||
org.gradle.jvmargs=-Xms2g -Xmx4g
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 82 KiB |
Binary file not shown.
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 164 KiB |
Binary file not shown.
Before Width: | Height: | Size: 81 KiB After Width: | Height: | 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;
|
||||
|
|
|
@ -8476,6 +8476,16 @@ QUuid Application::getTabletFrameID() const {
|
|||
return HMD->getCurrentTabletFrameID();
|
||||
}
|
||||
|
||||
QVector<QUuid> Application::getTabletIDs() const {
|
||||
// Most important overlays first.
|
||||
QVector<QUuid> result;
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
result << HMD->getCurrentTabletScreenID();
|
||||
result << HMD->getCurrentHomeButtonID();
|
||||
result << HMD->getCurrentTabletFrameID();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Application::setAvatarOverrideUrl(const QUrl& url, bool save) {
|
||||
_avatarOverrideUrl = url;
|
||||
_saveAvatarOverrideUrl = save;
|
||||
|
|
|
@ -298,6 +298,7 @@ public:
|
|||
OverlayID getTabletScreenID() const;
|
||||
OverlayID getTabletHomeButtonID() const;
|
||||
QUuid getTabletFrameID() const; // may be an entity or an overlay
|
||||
QVector<QUuid> getTabletIDs() const; // In order of most important IDs first.
|
||||
|
||||
void setAvatarOverrideUrl(const QUrl& url, bool save);
|
||||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -532,6 +532,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
bool visibleOnly, bool collidableOnly) {
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
bool bestIsFront = false;
|
||||
bool bestIsTablet = false;
|
||||
auto tabletIDs = qApp->getTabletIDs();
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
RayToOverlayIntersectionResult result;
|
||||
|
@ -554,10 +556,11 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay
|
|||
if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance,
|
||||
thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) {
|
||||
bool isDrawInFront = thisOverlay->getDrawInFront();
|
||||
if ((bestIsFront && isDrawInFront && thisDistance < bestDistance)
|
||||
|| (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) {
|
||||
|
||||
bool isTablet = tabletIDs.contains(thisID);
|
||||
if ((isDrawInFront && !bestIsFront && !bestIsTablet)
|
||||
|| ((isTablet || isDrawInFront || !bestIsFront) && thisDistance < bestDistance)) {
|
||||
bestIsFront = isDrawInFront;
|
||||
bestIsTablet = isTablet;
|
||||
bestDistance = thisDistance;
|
||||
result.intersects = true;
|
||||
result.distance = thisDistance;
|
||||
|
@ -828,40 +831,12 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay
|
|||
}
|
||||
|
||||
|
||||
RayToOverlayIntersectionResult Overlays::findRayIntersectionForMouseEvent(PickRay ray) {
|
||||
QVector<OverlayID> overlaysToInclude;
|
||||
QVector<OverlayID> overlaysToDiscard;
|
||||
RayToOverlayIntersectionResult rayPickResult;
|
||||
|
||||
// first priority is tablet screen
|
||||
overlaysToInclude << qApp->getTabletScreenID();
|
||||
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then tablet home button
|
||||
overlaysToInclude.clear();
|
||||
overlaysToInclude << qApp->getTabletHomeButtonID();
|
||||
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then tablet frame
|
||||
overlaysToInclude.clear();
|
||||
overlaysToInclude << OverlayID(qApp->getTabletFrameID());
|
||||
rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then whatever
|
||||
return findRayIntersection(ray);
|
||||
}
|
||||
|
||||
bool Overlays::mousePressEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mousePressEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector<OverlayID>(),
|
||||
QVector<OverlayID>());
|
||||
if (rayPickResult.intersects) {
|
||||
_currentClickingOnOverlayID = rayPickResult.overlayID;
|
||||
|
||||
|
@ -901,7 +876,8 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
|
|||
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector<OverlayID>(),
|
||||
QVector<OverlayID>());
|
||||
if (rayPickResult.intersects) {
|
||||
_currentClickingOnOverlayID = rayPickResult.overlayID;
|
||||
|
||||
|
@ -964,7 +940,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
|||
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector<OverlayID>(),
|
||||
QVector<OverlayID>());
|
||||
if (rayPickResult.intersects) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
|
||||
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
|
||||
|
@ -993,7 +970,8 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector<OverlayID>(),
|
||||
QVector<OverlayID>());
|
||||
if (rayPickResult.intersects) {
|
||||
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
|
||||
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
|
||||
|
|
|
@ -44,8 +44,7 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
|
|||
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
|
||||
|
||||
/**jsdoc
|
||||
* The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection} or
|
||||
* {@link Overlays.findRayIntersectionVector|findRayIntersectionVector}.
|
||||
* The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection}.
|
||||
* @typedef {object} Overlays.RayToOverlayIntersectionResult
|
||||
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected with a 3D overlay, otherwise
|
||||
* <code>false</code>.
|
||||
|
@ -383,7 +382,11 @@ public slots:
|
|||
OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties);
|
||||
|
||||
/**jsdoc
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}.
|
||||
* Find the closest 3D overlay intersected by a {@link PickRay}. Overlays with their <code>drawInFront</code> property set
|
||||
* to <code>true</code> have priority over overlays that don't, except that tablet overlays have priority over any
|
||||
* <code>drawInFront</code> overlays behind them. I.e., if a <code>drawInFront</code> overlay is behind one that isn't
|
||||
* <code>drawInFront</code>, the <code>drawInFront</code> overlay is returned, but if a tablet overlay is in front of a
|
||||
* <code>drawInFront</code> overlay, the tablet overlay is returned.
|
||||
* @function Overlays.findRayIntersection
|
||||
* @param {PickRay} pickRay - The PickRay to use for finding overlays.
|
||||
* @param {boolean} [precisionPicking=false] - <em>Unused</em>; exists to match Entity API.
|
||||
|
@ -750,8 +753,6 @@ private:
|
|||
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
|
||||
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
|
||||
|
||||
private slots:
|
||||
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
|
||||
|
||||
class Model;
|
||||
class EntityTreeRenderer;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
});
|
||||
|
|
|
@ -67,6 +67,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::gl::GLBackend::do_clearFramebuffer),
|
||||
(&::gpu::gl::GLBackend::do_blit),
|
||||
(&::gpu::gl::GLBackend::do_generateTextureMips),
|
||||
(&::gpu::gl::GLBackend::do_generateTextureMipsWithPipeline),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_advance),
|
||||
|
||||
|
@ -166,6 +167,10 @@ GLBackend::GLBackend() {
|
|||
GLBackend::~GLBackend() {}
|
||||
|
||||
void GLBackend::shutdown() {
|
||||
if (_mipGenerationFramebufferId) {
|
||||
glDeleteFramebuffers(1, &_mipGenerationFramebufferId);
|
||||
_mipGenerationFramebufferId = 0;
|
||||
}
|
||||
killInput();
|
||||
killTransform();
|
||||
killTextureManagementStage();
|
||||
|
|
|
@ -288,6 +288,7 @@ public:
|
|||
virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
// Transform Stage
|
||||
virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final;
|
||||
|
@ -407,6 +408,8 @@ public:
|
|||
protected:
|
||||
virtual GLint getRealUniformLocation(GLint location) const;
|
||||
|
||||
virtual void draw(GLenum mode, uint32 numVertices, uint32 startVertex) = 0;
|
||||
|
||||
void recycle() const override;
|
||||
|
||||
// FIXME instead of a single flag, create a features struct similar to
|
||||
|
@ -696,6 +699,8 @@ protected:
|
|||
virtual void initTextureManagementStage();
|
||||
virtual void killTextureManagementStage();
|
||||
|
||||
GLuint _mipGenerationFramebufferId{ 0 };
|
||||
|
||||
typedef void (GLBackend::*CommandCall)(const Batch&, size_t);
|
||||
static CommandCall _commandCalls[Batch::NUM_COMMANDS];
|
||||
friend class GLState;
|
||||
|
|
|
@ -79,3 +79,55 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) {
|
|||
|
||||
object->generateMips();
|
||||
}
|
||||
|
||||
void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) {
|
||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
if (!resourceTexture) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always make sure the GLObject is in sync
|
||||
GLTexture* object = syncGPUObject(resourceTexture);
|
||||
if (object) {
|
||||
GLuint to = object->_texture;
|
||||
glActiveTexture(GL_TEXTURE0 + gpu::slot::texture::MipCreationInput);
|
||||
glBindTexture(object->_target, to);
|
||||
(void)CHECK_GL_ERROR();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
auto numMips = batch._params[paramOffset + 1]._int;
|
||||
if (numMips < 0) {
|
||||
numMips = resourceTexture->getNumMips();
|
||||
} else {
|
||||
numMips = std::min(numMips, (int)resourceTexture->getNumMips());
|
||||
}
|
||||
|
||||
if (_mipGenerationFramebufferId == 0) {
|
||||
glGenFramebuffers(1, &_mipGenerationFramebufferId);
|
||||
Q_ASSERT(_mipGenerationFramebufferId > 0);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
|
||||
for (int level = 1; level < numMips; level++) {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level - 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1);
|
||||
|
||||
const auto mipDimensions = resourceTexture->evalMipDimensions(level);
|
||||
glViewport(0, 0, mipDimensions.x, mipDimensions.y);
|
||||
draw(GL_TRIANGLE_STRIP, 4, 0);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips - 1);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
resetOutputStage();
|
||||
// Restore viewport
|
||||
ivec4& vp = _transform._viewport;
|
||||
glViewport(vp.x, vp.y, vp.z, vp.w);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,29 @@ using namespace gpu::gl41;
|
|||
|
||||
const std::string GL41Backend::GL41_VERSION { "GL41" };
|
||||
|
||||
void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) {
|
||||
if (isStereo()) {
|
||||
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
|
||||
glDrawArraysInstanced(mode, startVertex, numVertices, 2);
|
||||
#else
|
||||
setupStereoSide(0);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
setupStereoSide(1);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
#endif
|
||||
_stats._DSNumTriangles += 2 * numVertices / 3;
|
||||
_stats._DSNumDrawcalls += 2;
|
||||
|
||||
} else {
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
_stats._DSNumTriangles += numVertices / 3;
|
||||
_stats._DSNumDrawcalls++;
|
||||
}
|
||||
_stats._DSNumAPIDrawcalls++;
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) {
|
||||
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
|
||||
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
|
||||
|
|
|
@ -130,6 +130,9 @@ public:
|
|||
};
|
||||
|
||||
protected:
|
||||
|
||||
void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override;
|
||||
|
||||
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
||||
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
|
||||
|
||||
|
|
|
@ -42,6 +42,30 @@ void GL45Backend::recycle() const {
|
|||
Parent::recycle();
|
||||
}
|
||||
|
||||
void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) {
|
||||
if (isStereo()) {
|
||||
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
|
||||
glDrawArraysInstanced(mode, startVertex, numVertices, 2);
|
||||
#else
|
||||
setupStereoSide(0);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
setupStereoSide(1);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
#endif
|
||||
|
||||
_stats._DSNumTriangles += 2 * numVertices / 3;
|
||||
_stats._DSNumDrawcalls += 2;
|
||||
|
||||
} else {
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
_stats._DSNumTriangles += numVertices / 3;
|
||||
_stats._DSNumDrawcalls++;
|
||||
}
|
||||
_stats._DSNumAPIDrawcalls++;
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) {
|
||||
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
|
||||
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
|
||||
|
|
|
@ -229,6 +229,7 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override;
|
||||
void recycle() const override;
|
||||
|
||||
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
||||
|
|
|
@ -20,6 +20,29 @@ using namespace gpu::gles;
|
|||
|
||||
const std::string GLESBackend::GLES_VERSION { "GLES" };
|
||||
|
||||
void GLESBackend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) {
|
||||
if (isStereo()) {
|
||||
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
|
||||
glDrawArraysInstanced(mode, startVertex, numVertices, 2);
|
||||
#else
|
||||
setupStereoSide(0);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
setupStereoSide(1);
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
#endif
|
||||
_stats._DSNumTriangles += 2 * numVertices / 3;
|
||||
_stats._DSNumDrawcalls += 2;
|
||||
|
||||
} else {
|
||||
glDrawArrays(mode, startVertex, numVertices);
|
||||
_stats._DSNumTriangles += numVertices / 3;
|
||||
_stats._DSNumDrawcalls++;
|
||||
}
|
||||
_stats._DSNumAPIDrawcalls++;
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) {
|
||||
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
|
||||
GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType];
|
||||
|
|
|
@ -126,6 +126,9 @@ public:
|
|||
};
|
||||
|
||||
protected:
|
||||
|
||||
void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override;
|
||||
|
||||
GLuint getFramebufferID(const FramebufferPointer& framebuffer) override;
|
||||
GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override;
|
||||
|
||||
|
|
|
@ -426,6 +426,13 @@ void Batch::generateTextureMips(const TexturePointer& texture) {
|
|||
_params.emplace_back(_textures.cache(texture));
|
||||
}
|
||||
|
||||
void Batch::generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips) {
|
||||
ADD_COMMAND(generateTextureMipsWithPipeline);
|
||||
|
||||
_params.emplace_back(_textures.cache(texture));
|
||||
_params.emplace_back(numMips);
|
||||
}
|
||||
|
||||
void Batch::beginQuery(const QueryPointer& query) {
|
||||
ADD_COMMAND(beginQuery);
|
||||
|
||||
|
|
|
@ -226,6 +226,8 @@ public:
|
|||
|
||||
// Generate the mips for a texture
|
||||
void generateTextureMips(const TexturePointer& texture);
|
||||
// Generate the mips for a texture using the current pipeline
|
||||
void generateTextureMipsWithPipeline(const TexturePointer& destTexture, int numMips = -1);
|
||||
|
||||
// Query Section
|
||||
void beginQuery(const QueryPointer& query);
|
||||
|
@ -326,6 +328,7 @@ public:
|
|||
COMMAND_clearFramebuffer,
|
||||
COMMAND_blit,
|
||||
COMMAND_generateTextureMips,
|
||||
COMMAND_generateTextureMipsWithPipeline,
|
||||
|
||||
COMMAND_advance,
|
||||
|
||||
|
|
20
libraries/gpu/src/gpu/MipGeneration.slh
Normal file
20
libraries/gpu/src/gpu/MipGeneration.slh
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!
|
||||
// MipGeneration.slh
|
||||
// libraries/gpu/src
|
||||
//
|
||||
// Created by Olivier Prat on 10/16/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
!>
|
||||
<@if not MIP_GENERATION_SLH@>
|
||||
<@def MIP_GENERATION_SLH@>
|
||||
|
||||
<@include gpu/ShaderConstants.h@>
|
||||
|
||||
layout(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D texMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
||||
<@endif@>
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#define GPU_TEXTURE_TRANSFORM_OBJECT 31
|
||||
|
||||
// Mip creation
|
||||
#define GPU_TEXTURE_MIP_CREATION_INPUT 30
|
||||
|
||||
#define GPU_STORAGE_TRANSFORM_OBJECT 7
|
||||
|
||||
#define GPU_ATTR_POSITION 0
|
||||
|
@ -67,7 +70,8 @@ enum Buffer {
|
|||
namespace texture {
|
||||
enum Texture {
|
||||
ObjectTransforms = GPU_TEXTURE_TRANSFORM_OBJECT,
|
||||
};
|
||||
MipCreationInput = GPU_TEXTURE_MIP_CREATION_INPUT,
|
||||
};
|
||||
} // namespace texture
|
||||
|
||||
namespace storage {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "DeferredFramebuffer.h"
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class HighlightRessources {
|
||||
class HighlightResources {
|
||||
public:
|
||||
HighlightRessources();
|
||||
HighlightResources();
|
||||
|
||||
gpu::FramebufferPointer getDepthFramebuffer();
|
||||
gpu::TexturePointer getDepthTexture();
|
||||
|
@ -44,7 +44,7 @@ protected:
|
|||
void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer);
|
||||
};
|
||||
|
||||
using HighlightRessourcesPointer = std::shared_ptr<HighlightRessources>;
|
||||
using HighlightResourcesPointer = std::shared_ptr<HighlightResources>;
|
||||
|
||||
class HighlightSharedParameters {
|
||||
public:
|
||||
|
@ -65,7 +65,7 @@ using HighlightSharedParametersPointer = std::shared_ptr<HighlightSharedParamete
|
|||
class PrepareDrawHighlight {
|
||||
public:
|
||||
using Inputs = gpu::FramebufferPointer;
|
||||
using Outputs = HighlightRessourcesPointer;
|
||||
using Outputs = HighlightResourcesPointer;
|
||||
using JobModel = render::Job::ModelIO<PrepareDrawHighlight, Inputs, Outputs>;
|
||||
|
||||
PrepareDrawHighlight();
|
||||
|
@ -74,7 +74,7 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
HighlightRessourcesPointer _ressources;
|
||||
HighlightResourcesPointer _resources;
|
||||
|
||||
};
|
||||
|
||||
|
@ -112,8 +112,7 @@ private:
|
|||
|
||||
class DrawHighlightMask {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet3<render::ShapeBounds, HighlightRessourcesPointer, glm::vec2>;
|
||||
using Inputs = render::VaryingSet3<render::ShapeBounds, HighlightResourcesPointer, glm::vec2>;
|
||||
using Outputs = glm::ivec4;
|
||||
using JobModel = render::Job::ModelIO<DrawHighlightMask, Inputs, Outputs>;
|
||||
|
||||
|
@ -122,7 +121,6 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
protected:
|
||||
|
||||
unsigned int _highlightPassIndex;
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
HighlightSharedParametersPointer _sharedParameters;
|
||||
|
@ -136,7 +134,7 @@ protected:
|
|||
class DrawHighlight {
|
||||
public:
|
||||
|
||||
using Inputs = render::VaryingSet5<DeferredFrameTransformPointer, HighlightRessourcesPointer, DeferredFramebufferPointer, glm::ivec4, gpu::FramebufferPointer>;
|
||||
using Inputs = render::VaryingSet5<DeferredFrameTransformPointer, HighlightResourcesPointer, DeferredFramebufferPointer, glm::ivec4, gpu::FramebufferPointer>;
|
||||
using Config = render::Job::Config;
|
||||
using JobModel = render::Job::ModelI<DrawHighlight, Inputs, Config>;
|
||||
|
||||
|
@ -163,11 +161,10 @@ private:
|
|||
|
||||
class DebugHighlightConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty)
|
||||
Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
bool viewMask{ false };
|
||||
bool viewMask { false };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
@ -175,7 +172,7 @@ signals:
|
|||
|
||||
class DebugHighlight {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<HighlightRessourcesPointer, glm::ivec4, glm::vec2, gpu::FramebufferPointer>;
|
||||
using Inputs = render::VaryingSet4<HighlightResourcesPointer, glm::ivec4, glm::vec2, gpu::FramebufferPointer>;
|
||||
using Config = DebugHighlightConfig;
|
||||
using JobModel = render::Job::ModelI<DebugHighlight, Inputs, Config>;
|
||||
|
||||
|
|
|
@ -209,6 +209,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
|||
bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning();
|
||||
|
||||
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
|
||||
_meshNumVertices = (int)modelMesh->getNumVertices();
|
||||
const Model::MeshState& state = model->getMeshState(_meshIndex);
|
||||
|
||||
updateMeshPart(modelMesh, partIndex);
|
||||
|
@ -238,19 +239,12 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
|
|||
|
||||
initCache(model);
|
||||
|
||||
if (_isBlendShaped) {
|
||||
auto buffer = model->_blendshapeBuffers.find(meshIndex);
|
||||
if (buffer != model->_blendshapeBuffers.end()) {
|
||||
_blendshapeBuffer = buffer->second;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// On mac AMD, we specifically need to have a _blendshapeBuffer bound when using a deformed mesh pipeline
|
||||
// On mac AMD, we specifically need to have a _meshBlendshapeBuffer bound when using a deformed mesh pipeline
|
||||
// it cannot be null otherwise we crash in the drawcall using a deformed pipeline with a skinned only (not blendshaped) mesh
|
||||
if ((_isBlendShaped || _isSkinned) && !_blendshapeBuffer) {
|
||||
if ((_isBlendShaped || _isSkinned)) {
|
||||
glm::vec4 data;
|
||||
_blendshapeBuffer = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), reinterpret_cast<const gpu::Byte*>(&data));
|
||||
_meshBlendshapeBuffer = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), reinterpret_cast<const gpu::Byte*>(&data));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -292,8 +286,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector<glm::mat4>& clu
|
|||
if (!_clusterBuffer) {
|
||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) clusterMatrices.data());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) clusterMatrices.data());
|
||||
}
|
||||
|
@ -313,8 +306,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector<Model::Transfor
|
|||
if (!_clusterBuffer) {
|
||||
_clusterBuffer = std::make_shared<gpu::Buffer>(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
||||
(const gpu::Byte*) clusterDualQuaternions.data());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion),
|
||||
(const gpu::Byte*) clusterDualQuaternions.data());
|
||||
}
|
||||
|
@ -403,8 +395,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
|||
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
|
||||
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
|
||||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
if (_blendshapeBuffer) {
|
||||
batch.setResourceBuffer(0, _blendshapeBuffer);
|
||||
if (_meshBlendshapeBuffer) {
|
||||
batch.setResourceBuffer(0, _meshBlendshapeBuffer);
|
||||
}
|
||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||
}
|
||||
|
@ -431,7 +423,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
|
|||
bindMesh(batch);
|
||||
|
||||
// IF deformed pass the mesh key
|
||||
auto drawcallInfo = (uint16_t) (((_isBlendShaped && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1));
|
||||
auto drawcallInfo = (uint16_t) (((_isBlendShaped && _meshBlendshapeBuffer && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1));
|
||||
if (drawcallInfo) {
|
||||
batch.setDrawcallUniform(drawcallInfo);
|
||||
}
|
||||
|
@ -483,3 +475,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector<Model::Tr
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes) {
|
||||
if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) {
|
||||
auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex);
|
||||
if (blendshapeBuffer != blendshapeBuffers.end()) {
|
||||
_meshBlendshapeBuffer = blendshapeBuffer->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,10 +134,13 @@ public:
|
|||
bool _isBlendShaped { false };
|
||||
bool _hasTangents { false };
|
||||
|
||||
void setBlendshapeBuffer(const std::unordered_map<int, gpu::BufferPointer>& blendshapeBuffers, const QVector<int>& blendedMeshSizes);
|
||||
|
||||
private:
|
||||
void initCache(const ModelPointer& model);
|
||||
|
||||
gpu::BufferPointer _blendshapeBuffer;
|
||||
gpu::BufferPointer _meshBlendshapeBuffer;
|
||||
int _meshNumVertices;
|
||||
render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() };
|
||||
};
|
||||
|
||||
|
|
55
libraries/render-utils/src/MetaModelPayload.cpp
Normal file
55
libraries/render-utils/src/MetaModelPayload.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// MetaModelPayload.cpp
|
||||
//
|
||||
// Created by Sam Gondelman on 10/9/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "MetaModelPayload.h"
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "MeshPartPayload.h"
|
||||
|
||||
void MetaModelPayload::setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes, const render::ItemIDs& subRenderItems) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
if (blendNumber < _appliedBlendNumber) {
|
||||
return;
|
||||
}
|
||||
_appliedBlendNumber = blendNumber;
|
||||
|
||||
// We have fewer meshes than before. Invalidate everything
|
||||
if (blendedMeshSizes.length() < (int)_blendshapeBuffers.size()) {
|
||||
_blendshapeBuffers.clear();
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < blendedMeshSizes.size(); i++) {
|
||||
int numVertices = blendedMeshSizes.at(i);
|
||||
|
||||
// This mesh isn't blendshaped
|
||||
if (numVertices == 0) {
|
||||
_blendshapeBuffers.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& buffer = _blendshapeBuffers.find(i);
|
||||
const auto blendShapeBufferSize = numVertices * sizeof(BlendshapeOffset);
|
||||
if (buffer == _blendshapeBuffers.end()) {
|
||||
_blendshapeBuffers[i] = std::make_shared<gpu::Buffer>(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset), blendShapeBufferSize);
|
||||
} else {
|
||||
buffer->second->setData(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset));
|
||||
}
|
||||
|
||||
index += numVertices;
|
||||
}
|
||||
|
||||
render::Transaction transaction;
|
||||
for (auto& id : subRenderItems) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(id, [this, blendedMeshSizes](ModelMeshPartPayload& data) {
|
||||
data.setBlendshapeBuffer(_blendshapeBuffers, blendedMeshSizes);
|
||||
});
|
||||
}
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
30
libraries/render-utils/src/MetaModelPayload.h
Normal file
30
libraries/render-utils/src/MetaModelPayload.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// MetaModelPayload.h
|
||||
//
|
||||
// Created by Sam Gondelman on 10/9/18.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MetaModelPayload_h
|
||||
#define hifi_MetaModelPayload_h
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
#include "gpu/Buffer.h"
|
||||
|
||||
class MetaModelPayload {
|
||||
public:
|
||||
void setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets, const QVector<int>& blendedMeshSizes, const render::ItemIDs& subRenderItems);
|
||||
|
||||
private:
|
||||
std::unordered_map<int, gpu::BufferPointer> _blendshapeBuffers;
|
||||
int _appliedBlendNumber { 0 };
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -62,8 +62,6 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
|
|||
_snapModelToRegistrationPoint(false),
|
||||
_snappedToRegistrationPoint(false),
|
||||
_url(HTTP_INVALID_COM),
|
||||
_blendNumber(0),
|
||||
_appliedBlendNumber(0),
|
||||
_isWireframe(false),
|
||||
_renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build())
|
||||
{
|
||||
|
@ -326,7 +324,7 @@ bool Model::updateGeometry() {
|
|||
initializeBlendshapes(mesh, i);
|
||||
i++;
|
||||
}
|
||||
_blendshapeBuffersInitialized = true;
|
||||
_blendshapeOffsetsInitialized = true;
|
||||
needFullUpdate = true;
|
||||
emit rigReady();
|
||||
}
|
||||
|
@ -1010,7 +1008,8 @@ const render::ItemKey Model::getRenderItemKeyGlobalFlags() const {
|
|||
|
||||
bool Model::addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters) {
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
BlendShapeOperator modelBlendshapeOperator) {
|
||||
if (!_addedToScene && isLoaded()) {
|
||||
updateClusterMatrices();
|
||||
if (_modelMeshRenderItems.empty()) {
|
||||
|
@ -1018,10 +1017,11 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
|||
}
|
||||
}
|
||||
|
||||
_modelBlendshapeOperator = modelBlendshapeOperator;
|
||||
|
||||
bool somethingAdded = false;
|
||||
|
||||
if (_modelMeshRenderItemsMap.empty()) {
|
||||
|
||||
bool hasTransparent = false;
|
||||
size_t verticesCount = 0;
|
||||
foreach(auto renderItem, _modelMeshRenderItems) {
|
||||
|
@ -1063,9 +1063,8 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
|
|||
_modelMeshMaterialNames.clear();
|
||||
_modelMeshRenderItemShapes.clear();
|
||||
|
||||
_blendshapeBuffers.clear();
|
||||
_blendshapeOffsets.clear();
|
||||
_blendshapeBuffersInitialized = false;
|
||||
_blendshapeOffsetsInitialized = false;
|
||||
|
||||
_addedToScene = false;
|
||||
|
||||
|
@ -1468,7 +1467,7 @@ void Model::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());
|
||||
}
|
||||
|
@ -1476,9 +1475,8 @@ void Model::updateClusterMatrices() {
|
|||
|
||||
void Model::deleteGeometry() {
|
||||
_deleteGeometryCounter++;
|
||||
_blendshapeBuffers.clear();
|
||||
_blendshapeOffsets.clear();
|
||||
_blendshapeBuffersInitialized = false;
|
||||
_blendshapeOffsetsInitialized = false;
|
||||
_meshStates.clear();
|
||||
_rig.destroyAnimGraph();
|
||||
_blendedBlendshapeCoefficients.clear();
|
||||
|
@ -1556,7 +1554,7 @@ void Model::createRenderItemSet() {
|
|||
shapeID++;
|
||||
}
|
||||
}
|
||||
_blendshapeBuffersInitialized = true;
|
||||
_blendshapeOffsetsInitialized = true;
|
||||
}
|
||||
|
||||
bool Model::isRenderable() const {
|
||||
|
@ -1685,6 +1683,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
|
|||
|
||||
void Blender::run() {
|
||||
QVector<BlendshapeOffset> blendshapeOffsets;
|
||||
QVector<int> blendedMeshSizes;
|
||||
if (_model && _model->isLoaded()) {
|
||||
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
|
||||
int offset = 0;
|
||||
|
@ -1693,12 +1692,22 @@ void Blender::run() {
|
|||
foreach(const FBXMesh& mesh, meshes) {
|
||||
auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++);
|
||||
if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) {
|
||||
// Not blendshaped or not initialized
|
||||
blendedMeshSizes.push_back(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) {
|
||||
// Mesh sizes don't match. Something has gone wrong
|
||||
blendedMeshSizes.push_back(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
blendshapeOffsets += modelMeshBlendshapeOffsets->second;
|
||||
BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset;
|
||||
offset += modelMeshBlendshapeOffsets->second.size();
|
||||
int numVertices = modelMeshBlendshapeOffsets->second.size();
|
||||
blendedMeshSizes.push_back(numVertices);
|
||||
offset += numVertices;
|
||||
std::vector<BlendshapeOffsetUnpacked> unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size());
|
||||
|
||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||
|
@ -1742,7 +1751,7 @@ void Blender::run() {
|
|||
}
|
||||
// post the result to the ModelBlender, which will dispatch to the model if still alive
|
||||
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
|
||||
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<BlendshapeOffset>, blendshapeOffsets));
|
||||
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector<BlendshapeOffset>, blendshapeOffsets), Q_ARG(QVector<int>, blendedMeshSizes));
|
||||
}
|
||||
|
||||
bool Model::maybeStartBlender() {
|
||||
|
@ -1753,43 +1762,18 @@ bool Model::maybeStartBlender() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Model::setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets) {
|
||||
PROFILE_RANGE(render, __FUNCTION__);
|
||||
if (!isLoaded() || blendNumber < _appliedBlendNumber || !_blendshapeBuffersInitialized) {
|
||||
return;
|
||||
}
|
||||
_appliedBlendNumber = blendNumber;
|
||||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||
int index = 0;
|
||||
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
|
||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
||||
auto meshBlendshapeOffsets = _blendshapeOffsets.find(i);
|
||||
const auto& buffer = _blendshapeBuffers.find(i);
|
||||
if (mesh.blendshapes.isEmpty() || meshBlendshapeOffsets == _blendshapeOffsets.end() || buffer == _blendshapeBuffers.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto blendshapeOffsetSize = meshBlendshapeOffsets->second.size() * sizeof(BlendshapeOffset);
|
||||
buffer->second->setSubData(0, blendshapeOffsetSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset));
|
||||
|
||||
index += meshBlendshapeOffsets->second.size();
|
||||
}
|
||||
}
|
||||
|
||||
void Model::initializeBlendshapes(const FBXMesh& mesh, int index) {
|
||||
if (mesh.blendshapes.empty()) {
|
||||
// mesh doesn't have blendshape, did we allocate one though ?
|
||||
if (_blendshapeBuffers.find(index) != _blendshapeBuffers.end()) {
|
||||
qWarning() << "Mesh does not have Blendshape yet a blendshapeOffset buffer is allocated ?";
|
||||
if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) {
|
||||
qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?";
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Mesh has blendshape, let s allocate the local buffer if not done yet
|
||||
if (_blendshapeBuffers.find(index) == _blendshapeBuffers.end()) {
|
||||
if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) {
|
||||
QVector<BlendshapeOffset> blendshapeOffset;
|
||||
blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size());
|
||||
const auto blendshapeOffsetsSize = blendshapeOffset.size() * sizeof(BlendshapeOffset);
|
||||
_blendshapeBuffers[index] = std::make_shared<gpu::Buffer>(blendshapeOffsetsSize, (const gpu::Byte*) blendshapeOffset.constData(), blendshapeOffsetsSize);
|
||||
_blendshapeOffsets[index] = blendshapeOffset;
|
||||
}
|
||||
}
|
||||
|
@ -1822,10 +1806,14 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
|
|||
}
|
||||
}
|
||||
|
||||
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets) {
|
||||
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets, QVector<int> blendedMeshSizes) {
|
||||
if (model) {
|
||||
model->setBlendedVertices(blendNumber, blendshapeOffsets);
|
||||
auto blendshapeOperator = model->getModelBlendshapeOperator();
|
||||
if (blendshapeOperator) {
|
||||
blendshapeOperator(blendNumber, blendshapeOffsets, blendedMeshSizes, model->fetchRenderItemIDs());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
_pendingBlenders--;
|
||||
|
|
|
@ -86,6 +86,7 @@ struct BlendshapeOffsetUnpacked {
|
|||
};
|
||||
|
||||
using BlendshapeOffset = BlendshapeOffsetPacked;
|
||||
using BlendShapeOperator = std::function<void(int, const QVector<BlendshapeOffset>&, const QVector<int>&, const render::ItemIDs&)>;
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject, public std::enable_shared_from_this<Model>, public scriptable::ModelProvider {
|
||||
|
@ -144,7 +145,14 @@ public:
|
|||
}
|
||||
bool addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters);
|
||||
BlendShapeOperator modelBlendshapeOperator) {
|
||||
auto getters = render::Item::Status::Getters(0);
|
||||
return addToScene(scene, transaction, getters, modelBlendshapeOperator);
|
||||
}
|
||||
bool addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
BlendShapeOperator modelBlendshapeOperator = nullptr);
|
||||
void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||
bool isRenderable() const;
|
||||
|
||||
|
@ -158,9 +166,6 @@ public:
|
|||
|
||||
bool maybeStartBlender();
|
||||
|
||||
/// Sets blended vertices computed in a separate thread.
|
||||
void setBlendedVertices(int blendNumber, const QVector<BlendshapeOffset>& blendshapeOffsets);
|
||||
|
||||
bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
|
||||
bool isAddedToScene() const { return _addedToScene; }
|
||||
|
||||
|
@ -342,6 +347,7 @@ public:
|
|||
|
||||
uint32_t getGeometryCounter() const { return _deleteGeometryCounter; }
|
||||
const QMap<render::ItemID, render::PayloadPointer>& getRenderItems() const { return _modelMeshRenderItemsMap; }
|
||||
BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; }
|
||||
|
||||
void renderDebugMeshBoxes(gpu::Batch& batch);
|
||||
|
||||
|
@ -435,18 +441,13 @@ protected:
|
|||
|
||||
virtual void deleteGeometry();
|
||||
|
||||
QVector<float> _blendshapeCoefficients;
|
||||
|
||||
QUrl _url;
|
||||
|
||||
std::unordered_map<int, gpu::BufferPointer> _blendshapeBuffers;
|
||||
bool _blendshapeBuffersInitialized{ false };
|
||||
|
||||
QVector<QVector<QSharedPointer<Texture>>> _dilatedTextures;
|
||||
|
||||
BlendShapeOperator _modelBlendshapeOperator { nullptr };
|
||||
QVector<float> _blendshapeCoefficients;
|
||||
QVector<float> _blendedBlendshapeCoefficients;
|
||||
int _blendNumber;
|
||||
int _appliedBlendNumber;
|
||||
int _blendNumber { 0 };
|
||||
bool _blendshapeOffsetsInitialized { false };
|
||||
|
||||
mutable QMutex _mutex{ QMutex::Recursive };
|
||||
|
||||
|
@ -463,7 +464,6 @@ protected:
|
|||
// debug rendering support
|
||||
int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID;
|
||||
|
||||
|
||||
static AbstractViewStateInterface* _viewState;
|
||||
|
||||
QVector<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItems;
|
||||
|
@ -536,7 +536,7 @@ public:
|
|||
bool shouldComputeBlendshapes() { return _computeBlendshapes; }
|
||||
|
||||
public slots:
|
||||
void setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets);
|
||||
void setBlendedVertices(ModelPointer model, int blendNumber, QVector<BlendshapeOffset> blendshapeOffsets, QVector<int> blendedMeshSizes);
|
||||
void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -78,7 +78,7 @@ void SoftAttachmentModel::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());
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
this.reticleMinY = MARGIN;
|
||||
this.reticleMaxY;
|
||||
this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters(
|
||||
540,
|
||||
160, // Same as webSurfaceLaserInput.
|
||||
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
|
||||
[],
|
||||
100,
|
||||
|
@ -63,7 +63,6 @@
|
|||
|
||||
this.processLaser = function(controllerData) {
|
||||
var controllerLocation = controllerData.controllerLocations[this.hand];
|
||||
// var otherModuleRunning = this.getOtherModule().running;
|
||||
if ((controllerData.triggerValues[this.hand] < ControllerDispatcherUtils.TRIGGER_ON_VALUE || !controllerLocation.valid) ||
|
||||
this.pointingAtTablet(controllerData)) {
|
||||
return false;
|
||||
|
|
|
@ -28,7 +28,7 @@ Script.include("/~/system/libraries/utils.js");
|
|||
this.reticleMaxY = null;
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
160,
|
||||
165, // Lower priority than webSurfaceLaserInput and hudOverlayPointer.
|
||||
this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"],
|
||||
[],
|
||||
100,
|
||||
|
@ -127,29 +127,41 @@ Script.include("/~/system/libraries/utils.js");
|
|||
};
|
||||
|
||||
this.run = function(controllerData) {
|
||||
var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTabletStylusInput" : "LeftTabletStylusInput");
|
||||
var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND
|
||||
? "RightTabletStylusInput" : "LeftTabletStylusInput");
|
||||
if (tabletStylusInput) {
|
||||
var tabletReady = tabletStylusInput.isReady(controllerData);
|
||||
|
||||
if (tabletReady.active) {
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput");
|
||||
if (overlayLaser) {
|
||||
var overlayLaserReady = overlayLaser.isReady(controllerData);
|
||||
var webLaser = getEnabledModuleByName(this.hand === RIGHT_HAND
|
||||
? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput");
|
||||
if (webLaser) {
|
||||
var webLaserReady = webLaser.isReady(controllerData);
|
||||
var target = controllerData.rayPicks[this.hand].objectID;
|
||||
this.sendPointingAtData(controllerData);
|
||||
if (overlayLaserReady.active && this.pointingAtTablet(target)) {
|
||||
if (webLaserReady.active && this.pointingAtTablet(target)) {
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay");
|
||||
if (!controllerData.triggerClicks[this.hand]) { // Don't grab if trigger pressed when laser starts intersecting.
|
||||
var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND
|
||||
? "RightHudOverlayPointer" : "LeftHudOverlayPointer");
|
||||
if (hudLaser) {
|
||||
var hudLaserReady = hudLaser.isReady(controllerData);
|
||||
if (hudLaserReady.active) {
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND
|
||||
? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay");
|
||||
if (nearOverlay) {
|
||||
var nearOverlayReady = nearOverlay.isReady(controllerData);
|
||||
|
||||
if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) {
|
||||
return this.exitModule();
|
||||
}
|
||||
|
|
|
@ -19,12 +19,13 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
function InVREditMode(hand) {
|
||||
this.hand = hand;
|
||||
this.disableModules = false;
|
||||
var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed.
|
||||
this.running = false;
|
||||
var NO_HAND_LASER = -1; // Invalid hand parameter so that standard laser is not displayed.
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
200, // Not too high otherwise the tablet laser doesn't work.
|
||||
this.hand === RIGHT_HAND ?
|
||||
["rightHand", "rightHandEquip", "rightHandTrigger"] :
|
||||
["leftHand", "leftHandEquip", "leftHandTrigger"],
|
||||
166, // Slightly lower priority than inEditMode.
|
||||
this.hand === RIGHT_HAND
|
||||
? ["rightHand", "rightHandEquip", "rightHandTrigger"]
|
||||
: ["leftHand", "leftHandEquip", "leftHandTrigger"],
|
||||
[],
|
||||
100,
|
||||
makeLaserParams(NO_HAND_LASER, false)
|
||||
|
@ -35,6 +36,35 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
(HMD.homeButtonID && objectID === HMD.homeButtonID);
|
||||
};
|
||||
|
||||
// The Shapes app has a non-standard laser: in particular, the laser end dot displays on its own when the laser is
|
||||
// pointing at the Shapes UI. The laser on/off is controlled by this module but the laser is implemented in the Shapes
|
||||
// app.
|
||||
// If, in the future, the Shapes app laser interaction is adopted as a standard UI style then the laser could be
|
||||
// implemented in the controller modules along side the other laser styles.
|
||||
var INVREDIT_MODULE_RUNNING = "Hifi-InVREdit-Module-Running";
|
||||
|
||||
this.runModule = function () {
|
||||
if (!this.running) {
|
||||
Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({
|
||||
hand: this.hand,
|
||||
running: true
|
||||
}));
|
||||
this.running = true;
|
||||
}
|
||||
return makeRunningValues(true, [], []);
|
||||
};
|
||||
|
||||
this.exitModule = function () {
|
||||
if (this.running) {
|
||||
Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({
|
||||
hand: this.hand,
|
||||
running: false
|
||||
}));
|
||||
this.running = false;
|
||||
}
|
||||
return makeRunningValues(false, [], []);
|
||||
};
|
||||
|
||||
this.isReady = function (controllerData) {
|
||||
if (this.disableModules) {
|
||||
return makeRunningValues(true, [], []);
|
||||
|
@ -45,7 +75,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
this.run = function (controllerData) {
|
||||
// Default behavior if disabling is not enabled.
|
||||
if (!this.disableModules) {
|
||||
return makeRunningValues(false, [], []);
|
||||
return this.exitModule();
|
||||
}
|
||||
|
||||
// Tablet stylus.
|
||||
|
@ -55,7 +85,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
if (tabletStylusInput) {
|
||||
var tabletReady = tabletStylusInput.isReady(controllerData);
|
||||
if (tabletReady.active) {
|
||||
return makeRunningValues(false, [], []);
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +97,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
var overlayLaserReady = overlayLaser.isReady(controllerData);
|
||||
var target = controllerData.rayPicks[this.hand].objectID;
|
||||
if (overlayLaserReady.active && this.pointingAtTablet(target)) {
|
||||
return makeRunningValues(false, [], []);
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +108,20 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
if (nearOverlay) {
|
||||
var nearOverlayReady = nearOverlay.isReady(controllerData);
|
||||
if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) {
|
||||
return makeRunningValues(false, [], []);
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
// HUD overlay.
|
||||
if (!controllerData.triggerClicks[this.hand]) {
|
||||
var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND
|
||||
? "RightHudOverlayPointer"
|
||||
: "LeftHudOverlayPointer");
|
||||
if (hudLaser) {
|
||||
var hudLaserReady = hudLaser.isReady(controllerData);
|
||||
if (hudLaserReady.active) {
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,12 +132,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
if (teleporter) {
|
||||
var teleporterReady = teleporter.isReady(controllerData);
|
||||
if (teleporterReady.active) {
|
||||
return makeRunningValues(false, [], []);
|
||||
return this.exitModule();
|
||||
}
|
||||
}
|
||||
|
||||
// Other behaviors are disabled.
|
||||
return makeRunningValues(true, [], []);
|
||||
return this.runModule();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1409,7 +1409,6 @@ Script.scriptEnding.connect(function () {
|
|||
Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE));
|
||||
Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||
|
||||
Settings.setValue(SETTING_EDIT_PREFIX + MENU_CREATE_ENTITIES_GRABBABLE, Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE));
|
||||
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LARGE));
|
||||
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, Menu.isOptionChecked(MENU_ALLOW_SELECTION_SMALL));
|
||||
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LIGHTS));
|
||||
|
@ -1710,7 +1709,7 @@ function onPromptTextChanged(prompt) {
|
|||
}
|
||||
}
|
||||
|
||||
function handeMenuEvent(menuItem) {
|
||||
function handleMenuEvent(menuItem) {
|
||||
if (menuItem === "Allow Selecting of Small Models") {
|
||||
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
|
||||
} else if (menuItem === "Allow Selecting of Large Models") {
|
||||
|
@ -1750,6 +1749,8 @@ function handeMenuEvent(menuItem) {
|
|||
entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE));
|
||||
} else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) {
|
||||
Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
|
||||
} else if (menuItem === MENU_CREATE_ENTITIES_GRABBABLE) {
|
||||
Settings.setValue(SETTING_EDIT_PREFIX + menuItem, Menu.isOptionChecked(menuItem));
|
||||
}
|
||||
tooltip.show(false);
|
||||
}
|
||||
|
@ -1875,7 +1876,7 @@ function importSVO(importURL) {
|
|||
}
|
||||
Window.svoImportRequested.connect(importSVO);
|
||||
|
||||
Menu.menuItemEvent.connect(handeMenuEvent);
|
||||
Menu.menuItemEvent.connect(handleMenuEvent);
|
||||
|
||||
var keyPressEvent = function (event) {
|
||||
if (isActive) {
|
||||
|
|
|
@ -23,12 +23,17 @@
|
|||
miniState = null,
|
||||
|
||||
// Hands.
|
||||
NO_HAND = -1,
|
||||
LEFT_HAND = 0,
|
||||
RIGHT_HAND = 1,
|
||||
HAND_NAMES = ["LeftHand", "RightHand"],
|
||||
|
||||
// Miscellaneous.
|
||||
// Track controller grabbing state.
|
||||
HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation",
|
||||
grabbingHand = NO_HAND,
|
||||
grabbedItem = null,
|
||||
|
||||
// Miscellaneous.
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"),
|
||||
DEBUG = false;
|
||||
|
||||
|
@ -737,37 +742,11 @@
|
|||
setState(MINI_VISIBLE);
|
||||
}
|
||||
|
||||
function enterMiniShowing(hand) {
|
||||
miniHand = hand;
|
||||
ui.show(miniHand);
|
||||
miniScaleStart = Date.now();
|
||||
miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT);
|
||||
}
|
||||
|
||||
function updateMiniShowing() {
|
||||
if (HMD.showTablet) {
|
||||
setState(MINI_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
function exitMiniShowing() {
|
||||
if (miniScaleTimer) {
|
||||
Script.clearTimeout(miniScaleTimer);
|
||||
miniScaleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateMiniVisible() {
|
||||
function checkMiniVisibility() {
|
||||
var showLeft,
|
||||
showRight;
|
||||
|
||||
// Hide mini tablet if tablet proper has been displayed by other means.
|
||||
if (HMD.showTablet) {
|
||||
setState(MINI_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is
|
||||
// Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is
|
||||
// gazing at.
|
||||
showLeft = shouldShowMini(LEFT_HAND);
|
||||
showRight = shouldShowMini(RIGHT_HAND);
|
||||
|
@ -790,8 +769,47 @@
|
|||
setState(MINI_HIDING);
|
||||
}
|
||||
} else {
|
||||
setState(MINI_HIDING);
|
||||
if (grabbedItem === null || grabbingHand !== miniHand) {
|
||||
setState(MINI_HIDING);
|
||||
} else {
|
||||
setState(MINI_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enterMiniShowing(hand) {
|
||||
miniHand = hand;
|
||||
ui.show(miniHand);
|
||||
miniScaleStart = Date.now();
|
||||
miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT);
|
||||
}
|
||||
|
||||
function updateMiniShowing() {
|
||||
// Hide mini tablet if tablet proper has been displayed by other means.
|
||||
if (HMD.showTablet) {
|
||||
setState(MINI_HIDDEN);
|
||||
}
|
||||
|
||||
// Hide mini tablet if it should no longer be visible.
|
||||
checkMiniVisibility();
|
||||
}
|
||||
|
||||
function exitMiniShowing() {
|
||||
if (miniScaleTimer) {
|
||||
Script.clearTimeout(miniScaleTimer);
|
||||
miniScaleTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateMiniVisible() {
|
||||
// Hide mini tablet if tablet proper has been displayed by other means.
|
||||
if (HMD.showTablet) {
|
||||
setState(MINI_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide mini tablet if it should no longer be visible.
|
||||
checkMiniVisibility();
|
||||
|
||||
// If state hasn't changed, update mini tablet rotation.
|
||||
if (miniState === MINI_VISIBLE) {
|
||||
|
@ -973,6 +991,21 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Track grabbed state and item.
|
||||
switch (message.action) {
|
||||
case "grab":
|
||||
grabbingHand = HAND_NAMES.indexOf(message.joint);
|
||||
grabbedItem = message.grabbedEntity;
|
||||
break;
|
||||
case "release":
|
||||
grabbingHand = NO_HAND;
|
||||
grabbedItem = null;
|
||||
break;
|
||||
default:
|
||||
error("Unexpected grab message!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.grabbedEntity !== HMD.tabletID && message.grabbedEntity !== ui.getMiniTabletID()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,14 +5,20 @@
|
|||
"Oops! Protocol version mismatch.",
|
||||
"Oops! Not authorized to join domain.",
|
||||
"Oops! Connection timed out.",
|
||||
"Oops! The domain is full.",
|
||||
"Oops! Something went wrong."
|
||||
];
|
||||
|
||||
var PROTOCOL_VERSION_MISMATCH = 1;
|
||||
var NOT_AUTHORIZED = 3;
|
||||
var DOMAIN_FULL = 4;
|
||||
var TIMEOUT = 5;
|
||||
var hardRefusalErrors = [PROTOCOL_VERSION_MISMATCH,
|
||||
NOT_AUTHORIZED, TIMEOUT];
|
||||
var hardRefusalErrors = [
|
||||
PROTOCOL_VERSION_MISMATCH,
|
||||
NOT_AUTHORIZED,
|
||||
TIMEOUT,
|
||||
DOMAIN_FULL
|
||||
];
|
||||
var timer = null;
|
||||
var isErrorState = false;
|
||||
|
||||
|
@ -26,7 +32,7 @@
|
|||
return ERROR_MESSAGE_MAP[errorMessageMapIndex];
|
||||
} else {
|
||||
// some other text.
|
||||
return ERROR_MESSAGE_MAP[4];
|
||||
return ERROR_MESSAGE_MAP[ERROR_MESSAGE_MAP.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,10 @@ try {
|
|||
}
|
||||
|
||||
function removeFromStoryIDsToMaybeDelete(story_id) {
|
||||
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1);
|
||||
story_id = parseInt(story_id);
|
||||
if (storyIDsToMaybeDelete.indexOf(story_id) > -1) {
|
||||
storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1);
|
||||
}
|
||||
print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
|
||||
}
|
||||
|
||||
|
@ -258,6 +261,7 @@ function onMessage(message) {
|
|||
}
|
||||
break;
|
||||
case 'removeFromStoryIDsToMaybeDelete':
|
||||
console.log("Facebook OR Twitter button clicked for story_id " + message.story_id);
|
||||
removeFromStoryIDsToMaybeDelete(message.story_id);
|
||||
break;
|
||||
default:
|
||||
|
@ -333,11 +337,13 @@ function fillImageDataFromPrevious() {
|
|||
var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID");
|
||||
var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled");
|
||||
var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled");
|
||||
|
||||
snapshotOptions = {
|
||||
containsGif: previousAnimatedSnapPath !== "",
|
||||
processingGif: false,
|
||||
shouldUpload: false,
|
||||
canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID"),
|
||||
canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID") &&
|
||||
snapshotDomainID === location.domainID,
|
||||
isLoggedIn: isLoggedIn
|
||||
};
|
||||
imageData = [];
|
||||
|
@ -369,7 +375,8 @@ function snapshotUploaded(isError, reply) {
|
|||
isGif = fileExtensionMatches(imageURL, "gif"),
|
||||
ignoreGifSnapshotData = false,
|
||||
ignoreStillSnapshotData = false;
|
||||
storyIDsToMaybeDelete.push(storyID);
|
||||
storyIDsToMaybeDelete.push(parseInt(storyID));
|
||||
print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete));
|
||||
if (isGif) {
|
||||
if (mostRecentGifSnapshotFilename !== replyJson.user_story.details.original_image_file_name) {
|
||||
ignoreGifSnapshotData = true;
|
||||
|
|
|
@ -338,13 +338,15 @@ const HifiNotificationType = hfNotifications.NotificationType;
|
|||
var pendingNotifications = {}
|
||||
var notificationState = NotificationState.UNNOTIFIED;
|
||||
|
||||
function setNotificationState (notificationType, pending = true) {
|
||||
pendingNotifications[notificationType] = pending;
|
||||
notificationState = NotificationState.UNNOTIFIED;
|
||||
for (var key in pendingNotifications) {
|
||||
if (pendingNotifications[key]) {
|
||||
notificationState = NotificationState.NOTIFIED;
|
||||
break;
|
||||
function setNotificationState (notificationType, pending = undefined) {
|
||||
if (pending !== undefined) {
|
||||
pendingNotifications[notificationType] = pending;
|
||||
notificationState = NotificationState.UNNOTIFIED;
|
||||
for (var key in pendingNotifications) {
|
||||
if (pendingNotifications[key]) {
|
||||
notificationState = NotificationState.NOTIFIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED);
|
||||
|
@ -568,7 +570,42 @@ function updateLabels(serverState) {
|
|||
labels.people.icon = pendingNotifications[HifiNotificationType.PEOPLE] ? menuNotificationIcon : null;
|
||||
labels.wallet.icon = pendingNotifications[HifiNotificationType.WALLET] ? menuNotificationIcon : null;
|
||||
labels.marketplace.icon = pendingNotifications[HifiNotificationType.MARKETPLACE] ? menuNotificationIcon : null;
|
||||
|
||||
var onlineUsers = trayNotifications.getOnlineUsers();
|
||||
delete labels.people.submenu;
|
||||
if (onlineUsers) {
|
||||
for (var name in onlineUsers) {
|
||||
if(labels.people.submenu == undefined) {
|
||||
labels.people.submenu = [];
|
||||
}
|
||||
labels.people.submenu.push({
|
||||
label: name,
|
||||
enabled: (onlineUsers[name].location != undefined),
|
||||
click: function (item) {
|
||||
setNotificationState(HifiNotificationType.PEOPLE, false);
|
||||
if(onlineUsers[item.label] && onlineUsers[item.label].location) {
|
||||
StartInterface("hifi://" + onlineUsers[item.label].location.root.name + onlineUsers[item.label].location.path);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
var currentStories = trayNotifications.getCurrentStories();
|
||||
delete labels.goto.submenu;
|
||||
if (currentStories) {
|
||||
for (var location in currentStories) {
|
||||
if(labels.goto.submenu == undefined) {
|
||||
labels.goto.submenu = [];
|
||||
}
|
||||
labels.goto.submenu.push({
|
||||
label: "event in " + location,
|
||||
location: location,
|
||||
click: function (item) {
|
||||
setNotificationState(HifiNotificationType.GOTO, false);
|
||||
StartInterface("hifi://" + item.location + currentStories[item.location].path);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTrayMenu(serverState) {
|
||||
|
@ -919,6 +956,8 @@ app.on('ready', function() {
|
|||
trayNotifications.startPolling();
|
||||
}
|
||||
updateTrayMenu(ProcessGroupStates.STOPPED);
|
||||
|
||||
maybeInstallDefaultContentSet(onContentLoaded);
|
||||
|
||||
if (isServerInstalled()) {
|
||||
maybeInstallDefaultContentSet(onContentLoaded);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -73,11 +73,17 @@ HifiNotification.prototype = {
|
|||
text = this.data + " of your connections are online."
|
||||
}
|
||||
message = "Click to open PEOPLE.";
|
||||
url="hifiapp:PEOPLE"
|
||||
url="hifiapp:PEOPLE";
|
||||
} else {
|
||||
text = this.data.username + " is available in " + this.data.location.root.name + ".";
|
||||
message = "Click to join them.";
|
||||
url="hifi://" + this.data.location.root.name + this.data.location.path;
|
||||
if (this.data.location) {
|
||||
text = this.data.username + " is available in " + this.data.location.root.name + ".";
|
||||
message = "Click to join them.";
|
||||
url="hifi://" + this.data.location.root.name + this.data.location.path;
|
||||
} else {
|
||||
text = this.data.username + " is online.";
|
||||
message = "Click to open PEOPLE.";
|
||||
url="hifiapp:PEOPLE";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -136,7 +142,8 @@ HifiNotification.prototype = {
|
|||
function HifiNotifications(config, menuNotificationCallback) {
|
||||
this.config = config;
|
||||
this.menuNotificationCallback = menuNotificationCallback;
|
||||
this.onlineUsers = new Set([]);
|
||||
this.onlineUsers = {};
|
||||
this.currentStories = {};
|
||||
this.storiesSince = new Date(this.config.get("storiesNotifySince", "1970-01-01T00:00:00.000Z"));
|
||||
this.peopleSince = new Date(this.config.get("peopleNotifySince", "1970-01-01T00:00:00.000Z"));
|
||||
this.walletSince = new Date(this.config.get("walletNotifySince", "1970-01-01T00:00:00.000Z"));
|
||||
|
@ -213,6 +220,12 @@ HifiNotifications.prototype = {
|
|||
clearInterval(this.marketplacePollTimer);
|
||||
}
|
||||
},
|
||||
getOnlineUsers: function () {
|
||||
return this.onlineUsers;
|
||||
},
|
||||
getCurrentStories: function () {
|
||||
return this.currentStories;
|
||||
},
|
||||
_showNotification: function () {
|
||||
var _this = this;
|
||||
|
||||
|
@ -225,7 +238,7 @@ HifiNotifications.prototype = {
|
|||
// previous notification immediately when a
|
||||
// new one is submitted
|
||||
_this.pendingNotifications.shift();
|
||||
if(_this.pendingNotifications.length > 0) {
|
||||
if (_this.pendingNotifications.length > 0) {
|
||||
_this._showNotification();
|
||||
}
|
||||
});
|
||||
|
@ -289,7 +302,6 @@ HifiNotifications.prototype = {
|
|||
finished(false);
|
||||
return;
|
||||
}
|
||||
console.log(content);
|
||||
if (!content.total_entries) {
|
||||
finished(true, token);
|
||||
return;
|
||||
|
@ -313,7 +325,6 @@ HifiNotifications.prototype = {
|
|||
notifyData = content.data.updates;
|
||||
break;
|
||||
}
|
||||
|
||||
notifyData.forEach(function (notifyDataEntry) {
|
||||
_this._addNotification(new HifiNotification(notifyType, notifyDataEntry));
|
||||
});
|
||||
|
@ -346,7 +357,7 @@ HifiNotifications.prototype = {
|
|||
'include_actions=announcement',
|
||||
'restriction=open,hifi',
|
||||
'require_online=true',
|
||||
'per_page=1'
|
||||
'per_page=' + MAX_NOTIFICATION_ITEMS
|
||||
];
|
||||
var url = METAVERSE_SERVER_URL + STORIES_URL + '?' + options.join('&');
|
||||
// call a second time to determine if there are no more stories and we should
|
||||
|
@ -357,7 +368,34 @@ HifiNotifications.prototype = {
|
|||
'bearer': token
|
||||
}
|
||||
}, function (error, data) {
|
||||
_this._pollToDisableHighlight(NotificationType.GOTO, error, data);
|
||||
if (error || !data.body) {
|
||||
console.log("Error: unable to get " + url);
|
||||
finished(false);
|
||||
return;
|
||||
}
|
||||
var content = JSON.parse(data.body);
|
||||
if (!content || content.status != 'success') {
|
||||
console.log("Error: unable to get " + url);
|
||||
finished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content.total_entries) {
|
||||
finished(true, token);
|
||||
return;
|
||||
}
|
||||
if (!content.total_entries) {
|
||||
_this.menuNotificationCallback(NotificationType.GOTO, false);
|
||||
}
|
||||
_this.currentStories = {};
|
||||
content.user_stories.forEach(function (story) {
|
||||
// only show a single instance of each story location
|
||||
// in the menu. This may cause issues with domains
|
||||
// where the story locations are significantly different
|
||||
// for each story.
|
||||
_this.currentStories[story.place_name] = story;
|
||||
});
|
||||
_this.menuNotificationCallback(NotificationType.GOTO);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -400,20 +438,19 @@ HifiNotifications.prototype = {
|
|||
console.log("Error: unable to get " + url);
|
||||
return false;
|
||||
}
|
||||
console.log(content);
|
||||
if (!content.total_entries) {
|
||||
_this.menuNotificationCallback(NotificationType.PEOPLE, false);
|
||||
_this.onlineUsers = new Set([]);
|
||||
_this.onlineUsers = {};
|
||||
return;
|
||||
}
|
||||
|
||||
var currentUsers = new Set([]);
|
||||
var currentUsers = {};
|
||||
var newUsers = new Set([]);
|
||||
content.data.users.forEach(function (user) {
|
||||
currentUsers.add(user.username);
|
||||
if (!_this.onlineUsers.has(user.username)) {
|
||||
currentUsers[user.username] = user;
|
||||
if (!(user.username in _this.onlineUsers)) {
|
||||
newUsers.add(user);
|
||||
_this.onlineUsers.add(user.username);
|
||||
_this.onlineUsers[user.username] = user;
|
||||
}
|
||||
});
|
||||
_this.onlineUsers = currentUsers;
|
||||
|
|
Loading…
Reference in a new issue