diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0ba83864c4..bea677aeb6 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -138,7 +138,6 @@ void Agent::handleJurisdictionPacket(QSharedPointer message, Sh void Agent::handleAudioPacket(QSharedPointer message) { _receivedAudioStream.parseData(*message); - _lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness(); _receivedAudioStream.clearBuffer(); } @@ -323,12 +322,14 @@ void Agent::scriptRequestFinished() { request->deleteLater(); } + void Agent::executeScript() { _scriptEngine = std::unique_ptr(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload)); _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); + connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); @@ -338,11 +339,33 @@ void Agent::executeScript() { // give this AvatarData object to the script engine _scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data()); + auto player = DependencyManager::get(); + connect(player.data(), &recording::Deck::playbackStateChanged, [=] { + if (player->isPlaying()) { + auto recordingInterface = DependencyManager::get(); + if (recordingInterface->getPlayFromCurrentLocation()) { + scriptedAvatar->setRecordingBasis(); + } + } else { + scriptedAvatar->clearRecordingBasis(); + } + }); using namespace recording; static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME); - // FIXME how to deal with driving multiple avatars locally? Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) { + + auto recordingInterface = DependencyManager::get(); + bool useFrameSkeleton = recordingInterface->getPlayerUseSkeletonModel(); + + // FIXME - the ability to switch the avatar URL is not actually supported when playing back from a recording + if (!useFrameSkeleton) { + static std::once_flag warning; + std::call_once(warning, [] { + qWarning() << "Recording.setPlayerUseSkeletonModel(false) is not currently supported."; + }); + } + AvatarData::fromFrame(frame->data, *scriptedAvatar); }); @@ -352,8 +375,11 @@ void Agent::executeScript() { const QByteArray& audio = frame->data; static quint16 audioSequenceNumber{ 0 }; Transform audioTransform; + + auto headOrientation = scriptedAvatar->getHeadOrientation(); audioTransform.setTranslation(scriptedAvatar->getPosition()); - audioTransform.setRotation(scriptedAvatar->getOrientation()); + audioTransform.setRotation(headOrientation); + QByteArray encodedBuffer; if (_encoder) { _encoder->encode(audio, encodedBuffer); @@ -537,7 +563,10 @@ void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) { } void Agent::processAgentAvatarAudio() { - if (_isAvatar && (_isListeningToAudioStream || _avatarSound)) { + auto recordingInterface = DependencyManager::get(); + bool isPlayingRecording = recordingInterface->isPlaying(); + + if (_isAvatar && ((_isListeningToAudioStream && !isPlayingRecording) || _avatarSound)) { // if we have an avatar audio stream then send it out to our audio-mixer auto scriptedAvatar = DependencyManager::get(); bool silentFrame = true; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index cb381024ba..a881bb960b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1029,7 +1029,7 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { - qDebug() << "Using default after failing to load Avatar model: " << _skeletonModelURL; + qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d4815b35c6..0174423ee9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1165,7 +1165,6 @@ void MyAvatar::clearJointsData() { } void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { - Avatar::setSkeletonModelURL(skeletonModelURL); render::ScenePointer scene = qApp->getMain3DScene(); _skeletonModel->setVisibleInScene(true, scene); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 146b193a69..aaf5a639a6 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -283,14 +283,20 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += sizeof(packetStateFlags); if (hasAvatarGlobalPosition) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); data->globalPosition[0] = _globalPosition.x; data->globalPosition[1] = _globalPosition.y; data->globalPosition[2] = _globalPosition.z; destinationBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); + + int numBytes = destinationBuffer - startSection; + + _globalPositionRateOutbound.increment(numBytes); } if (hasAvatarBoundingBox) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); data->avatarDimensions[0] = _globalBoundingBoxDimensions.x; @@ -302,36 +308,56 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent data->boundOriginOffset[2] = _globalBoundingBoxOffset.z; destinationBuffer += sizeof(AvatarDataPacket::AvatarBoundingBox); + + int numBytes = destinationBuffer - startSection; + _avatarBoundingBoxRateOutbound.increment(numBytes); } if (hasAvatarOrientation) { + auto startSection = destinationBuffer; auto localOrientation = getLocalOrientation(); destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, localOrientation); + + int numBytes = destinationBuffer - startSection; + _avatarOrientationRateOutbound.increment(numBytes); } if (hasAvatarScale) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); auto scale = getDomainLimitedScale(); packFloatRatioToTwoByte((uint8_t*)(&data->scale), scale); destinationBuffer += sizeof(AvatarDataPacket::AvatarScale); + + int numBytes = destinationBuffer - startSection; + _avatarScaleRateOutbound.increment(numBytes); } if (hasLookAtPosition) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); auto lookAt = _headData->getLookAtPosition(); data->lookAtPosition[0] = lookAt.x; data->lookAtPosition[1] = lookAt.y; data->lookAtPosition[2] = lookAt.z; destinationBuffer += sizeof(AvatarDataPacket::LookAtPosition); + + int numBytes = destinationBuffer - startSection; + _lookAtPositionRateOutbound.increment(numBytes); } if (hasAudioLoudness) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); data->audioLoudness = packFloatGainToByte(_headData->getAudioLoudness() / AUDIO_LOUDNESS_SCALE); destinationBuffer += sizeof(AvatarDataPacket::AudioLoudness); + + int numBytes = destinationBuffer - startSection; + _audioLoudnessRateOutbound.increment(numBytes); } if (hasSensorToWorldMatrix) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); packOrientationQuatToSixBytes(data->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); @@ -341,9 +367,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent data->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; data->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; destinationBuffer += sizeof(AvatarDataPacket::SensorToWorldMatrix); + + int numBytes = destinationBuffer - startSection; + _sensorToWorldRateOutbound.increment(numBytes); } if (hasAdditionalFlags) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); uint8_t flags { 0 }; @@ -370,27 +400,39 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent } data->flags = flags; destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags); + + int numBytes = destinationBuffer - startSection; + _additionalFlagsRateOutbound.increment(numBytes); } if (hasAvatarLocalPosition) { + auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); auto localPosition = getLocalPosition(); data->localPosition[0] = localPosition.x; data->localPosition[1] = localPosition.y; data->localPosition[2] = localPosition.z; destinationBuffer += sizeof(AvatarDataPacket::AvatarLocalPosition); + + int numBytes = destinationBuffer - startSection; + _localPositionRateOutbound.increment(numBytes); } if (hasParentInfo) { + auto startSection = destinationBuffer; auto parentInfo = reinterpret_cast(destinationBuffer); QByteArray referentialAsBytes = parentID.toRfc4122(); memcpy(parentInfo->parentUUID, referentialAsBytes.data(), referentialAsBytes.size()); parentInfo->parentJointIndex = _parentJointIndex; destinationBuffer += sizeof(AvatarDataPacket::ParentInfo); + + int numBytes = destinationBuffer - startSection; + _parentInfoRateOutbound.increment(numBytes); } // If it is connected, pack up the data if (hasFaceTrackerInfo) { + auto startSection = destinationBuffer; auto faceTrackerInfo = reinterpret_cast(destinationBuffer); faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; @@ -403,10 +445,14 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent // followed by a variable number of float coefficients memcpy(destinationBuffer, _headData->_blendshapeCoefficients.data(), _headData->_blendshapeCoefficients.size() * sizeof(float)); destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); + + int numBytes = destinationBuffer - startSection; + _faceTrackerRateOutbound.increment(numBytes); } // If it is connected, pack up the data if (hasJointData) { + auto startSection = destinationBuffer; QReadLocker readLock(&_jointDataLock); // joint rotation data @@ -554,6 +600,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent << (destinationBuffer - startPosition); } #endif + + int numBytes = destinationBuffer - startSection; + _jointDataRateOutbound.increment(numBytes); } int avatarDataSize = destinationBuffer - startPosition; @@ -1042,6 +1091,30 @@ float AvatarData::getDataRate(const QString& rateName) { return _faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointData") { return _jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "globalPositionOutbound") { + return _globalPositionRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "localPositionOutbound") { + return _localPositionRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "avatarBoundingBoxOutbound") { + return _avatarBoundingBoxRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "avatarOrientationOutbound") { + return _avatarOrientationRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "avatarScaleOutbound") { + return _avatarScaleRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "lookAtPositionOutbound") { + return _lookAtPositionRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "audioLoudnessOutbound") { + return _audioLoudnessRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "sensorToWorkMatrixOutbound") { + return _sensorToWorldRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "additionalFlagsOutbound") { + return _additionalFlagsRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "parentInfoOutbound") { + return _parentInfoRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "faceTrackerOutbound") { + return _faceTrackerRateOutbound.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDataOutbound") { + return _jointDataRateOutbound.rate() / BYTES_PER_KILOBIT; } return 0.0f; } @@ -1886,8 +1959,7 @@ QJsonObject AvatarData::toJson() const { return root; } -void AvatarData::fromJson(const QJsonObject& json) { - +void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { int version; if (json.contains(JSON_AVATAR_VERSION)) { version = json[JSON_AVATAR_VERSION].toInt(); @@ -1909,7 +1981,7 @@ void AvatarData::fromJson(const QJsonObject& json) { if (json.contains(JSON_AVATAR_BODY_MODEL)) { auto bodyModelURL = json[JSON_AVATAR_BODY_MODEL].toString(); - if (bodyModelURL != getSkeletonModelURL().toString()) { + if (useFrameSkeleton && bodyModelURL != getSkeletonModelURL().toString()) { setSkeletonModelURL(bodyModelURL); } } @@ -2002,8 +2074,9 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { } -void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { +void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result, bool useFrameSkeleton) { QJsonDocument doc = QJsonDocument::fromBinaryData(frameData); + #ifdef WANT_JSON_DEBUG { QJsonObject obj = doc.object(); @@ -2011,7 +2084,7 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { qCDebug(avatars).noquote() << QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Indented); } #endif - result.fromJson(doc.object()); + result.fromJson(doc.object(), useFrameSkeleton); } float AvatarData::getBodyYaw() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 4c24cf6ea6..d23e4f5caf 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -329,7 +329,7 @@ public: static const QString FRAME_NAME; - static void fromFrame(const QByteArray& frameData, AvatarData& avatar); + static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true); static QByteArray toFrame(const AvatarData& avatar); AvatarData(); @@ -380,8 +380,27 @@ public: void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. virtual void updateAttitude() {} // Tell skeleton mesh about changes - glm::quat getHeadOrientation() const { return _headData->getOrientation(); } - void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); } + glm::quat getHeadOrientation() { + lazyInitHeadData(); + return _headData->getOrientation(); + } + void setHeadOrientation(const glm::quat& orientation) { + if (_headData) { + _headData->setOrientation(orientation); + } + } + + void setLookAtPosition(const glm::vec3& lookAtPosition) { + if (_headData) { + _headData->setLookAtPosition(lookAtPosition); + } + } + + void setBlendshapeCoefficients(const QVector& blendshapeCoefficients) { + if (_headData) { + _headData->setBlendshapeCoefficients(blendshapeCoefficients); + } + } // access to Head().set/getMousePitch (degrees) float getHeadPitch() const { return _headData->getBasePitch(); } @@ -513,7 +532,7 @@ public: TransformPointer getRecordingBasis() const; void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); QJsonObject toJson() const; - void fromJson(const QJsonObject& json); + void fromJson(const QJsonObject& json, bool useFrameSkeleton = true); glm::vec3 getClientGlobalPosition() { return _globalPosition; } glm::vec3 getGlobalBoundingBoxCorner() { return _globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions; } @@ -597,7 +616,7 @@ protected: bool _forceFaceTrackerConnected; bool _hasNewJointData; // set in AvatarData, cleared in Avatar - HeadData* _headData; + HeadData* _headData { nullptr }; QUrl _skeletonModelURL; bool _firstSkeletonCheck { true }; @@ -675,6 +694,20 @@ protected: RateCounter<> _faceTrackerUpdateRate; RateCounter<> _jointDataUpdateRate; + // Some rate data for outgoing data + RateCounter<> _globalPositionRateOutbound; + RateCounter<> _localPositionRateOutbound; + RateCounter<> _avatarBoundingBoxRateOutbound; + RateCounter<> _avatarOrientationRateOutbound; + RateCounter<> _avatarScaleRateOutbound; + RateCounter<> _lookAtPositionRateOutbound; + RateCounter<> _audioLoudnessRateOutbound; + RateCounter<> _sensorToWorldRateOutbound; + RateCounter<> _additionalFlagsRateOutbound; + RateCounter<> _parentInfoRateOutbound; + RateCounter<> _faceTrackerRateOutbound; + RateCounter<> _jointDataRateOutbound; + glm::vec3 _globalBoundingBoxDimensions; glm::vec3 _globalBoundingBoxOffset; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2e6544fbdb..fcaef90527 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -537,6 +537,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS FBXGeometry* geometryPtr = new FBXGeometry; FBXGeometry& geometry = *geometryPtr; + geometry.originalURL = url; + float unitScaleFactor = 1.0f; glm::vec3 ambientColor; QString hifiGlobalNodeID; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index b0e34dd674..5910b8d312 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -288,6 +288,7 @@ class FBXGeometry { public: using Pointer = std::shared_ptr; + QString originalURL; QString author; QString applicationName; ///< the name of the application that generated the model diff --git a/script-archive/acScripts/BetterClientSimulationBotFromRecording.js b/script-archive/acScripts/BetterClientSimulationBotFromRecording.js new file mode 100644 index 0000000000..fafb761729 --- /dev/null +++ b/script-archive/acScripts/BetterClientSimulationBotFromRecording.js @@ -0,0 +1,183 @@ +// +// BetterClientSimulationBotFromRecording.js +// examples +// +// Created by Brad Hefta-Gaub on 2/6/17. +// 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 +// + + +var WANT_DEBUGGING = false; + +randFloat = function(low, high) { + return low + Math.random() * (high - low); +} + +var AVATARS_ARRAY = [ + "http://mpassets.highfidelity.com/0c2c264b-2fd2-46a4-bf80-de681881f66b-v1/F_MotRac.fst", + "http://mpassets.highfidelity.com/bd80a6d7-7173-489e-87c6-f7ee56e65530-v1/M_RetFut.fst", + "http://mpassets.highfidelity.com/47c8d706-d486-4c2d-afcc-70d4e1e25117-v1/M_RetSpaSuit.fst", + "http://mpassets.highfidelity.com/548d0792-0bac-4933-bbfc-57d71912d77e-v1/M_OutMer.fst", + "http://mpassets.highfidelity.com/13277c09-892f-4a5e-b9a5-8994a37d68bf-v1/F_WasWar.fst", + "http://mpassets.highfidelity.com/2d384111-0f0e-42e2-b800-66bfcab4aefb-v1/F_VooQue.fst", + "http://mpassets.highfidelity.com/57e4d1cd-9f52-4c95-9051-326f9bb114ea-v1/F_SteAvi.fst", + "http://mpassets.highfidelity.com/da2ad4cd-47d4-41da-b764-41f39ff77e30-v1/F_JerGir.fst", + "http://mpassets.highfidelity.com/96c747ab-f71b-44ee-8eb9-d19fc9593dda-v1/F_CatBur.fst", + "http://mpassets.highfidelity.com/ede82c38-c66e-4f67-9e0b-0bb0782db18f-v1/M_WesOut.fst", + "http://mpassets.highfidelity.com/8872ae86-a763-4db3-8373-d27514c1481e-v1/M_VinAvi.fst", + "http://mpassets.highfidelity.com/faf505f1-4fd1-4ed2-8909-816af246c48f-v1/M_VicGen.fst", + "http://mpassets.highfidelity.com/d807a7d2-5122-4436-a6f9-3173c94d1c49-v1/M_SuaGen.fst", + "http://mpassets.highfidelity.com/1dd41735-06f4-45a3-9ec0-d05215ace77b-v1/M_MarSen.fst", + "http://mpassets.highfidelity.com/2cad3894-8ab3-4ba5-a723-0234f93fbd6a-v1/M_BowBea.fst", + "http://mpassets.highfidelity.com/cf0eb1be-9ec7-4756-8eaf-ac8f3ec09eba-v1/F_ClaDef.fst", + "http://mpassets.highfidelity.com/0cedeca3-c1a4-4be9-9fd5-dad716afcc7e-v1/F_Cyria.fst", + "http://mpassets.highfidelity.com/dc55803b-9215-47dd-9408-eb835dac4082-v1/F_ParGir.fst", + "http://mpassets.highfidelity.com/775a8fb3-cfe7-494d-b603-a0a2d6910e55-v1/F_VinCov.fst", + "http://mpassets.highfidelity.com/eba0d8f8-aa72-4a6b-ab64-4d3fd4695b20-v1/F_VogHei.fst", + "http://mpassets.highfidelity.com/4f400c78-38f9-42af-b03b-11b5451d41b9-v1/M_MidRog.fst", + "http://mpassets.highfidelity.com/ad774d79-13f1-46e2-87c9-de49a261b264-v1/F_GunSli.fst", + "http://mpassets.highfidelity.com/5acbaefa-5455-49a2-8d40-89d12aa393ca-v1/M_KniWol.fst", + "http://mpassets.highfidelity.com/aaa1b0a8-3e1b-492a-9aee-600e5dc907db-v1/F_RetSciSuit.fst", + "http://mpassets.highfidelity.com/d8da10b6-25c1-40e2-9a66-369316c722d7-v1/F_AniSuit.fst", + "http://mpassets.highfidelity.com/f3fbb9f4-e159-49ed-ac32-03af9056b17e-v1/matthew.fst", + "http://mpassets.highfidelity.com/0c954ba0-4d87-4353-b65e-c45509f85658-v1/priscilla.fst", + "http://mpassets.highfidelity.com/e76946cc-c272-4adf-9bb6-02cde0a4b57d-v1/9e8c5c42a0cbd436962d6bd36f032ab3.fst", + "http://mpassets.highfidelity.com/72e083ee-194d-4113-9c61-0591d8257493-v1/skeleton_Rigged.fst", + "http://mpassets.highfidelity.com/f14bf7c9-49a1-4249-988a-0a577ed78957-v1/beingOfLight.fst", + "http://mpassets.highfidelity.com/1b7e1e7c-6c0b-4f20-9cd0-1d5ccedae620-v1/bb64e937acf86447f6829767e958073c.fst", + "http://mpassets.highfidelity.com/67d7c7aa-c300-4d03-85f4-86480130eaa5-v1/F_StarCrew.fst", + "http://mpassets.highfidelity.com/d293ef06-c659-467a-9288-c3cbaff0372a-v1/arya_avatar.fst", + "http://mpassets.highfidelity.com/faf249d5-12a8-48e2-a08e-fb0c33087011-v1/F_Ranger.fst", + "http://mpassets.highfidelity.com/b4502145-15eb-4023-b7d6-a81c5cbf6abf-v1/F_FitTra.fst", + "http://mpassets.highfidelity.com/548d0792-0bac-4933-bbfc-57d71912d77e-v1/M_OutMer.fst", + "http://mpassets.highfidelity.com/caa61e5d-5629-4165-81d8-6a7eb55e942d-v1/F_DeaSur.fst", + "http://mpassets.highfidelity.com/2cad3894-8ab3-4ba5-a723-0234f93fbd6a-v1/M_BowBea.fst", + "http://mpassets.highfidelity.com/fd4fa45a-9d2a-463e-a484-f9d1b3bba724-v1/M_BeaWar.fst", + "http://mpassets.highfidelity.com/367a5b60-8a92-4d56-a152-a00f3086f02b-v1/M_Espio.fst", + "http://mpassets.highfidelity.com/ab466729-31da-4b4c-a33c-366f7c1d38e5-v1/M_MMAFig.fst", + "http://mpassets.highfidelity.com/b0795a0c-493d-4abd-b4cc-5f32e6d6df46-v1/M_SalMer.fst", + "http://mpassets.highfidelity.com/0a1d44bf-a988-4199-b29e-a532ab85a2e8-v1/M_StaShi.fst", + "http://mpassets.highfidelity.com/d807a7d2-5122-4436-a6f9-3173c94d1c49-v1/M_SuaGen.fst", + "http://mpassets.highfidelity.com/cb20212c-36f2-4d41-bdad-132361ca6ff4-v1/M_TreTee.fst", + "http://mpassets.highfidelity.com/830988dc-619a-4e88-96e1-a19fa0aaa30f-v1/M_UrbEnf.fst", + "http://mpassets.highfidelity.com/faf505f1-4fd1-4ed2-8909-816af246c48f-v1/M_VicGen.fst", + "http://mpassets.highfidelity.com/883ac86f-dd29-4676-8bda-7dd52fb6465f-v1/M_WasWan.fst", + "http://mpassets.highfidelity.com/ede82c38-c66e-4f67-9e0b-0bb0782db18f-v1/M_WesOut.fst", + "http://mpassets.highfidelity.com/04c9a1e9-0390-4a7f-b6c6-5f135c19e3fb-v1/F_ArmTro.fst", + "http://mpassets.highfidelity.com/e863348f-a777-4f36-86e6-af6e65ffa161-v1/F_BloSam.fst", + "http://mpassets.highfidelity.com/cf0eb1be-9ec7-4756-8eaf-ac8f3ec09eba-v1/F_ClaDef.fst", + "http://mpassets.highfidelity.com/0cedeca3-c1a4-4be9-9fd5-dad716afcc7e-v1/F_Cyria.fst", + "http://mpassets.highfidelity.com/da2ad4cd-47d4-41da-b764-41f39ff77e30-v1/F_JerGir.fst", + "http://mpassets.highfidelity.com/534d42f8-ec13-4145-929f-5c8facac2fb7-v1/F_LegFig.fst", + "http://mpassets.highfidelity.com/dc55803b-9215-47dd-9408-eb835dac4082-v1/F_ParGir.fst", + "http://mpassets.highfidelity.com/f823e831-d8c4-4191-a3bd-427e406e69f9-v1/F_Shinjuku.fst", + "http://mpassets.highfidelity.com/eba0d8f8-aa72-4a6b-ab64-4d3fd4695b20-v1/F_VogHei.fst", + "http://mpassets.highfidelity.com/13277c09-892f-4a5e-b9a5-8994a37d68bf-v1/F_WasWar.fst", + "http://mpassets.highfidelity.com/9b589fbb-59e4-47a9-8b3f-bf8d3a0bd1d8-v1/M_LawSur.fst", + "http://mpassets.highfidelity.com/4f400c78-38f9-42af-b03b-11b5451d41b9-v1/M_MidRog.fst", + "http://mpassets.highfidelity.com/c90d755d-0456-48fd-b98c-09c4d85cd481-v1/M_MouOff.fst", + "http://mpassets.highfidelity.com/c2ed3b9a-b3a9-4424-9fd2-8a798209f32b-v1/M_PerTra.fst", + "http://mpassets.highfidelity.com/c48928ac-7657-41f4-bbdc-9b47385736ab-v1/M_SpaMar.fst", + "http://mpassets.highfidelity.com/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73-v1/4618d52e711fbb34df442b414da767bb.fst", + "http://mpassets.highfidelity.com/c85c497d-c87b-42b1-9bbf-5405e05a0ad3-v1/M_ArmSol.fst", + "http://mpassets.highfidelity.com/1dd41735-06f4-45a3-9ec0-d05215ace77b-v1/M_MarSen.fst", + "http://mpassets.highfidelity.com/bd80a6d7-7173-489e-87c6-f7ee56e65530-v1/M_RetFut.fst", + "http://mpassets.highfidelity.com/8872ae86-a763-4db3-8373-d27514c1481e-v1/M_VinAvi.fst", + "http://mpassets.highfidelity.com/f798d926-9a9e-481a-b298-af0e45451252-v1/F_Assassin.fst", + "http://mpassets.highfidelity.com/ad774d79-13f1-46e2-87c9-de49a261b264-v1/F_GunSli.fst", + "http://mpassets.highfidelity.com/aaa1b0a8-3e1b-492a-9aee-600e5dc907db-v1/F_RetSciSuit.fst" + ]; + + +var AVATAR_URL = AVATARS_ARRAY[Math.floor(Math.random() * AVATARS_ARRAY.length)]; +print("RANDOM AVATAR SELECTED:" + AVATAR_URL); + +var RECORDINGS_ARRAY = [ + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting6.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting7.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/waiting10.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot1.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot2.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot3.hfr", + "http://hifi-content.s3.amazonaws.com/DomainContent/Event%20/NPC%27s/bot4.hfr" + ]; + +var RECORDING_URL = RECORDINGS_ARRAY[Math.floor(Math.random() * RECORDINGS_ARRAY.length)]; +print("RANDOM RECORDING SELECTED:" + RECORDING_URL); + +// not quite what I want... +var LOCATIONS_ARRAY = [ + { min_x: 97.0, max_x: 103.0, y:-0.6, min_z: 30.8, max_z: 40 }, + { min_x: 92.7, max_x: 106.6, y:-0.3, min_z: 43 , max_z: 43 }, + { min_x: 92.7, max_x: 106.6, y: 0.3, min_z: 45 , max_z: 45 }, + { min_x: 92.7, max_x: 106.6, y: 1 , min_z: 47 , max_z: 47 }, + { min_x: 92.7, max_x: 106.6, y: 1.7, min_z: 51.9, max_z: 51.9 }, +]; + +var LOCATION_PARAMS = LOCATIONS_ARRAY[Math.floor(Math.random() * LOCATIONS_ARRAY.length)]; + +var LOCATION = { x: randFloat(LOCATION_PARAMS.min_x, LOCATION_PARAMS.max_x), y: LOCATION_PARAMS.y, z: randFloat(LOCATION_PARAMS.min_z, LOCATION_PARAMS.max_z) }; + +Vec3.print("RANDOM LOCATION SELECTED:", LOCATION); + +var playFromCurrentLocation = true; +var loop = true; + + +// Set position here if playFromCurrentLocation is true +Avatar.position = LOCATION; +Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); +Avatar.scale = 1.0; +Agent.isAvatar = true; + +// make the agent "listen" to the audio stream to cause additional audio-mixer load, technically this isn't needed when you're playing a recording +// but if you switch to a non-recording bot, you will need this, so we can leave this. +Agent.isListeningToAudioStream = true; +Avatar.skeletonModelURL = AVATAR_URL; // FIXME - currently setting an avatar while playing a recording doesn't work it will be ignored + +Recording.loadRecording(RECORDING_URL); + +count = 300; // This is necessary to wait for the audio mixer to connect +function update(event) { + if (count > 0) { + count--; + return; + } + if (count == 0) { + Recording.setPlayFromCurrentLocation(playFromCurrentLocation); + Recording.setPlayerLoop(loop); + Recording.setPlayerUseDisplayName(true); + Recording.setPlayerUseAttachments(true); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(false); // FIXME - this would allow you to override the recording avatar, but that's not currently working + Recording.startPlaying(); + Vec3.print("Playing from ", Avatar.position); + count--; + } else if (WANT_DEBUGGING) { + count = 100; + Vec3.print("Avatar at: ", Avatar.position); + Quat.print("Avatar head orientation: ", Avatar.headOrientation); + print("outbound:" + +" GP: " + Avatar.getDataRate("globalPositionOutbound").toFixed(2) + "\n" + +" LP: " + Avatar.getDataRate("localPositionOutbound").toFixed(2) + "\n" + +" BB: " + Avatar.getDataRate("avatarBoundingBoxOutbound").toFixed(2) + "\n" + +" AO: " + Avatar.getDataRate("avatarOrientationOutbound").toFixed(2) + "\n" + +" AS: " + Avatar.getDataRate("avatarScaleOutbound").toFixed(2) + "\n" + +" LA: " + Avatar.getDataRate("lookAtPositionOutbound").toFixed(2) + "\n" + +" AL: " + Avatar.getDataRate("audioLoudnessOutbound").toFixed(2) + "\n" + +" SW: " + Avatar.getDataRate("sensorToWorkMatrixOutbound").toFixed(2) + "\n" + +" AF: " + Avatar.getDataRate("additionalFlagsOutbound").toFixed(2) + "\n" + +" PI: " + Avatar.getDataRate("parentInfoOutbound").toFixed(2) + "\n" + +" FT: " + Avatar.getDataRate("faceTrackerOutbound").toFixed(2) + "\n" + +" JD: " + Avatar.getDataRate("jointDataOutbound").toFixed(2)); + } + + if (!Recording.isPlaying()) { + Script.update.disconnect(update); + } +} + +Script.update.connect(update);