mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 10:43:13 +02:00
Merge branch 'master' into 5783-Restarting-Interface-allows-you-to-have-Avatar-Scale-Too-Big
This commit is contained in:
commit
378e85e9b2
27 changed files with 723 additions and 491 deletions
|
@ -1101,9 +1101,9 @@ Rectangle {
|
|||
case 'nearbyUsers':
|
||||
var data = message.params;
|
||||
var index = -1;
|
||||
iAmAdmin = Users.canKick;
|
||||
index = findNearbySessionIndex('', data);
|
||||
if (index !== -1) {
|
||||
iAmAdmin = Users.canKick;
|
||||
myData = data[index];
|
||||
data.splice(index, 1);
|
||||
} else {
|
||||
|
|
|
@ -166,7 +166,7 @@ Rectangle {
|
|||
|
||||
ListView {
|
||||
anchors { left: parent.left; right: parent.right; leftMargin: 70 }
|
||||
height: 125;
|
||||
height: Math.min(250, contentHeight);
|
||||
spacing: 0;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
|
|
|
@ -52,8 +52,10 @@ Windows.ScrollingWindow {
|
|||
|
||||
// used to receive messages from interface script
|
||||
function fromScript(message) {
|
||||
if (loader.item.hasOwnProperty("fromScript")) {
|
||||
loader.item.fromScript(message);
|
||||
if (loader.item !== null) {
|
||||
if (loader.item.hasOwnProperty("fromScript")) {
|
||||
loader.item.fromScript(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2729,56 +2729,43 @@ bool Application::importSVOFromURL(const QString& urlString) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool _renderRequested { false };
|
||||
|
||||
bool Application::event(QEvent* event) {
|
||||
if (!Menu::getInstance()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Presentation/painting logic
|
||||
// TODO: Decouple presentation and painting loops
|
||||
static bool isPaintingThrottled = false;
|
||||
if ((int)event->type() == (int)Present) {
|
||||
if (isPaintingThrottled) {
|
||||
// If painting (triggered by presentation) is hogging the main thread,
|
||||
// repost as low priority to avoid hanging the GUI.
|
||||
// This has the effect of allowing presentation to exceed the paint budget by X times and
|
||||
// only dropping every (1/X) frames, instead of every ceil(X) frames
|
||||
// (e.g. at a 60FPS target, painting for 17us would fall to 58.82FPS instead of 30FPS).
|
||||
removePostedEvents(this, Present);
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Present)), Qt::LowEventPriority);
|
||||
isPaintingThrottled = false;
|
||||
int type = event->type();
|
||||
switch (type) {
|
||||
case Event::Lambda:
|
||||
static_cast<LambdaEvent*>(event)->call();
|
||||
return true;
|
||||
}
|
||||
|
||||
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||
if (shouldPaint(nsecsElapsed)) {
|
||||
_lastTimeUpdated.start();
|
||||
idle(nsecsElapsed);
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
||||
}
|
||||
isPaintingThrottled = true;
|
||||
case Event::Present:
|
||||
if (!_renderRequested) {
|
||||
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||
if (shouldPaint(nsecsElapsed)) {
|
||||
_renderRequested = true;
|
||||
_lastTimeUpdated.start();
|
||||
idle(nsecsElapsed);
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return true;
|
||||
} else if ((int)event->type() == (int)Paint) {
|
||||
// NOTE: This must be updated as close to painting as possible,
|
||||
// or AvatarInputs will mysteriously move to the bottom-right
|
||||
AvatarInputs::getInstance()->update();
|
||||
case Event::Paint:
|
||||
// NOTE: This must be updated as close to painting as possible,
|
||||
// or AvatarInputs will mysteriously move to the bottom-right
|
||||
AvatarInputs::getInstance()->update();
|
||||
paintGL();
|
||||
// wait for the next present event before starting idle / paint again
|
||||
removePostedEvents(this, Present);
|
||||
_renderRequested = false;
|
||||
return true;
|
||||
|
||||
paintGL();
|
||||
|
||||
isPaintingThrottled = false;
|
||||
|
||||
return true;
|
||||
} else if ((int)event->type() == (int)Idle) {
|
||||
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
|
||||
idle(nsecsElapsed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int)event->type() == (int)Lambda) {
|
||||
static_cast<LambdaEvent*>(event)->call();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -3166,59 +3153,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
#endif
|
||||
|
||||
case Qt::Key_H: {
|
||||
// whenever switching to/from full screen mirror from the keyboard, remember
|
||||
// the state you were in before full screen mirror, and return to that.
|
||||
auto previousMode = _myCamera.getMode();
|
||||
if (previousMode != CAMERA_MODE_MIRROR) {
|
||||
switch (previousMode) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::FirstPerson;
|
||||
break;
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
|
||||
// FIXME - it's not clear that these modes make sense to return to...
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::IndependentMode;
|
||||
break;
|
||||
case CAMERA_MODE_ENTITY:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::CameraEntityMode;
|
||||
break;
|
||||
|
||||
default:
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMirrorChecked = Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !isMirrorChecked);
|
||||
if (isMirrorChecked) {
|
||||
|
||||
// if we got here without coming in from a non-Full Screen mirror case, then our
|
||||
// _returnFromFullScreenMirrorTo is unknown. In that case we'll go to the old
|
||||
// behavior of returning to ThirdPerson
|
||||
if (_returnFromFullScreenMirrorTo.isEmpty()) {
|
||||
_returnFromFullScreenMirrorTo = MenuOption::ThirdPerson;
|
||||
}
|
||||
Menu::getInstance()->setIsOptionChecked(_returnFromFullScreenMirrorTo, true);
|
||||
}
|
||||
cameraMenuChanged();
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_P: {
|
||||
if (!(isShifted || isMeta || isOption)) {
|
||||
bool isFirstPersonChecked = Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, !isFirstPersonChecked);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, isFirstPersonChecked);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Slash:
|
||||
Menu::getInstance()->triggerOption(MenuOption::Stats);
|
||||
break;
|
||||
|
@ -7135,6 +7069,12 @@ void Application::updateDisplayMode() {
|
|||
// reset the avatar, to set head and hand palms back to a reasonable default pose.
|
||||
getMyAvatar()->reset(false);
|
||||
|
||||
// switch to first person if entering hmd and setting is checked
|
||||
if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) {
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
}
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ Menu::Menu() {
|
|||
|
||||
// View > First Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
MenuOption::FirstPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Third Person
|
||||
|
@ -233,7 +233,7 @@ Menu::Menu() {
|
|||
|
||||
// View > Mirror
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||
MenuOption::FullscreenMirror, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Independent [advanced]
|
||||
|
@ -258,6 +258,9 @@ Menu::Menu() {
|
|||
// View > Overlays
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Overlays, 0, true);
|
||||
|
||||
// View > Enter First Person Mode in HMD
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPersonHMD, 0, true);
|
||||
|
||||
// Navigate menu ----------------------------------
|
||||
MenuWrapper* navigateMenu = addMenu("Navigate");
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ namespace MenuOption {
|
|||
const QString ExpandPhysicsSimulationTiming = "Expand /physics";
|
||||
const QString ExpandUpdateTiming = "Expand /update";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FirstPersonHMD = "Enter First Person Mode in HMD";
|
||||
const QString FivePointCalibration = "5 Point Calibration";
|
||||
const QString FixGaze = "Fix Gaze (no saccade)";
|
||||
const QString Forward = "Forward";
|
||||
|
|
|
@ -152,6 +152,7 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
|
|||
OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(QString, type), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
@ -220,6 +221,7 @@ OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
|
|||
OverlayID Overlays::cloneOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
@ -307,6 +309,7 @@ void Overlays::deleteOverlay(OverlayID id) {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
_overlaysToDelete.push_back(overlayToDelete);
|
||||
emit overlayDeleted(id);
|
||||
}
|
||||
|
@ -314,6 +317,7 @@ void Overlays::deleteOverlay(OverlayID id) {
|
|||
QString Overlays::getOverlayType(OverlayID overlayId) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QString result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(OverlayID, overlayId));
|
||||
return result;
|
||||
}
|
||||
|
@ -328,6 +332,7 @@ QString Overlays::getOverlayType(OverlayID overlayId) {
|
|||
QObject* Overlays::getOverlayObject(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QObject* result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
@ -383,12 +388,6 @@ void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
|
|||
#endif
|
||||
|
||||
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!_enabled) {
|
||||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
|
@ -413,20 +412,47 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
}
|
||||
|
||||
OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayPropertyResult result;
|
||||
BLOCKING_INVOKE_METHOD(this, "getProperty", Q_RETURN_ARG(OverlayPropertyResult, result), Q_ARG(OverlayID, id), Q_ARG(QString, property));
|
||||
return result;
|
||||
}
|
||||
|
||||
OverlayPropertyResult result;
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
OverlayPropertyResult result;
|
||||
if (thisOverlay && thisOverlay->supportsGetProperty()) {
|
||||
result.value = thisOverlay->getProperty(property);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OverlayPropertyResult Overlays::getProperties(const OverlayID& id, const QStringList& properties) {
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
OverlayPropertyResult result;
|
||||
if (thisOverlay && thisOverlay->supportsGetProperty()) {
|
||||
QVariantMap mapResult;
|
||||
for (const auto& property : properties) {
|
||||
mapResult.insert(property, thisOverlay->getProperty(property));
|
||||
}
|
||||
result.value = mapResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OverlayPropertyResult Overlays::getOverlaysProperties(const QVariant& propertiesById) {
|
||||
QVariantMap map = propertiesById.toMap();
|
||||
OverlayPropertyResult result;
|
||||
QVariantMap resultMap;
|
||||
for (const auto& key : map.keys()) {
|
||||
OverlayID id = OverlayID(key);
|
||||
QVariantMap overlayResult;
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay && thisOverlay->supportsGetProperty()) {
|
||||
QStringList propertiesToFetch = map[key].toStringList();
|
||||
for (const auto& property : propertiesToFetch) {
|
||||
overlayResult[property] = thisOverlay->getProperty(property);
|
||||
}
|
||||
}
|
||||
resultMap[key] = overlayResult;
|
||||
}
|
||||
result.value = resultMap;
|
||||
return result;
|
||||
}
|
||||
|
||||
OverlayPropertyResult::OverlayPropertyResult() {
|
||||
}
|
||||
|
||||
|
@ -458,18 +484,6 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
|
|||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly, bool collidableOnly) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
RayToOverlayIntersectionResult result;
|
||||
BLOCKING_INVOKE_METHOD(this, "findRayIntersectionInternal", Q_RETURN_ARG(RayToOverlayIntersectionResult, result),
|
||||
Q_ARG(PickRay, ray),
|
||||
Q_ARG(bool, precisionPicking),
|
||||
Q_ARG(QVector<OverlayID>, overlaysToInclude),
|
||||
Q_ARG(QVector<OverlayID>, overlaysToDiscard),
|
||||
Q_ARG(bool, visibleOnly),
|
||||
Q_ARG(bool, collidableOnly));
|
||||
return result;
|
||||
}
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
bool bestIsFront = false;
|
||||
|
||||
|
@ -588,6 +602,7 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar
|
|||
bool Overlays::isLoaded(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
@ -602,26 +617,21 @@ bool Overlays::isLoaded(OverlayID id) {
|
|||
QSizeF Overlays::textSize(OverlayID id, const QString& text) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QSizeF result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(OverlayID, id), Q_ARG(QString, text));
|
||||
return result;
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay;
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
thisOverlay = _overlaysHUD[id];
|
||||
}
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
||||
return textOverlay->textSize(text);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
}
|
||||
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
|
||||
return text3dOverlay->textSize(text);
|
||||
if (thisOverlay->is3D()) {
|
||||
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
|
||||
return text3dOverlay->textSize(text);
|
||||
}
|
||||
} else {
|
||||
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
||||
return textOverlay->textSize(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QSizeF(0.0f, 0.0f);
|
||||
|
@ -686,6 +696,7 @@ void Overlays::deletePanel(OverlayID panelId) {
|
|||
bool Overlays::isAddedOverlay(OverlayID id) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "isAddedOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
|
||||
return result;
|
||||
}
|
||||
|
@ -721,6 +732,7 @@ void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) {
|
|||
OverlayID Overlays::getKeyboardFocusOverlay() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
OverlayID result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "getKeyboardFocusOverlay", Q_RETURN_ARG(OverlayID, result));
|
||||
return result;
|
||||
}
|
||||
|
@ -740,6 +752,7 @@ void Overlays::setKeyboardFocusOverlay(OverlayID id) {
|
|||
float Overlays::width() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "width", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
@ -751,6 +764,7 @@ float Overlays::width() {
|
|||
float Overlays::height() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
float result;
|
||||
PROFILE_RANGE(script, __FUNCTION__);
|
||||
BLOCKING_INVOKE_METHOD(this, "height", Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
|
@ -960,10 +974,11 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
||||
QVector<QUuid> result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "findOverlays", Q_RETURN_ARG(QVector<QUuid>, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius));
|
||||
return result;
|
||||
}
|
||||
//if (QThread::currentThread() != thread()) {
|
||||
// PROFILE_RANGE(script, __FUNCTION__);
|
||||
// BLOCKING_INVOKE_METHOD(this, "findOverlays", Q_RETURN_ARG(QVector<QUuid>, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius));
|
||||
// return result;
|
||||
//}
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||
|
|
|
@ -190,6 +190,10 @@ public slots:
|
|||
*/
|
||||
OverlayPropertyResult getProperty(OverlayID id, const QString& property);
|
||||
|
||||
OverlayPropertyResult getProperties(const OverlayID& id, const QStringList& properties);
|
||||
|
||||
OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties);
|
||||
|
||||
/*jsdoc
|
||||
* Find the closest 3D overlay hit by a pick ray.
|
||||
*
|
||||
|
|
|
@ -404,8 +404,18 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo
|
|||
}
|
||||
|
||||
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans()) + translation;
|
||||
if (QThread::currentThread() == thread()) {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
position = (rotation * _internalPoseSet._absolutePoses[jointIndex].trans()) + translation;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QReadLocker readLock(&_externalPoseSetLock);
|
||||
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
|
||||
position = (rotation * _externalPoseSet._absolutePoses[jointIndex].trans()) + translation;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -413,17 +423,31 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm:
|
|||
}
|
||||
|
||||
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
position = _internalPoseSet._absolutePoses[jointIndex].trans();
|
||||
return true;
|
||||
if (QThread::currentThread() == thread()) {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
position = _internalPoseSet._absolutePoses[jointIndex].trans();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
return getAbsoluteJointTranslationInRigFrame(jointIndex, position);
|
||||
}
|
||||
}
|
||||
|
||||
bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
result = rotation * _internalPoseSet._absolutePoses[jointIndex].rot();
|
||||
if (QThread::currentThread() == thread()) {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
result = rotation * _internalPoseSet._absolutePoses[jointIndex].rot();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QReadLocker readLock(&_externalPoseSetLock);
|
||||
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
|
||||
result = rotation * _externalPoseSet._absolutePoses[jointIndex].rot();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -431,6 +455,15 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const
|
|||
}
|
||||
|
||||
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
if (isIndexValid(jointIndex)) {
|
||||
rotation = _internalPoseSet._relativePoses[jointIndex].rot();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QReadLocker readLock(&_externalPoseSetLock);
|
||||
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._relativePoses.size()) {
|
||||
rotation = _externalPoseSet._relativePoses[jointIndex].rot();
|
||||
|
|
|
@ -1008,49 +1008,60 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
|||
}
|
||||
}
|
||||
|
||||
int Avatar::getJointIndex(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
int result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointIndex",
|
||||
Q_RETURN_ARG(int, result), Q_ARG(const QString&, name));
|
||||
return result;
|
||||
void Avatar::invalidateJointIndicesCache() const {
|
||||
QWriteLocker writeLock(&_modelJointIndicesCacheLock);
|
||||
_modelJointsCached = false;
|
||||
}
|
||||
|
||||
void Avatar::withValidJointIndicesCache(std::function<void()> const& worker) const {
|
||||
QReadLocker readLock(&_modelJointIndicesCacheLock);
|
||||
if (_modelJointsCached) {
|
||||
worker();
|
||||
} else {
|
||||
readLock.unlock();
|
||||
{
|
||||
QWriteLocker writeLock(&_modelJointIndicesCacheLock);
|
||||
if (!_modelJointsCached) {
|
||||
_modelJointIndicesCache.clear();
|
||||
if (_skeletonModel && _skeletonModel->isActive()) {
|
||||
_modelJointIndicesCache = _skeletonModel->getFBXGeometry().jointIndices;
|
||||
_modelJointsCached = true;
|
||||
}
|
||||
}
|
||||
worker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Avatar::getJointIndex(const QString& name) const {
|
||||
int result = getFauxJointIndex(name);
|
||||
if (result != -1) {
|
||||
return result;
|
||||
}
|
||||
return _skeletonModel->isActive() ? _skeletonModel->getFBXGeometry().getJointIndex(name) : -1;
|
||||
|
||||
withValidJointIndicesCache([&]() {
|
||||
if (_modelJointIndicesCache.contains(name)) {
|
||||
result = _modelJointIndicesCache[name] - 1;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList Avatar::getJointNames() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointNames",
|
||||
Q_RETURN_ARG(QStringList, result));
|
||||
return result;
|
||||
}
|
||||
return _skeletonModel->isActive() ? _skeletonModel->getFBXGeometry().getJointNames() : QStringList();
|
||||
QStringList result;
|
||||
withValidJointIndicesCache([&]() {
|
||||
result = _modelJointIndicesCache.keys();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(int index) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 position;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
|
||||
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index));
|
||||
return position;
|
||||
}
|
||||
glm::vec3 position;
|
||||
_skeletonModel->getJointPositionInWorldFrame(index, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 position;
|
||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
|
||||
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name));
|
||||
return position;
|
||||
}
|
||||
glm::vec3 position;
|
||||
_skeletonModel->getJointPositionInWorldFrame(getJointIndex(name), position);
|
||||
return position;
|
||||
|
@ -1071,6 +1082,8 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
}
|
||||
|
||||
void Avatar::setModelURLFinished(bool success) {
|
||||
invalidateJointIndicesCache();
|
||||
|
||||
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
||||
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
||||
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
|
||||
|
|
|
@ -267,6 +267,13 @@ protected:
|
|||
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
||||
|
||||
SkeletonModelPointer _skeletonModel;
|
||||
|
||||
void invalidateJointIndicesCache() const;
|
||||
void withValidJointIndicesCache(std::function<void()> const& worker) const;
|
||||
mutable QHash<QString, int> _modelJointIndicesCache;
|
||||
mutable QReadWriteLock _modelJointIndicesCacheLock;
|
||||
mutable bool _modelJointsCached { false };
|
||||
|
||||
glm::vec3 _skeletonOffset;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||
|
|
|
@ -1462,12 +1462,12 @@ int AvatarData::getJointIndex(const QString& name) const {
|
|||
return result;
|
||||
}
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _jointIndices.value(name) - 1;
|
||||
return _fstJointIndices.value(name) - 1;
|
||||
}
|
||||
|
||||
QStringList AvatarData::getJointNames() const {
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
return _jointNames;
|
||||
return _fstJointNames;
|
||||
}
|
||||
|
||||
glm::quat AvatarData::getOrientationOutbound() const {
|
||||
|
@ -1720,14 +1720,14 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
bool ok;
|
||||
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
|
||||
if (ok) {
|
||||
while (_jointNames.size() < jointIndex + 1) {
|
||||
_jointNames.append(QString());
|
||||
while (_fstJointNames.size() < jointIndex + 1) {
|
||||
_fstJointNames.append(QString());
|
||||
}
|
||||
_jointNames[jointIndex] = jointName;
|
||||
_fstJointNames[jointIndex] = jointName;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _jointNames.size(); i++) {
|
||||
_jointIndices.insert(_jointNames.at(i), i + 1);
|
||||
for (int i = 0; i < _fstJointNames.size(); i++) {
|
||||
_fstJointIndices.insert(_fstJointNames.at(i), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1781,8 +1781,8 @@ void AvatarData::sendIdentityPacket() {
|
|||
void AvatarData::updateJointMappings() {
|
||||
{
|
||||
QWriteLocker writeLock(&_jointDataLock);
|
||||
_jointIndices.clear();
|
||||
_jointNames.clear();
|
||||
_fstJointIndices.clear();
|
||||
_fstJointNames.clear();
|
||||
_jointData.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -704,8 +704,8 @@ protected:
|
|||
QString _displayName;
|
||||
QString _sessionDisplayName { };
|
||||
|
||||
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _jointNames; ///< in order of depth-first traversal
|
||||
QHash<QString, int> _fstJointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _fstJointNames; ///< in order of depth-first traversal
|
||||
|
||||
quint64 _errorLogExpiry; ///< time in future when to log an error
|
||||
|
||||
|
|
|
@ -32,13 +32,6 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||
if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) {
|
||||
// tiny shapes are not supported
|
||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DoubleHashKey key = info.getHash();
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
||||
|
||||
// Bullet doesn't support arbitrarily small shapes
|
||||
const float MIN_HALF_EXTENT = 0.005f; // 0.5 cm
|
||||
|
||||
void ShapeInfo::clear() {
|
||||
_url.clear();
|
||||
_pointCollection.clear();
|
||||
|
@ -26,14 +29,20 @@ void ShapeInfo::clear() {
|
|||
}
|
||||
|
||||
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
||||
_url = "";
|
||||
_type = type;
|
||||
_halfExtents = halfExtents;
|
||||
setHalfExtents(halfExtents);
|
||||
switch(type) {
|
||||
case SHAPE_TYPE_NONE:
|
||||
_halfExtents = glm::vec3(0.0f);
|
||||
break;
|
||||
case SHAPE_TYPE_BOX:
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
break;
|
||||
case SHAPE_TYPE_SPHERE: {
|
||||
float radius = glm::length(halfExtents) / SQUARE_ROOT_OF_3;
|
||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||
_halfExtents = glm::vec3(radius);
|
||||
}
|
||||
break;
|
||||
case SHAPE_TYPE_COMPOUND:
|
||||
case SHAPE_TYPE_STATIC_MESH:
|
||||
|
@ -48,14 +57,15 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
|||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_BOX;
|
||||
_halfExtents = halfExtents;
|
||||
setHalfExtents(halfExtents);
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setSphere(float radius) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_SPHERE;
|
||||
_halfExtents = glm::vec3(radius, radius, radius);
|
||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||
_halfExtents = glm::vec3(radius);
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
|
@ -68,6 +78,8 @@ void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollec
|
|||
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_CAPSULE_Y;
|
||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||
halfHeight = glm::max(halfHeight, 0.0f);
|
||||
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
@ -239,3 +251,7 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
|||
}
|
||||
return _doubleHashKey;
|
||||
}
|
||||
|
||||
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
||||
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
||||
}
|
||||
|
|
|
@ -89,6 +89,8 @@ public:
|
|||
const DoubleHashKey& getHash() const;
|
||||
|
||||
protected:
|
||||
void setHalfExtents(const glm::vec3& halfExtents);
|
||||
|
||||
QUrl _url; // url for model of convex collision hulls
|
||||
PointCollection _pointCollection;
|
||||
TriangleIndices _triangleIndices;
|
||||
|
|
|
@ -1010,7 +1010,6 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Entity List...",
|
||||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
@ -1041,7 +1040,6 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Allow Selecting of Large Models",
|
||||
shortcutKey: "CTRL+META+L",
|
||||
afterItem: GRABBABLE_ENTITIES_MENU_ITEM,
|
||||
isCheckable: true,
|
||||
isChecked: true,
|
||||
|
@ -1050,7 +1048,6 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Allow Selecting of Small Models",
|
||||
shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Selecting of Large Models",
|
||||
isCheckable: true,
|
||||
isChecked: true,
|
||||
|
@ -1059,7 +1056,6 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Allow Selecting of Lights",
|
||||
shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models",
|
||||
isCheckable: true,
|
||||
grouping: "Advanced"
|
||||
|
@ -1067,14 +1063,12 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities In Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+A",
|
||||
afterItem: "Allow Selecting of Lights",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Select All Entities Touching Box",
|
||||
shortcutKey: "CTRL+SHIFT+META+T",
|
||||
afterItem: "Select All Entities In Box",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
@ -1082,21 +1076,18 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Export Entities",
|
||||
shortcutKey: "CTRL+META+E",
|
||||
afterItem: "Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Import Entities",
|
||||
shortcutKey: "CTRL+META+I",
|
||||
afterItem: "Export Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "Import Entities from URL",
|
||||
shortcutKey: "CTRL+META+U",
|
||||
afterItem: "Import Entities",
|
||||
grouping: "Advanced"
|
||||
});
|
||||
|
|
|
@ -862,6 +862,9 @@ function avatarDisconnected(nodeID) {
|
|||
|
||||
function clearLocalQMLDataAndClosePAL() {
|
||||
sendToQml({ method: 'clearLocalQMLData' });
|
||||
if (onPalScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
}
|
||||
|
||||
function avatarAdded(avatarID) {
|
||||
|
|
|
@ -17,8 +17,8 @@ set_target_properties(ac-client PROPERTIES FOLDER "Tools")
|
|||
add_subdirectory(skeleton-dump)
|
||||
set_target_properties(skeleton-dump PROPERTIES FOLDER "Tools")
|
||||
|
||||
add_subdirectory(atp-get)
|
||||
set_target_properties(atp-get PROPERTIES FOLDER "Tools")
|
||||
add_subdirectory(atp-client)
|
||||
set_target_properties(atp-client PROPERTIES FOLDER "Tools")
|
||||
|
||||
add_subdirectory(oven)
|
||||
set_target_properties(oven PROPERTIES FOLDER "Tools")
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QLoggingCategory>
|
||||
#include <QCommandLineParser>
|
||||
#include <NetworkLogging.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedLogging.h>
|
||||
#include <AddressManager.h>
|
||||
#include <DependencyManager.h>
|
||||
|
@ -33,6 +34,9 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
|||
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||
parser.addOption(verboseOutput);
|
||||
|
||||
const QCommandLineOption authOption("u", "set usename and pass", "username:password");
|
||||
parser.addOption(authOption);
|
||||
|
||||
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
||||
parser.addOption(domainAddressOption);
|
||||
|
||||
|
@ -81,6 +85,18 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
|||
listenPort = parser.value(listenPortOption).toInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(authOption)) {
|
||||
QStringList pieces = parser.value(authOption).split(":");
|
||||
if (pieces.size() != 2) {
|
||||
qDebug() << "-u should be followed by username:password";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_username = pieces[0];
|
||||
_password = pieces[1];
|
||||
}
|
||||
|
||||
Setting::init();
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
||||
|
@ -88,6 +104,9 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
|||
DependencyManager::set<AddressManager>();
|
||||
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
accountManager->setIsAgent(true);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -96,27 +115,45 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
|
|||
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
||||
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
// start the nodeThread so its event loop is running
|
||||
// start the nodeThread so its event loop is running
|
||||
// (must happen after the checkin timer is created with the nodelist as it's parent)
|
||||
nodeList->startThread();
|
||||
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
// connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
||||
// connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded);
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &ACClientApp::nodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated);
|
||||
// connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
|
||||
// connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID);
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch);
|
||||
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||
|
||||
if (_verbose) {
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
qDebug() << "cached username is" << username << ", isLoggedIn =" << accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
if (!_username.isEmpty()) {
|
||||
|
||||
connect(accountManager.data(), &AccountManager::newKeypair, this, [&](){
|
||||
if (_verbose) {
|
||||
qDebug() << "new keypair has been created.";
|
||||
}
|
||||
});
|
||||
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, [&](){
|
||||
if (_verbose) {
|
||||
qDebug() << "login successful";
|
||||
}
|
||||
});
|
||||
connect(accountManager.data(), &AccountManager::loginFailed, this, [&](){
|
||||
qDebug() << "login failed.";
|
||||
});
|
||||
accountManager->requestAccessToken(_username, _password);
|
||||
}
|
||||
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
|
||||
|
||||
QTimer* doTimer = new QTimer(this);
|
||||
|
|
|
@ -47,6 +47,9 @@ private:
|
|||
bool _sawAvatarMixer { false };
|
||||
bool _sawAssetServer { false };
|
||||
bool _sawMessagesMixer { false };
|
||||
|
||||
QString _username;
|
||||
QString _password;
|
||||
};
|
||||
|
||||
#endif //hifi_ACClientApp_h
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(TARGET_NAME atp-get)
|
||||
set(TARGET_NAME atp-client)
|
||||
setup_hifi_project(Core Widgets)
|
||||
setup_memory_debugger()
|
||||
link_hifi_libraries(shared networking)
|
406
tools/atp-client/src/ATPClientApp.cpp
Normal file
406
tools/atp-client/src/ATPClientApp.cpp
Normal file
|
@ -0,0 +1,406 @@
|
|||
//
|
||||
// ATPClientApp.cpp
|
||||
// tools/atp-client/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-15
|
||||
// Copyright 2017 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 <QDataStream>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <QLoggingCategory>
|
||||
#include <QCommandLineParser>
|
||||
|
||||
#include <NetworkLogging.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedLogging.h>
|
||||
#include <AddressManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <AssetUpload.h>
|
||||
|
||||
#include "ATPClientApp.h"
|
||||
|
||||
#define HIGH_FIDELITY_ATP_CLIENT_USER_AGENT "Mozilla/5.0 (HighFidelityATPClient)"
|
||||
#define TIMEOUT_MILLISECONDS 8000
|
||||
|
||||
ATPClientApp::ATPClientApp(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv)
|
||||
{
|
||||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity ATP-Client");
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||
parser.addOption(verboseOutput);
|
||||
|
||||
const QCommandLineOption uploadOption("T", "upload local file", "local-file-to-send");
|
||||
parser.addOption(uploadOption);
|
||||
|
||||
const QCommandLineOption authOption("u", "set usename and pass", "username:password");
|
||||
parser.addOption(authOption);
|
||||
|
||||
const QCommandLineOption outputFilenameOption("o", "output filename", "output-file-name");
|
||||
parser.addOption(outputFilenameOption);
|
||||
|
||||
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
||||
parser.addOption(domainAddressOption);
|
||||
|
||||
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
||||
parser.addOption(listenPortOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_verbose = parser.isSet(verboseOutput);
|
||||
if (!_verbose) {
|
||||
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
|
||||
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
|
||||
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtDebugMsg, false);
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
||||
}
|
||||
|
||||
QStringList posArgs = parser.positionalArguments();
|
||||
if (posArgs.size() != 1) {
|
||||
qDebug() << "give remote url argument";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_url = QUrl(posArgs[0]);
|
||||
if (_url.scheme() != "atp") {
|
||||
qDebug() << "url should start with atp:";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
int domainPort = 40103;
|
||||
if (_url.port() != -1) {
|
||||
domainPort = _url.port();
|
||||
}
|
||||
|
||||
if (parser.isSet(outputFilenameOption)) {
|
||||
_localOutputFile = parser.value(outputFilenameOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(uploadOption)) {
|
||||
_localUploadFile = parser.value(uploadOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(authOption)) {
|
||||
QStringList pieces = parser.value(authOption).split(":");
|
||||
if (pieces.size() != 2) {
|
||||
qDebug() << "-u should be followed by username:password";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_username = pieces[0];
|
||||
_password = pieces[1];
|
||||
_waitingForLogin = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(listenPortOption)) {
|
||||
_listenPort = parser.value(listenPortOption).toInt();
|
||||
}
|
||||
|
||||
_domainServerAddress = QString("127.0.0.1") + ":" + QString::number(domainPort);
|
||||
if (parser.isSet(domainAddressOption)) {
|
||||
_domainServerAddress = parser.value(domainAddressOption);
|
||||
} else if (!_url.host().isEmpty()) {
|
||||
QUrl domainURL;
|
||||
domainURL.setScheme("hifi");
|
||||
domainURL.setHost(_url.host());
|
||||
_domainServerAddress = domainURL.toString();
|
||||
}
|
||||
|
||||
Setting::init();
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
||||
DependencyManager::set<AccountManager>([&]{ return QString(HIGH_FIDELITY_ATP_CLIENT_USER_AGENT); });
|
||||
DependencyManager::set<AddressManager>();
|
||||
DependencyManager::set<NodeList>(NodeType::Agent, _listenPort);
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
accountManager->setIsAgent(true);
|
||||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// setup a timer for domain-server check ins
|
||||
_domainCheckInTimer = new QTimer(nodeList.data());
|
||||
connect(_domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
||||
_domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
// start the nodeThread so its event loop is running
|
||||
// (must happen after the checkin timer is created with the nodelist as it's parent)
|
||||
nodeList->startThread();
|
||||
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPClientApp::domainConnectionRefused);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPClientApp::nodeAdded);
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPClientApp::nodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPClientApp::nodeActivated);
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPClientApp::notifyPacketVersionMismatch);
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||
|
||||
if (_verbose) {
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
qDebug() << "cached username is" << username << ", isLoggedIn =" << accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
if (!_username.isEmpty()) {
|
||||
|
||||
connect(accountManager.data(), &AccountManager::newKeypair, this, [&](){
|
||||
if (_verbose) {
|
||||
qDebug() << "new keypair has been created.";
|
||||
}
|
||||
});
|
||||
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, [&](){
|
||||
if (_verbose) {
|
||||
qDebug() << "login successful";
|
||||
}
|
||||
_waitingForLogin = false;
|
||||
go();
|
||||
});
|
||||
connect(accountManager.data(), &AccountManager::loginFailed, this, [&](){
|
||||
qDebug() << "login failed.";
|
||||
_waitingForLogin = false;
|
||||
go();
|
||||
});
|
||||
accountManager->requestAccessToken(_username, _password);
|
||||
}
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
assetClient->init();
|
||||
|
||||
if (_verbose) {
|
||||
qDebug() << "domain-server address is" << _domainServerAddress;
|
||||
}
|
||||
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(_domainServerAddress, false);
|
||||
|
||||
QTimer* _timeoutTimer = new QTimer(this);
|
||||
_timeoutTimer->setSingleShot(true);
|
||||
connect(_timeoutTimer, &QTimer::timeout, this, &ATPClientApp::timedOut);
|
||||
_timeoutTimer->start(TIMEOUT_MILLISECONDS);
|
||||
}
|
||||
|
||||
ATPClientApp::~ATPClientApp() {
|
||||
if (_domainCheckInTimer) {
|
||||
QMetaObject::invokeMethod(_domainCheckInTimer, "deleteLater", Qt::QueuedConnection);
|
||||
}
|
||||
if (_timeoutTimer) {
|
||||
QMetaObject::invokeMethod(_timeoutTimer, "deleteLater", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
// this is non-fatal if we are trying to get an authenticated connection -- it will be retried.
|
||||
if (_verbose) {
|
||||
qDebug() << "domainConnectionRefused";
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::domainChanged(const QString& domainHostname) {
|
||||
if (_verbose) {
|
||||
qDebug() << "domainChanged";
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::nodeAdded(SharedNodePointer node) {
|
||||
if (_verbose) {
|
||||
qDebug() << "node added: " << node->getType();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::nodeActivated(SharedNodePointer node) {
|
||||
if (!_waitingForLogin && node->getType() == NodeType::AssetServer) {
|
||||
_waitingForNode = false;
|
||||
go();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::go() {
|
||||
if (_waitingForLogin || _waitingForNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = _url.path();
|
||||
|
||||
if (_verbose) {
|
||||
qDebug() << "path is " << path;
|
||||
}
|
||||
|
||||
if (!_localUploadFile.isEmpty()) {
|
||||
uploadAsset();
|
||||
} else if (path == "/") {
|
||||
listAssets();
|
||||
} else {
|
||||
lookupAsset();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::nodeKilled(SharedNodePointer node) {
|
||||
if (_verbose) {
|
||||
qDebug() << "nodeKilled" << node->getType();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPClientApp::timedOut() {
|
||||
finish(1);
|
||||
}
|
||||
|
||||
void ATPClientApp::notifyPacketVersionMismatch() {
|
||||
if (_verbose) {
|
||||
qDebug() << "packet version mismatch";
|
||||
}
|
||||
finish(1);
|
||||
}
|
||||
|
||||
void ATPClientApp::uploadAsset() {
|
||||
auto path = _url.path();
|
||||
if (path == "/") {
|
||||
qDebug() << "cannot upload to path of /";
|
||||
QCoreApplication::exit(1);
|
||||
}
|
||||
|
||||
auto upload = DependencyManager::get<AssetClient>()->createUpload(_localUploadFile);
|
||||
QObject::connect(upload, &AssetUpload::finished, this, [=](AssetUpload* upload, const QString& hash) mutable {
|
||||
if (upload->getError() != AssetUpload::NoError) {
|
||||
qDebug() << "upload failed: " << upload->getErrorString();
|
||||
} else {
|
||||
setMapping(hash);
|
||||
}
|
||||
|
||||
upload->deleteLater();
|
||||
});
|
||||
|
||||
upload->start();
|
||||
}
|
||||
|
||||
void ATPClientApp::setMapping(QString hash) {
|
||||
auto path = _url.path();
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto request = assetClient->createSetMappingRequest(path, hash);
|
||||
|
||||
connect(request, &SetMappingRequest::finished, this, [=](SetMappingRequest* request) mutable {
|
||||
if (request->getError() != SetMappingRequest::NoError) {
|
||||
qDebug() << "upload succeeded, but couldn't set mapping: " << request->getErrorString();
|
||||
} else if (_verbose) {
|
||||
qDebug() << "mapping set.";
|
||||
}
|
||||
request->deleteLater();
|
||||
finish(0);
|
||||
});
|
||||
|
||||
request->start();
|
||||
}
|
||||
|
||||
void ATPClientApp::listAssets() {
|
||||
auto request = DependencyManager::get<AssetClient>()->createGetAllMappingsRequest();
|
||||
QObject::connect(request, &GetAllMappingsRequest::finished, this, [=](GetAllMappingsRequest* request) mutable {
|
||||
auto result = request->getError();
|
||||
if (result == GetAllMappingsRequest::NotFound) {
|
||||
qDebug() << "not found: " << request->getErrorString();
|
||||
} else if (result == GetAllMappingsRequest::NoError) {
|
||||
auto mappings = request->getMappings();
|
||||
for (auto& kv : mappings ) {
|
||||
qDebug() << kv.first << kv.second;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||
}
|
||||
request->deleteLater();
|
||||
finish(0);
|
||||
});
|
||||
request->start();
|
||||
}
|
||||
|
||||
void ATPClientApp::lookupAsset() {
|
||||
auto path = _url.path();
|
||||
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
||||
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
||||
auto result = request->getError();
|
||||
if (result == GetMappingRequest::NotFound) {
|
||||
qDebug() << "not found: " << request->getErrorString();
|
||||
} else if (result == GetMappingRequest::NoError) {
|
||||
qDebug() << "found, hash is " << request->getHash();
|
||||
download(request->getHash());
|
||||
} else {
|
||||
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
request->start();
|
||||
}
|
||||
|
||||
void ATPClientApp::download(AssetHash hash) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto assetRequest = new AssetRequest(hash);
|
||||
|
||||
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable {
|
||||
Q_ASSERT(request->getState() == AssetRequest::Finished);
|
||||
|
||||
if (request->getError() == AssetRequest::Error::NoError) {
|
||||
QString data = QString::fromUtf8(request->getData());
|
||||
if (_localOutputFile == "" || _localOutputFile == "-") {
|
||||
QTextStream cout(stdout);
|
||||
cout << data;
|
||||
} else {
|
||||
QFile outputHandle(_localOutputFile);
|
||||
if (outputHandle.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream( &outputHandle );
|
||||
stream << data;
|
||||
} else {
|
||||
qDebug() << "couldn't open output file:" << _localOutputFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request->deleteLater();
|
||||
finish(0);
|
||||
});
|
||||
|
||||
assetRequest->start();
|
||||
}
|
||||
|
||||
void ATPClientApp::finish(int exitCode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->setIsShuttingDown(true);
|
||||
|
||||
// tell the packet receiver we're shutting down, so it can drop packets
|
||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
// remove the NodeList from the DependencyManager
|
||||
DependencyManager::destroy<NodeList>();
|
||||
|
||||
QCoreApplication::exit(exitCode);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// ATPGetApp.h
|
||||
// tools/atp-get/src
|
||||
// ATPClientApp.h
|
||||
// tools/atp-client/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
|
@ -10,8 +10,8 @@
|
|||
//
|
||||
|
||||
|
||||
#ifndef hifi_ATPGetApp_h
|
||||
#define hifi_ATPGetApp_h
|
||||
#ifndef hifi_ATPClientApp_h
|
||||
#define hifi_ATPClientApp_h
|
||||
|
||||
#include <QApplication>
|
||||
#include <udt/Constants.h>
|
||||
|
@ -23,11 +23,11 @@
|
|||
#include <MappingRequest.h>
|
||||
|
||||
|
||||
class ATPGetApp : public QCoreApplication {
|
||||
class ATPClientApp : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ATPGetApp(int argc, char* argv[]);
|
||||
~ATPGetApp();
|
||||
ATPClientApp(int argc, char* argv[]);
|
||||
~ATPClientApp();
|
||||
|
||||
private slots:
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
|
||||
|
@ -38,15 +38,33 @@ private slots:
|
|||
void notifyPacketVersionMismatch();
|
||||
|
||||
private:
|
||||
void go();
|
||||
NodeList* _nodeList;
|
||||
void timedOut();
|
||||
void lookup();
|
||||
void uploadAsset();
|
||||
void setMapping(QString hash);
|
||||
void lookupAsset();
|
||||
void listAssets();
|
||||
void download(AssetHash hash);
|
||||
void finish(int exitCode);
|
||||
bool _verbose;
|
||||
|
||||
QUrl _url;
|
||||
QString _localOutputFile;
|
||||
QString _localUploadFile;
|
||||
|
||||
int _listenPort { INVALID_PORT };
|
||||
|
||||
QString _domainServerAddress;
|
||||
|
||||
QString _username;
|
||||
QString _password;
|
||||
|
||||
bool _waitingForLogin { false };
|
||||
bool _waitingForNode { true };
|
||||
|
||||
QTimer* _domainCheckInTimer { nullptr };
|
||||
QTimer* _timeoutTimer { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_ATPGetApp_h
|
||||
#endif // hifi_ATPClientApp_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// main.cpp
|
||||
// tools/atp-get/src
|
||||
// tools/atp-client/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-15
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include <BuildInfo.h>
|
||||
|
||||
#include "ATPGetApp.h"
|
||||
#include "ATPClientApp.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -25,7 +25,7 @@ int main(int argc, char * argv[]) {
|
|||
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
|
||||
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
||||
|
||||
ATPGetApp app(argc, argv);
|
||||
ATPClientApp app(argc, argv);
|
||||
|
||||
return app.exec();
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
//
|
||||
// ATPGetApp.cpp
|
||||
// tools/atp-get/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-15
|
||||
// Copyright 2017 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 <QDataStream>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <QLoggingCategory>
|
||||
#include <QCommandLineParser>
|
||||
#include <NetworkLogging.h>
|
||||
#include <SharedLogging.h>
|
||||
#include <AddressManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "ATPGetApp.h"
|
||||
|
||||
ATPGetApp::ATPGetApp(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv)
|
||||
{
|
||||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity ATP-Get");
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||
parser.addOption(verboseOutput);
|
||||
|
||||
const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1");
|
||||
parser.addOption(domainAddressOption);
|
||||
|
||||
const QCommandLineOption cacheSTUNOption("s", "cache stun-server response");
|
||||
parser.addOption(cacheSTUNOption);
|
||||
|
||||
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
|
||||
parser.addOption(listenPortOption);
|
||||
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_verbose = parser.isSet(verboseOutput);
|
||||
if (!_verbose) {
|
||||
QLoggingCategory::setFilterRules("qt.network.ssl.warning=false");
|
||||
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtDebugMsg, false);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtInfoMsg, false);
|
||||
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
|
||||
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtDebugMsg, false);
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
|
||||
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
|
||||
}
|
||||
|
||||
|
||||
QStringList filenames = parser.positionalArguments();
|
||||
if (filenames.empty() || filenames.size() > 2) {
|
||||
qDebug() << "give remote url and optional local filename as arguments";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
_url = QUrl(filenames[0]);
|
||||
if (_url.scheme() != "atp") {
|
||||
qDebug() << "url should start with atp:";
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (filenames.size() == 2) {
|
||||
_localOutputFile = filenames[1];
|
||||
}
|
||||
|
||||
QString domainServerAddress = "127.0.0.1:40103";
|
||||
if (parser.isSet(domainAddressOption)) {
|
||||
domainServerAddress = parser.value(domainAddressOption);
|
||||
}
|
||||
|
||||
if (_verbose) {
|
||||
qDebug() << "domain-server address is" << domainServerAddress;
|
||||
}
|
||||
|
||||
int listenPort = INVALID_PORT;
|
||||
if (parser.isSet(listenPortOption)) {
|
||||
listenPort = parser.value(listenPortOption).toInt();
|
||||
}
|
||||
|
||||
Setting::init();
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
||||
DependencyManager::set<AccountManager>([&]{ return QString("Mozilla/5.0 (HighFidelityATPGet)"); });
|
||||
DependencyManager::set<AddressManager>();
|
||||
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->startThread();
|
||||
|
||||
// setup a timer for domain-server check ins
|
||||
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
|
||||
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
|
||||
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
|
||||
|
||||
const DomainHandler& domainHandler = nodeList->getDomainHandler();
|
||||
|
||||
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
// connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
|
||||
// connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPGetApp::domainConnectionRefused);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPGetApp::nodeAdded);
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &ATPGetApp::nodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &ATPGetApp::nodeActivated);
|
||||
// connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
|
||||
// connect(nodeList.data(), &NodeList::uuidChanged, this, &ATPGetApp::setSessionUUID);
|
||||
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ATPGetApp::notifyPacketVersionMismatch);
|
||||
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);
|
||||
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(domainServerAddress, false);
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>();
|
||||
assetClient->init();
|
||||
|
||||
QTimer* doTimer = new QTimer(this);
|
||||
doTimer->setSingleShot(true);
|
||||
connect(doTimer, &QTimer::timeout, this, &ATPGetApp::timedOut);
|
||||
doTimer->start(4000);
|
||||
}
|
||||
|
||||
ATPGetApp::~ATPGetApp() {
|
||||
}
|
||||
|
||||
|
||||
void ATPGetApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {
|
||||
qDebug() << "domainConnectionRefused";
|
||||
}
|
||||
|
||||
void ATPGetApp::domainChanged(const QString& domainHostname) {
|
||||
if (_verbose) {
|
||||
qDebug() << "domainChanged";
|
||||
}
|
||||
}
|
||||
|
||||
void ATPGetApp::nodeAdded(SharedNodePointer node) {
|
||||
if (_verbose) {
|
||||
qDebug() << "node added: " << node->getType();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPGetApp::nodeActivated(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::AssetServer) {
|
||||
lookup();
|
||||
}
|
||||
}
|
||||
|
||||
void ATPGetApp::nodeKilled(SharedNodePointer node) {
|
||||
qDebug() << "nodeKilled";
|
||||
}
|
||||
|
||||
void ATPGetApp::timedOut() {
|
||||
finish(1);
|
||||
}
|
||||
|
||||
void ATPGetApp::notifyPacketVersionMismatch() {
|
||||
if (_verbose) {
|
||||
qDebug() << "packet version mismatch";
|
||||
}
|
||||
finish(1);
|
||||
}
|
||||
|
||||
void ATPGetApp::lookup() {
|
||||
|
||||
auto path = _url.path();
|
||||
qDebug() << "path is " << path;
|
||||
|
||||
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
||||
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
||||
auto result = request->getError();
|
||||
if (result == GetMappingRequest::NotFound) {
|
||||
qDebug() << "not found";
|
||||
} else if (result == GetMappingRequest::NoError) {
|
||||
qDebug() << "found, hash is " << request->getHash();
|
||||
download(request->getHash());
|
||||
} else {
|
||||
qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString();
|
||||
}
|
||||
request->deleteLater();
|
||||
});
|
||||
request->start();
|
||||
}
|
||||
|
||||
void ATPGetApp::download(AssetHash hash) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto assetRequest = new AssetRequest(hash);
|
||||
|
||||
connect(assetRequest, &AssetRequest::finished, this, [this](AssetRequest* request) mutable {
|
||||
Q_ASSERT(request->getState() == AssetRequest::Finished);
|
||||
|
||||
if (request->getError() == AssetRequest::Error::NoError) {
|
||||
QString data = QString::fromUtf8(request->getData());
|
||||
if (_localOutputFile == "") {
|
||||
QTextStream cout(stdout);
|
||||
cout << data;
|
||||
} else {
|
||||
QFile outputHandle(_localOutputFile);
|
||||
if (outputHandle.open(QIODevice::ReadWrite)) {
|
||||
QTextStream stream( &outputHandle );
|
||||
stream << data;
|
||||
} else {
|
||||
qDebug() << "couldn't open output file:" << _localOutputFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request->deleteLater();
|
||||
finish(0);
|
||||
});
|
||||
|
||||
assetRequest->start();
|
||||
}
|
||||
|
||||
void ATPGetApp::finish(int exitCode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->setIsShuttingDown(true);
|
||||
|
||||
// tell the packet receiver we're shutting down, so it can drop packets
|
||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
// remove the NodeList from the DependencyManager
|
||||
DependencyManager::destroy<NodeList>();
|
||||
|
||||
QCoreApplication::exit(exitCode);
|
||||
}
|
|
@ -354,8 +354,8 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
|||
FbxFileTexture* fileTexture = property.GetSrcObject<FbxFileTexture>(j);
|
||||
|
||||
// use QFileInfo to easily split up the existing texture filename into its components
|
||||
QString fbxFileName { fileTexture->GetFileName() };
|
||||
QFileInfo textureFileInfo { fbxFileName.replace("\\", "/") };
|
||||
QString fbxTextureFileName { fileTexture->GetFileName() };
|
||||
QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") };
|
||||
|
||||
// make sure this texture points to something and isn't one we've already re-mapped
|
||||
if (!textureFileInfo.filePath().isEmpty()
|
||||
|
@ -372,6 +372,9 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
|||
qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName()
|
||||
<< "to" << bakedTextureFilePath;
|
||||
|
||||
// figure out the URL to this texture, embedded or external
|
||||
auto urlToTexture = getTextureURL(textureFileInfo, fileTexture);
|
||||
|
||||
// write the new filename into the FBX scene
|
||||
fileTexture->SetFileName(bakedTextureFilePath.toLocal8Bit());
|
||||
|
||||
|
@ -379,9 +382,6 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
|
|||
// be right beside the FBX
|
||||
fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData());
|
||||
|
||||
// figure out the URL to this texture, embedded or external
|
||||
auto urlToTexture = getTextureURL(textureFileInfo, fileTexture);
|
||||
|
||||
if (!_bakingTextures.contains(urlToTexture)) {
|
||||
// bake this texture asynchronously
|
||||
bakeTexture(urlToTexture, textureType, _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER);
|
||||
|
|
Loading…
Reference in a new issue