diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 6ca93a7b11..4d0e1bf19c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include "AudioRingBuffer.h" @@ -61,7 +61,7 @@ const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18; - +const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.001f; const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; @@ -78,12 +78,17 @@ bool AudioMixer::_printStreamStats = false; bool AudioMixer::_enableFilter = true; +bool AudioMixer::shouldMute(float quietestFrame) { + return (quietestFrame > _noiseMutingThreshold); +} + AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), _trailingSleepRatio(1.0f), _minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f), _performanceThrottlingRatio(0.0f), _attenuationPerDoublingInDistance(DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE), + _noiseMutingThreshold(DEFAULT_NOISE_MUTING_THRESHOLD), _numStatFrames(0), _sumListeners(0), _sumMixes(0), @@ -353,7 +358,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l } } - if (!sourceIsSelf && _enableFilter) { + if (!sourceIsSelf && _enableFilter && !streamToAdd->ignorePenumbraFilter()) { const float TWO_OVER_PI = 2.0f / PI; @@ -400,15 +405,12 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData* l penumbraFilterGainR += (1.f - penumbraFilterGainR) * (1.f - distanceBetween / RADIUS_OF_HEAD); } - -#if 0 - qDebug() << "gainL=" - << penumbraFilterGainL - << "gainR=" - << penumbraFilterGainR - << "angle=" - << -bearingRelativeAngleToSource; -#endif + bool wantDebug = false; + if (wantDebug) { + qDebug() << "gainL=" << penumbraFilterGainL + << "gainR=" << penumbraFilterGainR + << "angle=" << -bearingRelativeAngleToSource; + } // Get our per listener/source data so we can get our filter AudioFilterHSF1s& penumbraFilter = listenerNodeData->getListenerSourcePairData(streamUUID)->getPenumbraFilter(); @@ -463,6 +465,63 @@ int AudioMixer::prepareMixForListeningNode(Node* node) { return streamsMixed; } +void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { + static char clientEnvBuffer[MAX_PACKET_SIZE]; + + // Send stream properties + bool hasReverb = false; + float reverbTime, wetLevel; + // find reverb properties + for (int i = 0; i < _zoneReverbSettings.size(); ++i) { + AudioMixerClientData* data = static_cast(node->getLinkedData()); + glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition(); + if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) { + hasReverb = true; + reverbTime = _zoneReverbSettings[i].reverbTime; + wetLevel = _zoneReverbSettings[i].wetLevel; + break; + } + } + AudioMixerClientData* nodeData = static_cast(node->getLinkedData()); + AvatarAudioStream* stream = nodeData->getAvatarAudioStream(); + bool dataChanged = (stream->hasReverb() != hasReverb) || + (stream->hasReverb() && (stream->getRevebTime() != reverbTime || + stream->getWetLevel() != wetLevel)); + if (dataChanged) { + // Update stream + if (hasReverb) { + stream->setReverb(reverbTime, wetLevel); + } else { + stream->clearReverb(); + } + } + + // Send at change or every so often + float CHANCE_OF_SEND = 0.01f; + bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); + + if (sendData) { + int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); + char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; + + unsigned char bitset = 0; + if (hasReverb) { + setAtBit(bitset, HAS_REVERB_BIT); + } + + memcpy(envDataAt, &bitset, sizeof(unsigned char)); + envDataAt += sizeof(unsigned char); + + if (hasReverb) { + memcpy(envDataAt, &reverbTime, sizeof(float)); + envDataAt += sizeof(float); + memcpy(envDataAt, &wetLevel, sizeof(float)); + envDataAt += sizeof(float); + } + NodeList::getInstance()->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); + } +} + void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { NodeList* nodeList = NodeList::getInstance(); @@ -640,7 +699,6 @@ void AudioMixer::run() { timer.start(); char clientMixBuffer[MAX_PACKET_SIZE]; - char clientEnvBuffer[MAX_PACKET_SIZE]; int usecToSleep = BUFFER_SEND_INTERVAL_USECS; @@ -715,6 +773,29 @@ void AudioMixer::run() { // That's how the popped audio data will be read for mixing (but only if the pop was successful) nodeData->checkBuffersBeforeFrameSend(); + // if the stream should be muted, send mute packet + if (shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { + static const int TIME_BETWEEN_MUTES = 5; // in secs + if (usecTimestampNow() - nodeData->getAvatarAudioStream()->getLastMuted() > + TIME_BETWEEN_MUTES * USECS_PER_SECOND) { + int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); + int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float); + + // Fake data to force mute + glm::vec3 position = nodeData->getAvatarAudioStream()->getPosition(); + float radius = 1.0f; + + char* packet = (char*)malloc(packetSize); + populatePacketHeader(packet, PacketTypeMuteEnvironment); + memcpy(packet + headerSize, &position, sizeof(glm::vec3)); + memcpy(packet + headerSize + sizeof(glm::vec3), &radius, sizeof(float)); + + nodeList->writeDatagram(packet, packetSize, node); + nodeData->getAvatarAudioStream()->setLastMutedNow(); + free(packet); + } + } + if (node->getType() == NodeType::Agent && node->getActiveSocket() && nodeData->getAvatarAudioStream()) { @@ -734,58 +815,6 @@ void AudioMixer::run() { // pack mixed audio samples memcpy(mixDataAt, _mixSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); mixDataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO; - - // Send stream properties - bool hasReverb = false; - float reverbTime, wetLevel; - // find reverb properties - for (int i = 0; i < _zoneReverbSettings.size(); ++i) { - AudioMixerClientData* data = static_cast(node->getLinkedData()); - glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition(); - if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) { - hasReverb = true; - reverbTime = _zoneReverbSettings[i].reverbTime; - wetLevel = _zoneReverbSettings[i].wetLevel; - break; - } - } - AvatarAudioStream* stream = nodeData->getAvatarAudioStream(); - bool dataChanged = (stream->hasReverb() != hasReverb) || - (stream->hasReverb() && (stream->getRevebTime() != reverbTime || - stream->getWetLevel() != wetLevel)); - if (dataChanged) { - // Update stream - if (hasReverb) { - stream->setReverb(reverbTime, wetLevel); - } else { - stream->clearReverb(); - } - } - - // Send at change or every so often - float CHANCE_OF_SEND = 0.01f; - bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); - - if (sendData) { - int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); - char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; - - unsigned char bitset = 0; - if (hasReverb) { - setAtBit(bitset, HAS_REVERB_BIT); - } - - memcpy(envDataAt, &bitset, sizeof(unsigned char)); - envDataAt += sizeof(unsigned char); - - if (hasReverb) { - memcpy(envDataAt, &reverbTime, sizeof(float)); - envDataAt += sizeof(float); - memcpy(envDataAt, &wetLevel, sizeof(float)); - envDataAt += sizeof(float); - } - nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); - } } else { // pack header int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); @@ -801,6 +830,9 @@ void AudioMixer::run() { memcpy(mixDataAt, &numSilentSamples, sizeof(quint16)); mixDataAt += sizeof(quint16); } + + // Send audio environment + sendAudioEnvironmentPacket(node); // send mixed audio packet nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node); @@ -1003,7 +1035,17 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { qDebug() << "Attenuation per doubling in distance changed to" << _attenuationPerDoublingInDistance; } } - + + const QString NOISE_MUTING_THRESHOLD = "noise_muting_threshold"; + if (audioEnvGroupObject[NOISE_MUTING_THRESHOLD].isString()) { + bool ok = false; + float noiseMutingThreshold = audioEnvGroupObject[NOISE_MUTING_THRESHOLD].toString().toFloat(&ok); + if (ok) { + _noiseMutingThreshold = noiseMutingThreshold; + qDebug() << "Noise muting threshold changed to" << _noiseMutingThreshold; + } + } + const QString FILTER_KEY = "enable_filter"; if (audioEnvGroupObject[FILTER_KEY].isBool()) { _enableFilter = audioEnvGroupObject[FILTER_KEY].toBool(); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index ff976dec61..cc88e368b2 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -49,6 +49,9 @@ private: /// prepares and sends a mix to one Node int prepareMixForListeningNode(Node* node); + + /// Send Audio Environment packet for a single node + void sendAudioEnvironmentPacket(SharedNodePointer node); // used on a per stream basis to run the filter on before mixing, large enough to handle the historical // data from a phase delay as well as an entire network buffer @@ -59,6 +62,8 @@ private: int16_t _mixSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; void perSecondActions(); + + bool shouldMute(float quietestFrame); QString getReadPendingDatagramsCallsPerSecondsStatsString() const; QString getReadPendingDatagramsPacketsPerCallStatsString() const; @@ -71,6 +76,7 @@ private: float _minAudibilityThreshold; float _performanceThrottlingRatio; float _attenuationPerDoublingInDistance; + float _noiseMutingThreshold; int _numStatFrames; int _sumListeners; int _sumMixes; diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index 90dcefa09d..cd67722a2e 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -14,7 +14,8 @@ #include "AvatarAudioStream.h" AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings) : - PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, settings) + PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, settings), + _lastMuted(usecTimestampNow()) { } diff --git a/assignment-client/src/audio/AvatarAudioStream.h b/assignment-client/src/audio/AvatarAudioStream.h index cc2ff1aca7..e1fb6dd486 100644 --- a/assignment-client/src/audio/AvatarAudioStream.h +++ b/assignment-client/src/audio/AvatarAudioStream.h @@ -19,13 +19,18 @@ class AvatarAudioStream : public PositionalAudioStream { public: AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings); - + + qint64 getLastMuted() const { return _lastMuted; } + void setLastMutedNow() { _lastMuted = usecTimestampNow(); } + private: // disallow copying of AvatarAudioStream objects AvatarAudioStream(const AvatarAudioStream&); AvatarAudioStream& operator= (const AvatarAudioStream&); int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples); + + qint64 _lastMuted; }; #endif // hifi_AvatarAudioStream_h diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index dc19f33fab..563efed288 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -34,6 +34,7 @@ public: virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; } virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; } virtual PacketType getMyEditNackType() const { return PacketTypeEntityEditNack; } + virtual QString getMyDomainSettingsKey() const { return QString("entity_server_settings"); } // subclass may implement these method virtual void beforeRun(); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index bd3a7ceee5..b9bde8d544 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -98,15 +98,28 @@ void OctreeInboundPacketProcessor::processPacket(const SharedNodePointer& sendin unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); quint64 sentAt = (*((quint64*)(packetData + numBytesPacketHeader + sizeof(sequence)))); quint64 arrivedAt = usecTimestampNow(); + if (sentAt > arrivedAt) { + if (debugProcessPacket || _myServer->wantsDebugReceiving()) { + qDebug() << "unreasonable sentAt=" << sentAt << " usecs"; + qDebug() << "setting sentAt to arrivedAt=" << arrivedAt << " usecs"; + } + sentAt = arrivedAt; + } quint64 transitTime = arrivedAt - sentAt; int editsInPacket = 0; quint64 processTime = 0; quint64 lockWaitTime = 0; if (debugProcessPacket || _myServer->wantsDebugReceiving()) { - qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount - << " command from client receivedBytes=" << packet.size() - << " sequence=" << sequence << " transitTime=" << transitTime << " usecs"; + qDebug() << "PROCESSING THREAD: got '" << packetType << "' packet - " << _receivedPacketCount << " command from client"; + qDebug() << " receivedBytes=" << packet.size(); + qDebug() << " sequence=" << sequence; + qDebug() << " sentAt=" << sentAt << " usecs"; + qDebug() << " arrivedAt=" << arrivedAt << " usecs"; + qDebug() << " transitTime=" << transitTime << " usecs"; + qDebug() << " sendingNode->getClockSkewUsec()=" << sendingNode->getClockSkewUsec() << " usecs"; + + } if (debugProcessPacket) { diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index f905ae652f..46ea49e9a7 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -452,11 +452,10 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus _myServer->getOctree()->lockForRead(); quint64 lockWaitEnd = usecTimestampNow(); lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); - quint64 encodeStart = usecTimestampNow(); bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params); - + quint64 encodeEnd = usecTimestampNow(); encodeElapsedUsec = (float)(encodeEnd - encodeStart); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 121cac0273..26cf94269b 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -900,85 +900,206 @@ void OctreeServer::setupDatagramProcessingThread() { // start the datagram processing thread _datagramProcessingThread->start(); +} +bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) { + result = false; // assume it doesn't exist + bool optionAvailable = false; + QString argName = "--" + optionName; + bool argExists = cmdOptionExists(_argc, _argv, qPrintable(argName)); + if (argExists) { + optionAvailable = true; + result = argExists; + qDebug() << "From payload arguments: " << qPrintable(argName) << ":" << result; + } else if (settingsSectionObject.contains(optionName)) { + optionAvailable = true; + result = settingsSectionObject[optionName].toBool(); + qDebug() << "From domain settings: " << qPrintable(optionName) << ":" << result; + } + return optionAvailable; +} + +bool OctreeServer::readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result) { + bool optionAvailable = false; + QString argName = "--" + optionName; + const char* argValue = getCmdOption(_argc, _argv, qPrintable(argName)); + if (argValue) { + optionAvailable = true; + result = atoi(argValue); + qDebug() << "From payload arguments: " << qPrintable(argName) << ":" << result; + } else if (settingsSectionObject.contains(optionName)) { + optionAvailable = true; + result = settingsSectionObject[optionName].toString().toInt(&optionAvailable); + if (optionAvailable) { + qDebug() << "From domain settings: " << qPrintable(optionName) << ":" << result; + } + } + return optionAvailable; +} + +bool OctreeServer::readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result) { + bool optionAvailable = false; + QString argName = "--" + optionName; + const char* argValue = getCmdOption(_argc, _argv, qPrintable(argName)); + if (argValue) { + optionAvailable = true; + result = QString(argValue); + qDebug() << "From payload arguments: " << qPrintable(argName) << ":" << qPrintable(result); + } else if (settingsSectionObject.contains(optionName)) { + optionAvailable = true; + result = settingsSectionObject[optionName].toString(); + qDebug() << "From domain settings: " << qPrintable(optionName) << ":" << qPrintable(result); + } + return optionAvailable; +} + +void OctreeServer::readConfiguration() { + // if the assignment had a payload, read and parse that + if (getPayload().size() > 0) { + parsePayload(); + } + + // wait until we have the domain-server settings, otherwise we bail + NodeList* nodeList = NodeList::getInstance(); + DomainHandler& domainHandler = nodeList->getDomainHandler(); + + qDebug() << "Waiting for domain settings from domain-server."; + + // block until we get the settingsRequestComplete signal + QEventLoop loop; + connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); + domainHandler.requestDomainSettings(); + loop.exec(); + + if (domainHandler.getSettingsObject().isEmpty()) { + qDebug() << "No settings object from domain-server."; + } + const QJsonObject& settingsObject = domainHandler.getSettingsObject(); + QString settingsKey = getMyDomainSettingsKey(); + QJsonObject settingsSectionObject = settingsObject[settingsKey].toObject(); + + if (!readOptionString(QString("statusHost"), settingsSectionObject, _statusHost) || _statusHost.isEmpty()) { + _statusHost = getLocalAddress().toString(); + } + qDebug("statusHost=%s", qPrintable(_statusHost)); + + if (readOptionInt(QString("statusPort"), settingsSectionObject, _statusPort)) { + initHTTPManager(_statusPort); + qDebug() << "statusPort=" << _statusPort; + } else { + qDebug() << "statusPort= DISABLED"; + } + + QString jurisdictionFile; + if (readOptionString(QString("jurisdictionFile"), settingsSectionObject, jurisdictionFile)) { + qDebug("jurisdictionFile=%s", qPrintable(jurisdictionFile)); + qDebug("about to readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile)); + _jurisdiction = new JurisdictionMap(qPrintable(jurisdictionFile)); + qDebug("after readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile)); + } else { + QString jurisdictionRoot; + bool hasRoot = readOptionString(QString("jurisdictionRoot"), settingsSectionObject, jurisdictionRoot); + QString jurisdictionEndNodes; + bool hasEndNodes = readOptionString(QString("jurisdictionEndNodes"), settingsSectionObject, jurisdictionEndNodes); + + if (hasRoot || hasEndNodes) { + _jurisdiction = new JurisdictionMap(qPrintable(jurisdictionRoot), qPrintable(jurisdictionEndNodes)); + } + } + + readOptionBool(QString("verboseDebug"), settingsSectionObject, _verboseDebug); + qDebug("verboseDebug=%s", debug::valueOf(_verboseDebug)); + + readOptionBool(QString("debugSending"), settingsSectionObject, _debugSending); + qDebug("debugSending=%s", debug::valueOf(_debugSending)); + + readOptionBool(QString("debugReceiving"), settingsSectionObject, _debugReceiving); + qDebug("debugReceiving=%s", debug::valueOf(_debugReceiving)); + + bool noPersist; + readOptionBool(QString("NoPersist"), settingsSectionObject, noPersist); + _wantPersist = !noPersist; + qDebug("wantPersist=%s", debug::valueOf(_wantPersist)); + + if (_wantPersist) { + QString persistFilename; + if (!readOptionString(QString("persistFilename"), settingsSectionObject, persistFilename)) { + persistFilename = getMyDefaultPersistFilename(); + } + strcpy(_persistFilename, qPrintable(persistFilename)); + qDebug("persistFilename=%s", _persistFilename); + } else { + qDebug("persistFilename= DISABLED"); + } + + // Debug option to demonstrate that the server's local time does not + // need to be in sync with any other network node. This forces clock + // skew for the individual server node + int clockSkew; + if (readOptionInt(QString("clockSkew"), settingsSectionObject, clockSkew)) { + usecTimestampNowForceClockSkew(clockSkew); + qDebug("clockSkew=%d", clockSkew); + } + + // Check to see if the user passed in a command line option for setting packet send rate + int packetsPerSecondPerClientMax = -1; + if (readOptionInt(QString("packetsPerSecondPerClientMax"), settingsSectionObject, packetsPerSecondPerClientMax)) { + _packetsPerClientPerInterval = packetsPerSecondPerClientMax / INTERVALS_PER_SECOND; + if (_packetsPerClientPerInterval < 1) { + _packetsPerClientPerInterval = 1; + } + } + qDebug("packetsPerSecondPerClientMax=%d _packetsPerClientPerInterval=%d", + packetsPerSecondPerClientMax, _packetsPerClientPerInterval); + + // Check to see if the user passed in a command line option for setting packet send rate + int packetsPerSecondTotalMax = -1; + if (readOptionInt(QString("packetsPerSecondTotalMax"), settingsSectionObject, packetsPerSecondTotalMax)) { + _packetsTotalPerInterval = packetsPerSecondTotalMax / INTERVALS_PER_SECOND; + if (_packetsTotalPerInterval < 1) { + _packetsTotalPerInterval = 1; + } + } + qDebug("packetsPerSecondTotalMax=%d _packetsTotalPerInterval=%d", + packetsPerSecondTotalMax, _packetsTotalPerInterval); + + + readAdditionalConfiguration(settingsSectionObject); } void OctreeServer::run() { + qInstallMessageHandler(LogHandler::verboseMessageHandler); + _safeServerName = getMyServerName(); - + // Before we do anything else, create our tree... OctreeElement::resetPopulationStatistics(); _tree = createTree(); _tree->setIsServer(true); + + // make sure our NodeList knows what type we are + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(getMyNodeType()); + // use common init to setup common timers and logging commonInit(getMyLoggingServerTargetName(), getMyNodeType()); setupDatagramProcessingThread(); - - // Now would be a good time to parse our arguments, if we got them as assignment - if (getPayload().size() > 0) { - parsePayload(); - } + // read the configuration from either the payload or the domain server configuration + readConfiguration(); + beforeRun(); // after payload has been processed - qInstallMessageHandler(LogHandler::verboseMessageHandler); - - const char* STATUS_PORT = "--statusPort"; - const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); - if (statusPort) { - _statusPort = atoi(statusPort); - initHTTPManager(_statusPort); - } - - const char* STATUS_HOST = "--statusHost"; - const char* statusHost = getCmdOption(_argc, _argv, STATUS_HOST); - if (statusHost) { - qDebug("--statusHost=%s", statusHost); - _statusHost = statusHost; - } else { - _statusHost = getLocalAddress().toString(); - } - qDebug("statusHost=%s", qPrintable(_statusHost)); - - - const char* JURISDICTION_FILE = "--jurisdictionFile"; - const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE); - if (jurisdictionFile) { - qDebug("jurisdictionFile=%s", jurisdictionFile); - - qDebug("about to readFromFile().... jurisdictionFile=%s", jurisdictionFile); - _jurisdiction = new JurisdictionMap(jurisdictionFile); - qDebug("after readFromFile().... jurisdictionFile=%s", jurisdictionFile); - } else { - const char* JURISDICTION_ROOT = "--jurisdictionRoot"; - const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT); - if (jurisdictionRoot) { - qDebug("jurisdictionRoot=%s", jurisdictionRoot); - } - - const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes"; - const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES); - if (jurisdictionEndNodes) { - qDebug("jurisdictionEndNodes=%s", jurisdictionEndNodes); - } - - if (jurisdictionRoot || jurisdictionEndNodes) { - _jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); - } - } - - NodeList* nodeList = NodeList::getInstance(); - nodeList->setOwnerType(getMyNodeType()); - connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)),SLOT(nodeKilled(SharedNodePointer))); // we need to ask the DS about agents so we can ping/reply with them nodeList->addNodeTypeToInterestSet(NodeType::Agent); - + #ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); #endif @@ -987,39 +1108,9 @@ void OctreeServer::run() { srand((unsigned)time(0)); - const char* VERBOSE_DEBUG = "--verboseDebug"; - _verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG); - qDebug("verboseDebug=%s", debug::valueOf(_verboseDebug)); - - const char* DEBUG_SENDING = "--debugSending"; - _debugSending = cmdOptionExists(_argc, _argv, DEBUG_SENDING); - qDebug("debugSending=%s", debug::valueOf(_debugSending)); - - const char* DEBUG_RECEIVING = "--debugReceiving"; - _debugReceiving = cmdOptionExists(_argc, _argv, DEBUG_RECEIVING); - qDebug("debugReceiving=%s", debug::valueOf(_debugReceiving)); - - // By default we will persist, if you want to disable this, then pass in this parameter - const char* NO_PERSIST = "--NoPersist"; - if (cmdOptionExists(_argc, _argv, NO_PERSIST)) { - _wantPersist = false; - } - qDebug("wantPersist=%s", debug::valueOf(_wantPersist)); - // if we want Persistence, set up the local file and persist thread if (_wantPersist) { - // Check to see if the user passed in a command line option for setting packet send rate - const char* PERSIST_FILENAME = "--persistFilename"; - const char* persistFilenameParameter = getCmdOption(_argc, _argv, PERSIST_FILENAME); - if (persistFilenameParameter) { - strcpy(_persistFilename, persistFilenameParameter); - } else { - strcpy(_persistFilename, getMyDefaultPersistFilename()); - } - - qDebug("persistFilename=%s", _persistFilename); - // now set up PersistThread _persistThread = new OctreePersistThread(_tree, _persistFilename); if (_persistThread) { @@ -1027,41 +1118,6 @@ void OctreeServer::run() { } } - // Debug option to demonstrate that the server's local time does not - // need to be in sync with any other network node. This forces clock - // skew for the individual server node - const char* CLOCK_SKEW = "--clockSkew"; - const char* clockSkewOption = getCmdOption(_argc, _argv, CLOCK_SKEW); - if (clockSkewOption) { - int clockSkew = atoi(clockSkewOption); - usecTimestampNowForceClockSkew(clockSkew); - qDebug("clockSkewOption=%s clockSkew=%d", clockSkewOption, clockSkew); - } - - // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND_PER_CLIENT_MAX = "--packetsPerSecondPerClientMax"; - const char* packetsPerSecondPerClientMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_PER_CLIENT_MAX); - if (packetsPerSecondPerClientMax) { - _packetsPerClientPerInterval = atoi(packetsPerSecondPerClientMax) / INTERVALS_PER_SECOND; - if (_packetsPerClientPerInterval < 1) { - _packetsPerClientPerInterval = 1; - } - } - qDebug("packetsPerSecondPerClientMax=%s _packetsPerClientPerInterval=%d", - packetsPerSecondPerClientMax, _packetsPerClientPerInterval); - - // Check to see if the user passed in a command line option for setting packet send rate - const char* PACKETS_PER_SECOND_TOTAL_MAX = "--packetsPerSecondTotalMax"; - const char* packetsPerSecondTotalMax = getCmdOption(_argc, _argv, PACKETS_PER_SECOND_TOTAL_MAX); - if (packetsPerSecondTotalMax) { - _packetsTotalPerInterval = atoi(packetsPerSecondTotalMax) / INTERVALS_PER_SECOND; - if (_packetsTotalPerInterval < 1) { - _packetsTotalPerInterval = 1; - } - } - qDebug("packetsPerSecondTotalMax=%s _packetsTotalPerInterval=%d", - packetsPerSecondTotalMax, _packetsTotalPerInterval); - HifiSockAddr senderSockAddr; // set up our jurisdiction broadcaster... diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 2fcaae8c78..b05896596c 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -69,6 +69,7 @@ public: virtual const char* getMyLoggingServerTargetName() const = 0; virtual const char* getMyDefaultPersistFilename() const = 0; virtual PacketType getMyEditNackType() const = 0; + virtual QString getMyDomainSettingsKey() const { return QString("octree_server_settings"); } // subclass may implement these method virtual void beforeRun() { } @@ -131,6 +132,11 @@ public slots: void readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); protected: + bool readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result); + bool readOptionInt(const QString& optionName, const QJsonObject& settingsSectionObject, int& result); + bool readOptionString(const QString& optionName, const QJsonObject& settingsSectionObject, QString& result); + void readConfiguration(); + virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) { }; void parsePayload(); void initHTTPManager(int port); void resetSendingStats(); diff --git a/assignment-client/src/voxels/VoxelServer.cpp b/assignment-client/src/voxels/VoxelServer.cpp index b021ddd9f6..a83e3e99e3 100644 --- a/assignment-client/src/voxels/VoxelServer.cpp +++ b/assignment-client/src/voxels/VoxelServer.cpp @@ -82,18 +82,18 @@ int VoxelServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNod } -void VoxelServer::beforeRun() { +void VoxelServer::readAdditionalConfiguration(const QJsonObject& settingsSectionObject) { // should we send environments? Default is yes, but this command line suppresses sending - const char* SEND_ENVIRONMENTS = "--sendEnvironments"; - bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS); + readOptionBool(QString("sendEnvironments"), settingsSectionObject, _sendEnvironments); + bool dontSendEnvironments = !_sendEnvironments; if (dontSendEnvironments) { qDebug("Sending environments suppressed..."); - _sendEnvironments = false; } else { - _sendEnvironments = true; // should we send environments? Default is yes, but this command line suppresses sending - const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment"; - _sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT); + //const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment"; + //_sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT); + + readOptionBool(QString("minimalEnvironment"), settingsSectionObject, _sendMinimalEnvironment); qDebug("Using Minimal Environment=%s", debug::valueOf(_sendMinimalEnvironment)); } qDebug("Sending environments=%s", debug::valueOf(_sendEnvironments)); diff --git a/assignment-client/src/voxels/VoxelServer.h b/assignment-client/src/voxels/VoxelServer.h index fadcca2d19..f4b6bd3a42 100644 --- a/assignment-client/src/voxels/VoxelServer.h +++ b/assignment-client/src/voxels/VoxelServer.h @@ -43,12 +43,15 @@ public: virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; } virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; } virtual PacketType getMyEditNackType() const { return PacketTypeVoxelEditNack; } + virtual QString getMyDomainSettingsKey() const { return QString("voxel_server_settings"); } // subclass may implement these method - virtual void beforeRun(); virtual bool hasSpecialPacketToSend(const SharedNodePointer& node); virtual int sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent); +protected: + virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject); + private: bool _sendEnvironments; bool _sendMinimalEnvironment; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 026fe252b2..a49e1072eb 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -89,6 +89,14 @@ "default": "0.18", "advanced": false }, + { + "name": "noise_muting_threshold", + "label": "Noise Muting Threshold", + "help": "Loudness value for noise background between 0 and 1.0 (0: mute everyone, 1.0: never mute)", + "placeholder": "0.001", + "default": "0.001", + "advanced": false + }, { "name": "enable_filter", "type": "checkbox", @@ -262,5 +270,106 @@ "advanced": true } ] - } + }, + { + "name": "entity_server_settings", + "label": "Entity Server Settings", + "assignment-types": [6], + "settings": [ + { + "name": "statusHost", + "label": "Status Hostname", + "help": "host name or IP address of the server for accessing the status page", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "statusPort", + "label": "Status Port", + "help": "port of the server for accessing the status page", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "verboseDebug", + "type": "checkbox", + "help": "lots of debugging", + "default": false, + "advanced": true + }, + { + "name": "debugReceiving", + "type": "checkbox", + "help": "extra debugging on receiving", + "default": false, + "advanced": true + }, + { + "name": "debugSending", + "type": "checkbox", + "help": "extra debugging on sending", + "default": false, + "advanced": true + }, + { + "name": "clockSkew", + "label": "Clock Skew", + "help": "Number of msecs to skew the server clock by to test clock skew", + "placeholder": "0", + "default": "0", + "advanced": true + } + ] + }, + + + { + "name": "voxel_server_settings", + "label": "Voxel Server Settings", + "assignment-types": [3], + "settings": [ + + { + "name": "statusHost", + "label": "Status Hostname", + "help": "host name or IP address of the server for accessing the status page", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "statusPort", + "label": "Status Port", + "help": "port of the server for accessing the status page", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "clockSkew", + "label": "Clock Skew", + "help": "Number of msecs to skew the server clock by to test clock skew", + "placeholder": "0", + "default": "0", + "advanced": true + }, + { + "name": "sendEnvironments", + "type": "checkbox", + "help": "send environmental data", + "default": false, + "advanced": true + }, + { + "name": "minimalEnvironment", + "type": "checkbox", + "help": "send minimal environmental data if sending environmental data", + "default": false, + "advanced": true + } + ] + } + ] \ No newline at end of file diff --git a/examples/birdSongs.js b/examples/birdSongs.js new file mode 100644 index 0000000000..94e013d92b --- /dev/null +++ b/examples/birdSongs.js @@ -0,0 +1,164 @@ +// +// birdSongs.js +// examples +// +// Copyright 2014 High Fidelity, Inc. +// Plays a sample audio file at the avatar's current location +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +// First, load a sample sound from a URL +var birds = []; +var playing = []; + +var lowerCorner = { x: 0, y: 8, z: 0 }; +var upperCorner = { x: 10, y: 10, z: 10 }; + +var RATE = 0.035; +var numPlaying = 0; +var BIRD_SIZE = 0.1; +var BIRD_VELOCITY = 2.0; +var LIGHT_RADIUS = 10.0; +var BIRD_MASTER_VOLUME = 0.5; + +var useLights = true; + +function randomVector(scale) { + return { x: Math.random() * scale - scale / 2.0, y: Math.random() * scale - scale / 2.0, z: Math.random() * scale - scale / 2.0 }; +} + +function maybePlaySound(deltaTime) { + if (Math.random() < RATE) { + // Set the location and other info for the sound to play + var whichBird = Math.floor(Math.random() * birds.length); + //print("playing sound # " + whichBird); + var options = new AudioInjectionOptions(); + var position = { x: lowerCorner.x + Math.random() * (upperCorner.x - lowerCorner.x), + y: lowerCorner.y + Math.random() * (upperCorner.y - lowerCorner.y), + z: lowerCorner.z + Math.random() * (upperCorner.z - lowerCorner.z) }; + options.position = position; + options.volume = BIRD_MASTER_VOLUME; + // + var entityId = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }, + color: birds[whichBird].color, + lifetime: 10 + }); + + if (useLights) { + var lightId = Entities.addEntity({ + type: "Light", + position: position, + dimensions: { x: LIGHT_RADIUS, y: LIGHT_RADIUS, z: LIGHT_RADIUS }, + + isSpotlight: false, + diffuseColor: birds[whichBird].color, + ambientColor: { red: 0, green: 0, blue: 0 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 0, + linearAttenuation: 4.0, + quadraticAttenuation: 2.0, + lifetime: 10 + }); + } + + playing.push({ audioId: Audio.playSound(birds[whichBird].sound, options), entityId: entityId, lightId: lightId, color: birds[whichBird].color }); + } + if (playing.length != numPlaying) { + numPlaying = playing.length; + //print("number playing = " + numPlaying); + } + for (var i = 0; i < playing.length; i++) { + if (!Audio.isInjectorPlaying(playing[i].audioId)) { + Entities.deleteEntity(playing[i].entityId); + if (useLights) { + Entities.deleteEntity(playing[i].lightId); + } + playing.splice(i, 1); + } else { + var loudness = Audio.getLoudness(playing[i].audioId); + var newColor = { red: playing[i].color.red, green: playing[i].color.green, blue: playing[i].color.blue }; + if (loudness > 0.05) { + newColor.red *= (1.0 - loudness); + newColor.green *= (1.0 - loudness); + newColor.blue *= (1.0 - loudness); + } + var properties = Entities.getEntityProperties(playing[i].entityId); + var newPosition = Vec3.sum(properties.position, randomVector(BIRD_VELOCITY * deltaTime)); + if (properties) { + properties.position = newPosition; + Entities.editEntity(playing[i].entityId, { position: properties.position, color: newColor }); + } + if (useLights) { + var lightProperties = Entities.getEntityProperties(playing[i].lightId); + if (lightProperties) { + Entities.editEntity(playing[i].lightId, { position: newPosition, diffuseColor: newColor }); + } + } + } + } +} + +loadBirds(); +// Connect a call back that happens every frame +Script.update.connect(maybePlaySound); + +// Delete our little friends if script is stopped +Script.scriptEnding.connect(function() { + for (var i = 0; i < playing.length; i++) { + Entities.deleteEntity(playing[i].entityId); + if (useLights) { + Entities.deleteEntity(playing[i].lightId); + } + } +}); + +function loadBirds() { + var sound_filenames = ["bushtit_1.raw", "bushtit_2.raw", "bushtit_3.raw", "mexicanWhipoorwill.raw", + "rosyfacedlovebird.raw", "saysphoebe.raw", "westernscreechowl.raw", "bandtailedpigeon.wav", "bridledtitmouse.wav", + "browncrestedflycatcher.wav", "commonnighthawk.wav", "commonpoorwill.wav", "doublecrestedcormorant.wav", + "gambelsquail.wav", "goldcrownedkinglet.wav", "greaterroadrunner.wav","groovebilledani.wav","hairywoodpecker.wav", + "housewren.wav","hummingbird.wav", "mountainchickadee.wav", "nightjar.wav", "piebilledgrieb.wav", "pygmynuthatch.wav", + "whistlingduck.wav", "woodpecker.wav"]; + + var colors = [ + { red: 242, green: 207, blue: 013 }, + { red: 238, green: 94, blue: 11 }, + { red: 81, green: 30, blue: 7 }, + { red: 195, green: 176, blue: 81 }, + { red: 235, green: 190, blue: 152 }, + { red: 167, green: 99, blue: 52 }, + { red: 199, green: 122, blue: 108 }, + { red: 246, green: 220, blue: 189 }, + { red: 208, green: 145, blue: 65 }, + { red: 173, green: 120 , blue: 71 }, + { red: 132, green: 147, blue: 174 }, + { red: 164, green: 74, blue: 40 }, + { red: 131, green: 127, blue: 134 }, + { red: 209, green: 157, blue: 117 }, + { red: 205, green: 191, blue: 193 }, + { red: 193, green: 154, blue: 118 }, + { red: 205, green: 190, blue: 169 }, + { red: 199, green: 111, blue: 69 }, + { red: 221, green: 223, blue: 228 }, + { red: 115, green: 92, blue: 87 }, + { red: 214, green: 165, blue: 137 }, + { red: 160, green: 124, blue: 33 }, + { red: 117, green: 91, blue: 86 }, + { red: 113, green: 104, blue: 107 }, + { red: 216, green: 153, blue: 99 }, + { red: 242, green: 226, blue: 64 } + ]; + + var SOUND_BASE_URL = "http://public.highfidelity.io/sounds/Animals/"; + + for (var i = 0; i < sound_filenames.length; i++) { + birds.push({ sound: new Sound(SOUND_BASE_URL + sound_filenames[i]), + color: colors[i] + } ); + } +} \ No newline at end of file diff --git a/examples/guidedTour.js b/examples/guidedTour.js index 1882b527d7..8729850927 100644 --- a/examples/guidedTour.js +++ b/examples/guidedTour.js @@ -12,6 +12,8 @@ var MIN_CHANGE = 2.0; var LANDING_DISTANCE = 2.0; var LANDING_RANDOM = 0.2; +var relativePosition; + function update(deltaTime) { if (Math.random() < deltaTime) { @@ -26,20 +28,15 @@ function update(deltaTime) { } if (guide) { + relativePosition = Vec3.subtract(MyAvatar.position, lastGuidePosition); // Check whether guide has moved, update if so if (Vec3.length(lastGuidePosition) == 0.0) { lastGuidePosition = guide.position; } else { if (Vec3.length(Vec3.subtract(lastGuidePosition, guide.position)) > MIN_CHANGE) { - var meToGuide = Vec3.multiply(Vec3.normalize(Vec3.subtract(guide.position, MyAvatar.position)), LANDING_DISTANCE); - var newPosition = Vec3.subtract(guide.position, meToGuide); - newPosition = Vec3.sum(newPosition, { x: Math.random() * LANDING_RANDOM - LANDING_RANDOM / 2.0, - y: 0, - z: Math.random() * LANDING_RANDOM - LANDING_RANDOM / 2.0 }); + var newPosition = Vec3.sum(guide.position, relativePosition); MyAvatar.position = newPosition; - lastGuidePosition = guide.position; - MyAvatar.orientation = guide.orientation; } } } diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 01e0c76e0d..ef597549f2 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -54,6 +54,8 @@ EntityPropertyDialogBox = (function () { index++; array.push({ label: "Textures:", value: properties.textures }); index++; + array.push({ label: "Original Textures:\n" + properties.originalTextures, type: "header" }); + index++; } array.push({ label: "Position:", type: "header" }); index++; @@ -239,6 +241,7 @@ EntityPropertyDialogBox = (function () { properties.animationFPS = array[index++].value; properties.animationFrameIndex = array[index++].value; properties.textures = array[index++].value; + index++; // skip textureNames label } index++; // skip header properties.position.x = array[index++].value; diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js new file mode 100644 index 0000000000..0175c56e18 --- /dev/null +++ b/examples/libraries/walkApi.js @@ -0,0 +1,412 @@ +// +// walkObjects.js +// +// version 1.001 +// +// Created by David Wooldridge, Autumn 2014 +// +// Motion, state and Transition objects for use by the walk.js script v1.1 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// constructor for the Motion object +Motion = function() { + + this.setGender = function(gender) { + + this.avatarGender = gender; + + switch(this.avatarGender) { + + case MALE: + + this.selWalk = walkAssets.maleStandardWalk; + this.selStand = walkAssets.maleStandOne; + this.selFlyUp = walkAssets.maleFlyingUp; + this.selFly = walkAssets.maleFlying; + this.selFlyDown = walkAssets.maleFlyingDown; + this.selSideStepLeft = walkAssets.maleSideStepLeft; + this.selSideStepRight = walkAssets.maleSideStepRight; + this.curAnim = this.selStand; + return; + + case FEMALE: + + this.selWalk = walkAssets.femaleStandardWalk; + this.selStand = walkAssets.femaleStandOne; + this.selFlyUp = walkAssets.femaleFlyingUp; + this.selFly = walkAssets.femaleFlying; + this.selFlyDown = walkAssets.femaleFlyingDown; + this.selSideStepLeft = walkAssets.femaleSideStepLeft; + this.selSideStepRight = walkAssets.femaleSideStepRight; + this.curAnim = this.selStand; + return; + } + } + + this.hydraCheck = function() { + + // function courtesy of Thijs Wenker, frisbee.js + var numberOfButtons = Controller.getNumberOfButtons(); + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2); + return hydrasConnected; + } + + // settings + this.armsFree = this.hydraCheck(); // automatically sets true for Hydra support - temporary fix + this.makesFootStepSounds = true; + this.avatarGender = MALE; + this.motionPitchMax = 60; + this.motionRollMax = 40; + + // timing + this.frameStartTime = 0; // used for measuring frame execution times + this.frameExecutionTimeMax = 0; // keep track of the longest frame execution time + this.cumulativeTime = 0.0; + this.lastWalkStartTime = 0; + + // selected animations + this.selWalk = walkAssets.maleStandardWalk; + this.selStand = walkAssets.maleStandOne; + this.selFlyUp = walkAssets.maleFlyingUp; + this.selFly = walkAssets.maleFlying; + this.selFlyDown = walkAssets.maleFlyingDown; + this.selSideStepLeft = walkAssets.maleSideStepLeft; + this.selSideStepRight = walkAssets.maleSideStepRight; + + // the currently selected animation, joint and transition + this.curAnim = this.selStand; + this.curJointIndex = 0; + this.curTransition = null; + + // zero out avi's joints, curl the fingers nicely then take some measurements + this.avatarJointNames = MyAvatar.getJointNames(); + if (!this.armsFree) { + + for (var i = 0; i < this.avatarJointNames.length; i++) { + + if (i > 17 || i < 34) { + // left hand fingers + MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0)); + } else if (i > 33 || i < 38) { + // left hand thumb + MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0)); + } else if (i > 41 || i < 58) { + // right hand fingers + MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(16, 0, 0)); + } else if (i > 57 || i < 62) { + // right hand thumb + MyAvatar.setJointData(this.avatarJointNames[i], Quat.fromPitchYawRollDegrees(4, 0, 0)); + } else { + // zero out the remaining joints + MyAvatar.clearJointData(this.avatarJointNames[i]); + } + } + } + + this.footRPos = MyAvatar.getJointPosition("RightFoot"); + this.hipsToFeet = MyAvatar.getJointPosition("Hips").y - this.footRPos.y; + + // walkwheel (foot / ground speed matching) + this.direction = FORWARDS; + this.nextStep = RIGHT; + this.nFrames = 0; + this.strideLength = this.selWalk.calibration.strideLengthForwards; + this.walkWheelPos = 0; + + this.advanceWalkWheel = function(angle){ + this.walkWheelPos += angle; + if (motion.walkWheelPos >= 360) { + this.walkWheelPos = this.walkWheelPos % 360; + } + } + + // last frame history + this.lastDirection = 0; + this.lastVelocity = 0; + this.lastStrideLength = 0; // kept for use during transitions + +}; // end Motion constructor + +// finite state machine +state = (function () { + + return { + + // the finite list of states + STANDING: 1, + WALKING: 2, + SIDE_STEP: 3, + FLYING: 4, + EDIT_WALK_STYLES: 5, + EDIT_WALK_TWEAKS: 6, + EDIT_WALK_JOINTS: 7, + EDIT_STANDING: 8, + EDIT_FLYING: 9, + EDIT_FLYING_UP: 10, + EDIT_FLYING_DOWN: 11, + EDIT_SIDESTEP_LEFT: 12, + EDIT_SIDESTEP_RIGHT: 14, + currentState: this.STANDING, + + // status vars + powerOn: true, + minimised: true, + editing: false, + editingTranslation: false, + + setInternalState: function(newInternalState) { + + switch (newInternalState) { + + case this.WALKING: + + this.currentState = this.WALKING; + this.editing = false; + motion.lastWalkStartTime = new Date().getTime(); + walkInterface.updateMenu(); + return; + + case this.FLYING: + + this.currentState = this.FLYING; + this.editing = false; + motion.lastWalkStartTime = 0; + walkInterface.updateMenu(); + return; + + case this.SIDE_STEP: + + this.currentState = this.SIDE_STEP; + this.editing = false; + motion.lastWalkStartTime = new Date().getTime(); + walkInterface.updateMenu(); + return; + + case this.EDIT_WALK_STYLES: + + this.currentState = this.EDIT_WALK_STYLES; + this.editing = true; + motion.lastWalkStartTime = new Date().getTime(); + motion.curAnim = motion.selWalk; + walkInterface.updateMenu(); + return; + + case this.EDIT_WALK_TWEAKS: + + this.currentState = this.EDIT_WALK_TWEAKS; + this.editing = true; + motion.lastWalkStartTime = new Date().getTime(); + motion.curAnim = motion.selWalk; + walkInterface.updateMenu(); + return; + + case this.EDIT_WALK_JOINTS: + + this.currentState = this.EDIT_WALK_JOINTS; + this.editing = true; + motion.lastWalkStartTime = new Date().getTime(); + motion.curAnim = motion.selWalk; + walkInterface.updateMenu(); + return; + + case this.EDIT_STANDING: + + this.currentState = this.EDIT_STANDING; + this.editing = true; + motion.lastWalkStartTime = 0; + motion.curAnim = motion.selStand; + walkInterface.updateMenu(); + return; + + case this.EDIT_SIDESTEP_LEFT: + + this.currentState = this.EDIT_SIDESTEP_LEFT; + this.editing = true; + motion.lastWalkStartTime = new Date().getTime(); + motion.curAnim = motion.selSideStepLeft; + walkInterface.updateMenu(); + return; + + case this.EDIT_SIDESTEP_RIGHT: + + this.currentState = this.EDIT_SIDESTEP_RIGHT; + this.editing = true; + motion.lastWalkStartTime = new Date().getTime(); + motion.curAnim = motion.selSideStepRight; + walkInterface.updateMenu(); + return; + + case this.EDIT_FLYING: + + this.currentState = this.EDIT_FLYING; + this.editing = true; + motion.lastWalkStartTime = 0; + motion.curAnim = motion.selFly; + walkInterface.updateMenu(); + return; + + case this.EDIT_FLYING_UP: + + this.currentState = this.EDIT_FLYING_UP; + this.editing = true; + motion.lastWalkStartTime = 0; + motion.curAnim = motion.selFlyUp; + walkInterface.updateMenu(); + return; + + case this.EDIT_FLYING_DOWN: + + this.currentState = this.EDIT_FLYING_DOWN; + this.editing = true; + motion.lastWalkStartTime = 0; + motion.curAnim = motion.selFlyDown; + walkInterface.updateMenu(); + return; + + case this.STANDING: + default: + + this.currentState = this.STANDING; + this.editing = false; + motion.lastWalkStartTime = 0; + motion.curAnim = motion.selStand; + walkInterface.updateMenu(); + + // initialisation - runs at script startup only + if (motion.strideLength === 0) { + + motion.setGender(MALE); + if (motion.direction === BACKWARDS) { + motion.strideLength = motion.selWalk.calibration.strideLengthBackwards; + } else { + motion.strideLength = motion.selWalk.calibration.strideLengthForwards; + } + } + return; + } + } + } +})(); // end state object literal + +// constructor for animation Transition +Transition = function(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) { + + this.lastAnim = lastAnimation; // name of last animation + this.nextAnimation = nextAnimation; // name of next animation + if (lastAnimation === motion.selWalk || + nextAnimation === motion.selSideStepLeft || + nextAnimation === motion.selSideStepRight) { + // boolean - is the last animation a walking animation? + this.walkingAtStart = true; + } else { + this.walkingAtStart = false; + } + if (nextAnimation === motion.selWalk || + nextAnimation === motion.selSideStepLeft || + nextAnimation === motion.selSideStepRight) { + // boolean - is the next animation a walking animation? + this.walkingAtEnd = true; + } else { + this.walkingAtEnd = false; + } + this.reachPoses = reachPoses; // placeholder / stub: array of reach poses for squash and stretch techniques + this.transitionDuration = transitionDuration; // length of transition (seconds) + this.easingLower = easingLower; // Bezier curve handle (normalised) + this.easingUpper = easingUpper; // Bezier curve handle (normalised) + this.startTime = new Date().getTime(); // Starting timestamp (seconds) + this.progress = 0; // how far are we through the transition? + this.walkWheelIncrement = 3; // how much to turn the walkwheel each frame when transitioning to / from walking + this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition + this.walkStopAngle = 0; // what angle should we stop the walk cycle? + +}; // end Transition constructor + + +walkAssets = (function () { + + // path to the sounds used for the footsteps + var _pathToSounds = 'https://s3.amazonaws.com/hifi-public/sounds/Footsteps/'; + + // read in the sounds + var _footsteps = []; + _footsteps.push(new Sound(_pathToSounds+"FootstepW2Left-12db.wav")); + _footsteps.push(new Sound(_pathToSounds+"FootstepW2Right-12db.wav")); + _footsteps.push(new Sound(_pathToSounds+"FootstepW3Left-12db.wav")); + _footsteps.push(new Sound(_pathToSounds+"FootstepW3Right-12db.wav")); + _footsteps.push(new Sound(_pathToSounds+"FootstepW5Left-12db.wav")); + _footsteps.push(new Sound(_pathToSounds+"FootstepW5Right-12db.wav")); + + // load the animation datafiles + Script.include(pathToAssets+"animations/dd-female-standard-walk-animation.js"); + Script.include(pathToAssets+"animations/dd-female-flying-up-animation.js"); + Script.include(pathToAssets+"animations/dd-female-flying-animation.js"); + Script.include(pathToAssets+"animations/dd-female-flying-down-animation.js"); + Script.include(pathToAssets+"animations/dd-female-standing-one-animation.js"); + Script.include(pathToAssets+"animations/dd-female-sidestep-left-animation.js"); + Script.include(pathToAssets+"animations/dd-female-sidestep-right-animation.js"); + Script.include(pathToAssets+"animations/dd-male-standard-walk-animation.js"); + Script.include(pathToAssets+"animations/dd-male-flying-up-animation.js"); + Script.include(pathToAssets+"animations/dd-male-flying-animation.js"); + Script.include(pathToAssets+"animations/dd-male-flying-down-animation.js"); + Script.include(pathToAssets+"animations/dd-male-standing-one-animation.js"); + Script.include(pathToAssets+"animations/dd-male-sidestep-left-animation.js"); + Script.include(pathToAssets+"animations/dd-male-sidestep-right-animation.js"); + + // read in the animation files + var _FemaleStandardWalkFile = new FemaleStandardWalk(); + var _femaleStandardWalk = _FemaleStandardWalkFile.loadAnimation(); + var _FemaleFlyingUpFile = new FemaleFlyingUp(); + var _femaleFlyingUp = _FemaleFlyingUpFile.loadAnimation(); + var _FemaleFlyingFile = new FemaleFlying(); + var _femaleFlying = _FemaleFlyingFile.loadAnimation(); + var _FemaleFlyingDownFile = new FemaleFlyingDown(); + var _femaleFlyingDown = _FemaleFlyingDownFile.loadAnimation(); + var _FemaleStandOneFile = new FemaleStandingOne(); + var _femaleStandOne = _FemaleStandOneFile.loadAnimation(); + var _FemaleSideStepLeftFile = new FemaleSideStepLeft(); + var _femaleSideStepLeft = _FemaleSideStepLeftFile.loadAnimation(); + var _FemaleSideStepRightFile = new FemaleSideStepRight(); + var _femaleSideStepRight = _FemaleSideStepRightFile.loadAnimation(); + var _MaleStandardWalkFile = new MaleStandardWalk(filter); + var _maleStandardWalk = _MaleStandardWalkFile.loadAnimation(); + var _MaleFlyingUpFile = new MaleFlyingUp(); + var _maleFlyingUp = _MaleFlyingUpFile.loadAnimation(); + var _MaleFlyingFile = new MaleFlying(); + var _maleFlying = _MaleFlyingFile.loadAnimation(); + var _MaleFlyingDownFile = new MaleFlyingDown(); + var _maleFlyingDown = _MaleFlyingDownFile.loadAnimation(); + var _MaleStandOneFile = new MaleStandingOne(); + var _maleStandOne = _MaleStandOneFile.loadAnimation(); + var _MaleSideStepLeftFile = new MaleSideStepLeft(); + var _maleSideStepLeft = _MaleSideStepLeftFile.loadAnimation(); + var _MaleSideStepRightFile = new MaleSideStepRight(); + var _maleSideStepRight = _MaleSideStepRightFile.loadAnimation(); + + return { + + // expose the sound assets + footsteps: _footsteps, + + // expose the animation assets + femaleStandardWalk: _femaleStandardWalk, + femaleFlyingUp: _femaleFlyingUp, + femaleFlying: _femaleFlying, + femaleFlyingDown: _femaleFlyingDown, + femaleStandOne: _femaleStandOne, + femaleSideStepLeft: _femaleSideStepLeft, + femaleSideStepRight: _femaleSideStepRight, + maleStandardWalk: _maleStandardWalk, + maleFlyingUp: _maleFlyingUp, + maleFlying: _maleFlying, + maleFlyingDown: _maleFlyingDown, + maleStandOne: _maleStandOne, + maleSideStepLeft: _maleSideStepLeft, + maleSideStepRight: _maleSideStepRight, + } +})(); \ No newline at end of file diff --git a/examples/libraries/walkFilters.js b/examples/libraries/walkFilters.js new file mode 100644 index 0000000000..98a7562bb4 --- /dev/null +++ b/examples/libraries/walkFilters.js @@ -0,0 +1,225 @@ +// +// walkFilters.js +// +// version 1.001 +// +// Created by David Wooldridge, Autumn 2014 +// +// Provides a variety of filters for use by the walk.js script v1.1 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +AveragingFilter = function(length) { + + //this.name = name; + this.pastValues = []; + + for(var i = 0; i < length; i++) { + this.pastValues.push(0); + } + + // single arg is the nextInputValue + this.process = function() { + + if (this.pastValues.length === 0 && arguments[0]) { + return arguments[0]; + } else if (arguments[0]) { + // apply quick and simple LP filtering + this.pastValues.push(arguments[0]); + this.pastValues.shift(); + var nextOutputValue = 0; + for (var ea in this.pastValues) nextOutputValue += this.pastValues[ea]; + return nextOutputValue / this.pastValues.length; + } else { + return 0; + } + }; +}; + +// 2nd order Butterworth LP filter - calculate coeffs here: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html +// provides LP filtering with a more stable frequency / phase response +ButterworthFilter = function(cutOff) { + + // cut off frequency = 5Hz + this.gain = 20.20612010; + this.coeffOne = -0.4775922501; + this.coeffTwo = 1.2796324250; + + // initialise the arrays + this.xv = []; + this.yv = []; + for(var i = 0; i < 3; i++) { + this.xv.push(0); + this.yv.push(0); + } + + // process values + this.process = function(nextInputValue) { + + this.xv[0] = this.xv[1]; + this.xv[1] = this.xv[2]; + this.xv[2] = nextInputValue / this.gain; + + this.yv[0] = this.yv[1]; + this.yv[1] = this.yv[2]; + this.yv[2] = (this.xv[0] + this.xv[2]) + + 2 * this.xv[1] + + (this.coeffOne * this.yv[0]) + + (this.coeffTwo * this.yv[1]); + + return this.yv[2]; + }; +}; // end Butterworth filter contructor + +// Add harmonics to a given sine wave to form square, sawtooth or triangle waves +// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html +WaveSynth = function(waveShape, numHarmonics, smoothing) { + + this.numHarmonics = numHarmonics; + this.waveShape = waveShape; + this.averagingFilter = new AveragingFilter(smoothing); + + // NB: frequency in radians + this.shapeWave = function(frequency) { + + // make some shapes + var harmonics = 0; + var multiplier = 0; + var iterations = this.numHarmonics * 2 + 2; + if (this.waveShape === TRIANGLE) { + iterations++; + } + + for(var n = 2; n < iterations; n++) { + + switch(this.waveShape) { + + case SAWTOOTH: { + + multiplier = 1 / n; + harmonics += multiplier * Math.sin(n * frequency); + break; + } + + case TRIANGLE: { + + if (n % 2 === 1) { + var mulitplier = 1 / (n * n); + // multiply (4n-1)th harmonics by -1 + if (n === 3 || n === 7 || n === 11 || n === 15) { + mulitplier *= -1; + } + harmonics += mulitplier * Math.sin(n * frequency); + } + break; + } + + case SQUARE: { + + if (n % 2 === 1) { + multiplier = 1 / n; + harmonics += multiplier * Math.sin(n * frequency); + } + break; + } + } + } + + // smooth the result and return + return this.averagingFilter.process(harmonics); + }; +}; + +// Create a wave shape by summing pre-calcualted sinusoidal harmonics +HarmonicsFilter = function(magnitudes, phaseAngles) { + + this.magnitudes = magnitudes; + this.phaseAngles = phaseAngles; + + this.calculate = function(twoPiFT) { + + var harmonics = 0; + var numHarmonics = magnitudes.length; + + for(var n = 0; n < numHarmonics; n++) { + harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]); + } + return harmonics; + }; +}; + +// the main filter object literal +filter = (function() { + + // Bezier private functions + function _B1(t) { return t * t * t }; + function _B2(t) { return 3 * t * t * (1 - t) }; + function _B3(t) { return 3 * t * (1 - t) * (1 - t) }; + function _B4(t) { return (1 - t) * (1 - t) * (1 - t) }; + + return { + + // helper methods + degToRad: function(degrees) { + + var convertedValue = degrees * Math.PI / 180; + return convertedValue; + }, + + radToDeg: function(radians) { + + var convertedValue = radians * 180 / Math.PI; + return convertedValue; + }, + + // these filters need instantiating, as they hold arrays of previous values + createAveragingFilter: function(length) { + + var newAveragingFilter = new AveragingFilter(length); + return newAveragingFilter; + }, + + createButterworthFilter: function(cutoff) { + + var newButterworthFilter = new ButterworthFilter(cutoff); + return newButterworthFilter; + }, + + createWaveSynth: function(waveShape, numHarmonics, smoothing) { + + var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing); + return newWaveSynth; + }, + + createHarmonicsFilter: function(magnitudes, phaseAngles) { + + var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles); + return newHarmonicsFilter; + }, + + + // the following filters do not need separate instances, as they hold no previous values + bezier: function(percent, C1, C2, C3, C4) { + + // Bezier functions for more natural transitions + // based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/ + var pos = {x: 0, y: 0}; + pos.x = C1.x * _B1(percent) + C2.x * _B2(percent) + C3.x * _B3(percent) + C4.x * _B4(percent); + pos.y = C1.y * _B1(percent) + C2.y * _B2(percent) + C3.y * _B3(percent) + C4.y * _B4(percent); + return pos; + }, + + // simple clipping filter (clips bottom of wave only, special case for hips y-axis skeleton offset) + clipTrough: function(inputValue, peak, strength) { + + var outputValue = inputValue * strength; + if (outputValue < -peak) { + outputValue = -peak; + } + return outputValue; + } + } + +})(); \ No newline at end of file diff --git a/examples/libraries/walkInterface.js b/examples/libraries/walkInterface.js new file mode 100644 index 0000000000..aa0b533101 --- /dev/null +++ b/examples/libraries/walkInterface.js @@ -0,0 +1,2690 @@ +// +// walkInterface.js +// +// version 1.001 +// +// Created by David Wooldridge, Autumn 2014 +// +// Presents the UI for the walk.js script v1.1 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +walkInterface = (function() { + + // controller element positions and dimensions + var _backgroundWidth = 350; + var _backgroundHeight = 700; + var _backgroundX = Window.innerWidth - _backgroundWidth - 58; + var _backgroundY = Window.innerHeight / 2 - _backgroundHeight / 2; + var _minSliderX = _backgroundX + 30; + var _sliderRangeX = 295 - 30; + var _jointsControlWidth = 200; + var _jointsControlHeight = 300; + var _jointsControlX = _backgroundX + _backgroundWidth / 2 - _jointsControlWidth / 2; + var _jointsControlY = _backgroundY + 242 - _jointsControlHeight / 2; + var _buttonsY = 20; // distance from top of panel to menu buttons + var _bigButtonsY = 408; // distance from top of panel to top of first big button + + // arrays of overlay names + var _sliderThumbOverlays = []; + var _backgroundOverlays = []; + var _buttonOverlays = []; + var _jointsControlOverlays = []; + var _bigbuttonOverlays = []; + + // reference to the internal state + var _state = { + editingTranslation: false + }; + + // reference to the Motion object + var _motion = null; + + var _walkAssets = null; + + // constants + var MAX_WALK_SPEED = 1257; + + // look and feel + var momentaryButtonTimer = null; + + // all slider controls have a range (with the exception of phase controls that are always +-180) + var _sliderRanges = { + "joints": [{ + "name": "hips", + "pitchRange": 12, + "yawRange": 18, + "rollRange": 12, + "pitchOffsetRange": 25, + "yawOffsetRange": 25, + "rollOffsetRange": 25, + "swayRange": 0.12, + "bobRange": 0.05, + "thrustRange": 0.05, + "swayOffsetRange": 0.25, + "bobOffsetRange": 0.25, + "thrustOffsetRange": 0.25 + }, { + "name": "upperLegs", + "pitchRange": 30, + "yawRange": 35, + "rollRange": 35, + "pitchOffsetRange": 20, + "yawOffsetRange": 20, + "rollOffsetRange": 20 + }, { + "name": "lowerLegs", + "pitchRange": 10, + "yawRange": 20, + "rollRange": 20, + "pitchOffsetRange": 180, + "yawOffsetRange": 20, + "rollOffsetRange": 20 + }, { + "name": "feet", + "pitchRange": 10, + "yawRange": 20, + "rollRange": 20, + "pitchOffsetRange": 180, + "yawOffsetRange": 50, + "rollOffsetRange": 50 + }, { + "name": "toes", + "pitchRange": 90, + "yawRange": 20, + "rollRange": 20, + "pitchOffsetRange": 90, + "yawOffsetRange": 20, + "rollOffsetRange": 20 + }, { + "name": "spine", + "pitchRange": 40, + "yawRange": 40, + "rollRange": 40, + "pitchOffsetRange": 90, + "yawOffsetRange": 50, + "rollOffsetRange": 50 + }, { + "name": "spine1", + "pitchRange": 20, + "yawRange": 40, + "rollRange": 20, + "pitchOffsetRange": 90, + "yawOffsetRange": 50, + "rollOffsetRange": 50 + }, { + "name": "spine2", + "pitchRange": 20, + "yawRange": 40, + "rollRange": 20, + "pitchOffsetRange": 90, + "yawOffsetRange": 50, + "rollOffsetRange": 50 + }, { + "name": "shoulders", + "pitchRange": 35, + "yawRange": 40, + "rollRange": 20, + "pitchOffsetRange": 180, + "yawOffsetRange": 180, + "rollOffsetRange": 180 + }, { + "name": "upperArms", + "pitchRange": 90, + "yawRange": 90, + "rollRange": 90, + "pitchOffsetRange": 180, + "yawOffsetRange": 180, + "rollOffsetRange": 180 + }, { + "name": "lowerArms", + "pitchRange": 90, + "yawRange": 90, + "rollRange": 120, + "pitchOffsetRange": 180, + "yawOffsetRange": 180, + "rollOffsetRange": 180 + }, { + "name": "hands", + "pitchRange": 90, + "yawRange": 180, + "rollRange": 90, + "pitchOffsetRange": 180, + "yawOffsetRange": 180, + "rollOffsetRange": 180 + }, { + "name": "head", + "pitchRange": 20, + "yawRange": 20, + "rollRange": 20, + "pitchOffsetRange": 90, + "yawOffsetRange": 90, + "rollOffsetRange": 90 + }] + }; + + // load overlay images + var _controlsMinimisedTab = Overlays.addOverlay("image", { + x: Window.innerWidth - 58, + y: Window.innerHeight - 145, + width: 50, + height: 50, + imageURL: pathToAssets + 'overlay-images/ddao-minimise-tab.png', + visible: true, + alpha: 0.9 + }); + + var _controlsBackground = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX, + y: _backgroundY, + width: _backgroundWidth, + height: _backgroundHeight + }, + imageURL: pathToAssets + "overlay-images/ddao-background.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _backgroundOverlays.push(_controlsBackground); + + var _controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX, + y: _backgroundY, + width: _backgroundWidth, + height: _backgroundHeight + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-styles.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _backgroundOverlays.push(_controlsBackgroundWalkEditStyles); + + var _controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX, + y: _backgroundY, + width: _backgroundWidth, + height: _backgroundHeight + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-tweaks.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _backgroundOverlays.push(_controlsBackgroundWalkEditTweaks); + + var _controlsBackgroundWalkEditHipTrans = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX, + y: _backgroundY, + width: _backgroundWidth, + height: _backgroundHeight + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-translation.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _backgroundOverlays.push(_controlsBackgroundWalkEditHipTrans); + + var _controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX, + y: _backgroundY, + width: _backgroundWidth, + height: _backgroundHeight + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-joints.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _backgroundOverlays.push(_controlsBackgroundWalkEditJoints); + + // load character joint selection control images + var _hipsJointsTranslation = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-hips-translation.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_hipsJointsTranslation); + + var _hipsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-hips.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_hipsJointControl); + + var _upperLegsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-upper-legs.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_upperLegsJointControl); + + var _lowerLegsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-lower-legs.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_lowerLegsJointControl); + + var _feetJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-feet.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_feetJointControl); + + var _toesJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-toes.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_toesJointControl); + + var _spineJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_spineJointControl); + + var _spine1JointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine1.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_spine1JointControl); + + var _spine2JointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-spine2.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_spine2JointControl); + + var _shouldersJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-shoulders.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_shouldersJointControl); + + var _upperArmsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-upper-arms.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_upperArmsJointControl); + + var _forearmsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-forearms.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_forearmsJointControl); + + var _handsJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-hands.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_handsJointControl); + + var _headJointControl = Overlays.addOverlay("image", { + bounds: { + x: _jointsControlX, + y: _jointsControlY, + width: 200, + height: 300 + }, + imageURL: pathToAssets + "overlay-images/ddao-background-edit-head.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _jointsControlOverlays.push(_headJointControl); + + + // slider thumb overlays + var _sliderOne = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderOne); + + var _sliderTwo = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderTwo); + + var _sliderThree = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderThree); + + var _sliderFour = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderFour); + + var _sliderFive = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderFive); + + var _sliderSix = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderSix); + + var _sliderSeven = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderSeven); + + var _sliderEight = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderEight); + + var _sliderNine = Overlays.addOverlay("image", { + bounds: { + x: 0, + y: 0, + width: 25, + height: 25 + }, + imageURL: pathToAssets + "overlay-images/ddao-slider-handle.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _sliderThumbOverlays.push(_sliderNine); + + + // button overlays + var _onButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 20, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-on-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_onButton); + + var _offButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 20, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-off-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_offButton); + + var _configWalkButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-walk-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkButton); + + var _configWalkButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-walk-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkButtonSelected); + + var _configStandButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-stand-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configStandButton); + + var _configStandButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-stand-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configStandButtonSelected); + + var _configFlyingButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingButton); + + var _configFlyingButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingButtonSelected); + + var _configFlyingUpButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-up-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingUpButton); + + var _configFlyingUpButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-up-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingUpButtonSelected); + + var _configFlyingDownButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-down-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingDownButton); + + var _configFlyingDownButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-fly-down-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configFlyingDownButtonSelected); + + var _hideButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 272, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-hide-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_hideButton); + + var _hideButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 272, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-hide-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_hideButtonSelected); + + var _configWalkStylesButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-styles-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkStylesButton); + + var _configWalkStylesButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-styles-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkStylesButtonSelected); + + var _configWalkTweaksButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-tweaks-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkTweaksButton); + + var _configWalkTweaksButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 146, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-tweaks-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkTweaksButtonSelected); + + var _configSideStepLeftButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-left-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configSideStepLeftButton); + + var _configSideStepLeftButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 83, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-left-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configSideStepLeftButtonSelected); + + var _configSideStepRightButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-right-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configSideStepRightButton); + + var _configSideStepRightButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-edit-sidestep-right-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configSideStepRightButtonSelected); + + var _configWalkJointsButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-joints-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkJointsButton); + + var _configWalkJointsButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 209, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-joints-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_configWalkJointsButtonSelected); + + var _backButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 272, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-back-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_backButton); + + var _backButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + 272, + y: _backgroundY + _buttonsY, + width: 60, + height: 47 + }, + imageURL: pathToAssets + "overlay-images/ddao-back-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _buttonOverlays.push(_backButtonSelected); + + // big button overlays - front panel + var _femaleBigButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-female-big-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_femaleBigButton); + + var _femaleBigButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-female-big-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_femaleBigButtonSelected); + + var _maleBigButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 60, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-male-big-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_maleBigButton); + + var _maleBigButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 60, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-male-big-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_maleBigButtonSelected); + + var _armsFreeBigButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 120, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-arms-free-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_armsFreeBigButton); + + var _armsFreeBigButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 120, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-arms-free-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_armsFreeBigButtonSelected); + + var _footstepsBigButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 180, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-footsteps-big-button.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_footstepsBigButton); + + var _footstepsBigButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY + 180, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-footsteps-big-button-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_footstepsBigButtonSelected); + + + // walk styles + _bigButtonsY = 121; + var _standardWalkBigButton = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-select-button-standard.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_standardWalkBigButton); + + var _standardWalkBigButtonSelected = Overlays.addOverlay("image", { + bounds: { + x: _backgroundX + _backgroundWidth / 2 - 115, + y: _backgroundY + _bigButtonsY, + width: 230, + height: 36 + }, + imageURL: pathToAssets + "overlay-images/ddao-walk-select-button-standard-selected.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1, + visible: false + }); + _bigbuttonOverlays.push(_standardWalkBigButtonSelected); + + // various show / hide GUI element functions + function minimiseDialog(minimise) { + + if (momentaryButtonTimer) { + Script.clearInterval(momentaryButtonTimer); + momentaryButtonTimer = null; + } + + if (minimise) { + setBackground(); + hideMenuButtons(); + setSliderThumbsVisible(false); + hideJointSelectors(); + initialiseFrontPanel(false); + Overlays.editOverlay(_controlsMinimisedTab, { + visible: true + }); + } else { + Overlays.editOverlay(_controlsMinimisedTab, { + visible: false + }); + } + }; + + function setBackground(backgroundID) { + for (var i in _backgroundOverlays) { + if (_backgroundOverlays[i] === backgroundID) { + Overlays.editOverlay(_backgroundOverlays[i], { + visible: true + }); + } else { + Overlays.editOverlay(_backgroundOverlays[i], { visible: false }); + } + } + }; + + // top row menu type buttons (on | walk | stand | fly | hide) + function hideMenuButtons() { + for (var i in _buttonOverlays) { + Overlays.editOverlay(_buttonOverlays[i], { visible: false }); + } + }; + + function hideJointSelectors() { + for (var i in _jointsControlOverlays) { + Overlays.editOverlay(_jointsControlOverlays[i], { + visible: false + }); + } + }; + + function setSliderThumbsVisible(thumbsVisible) { + for (var i = 0; i < _sliderThumbOverlays.length; i++) { + Overlays.editOverlay(_sliderThumbOverlays[i], { + visible: thumbsVisible + }); + } + }; + + function setButtonOverlayVisible(buttonOverlayName) { + for (var i in _buttonOverlays) { + if (_buttonOverlays[i] === buttonOverlayName) { + Overlays.editOverlay(buttonOverlayName, { visible: true }); + } + } + }; + + function initialiseFrontPanel(showButtons) { + + if (_motion.avatarGender === FEMALE) { + Overlays.editOverlay(_femaleBigButtonSelected, { + visible: showButtons + }); + Overlays.editOverlay(_femaleBigButton, { + visible: false + }); + Overlays.editOverlay(_maleBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_maleBigButton, { + visible: showButtons + }); + + } else { + + Overlays.editOverlay(_femaleBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_femaleBigButton, { + visible: showButtons + }); + Overlays.editOverlay(_maleBigButtonSelected, { + visible: showButtons + }); + Overlays.editOverlay(_maleBigButton, { + visible: false + }); + } + if (_motion.armsFree) { + Overlays.editOverlay(_armsFreeBigButtonSelected, { + visible: showButtons + }); + Overlays.editOverlay(_armsFreeBigButton, { + visible: false + }); + + } else { + + Overlays.editOverlay(_armsFreeBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_armsFreeBigButton, { + visible: showButtons + }); + } + if (_motion.makesFootStepSounds) { + Overlays.editOverlay(_footstepsBigButtonSelected, { + visible: showButtons + }); + Overlays.editOverlay(_footstepsBigButton, { + visible: false + }); + + } else { + + Overlays.editOverlay(_footstepsBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_footstepsBigButton, { + visible: showButtons + }); + } + }; + + function initialiseWalkStylesPanel(showButtons) { + + // set all big buttons to hidden, but skip the first 8, as are used by the front panel + for (var i = 8; i < _bigbuttonOverlays.length; i++) { + Overlays.editOverlay(_bigbuttonOverlays[i], { + visible: false + }); + } + + if (!showButtons) { + return; + } + + // set all the non-selected ones to showing + for (var i = 8; i < _bigbuttonOverlays.length; i += 2) { + Overlays.editOverlay(_bigbuttonOverlays[i], { visible: true }); + } + + // set the currently selected one + if (_motion.selWalk === _walkAssets.femaleStandardWalk || + _motion.selWalk === _walkAssets.maleStandardWalk) { + + Overlays.editOverlay(_standardWalkBigButtonSelected, { + visible: true + }); + Overlays.editOverlay(_standardWalkBigButton, { + visible: false + }); + } + }; + + function initialiseWalkTweaksPanel() { + + // sliders for commonly required walk adjustments + var i = 0; + var yLocation = _backgroundY + 71; + + // walk speed + var sliderXPos = _motion.curAnim.calibration.frequency / MAX_WALK_SPEED * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // lean (hips pitch offset) + sliderXPos = (((_sliderRanges.joints[0].pitchOffsetRange + _motion.curAnim.joints[0].pitchOffset) / 2) / + _sliderRanges.joints[0].pitchOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // stride (upper legs pitch) + sliderXPos = _motion.curAnim.joints[1].pitch / _sliderRanges.joints[1].pitchRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Legs separation (upper legs roll offset) + sliderXPos = (((_sliderRanges.joints[1].rollOffsetRange + _motion.curAnim.joints[1].rollOffset) / 2) / + _sliderRanges.joints[1].rollOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Legs forward (upper legs pitch offset) + sliderXPos = (((_sliderRanges.joints[1].pitchOffsetRange + _motion.curAnim.joints[1].pitchOffset) / 2) / + _sliderRanges.joints[1].pitchOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Lower legs splay (lower legs roll offset) + sliderXPos = (((_sliderRanges.joints[2].rollOffsetRange + _motion.curAnim.joints[2].rollOffset) / 2) / + _sliderRanges.joints[2].rollOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Arms forward (upper arms yaw offset) + sliderXPos = (((_sliderRanges.joints[9].yawOffsetRange + _motion.curAnim.joints[9].yawOffset) / 2) / + _sliderRanges.joints[9].yawOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Arms out (upper arm pitch offset) + sliderXPos = (((_sliderRanges.joints[9].pitchOffsetRange - _motion.curAnim.joints[9].pitchOffset) / 2) / + _sliderRanges.joints[9].pitchOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + + // Lower arms splay (lower arm pitch offset) + sliderXPos = (((_sliderRanges.joints[10].pitchOffsetRange - _motion.curAnim.joints[10].pitchOffset) / 2) / + _sliderRanges.joints[10].pitchOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 60, + visible: true + }); + }; + + function initialiseJointsEditingPanel() { + + var i = 0; + var yLocation = _backgroundY + 359; + hideJointSelectors(); + + if (_state.editingTranslation) { + + // display the joint control selector for hips translations + Overlays.editOverlay(_hipsJointsTranslation, {visible: true}); + + // Hips sway + var sliderXPos = _motion.curAnim.joints[0].sway / _sliderRanges.joints[0].swayRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // Hips bob + sliderXPos = _motion.curAnim.joints[0].bob / _sliderRanges.joints[0].bobRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // Hips thrust + sliderXPos = _motion.curAnim.joints[0].thrust / _sliderRanges.joints[0].thrustRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // Sway Phase + sliderXPos = (90 + _motion.curAnim.joints[0].swayPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // Bob Phase + sliderXPos = (90 + _motion.curAnim.joints[0].bobPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // Thrust Phase + sliderXPos = (90 + _motion.curAnim.joints[0].thrustPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // offset ranges are also -ve thr' zero to +ve, so we centre them + sliderXPos = (((_sliderRanges.joints[0].swayOffsetRange + _motion.curAnim.joints[0] + .swayOffset) / 2) / _sliderRanges.joints[0].swayOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (((_sliderRanges.joints[0].bobOffsetRange + _motion.curAnim.joints[0] + .bobOffset) / 2) / _sliderRanges.joints[0].bobOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (((_sliderRanges.joints[0].thrustOffsetRange + _motion.curAnim.joints[0] + .thrustOffset) / 2) / _sliderRanges.joints[0].thrustOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + } else { + + switch (_motion.curJointIndex) { + + case 0: + Overlays.editOverlay(_hipsJointControl, { + visible: true + }); + break; + case 1: + Overlays.editOverlay(_upperLegsJointControl, { + visible: true + }); + break; + case 2: + Overlays.editOverlay(_lowerLegsJointControl, { + visible: true + }); + break; + case 3: + Overlays.editOverlay(_feetJointControl, { + visible: true + }); + break; + case 4: + Overlays.editOverlay(_toesJointControl, { + visible: true + }); + break; + case 5: + Overlays.editOverlay(_spineJointControl, { + visible: true + }); + break; + case 6: + Overlays.editOverlay(_spine1JointControl, { + visible: true + }); + break; + case 7: + Overlays.editOverlay(_spine2JointControl, { + visible: true + }); + break; + case 8: + Overlays.editOverlay(_shouldersJointControl, { + visible: true + }); + break; + case 9: + Overlays.editOverlay(_upperArmsJointControl, { + visible: true + }); + break; + case 10: + Overlays.editOverlay(_forearmsJointControl, { + visible: true + }); + break; + case 11: + Overlays.editOverlay(_handsJointControl, { + visible: true + }); + break; + case 12: + Overlays.editOverlay(_headJointControl, { + visible: true + }); + break; + } + + var sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].pitch / + _sliderRanges.joints[_motion.curJointIndex].pitchRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].yaw / + _sliderRanges.joints[_motion.curJointIndex].yawRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = _motion.curAnim.joints[_motion.curJointIndex].roll / + _sliderRanges.joints[_motion.curJointIndex].rollRange * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // set phases (full range, -180 to 180) + sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].pitchPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].yawPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (90 + _motion.curAnim.joints[_motion.curJointIndex].rollPhase / 2) / 180 * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + // offset ranges are also -ve thr' zero to +ve, so we offset + sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange + + _motion.curAnim.joints[_motion.curJointIndex].pitchOffset) / 2) / + _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].yawOffsetRange + + _motion.curAnim.joints[_motion.curJointIndex].yawOffset) / 2) / + _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + + sliderXPos = (((_sliderRanges.joints[_motion.curJointIndex].rollOffsetRange + + _motion.curAnim.joints[_motion.curJointIndex].rollOffset) / 2) / + _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange) * _sliderRangeX; + Overlays.editOverlay(_sliderThumbOverlays[++i], { + x: _minSliderX + sliderXPos, + y: yLocation += 30, + visible: true + }); + } + }; + + // mouse event handlers + var _movingSliderOne = false; + var _movingSliderTwo = false; + var _movingSliderThree = false; + var _movingSliderFour = false; + var _movingSliderFive = false; + var _movingSliderSix = false; + var _movingSliderSeven = false; + var _movingSliderEight = false; + var _movingSliderNine = false; + + function mousePressEvent(event) { + + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + if (_state.currentState === _state.EDIT_WALK_JOINTS || + _state.currentState === _state.EDIT_STANDING || + _state.currentState === _state.EDIT_FLYING || + _state.currentState === _state.EDIT_FLYING_UP || + _state.currentState === _state.EDIT_FLYING_DOWN || + _state.currentState === _state.EDIT_SIDESTEP_LEFT || + _state.currentState === _state.EDIT_SIDESTEP_RIGHT) { + + // check for new joint selection and update display accordingly + var clickX = event.x - _backgroundX - 75; + var clickY = event.y - _backgroundY - 92; + + if (clickX > 60 && clickX < 120 && clickY > 123 && clickY < 155) { + _motion.curJointIndex = 0; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 63 && clickX < 132 && clickY > 156 && clickY < 202) { + _motion.curJointIndex = 1; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 58 && clickX < 137 && clickY > 203 && clickY < 250) { + _motion.curJointIndex = 2; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 58 && clickX < 137 && clickY > 250 && clickY < 265) { + _motion.curJointIndex = 3; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 58 && clickX < 137 && clickY > 265 && clickY < 280) { + _motion.curJointIndex = 4; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 78 && clickX < 121 && clickY > 111 && clickY < 128) { + _motion.curJointIndex = 5; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 78 && clickX < 128 && clickY > 89 && clickY < 111) { + _motion.curJointIndex = 6; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 85 && clickX < 118 && clickY > 77 && clickY < 94) { + _motion.curJointIndex = 7; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 64 && clickX < 125 && clickY > 55 && clickY < 77) { + _motion.curJointIndex = 8; + initialiseJointsEditingPanel(); + return; + + } else if ((clickX > 44 && clickX < 73 && clickY > 71 && clickY < 94) || + (clickX > 125 && clickX < 144 && clickY > 71 && clickY < 94)) { + _motion.curJointIndex = 9; + initialiseJointsEditingPanel(); + return; + + } else if ((clickX > 28 && clickX < 57 && clickY > 94 && clickY < 119) || + (clickX > 137 && clickX < 170 && clickY > 97 && clickY < 114)) { + _motion.curJointIndex = 10; + initialiseJointsEditingPanel(); + return; + + } else if ((clickX > 18 && clickX < 37 && clickY > 115 && clickY < 136) || + (clickX > 157 && clickX < 182 && clickY > 115 && clickY < 136)) { + _motion.curJointIndex = 11; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 81 && clickX < 116 && clickY > 12 && clickY < 53) { + _motion.curJointIndex = 12; + initialiseJointsEditingPanel(); + return; + + } else if (clickX > 188 && clickX < 233 && clickY > 6 && clickY < 34) { + + // translation editing radio selection + if (_state.editingTranslation) { + + hideJointSelectors(); + setBackground(_controlsBackgroundWalkEditJoints); + _state.editingTranslation = false; + + } else { + + hideJointSelectors(); + setBackground(_controlsBackgroundWalkEditHipTrans); + _state.editingTranslation = true; + } + initialiseJointsEditingPanel(); + return; + } + } + + switch (clickedOverlay) { + + case _offButton: + + _state.powerOn = true; + Overlays.editOverlay(_offButton, { + visible: false + }); + Overlays.editOverlay(_onButton, { + visible: true + }); + _state.setInternalState(state.STANDING); + return; + + case _controlsMinimisedTab: + + _state.minimised = false; + minimiseDialog(_state.minimised); + _state.setInternalState(_state.STANDING); + return; + + case _hideButton: + case _hideButtonSelected: + + Overlays.editOverlay(_hideButton, {visible: false}); + Overlays.editOverlay(_hideButtonSelected, {visible: true}); + _state.minimised = true; + momentaryButtonTimer = Script.setInterval(function() { + minimiseDialog(_state.minimised); + }, 80); + return; + + case _backButton: + + Overlays.editOverlay(_backButton, { + visible: false + }); + Overlays.editOverlay(_backButtonSelected, { + visible: true + }); + momentaryButtonTimer = Script.setInterval(function() { + + _state.setInternalState(_state.STANDING); + Overlays.editOverlay(_backButton, { + visible: false + }); + Overlays.editOverlay(_backButtonSelected, { + visible: false + }); + Script.clearInterval(momentaryButtonTimer); + momentaryButtonTimer = null; + }, 80); + return; + + case _footstepsBigButton: + + _motion.makesFootStepSounds = true; + Overlays.editOverlay(_footstepsBigButtonSelected, { + visible: true + }); + Overlays.editOverlay(_footstepsBigButton, { + visible: false + }); + return; + + case _footstepsBigButtonSelected: + + _motion.makesFootStepSounds = false; + Overlays.editOverlay(_footstepsBigButton, { + visible: true + }); + Overlays.editOverlay(_footstepsBigButtonSelected, { + visible: false + }); + return; + + case _femaleBigButton: + case _maleBigButtonSelected: + + _motion.setGender(FEMALE); + + Overlays.editOverlay(_femaleBigButtonSelected, { + visible: true + }); + Overlays.editOverlay(_femaleBigButton, { + visible: false + }); + Overlays.editOverlay(_maleBigButton, { + visible: true + }); + Overlays.editOverlay(_maleBigButtonSelected, { + visible: false + }); + return; + + case _maleBigButton: + case _femaleBigButtonSelected: + + _motion.setGender(MALE); + + Overlays.editOverlay(_femaleBigButton, { + visible: true + }); + Overlays.editOverlay(_femaleBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_maleBigButtonSelected, { + visible: true + }); + Overlays.editOverlay(_maleBigButton, { + visible: false + }); + return; + + case _armsFreeBigButton: + + _motion.armsFree = true; + + Overlays.editOverlay(_armsFreeBigButtonSelected, { + visible: true + }); + Overlays.editOverlay(_armsFreeBigButton, { + visible: false + }); + return; + + case _armsFreeBigButtonSelected: + + _motion.armsFree = false; + + Overlays.editOverlay(_armsFreeBigButtonSelected, { + visible: false + }); + Overlays.editOverlay(_armsFreeBigButton, { + visible: true + }); + return; + + case _standardWalkBigButton: + + if (_motion.avatarGender === FEMALE) { + _motion.selWalk = _motion.femaleStandardWalk; + } else { + _motion.selWalk = _motion.maleStandardWalk; + } + _motion.curAnim = _motion.selWalk; + initialiseWalkStylesPanel(true); + return; + + case _standardWalkBigButtonSelected: + + // toggle forwards / backwards walk display + if (_motion.direction === FORWARDS) { + _motion.direction = BACKWARDS; + } else { + _motion.direction = FORWARDS; + } + return; + + case _sliderOne: + + _movingSliderOne = true; + return; + + case _sliderTwo: + + _movingSliderTwo = true; + return; + + case _sliderThree: + + _movingSliderThree = true; + return; + + case _sliderFour: + + _movingSliderFour = true; + return; + + case _sliderFive: + + _movingSliderFive = true; + return; + + case _sliderSix: + + _movingSliderSix = true; + return; + + case _sliderSeven: + + _movingSliderSeven = true; + return; + + case _sliderEight: + + _movingSliderEight = true; + return; + + case _sliderNine: + + _movingSliderNine = true; + return; + + case _configWalkButtonSelected: + case _configStandButtonSelected: + case _configSideStepLeftButtonSelected: + case _configSideStepRightButtonSelected: + case _configFlyingButtonSelected: + case _configFlyingUpButtonSelected: + case _configFlyingDownButtonSelected: + case _configWalkStylesButtonSelected: + case _configWalkTweaksButtonSelected: + case _configWalkJointsButtonSelected: + + // exit edit modes + _motion.curAnim = _motion.selStand; + _state.setInternalState(_state.STANDING); + return; + + case _onButton: + + _state.powerOn = false; + _state.setInternalState(state.STANDING); + Overlays.editOverlay(_offButton, { + visible: true + }); + Overlays.editOverlay(_onButton, { + visible: false + }); + return; + + case _backButton: + case _backButtonSelected: + + Overlays.editOverlay(_backButton, { + visible: false + }); + Overlays.editOverlay(_backButtonSelected, { + visible: false + }); + _state.setInternalState(_state.STANDING); + return; + + case _configWalkStylesButton: + + _state.setInternalState(_state.EDIT_WALK_STYLES); + return; + + case _configWalkTweaksButton: + + _state.setInternalState(_state.EDIT_WALK_TWEAKS); + return; + + case _configWalkJointsButton: + + _state.setInternalState(_state.EDIT_WALK_JOINTS); + return; + + case _configWalkButton: + + _state.setInternalState(_state.EDIT_WALK_STYLES); + return; + + case _configStandButton: + + _state.setInternalState(_state.EDIT_STANDING); + return; + + case _configSideStepLeftButton: + + _state.setInternalState(_state.EDIT_SIDESTEP_LEFT); + return; + + case _configSideStepRightButton: + + _state.setInternalState(_state.EDIT_SIDESTEP_RIGHT); + return; + + case _configFlyingButton: + + _state.setInternalState(_state.EDIT_FLYING); + return; + + case _configFlyingUpButton: + + _state.setInternalState(_state.EDIT_FLYING_UP); + return; + + case _configFlyingDownButton: + + _state.setInternalState(_state.EDIT_FLYING_DOWN); + return; + } + }; + + function mouseMoveEvent(event) { + + // workaround for bug (https://worklist.net/20160) + if ((event.x > 310 && event.x < 318 && event.y > 1350 && event.y < 1355) || + (event.x > 423 && event.x < 428 && event.y > 1505 && event.y < 1508 )) { + return; + } + + if (_state.currentState === _state.EDIT_WALK_JOINTS || + _state.currentState === _state.EDIT_STANDING || + _state.currentState === _state.EDIT_FLYING || + _state.currentState === _state.EDIT_FLYING_UP || + _state.currentState === _state.EDIT_FLYING_DOWN || + _state.currentState === _state.EDIT_SIDESTEP_LEFT || + _state.currentState === _state.EDIT_SIDESTEP_RIGHT) { + + var thumbClickOffsetX = event.x - _minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX; + if (thumbPositionNormalised < 0) { + thumbPositionNormalised = 0; + } else if (thumbPositionNormalised > 1) { + thumbPositionNormalised = 1; + } + var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range + + if (_movingSliderOne) { + + // currently selected joint pitch or sway + Overlays.editOverlay(_sliderOne, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + _motion.curAnim.joints[0].sway = + thumbPositionNormalised * _sliderRanges.joints[0].swayRange; + } else { + _motion.curAnim.joints[_motion.curJointIndex].pitch = + thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].pitchRange; + } + + } else if (_movingSliderTwo) { + + // currently selected joint yaw or bob + Overlays.editOverlay(_sliderTwo, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + _motion.curAnim.joints[0].bob = + thumbPositionNormalised * _sliderRanges.joints[0].bobRange; + } else { + _motion.curAnim.joints[_motion.curJointIndex].yaw = + thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].yawRange; + } + + } else if (_movingSliderThree) { + + // currently selected joint roll or thrust + Overlays.editOverlay(_sliderThree, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + _motion.curAnim.joints[0].thrust = + thumbPositionNormalised * _sliderRanges.joints[0].thrustRange; + } else { + _motion.curAnim.joints[_motion.curJointIndex].roll = + thumbPositionNormalised * _sliderRanges.joints[_motion.curJointIndex].rollRange; + } + + } else if (_movingSliderFour) { + + // currently selected joint pitch phase + Overlays.editOverlay(_sliderFour, { + x: sliderX + _minSliderX + }); + + var newPhase = 360 * thumbPositionNormalised - 180; + + if (_state.editingTranslation) { + _motion.curAnim.joints[0].swayPhase = newPhase; + } else { + _motion.curAnim.joints[_motion.curJointIndex].pitchPhase = newPhase; + } + + } else if (_movingSliderFive) { + + // currently selected joint yaw phase; + Overlays.editOverlay(_sliderFive, { + x: sliderX + _minSliderX + }); + + var newPhase = 360 * thumbPositionNormalised - 180; + + if (_state.editingTranslation) { + _motion.curAnim.joints[0].bobPhase = newPhase; + } else { + _motion.curAnim.joints[_motion.curJointIndex].yawPhase = newPhase; + } + + } else if (_movingSliderSix) { + + // currently selected joint roll phase + Overlays.editOverlay(_sliderSix, { + x: sliderX + _minSliderX + }); + + var newPhase = 360 * thumbPositionNormalised - 180; + + if (_state.editingTranslation) { + _motion.curAnim.joints[0].thrustPhase = newPhase; + } else { + _motion.curAnim.joints[_motion.curJointIndex].rollPhase = newPhase; + } + + } else if (_movingSliderSeven) { + + // currently selected joint pitch offset + Overlays.editOverlay(_sliderSeven, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[0].swayOffsetRange; + _motion.curAnim.joints[0].swayOffset = newOffset; + } else { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[_motion.curJointIndex].pitchOffsetRange; + _motion.curAnim.joints[_motion.curJointIndex].pitchOffset = newOffset; + } + + } else if (_movingSliderEight) { + + // currently selected joint yaw offset + Overlays.editOverlay(_sliderEight, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 *_sliderRanges.joints[0].bobOffsetRange; + _motion.curAnim.joints[0].bobOffset = newOffset; + } else { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[_motion.curJointIndex].yawOffsetRange; + _motion.curAnim.joints[_motion.curJointIndex].yawOffset = newOffset; + } + + } else if (_movingSliderNine) { + + // currently selected joint roll offset + Overlays.editOverlay(_sliderNine, { + x: sliderX + _minSliderX + }); + if (_state.editingTranslation) { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[0].thrustOffsetRange; + _motion.curAnim.joints[0].thrustOffset = newOffset; + } else { + var newOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[_motion.curJointIndex].rollOffsetRange; + _motion.curAnim.joints[_motion.curJointIndex].rollOffset = newOffset; + } + } + + // end if editing joints + + } else if (_state.currentState === _state.EDIT_WALK_TWEAKS) { + + // sliders for commonly required walk adjustments + var thumbClickOffsetX = event.x - _minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / _sliderRangeX; + if (thumbPositionNormalised < 0) thumbPositionNormalised = 0; + if (thumbPositionNormalised > 1) thumbPositionNormalised = 1; + var sliderX = thumbPositionNormalised * _sliderRangeX; // sets range + + if (_movingSliderOne) { + // walk speed + Overlays.editOverlay(_sliderOne, { + x: sliderX + _minSliderX + }); + _motion.curAnim.calibration.frequency = thumbPositionNormalised * MAX_WALK_SPEED; + } else if (_movingSliderTwo) { + // lean (hips pitch offset) + Overlays.editOverlay(_sliderTwo, { + x: sliderX + _minSliderX + }); + var newOffset = (thumbPositionNormalised - 0.5) * 2 * _sliderRanges.joints[0].pitchOffsetRange; + _motion.curAnim.joints[0].pitchOffset = newOffset; + } else if (_movingSliderThree) { + // stride (upper legs pitch) + Overlays.editOverlay(_sliderThree, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[1].pitch = thumbPositionNormalised * _sliderRanges.joints[1].pitchRange; + } else if (_movingSliderFour) { + // legs separation (upper legs roll) + Overlays.editOverlay(_sliderFour, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[1].rollOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[1].rollOffsetRange; + } else if (_movingSliderFive) { + // legs forward (lower legs pitch offset) + Overlays.editOverlay(_sliderFive, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[1].pitchOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[1].pitchOffsetRange; + } else if (_movingSliderSix) { + // lower legs splay (lower legs roll offset) + Overlays.editOverlay(_sliderSix, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[2].rollOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[2].rollOffsetRange; + + } else if (_movingSliderSeven) { + // arms forward (upper arms yaw offset) + Overlays.editOverlay(_sliderSeven, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[9].yawOffset = (thumbPositionNormalised - 0.5) * + 2 * _sliderRanges.joints[9].yawOffsetRange; + } else if (_movingSliderEight) { + // arms out (upper arm pitch offset) + Overlays.editOverlay(_sliderEight, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[9].pitchOffset = (thumbPositionNormalised - 0.5) * + -2 * _sliderRanges.joints[9].pitchOffsetRange; + } else if (_movingSliderNine) { + // lower arms splay (lower arm pitch offset) + Overlays.editOverlay(_sliderNine, { + x: sliderX + _minSliderX + }); + _motion.curAnim.joints[10].pitchOffset = (thumbPositionNormalised - 0.5) * + -2 * _sliderRanges.joints[10].pitchOffsetRange; + } + } // if tweaking + }; + + function mouseReleaseEvent(event) { + + if (_movingSliderOne) { + _movingSliderOne = false; + } else if (_movingSliderTwo) { + _movingSliderTwo = false; + } else if (_movingSliderThree) { + _movingSliderThree = false; + } else if (_movingSliderFour) { + _movingSliderFour = false; + } else if (_movingSliderFive) { + _movingSliderFive = false; + } else if (_movingSliderSix) { + _movingSliderSix = false; + } else if (_movingSliderSeven) { + _movingSliderSeven = false; + } else if (_movingSliderEight) { + _movingSliderEight = false; + } else if (_movingSliderNine) { + _movingSliderNine = false; + } + }; + + Controller.mousePressEvent.connect(mousePressEvent); + Controller.mouseMoveEvent.connect(mouseMoveEvent); + Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + + // Script ending + Script.scriptEnding.connect(function() { + + // delete the background overlays + for (var i in _backgroundOverlays) { + Overlays.deleteOverlay(_backgroundOverlays[i]); + } + // delete the button overlays + for (var i in _buttonOverlays) { + Overlays.deleteOverlay(_buttonOverlays[i]); + } + // delete the slider thumb overlays + for (var i in _sliderThumbOverlays) { + Overlays.deleteOverlay(_sliderThumbOverlays[i]); + } + // delete the character joint control overlays + for (var i in _jointsControlOverlays) { + Overlays.deleteOverlay(_jointsControlOverlays[i]); + } + // delete the big button overlays + for (var i in _bigbuttonOverlays) { + Overlays.deleteOverlay(_bigbuttonOverlays[i]); + } + // delete the mimimised tab + Overlays.deleteOverlay(_controlsMinimisedTab); + }); + + // public methods + return { + + // gather references to objects from the walk.js script + initialise: function(state, motion, walkAssets) { + + _state = state; + _motion = motion; + _walkAssets = walkAssets; + }, + + updateMenu: function() { + + if (!_state.minimised) { + + switch (_state.currentState) { + + case _state.EDIT_WALK_STYLES: + case _state.EDIT_WALK_TWEAKS: + case _state.EDIT_WALK_JOINTS: { + + hideMenuButtons(); + initialiseFrontPanel(false); + hideJointSelectors(); + + if (_state.currentState === _state.EDIT_WALK_STYLES) { + + setBackground(_controlsBackgroundWalkEditStyles); + initialiseWalkStylesPanel(true); + setSliderThumbsVisible(false); + hideJointSelectors(); + setButtonOverlayVisible(_configWalkStylesButtonSelected); + setButtonOverlayVisible(_configWalkTweaksButton); + setButtonOverlayVisible(_configWalkJointsButton); + + } else if (_state.currentState === _state.EDIT_WALK_TWEAKS) { + + setBackground(_controlsBackgroundWalkEditTweaks); + initialiseWalkStylesPanel(false); + setSliderThumbsVisible(true); + hideJointSelectors(); + initialiseWalkTweaksPanel(); + setButtonOverlayVisible(_configWalkStylesButton); + setButtonOverlayVisible(_configWalkTweaksButtonSelected); + setButtonOverlayVisible(_configWalkJointsButton); + + } else if (_state.currentState === _state.EDIT_WALK_JOINTS) { + + if (_state.editingTranslation) { + setBackground(_controlsBackgroundWalkEditHipTrans); + } else { + setBackground(_controlsBackgroundWalkEditJoints); + } + + initialiseWalkStylesPanel(false); + setSliderThumbsVisible(true); + setButtonOverlayVisible(_configWalkStylesButton); + setButtonOverlayVisible(_configWalkTweaksButton); + setButtonOverlayVisible(_configWalkJointsButtonSelected); + initialiseJointsEditingPanel(); + } + setButtonOverlayVisible(_onButton); + setButtonOverlayVisible(_backButton); + return; + } + + case _state.EDIT_STANDING: + case _state.EDIT_SIDESTEP_LEFT: + case _state.EDIT_SIDESTEP_RIGHT: { + + if (_state.editingTranslation) { + setBackground(_controlsBackgroundWalkEditHipTrans); + } else { + setBackground(_controlsBackgroundWalkEditJoints); + } + hideMenuButtons(); + initialiseWalkStylesPanel(false); + initialiseFrontPanel(false); + + if (_state.currentState === _state.EDIT_SIDESTEP_LEFT) { + + setButtonOverlayVisible(_configSideStepRightButton); + setButtonOverlayVisible(_configSideStepLeftButtonSelected); + setButtonOverlayVisible(_configStandButton); + + } else if (_state.currentState === _state.EDIT_SIDESTEP_RIGHT) { + + setButtonOverlayVisible(_configSideStepRightButtonSelected); + setButtonOverlayVisible(_configSideStepLeftButton); + setButtonOverlayVisible(_configStandButton); + + } else if (_state.currentState === _state.EDIT_STANDING) { + + setButtonOverlayVisible(_configSideStepRightButton); + setButtonOverlayVisible(_configSideStepLeftButton); + setButtonOverlayVisible(_configStandButtonSelected); + } + initialiseJointsEditingPanel(); + setButtonOverlayVisible(_onButton); + setButtonOverlayVisible(_backButton); + return; + } + + case _state.EDIT_FLYING: + case _state.EDIT_FLYING_UP: + case _state.EDIT_FLYING_DOWN: { + + setBackground(_controlsBackgroundWalkEditJoints); + hideMenuButtons(); + initialiseWalkStylesPanel(false); + initialiseFrontPanel(false); + if (_state.currentState === _state.EDIT_FLYING) { + + setButtonOverlayVisible(_configFlyingUpButton); + setButtonOverlayVisible(_configFlyingDownButton); + setButtonOverlayVisible(_configFlyingButtonSelected); + + } else if (_state.currentState === _state.EDIT_FLYING_UP) { + + setButtonOverlayVisible(_configFlyingUpButtonSelected); + setButtonOverlayVisible(_configFlyingDownButton); + setButtonOverlayVisible(_configFlyingButton); + + } else if (_state.currentState === _state.EDIT_FLYING_DOWN) { + + setButtonOverlayVisible(_configFlyingUpButton); + setButtonOverlayVisible(_configFlyingDownButtonSelected); + setButtonOverlayVisible(_configFlyingButton); + } + initialiseJointsEditingPanel(); + setButtonOverlayVisible(_onButton); + setButtonOverlayVisible(_backButton); + return; + } + + case _state.STANDING: + case _state.WALKING: + case _state.FLYING: + case _state.SIDE_STEP: + default: { + + hideMenuButtons(); + hideJointSelectors(); + setBackground(_controlsBackground); + if (_state.powerOn) { + setButtonOverlayVisible(_onButton); + } else { + setButtonOverlayVisible(_offButton); + } + setButtonOverlayVisible(_configWalkButton); + setButtonOverlayVisible(_configStandButton); + setButtonOverlayVisible(_configFlyingButton); + setButtonOverlayVisible(_hideButton); + setSliderThumbsVisible(false); + initialiseFrontPanel(true); + initialiseWalkStylesPanel(false); + return; + } + } + } + } + }; // end public methods (return) +})(); \ No newline at end of file diff --git a/examples/lightExample.js b/examples/lightExample.js new file mode 100644 index 0000000000..7a90eb8714 --- /dev/null +++ b/examples/lightExample.js @@ -0,0 +1,46 @@ +// +// lightExample.js +// examples +// +// Created by Philip Rosedale on November 5, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Makes a light right in front of your avatar, as well as a sphere at that location. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var position = Vec3.sum(MyAvatar.position, Quat.getFront(Camera.getOrientation())); + +var sphereID = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { x: 0.1, y: 0.1, z: 0.1 }, + color: { red: 255, green: 255, blue: 0 } + }); + +var lightID = Entities.addEntity({ + type: "Light", + position: position, + dimensions: { x: 1, y: 1, z: 1 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + angularDamping: 0, + + isSpotlight: false, + diffuseColor: { red: 255, green: 255, blue: 0 }, + ambientColor: { red: 0, green: 0, blue: 0 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 0, + linearAttenuation: 1, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees +}); + +Script.scriptEnding.connect(function() { + print("Deleted sphere and light"); + Entities.deleteEntity(sphereID); + Entities.deleteEntity(lightID); +}); diff --git a/examples/walk.js b/examples/walk.js index a9e8f401d6..279516aa2a 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -1,3851 +1,2612 @@ // // walk.js // -// version 1.007b +// version 1.1 // -// Created by Davedub, August / September 2014 +// Created by David Wooldridge, Autumn 2014 +// +// Animates an avatar using procedural animation techniques // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// path to the animation files -var pathToAnimFiles = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/animation-files/'; // working (but only without https) - -// path to the images used for the overlays -var pathToOverlays = 'http://s3-us-west-1.amazonaws.com/highfidelity-public/procedural-animator/overlays/'; // working (but only without https) - -// path to the sounds used for the footsteps -var pathToSounds = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Footsteps/'; - -// load the animation datafiles -Script.include(pathToAnimFiles+"dd-female-strut-walk-animation.js"); -Script.include(pathToAnimFiles+"dd-female-flying-up-animation.js"); -Script.include(pathToAnimFiles+"dd-female-flying-animation.js"); -Script.include(pathToAnimFiles+"dd-female-flying-down-animation.js"); -Script.include(pathToAnimFiles+"dd-female-standing-one-animation.js"); -Script.include(pathToAnimFiles+"dd-female-sidestep-left-animation.js"); -Script.include(pathToAnimFiles+"dd-female-sidestep-right-animation.js"); -Script.include(pathToAnimFiles+"dd-male-strut-walk-animation.js"); -Script.include(pathToAnimFiles+"dd-male-flying-up-animation.js"); -Script.include(pathToAnimFiles+"dd-male-flying-animation.js"); -Script.include(pathToAnimFiles+"dd-male-flying-down-animation.js"); -Script.include(pathToAnimFiles+"dd-male-standing-one-animation.js"); -Script.include(pathToAnimFiles+"dd-male-sidestep-left-animation.js"); -Script.include(pathToAnimFiles+"dd-male-sidestep-right-animation.js"); - -// read in the data from the animation files -var FemaleStrutWalkFile = new FemaleStrutWalk(); -var femaleStrutWalk = FemaleStrutWalkFile.loadAnimation(); -var FemaleFlyingUpFile = new FemaleFlyingUp(); -var femaleFlyingUp = FemaleFlyingUpFile.loadAnimation(); -var FemaleFlyingFile = new FemaleFlying(); -var femaleFlying = FemaleFlyingFile.loadAnimation(); -var FemaleFlyingDownFile = new FemaleFlyingDown(); -var femaleFlyingDown = FemaleFlyingDownFile.loadAnimation(); -var FemaleStandOneFile = new FemaleStandingOne(); -var femaleStandOne = FemaleStandOneFile.loadAnimation(); -var FemaleSideStepLeftFile = new FemaleSideStepLeft(); -var femaleSideStepLeft = FemaleSideStepLeftFile.loadAnimation(); -var FemaleSideStepRightFile = new FemaleSideStepRight(); -var femaleSideStepRight = FemaleSideStepRightFile.loadAnimation(); -var MaleStrutWalkFile = new MaleStrutWalk(); -var maleStrutWalk = MaleStrutWalkFile.loadAnimation(); -var MaleFlyingUpFile = new MaleFlyingUp(); -var maleFlyingUp = MaleFlyingUpFile.loadAnimation(); -var MaleFlyingFile = new MaleFlying(); -var maleFlying = MaleFlyingFile.loadAnimation(); -var MaleFlyingDownFile = new MaleFlyingDown(); -var maleFlyingDown = MaleFlyingDownFile.loadAnimation(); -var MaleStandOneFile = new MaleStandingOne(); -var maleStandOne = MaleStandOneFile.loadAnimation(); -var MaleSideStepLeftFile = new MaleSideStepLeft(); -var maleSideStepLeft = MaleSideStepLeftFile.loadAnimation(); -var MaleSideStepRightFile = new MaleSideStepRight(); -var maleSideStepRight = MaleSideStepRightFile.loadAnimation(); - -// read in the sounds -var footsteps = []; -footsteps.push(new Sound(pathToSounds+"FootstepW2Left-12db.wav")); -footsteps.push(new Sound(pathToSounds+"FootstepW2Right-12db.wav")); -footsteps.push(new Sound(pathToSounds+"FootstepW3Left-12db.wav")); -footsteps.push(new Sound(pathToSounds+"FootstepW3Right-12db.wav")); -footsteps.push(new Sound(pathToSounds+"FootstepW5Left-12db.wav")); -footsteps.push(new Sound(pathToSounds+"FootstepW5Right-12db.wav")); - -// all slider controls have a range (with the exception of phase controls that are always +-180) so we store them all here -var sliderRanges = {"joints":[{"name":"hips","pitchRange":25,"yawRange":25,"rollRange":25,"pitchOffsetRange":25,"yawOffsetRange":25,"rollOffsetRange":25,"thrustRange":0.01,"bobRange":0.02,"swayRange":0.01},{"name":"upperLegs","pitchRange":90,"yawRange":35,"rollRange":35,"pitchOffsetRange":60,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"lowerLegs","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"feet","pitchRange":60,"yawRange":20,"rollRange":20,"pitchOffsetRange":60,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"toes","pitchRange":90,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":20,"rollOffsetRange":20},{"name":"spine","pitchRange":40,"yawRange":40,"rollRange":40,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine1","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"spine2","pitchRange":20,"yawRange":40,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":50,"rollOffsetRange":50},{"name":"shoulders","pitchRange":35,"yawRange":40,"rollRange":20,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"upperArms","pitchRange":90,"yawRange":90,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"lowerArms","pitchRange":90,"yawRange":90,"rollRange":120,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"hands","pitchRange":90,"yawRange":180,"rollRange":90,"pitchOffsetRange":180,"yawOffsetRange":180,"rollOffsetRange":180},{"name":"head","pitchRange":20,"yawRange":20,"rollRange":20,"pitchOffsetRange":90,"yawOffsetRange":90,"rollOffsetRange":90}]}; - -// internal state (FSM based) constants -var STANDING = 1; -var WALKING = 2; -var SIDE_STEPPING = 3; -var FLYING = 4; -var CONFIG_WALK_STYLES = 5; -var CONFIG_WALK_TWEAKS = 6; -var CONFIG_WALK_JOINTS = 7; -var CONFIG_STANDING = 8; -var CONFIG_FLYING = 9; -var CONFIG_FLYING_UP = 10; -var CONFIG_FLYING_DOWN = 11; -var CONFIG_SIDESTEP_LEFT = 12; -var CONFIG_SIDESTEP_RIGHT = 14; -var INTERNAL_STATE = STANDING; - -// status -var powerOn = true; -var paused = false; // pause animation playback whilst adjusting certain parameters -var minimised = true; -var armsFree = true; // set true for hydra support - temporary fix for Hydras -var statsOn = false; -var playFootStepSounds = true; - // constants -var MAX_WALK_SPEED = 1257; // max oscillation speed -var FLYING_SPEED = 6.4;// 12.4; // m/s - real humans can't run any faster than 12.4 m/s -var TERMINAL_VELOCITY = 300; // max speed imposed by Interface -var DIRECTION_UP = 1; -var DIRECTION_DOWN = 2; -var DIRECTION_LEFT = 4; -var DIRECTION_RIGHT = 8; -var DIRECTION_FORWARDS = 16; -var DIRECTION_BACKWARDS = 32; -var DIRECTION_NONE = 64; var MALE = 1; var FEMALE = 2; +var MAX_WALK_SPEED = 2.5;//3.919; +var TAKE_FLIGHT_SPEED = 4.55; +var TOP_SPEED = 300; +var UP = 1; +var DOWN = 2; +var LEFT = 4; +var RIGHT = 8; +var FORWARDS = 16; +var BACKWARDS = 32; -// start of animation control section -var cumulativeTime = 0.0; -var lastOrientation; +// location of animation files and overlay images +var pathToAssets = 'http://s3.amazonaws.com/hifi-public/WalkScript/'; -// avi gender and default animations -var avatarGender = MALE; -var selectedWalk = maleStrutWalk; // the currently selected animation walk file (to edit any animation, paste it's name here and select walk editing mode) -var selectedStand = maleStandOne; -var selectedFlyUp = maleFlyingUp; -var selectedFly = maleFlying; -var selectedFlyDown = maleFlyingDown; -var selectedSideStepLeft = maleSideStepLeft; -var selectedSideStepRight = maleSideStepRight; -if(avatarGender===FEMALE) { +// load the UI +Script.include("./libraries/walkInterface.js"); - // to make toggling the default quick - selectedWalk = femaleStrutWalk; - selectedStand = femaleStandOne; - selectedFlyUp = femaleFlyingUp; - selectedFly = femaleFlying; - selectedFlyDown = femaleFlyingDown; - selectedSideStepLeft = femaleSideStepLeft; - selectedSideStepRight = femaleSideStepRight; -} -var currentAnimation = selectedStand; // the current animation -var selectedJointIndex = 0; // the index of the joint currently selected for editing -var currentTransition = null; // used as a pointer to a Transition +// load filters (Bezier, Butterworth, add harmonics, averaging) +Script.include("./libraries/walkFilters.js"); -// walkwheel (foot / ground speed matching) -var sideStepCycleStartLeft = 270; -var sideStepCycleStartRight = 90; -var walkWheelPosition = 0; -var nextStep = DIRECTION_RIGHT; // first step is always right, because the sine waves say so. Unless you're mirrored. -var nFrames = 0; // counts number of frames -var strideLength = 0; // stride calibration -var aviFootSize = {x:0.1, y:0.1, z:0.25}; // experimental values for addition to stride length - TODO: analyse and confirm is increasing smaller stride lengths accuracy once we have better ground detection +// load objects, constructors and assets (state, Motion, Transition, walkAssets) +Script.include("./libraries/walkApi.js"); -// stats -var frameStartTime = 0; // when the frame first starts we take a note of the time -var frameExecutionTimeMax = 0; // keep track of the longest frame execution time +// initialise the motion state / history object +var motion = new Motion(); -// constructor for recent RecentMotion (i.e. frame data) class -function RecentMotion(velocity, acceleration, principleDirection, state) { - this.velocity = velocity; - this.acceleration = acceleration; - this.principleDirection = principleDirection; - this.state = state; -} +// initialise Transitions +var nullTransition = new Transition(); +motion.curTransition = nullTransition; -// constructor for the FramesHistory object -function FramesHistory() { - this.recentMotions = []; - for(var i = 0 ; i < 10 ; i++) { - var blank = new RecentMotion({ x:0, y:0, z:0 }, { x:0, y:0, z:0 }, DIRECTION_FORWARDS, STANDING ); - this.recentMotions.push(blank); - } +// initialise the UI +walkInterface.initialise(state, motion, walkAssets); - // recentDirection 'method' args: (direction enum, number of steps back to compare) - this.recentDirection = function () { +// wave shapes +var SAWTOOTH = 1; +var TRIANGLE = 2; +var SQUARE = 4; - if( arguments[0] && arguments[1] ) { +// various filters for synthesising more complex, natural waveforms +var leanPitchFilter = filter.createAveragingFilter(15); +var leanRollFilter = filter.createAveragingFilter(15); +var hipsYawShaper = filter.createWaveSynth(TRIANGLE, 3, 2); +var hipsBobLPFilter = filter.createButterworthFilter(5); - var directionCount = 0; - if( arguments[1] > this.recentMotions.length ) - arguments[1] = this.recentMotions.length; - - for(var i = 0 ; i < arguments[1] ; i++ ) { - if( this.recentMotions[i].principleDirection === arguments[0] ) - directionCount++; - } - return directionCount / arguments[1] === 1 ? true : false; - } - return false; - } - - // recentState 'method' args: (state enum, number of steps back to compare) - this.recentState = function () { - - if( arguments[0] && arguments[1] ) { - - var stateCount = 0; - if( arguments[1] > this.recentMotions.length ) - arguments[1] = this.recentMotions.length; - - for(var i = 0 ; i < arguments[1] ; i++ ) { - if( this.recentMotions[i].state === arguments[0] ) - stateCount++; - } - return stateCount / arguments[1] === 1 ? true : false; - } - return false; - } - this.lastWalkStartTime = 0; // short walks and long walks need different handling -} -var framesHistory = new FramesHistory(); - -// constructor for animation Transition class -function Transition(lastAnimation, nextAnimation, reachPoses, transitionDuration, easingLower, easingUpper) { - this.lastAnimation = lastAnimation; // name of last animation - if(lastAnimation === selectedWalk || - nextAnimation === selectedSideStepLeft || - nextAnimation === selectedSideStepRight) - this.walkingAtStart = true; // boolean - is the last animation a walking animation? - else - this.walkingAtStart = false; // boolean - is the last animation a walking animation? - this.nextAnimation = nextAnimation; // name of next animation - if(nextAnimation === selectedWalk || - nextAnimation === selectedSideStepLeft || - nextAnimation === selectedSideStepRight) - this.walkingAtEnd = true; // boolean - is the next animation a walking animation? - else - this.walkingAtEnd = false; // boolean - is the next animation a walking animation? - this.reachPoses = reachPoses; // array of reach poses - am very much looking forward to putting these in! - this.transitionDuration = transitionDuration; // length of transition (seconds) - this.easingLower = easingLower; // Bezier curve handle (normalised) - this.easingUpper = easingUpper; // Bezier curve handle (normalised) - this.startTime = new Date().getTime(); // Starting timestamp (seconds) - this.progress = 0; // how far are we through the transition? - this.walkWheelIncrement = 3; // how much to turn the walkwheel each frame when coming to a halt. Get's set to 0 once the feet are under the avi - this.walkWheelAdvance = 0; // how many degrees the walk wheel has been advanced during the transition - this.walkStopAngle = 0; // what angle should we stop the walk cycle? (calculated on the fly) -} - -// convert a local (to the avi) translation to a global one -function localToGlobal(localTranslation) { - - var aviOrientation = MyAvatar.orientation; - var front = Quat.getFront(aviOrientation); - var right = Quat.getRight(aviOrientation); - var up = Quat.getUp (aviOrientation); - var aviFront = Vec3.multiply(front,localTranslation.z); - var aviRight = Vec3.multiply(right,localTranslation.x); - var aviUp = Vec3.multiply(up ,localTranslation.y); - var globalTranslation = {x:0,y:0,z:0}; // final value - globalTranslation = Vec3.sum(globalTranslation, aviFront); - globalTranslation = Vec3.sum(globalTranslation, aviRight); - globalTranslation = Vec3.sum(globalTranslation, aviUp); - return globalTranslation; -} - -// similar ot above - convert hips translations to global and apply -function translateHips(localHipsTranslation) { - - var aviOrientation = MyAvatar.orientation; - var front = Quat.getFront(aviOrientation); - var right = Quat.getRight(aviOrientation); - var up = Quat.getUp (aviOrientation); - var aviFront = Vec3.multiply(front,localHipsTranslation.y); - var aviRight = Vec3.multiply(right,localHipsTranslation.x); - var aviUp = Vec3.multiply(up ,localHipsTranslation.z); - var AviTranslationOffset = {x:0,y:0,z:0}; // final value - AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviFront); - AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviRight); - AviTranslationOffset = Vec3.sum(AviTranslationOffset, aviUp); - MyAvatar.position = {x: MyAvatar.position.x + AviTranslationOffset.x, - y: MyAvatar.position.y + AviTranslationOffset.y, - z: MyAvatar.position.z + AviTranslationOffset.z }; -} - -// clear all joint data -function resetJoints() { - - var avatarJointNames = MyAvatar.getJointNames(); - for (var i = 0; i < avatarJointNames.length; i++) { - MyAvatar.clearJointData(avatarJointNames[i]); - } -} - -// play footstep sound -function playFootstep(side) { - - var options = new AudioInjectionOptions(); - options.position = Camera.getPosition(); - options.volume = 0.5; - var walkNumber = 2; // 0 to 2 - if(side===DIRECTION_RIGHT && playFootStepSounds) { - Audio.playSound(footsteps[walkNumber+1], options); - } - else if(side===DIRECTION_LEFT && playFootStepSounds) { - Audio.playSound(footsteps[walkNumber], options); - } -} - -// put the fingers into a relaxed pose -function curlFingers() { - - // left hand fingers - for(var i = 18 ; i < 34 ; i++) { - MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0)); - } - // left hand thumb - for(var i = 34 ; i < 38 ; i++) { - MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0)); - } - // right hand fingers - for(var i = 42 ; i < 58 ; i++) { - MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(8,0,0)); - } - // right hand thumb - for(var i = 58 ; i < 62 ; i++) { - MyAvatar.setJointData(jointList[i], Quat.fromPitchYawRollDegrees(0,0,0)); - } -} - -// additional maths functions -function degToRad( degreesValue ) { return degreesValue * Math.PI / 180; } -function radToDeg( radiansValue ) { return radiansValue * 180 / Math.PI; } - -// animateAvatar - sine wave generators working like clockwork -function animateAvatar( deltaTime, velocity, principleDirection ) { - - // adjusting the walk speed in edit mode causes a nasty flicker. pausing the animation stops this - if(paused) return; - - var cycle = cumulativeTime; - var transitionProgress = 1; - var adjustedFrequency = currentAnimation.settings.baseFrequency; - - // upper legs phase reversal for walking backwards - var forwardModifier = 1; - if(principleDirection===DIRECTION_BACKWARDS) - forwardModifier = -1; - - // don't want to lean forwards if going directly upwards - var leanPitchModifier = 1; - if(principleDirection===DIRECTION_UP) - leanPitchModifier = 0; - - // is there a Transition to include? - if(currentTransition!==null) { - - // if is a new transiton - if(currentTransition.progress===0) { - - if( currentTransition.walkingAtStart ) { - - if( INTERNAL_STATE !== SIDE_STEPPING ) { - - // work out where we want the walk cycle to stop - var leftStop = selectedWalk.settings.stopAngleForwards + 180; - var rightStop = selectedWalk.settings.stopAngleForwards; - if( principleDirection === DIRECTION_BACKWARDS ) { - leftStop = selectedWalk.settings.stopAngleBackwards + 180; - rightStop = selectedWalk.settings.stopAngleBackwards; - } - - // find the closest stop point from the walk wheel's angle - var angleToLeftStop = 180 - Math.abs( Math.abs( walkWheelPosition - leftStop ) - 180); - var angleToRightStop = 180 - Math.abs( Math.abs( walkWheelPosition - rightStop ) - 180); - if( walkWheelPosition > angleToLeftStop ) angleToLeftStop = 360 - angleToLeftStop; - if( walkWheelPosition > angleToRightStop ) angleToRightStop = 360 - angleToRightStop; - - currentTransition.walkWheelIncrement = 6; - - // keep the walkwheel turning by setting the walkWheelIncrement until our feet are tucked nicely underneath us. - if( angleToLeftStop < angleToRightStop ) { - - currentTransition.walkStopAngle = leftStop; - - } else { - - currentTransition.walkStopAngle = rightStop; - } - - } else { - - // freeze wheel for sidestepping transitions - currentTransition.walkWheelIncrement = 0; - } - } - } // end if( currentTransition.walkingAtStart ) - - // calculate the Transition progress - var elapasedTime = (new Date().getTime() - currentTransition.startTime) / 1000; - currentTransition.progress = elapasedTime / currentTransition.transitionDuration; - transitionProgress = getBezier((1-currentTransition.progress), {x:0,y:0}, currentTransition.easingLower, currentTransition.easingUpper, {x:1,y:1}).y; - - if(currentTransition.progress>=1) { - - // time to kill off the transition - delete currentTransition; - currentTransition = null; - - } else { - - if( currentTransition.walkingAtStart ) { - - if( INTERNAL_STATE !== SIDE_STEPPING ) { - - // if at a stop angle, hold the walk wheel position for remainder of transition - var tolerance = 7; // must be greater than the walkWheel increment - if(( walkWheelPosition > (currentTransition.walkStopAngle - tolerance )) && - ( walkWheelPosition < (currentTransition.walkStopAngle + tolerance ))) { - - currentTransition.walkWheelIncrement = 0; - } - // keep turning walk wheel until both feet are below the avi - walkWheelPosition += currentTransition.walkWheelIncrement; - currentTransition.walkWheelAdvance += currentTransition.walkWheelIncrement; - } - } - } - } - - // will we need to use the walk wheel this frame? - if(currentAnimation === selectedWalk || - currentAnimation === selectedSideStepLeft || - currentAnimation === selectedSideStepRight || - currentTransition !== null) { - - // set the stride length - if( INTERNAL_STATE!==SIDE_STEPPING && - INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT && - INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT && - currentTransition===null ) { - - // if the timing's right, take a snapshot of the stride max and recalibrate - var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy - var strideOne = 40; - var strideTwo = 220; - - if( principleDirection === DIRECTION_BACKWARDS ) { - strideOne = 130; - strideTwo = 300; - } - - if(( walkWheelPosition < (strideOne+tolerance) && walkWheelPosition > (strideOne-tolerance) ) || - ( walkWheelPosition < (strideTwo+tolerance) && walkWheelPosition > (strideTwo-tolerance) )) { - - // calculate the feet's offset from each other (in local Z only) - var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot")); - var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot")); - var footOffsetZ = Math.abs(footRPos.z - footLPos.z); - if(footOffsetZ>1) strideLength = 2 * footOffsetZ + aviFootSize.z; // sometimes getting very low value here - just ignore for now - if(statsOn) print('Stride length calibrated to '+strideLength.toFixed(4)+' metres at '+walkWheelPosition.toFixed(1)+' degrees'); - if(principleDirection===DIRECTION_FORWARDS) { - currentAnimation.calibration.strideLengthForwards = strideLength; - } else if (principleDirection===DIRECTION_BACKWARDS) { - currentAnimation.calibration.strideLengthBackwards = strideLength; - } - - } else { - - if(principleDirection===DIRECTION_FORWARDS) - strideLength = currentAnimation.calibration.strideLengthForwards; - else if (principleDirection===DIRECTION_BACKWARDS) - strideLength = currentAnimation.calibration.strideLengthBackwards; - } - } - else if(( INTERNAL_STATE===SIDE_STEPPING || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) ) { - - // calculate lateral stride - same same, but different - // if the timing's right, take a snapshot of the stride max and recalibrate the stride length - var tolerance = 1.0; // higher the number, the higher the chance of a calibration taking place, but is traded off with lower accuracy - if(principleDirection===DIRECTION_LEFT) { - - if( walkWheelPosition < (3+tolerance) && walkWheelPosition > (3-tolerance) ) { - - // calculate the feet's offset from the hips (in local X only) - var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot")); - var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot")); - var footOffsetX = Math.abs(footRPos.x - footLPos.x); - strideLength = footOffsetX; - if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees'); - currentAnimation.calibration.strideLengthLeft = strideLength; - - } else { - - strideLength = currentAnimation.calibration.strideLengthLeft; - } - } - else if (principleDirection===DIRECTION_RIGHT) { - - if( walkWheelPosition < (170+tolerance) && walkWheelPosition > (170-tolerance) ) { - - // calculate the feet's offset from the hips (in local X only) - var footRPos = localToGlobal(MyAvatar.getJointPosition("RightFoot")); - var footLPos = localToGlobal(MyAvatar.getJointPosition("LeftFoot")); - var footOffsetX = Math.abs(footRPos.x - footLPos.x); - strideLength = footOffsetX; - if(statsOn) print('Stride width calibrated to '+strideLength.toFixed(4)+ ' metres at '+walkWheelPosition.toFixed(1)+' degrees'); - currentAnimation.calibration.strideLengthRight = strideLength; - - } else { - - strideLength = currentAnimation.calibration.strideLengthRight; - } - } - } // end stride length calculations - - // wrap the stride length around a 'surveyor's wheel' twice and calculate the angular velocity at the given (linear) velocity - // omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride length - var wheelRadius = strideLength / Math.PI; - var angularVelocity = velocity / wheelRadius; - - // calculate the degrees turned (at this angular velocity) since last frame - var radiansTurnedSinceLastFrame = deltaTime * angularVelocity; - var degreesTurnedSinceLastFrame = radToDeg(radiansTurnedSinceLastFrame); - - // if we are in an edit mode, we will need fake time to turn the wheel - if( INTERNAL_STATE!==WALKING && - INTERNAL_STATE!==SIDE_STEPPING ) - degreesTurnedSinceLastFrame = currentAnimation.settings.baseFrequency / 70; - - if( walkWheelPosition >= 360 ) - walkWheelPosition = walkWheelPosition % 360; - - // advance the walk wheel the appropriate amount - if( currentTransition===null || currentTransition.walkingAtEnd ) - walkWheelPosition += degreesTurnedSinceLastFrame; - - // set the new values for the exact correct walk cycle speed at this velocity - adjustedFrequency = 1; - cycle = walkWheelPosition; - - // show stats and walk wheel? - if(statsOn) { - - var distanceTravelled = velocity * deltaTime; - var deltaTimeMS = deltaTime * 1000; - - if( INTERNAL_STATE===SIDE_STEPPING || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) { - - // draw the walk wheel turning around the z axis for sidestepping - var directionSign = 1; - if(principleDirection===DIRECTION_RIGHT) directionSign = -1; - var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down - var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( directionSign * walkWheelPosition )); - var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( directionSign * -walkWheelPosition )); - var wheelXPos = {x: cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0}; - var wheelXEnd = {x: -cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0}; - sinWalkWheelPosition = wheelRadius * Math.sin(degToRad( -directionSign * walkWheelPosition+90 )); - cosWalkWheelPosition = wheelRadius * Math.cos(degToRad( -directionSign * walkWheelPosition+90 )); - var wheelYPos = {x:cosWalkWheelPosition, y:sinWalkWheelPosition - yOffset, z: 0}; - var wheelYEnd = {x:-cosWalkWheelPosition, y:-sinWalkWheelPosition - yOffset, z: 0}; - Overlays.editOverlay(walkWheelYLine, {visible: true, position:wheelYPos, end:wheelYEnd}); - Overlays.editOverlay(walkWheelZLine, {visible: true, position:wheelXPos, end:wheelXEnd}); - - } else { - - // draw the walk wheel turning around the x axis for walking forwards or backwards - var yOffset = hipsToFeetDistance - (wheelRadius/1.2); // /1.2 is a visual kludge, probably necessary because of either the 'avi feet penetrate floor' issue - TODO - once ground plane following is in Interface, lock this down - var sinWalkWheelPosition = wheelRadius * Math.sin(degToRad((forwardModifier*-1) * walkWheelPosition)); - var cosWalkWheelPosition = wheelRadius * Math.cos(degToRad((forwardModifier*-1) * -walkWheelPosition)); - var wheelZPos = {x:0, y:-sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition}; - var wheelZEnd = {x:0, y:sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition}; - sinWalkWheelPosition = wheelRadius * Math.sin(degToRad(forwardModifier * walkWheelPosition+90)); - cosWalkWheelPosition = wheelRadius * Math.cos(degToRad(forwardModifier * walkWheelPosition+90)); - var wheelYPos = {x:0, y:sinWalkWheelPosition - yOffset, z: cosWalkWheelPosition}; - var wheelYEnd = {x:0, y:-sinWalkWheelPosition - yOffset, z: -cosWalkWheelPosition}; - Overlays.editOverlay(walkWheelYLine, { visible: true, position:wheelYPos, end:wheelYEnd }); - Overlays.editOverlay(walkWheelZLine, { visible: true, position:wheelZPos, end:wheelZEnd }); - } - - // populate stats overlay - var walkWheelInfo = - ' Walk Wheel Stats\n--------------------------------------\n \n \n' - + '\nFrame time: '+deltaTimeMS.toFixed(2) - + ' mS\nVelocity: '+velocity.toFixed(2) - + ' m/s\nDistance: '+distanceTravelled.toFixed(3) - + ' m\nOmega: '+angularVelocity.toFixed(3) - + ' rad / s\nDeg to turn: '+degreesTurnedSinceLastFrame.toFixed(2) - + ' deg\nWheel position: '+cycle.toFixed(1) - + ' deg\nWheel radius: '+wheelRadius.toFixed(3) - + ' m\nHips To Feet: '+hipsToFeetDistance.toFixed(3) - + ' m\nStride: '+strideLength.toFixed(3) - + ' m\n'; - Overlays.editOverlay(walkWheelStats, {text: walkWheelInfo}); - } - } // end of walk wheel and stride length calculation - - - // Start applying motion - var pitchOscillation = 0; - var yawOscillation = 0; - var rollOscillation = 0; - var pitchOscillationLast = 0; - var yawOscillationLast = 0; - var rollOscillationLast = 0; - var pitchOffset = 0; - var yawOffset = 0; - var rollOffset = 0; - var pitchOffsetLast = 0; - var yawOffsetLast = 0; - var rollOffsetLast = 0; - - // calcualte any hips translation - // Note: can only apply hips translations whilst in a config (edit) mode at present, not whilst under locomotion - if( INTERNAL_STATE===CONFIG_WALK_STYLES || - INTERNAL_STATE===CONFIG_WALK_TWEAKS || - INTERNAL_STATE===CONFIG_WALK_JOINTS || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT || - INTERNAL_STATE===CONFIG_FLYING || - INTERNAL_STATE===CONFIG_FLYING_UP || - INTERNAL_STATE===CONFIG_FLYING_DOWN ) { - - // calculate hips translation - var motorOscillation = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].thrustPhase)) + currentAnimation.joints[0].thrustOffset; - var swayOscillation = Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[0].swayPhase)) + currentAnimation.joints[0].swayOffset; - var bobOscillation = Math.sin(degToRad((cycle * adjustedFrequency * 2) + currentAnimation.joints[0].bobPhase)) + currentAnimation.joints[0].bobOffset; - - // apply hips translation - translateHips({x:swayOscillation*currentAnimation.joints[0].sway, y:motorOscillation*currentAnimation.joints[0].thrust, z:bobOscillation*currentAnimation.joints[0].bob}); - } - - // hips rotation - // apply the current Transition? - if(currentTransition!==null) { - - if( currentTransition.walkingAtStart ) { - - pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) - + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset; - - yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset; - - rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset); - - pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( walkWheelPosition * 2) - + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( walkWheelPosition ) - + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset; - - rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( walkWheelPosition ) - + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset); - - } else { - - pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2) - + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset; - - yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) - + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset; - - rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad((cycle * adjustedFrequency ) - + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset); - - pitchOscillationLast = currentTransition.lastAnimation.joints[0].pitch * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2) - + currentTransition.lastAnimation.joints[0].pitchPhase)) + currentTransition.lastAnimation.joints[0].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[0].yaw * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[0].yawPhase)) + currentTransition.lastAnimation.joints[0].yawOffset; - - rollOscillationLast = (currentTransition.lastAnimation.joints[0].roll * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[0].rollPhase)) + currentTransition.lastAnimation.joints[0].rollOffset); - - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - } else { - - pitchOscillation = currentAnimation.joints[0].pitch * Math.sin(degToRad((cycle * adjustedFrequency * 2) - + currentAnimation.joints[0].pitchPhase)) + currentAnimation.joints[0].pitchOffset; - - yawOscillation = currentAnimation.joints[0].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) - + currentAnimation.joints[0].yawPhase)) + currentAnimation.joints[0].yawOffset; - - rollOscillation = (currentAnimation.joints[0].roll * Math.sin(degToRad((cycle * adjustedFrequency ) - + currentAnimation.joints[0].rollPhase)) + currentAnimation.joints[0].rollOffset); - } - - // apply hips rotation - MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees(-pitchOscillation + (leanPitchModifier * getLeanPitch(velocity)), // getLeanPitch - lean forwards as velocity increases - yawOscillation, // Yup, that's correct ;-) - rollOscillation + getLeanRoll(deltaTime, velocity))); // getLeanRoll - banking on cornering - - // upper legs - if( INTERNAL_STATE!==SIDE_STEPPING && - INTERNAL_STATE!==CONFIG_SIDESTEP_LEFT && - INTERNAL_STATE!==CONFIG_SIDESTEP_RIGHT ) { - - // apply the current Transition to the upper legs? - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase))); - yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].yawPhase)); - rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[1].rollPhase)); - - pitchOffset = currentAnimation.joints[1].pitchOffset; - yawOffset = currentAnimation.joints[1].yawOffset; - rollOffset = currentAnimation.joints[1].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch - * Math.sin(degToRad( walkWheelPosition + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase ))); - - yawOscillationLast = currentTransition.lastAnimation.joints[1].yaw - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].yawPhase )); - - rollOscillationLast = currentTransition.lastAnimation.joints[1].roll - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[1].rollPhase )); - - pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase))); - yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase)); - rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase)); - - pitchOffset = currentAnimation.joints[1].pitchOffset; - yawOffset = currentAnimation.joints[1].yawOffset; - rollOffset = currentAnimation.joints[1].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[1].pitch - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + (forwardModifier * currentTransition.lastAnimation.joints[1].pitchPhase ))); - - yawOscillationLast = currentTransition.lastAnimation.joints[1].yaw - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[1].yawPhase )); - - rollOscillationLast = currentTransition.lastAnimation.joints[1].roll - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[1].rollPhase )); - - pitchOffsetLast = currentTransition.lastAnimation.joints[1].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[1].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[1].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast); - - pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast); - yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast); - rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + (forwardModifier * currentAnimation.joints[1].pitchPhase))); - yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase)); - rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase)); - - pitchOffset = currentAnimation.joints[1].pitchOffset; - yawOffset = currentAnimation.joints[1].yawOffset; - rollOffset = currentAnimation.joints[1].rollOffset; - } - - // apply the upper leg rotations - MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation - rollOffset )); - MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, -rollOscillation + rollOffset )); - - - // lower leg - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].pitchPhase)); - yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].yawPhase)); - rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[2].rollPhase)); - - pitchOffset = currentAnimation.joints[2].pitchOffset; - yawOffset = currentAnimation.joints[2].yawOffset; - rollOffset = currentAnimation.joints[2].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].pitchPhase)); - yawOscillationLast = currentTransition.lastAnimation.joints[2].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].yawPhase)); - rollOscillationLast = currentTransition.lastAnimation.joints[2].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[2].rollPhase)); - - pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase)); - yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase)); - rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase)); - - pitchOffset = currentAnimation.joints[2].pitchOffset; - yawOffset = currentAnimation.joints[2].yawOffset; - rollOffset = currentAnimation.joints[2].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[2].pitch - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[2].pitchPhase)); - yawOscillationLast = currentTransition.lastAnimation.joints[2].yaw - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[2].yawPhase)); - rollOscillationLast = currentTransition.lastAnimation.joints[2].roll - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[2].rollPhase)); - - pitchOffsetLast = currentTransition.lastAnimation.joints[2].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[2].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[2].rollOffset; - } - - pitchOscillation = ( transitionProgress * pitchOscillation ) + ( (1-transitionProgress) * pitchOscillationLast ); - yawOscillation = ( transitionProgress * yawOscillation ) + ( (1-transitionProgress) * yawOscillationLast ); - rollOscillation = ( transitionProgress * rollOscillation ) + ( (1-transitionProgress) * rollOscillationLast ); - - pitchOffset = ( transitionProgress * pitchOffset ) + ( (1-transitionProgress) * pitchOffsetLast ); - yawOffset = ( transitionProgress * yawOffset ) + ( (1-transitionProgress) * yawOffsetLast ); - rollOffset = ( transitionProgress * rollOffset ) + ( (1-transitionProgress) * rollOffsetLast ); - - } else { - - pitchOscillation = currentAnimation.joints[2].pitch * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase )); - yawOscillation = currentAnimation.joints[2].yaw * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase )); - rollOscillation = currentAnimation.joints[2].roll * Math.sin( degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase )); - - pitchOffset = currentAnimation.joints[2].pitchOffset; - yawOffset = currentAnimation.joints[2].yawOffset; - rollOffset = currentAnimation.joints[2].rollOffset; - } - - // apply lower leg joint rotations - MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset )); - MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset )); - - } // end if !SIDE_STEPPING - - else if( INTERNAL_STATE===SIDE_STEPPING || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) { - - // sidestepping uses the sinewave generators slightly differently for the legs - pitchOscillation = currentAnimation.joints[1].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].pitchPhase)); - yawOscillation = currentAnimation.joints[1].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].yawPhase)); - rollOscillation = currentAnimation.joints[1].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[1].rollPhase)); - - // apply upper leg rotations for sidestepping - MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees( - -pitchOscillation + currentAnimation.joints[1].pitchOffset, - yawOscillation + currentAnimation.joints[1].yawOffset, - rollOscillation + currentAnimation.joints[1].rollOffset )); - MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees( - pitchOscillation + currentAnimation.joints[1].pitchOffset, - yawOscillation - currentAnimation.joints[1].yawOffset, - -rollOscillation - currentAnimation.joints[1].rollOffset )); - - // calculate lower leg joint rotations for sidestepping - pitchOscillation = currentAnimation.joints[2].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].pitchPhase)); - yawOscillation = currentAnimation.joints[2].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].yawPhase)); - rollOscillation = currentAnimation.joints[2].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[2].rollPhase)); - - // apply lower leg joint rotations - MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( - -pitchOscillation + currentAnimation.joints[2].pitchOffset, - yawOscillation - currentAnimation.joints[2].yawOffset, - rollOscillation - currentAnimation.joints[2].rollOffset)); // TODO: needs a kick just before fwd peak - MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( - pitchOscillation + currentAnimation.joints[2].pitchOffset, - yawOscillation + currentAnimation.joints[2].yawOffset, - rollOscillation + currentAnimation.joints[2].rollOffset)); - } - - // feet - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].pitchPhase)); - yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].yawPhase)); - rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[3].rollPhase)); - - pitchOffset = currentAnimation.joints[3].pitchOffset; - yawOffset = currentAnimation.joints[3].yawOffset; - rollOffset = currentAnimation.joints[3].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].pitchPhase)); - yawOscillationLast = currentTransition.lastAnimation.joints[3].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].yawPhase)); - rollOscillationLast = currentTransition.lastAnimation.joints[3].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[3].rollPhase)); - - pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase)); - yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase)); - rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase)); - - pitchOffset = currentAnimation.joints[3].pitchOffset; - yawOffset = currentAnimation.joints[3].yawOffset; - rollOffset = currentAnimation.joints[3].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[3].pitch - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[3].pitchPhase)); - - yawOscillationLast = currentTransition.lastAnimation.joints[3].yaw - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[3].yawPhase)); - - rollOscillationLast = currentTransition.lastAnimation.joints[3].roll - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[3].rollPhase)); - - pitchOffsetLast = currentTransition.lastAnimation.joints[3].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[3].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[3].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast); - - pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast); - yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast); - rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[3].pitch * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase)); - yawOscillation = currentAnimation.joints[3].yaw * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].yawPhase)); - rollOscillation = currentAnimation.joints[3].roll * Math.sin(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].rollPhase)); - - pitchOffset = currentAnimation.joints[3].pitchOffset; - yawOffset = currentAnimation.joints[3].yawOffset; - rollOffset = currentAnimation.joints[3].rollOffset; - } - - // apply foot rotations - MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, rollOscillation + rollOffset )); - MyAvatar.setJointData("LeftFoot", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation - rollOffset )); - - // play footfall sound yet? To determine this, we take the differential of the foot's pitch curve to decide when the foot hits the ground. - if( INTERNAL_STATE===WALKING || - INTERNAL_STATE===SIDE_STEPPING || - INTERNAL_STATE===CONFIG_WALK_STYLES || - INTERNAL_STATE===CONFIG_WALK_TWEAKS || - INTERNAL_STATE===CONFIG_WALK_JOINTS || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) { - - // finding dy/dx is as simple as determining the cosine wave for the foot's pitch function. - var feetPitchDifferential = Math.cos(degToRad((cycle * adjustedFrequency ) + currentAnimation.joints[3].pitchPhase)); - var threshHold = 0.9; // sets the audio trigger point. with accuracy. - if(feetPitchDifferential<-threshHold && - nextStep===DIRECTION_LEFT && - principleDirection!==DIRECTION_UP && - principleDirection!==DIRECTION_DOWN) { - - playFootstep(DIRECTION_LEFT); - nextStep = DIRECTION_RIGHT; - } - else if(feetPitchDifferential>threshHold && - nextStep===DIRECTION_RIGHT && - principleDirection!==DIRECTION_UP && - principleDirection!==DIRECTION_DOWN) { - - playFootstep(DIRECTION_RIGHT); - nextStep = DIRECTION_LEFT; - } - } - - // toes - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].pitchPhase)); - yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset; - rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset; - - pitchOffset = currentAnimation.joints[4].pitchOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].pitchPhase)); - yawOscillationLast = currentTransition.lastAnimation.joints[4].yaw * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;; - rollOscillationLast = currentTransition.lastAnimation.joints[4].roll * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset; - - pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset; - - } else { - - pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase)); - yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset; - rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset; - - pitchOffset = currentAnimation.joints[4].pitchOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[4].pitch - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[4].pitchPhase)); - - yawOscillationLast = currentTransition.lastAnimation.joints[4].yaw - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[4].yawPhase)) + currentTransition.lastAnimation.joints[4].yawOffset;; - - rollOscillationLast = currentTransition.lastAnimation.joints[4].roll - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[4].rollPhase))+ currentTransition.lastAnimation.joints[4].rollOffset; - - pitchOffsetLast = currentTransition.lastAnimation.joints[4].pitchOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[4].pitch * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].pitchPhase)); - yawOscillation = currentAnimation.joints[4].yaw * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].yawPhase)) + currentAnimation.joints[4].yawOffset; - rollOscillation = currentAnimation.joints[4].roll * Math.sin(degToRad((cycle * adjustedFrequency) + currentAnimation.joints[4].rollPhase)) + currentAnimation.joints[4].rollOffset; - - pitchOffset = currentAnimation.joints[4].pitchOffset; - } - - // apply toe rotations - MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees(-pitchOscillation + pitchOffset, yawOscillation, rollOscillation)); - MyAvatar.setJointData("LeftToeBase", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation, rollOscillation)); - - // spine - if( currentTransition !== null ) { - - if( currentTransition.walkingAtStart ) { - - pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2 ) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset; - yawOscillation = currentAnimation.joints[5].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset; - rollOscillation = currentAnimation.joints[5].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset; - - // calculate where we would have been if we'd continued in the last state - pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch - * Math.sin(degToRad(( walkWheelPosition * 2 ) + currentTransition.lastAnimation.joints[5].pitchPhase)) - + currentTransition.lastAnimation.joints[5].pitchOffset; - yawOscillationLast = currentTransition.lastAnimation.joints[5].yaw - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].yawPhase)) - + currentTransition.lastAnimation.joints[5].yawOffset; - rollOscillationLast = currentTransition.lastAnimation.joints[5].roll - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[5].rollPhase)) - + currentTransition.lastAnimation.joints[5].rollOffset; - } else { - - pitchOscillation = currentAnimation.joints[5].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset; - - yawOscillation = currentAnimation.joints[5].yaw * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset; - - rollOscillation = currentAnimation.joints[5].roll * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[5].pitch - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2 ) - + currentTransition.lastAnimation.joints[5].pitchPhase)) - + currentTransition.lastAnimation.joints[5].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[5].yaw - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[5].yawPhase)) - + currentTransition.lastAnimation.joints[5].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[5].roll - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[5].rollPhase)) - + currentTransition.lastAnimation.joints[5].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast); - - } else { - - pitchOscillation = currentAnimation.joints[5].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[5].pitchPhase)) + currentAnimation.joints[5].pitchOffset; - yawOscillation = currentAnimation.joints[5].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[5].yawPhase)) + currentAnimation.joints[5].yawOffset; - rollOscillation = currentAnimation.joints[5].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[5].rollPhase)) + currentAnimation.joints[5].rollOffset; - } - - // apply spine joint rotations - MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation )); - - // spine 1 - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset; - yawOscillation = currentAnimation.joints[6].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset; - rollOscillation = currentAnimation.joints[6].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch - * Math.sin(degToRad(( walkWheelPosition * 2 ) + currentTransition.lastAnimation.joints[6].pitchPhase)) - + currentTransition.lastAnimation.joints[6].pitchOffset; - yawOscillationLast = currentTransition.lastAnimation.joints[6].yaw - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].yawPhase)) - + currentTransition.lastAnimation.joints[6].yawOffset; - rollOscillationLast = currentTransition.lastAnimation.joints[6].roll - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[6].rollPhase)) - + currentTransition.lastAnimation.joints[6].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[6].pitch * Math.sin( degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset; - - yawOscillation = currentAnimation.joints[6].yaw * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset; - - rollOscillation = currentAnimation.joints[6].roll * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[6].pitch - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2 ) - + currentTransition.lastAnimation.joints[6].pitchPhase)) - + currentTransition.lastAnimation.joints[6].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[6].yaw - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[6].yawPhase)) - + currentTransition.lastAnimation.joints[6].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[6].roll - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[6].rollPhase)) - + currentTransition.lastAnimation.joints[6].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - } else { - - pitchOscillation = currentAnimation.joints[6].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[6].pitchPhase)) + currentAnimation.joints[6].pitchOffset; - yawOscillation = currentAnimation.joints[6].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[6].yawPhase)) + currentAnimation.joints[6].yawOffset; - rollOscillation = currentAnimation.joints[6].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[6].rollPhase)) + currentAnimation.joints[6].rollOffset; - } - // apply spine1 joint rotations - MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation )); - - // spine 2 - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[7].pitchPhase)) + currentAnimation.joints[7].pitchOffset; - yawOscillation = currentAnimation.joints[7].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[7].yawPhase)) + currentAnimation.joints[7].yawOffset; - rollOscillation = currentAnimation.joints[7].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[7].rollPhase)) + currentAnimation.joints[7].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].pitchPhase)) - + currentTransition.lastAnimation.joints[7].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[7].yaw - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].yawPhase)) - + currentTransition.lastAnimation.joints[7].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[7].roll - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[7].rollPhase)) - + currentTransition.lastAnimation.joints[7].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[7].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[7].pitchPhase)) - + currentAnimation.joints[7].pitchOffset; - - yawOscillation = currentAnimation.joints[7].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[7].yawPhase)) - + currentAnimation.joints[7].yawOffset; - - rollOscillation = currentAnimation.joints[7].roll - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[7].rollPhase)) - + currentAnimation.joints[7].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[7].pitch - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[7].pitchPhase)) - + currentTransition.lastAnimation.joints[7].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[7].yaw - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[7].yawPhase)) - + currentTransition.lastAnimation.joints[7].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[7].roll - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[7].rollPhase)) - + currentTransition.lastAnimation.joints[7].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation ) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation ) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation ) + ((1-transitionProgress) * rollOscillationLast); - - } else { - - pitchOscillation = currentAnimation.joints[7].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[7].pitchPhase)) - + currentAnimation.joints[7].pitchOffset; - - yawOscillation = currentAnimation.joints[7].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[7].yawPhase)) - + currentAnimation.joints[7].yawOffset; - - rollOscillation = currentAnimation.joints[7].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[7].rollPhase)) - + currentAnimation.joints[7].rollOffset; - } - // apply spine2 joint rotations - MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation )); - - if(!armsFree) { - - // shoulders - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset; - - yawOscillation = currentAnimation.joints[8].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[8].yawPhase)); - - rollOscillation = currentAnimation.joints[8].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) - + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset; - - yawOffset = currentAnimation.joints[8].yawOffset; - - - pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].pitchPhase)) - + currentTransition.lastAnimation.joints[8].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[8].yaw - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].yawPhase)) - - rollOscillationLast = currentTransition.lastAnimation.joints[8].roll - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[8].rollPhase)) - + currentTransition.lastAnimation.joints[8].rollOffset; - - yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset; - - } else { - - pitchOscillation = currentAnimation.joints[8].pitch * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset; - - yawOscillation = currentAnimation.joints[8].yaw * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[8].yawPhase)); - - rollOscillation = currentAnimation.joints[8].roll * Math.sin( degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset; - - yawOffset = currentAnimation.joints[8].yawOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[8].pitch - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[8].pitchPhase)) - + currentTransition.lastAnimation.joints[8].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[8].yaw - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[8].yawPhase)) - - rollOscillationLast = currentTransition.lastAnimation.joints[8].roll - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[8].rollPhase)) - + currentTransition.lastAnimation.joints[8].rollOffset; - - yawOffsetLast = currentTransition.lastAnimation.joints[8].yawOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[8].pitch * Math.sin(degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[8].pitchPhase)) + currentAnimation.joints[8].pitchOffset; - - yawOscillation = currentAnimation.joints[8].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[8].yawPhase)); - - rollOscillation = currentAnimation.joints[8].roll * Math.sin(degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[8].rollPhase)) + currentAnimation.joints[8].rollOffset; - - yawOffset = currentAnimation.joints[8].yawOffset; - } - - MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation + yawOffset, rollOscillation )); - MyAvatar.setJointData("LeftShoulder", Quat.fromPitchYawRollDegrees(pitchOscillation, yawOscillation - yawOffset, -rollOscillation )); - - // upper arms - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[9].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].pitchPhase)); - yawOscillation = currentAnimation.joints[9].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[9].yawPhase)); - rollOscillation = currentAnimation.joints[9].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) + currentAnimation.joints[9].rollPhase)) + currentAnimation.joints[9].rollOffset; - - pitchOffset = currentAnimation.joints[9].pitchOffset; - yawOffset = currentAnimation.joints[9].yawOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].pitchPhase)) - - yawOscillationLast = currentTransition.lastAnimation.joints[9].yaw - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].yawPhase)) - - rollOscillationLast = currentTransition.lastAnimation.joints[9].roll - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[9].rollPhase)) - + currentTransition.lastAnimation.joints[9].rollOffset; - - pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset; - - } else { - - pitchOscillation = currentAnimation.joints[9].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[9].pitchPhase)); - - yawOscillation = currentAnimation.joints[9].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[9].yawPhase)); - - rollOscillation = currentAnimation.joints[9].roll - * Math.sin(degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[9].rollPhase)) - + currentAnimation.joints[9].rollOffset; - - pitchOffset = currentAnimation.joints[9].pitchOffset; - yawOffset = currentAnimation.joints[9].yawOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[9].pitch - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[9].pitchPhase)) - - yawOscillationLast = currentTransition.lastAnimation.joints[9].yaw - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[9].yawPhase)) - - rollOscillationLast = currentTransition.lastAnimation.joints[9].roll - * Math.sin( degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[9].rollPhase)) - + currentTransition.lastAnimation.joints[9].rollOffset; - - pitchOffsetLast = currentTransition.lastAnimation.joints[9].pitchOffset; - yawOffsetLast = currentTransition.lastAnimation.joints[9].yawOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - pitchOffset = (transitionProgress * pitchOffset) + ((1-transitionProgress) * pitchOffsetLast); - yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[9].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[9].pitchPhase)); - - yawOscillation = currentAnimation.joints[9].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[9].yawPhase)); - - rollOscillation = currentAnimation.joints[9].roll - * Math.sin( degToRad(( cycle * adjustedFrequency * 2) - + currentAnimation.joints[9].rollPhase)) - + currentAnimation.joints[9].rollOffset; - - pitchOffset = currentAnimation.joints[9].pitchOffset; - yawOffset = currentAnimation.joints[9].yawOffset; - - } - MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( -pitchOscillation + pitchOffset, yawOscillation - yawOffset, rollOscillation )); - MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees( pitchOscillation + pitchOffset, yawOscillation + yawOffset, -rollOscillation )); - - // forearms - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[10].pitch - * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[10].pitchPhase )) - + currentAnimation.joints[10].pitchOffset; - - yawOscillation = currentAnimation.joints[10].yaw - * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[10].yawPhase )); - - rollOscillation = currentAnimation.joints[10].roll - * Math.sin( degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[10].rollPhase )); - - yawOffset = currentAnimation.joints[10].yawOffset; - rollOffset = currentAnimation.joints[10].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch - * Math.sin( degToRad(( walkWheelPosition ) - + currentTransition.lastAnimation.joints[10].pitchPhase)) - + currentTransition.lastAnimation.joints[10].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[10].yaw - * Math.sin( degToRad(( walkWheelPosition ) - + currentTransition.lastAnimation.joints[10].yawPhase)); - - rollOscillationLast = currentTransition.lastAnimation.joints[10].roll - * Math.sin( degToRad(( walkWheelPosition ) - + currentTransition.lastAnimation.joints[10].rollPhase)); - - yawOffsetLast = currentTransition.lastAnimation.joints[10].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[10].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].pitchPhase )) - + currentAnimation.joints[10].pitchOffset; - - yawOscillation = currentAnimation.joints[10].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].yawPhase )); - - rollOscillation = currentAnimation.joints[10].roll - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].rollPhase )); - - yawOffset = currentAnimation.joints[10].yawOffset; - rollOffset = currentAnimation.joints[10].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[10].pitch - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[10].pitchPhase)) - + currentTransition.lastAnimation.joints[10].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[10].yaw - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[10].yawPhase)); - - rollOscillationLast = currentTransition.lastAnimation.joints[10].roll - * Math.sin( degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[10].rollPhase)); - - yawOffsetLast = currentTransition.lastAnimation.joints[10].yawOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[10].rollOffset; - } - - // blend the previous and next - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = -(transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - yawOffset = (transitionProgress * yawOffset) + ((1-transitionProgress) * yawOffsetLast); - rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[10].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].pitchPhase )) - + currentAnimation.joints[10].pitchOffset; - - yawOscillation = currentAnimation.joints[10].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].yawPhase )); - - rollOscillation = currentAnimation.joints[10].roll - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[10].rollPhase )); - - yawOffset = currentAnimation.joints[10].yawOffset; - rollOffset = currentAnimation.joints[10].rollOffset; - } - - // apply forearms rotations - MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation + yawOffset, rollOscillation + rollOffset )); - MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation - yawOffset, rollOscillation - rollOffset )); - - // hands - var sideStepSign = 1; - if(INTERNAL_STATE===SIDE_STEPPING) { - sideStepSign = 1; - } - if(currentTransition!==null) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset; - yawOscillation = currentAnimation.joints[11].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].yawPhase)) + currentAnimation.joints[11].yawOffset; - rollOscillation = currentAnimation.joints[11].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) + currentAnimation.joints[11].rollPhase)) ; - - pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].pitchPhase)) - + currentTransition.lastAnimation.joints[11].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[11].yaw - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].yawPhase)) - + currentTransition.lastAnimation.joints[11].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[11].roll - * Math.sin(degToRad( walkWheelPosition + currentTransition.lastAnimation.joints[11].rollPhase)) - - rollOffset = currentAnimation.joints[11].rollOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset; - - } else { - - pitchOscillation = currentAnimation.joints[11].pitch - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[11].pitchPhase)) - + currentAnimation.joints[11].pitchOffset; - - yawOscillation = currentAnimation.joints[11].yaw - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[11].yawPhase)) - + currentAnimation.joints[11].yawOffset; - - rollOscillation = currentAnimation.joints[11].roll - * Math.sin( degToRad(( cycle * adjustedFrequency ) - + currentAnimation.joints[11].rollPhase)); - - rollOffset = currentAnimation.joints[11].rollOffset; - - pitchOscillationLast = currentTransition.lastAnimation.joints[11].pitch - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[11].pitchPhase)) - + currentTransition.lastAnimation.joints[11].pitchOffset; - - yawOscillationLast = currentTransition.lastAnimation.joints[11].yaw - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[11].yawPhase)) - + currentTransition.lastAnimation.joints[11].yawOffset; - - rollOscillationLast = currentTransition.lastAnimation.joints[11].roll - * Math.sin(degToRad( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency - + currentTransition.lastAnimation.joints[11].rollPhase)) - - rollOffset = currentAnimation.joints[11].rollOffset; - rollOffsetLast = currentTransition.lastAnimation.joints[11].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - rollOffset = (transitionProgress * rollOffset) + ((1-transitionProgress) * rollOffsetLast); - - } else { - - pitchOscillation = currentAnimation.joints[11].pitch * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].pitchPhase)) + currentAnimation.joints[11].pitchOffset; - yawOscillation = currentAnimation.joints[11].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].yawPhase)) + currentAnimation.joints[11].yawOffset; - rollOscillation = currentAnimation.joints[11].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[11].rollPhase)); - - rollOffset = currentAnimation.joints[11].rollOffset; - } - - // set the hand rotations - MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( sideStepSign * pitchOscillation, yawOscillation, rollOscillation + rollOffset)); - MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollDegrees( pitchOscillation, -yawOscillation, rollOscillation - rollOffset)); - - } // end if(!armsFree) - - // head (includes neck joint) - currently zeroed out in STANDING animation files by request - if( currentTransition !== null ) { - - if(currentTransition.walkingAtStart) { - - pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency * 2) - + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset; - - yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset; - - rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cumulativeTime * currentAnimation.settings.baseFrequency ) - + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset; - - pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch - * Math.sin(degToRad(( walkWheelPosition * 2) + currentTransition.lastAnimation.joints[12].pitchPhase)) - + currentTransition.lastAnimation.joints[12].pitchOffset; - - yawOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].yaw - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].yawPhase)) - + currentTransition.lastAnimation.joints[12].yawOffset; - - rollOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].roll - * Math.sin(degToRad(( walkWheelPosition ) + currentTransition.lastAnimation.joints[12].rollPhase)) - + currentTransition.lastAnimation.joints[12].rollOffset; - - } else { - - pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset; - yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset; - rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset; - - pitchOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].pitch - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency * 2) - + currentTransition.lastAnimation.joints[12].pitchPhase)) - + currentTransition.lastAnimation.joints[12].pitchOffset; - - yawOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].yaw - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[12].yawPhase)) - + currentTransition.lastAnimation.joints[12].yawOffset; - - rollOscillationLast = 0.5 * currentTransition.lastAnimation.joints[12].roll - * Math.sin(degToRad(( cumulativeTime * currentTransition.lastAnimation.settings.baseFrequency ) - + currentTransition.lastAnimation.joints[12].rollPhase)) - + currentTransition.lastAnimation.joints[12].rollOffset; - } - - pitchOscillation = (transitionProgress * pitchOscillation) + ((1-transitionProgress) * pitchOscillationLast); - yawOscillation = (transitionProgress * yawOscillation) + ((1-transitionProgress) * yawOscillationLast); - rollOscillation = (transitionProgress * rollOscillation) + ((1-transitionProgress) * rollOscillationLast); - - } else { - - pitchOscillation = 0.5 * currentAnimation.joints[12].pitch * Math.sin(degToRad(( cycle * adjustedFrequency * 2) + currentAnimation.joints[12].pitchPhase)) + currentAnimation.joints[12].pitchOffset; - yawOscillation = 0.5 * currentAnimation.joints[12].yaw * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].yawPhase)) + currentAnimation.joints[12].yawOffset; - rollOscillation = 0.5 * currentAnimation.joints[12].roll * Math.sin(degToRad(( cycle * adjustedFrequency ) + currentAnimation.joints[12].rollPhase)) + currentAnimation.joints[12].rollOffset; - } - - MyAvatar.setJointData( "Head", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation )); - MyAvatar.setJointData( "Neck", Quat.fromPitchYawRollDegrees( pitchOscillation, yawOscillation, rollOscillation )); -} - - - -// Bezier function for applying to transitions - src: Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/ -Coord = function (x,y) { - if(!x) var x=0; - if(!y) var y=0; - return {x: x, y: y}; -} - -function B1(t) { return t*t*t } -function B2(t) { return 3*t*t*(1-t) } -function B3(t) { return 3*t*(1-t)*(1-t) } -function B4(t) { return (1-t)*(1-t)*(1-t) } - -function getBezier(percent,C1,C2,C3,C4) { - var pos = new Coord(); - pos.x = C1.x*B1(percent) + C2.x*B2(percent) + C3.x*B3(percent) + C4.x*B4(percent); - pos.y = C1.y*B1(percent) + C2.y*B2(percent) + C3.y*B3(percent) + C4.y*B4(percent); - return pos; -} - -// Butterworth LP filter - coeffs calculated using: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html -var NZEROS = 8; -var NPOLES = 8; -var GAIN = 17.40692157; -var xv = [0,0,0,0,0,0,0,0,0]; //xv.length = NZEROS+1; -var yv = [0,0,0,0,0,0,0,0,0]; //yv.length = NPOLES+1; - -function filterButterworth(nextInputValue) -{ - xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8]; - xv[8] = nextInputValue / GAIN; - yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8]; - yv[8] = (xv[0] + xv[8]) + 8 * (xv[1] + xv[7]) + 28 * (xv[2] + xv[6]) - + 56 * (xv[3] + xv[5]) + 70 * xv[4] - + ( -0.0033008230 * yv[0]) + ( -0.0440409341 * yv[1]) - + ( -0.2663485333 * yv[2]) + ( -0.9570250765 * yv[3]) - + ( -2.2596729000 * yv[4]) + ( -3.6088345059 * yv[5]) - + ( -3.9148571397 * yv[6]) + ( -2.6527135283 * yv[7]); - return yv[8]; -} - -// the faster we go, the further we lean forward. the angle is calcualted here -var leanAngles = []; // smooth out and add damping with simple averaging filter. -leanAngles.length = 15; - -function getLeanPitch(velocity) { - - if(velocity>TERMINAL_VELOCITY) velocity=TERMINAL_VELOCITY; - var leanProgress = velocity / TERMINAL_VELOCITY; - var responseSharpness = 1.8; - if(principleDirection==DIRECTION_BACKWARDS) responseSharpness = 3.6; // lean back a bit extra when walking backwards - var leanProgressBezier = getBezier((1-leanProgress),{x:0,y:0},{x:0,y:responseSharpness},{x:0,y:1},{x:1,y:1}).y; - - // simple averaging filter seems to give best results - leanAngles.push(leanProgressBezier); - leanAngles.shift(); // FIFO - var totalLeanAngles = 0; - for(ea in leanAngles) totalLeanAngles += leanAngles[ea]; - var leanProgressAverageFiltered = totalLeanAngles / leanAngles.length; - - // calculate final return value - var leanPitchFinal = 0; - if(principleDirection===DIRECTION_BACKWARDS) { - leanPitchFinal = -currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse; - } else { - leanPitchFinal = currentAnimation.settings.flyingHipsPitch * leanProgressAverageFiltered;// + finalAccelerationResponse; - } - return leanPitchFinal; -} - -// calculate the angle at which to bank into corners when turning -var leanRollAngles = []; // smooth out and add damping with simple averaging filter -leanRollAngles.length = 25; - -var angularVelocities = []; // keep a record of the last few so can filter out spurious values -angularVelocities.length = 5; - -function getLeanRoll(deltaTime, velocity) { - - // what's our current anglular velocity? - var angularVelocityMax = 70; // from observation - var currentOrientationVec3 = Quat.safeEulerAngles(MyAvatar.orientation); - var lastOrientationVec3 = Quat.safeEulerAngles(lastOrientation); - var deltaYaw = lastOrientationVec3.y-currentOrientationVec3.y; - lastOrientation = MyAvatar.orientation; - - var angularVelocity = deltaYaw / deltaTime; - if(angularVelocity>angularVelocityMax) angularVelocity = angularVelocityMax; - if(angularVelocity<-angularVelocityMax) angularVelocity = -angularVelocityMax; - - // filter the angular velocity for a nicer response and a bit of wobble (intentional overshoot / ringing) - angularVelocity = filterButterworth(angularVelocity); - - var turnSign = 1; - if(angularVelocity<0) turnSign = -1; - if(principleDirection===DIRECTION_BACKWARDS) - turnSign *= -1; - - // calculate the amount of roll based on both angular and linear velocities - if(velocity>TERMINAL_VELOCITY) velocity = TERMINAL_VELOCITY; - var leanRollProgress = (velocity / TERMINAL_VELOCITY) * (Math.abs(angularVelocity) / angularVelocityMax); - - // apply our response curve - var leanRollProgressBezier = getBezier((1-leanRollProgress),{x:0,y:0},{x:0,y:2.5},{x:0,y:1},{x:1,y:1}).y; - - // simple averaging filter - leanRollAngles.push(turnSign * leanRollProgressBezier); - leanRollAngles.shift(); // FIFO - var totalLeanRollAngles = 0; - for(var ea in leanRollAngles) totalLeanRollAngles += leanRollAngles[ea]; - var leanRollProgressAverageFiltered = totalLeanRollAngles / leanRollAngles.length; - - return currentAnimation.settings.maxBankingAngle * leanRollProgressAverageFiltered; -} - -// set up the interface components, update the internal state and kick off any transitions -function setInternalState(newInternalState) { - - switch(newInternalState) { - - case WALKING: - - if(!minimised) doStandardMenu(); - INTERNAL_STATE = WALKING; - return; - - case FLYING: - - if(!minimised) doStandardMenu(); - INTERNAL_STATE = FLYING; - return; - - case SIDE_STEPPING: - - if(!minimised) doStandardMenu(); - INTERNAL_STATE = SIDE_STEPPING; - return; - - case CONFIG_WALK_STYLES: - - INTERNAL_STATE = CONFIG_WALK_STYLES; - currentAnimation = selectedWalk; - if(!minimised) { - hidebuttonOverlays(); - hideJointControls(); - showFrontPanelButtons(false); - showWalkStyleButtons(true); - setBackground(controlsBackgroundWalkEditStyles); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configWalkStylesButtonSelected); - setButtonOverlayVisible(configWalkTweaksButton); - setButtonOverlayVisible(configWalkJointsButton); - setButtonOverlayVisible(backButton); - setSliderThumbsVisible(false); - } - return; - - case CONFIG_WALK_TWEAKS: - - INTERNAL_STATE = CONFIG_WALK_TWEAKS; - currentAnimation = selectedWalk; - if(!minimised) { - hidebuttonOverlays(); - hideJointControls(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditTweaks); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configWalkStylesButton); - setButtonOverlayVisible(configWalkTweaksButtonSelected); - setButtonOverlayVisible(configWalkJointsButton); - setButtonOverlayVisible(backButton); - initialiseWalkTweaks(); - } - return; - - case CONFIG_WALK_JOINTS: - - INTERNAL_STATE = CONFIG_WALK_JOINTS; - currentAnimation = selectedWalk; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configWalkStylesButton); - setButtonOverlayVisible(configWalkTweaksButton); - setButtonOverlayVisible(configWalkJointsButtonSelected); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_STANDING: - - INTERNAL_STATE = CONFIG_STANDING; - currentAnimation = selectedStand; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configSideStepRightButton); - setButtonOverlayVisible(configSideStepLeftButton); - setButtonOverlayVisible(configStandButtonSelected); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_SIDESTEP_LEFT: - - INTERNAL_STATE = CONFIG_SIDESTEP_LEFT; - currentAnimation = selectedSideStepLeft; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configSideStepRightButton); - setButtonOverlayVisible(configSideStepLeftButtonSelected); - setButtonOverlayVisible(configStandButton); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_SIDESTEP_RIGHT: - - INTERNAL_STATE = CONFIG_SIDESTEP_RIGHT; - currentAnimation = selectedSideStepRight; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configSideStepRightButtonSelected); - setButtonOverlayVisible(configSideStepLeftButton); - setButtonOverlayVisible(configStandButton); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_FLYING: - - INTERNAL_STATE = CONFIG_FLYING; - currentAnimation = selectedFly; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configFlyingUpButton); - setButtonOverlayVisible(configFlyingDownButton); - setButtonOverlayVisible(configFlyingButtonSelected); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_FLYING_UP: - - INTERNAL_STATE = CONFIG_FLYING_UP; - currentAnimation = selectedFlyUp; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configFlyingUpButtonSelected); - setButtonOverlayVisible(configFlyingDownButton); - setButtonOverlayVisible(configFlyingButton); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case CONFIG_FLYING_DOWN: - - INTERNAL_STATE = CONFIG_FLYING_DOWN; - currentAnimation = selectedFlyDown; - if(!minimised) { - hidebuttonOverlays(); - showFrontPanelButtons(false); - showWalkStyleButtons(false); - setBackground(controlsBackgroundWalkEditJoints); - setButtonOverlayVisible(onButton); - setButtonOverlayVisible(configFlyingUpButton); - setButtonOverlayVisible(configFlyingDownButtonSelected); - setButtonOverlayVisible(configFlyingButton); - setButtonOverlayVisible(backButton); - initialiseJointsEditingPanel(selectedJointIndex); - } - return; - - case STANDING: - default: - - INTERNAL_STATE = STANDING; - if(!minimised) doStandardMenu(); - - // initialisation - runs at script startup only - if(strideLength===0) { - - if(principleDirection===DIRECTION_BACKWARDS) - strideLength = selectedWalk.calibration.strideLengthBackwards; - else - strideLength = selectedWalk.calibration.strideLengthForwards; - - curlFingers(); - } - return; - } -} // Main loop - -// stabilising vars - most state changes are preceded by a couple of hints that they are about to happen rather than -// momentarilly switching between states (causes flicker), we count the number of hints in a row before actually changing state -var standHints = 0; -var walkHints = 0; -var flyHints = 0; -var requiredHints = 2; // tweakable - debounce state changes - how many times do we get a state change request in a row before we actually change state? (used to be 4 or 5) -var principleDirection = 0; - -// helper function for stats output -function directionAsString(directionEnum) { - - switch(directionEnum) { - case DIRECTION_UP: return 'Up'; - case DIRECTION_DOWN: return 'Down'; - case DIRECTION_LEFT: return 'Left'; - case DIRECTION_RIGHT: return 'Right'; - case DIRECTION_FORWARDS: return 'Forwards'; - case DIRECTION_BACKWARDS: return 'Backwards'; - default: return 'Unknown'; - } -} -// helper function for stats output -function internalStateAsString(internalState) { - - switch(internalState) { - case STANDING: return 'Standing'; - case WALKING: return 'Walking'; - case SIDE_STEPPING: return 'Side Stepping'; - case FLYING: return 'Flying'; - default: return 'Editing'; - } -} - Script.update.connect(function(deltaTime) { - if(powerOn) { + if (state.powerOn) { - frameStartTime = new Date().getTime(); - cumulativeTime += deltaTime; - nFrames++; - var speed = 0; + motion.frameStartTime = new Date().getTime(); + motion.cumulativeTime += deltaTime; + motion.nFrames++; + var speed = 0; - // firstly check for editing modes, as these require no positioning calculations - var editing = false; - switch(INTERNAL_STATE) { + // check for editing modes first, as these require no positioning calculations + switch (state.currentState) { - case CONFIG_WALK_STYLES: - currentAnimation = selectedWalk; - animateAvatar(deltaTime, speed, principleDirection); - editing = true; - break; - case CONFIG_WALK_TWEAKS: - currentAnimation = selectedWalk; - animateAvatar(deltaTime, speed, DIRECTION_FORWARDS); - editing = true; - break; - case CONFIG_WALK_JOINTS: - currentAnimation = selectedWalk; - animateAvatar(deltaTime, speed, DIRECTION_FORWARDS); - editing = true; - break; - case CONFIG_STANDING: - currentAnimation = selectedStand; - animateAvatar(deltaTime, speed, DIRECTION_FORWARDS); - editing = true; - break; - case CONFIG_SIDESTEP_LEFT: - currentAnimation = selectedSideStepLeft; - animateAvatar(deltaTime, speed, DIRECTION_LEFT); - editing = true; - break; - case CONFIG_SIDESTEP_RIGHT: - currentAnimation = selectedSideStepRight; - animateAvatar(deltaTime, speed, DIRECTION_RIGHT); - editing = true; - break; - case CONFIG_FLYING: - currentAnimation = selectedFly; - animateAvatar(deltaTime, speed, DIRECTION_FORWARDS); - editing = true; - break; - case CONFIG_FLYING_UP: - currentAnimation = selectedFlyUp; - animateAvatar(deltaTime, speed, DIRECTION_UP); - editing = true; - break; - case CONFIG_FLYING_DOWN: - currentAnimation = selectedFlyDown; - animateAvatar(deltaTime, speed, DIRECTION_DOWN); - editing = true; - break; - default: - break; - } - - // we have to declare these vars here ( outside 'if(!editing)' ), so they are still in scope - // when we record the frame's data and when we do the stats update at the end - var deltaX = 0; - var deltaY = 0; - var deltaZ = 0; - var acceleration = { x:0, y:0, z:0 }; - var accelerationJS = MyAvatar.getAcceleration(); - - // calculate overriding (local) direction of translation for use later when decide which animation should be played - var inverseRotation = Quat.inverse(MyAvatar.orientation); - var localVelocity = Vec3.multiplyQbyV(inverseRotation, MyAvatar.getVelocity()); - - if(!editing) { - - // the first thing to do is find out how fast we're going, - // what our acceleration is and which direction we're principly moving in - - // calcualte (local) change in velocity - var velocity = MyAvatar.getVelocity(); - speed = Vec3.length(velocity); - - // determine the candidate animation to play - var actionToTake = 0; - if( speed < 0.5) { - actionToTake = STANDING; - standHints++; - } - else if( speed < FLYING_SPEED ) { - actionToTake = WALKING; - walkHints++; - } - else if( speed >= FLYING_SPEED ) { - actionToTake = FLYING; - flyHints++; + case state.EDIT_WALK_STYLES: { + motion.curAnim = motion.selWalk; + animateAvatar(deltaTime, speed); + break; } - deltaX = localVelocity.x; - deltaY = localVelocity.y; - deltaZ = -localVelocity.z; + case state.EDIT_WALK_TWEAKS: { + motion.curAnim = motion.selWalk; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_WALK_JOINTS: { + motion.curAnim = motion.selWalk; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_STANDING: { + motion.curAnim = motion.selStand; + motion.direction = FORWARDS; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_SIDESTEP_LEFT: { + motion.curAnim = motion.selSideStepLeft; + motion.direction = LEFT; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_SIDESTEP_RIGHT: { + motion.curAnim = motion.selSideStepRight; + motion.direction = RIGHT; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_FLYING: { + motion.curAnim = motion.selFly; + motion.direction = FORWARDS; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_FLYING_UP: { + motion.curAnim = motion.selFlyUp; + motion.direction = UP; + animateAvatar(deltaTime, speed); + break; + } + + case state.EDIT_FLYING_DOWN: { + motion.curAnim = motion.selFlyDown; + motion.direction = DOWN; + animateAvatar(deltaTime, speed); + break; + } + + default: + break; + } + + // calcualte velocity and speed + var velocity = MyAvatar.getVelocity(); + speed = Vec3.length(velocity); + + if (motion.curTransition !== nullTransition) { + + // finish any live transition before changing state + animateAvatar(deltaTime, speed); + return; + } + var localVelocity = {x: 0, y: 0, z: 0}; + if (speed > 0) { + localVelocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), velocity); + } + + if (!state.editing) { + + // determine the candidate animation state + var actionToTake = undefined; + if (speed < 0.05) { + actionToTake = state.STANDING; + } else if (speed < TAKE_FLIGHT_SPEED) { + actionToTake = state.WALKING; + } else if (speed >= TAKE_FLIGHT_SPEED) { + actionToTake = state.FLYING; + } // determine the principle direction - if(Math.abs(deltaX)>Math.abs(deltaY) - &&Math.abs(deltaX)>Math.abs(deltaZ)) { - if(deltaX<0) { - principleDirection = DIRECTION_RIGHT; - } else { - principleDirection = DIRECTION_LEFT; - } - } - else if(Math.abs(deltaY)>Math.abs(deltaX) - &&Math.abs(deltaY)>Math.abs(deltaZ)) { - if(deltaY>0) { - principleDirection = DIRECTION_UP; - } - else { - principleDirection = DIRECTION_DOWN; - } - } - else if(Math.abs(deltaZ)>Math.abs(deltaX) - &&Math.abs(deltaZ)>Math.abs(deltaY)) { - if(deltaZ>0) { - principleDirection = DIRECTION_FORWARDS; - } else { - principleDirection = DIRECTION_BACKWARDS; - } - } + if (Math.abs(localVelocity.x) > Math.abs(localVelocity.y) && + Math.abs(localVelocity.x) > Math.abs(localVelocity.z)) { - // NB: this section will change significantly once we are ground plane aware - // it will change even more once we have uneven surfaces to deal with + if (localVelocity.x < 0) { + motion.direction = LEFT; + } else { + motion.direction = RIGHT; + } + + } else if (Math.abs(localVelocity.y) > Math.abs(localVelocity.x) && + Math.abs(localVelocity.y) > Math.abs(localVelocity.z)) { + + if (localVelocity.y > 0) { + motion.direction = UP; + } else { + motion.direction = DOWN; + } + + } else if (Math.abs(localVelocity.z) > Math.abs(localVelocity.x) && + Math.abs(localVelocity.z) > Math.abs(localVelocity.y)) { + + if (localVelocity.z < 0) { + motion.direction = FORWARDS; + } else { + motion.direction = BACKWARDS; + } + } // maybe at walking speed, but sideways? - if( actionToTake === WALKING && - ( principleDirection === DIRECTION_LEFT || - principleDirection === DIRECTION_RIGHT )) { - - actionToTake = SIDE_STEPPING; + if (actionToTake === state.WALKING && + (motion.direction === LEFT || + motion.direction === RIGHT)) { + actionToTake = state.SIDE_STEP; } - // maybe at walking speed, but flying up? - if( actionToTake === WALKING && - ( principleDirection === DIRECTION_UP )) { - - actionToTake = FLYING; - standHints--; - flyHints++; + // maybe at walking speed, but flying up or down? + if (actionToTake === state.WALKING && + (motion.direction === UP || + motion.direction === DOWN)) { + actionToTake = state.FLYING; } - // maybe at walking speed, but flying down? - if( actionToTake === WALKING && - ( principleDirection === DIRECTION_DOWN )) { - - actionToTake = FLYING; - standHints--; - flyHints++; - } - - // log this frame's motion for later reference - var accelerationX = ( framesHistory.recentMotions[0].velocity.x - localVelocity.x ) / deltaTime; - var accelerationY = ( localVelocity.y - framesHistory.recentMotions[0].velocity.y ) / deltaTime; - var accelerationZ = ( framesHistory.recentMotions[0].velocity.z - localVelocity.z ) / deltaTime; - acceleration = {x:accelerationX, y:accelerationY, z:accelerationZ}; - - // select appropriate animation and initiate Transition if required - switch(actionToTake) { + // note: The transitions are not compete, and are the most likely + // candidate for the next worklist item + switch (actionToTake) { - case STANDING: + case state.STANDING: { - if( standHints > requiredHints || INTERNAL_STATE===STANDING) { // wait for a few consecutive hints (17mS each) + // do we need to change state? + if (state.currentState !== state.STANDING) { - standHints = 0; - walkHints = 0; - flyHints = 0; + switch (motion.curAnim) { - // do we need to change state? - if( INTERNAL_STATE!==STANDING ) { + case motion.selWalk: { - // initiate the transition - if(currentTransition) { - delete currentTransition; - currentTransition = null; + // Walking to standing + motion.curTransition = new Transition( + motion.curAnim, + motion.selWalk, + [], 0.25, + {x: 0.1, y: 0.5}, + {x: -0.25, y: 1.22}); + break; } - switch(currentAnimation) { + case motion.selFly: + case motion.selFlyUp: + case motion.selFlyDown: { - case selectedWalk: - - // Walking to Standing - var timeWalking = new Date().getTime() - framesHistory.lastWalkStartTime; - - var bezierCoeffsOne = {x:0.0, y:1.0}; - var bezierCoeffsTwo = {x:0.0, y:1.0}; - var transitionTime = 0.4; - - // very different curves for incremental steps - if( timeWalking < 550 ) { - bezierCoeffsOne = {x:0.63, y:0.17}; - bezierCoeffsTwo = {x:0.77, y:0.3}; - transitionTime = 0.75; - } - currentTransition = new Transition( currentAnimation, selectedStand, [], transitionTime, bezierCoeffsOne, bezierCoeffsTwo ); - break; - - - case selectedSideStepLeft: - case selectedSideStepRight: - - break; - - default: - - currentTransition = new Transition(currentAnimation, selectedStand, [], 0.3, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; + // Flying to Standing + motion.curTransition = new Transition( + motion.curAnim, + motion.selStand, + [], 0.5, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; } - setInternalState(STANDING); - currentAnimation = selectedStand; + default: + break; } - animateAvatar(1,0,principleDirection); + state.setInternalState(state.STANDING); + motion.curAnim = motion.selStand; } + animateAvatar(deltaTime, speed); break; + } - case WALKING: - case SIDE_STEPPING: - if( walkHints > requiredHints || - INTERNAL_STATE===WALKING || - INTERNAL_STATE===SIDE_STEPPING ) { // wait for few consecutive hints (17mS each) + case state.WALKING: { - standHints = 0; - walkHints = 0; - flyHints = 0; - - if( actionToTake === WALKING && INTERNAL_STATE !== WALKING) { - - // initiate the transition - if(currentTransition) { - delete currentTransition; - currentTransition = null; - } - - // set the appropriate start position for the walk wheel - if( principleDirection === DIRECTION_BACKWARDS ) { - - walkWheelPosition = selectedWalk.settings.startAngleBackwards; - - } else { - - walkWheelPosition = selectedWalk.settings.startAngleForwards; - } - - switch(currentAnimation) { - - case selectedStand: - - // Standing to Walking - currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.25, {x:0.5,y:0.08}, {x:0.05,y:0.75}); - break; - - case selectedSideStepLeft: - case selectedSideStepRight: - - break; - - default: - - currentTransition = new Transition(currentAnimation, selectedWalk, [], 0.3, {x:0.5,y:0.08}, {x:0.05,y:0.75}); - break; - } - framesHistory.lastWalkStartTime = new Date().getTime(); - setInternalState(WALKING); - currentAnimation = selectedWalk; - } - else if(actionToTake===SIDE_STEPPING) { - - var selectedSideStep = selectedSideStepRight; - if( principleDirection === DIRECTION_LEFT ) { - - selectedSideStep = selectedSideStepLeft; - - } else { - - selectedSideStep = selectedSideStepRight; - } - - if( INTERNAL_STATE !== SIDE_STEPPING ) { - - if( principleDirection === DIRECTION_LEFT ) { - - walkWheelPosition = sideStepCycleStartLeft; - - } else { - - walkWheelPosition = sideStepCycleStartRight; - } - switch(currentAnimation) { - - case selectedStand: - - break; - - default: - - break; - } - setInternalState(SIDE_STEPPING); - } - - currentAnimation = selectedSideStep; - } - animateAvatar( deltaTime, speed, principleDirection ); - } - break; - - case FLYING: - - if( flyHints > requiredHints - 1 || INTERNAL_STATE===FLYING ) { // wait for a few consecutive hints (17mS each) - - standHints = 0; - walkHints = 0; - flyHints = 0; - - if(INTERNAL_STATE!==FLYING) setInternalState(FLYING); - - // change animation for flying directly up or down. TODO - check RecentMotions, if is a change then put a transition on it - if(principleDirection===DIRECTION_UP) { - - if(currentAnimation !== selectedFlyUp) { - - // initiate a Transition - if(currentTransition && currentTransition.nextAnimation!==selectedFlyUp) { - delete currentTransition; - currentTransition = null; - } - switch(currentAnimation) { - - case selectedStand: - - currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - - case selectedSideStepLeft: - case selectedSideStepRight: - - break; - - default: - - currentTransition = new Transition(currentAnimation, selectedFlyUp, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - } - currentAnimation = selectedFlyUp; - } - - } else if(principleDirection==DIRECTION_DOWN) { - - if(currentAnimation !== selectedFlyDown) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced - - // initiate a Transition - if(currentTransition && currentTransition.nextAnimation!==selectedFlyDown) { - delete currentTransition; - currentTransition = null; - } - switch(currentAnimation) { - - case selectedStand: - - currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - - case selectedSideStepLeft: - case selectedSideStepRight: - - break; - - default: - - currentTransition = new Transition(currentAnimation, selectedFlyDown, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - } - currentAnimation = selectedFlyDown; - } + if (state.currentState !== state.WALKING) { + if (motion.direction === BACKWARDS) { + motion.walkWheelPos = motion.selWalk.calibration.startAngleBackwards; } else { - - if(currentAnimation !== selectedFly) { // TODO: as the locomotion gets cleaner (i.e. less false reports from Interface) this value can be reduced - - // initiate a Transition - if(currentTransition && currentTransition.nextAnimation!==selectedFly) { - delete currentTransition; - currentTransition = null; - } - switch(currentAnimation) { - - case selectedStand: - - currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - - case selectedSideStepLeft: - case selectedSideStepRight: - - break; - - default: - - currentTransition = new Transition(currentAnimation, selectedFly, [], 0.35, {x:0.5,y:0.08}, {x:0.28,y:1}); - break; - } - currentAnimation = selectedFly; - } + motion.walkWheelPos = motion.selWalk.calibration.startAngleForwards; } - animateAvatar(deltaTime, speed, principleDirection); + + switch (motion.curAnim) { + + case motion.selStand: { + + // Standing to Walking + motion.curTransition = new Transition( + motion.curAnim, + motion.selWalk, + [], 0.25, + {x: 0.5, y: 0.5}, + {x: 0.5, y: 0.5}); + break; + } + + case motion.selFly: + case motion.selFlyUp: + case motion.selFlyDown: { + + // Flying to Walking + motion.curTransition = new Transition( + motion.curAnim, + motion.selWalk, + [], 0.1, + {x: 0.24, y: 0.03}, + {x: 0.42, y: 1.0}); + break; + } + + default: + + break; + } + state.setInternalState(state.WALKING); } + motion.curAnim = motion.selWalk; + animateAvatar(deltaTime, speed); break; + } + + case state.SIDE_STEP: { + + var selSideStep = 0; + if (motion.direction === LEFT) { + + if (motion.lastDirection !== LEFT) { + motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart; + } + selSideStep = motion.selSideStepLeft; + + } else { + + if (motion.lastDirection !== RIGHT) { + motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart; + } + selSideStep = motion.selSideStepRight; + } + + if (state.currentState !== state.SIDE_STEP) { + + if (motion.direction === LEFT) { + motion.walkWheelPos = motion.selSideStepLeft.calibration.cycleStart; + } else { + motion.walkWheelPos = motion.selSideStepRight.calibration.cycleStart; + } + state.setInternalState(state.SIDE_STEP); + } + motion.curAnim = selSideStep; + animateAvatar(deltaTime, speed); + break; + } + + case state.FLYING: { + + if (state.currentState !== state.FLYING) { + state.setInternalState(state.FLYING); + } + + // change animation for flying directly up or down + if (motion.direction === UP) { + + if (motion.curAnim !== motion.selFlyUp) { + + switch (motion.curAnim) { + + case motion.selStand: + case motion.selWalk: { + + // standing | walking to flying up + motion.curTransition = new Transition( + motion.curAnim, + motion.selFlyUp, + [], 0.35, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; + } + + case motion.selFly: + case motion.selFlyUp: + case motion.selFlyDown: { + + motion.curTransition = new Transition( + motion.curAnim, + motion.selFlyUp, + [], 0.35, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; + } + + default: + + break; + } + motion.curAnim = motion.selFlyUp; + } + + } else if (motion.direction == DOWN) { + + if (motion.curAnim !== motion.selFlyDown) { + + switch (motion.curAnim) { + + case motion.selStand: + case motion.selWalk: { + + motion.curTransition = new Transition( + motion.curAnim, + motion.selFlyDown, + [], 0.35, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; + } + + case motion.selFly: + case motion.selFlyUp: + case motion.selFlyDown: { + + motion.curTransition = new Transition( + motion.curAnim, + motion.selFlyDown, + [], 0.45, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; + } + + default: + + break; + } + motion.curAnim = motion.selFlyDown; + } + + } else { + + if (motion.curAnim !== motion.selFly) { + + switch (motion.curAnim) { + + case motion.selStand: + case motion.selWalk: { + + motion.curTransition = new Transition( + motion.curAnim, + motion.selFly, + [], 0.35, + {x: 1.44, y:0.24}, + {x: 0.61, y:0.92}); + break; + } + + case motion.selFly: + case motion.selFlyUp: + case motion.selFlyDown: { + + motion.curTransition = new Transition( + motion.curAnim, + motion.selFly, + [], 0.75, + {x: 0.5, y: 0.08}, + {x: 0.28, y: 1}); + break; + } + + default: + + break; + } + motion.curAnim = motion.selFly; + } + } + animateAvatar(deltaTime, speed); + break; + }// end case state.FLYING } // end switch(actionToTake) - } // end if(!editing) + } // end if (!state.editing) + + // record the frame's direction and local avatar's local velocity for future reference + motion.lastDirection = motion.direction; + motion.lastVelocity = localVelocity; + } +}); + +// the faster we go, the further we lean forward. the angle is calcualted here +function getLeanPitch(speed) { + + if (speed > TOP_SPEED) { + speed = TOP_SPEED; + } + var leanProgress = speed / TOP_SPEED; + + if (motion.direction === LEFT || + motion.direction === RIGHT) { + leanProgress = 0; + } else { + + var responseSharpness = 1.5; + if (motion.direction == BACKWARDS) { + responseSharpness = 3.0; + } + + leanProgress = filter.bezier((1 - leanProgress), + {x: 0, y: 0.0}, + {x: 0, y: responseSharpness}, + {x: 0, y: 1.5}, + {x: 1, y: 1}).y; + + // determine final pitch and adjust for direction of momentum + if (motion.direction === BACKWARDS) { + leanProgress = -motion.motionPitchMax * leanProgress; + } else { + leanProgress = motion.motionPitchMax * leanProgress; + } + } + + // return the smoothed response + return leanPitchFilter.process(leanProgress); +} + +// calculate the angle at which to bank into corners when turning +function getLeanRoll(deltaTime, speed) { + + var leanRollProgress = 0; + if (speed > TOP_SPEED) { + speed = TOP_SPEED; + } + + // what's our our anglular velocity? + var angularVelocityMax = 70; // from observation + var angularVelocity = filter.radToDeg(MyAvatar.getAngularVelocity().y); + if (angularVelocity > angularVelocityMax) { + angularVelocity = angularVelocityMax; + } + if (angularVelocity < -angularVelocityMax) { + angularVelocity = -angularVelocityMax; + } + + leanRollProgress = speed / TOP_SPEED; + + if (motion.direction !== LEFT && + motion.direction !== RIGHT) { + leanRollProgress *= (Math.abs(angularVelocity) / angularVelocityMax); + } + + // apply our response curve + leanRollProgress = filter.bezier((1 - leanRollProgress), + {x: 0, y: 0}, + {x: 0, y: 1}, + {x: 0, y: 1}, + {x: 1, y: 1}).y; + // which way to lean? + var turnSign = -1; + if (angularVelocity < 0.001) { + turnSign = 1; + } + if (motion.direction === BACKWARDS || + motion.direction === LEFT) { + turnSign *= -1; + } + if (motion.direction === LEFT || + motion.direction === RIGHT) { + leanRollProgress *= 2; + } + + // add damping with simple averaging filter + leanRollProgress = leanRollFilter.process(turnSign * leanRollProgress); + return motion.motionRollMax * leanRollProgress; +} + +function playFootstep(side) { + + var options = new AudioInjectionOptions(); + options.position = Camera.getPosition(); + options.volume = 0.3; + var soundNumber = 2; // 0 to 2 + if (side === RIGHT && motion.makesFootStepSounds) { + Audio.playSound(walkAssets.footsteps[soundNumber + 1], options); + } else if (side === LEFT && motion.makesFootStepSounds) { + Audio.playSound(walkAssets.footsteps[soundNumber], options); + } +} + +// animate the avatar using sine wave generators. inspired by Victorian clockwork dolls +function animateAvatar(deltaTime, speed) { + + var cycle = motion.cumulativeTime; + var transProgress = 1; + var adjFreq = motion.curAnim.calibration.frequency; + + // legs phase and cycle reversal for walking backwards + var reverseModifier = 0; + var reverseSignModifier = 1; + if (motion.direction === BACKWARDS) { + reverseModifier = -180; + reverseSignModifier = -1; + } + + // don't lean into the direction of travel if going up + var leanMod = 1; + if (motion.direction === UP) { + leanMod = 0; + } + + // adjust leaning direction for flying + var flyingModifier = 1; + if (state.currentState.FLYING) { + flyingModifier = -1; + } + + if (motion.curTransition !== nullTransition) { + + // new transiton? + if (motion.curTransition.progress === 0 && + motion.curTransition.walkingAtStart) { + + if (state.currentState !== state.SIDE_STEP) { + + // work out where we want the walk cycle to stop + var leftStop = motion.selWalk.calibration.stopAngleForwards + 180; + var rightStop = motion.selWalk.calibration.stopAngleForwards; + + if (motion.direction === BACKWARDS) { + leftStop = motion.selWalk.calibration.stopAngleBackwards + 180; + rightStop = motion.selWalk.calibration.stopAngleBackwards; + } + + // find the closest stop point from the walk wheel's angle + var angleToLeftStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - leftStop) - 180); + var angleToRightStop = 180 - Math.abs(Math.abs(motion.walkWheelPos - rightStop) - 180); + if (motion.walkWheelPos > angleToLeftStop) { + angleToLeftStop = 360 - angleToLeftStop; + } + if (motion.walkWheelPos > angleToRightStop) { + angleToRightStop = 360 - angleToRightStop; + } + + motion.curTransition.walkWheelIncrement = 3; + + // keep the walkwheel turning by setting the walkWheelIncrement + // until our feet are tucked nicely underneath us. + if (angleToLeftStop < angleToRightStop) { + motion.curTransition.walkStopAngle = leftStop; + } else { + motion.curTransition.walkStopAngle = rightStop; + } + + } else { + + // freeze wheel for sidestepping transitions (for now) + motion.curTransition.walkWheelIncrement = 0; + } + } // end if (new transition and curTransition.walkingAtStart) + + // update the Transition progress + var elapasedTime = (new Date().getTime() - motion.curTransition.startTime) / 1000; + motion.curTransition.progress = elapasedTime / motion.curTransition.transitionDuration; + transProgress = filter.bezier((1 - motion.curTransition.progress), {x: 0, y: 0}, + motion.curTransition.easingLower, + motion.curTransition.easingUpper, {x: 1, y: 1}).y; + + if (motion.curTransition.progress >= 1) { + + // time to kill off the transition + delete motion.curTransition; + motion.curTransition = nullTransition; + + } else { + + if (motion.curTransition.walkingAtStart) { + + if (state.currentState !== state.SIDE_STEP) { + + // if at a stop angle, hold the walk wheel position for remainder of transition + var tolerance = 7; // must be greater than the walkWheel increment + if ((motion.walkWheelPos > (motion.curTransition.walkStopAngle - tolerance)) && + (motion.walkWheelPos < (motion.curTransition.walkStopAngle + tolerance))) { + + motion.curTransition.walkWheelIncrement = 0; + } + // keep turning walk wheel until both feet are below the avi + motion.advanceWalkWheel(motion.curTransition.walkWheelIncrement); + + } else motion.curTransition.walkWheelIncrement = 0; // sidestep + } + } } // end motion.curTransition !== nullTransition - // record the frame's stats for later reference - var thisMotion = new RecentMotion(localVelocity, acceleration, principleDirection, INTERNAL_STATE); - framesHistory.recentMotions.push(thisMotion); - framesHistory.recentMotions.shift(); + // walking? then get the stride length + if (motion.curAnim === motion.selWalk) { + // if the timing's right, take a snapshot of the stride max and recalibrate + var strideMaxAt = motion.curAnim.calibration.forwardStrideMaxAt; + if (motion.direction === BACKWARDS) { + strideMaxAt = motion.curAnim.calibration.backwardsStrideMaxAt; + } - // before we go, populate the stats overlay - if( statsOn ) { + var tolerance = 1.0; + if (motion.walkWheelPos < (strideMaxAt + tolerance) && + motion.walkWheelPos > (strideMaxAt - tolerance)) { - var cumulativeTimeMS = Math.floor(cumulativeTime*1000); - var deltaTimeMS = deltaTime * 1000; - var frameExecutionTime = new Date().getTime() - frameStartTime; - if(frameExecutionTime>frameExecutionTimeMax) frameExecutionTimeMax = frameExecutionTime; + // measure and save stride length + var footRPos = MyAvatar.getJointPosition("RightFoot"); + var footLPos = MyAvatar.getJointPosition("LeftFoot"); + motion.strideLength = Vec3.distance(footRPos, footLPos); - var angluarVelocity = Vec3.length(MyAvatar.getAngularVelocity()); + if (motion.direction === FORWARDS) { + motion.curAnim.calibration.strideLengthForwards = motion.strideLength; + } else if (motion.direction === BACKWARDS) { + motion.curAnim.calibration.strideLengthBackwards = motion.strideLength; + } - var debugInfo = '\n \n \n \n Stats\n--------------------------------------\n \n \n' - + '\nFrame number: '+nFrames - + '\nFrame time: '+deltaTimeMS.toFixed(2) - + ' mS\nRender time: '+frameExecutionTime.toFixed(0) - + ' mS\nLocalised speed: '+speed.toFixed(3) - + ' m/s\nCumulative Time '+cumulativeTimeMS.toFixed(0) - + ' mS\nState: '+internalStateAsString(INTERNAL_STATE) - + ' \nDirection: '+directionAsString(principleDirection) - + ' \nAngular Velocity: ' + angluarVelocity.toFixed(3) - + ' rad/s'; - Overlays.editOverlay(debugStats, {text: debugInfo}); + } else { - // update these every 250 mS (assuming 60 fps) - if( nFrames % 15 === 0 ) { - var debugInfo = ' Periodic Stats\n--------------------------------------\n \n \n' - + ' \n \nRender time peak hold: '+frameExecutionTimeMax.toFixed(0) - + ' mS\n \n \n(L) MyAvatar.getVelocity()' - + ' \n \nlocalVelocityX: '+deltaX.toFixed(1) - + ' m/s\nlocalVelocityY: '+deltaY.toFixed(1) - + ' m/s\nlocalVelocityZ: '+deltaZ.toFixed(1) - + ' m/s\n \n(G) MyAvatar.getAcceleration()' - + ' \n\nAcceleration X: '+accelerationJS.x.toFixed(1) - + ' m/s/s\nAcceleration Y: '+accelerationJS.y.toFixed(1) - + ' m/s/s\nAcceleration Z: '+accelerationJS.z.toFixed(1) - + ' m/s/s\n \n(L) Acceleration using\nMyAvatar.getVelocity()' - + ' \n \nAcceleration X: '+acceleration.x.toFixed(1) - + ' m/s/s\nAcceleration Y: '+acceleration.y.toFixed(1) - + ' m/s/s\nAcceleration Z: '+acceleration.z.toFixed(1) - + ' m/s/s'; - Overlays.editOverlay(debugStatsPeriodic, {text: debugInfo}); - frameExecutionTimeMax = 0; + // use the saved value for stride length + if (motion.direction === FORWARDS) { + motion.strideLength = motion.curAnim.calibration.strideLengthForwards; + } else if (motion.direction === BACKWARDS) { + motion.strideLength = motion.curAnim.calibration.strideLengthBackwards; } } - } -}); + } // end get walk stride length + // sidestepping? get the stride length + if (motion.curAnim === motion.selSideStepLeft || + motion.curAnim === motion.selSideStepRight) { -// overlays start + // if the timing's right, take a snapshot of the stride max and recalibrate the stride length + var tolerance = 1.0; + if (motion.direction === LEFT) { -// controller dimensions -var backgroundWidth = 350; -var backgroundHeight = 700; -var backgroundX = Window.innerWidth-backgroundWidth-58; -var backgroundY = Window.innerHeight/2 - backgroundHeight/2; -var minSliderX = backgroundX + 30; -var maxSliderX = backgroundX + 295; -var sliderRangeX = 295 - 30; -var jointsControlWidth = 200; -var jointsControlHeight = 300; -var jointsControlX = backgroundX + backgroundWidth/2 - jointsControlWidth/2; -var jointsControlY = backgroundY + 242 - jointsControlHeight/2; -var buttonsY = 20; // distance from top of panel to buttons + if (motion.walkWheelPos < motion.curAnim.calibration.strideMaxAt + tolerance && + motion.walkWheelPos > motion.curAnim.calibration.strideMaxAt - tolerance) { -// arrays of overlay names -var sliderThumbOverlays = []; // thumb sliders -var backgroundOverlays = []; -var buttonOverlays = []; -var jointsControlOverlays = []; -var bigButtonOverlays = []; + var footRPos = MyAvatar.getJointPosition("RightFoot"); + var footLPos = MyAvatar.getJointPosition("LeftFoot"); + motion.strideLength = Vec3.distance(footRPos, footLPos); + motion.curAnim.calibration.strideLength = motion.strideLength; + } else motion.strideLength = motion.selSideStepLeft.calibration.strideLength; -// load UI backgrounds -var controlsBackground = Overlays.addOverlay("image", { - bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight }, - imageURL: pathToOverlays+"ddao-background.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -backgroundOverlays.push(controlsBackground); + } else if (motion.direction === RIGHT) { -var controlsBackgroundWalkEditStyles = Overlays.addOverlay("image", { - bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight }, - imageURL: pathToOverlays+"ddao-background-edit-styles.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -backgroundOverlays.push(controlsBackgroundWalkEditStyles); + if (motion.walkWheelPos < motion.curAnim.calibration.strideMaxAt + tolerance && + motion.walkWheelPos > motion.curAnim.calibration.strideMaxAt - tolerance) { -var controlsBackgroundWalkEditTweaks = Overlays.addOverlay("image", { - bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight }, - imageURL: pathToOverlays+"ddao-background-edit-tweaks.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -backgroundOverlays.push(controlsBackgroundWalkEditTweaks); + var footRPos = MyAvatar.getJointPosition("RightFoot"); + var footLPos = MyAvatar.getJointPosition("LeftFoot"); + motion.strideLength = Vec3.distance(footRPos, footLPos); + motion.curAnim.calibration.strideLength = motion.strideLength; -var controlsBackgroundWalkEditJoints = Overlays.addOverlay("image", { - bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight }, - imageURL: pathToOverlays+"ddao-background-edit-joints.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -backgroundOverlays.push(controlsBackgroundWalkEditJoints); + } else motion.strideLength = motion.selSideStepRight.calibration.strideLength; + } + } // end get sidestep stride length -var controlsBackgroundFlyingEdit = Overlays.addOverlay("image", { - bounds: { x: backgroundX, y: backgroundY, width: backgroundWidth, height: backgroundHeight }, - imageURL: pathToOverlays+"ddao-background-flying-edit.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -backgroundOverlays.push(controlsBackgroundFlyingEdit); + // turn the walk wheel + if (motion.curAnim === motion.selWalk || + motion.curAnim === motion.selSideStepLeft || + motion.curAnim === motion.selSideStepRight || + motion.curTransition.walkingAtStart) { -// minimised tab - not put in array, as is a one off -var controlsMinimisedTab = Overlays.addOverlay("image", { - x: Window.innerWidth-58, y: Window.innerHeight -145, width: 50, height: 50, - //subImage: { x: 0, y: 50, width: 50, height: 50 }, - imageURL: pathToOverlays + 'ddao-minimise-tab.png', - visible: minimised, - alpha: 0.9 - }); + // wrap the stride length around a 'surveyor's wheel' twice and calculate + // the angular speed at the given (linear) speed: + // omega = v / r , where r = circumference / 2 PI , where circumference = 2 * stride length + var strideLength = motion.strideLength; + var wheelRadius = strideLength / Math.PI; + var angularVelocity = speed / wheelRadius; -// load character joint selection control images -var hipsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-hips.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(hipsJointControl); + // calculate the degrees turned (at this angular speed) since last frame + var radiansTurnedSinceLastFrame = deltaTime * angularVelocity; + var degreesTurnedSinceLastFrame = filter.radToDeg(radiansTurnedSinceLastFrame); -var upperLegsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-upper-legs.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(upperLegsJointControl); + // if we are in an edit mode, we will need fake time to turn the wheel + if (state.currentState !== state.WALKING && + state.currentState !== state.SIDE_STEP) { + degreesTurnedSinceLastFrame = motion.curAnim.calibration.frequency / 70; + } -var lowerLegsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-lower-legs.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(lowerLegsJointControl); + // advance the walk wheel the appropriate amount + motion.advanceWalkWheel(degreesTurnedSinceLastFrame); -var feetJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-feet.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(feetJointControl); + // set the new values for the exact correct walk cycle speed + adjFreq = 1; + cycle = motion.walkWheelPos; -var toesJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-toes.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(toesJointControl); + } // end of walk wheel and stride length calculation -var spineJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-spine.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(spineJointControl); + // motion vars + var pitchOsc = 0; + var pitchOscLeft = 0; + var pitchOscRight = 0; + var yawOsc = 0; + var yawOscLeft = 0; + var yawOscRight = 0; + var rollOsc = 0; + var pitchOffset = 0; + var yawOffset = 0; + var rollOffset = 0; + var swayOsc = 0; + var bobOsc = 0; + var thrustOsc = 0; + var swayOscLast = 0; + var bobOscLast = 0; + var thrustOscLast = 0; -var spine1JointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-spine1.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(spine1JointControl); + // historical (for transitions) + var pitchOscLast = 0; + var pitchOscLeftLast = 0; + var pitchOscRightLast = 0; + var yawOscLast = 0; + var rollOscLast = 0; + var pitchOffsetLast = 0; + var yawOffsetLast = 0; + var rollOffsetLast = 0; -var spine2JointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-spine2.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(spine2JointControl); + // feet + var sideStepFootPitchModifier = 1; + var sideStepHandPitchSign = 1; -var shouldersJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-shoulders.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(shouldersJointControl); + // calculate hips translation + if (motion.curTransition !== nullTransition) { -var upperArmsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-upper-arms.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(upperArmsJointControl); + if (motion.curTransition.walkingAtStart) { -var forearmsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-forearms.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(forearmsJointControl); + swayOsc = motion.curAnim.joints[0].sway * + Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency + + motion.curAnim.joints[0].swayPhase)) + motion.curAnim.joints[0].swayOffset; -var handsJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-hands.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(handsJointControl); + var bobPhase = motion.curAnim.joints[0].bobPhase; + if (motion.direction === motion.BACKWARDS) { + bobPhase += 90; + } + bobOsc = motion.curAnim.joints[0].bob * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + bobPhase)) + + motion.curAnim.joints[0].bobOffset; -var headJointControl = Overlays.addOverlay("image", { - bounds: { x: jointsControlX, y: jointsControlY, width: 200, height: 300}, - imageURL: pathToOverlays+"ddao-background-edit-head.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -jointsControlOverlays.push(headJointControl); + thrustOsc = motion.curAnim.joints[0].thrust * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2 + + motion.curAnim.joints[0].thrustPhase)) + + motion.curAnim.joints[0].thrustOffset; + swayOscLast = motion.curTransition.lastAnim.joints[0].sway * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[0].swayPhase)) + + motion.curTransition.lastAnim.joints[0].swayOffset; -// sider thumb overlays -var sliderOne = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderOne); -var sliderTwo = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderTwo); -var sliderThree = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderThree); -var sliderFour = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderFour); -var sliderFive = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderFive); -var sliderSix = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderSix); -var sliderSeven = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderSeven); -var sliderEight = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderEight); -var sliderNine = Overlays.addOverlay("image", { - bounds: { x: 0, y: 0, width: 25, height: 25 }, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -sliderThumbOverlays.push(sliderNine); + var bobPhaseLast = motion.curTransition.lastAnim.joints[0].bobPhase; + if (motion.direction === motion.BACKWARDS) { + bobPhaseLast +=90; + } + bobOscLast = motion.curTransition.lastAnim.joints[0].bob * + Math.sin(filter.degToRad(motion.walkWheelPos + bobPhaseLast)); + bobOscLast = filter.clipTrough(bobOscLast, motion.curTransition.lastAnim.joints[0].bob , 2); + bobOscLast = hipsBobLPFilter.process(bobOscLast); + bobOscLast += motion.curTransition.lastAnim.joints[0].bobOffset; + thrustOscLast = motion.curTransition.lastAnim.joints[0].thrust * + Math.sin(filter.degToRad(motion.walkWheelPos * 2 + + motion.curTransition.lastAnim.joints[0].thrustPhase)) + + motion.curTransition.lastAnim.joints[0].thrustOffset; -// button overlays -var onButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-on-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(onButton); -var offButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+20, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-off-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(offButton); -var configWalkButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-walk-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkButton); -var configWalkButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-walk-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkButtonSelected); -var configStandButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-stand-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configStandButton); -var configStandButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-stand-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configStandButtonSelected); + // end if walking at start of transition -var configFlyingButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingButton); -var configFlyingButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingButtonSelected); + } else { -var configFlyingUpButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-up-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingUpButton); -var configFlyingUpButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-up-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingUpButtonSelected); + swayOsc = motion.curAnim.joints[0].sway * + Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) + + motion.curAnim.joints[0].swayOffset; -var configFlyingDownButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-down-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingDownButton); -var configFlyingDownButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-fly-down-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configFlyingDownButtonSelected); + var bobPhase = motion.curAnim.joints[0].bobPhase; + if (motion.direction === motion.BACKWARDS) { + bobPhase += 90; + } + bobOsc = motion.curAnim.joints[0].bob * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + bobPhase)); + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { -var hideButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-hide-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(hideButton); -var hideButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-hide-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(hideButtonSelected); -var configWalkStylesButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-walk-styles-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkStylesButton); -var configWalkStylesButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-walk-styles-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkStylesButtonSelected); -var configWalkTweaksButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-walk-tweaks-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkTweaksButton); -var configWalkTweaksButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+146, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-walk-tweaks-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkTweaksButtonSelected); + // apply clipping filter to flatten the curve's peaks (inputValue, peak, strength) + bobOsc = filter.clipTrough(bobOsc, motion.curAnim.joints[0].bob , 2); + bobOsc = hipsBobLPFilter.process(bobOsc); + } + bobOsc += motion.curAnim.joints[0].bobOffset; -var configSideStepLeftButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-sidestep-left-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configSideStepLeftButton); -var configSideStepLeftButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+83, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-sidestep-left-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configSideStepLeftButtonSelected); + thrustOsc = motion.curAnim.joints[0].thrust * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + + motion.curAnim.joints[0].thrustPhase)) + + motion.curAnim.joints[0].thrustOffset; -var configSideStepRightButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-sidestep-right-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configSideStepRightButton); -var configSideStepRightButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-edit-sidestep-right-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configSideStepRightButtonSelected); + swayOscLast = motion.curTransition.lastAnim.joints[0].sway * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[0].swayPhase)) + + motion.curTransition.lastAnim.joints[0].swayOffset; -var configWalkJointsButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-bones-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkJointsButton); -var configWalkJointsButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+209, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-bones-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(configWalkJointsButtonSelected); + bobOscLast = motion.curTransition.lastAnim.joints[0].bob * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2 + + motion.curTransition.lastAnim.joints[0].bobPhase)) + + motion.curTransition.lastAnim.joints[0].bobOffset; -var backButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-back-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(backButton); -var backButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX+272, y: backgroundY+buttonsY, width: 60, height: 47 }, - imageURL: pathToOverlays+"ddao-back-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -buttonOverlays.push(backButtonSelected); + thrustOscLast = motion.curTransition.lastAnim.joints[0].thrust * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2 + + motion.curTransition.lastAnim.joints[0].thrustPhase)) + + motion.curTransition.lastAnim.joints[0].thrustOffset; + } -// big button overlays - front panel -var bigButtonYOffset = 408; // distance from top of panel to top of first button + swayOsc = (transProgress * swayOsc) + ((1 - transProgress) * swayOscLast); + bobOsc = (transProgress * bobOsc) + ((1 - transProgress) * bobOscLast); + thrustOsc = (transProgress * thrustOsc) + ((1 - transProgress) * thrustOscLast); -var femaleBigButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-female-big-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(femaleBigButton); + // end if walking at start of transition -var femaleBigButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-female-big-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(femaleBigButtonSelected); + } else { -var maleBigButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-male-big-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(maleBigButton); + swayOsc = motion.curAnim.joints[0].sway * + Math.sin(filter.degToRad(cycle * adjFreq + motion.curAnim.joints[0].swayPhase)) + + motion.curAnim.joints[0].swayOffset; -var maleBigButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 60, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-male-big-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(maleBigButtonSelected); + bobPhase = motion.curAnim.joints[0].bobPhase; + if (motion.direction === motion.BACKWARDS) bobPhase += 90; + bobOsc = motion.curAnim.joints[0].bob * Math.sin(filter.degToRad(cycle * adjFreq * 2 + bobPhase)); + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { -var armsFreeBigButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-arms-free-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(armsFreeBigButton); + // apply clipping filter to flatten the curve's peaks (inputValue, peak, strength) + bobOsc = filter.clipTrough(bobOsc, motion.curAnim.joints[0].bob , 2); + bobOsc = hipsBobLPFilter.process(bobOsc); + } + bobOsc += motion.curAnim.joints[0].bobOffset; -var armsFreeBigButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 120, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-arms-free-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(armsFreeBigButtonSelected); + thrustOsc = motion.curAnim.joints[0].thrust * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + + motion.curAnim.joints[0].thrustPhase)) + + motion.curAnim.joints[0].thrustOffset; + } -var footstepsBigButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-footsteps-big-button.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(footstepsBigButton); + // convert local hips translations to global and apply + var aviOrientation = MyAvatar.orientation; + var front = Quat.getFront(aviOrientation); + var right = Quat.getRight(aviOrientation); + var up = Quat.getUp(aviOrientation); + var aviFront = Vec3.multiply(front, thrustOsc); + var aviRight = Vec3.multiply(right, swayOsc); + var aviUp = Vec3.multiply(up, bobOsc); + var aviTranslationOffset = {x: 0, y: 0, z: 0}; -var footstepsBigButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset + 180, width: 230, height: 36}, - imageURL: pathToOverlays+"ddao-footsteps-big-button-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(footstepsBigButtonSelected); + aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviFront); + aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviRight); + aviTranslationOffset = Vec3.sum(aviTranslationOffset, aviUp); + MyAvatar.setSkeletonOffset({ + x: aviTranslationOffset.x, + y: aviTranslationOffset.y, + z: aviTranslationOffset.z + }); -// walk styles -bigButtonYOffset = 121; -var strutWalkBigButton = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 }, - imageURL: pathToOverlays+"ddao-walk-select-button-strut.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(strutWalkBigButton); + // hips rotation + if (motion.curTransition !== nullTransition) { -var strutWalkBigButtonSelected = Overlays.addOverlay("image", { - bounds: { x: backgroundX + backgroundWidth/2 - 115, y: backgroundY + bigButtonYOffset, width: 230, height: 36 }, - imageURL: pathToOverlays+"ddao-walk-select-button-strut-selected.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1, - visible: false - }); -bigButtonOverlays.push(strutWalkBigButtonSelected); + if (motion.curTransition.walkingAtStart) { -// overlays to show the walk wheel stats -var walkWheelZLine = Overlays.addOverlay("line3d", { - position: { x: 0, y: 0, z:hipsToFeetDistance }, - end: { x: 0, y: 0, z: -hipsToFeetDistance }, - color: { red: 0, green: 255, blue: 255}, - alpha: 1, - lineWidth: 5, - visible: statsOn, - anchor: "MyAvatar" - }); -var walkWheelYLine = Overlays.addOverlay("line3d", { - position: { x: 0, y: hipsToFeetDistance, z:0 }, - end: { x: 0, y: -hipsToFeetDistance, z:0 }, - color: { red: 255, green: 0, blue: 255}, - alpha: 1, - lineWidth: 5, - visible: statsOn, - anchor: "MyAvatar" - }); -var debugStats = Overlays.addOverlay("text", { - x: backgroundX-199, y: backgroundY, - width: 200, - height: 180, - color: { red: 204, green: 204, blue: 204}, - topMargin: 10, - leftMargin: 15, - visible: statsOn, - backgroundColor: { red: 34, green: 34, blue: 34}, - alpha: 1.0, - text: "Debug Stats\n\n\nNothing to report yet." - }); -var debugStatsPeriodic = Overlays.addOverlay("text", { - x: backgroundX-199, y: backgroundY+179, - width: 200, - height: 392, - color: { red: 204, green: 204, blue: 204}, - topMargin: 5, - leftMargin: 15, - visible: statsOn, - backgroundColor: { red: 34, green: 34, blue: 34}, - alpha: 1.0, - text: "Debug Stats\n\n\nNothing to report yet." - }); -var walkWheelStats = Overlays.addOverlay("text", { - x: backgroundX-199, y: backgroundY+510, - width: 200, - height: 190, - color: { red: 204, green: 204, blue: 204}, - topMargin: 5, - leftMargin: 15, - visible: statsOn, - backgroundColor: { red: 34, green: 34, blue: 34}, - alpha: 1.0, - text: "WalkWheel Stats\n\n\nNothing to report yet.\n\n\nPlease start walking\nto see the walkwheel." - }); + pitchOsc = motion.curAnim.joints[0].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency * 2 + + motion.curAnim.joints[0].pitchPhase)) + motion.curAnim.joints[0].pitchOffset; -// various show / hide GUI element functions -function doStandardMenu() { - hidebuttonOverlays(); - hideJointControls(); - setBackground(controlsBackground); - if(powerOn) setButtonOverlayVisible(onButton); - else setButtonOverlayVisible(offButton); - setButtonOverlayVisible(configWalkButton); - setButtonOverlayVisible(configStandButton); - setButtonOverlayVisible(configFlyingButton); - setButtonOverlayVisible(hideButton); - setSliderThumbsVisible(false); - showFrontPanelButtons(true); - showWalkStyleButtons(false); -} -function showFrontPanelButtons(showButtons) { + yawOsc = motion.curAnim.joints[0].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency + + motion.curAnim.joints[0].yawPhase - reverseModifier)) + motion.curAnim.joints[0].yawOffset; + + rollOsc = motion.curAnim.joints[0].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * motion.curAnim.calibration.frequency + + motion.curAnim.joints[0].rollPhase)) + motion.curAnim.joints[0].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[0].pitch * + Math.sin(filter.degToRad(motion.walkWheelPos * 2 + + motion.curTransition.lastAnim.joints[0].pitchPhase)) + + motion.curTransition.lastAnim.joints[0].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[0].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[0].yawPhase)); + + yawOscLast += motion.curTransition.lastAnim.joints[0].yaw * + hipsYawShaper.shapeWave(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[0].yawPhase - reverseModifier)) + + motion.curTransition.lastAnim.joints[0].yawOffset; + + rollOscLast = (motion.curTransition.lastAnim.joints[0].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[0].rollPhase)) + + motion.curTransition.lastAnim.joints[0].rollOffset); + + } else { + + pitchOsc = motion.curAnim.joints[0].pitch * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + + motion.curAnim.joints[0].pitchPhase)) + + motion.curAnim.joints[0].pitchOffset; + + yawOsc = motion.curAnim.joints[0].yaw * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[0].yawPhase - reverseModifier)) + + motion.curAnim.joints[0].yawOffset; + + rollOsc = motion.curAnim.joints[0].roll * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[0].rollPhase)) + + motion.curAnim.joints[0].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[0].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2 + + motion.curTransition.lastAnim.joints[0].pitchPhase)) + + motion.curTransition.lastAnim.joints[0].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[0].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[0].yawPhase - reverseModifier)) + + motion.curTransition.lastAnim.joints[0].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[0].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[0].rollPhase)) + + motion.curTransition.lastAnim.joints[0].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); - if(avatarGender===FEMALE) { - Overlays.editOverlay(femaleBigButtonSelected, { visible: showButtons } ); - Overlays.editOverlay(femaleBigButton, { visible: false } ); - Overlays.editOverlay(maleBigButtonSelected, { visible: false } ); - Overlays.editOverlay(maleBigButton, { visible: showButtons } ); } else { - Overlays.editOverlay(femaleBigButtonSelected, { visible: false } ); - Overlays.editOverlay(femaleBigButton, { visible: showButtons } ); - Overlays.editOverlay(maleBigButtonSelected, { visible: showButtons } ); - Overlays.editOverlay(maleBigButton, { visible: false } ); + + pitchOsc = motion.curAnim.joints[0].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[0].pitchPhase)) + + motion.curAnim.joints[0].pitchOffset; + + yawOsc = motion.curAnim.joints[0].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[0].yawPhase)); + + yawOsc += motion.curAnim.joints[0].yaw * + hipsYawShaper.shapeWave(filter.degToRad(cycle * adjFreq) + + motion.curAnim.joints[0].yawPhase - reverseModifier)+ + motion.curAnim.joints[0].yawOffset; + + rollOsc = (motion.curAnim.joints[0].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[0].rollPhase)) + + motion.curAnim.joints[0].rollOffset); } - if(armsFree) { - Overlays.editOverlay(armsFreeBigButtonSelected, { visible: showButtons } ); - Overlays.editOverlay(armsFreeBigButton, { visible: false } ); + + // apply hips rotation + MyAvatar.setJointData("Hips", Quat.fromPitchYawRollDegrees( + pitchOsc + (leanMod * getLeanPitch(speed)), + yawOsc, + rollOsc + getLeanRoll(deltaTime, speed))); + + // upper legs + if (state.currentState !== state.SIDE_STEP && + state.currentState !== state.EDIT_SIDESTEP_LEFT && + state.currentState !== state.EDIT_SIDESTEP_RIGHT) { + + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOscLeft = motion.curAnim.joints[1].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + reverseModifier * motion.curAnim.joints[1].pitchPhase)); + + pitchOscRight = motion.curAnim.joints[1].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + reverseModifier * motion.curAnim.joints[1].pitchPhase)); + + yawOsc = motion.curAnim.joints[1].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[1].yawPhase)); + + rollOsc = motion.curAnim.joints[1].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[1].rollPhase)); + + pitchOffset = motion.curAnim.joints[1].pitchOffset; + yawOffset = motion.curAnim.joints[1].yawOffset; + rollOffset = motion.curAnim.joints[1].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[1].pitch * + motion.curTransition.lastAnim.harmonics.leftUpperLeg.calculate( + filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[1].pitchPhase + 180 + reverseModifier)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[1].pitch * + motion.curTransition.lastAnim.harmonics.rightUpperLeg.calculate( + filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[1].pitchPhase + reverseModifier)); + + yawOscLast = motion.curTransition.lastAnim.joints[1].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[1].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[1].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[1].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[1].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[1].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[1].rollOffset; + + } else { + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.joints[1].pitch * + motion.curAnim.harmonics.leftUpperLeg.calculate(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase + 180 + reverseModifier)); + pitchOscRight = motion.curAnim.joints[1].pitch * + motion.curAnim.harmonics.rightUpperLeg.calculate(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase + reverseModifier)); + } else { + + pitchOscLeft = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase)); + pitchOscRight = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase)); + } + + yawOsc = motion.curAnim.joints[1].yaw * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].yawPhase)); + + rollOsc = motion.curAnim.joints[1].roll * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].rollPhase)); + + pitchOffset = motion.curAnim.joints[1].pitchOffset; + yawOffset = motion.curAnim.joints[1].yawOffset; + rollOffset = motion.curAnim.joints[1].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[1].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + reverseModifier * motion.curTransition.lastAnim.joints[1].pitchPhase)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[1].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + reverseModifier * motion.curTransition.lastAnim.joints[1].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[1].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[1].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[1].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[1].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[1].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[1].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[1].rollOffset; + } + pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast); + pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast); + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + + } else { + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.joints[1].pitch * + motion.curAnim.harmonics.leftUpperLeg.calculate(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase + 180 + reverseModifier)); + pitchOscRight = motion.curAnim.joints[1].pitch * + motion.curAnim.harmonics.rightUpperLeg.calculate(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase + reverseModifier)); + } else { + + pitchOscLeft = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase)); + pitchOscRight = motion.curAnim.joints[1].pitch * Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[1].pitchPhase)); + } + + yawOsc = motion.curAnim.joints[1].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[1].yawPhase)); + + rollOsc = motion.curAnim.joints[1].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[1].rollPhase)); + + pitchOffset = motion.curAnim.joints[1].pitchOffset; + yawOffset = motion.curAnim.joints[1].yawOffset; + rollOffset = motion.curAnim.joints[1].rollOffset; + } + + // apply the upper leg rotations + MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees( + pitchOscLeft + pitchOffset, + yawOsc - yawOffset, + -rollOsc + rollOffset)); + + MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees( + pitchOscRight + pitchOffset, + yawOsc + yawOffset, + -rollOsc - rollOffset)); + + // lower leg + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOscLeft = motion.curAnim.joints[2].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[2].pitchPhase + 180)); + + pitchOscRight = motion.curAnim.joints[2].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[2].pitchPhase)); + + yawOsc = motion.curAnim.joints[2].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[2].yawPhase)); + + rollOsc = motion.curAnim.joints[2].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[2].rollPhase)); + + pitchOffset = motion.curAnim.joints[2].pitchOffset; + yawOffset = motion.curAnim.joints[2].yawOffset; + rollOffset = motion.curAnim.joints[2].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[2].pitch * + motion.curTransition.lastAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[2].pitchPhase + 180)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[2].pitch * + motion.curTransition.lastAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[2].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[2].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[2].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[2].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[2].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[2].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[2].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[2].rollOffset; + + } else { + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[2].pitchPhase + 180)); + pitchOscRight = motion.curAnim.harmonics.rightLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[2].pitchPhase)); + + } else { + + pitchOscLeft = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].pitchPhase + 180)); + + pitchOscRight = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].pitchPhase)); + } + pitchOscLeft *= motion.curAnim.joints[2].pitch; + pitchOscRight *= motion.curAnim.joints[2].pitch; + + yawOsc = motion.curAnim.joints[2].yaw * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[2].yawPhase)); + + rollOsc = motion.curAnim.joints[2].roll * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[2].rollPhase)); + + pitchOffset = motion.curAnim.joints[2].pitchOffset; + yawOffset = motion.curAnim.joints[2].yawOffset; + rollOffset = motion.curAnim.joints[2].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[2].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[2].pitchPhase + 180)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[2].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[2].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[2].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[2].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[2].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[2].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[2].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[2].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[2].rollOffset; + } + + pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast); + pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast); + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + + rollOscLeft = rollOsc; + rollOscRight = rollOsc; + + } else { // end if transitioning + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.harmonics.leftLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[2].pitchPhase + 180)); + pitchOscRight = motion.curAnim.harmonics.rightLowerLeg.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[2].pitchPhase)); + + } else { + + pitchOscLeft = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].pitchPhase + 180)); + + pitchOscRight = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].pitchPhase)); + } + + pitchOscLeft *= motion.curAnim.joints[2].pitch; + pitchOscRight *= motion.curAnim.joints[2].pitch; + + yawOsc = motion.curAnim.joints[2].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].yawPhase)); + + rollOsc = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].rollPhase)); + + rollOsc = motion.curAnim.joints[2].roll; + + pitchOffset = motion.curAnim.joints[2].pitchOffset; + yawOffset = motion.curAnim.joints[2].yawOffset; + rollOffset = motion.curAnim.joints[2].rollOffset; + } + + pitchOscLeft += pitchOffset; + pitchOscRight += pitchOffset; + + // apply lower leg joint rotations + MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( + pitchOscLeft, + yawOsc + yawOffset, + rollOsc + rollOffset)); + MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( + pitchOscRight, + yawOsc - yawOffset, + rollOsc - rollOffset)); + + } // end if !state.SIDE_STEP + + else if (state.currentState === state.SIDE_STEP || + state.currentState === state.EDIT_SIDESTEP_LEFT || + state.currentState === state.EDIT_SIDESTEP_RIGHT) { + + // sidestepping uses the sinewave generators slightly differently for the legs + pitchOsc = motion.curAnim.joints[1].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[1].pitchPhase)); + + yawOsc = motion.curAnim.joints[1].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[1].yawPhase)); + + rollOsc = motion.curAnim.joints[1].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[1].rollPhase)); + + // apply upper leg rotations for sidestepping + MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees( + -pitchOsc + motion.curAnim.joints[1].pitchOffset, + yawOsc + motion.curAnim.joints[1].yawOffset, + rollOsc + motion.curAnim.joints[1].rollOffset)); + + MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees( + pitchOsc + motion.curAnim.joints[1].pitchOffset, + yawOsc - motion.curAnim.joints[1].yawOffset, + -rollOsc - motion.curAnim.joints[1].rollOffset)); + + // calculate lower leg joint rotations for sidestepping + pitchOsc = motion.curAnim.joints[2].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].pitchPhase)); + + yawOsc = motion.curAnim.joints[2].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].yawPhase)); + + rollOsc = motion.curAnim.joints[2].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[2].rollPhase)); + + // apply lower leg joint rotations + MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( + -pitchOsc + motion.curAnim.joints[2].pitchOffset, + yawOsc - motion.curAnim.joints[2].yawOffset, + rollOsc - motion.curAnim.joints[2].rollOffset)); + + MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( + pitchOsc + motion.curAnim.joints[2].pitchOffset, + yawOsc + motion.curAnim.joints[2].yawOffset, + rollOsc + motion.curAnim.joints[2].rollOffset)); + } + + // feet + if (motion.curAnim === motion.selSideStepLeft || + motion.curAnim === motion.selSideStepRight ) { + + sideStepHandPitchSign = -1; + sideStepFootPitchModifier = 0.5; + } + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOscLeft = motion.curAnim.joints[3].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[3].pitchPhase) + 180); + + pitchOscRight = motion.curAnim.joints[3].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[3].pitchPhase)); + + yawOsc = motion.curAnim.joints[3].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[3].yawPhase)); + + rollOsc = motion.curAnim.joints[3].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[3].rollPhase)); + + pitchOffset = motion.curAnim.joints[3].pitchOffset; + yawOffset = motion.curAnim.joints[3].yawOffset; + rollOffset = motion.curAnim.joints[3].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[3].pitch * + motion.curTransition.lastAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * motion.walkWheelPos + + motion.curTransition.lastAnim.joints[3].pitchPhase + reverseModifier)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[3].pitch * + motion.curTransition.lastAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * motion.walkWheelPos + + motion.curTransition.lastAnim.joints[3].pitchPhase + 180 + reverseModifier)); + + yawOscLast = motion.curTransition.lastAnim.joints[3].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[3].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[3].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[3].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[3].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[3].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[3].rollOffset; + + } else { + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[3].pitchPhase + reverseModifier)); + + pitchOscRight = motion.curAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[3].pitchPhase + 180 + reverseModifier)); + + } else { + + pitchOscLeft = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier + + motion.curAnim.joints[3].pitchPhase)); + + pitchOscRight = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier + + motion.curAnim.joints[3].pitchPhase + 180)); + } + + yawOsc = motion.curAnim.joints[3].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[3].yawPhase)); + + rollOsc = motion.curAnim.joints[3].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[3].rollPhase)); + + pitchOffset = motion.curAnim.joints[3].pitchOffset; + yawOffset = motion.curAnim.joints[3].yawOffset; + rollOffset = motion.curAnim.joints[3].rollOffset; + + pitchOscLeftLast = motion.curTransition.lastAnim.joints[3].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[3].pitchPhase + 180)); + + pitchOscRightLast = motion.curTransition.lastAnim.joints[3].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[3].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[3].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[3].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[3].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[3].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[3].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[3].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[3].rollOffset; + } + + pitchOscLeft = (transProgress * pitchOscLeft) + ((1 - transProgress) * pitchOscLeftLast); + pitchOscRight = (transProgress * pitchOscRight) + ((1 - transProgress) * pitchOscRightLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast); + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + } else { - Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } ); - Overlays.editOverlay(armsFreeBigButton, { visible: showButtons } ); + + if (state.currentState === state.WALKING || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS) { + + pitchOscLeft = motion.curAnim.harmonics.leftFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[3].pitchPhase + reverseModifier)); + + pitchOscRight = motion.curAnim.harmonics.rightFoot.calculate(filter.degToRad(reverseSignModifier * cycle * adjFreq + + motion.curAnim.joints[3].pitchPhase + 180 + reverseModifier)); + + } else { + + pitchOscLeft = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier + + motion.curAnim.joints[3].pitchPhase)); + + pitchOscRight = Math.sin(filter.degToRad(cycle * adjFreq * sideStepFootPitchModifier + + motion.curAnim.joints[3].pitchPhase + 180)); + } + + yawOsc = Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[3].yawPhase)); + + pitchOscLeft *= motion.curAnim.joints[3].pitch; + pitchOscRight *= motion.curAnim.joints[3].pitch; + + yawOsc *= motion.curAnim.joints[3].yaw; + + rollOsc = motion.curAnim.joints[3].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[3].rollPhase)); + + pitchOffset = motion.curAnim.joints[3].pitchOffset; + yawOffset = motion.curAnim.joints[3].yawOffset; + rollOffset = motion.curAnim.joints[3].rollOffset; } - if(playFootStepSounds) { - Overlays.editOverlay(footstepsBigButtonSelected, { visible: showButtons } ); - Overlays.editOverlay(footstepsBigButton, { visible: false } ); + + // apply foot rotations + MyAvatar.setJointData("LeftFoot", Quat.fromPitchYawRollDegrees( + pitchOscLeft + pitchOffset, + yawOsc - yawOffset, + rollOsc - rollOffset)); + + MyAvatar.setJointData("RightFoot", Quat.fromPitchYawRollDegrees( + pitchOscRight + pitchOffset, + yawOsc + yawOffset, + rollOsc + rollOffset)); + + // play footfall sound yet? To determine this, we take the differential of the + // foot's pitch curve to decide when the foot hits the ground. + if (state.currentState === state.WALKING || + state.currentState === state.SIDE_STEP || + state.currentState === state.EDIT_WALK_STYLES || + state.currentState === state.EDIT_WALK_TWEAKS || + state.currentState === state.EDIT_WALK_JOINTS || + state.currentState === state.EDIT_SIDESTEP_LEFT || + state.currentState === state.EDIT_SIDESTEP_RIGHT) { + + // find dy/dx by determining the cosine wave for the foot's pitch function. + var feetPitchDifferential = Math.cos(filter.degToRad((cycle * adjFreq) + motion.curAnim.joints[3].pitchPhase)); + var threshHold = 0.9; // sets the audio trigger point. with accuracy. + if (feetPitchDifferential < -threshHold && + motion.nextStep === LEFT && + motion.direction !== UP && + motion.direction !== DOWN) { + + playFootstep(LEFT); + motion.nextStep = RIGHT; + } else if (feetPitchDifferential > threshHold && + motion.nextStep === RIGHT && + motion.direction !== UP && + motion.direction !== DOWN) { + + playFootstep(RIGHT); + motion.nextStep = LEFT; + } + } + + // toes + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[4].pitch * + Math.sin(filter.degToRad((2 * motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[4].pitchPhase)); + + yawOsc = motion.curAnim.joints[4].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[4].yawPhase)); + + rollOsc = motion.curAnim.joints[4].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[4].rollPhase)); + + pitchOffset = motion.curAnim.joints[4].pitchOffset; + yawOffset = motion.curAnim.joints[4].yawOffset; + rollOffset = motion.curAnim.joints[4].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[4].pitch * + Math.sin(filter.degToRad((2 * motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[4].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[4].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[4].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[4].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[4].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[4].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[4].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[4].rollOffset; + + } else { + + pitchOsc = motion.curAnim.joints[4].pitch * + Math.sin(filter.degToRad((2 * cycle * adjFreq) + + motion.curAnim.joints[4].pitchPhase)); + + yawOsc = motion.curAnim.joints[4].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[4].yawPhase)); + + rollOsc = motion.curAnim.joints[4].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[4].rollPhase)); + + pitchOffset = motion.curAnim.joints[4].pitchOffset; + yawOffset = motion.curAnim.joints[4].yawOffset; + rollOffset = motion.curAnim.joints[4].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[4].pitch * + Math.sin(filter.degToRad((2 * motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[4].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[4].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[4].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[4].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[4].rollPhase)); + + pitchOffsetLast = motion.curTransition.lastAnim.joints[4].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[4].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[4].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast); + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + } else { - Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } ); - Overlays.editOverlay(footstepsBigButton, { visible: showButtons } ); - } -} -function minimiseDialog() { - if(minimised) { - setBackground(); - hidebuttonOverlays(); - setSliderThumbsVisible(false); - hideJointControls(); - showFrontPanelButtons(false); - Overlays.editOverlay(controlsMinimisedTab, { visible: true } ); - } else { - setInternalState(STANDING); // show all the controls again - Overlays.editOverlay(controlsMinimisedTab, { visible: false } ); - } -} -function setBackground(backgroundName) { - for(var i in backgroundOverlays) { - if(backgroundOverlays[i] === backgroundName) - Overlays.editOverlay(backgroundName, { visible: true } ); - else Overlays.editOverlay(backgroundOverlays[i], { visible: false } ); - } -} -function setButtonOverlayVisible(buttonOverlayName) { - for(var i in buttonOverlays) { - if(buttonOverlays[i] === buttonOverlayName) { - Overlays.editOverlay(buttonOverlayName, { visible: true } ); - } - } -} -// top row menu type buttons (smaller) -function hidebuttonOverlays() { - for(var i in buttonOverlays) { - Overlays.editOverlay(buttonOverlays[i], { visible: false } ); - } -} -function hideJointControls() { - for(var i in jointsControlOverlays) { - Overlays.editOverlay(jointsControlOverlays[i], { visible: false } ); - } -} -function setSliderThumbsVisible(thumbsVisible) { - for(var i = 0 ; i < sliderThumbOverlays.length ; i++) { - Overlays.editOverlay(sliderThumbOverlays[i], { visible: thumbsVisible } ); - } -} -function initialiseJointsEditingPanel(propertyIndex) { + pitchOsc = motion.curAnim.joints[4].pitch * + Math.sin(filter.degToRad((2 * cycle * adjFreq) + + motion.curAnim.joints[4].pitchPhase)); - selectedJointIndex = propertyIndex; + yawOsc = motion.curAnim.joints[4].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[4].yawPhase)); - // set the image for the selected joint on the character control - hideJointControls(); - switch (selectedJointIndex) { - case 0: - Overlays.editOverlay(hipsJointControl, { visible: true }); - break; - case 1: - Overlays.editOverlay(upperLegsJointControl, { visible: true }); - break; - case 2: - Overlays.editOverlay(lowerLegsJointControl, { visible: true }); - break; - case 3: - Overlays.editOverlay(feetJointControl, { visible: true }); - break; - case 4: - Overlays.editOverlay(toesJointControl, { visible: true }); - break; - case 5: - Overlays.editOverlay(spineJointControl, { visible: true }); - break; - case 6: - Overlays.editOverlay(spine1JointControl, { visible: true }); - break; - case 7: - Overlays.editOverlay(spine2JointControl, { visible: true }); - break; - case 8: - Overlays.editOverlay(shouldersJointControl, { visible: true }); - break; - case 9: - Overlays.editOverlay(upperArmsJointControl, { visible: true }); - break; - case 10: - Overlays.editOverlay(forearmsJointControl, { visible: true }); - break; - case 11: - Overlays.editOverlay(handsJointControl, { visible: true }); - break; - case 12: - Overlays.editOverlay(headJointControl, { visible: true }); - break; - } + rollOsc = motion.curAnim.joints[4].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[4].rollPhase)); - // set sliders to adjust individual joint properties - var i = 0; - var yLocation = backgroundY+359; - - // pitch your role - var sliderXPos = currentAnimation.joints[selectedJointIndex].pitch - / sliderRanges.joints[selectedJointIndex].pitchRange * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = currentAnimation.joints[selectedJointIndex].yaw - / sliderRanges.joints[selectedJointIndex].yawRange * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = currentAnimation.joints[selectedJointIndex].roll - / sliderRanges.joints[selectedJointIndex].rollRange * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - - // set phases (full range, -180 to 180) - sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].pitchPhase/2)/180 * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].yawPhase/2)/180 * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = (90 + currentAnimation.joints[selectedJointIndex].rollPhase/2)/180 * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - - // offset ranges are also -ve thr' zero to +ve, so have to offset - sliderXPos = (((sliderRanges.joints[selectedJointIndex].pitchOffsetRange+currentAnimation.joints[selectedJointIndex].pitchOffset)/2) - /sliderRanges.joints[selectedJointIndex].pitchOffsetRange) * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = (((sliderRanges.joints[selectedJointIndex].yawOffsetRange+currentAnimation.joints[selectedJointIndex].yawOffset)/2) - /sliderRanges.joints[selectedJointIndex].yawOffsetRange) * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); - sliderXPos = (((sliderRanges.joints[selectedJointIndex].rollOffsetRange+currentAnimation.joints[selectedJointIndex].rollOffset)/2) - /sliderRanges.joints[selectedJointIndex].rollOffsetRange) * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=30, visible: true }); -} - -function initialiseWalkTweaks() { - - // set sliders to adjust walk properties - var i = 0; - var yLocation = backgroundY+71; - - var sliderXPos = currentAnimation.settings.baseFrequency / MAX_WALK_SPEED * sliderRangeX; // walk speed - Overlays.editOverlay(sliderThumbOverlays[i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = 0 * sliderRangeX; // start flying speed - depricated - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = currentAnimation.joints[0].sway / sliderRanges.joints[0].swayRange * sliderRangeX; // Hips sway - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = currentAnimation.joints[0].bob / sliderRanges.joints[0].bobRange * sliderRangeX; // Hips bob - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = currentAnimation.joints[0].thrust / sliderRanges.joints[0].thrustRange * sliderRangeX; // Hips thrust - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = (((sliderRanges.joints[1].rollOffsetRange+currentAnimation.joints[1].rollOffset)/2) // legs separation - is upper legs roll offset - / sliderRanges.joints[1].rollOffsetRange) * sliderRangeX; - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = currentAnimation.joints[1].pitch / sliderRanges.joints[1].pitchRange * sliderRangeX; // stride - is upper legs pitch - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = currentAnimation.joints[9].yaw / sliderRanges.joints[9].yawRange * sliderRangeX; // arms swing - is upper arms yaw - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); - sliderXPos = (((sliderRanges.joints[9].pitchOffsetRange-currentAnimation.joints[9].pitchOffset)/2) - / sliderRanges.joints[9].pitchOffsetRange) * sliderRangeX; // arms out - is upper arms pitch offset - Overlays.editOverlay(sliderThumbOverlays[++i], { x: minSliderX + sliderXPos, y: yLocation+=60, visible: true }); -} - -function showWalkStyleButtons(showButtons) { - - // set all big buttons to hidden, but skip the first 8, as are for the front panel - for(var i = 8 ; i < bigButtonOverlays.length ; i++) { - Overlays.editOverlay(bigButtonOverlays[i], { visible: false }); + pitchOffset = motion.curAnim.joints[4].pitchOffset; + yawOffset = motion.curAnim.joints[4].yawOffset; + rollOffset = motion.curAnim.joints[4].rollOffset; } - if(!showButtons) return; + // apply toe rotations + MyAvatar.setJointData("RightToeBase", Quat.fromPitchYawRollDegrees( + pitchOsc + pitchOffset, + yawOsc + yawOffset, + rollOsc + rollOffset)); - // set all the non-selected ones to showing - for(var i = 8 ; i < bigButtonOverlays.length ; i+=2) { - Overlays.editOverlay(bigButtonOverlays[i], { visible: showButtons }); + MyAvatar.setJointData("LeftToeBase", Quat.fromPitchYawRollDegrees( + pitchOsc + pitchOffset, + yawOsc - yawOffset, + rollOsc - rollOffset)); + + // spine + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[5].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2) + + motion.curAnim.joints[5].pitchPhase)) + + motion.curAnim.joints[5].pitchOffset; + + yawOsc = motion.curAnim.joints[5].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[5].yawPhase)) + + motion.curAnim + .joints[5].yawOffset; + + rollOsc = motion.curAnim.joints[5].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[5].rollPhase)) + + motion.curAnim.joints[5].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[5].pitch * + Math.sin(filter.degToRad((motion.walkWheelPos * 2) + + motion.curTransition.lastAnim.joints[5].pitchPhase)) + + motion.curTransition.lastAnim.joints[5].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[5].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[5].yawPhase)) + + motion.curTransition.lastAnim.joints[5].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[5].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[5].rollPhase)) + + motion.curTransition.lastAnim.joints[5].rollOffset; + } else { + + pitchOsc = motion.curAnim.joints[5].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[5].pitchPhase)) + + motion.curAnim.joints[5].pitchOffset; + + yawOsc = motion.curAnim.joints[5].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[5].yawPhase)) + + motion.curAnim.joints[5].yawOffset; + + rollOsc = motion.curAnim.joints[5].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[5].rollPhase)) + + motion.curAnim.joints[5].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[5].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2) + + motion.curTransition.lastAnim.joints[5].pitchPhase)) + + motion.curTransition.lastAnim.joints[5].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[5].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[5].yawPhase)) + + motion.curTransition.lastAnim.joints[5].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[5].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[5].rollPhase)) + + motion.curTransition.lastAnim.joints[5].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + } else { + + pitchOsc = motion.curAnim.joints[5].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[5].pitchPhase)) + + motion.curAnim.joints[5].pitchOffset; + + yawOsc = motion.curAnim.joints[5].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[5].yawPhase)) + + motion.curAnim.joints[5].yawOffset; + + rollOsc = motion.curAnim.joints[5].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[5].rollPhase)) + + motion.curAnim.joints[5].rollOffset; } - // set the currently selected one - if(selectedWalk === femaleStrutWalk || selectedWalk === maleStrutWalk) { - Overlays.editOverlay(strutWalkBigButtonSelected, { visible: showButtons }); - Overlays.editOverlay(strutWalkBigButton, {visible: false}); + // apply spine joint rotations + MyAvatar.setJointData("Spine", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc)); + + // spine 1 + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[6].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2) + + motion.curAnim.joints[6].pitchPhase)) + + motion.curAnim.joints[6].pitchOffset; + + yawOsc = motion.curAnim.joints[6].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[6].yawPhase)) + + motion.curAnim.joints[6].yawOffset; + + rollOsc = motion.curAnim.joints[6].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[6].rollPhase)) + + motion.curAnim.joints[6].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[6].pitch * + Math.sin(filter.degToRad((motion.walkWheelPos * 2) + + motion.curTransition.lastAnim.joints[6].pitchPhase)) + + motion.curTransition.lastAnim.joints[6].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[6].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[6].yawPhase)) + + motion.curTransition.lastAnim.joints[6].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[6].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[6].rollPhase)) + + motion.curTransition.lastAnim.joints[6].rollOffset; + + } else { + + pitchOsc = motion.curAnim.joints[6].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[6].pitchPhase)) + + motion.curAnim.joints[6].pitchOffset; + + yawOsc = motion.curAnim.joints[6].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[6].yawPhase)) + + motion.curAnim.joints[6].yawOffset; + + rollOsc = motion.curAnim.joints[6].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[6].rollPhase)) + + motion.curAnim.joints[6].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[6].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2) + + motion.curTransition.lastAnim.joints[6].pitchPhase)) + + motion.curTransition.lastAnim.joints[6].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[6].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[6].yawPhase)) + + motion.curTransition.lastAnim.joints[6].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[6].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[6].rollPhase)) + + motion.curTransition.lastAnim.joints[6].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + } else { + + pitchOsc = motion.curAnim.joints[6].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[6].pitchPhase)) + + motion.curAnim.joints[6].pitchOffset; + + yawOsc = motion.curAnim.joints[6].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[6].yawPhase)) + + motion.curAnim.joints[6].yawOffset; + + rollOsc = motion.curAnim.joints[6].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[6].rollPhase)) + + motion.curAnim.joints[6].rollOffset; } + + // apply spine1 joint rotations + MyAvatar.setJointData("Spine1", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc)); + + // spine 2 + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[7].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2 + + motion.curAnim.joints[7].pitchPhase)) + + motion.curAnim.joints[7].pitchOffset; + + yawOsc = motion.curAnim.joints[7].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[7].yawPhase)) + + motion.curAnim.joints[7].yawOffset; + + rollOsc = motion.curAnim.joints[7].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[7].rollPhase)) + + motion.curAnim.joints[7].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[7].pitch * + Math.sin(filter.degToRad(motion.walkWheelPos * 2 + + motion.curTransition.lastAnim.joints[7].pitchPhase)) + + motion.curTransition.lastAnim.joints[7].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[7].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[7].yawPhase)) + + motion.curTransition.lastAnim.joints[7].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[7].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[7].rollPhase)) + + motion.curTransition.lastAnim.joints[7].rollOffset; + } else { + + pitchOsc = motion.curAnim.joints[7].pitch * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + + motion.curAnim.joints[7].pitchPhase)) + + motion.curAnim.joints[7].pitchOffset; + + yawOsc = motion.curAnim.joints[7].yaw * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[7].yawPhase)) + + motion.curAnim.joints[7].yawOffset; + + rollOsc = motion.curAnim.joints[7].roll * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[7].rollPhase)) + + motion.curAnim.joints[7].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[7].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * 2 * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[7].pitchPhase)) + + motion.curTransition.lastAnim.joints[7].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[7].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[7].yawPhase)) + + motion.curTransition.lastAnim.joints[7].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[7].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[7].rollPhase)) + + motion.curTransition.lastAnim.joints[7].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + } else { + + pitchOsc = motion.curAnim.joints[7].pitch * + Math.sin(filter.degToRad(cycle * adjFreq * 2 + + motion.curAnim.joints[7].pitchPhase)) + + motion.curAnim.joints[7].pitchOffset; + + yawOsc = motion.curAnim.joints[7].yaw * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[7].yawPhase)) + + motion.curAnim.joints[7].yawOffset; + + rollOsc = motion.curAnim.joints[7].roll * + Math.sin(filter.degToRad(cycle * adjFreq + + motion.curAnim.joints[7].rollPhase)) + + motion.curAnim.joints[7].rollOffset; + } + + // apply spine2 joint rotations + MyAvatar.setJointData("Spine2", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc)); + + if (!motion.armsFree) { + + // shoulders + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[8].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[8].pitchPhase)) + + motion.curAnim.joints[8].pitchOffset; + + yawOsc = motion.curAnim.joints[8].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[8].yawPhase)); + + rollOsc = motion.curAnim.joints[8].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curAnim.calibration.frequency + + motion.curAnim.joints[8].rollPhase)) + + motion.curAnim.joints[8].rollOffset; + + yawOffset = motion.curAnim.joints[8].yawOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[8].pitch * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[8].pitchPhase)) + + motion.curTransition.lastAnim.joints[8].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[8].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[8].yawPhase)) + + rollOscLast = motion.curTransition.lastAnim.joints[8].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[8].rollPhase)) + + motion.curTransition.lastAnim.joints[8].rollOffset; + + yawOffsetLast = motion.curTransition.lastAnim.joints[8].yawOffset; + + } else { + + pitchOsc = motion.curAnim.joints[8].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].pitchPhase)) + + motion.curAnim.joints[8].pitchOffset; + + yawOsc = motion.curAnim.joints[8].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].yawPhase)); + + rollOsc = motion.curAnim.joints[8].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].rollPhase)) + + motion.curAnim.joints[8].rollOffset; + + yawOffset = motion.curAnim.joints[8].yawOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[8].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[8].pitchPhase)) + + motion.curTransition.lastAnim.joints[8].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[8].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[8].yawPhase)) + + rollOscLast = motion.curTransition.lastAnim.joints[8].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[8].rollPhase)) + + motion.curTransition.lastAnim.joints[8].rollOffset; + + yawOffsetLast = motion.curTransition.lastAnim.joints[8].yawOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + + } else { + + pitchOsc = motion.curAnim.joints[8].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].pitchPhase)) + + motion.curAnim.joints[8].pitchOffset; + + yawOsc = motion.curAnim.joints[8].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].yawPhase)); + + rollOsc = motion.curAnim.joints[8].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[8].rollPhase)) + + motion.curAnim.joints[8].rollOffset; + + yawOffset = motion.curAnim.joints[8].yawOffset; + } + + MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc + yawOffset, rollOsc)); + MyAvatar.setJointData("LeftShoulder", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc - yawOffset, -rollOsc)); + + // upper arms + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[9].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[9].pitchPhase)); + + yawOsc = motion.curAnim.joints[9].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[9].yawPhase)); + + rollOsc = motion.curAnim.joints[9].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2) + + motion.curAnim.joints[9].rollPhase)) + + motion.curAnim.joints[9].rollOffset; + + pitchOffset = motion.curAnim.joints[9].pitchOffset; + yawOffset = motion.curAnim.joints[9].yawOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[9].pitch * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[9].pitchPhase)); + + yawOscLast = motion.curTransition.lastAnim.joints[9].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[9].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[9].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[9].rollPhase)) + + motion.curTransition.lastAnim.joints[9].rollOffset; + + pitchOffsetLast = motion.curTransition.lastAnim.joints[9].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[9].yawOffset; + + } else { + + pitchOsc = motion.curAnim.joints[9].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[9].pitchPhase)); + + yawOsc = motion.curAnim.joints[9].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[9].yawPhase)); + + rollOsc = motion.curAnim.joints[9].roll * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[9].rollPhase)) + + motion.curAnim.joints[9].rollOffset; + + pitchOffset = motion.curAnim.joints[9].pitchOffset; + yawOffset = motion.curAnim.joints[9].yawOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[9].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[9].pitchPhase)) + + yawOscLast = motion.curTransition.lastAnim.joints[9].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[9].yawPhase)) + + rollOscLast = motion.curTransition.lastAnim.joints[9].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[9].rollPhase)) + + motion.curTransition.lastAnim.joints[9].rollOffset; + + pitchOffsetLast = motion.curTransition.lastAnim.joints[9].pitchOffset; + yawOffsetLast = motion.curTransition.lastAnim.joints[9].yawOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + pitchOffset = (transProgress * pitchOffset) + ((1 - transProgress) * pitchOffsetLast); + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + + } else { + + pitchOsc = motion.curAnim.joints[9].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[9].pitchPhase)); + + yawOsc = motion.curAnim.joints[9].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[9].yawPhase)); + + rollOsc = motion.curAnim.joints[9].roll * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[9].rollPhase)) + + motion.curAnim.joints[9].rollOffset; + + pitchOffset = motion.curAnim.joints[9].pitchOffset; + yawOffset = motion.curAnim.joints[9].yawOffset; + + } + + MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( + (-1 * flyingModifier) * pitchOsc + pitchOffset, + yawOsc - yawOffset, + rollOsc)); + + MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees( + pitchOsc + pitchOffset, + yawOsc + yawOffset, + -rollOsc)); + + // forearms + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[10].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[10].pitchPhase)) + + motion.curAnim.joints[10].pitchOffset; + + yawOsc = motion.curAnim.joints[10].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[10].yawPhase)); + + rollOsc = motion.curAnim.joints[10].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[10].rollPhase)); + + yawOffset = motion.curAnim.joints[10].yawOffset; + rollOffset = motion.curAnim.joints[10].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[10].pitch * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[10].pitchPhase)) + + motion.curTransition.lastAnim.joints[10].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[10].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[10].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[10].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[10].rollPhase)); + + yawOffsetLast = motion.curTransition.lastAnim.joints[10].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[10].rollOffset; + + } else { + + pitchOsc = motion.curAnim.joints[10].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].pitchPhase)) + + motion.curAnim.joints[10].pitchOffset; + + yawOsc = motion.curAnim.joints[10].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].yawPhase)); + + rollOsc = motion.curAnim.joints[10].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].rollPhase)); + + yawOffset = motion.curAnim.joints[10].yawOffset; + rollOffset = motion.curAnim.joints[10].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[10].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[10].pitchPhase)) + + motion.curTransition.lastAnim.joints[10].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[10].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[10].yawPhase)); + + rollOscLast = motion.curTransition.lastAnim.joints[10].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[10].rollPhase)); + + yawOffsetLast = motion.curTransition.lastAnim.joints[10].yawOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[10].rollOffset; + } + + // blend the animations + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = -(transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + yawOffset = (transProgress * yawOffset) + ((1 - transProgress) * yawOffsetLast); + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + + } else { + + pitchOsc = motion.curAnim.joints[10].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].pitchPhase)) + + motion.curAnim.joints[10].pitchOffset; + + yawOsc = motion.curAnim.joints[10].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].yawPhase)); + + rollOsc = motion.curAnim.joints[10].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[10].rollPhase)); + + yawOffset = motion.curAnim.joints[10].yawOffset; + rollOffset = motion.curAnim.joints[10].rollOffset; + } + + // apply forearms rotations + MyAvatar.setJointData("RightForeArm", + Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc + yawOffset, rollOsc + rollOffset)); + MyAvatar.setJointData("LeftForeArm", + Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc - yawOffset, rollOsc - rollOffset)); + + // hands + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = motion.curAnim.joints[11].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[11].pitchPhase)) + + motion.curAnim.joints[11].pitchOffset; + + yawOsc = motion.curAnim.joints[11].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[11].yawPhase)) + + motion.curAnim.joints[11].yawOffset; + + rollOsc = motion.curAnim.joints[11].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[11].rollPhase)); + + pitchOscLast = motion.curTransition.lastAnim.joints[11].pitch * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[11].pitchPhase)) + + motion.curTransition.lastAnim.joints[11].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[11].yaw * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[11].yawPhase)) + + motion.curTransition.lastAnim.joints[11].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[11].roll * + Math.sin(filter.degToRad(motion.walkWheelPos + + motion.curTransition.lastAnim.joints[11].rollPhase)) + + rollOffset = motion.curAnim.joints[11].rollOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[11].rollOffset; + + } else { + + pitchOsc = motion.curAnim.joints[11].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].pitchPhase)) + + motion.curAnim.joints[11].pitchOffset; + + yawOsc = motion.curAnim.joints[11].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].yawPhase)) + + motion.curAnim.joints[11].yawOffset; + + rollOsc = motion.curAnim.joints[11].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].rollPhase)); + + rollOffset = motion.curAnim.joints[11].rollOffset; + + pitchOscLast = motion.curTransition.lastAnim.joints[11].pitch * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[11].pitchPhase)) + + motion.curTransition.lastAnim.joints[11].pitchOffset; + + yawOscLast = motion.curTransition.lastAnim.joints[11].yaw * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[11].yawPhase)) + + motion.curTransition.lastAnim.joints[11].yawOffset; + + rollOscLast = motion.curTransition.lastAnim.joints[11].roll * + Math.sin(filter.degToRad(motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency + + motion.curTransition.lastAnim.joints[11].rollPhase)) + + rollOffset = motion.curAnim.joints[11].rollOffset; + rollOffsetLast = motion.curTransition.lastAnim.joints[11].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + rollOffset = (transProgress * rollOffset) + ((1 - transProgress) * rollOffsetLast); + + } else { + + pitchOsc = motion.curAnim.joints[11].pitch * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].pitchPhase)) + + motion.curAnim.joints[11].pitchOffset; + + yawOsc = motion.curAnim.joints[11].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].yawPhase)) + + motion.curAnim.joints[11].yawOffset; + + rollOsc = motion.curAnim.joints[11].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[11].rollPhase)); + + rollOffset = motion.curAnim.joints[11].rollOffset; + } + + // set the hand rotations + MyAvatar.setJointData("RightHand", + Quat.fromPitchYawRollDegrees(sideStepHandPitchSign * pitchOsc, yawOsc, rollOsc + rollOffset)); + MyAvatar.setJointData("LeftHand", + Quat.fromPitchYawRollDegrees(pitchOsc, -yawOsc, rollOsc - rollOffset)); + + } // end if (!motion.armsFree) + + // head and neck + if (motion.curTransition !== nullTransition) { + + if (motion.curTransition.walkingAtStart) { + + pitchOsc = 0.5 * motion.curAnim.joints[12].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency * 2) + + motion.curAnim.joints[12].pitchPhase)) + + motion.curAnim.joints[12].pitchOffset; + + yawOsc = 0.5 * motion.curAnim.joints[12].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[12].yawPhase)) + + motion.curAnim.joints[12].yawOffset; + + rollOsc = 0.5 * motion.curAnim.joints[12].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curAnim.calibration.frequency) + + motion.curAnim.joints[12].rollPhase)) + + motion.curAnim.joints[12].rollOffset; + + pitchOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].pitch * + Math.sin(filter.degToRad((motion.walkWheelPos * 2) + + motion.curTransition.lastAnim.joints[12].pitchPhase)) + + motion.curTransition.lastAnim.joints[12].pitchOffset; + + yawOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].yaw * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[12].yawPhase)) + + motion.curTransition.lastAnim.joints[12].yawOffset; + + rollOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].roll * + Math.sin(filter.degToRad((motion.walkWheelPos) + + motion.curTransition.lastAnim.joints[12].rollPhase)) + + motion.curTransition.lastAnim.joints[12].rollOffset; + + } else { + + pitchOsc = 0.5 * motion.curAnim.joints[12].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[12].pitchPhase)) + + motion.curAnim.joints[12].pitchOffset; + + yawOsc = 0.5 * motion.curAnim.joints[12].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[12].yawPhase)) + + motion.curAnim.joints[12].yawOffset; + + rollOsc = 0.5 * motion.curAnim.joints[12].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[12].rollPhase)) + + motion.curAnim.joints[12].rollOffset; + + pitchOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].pitch * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency * 2) + + motion.curTransition.lastAnim.joints[12].pitchPhase)) + + motion.curTransition.lastAnim.joints[12].pitchOffset; + + yawOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].yaw * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[12].yawPhase)) + + motion.curTransition.lastAnim.joints[12].yawOffset; + + rollOscLast = 0.5 * motion.curTransition.lastAnim.joints[12].roll * + Math.sin(filter.degToRad((motion.cumulativeTime * + motion.curTransition.lastAnim.calibration.frequency) + + motion.curTransition.lastAnim.joints[12].rollPhase)) + + motion.curTransition.lastAnim.joints[12].rollOffset; + } + + pitchOsc = (transProgress * pitchOsc) + ((1 - transProgress) * pitchOscLast); + yawOsc = (transProgress * yawOsc) + ((1 - transProgress) * yawOscLast); + rollOsc = (transProgress * rollOsc) + ((1 - transProgress) * rollOscLast); + + } else { + + pitchOsc = 0.5 * motion.curAnim.joints[12].pitch * + Math.sin(filter.degToRad((cycle * adjFreq * 2) + + motion.curAnim.joints[12].pitchPhase)) + + motion.curAnim.joints[12].pitchOffset; + + yawOsc = 0.5 * motion.curAnim.joints[12].yaw * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[12].yawPhase)) + + motion.curAnim.joints[12].yawOffset; + + rollOsc = 0.5 * motion.curAnim.joints[12].roll * + Math.sin(filter.degToRad((cycle * adjFreq) + + motion.curAnim.joints[12].rollPhase)) + + motion.curAnim.joints[12].rollOffset; + } + + MyAvatar.setJointData("Head", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc)); + MyAvatar.setJointData("Neck", Quat.fromPitchYawRollDegrees(pitchOsc, yawOsc, rollOsc)); } -// mouse event handlers -var movingSliderOne = false; -var movingSliderTwo = false; -var movingSliderThree = false; -var movingSliderFour = false; -var movingSliderFive = false; -var movingSliderSix = false; -var movingSliderSeven = false; -var movingSliderEight = false; -var movingSliderNine = false; - -function mousePressEvent(event) { - - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - - // check for a character joint control click - switch (clickedOverlay) { - - case hideButton: - Overlays.editOverlay(hideButton, { visible: false } ); - Overlays.editOverlay(hideButtonSelected, { visible: true } ); - return; - - case backButton: - Overlays.editOverlay(backButton, { visible: false } ); - Overlays.editOverlay(backButtonSelected, { visible: true } ); - return; - - case controlsMinimisedTab: - // TODO: add visual user feedback for tab click - return; - - case footstepsBigButton: - playFootStepSounds = true; - Overlays.editOverlay(footstepsBigButtonSelected, { visible: true } ); - Overlays.editOverlay(footstepsBigButton, { visible: false } ); - return; - - case footstepsBigButtonSelected: - playFootStepSounds = false; - Overlays.editOverlay(footstepsBigButton, { visible: true } ); - Overlays.editOverlay(footstepsBigButtonSelected, { visible: false } ); - return; - - case femaleBigButton: - case maleBigButtonSelected: - avatarGender = FEMALE; - selectedWalk = femaleStrutWalk; - selectedStand = femaleStandOne; - selectedFlyUp = femaleFlyingUp; - selectedFly = femaleFlying; - selectedFlyDown = femaleFlyingDown; - selectedSideStepLeft = femaleSideStepLeft; - selectedSideStepRight = femaleSideStepRight; - Overlays.editOverlay(femaleBigButtonSelected, { visible: true } ); - Overlays.editOverlay(femaleBigButton, { visible: false } ); - Overlays.editOverlay(maleBigButton, { visible: true } ); - Overlays.editOverlay(maleBigButtonSelected, { visible: false } ); - return; - - case armsFreeBigButton: - armsFree = true; - Overlays.editOverlay(armsFreeBigButtonSelected, { visible: true } ); - Overlays.editOverlay(armsFreeBigButton, { visible: false } ); - return; - - case armsFreeBigButtonSelected: - armsFree = false; - Overlays.editOverlay(armsFreeBigButtonSelected, { visible: false } ); - Overlays.editOverlay(armsFreeBigButton, { visible: true } ); - return; - - case maleBigButton: - case femaleBigButtonSelected: - avatarGender = MALE; - selectedWalk = maleStrutWalk; - selectedStand = maleStandOne; - selectedFlyUp = maleFlyingUp; - selectedFly = maleFlying; - selectedFlyDown = maleFlyingDown; - selectedSideStepLeft = maleSideStepLeft; - selectedSideStepRight = maleSideStepRight; - Overlays.editOverlay(femaleBigButton, { visible: true } ); - Overlays.editOverlay(femaleBigButtonSelected, { visible: false } ); - Overlays.editOverlay(maleBigButtonSelected, { visible: true } ); - Overlays.editOverlay(maleBigButton, { visible: false } ); - return; - - - case strutWalkBigButton: - if(avatarGender===FEMALE) selectedWalk = femaleStrutWalk; - else selectedWalk = maleStrutWalk; - currentAnimation = selectedWalk; - showWalkStyleButtons(true); - return; - - case strutWalkBigButtonSelected: - - // toggle forwards / backwards walk display - if(principleDirection===DIRECTION_FORWARDS) { - principleDirection=DIRECTION_BACKWARDS; - } else principleDirection=DIRECTION_FORWARDS; - return; - - case sliderOne: - movingSliderOne = true; - return; - - case sliderTwo: - movingSliderTwo = true; - return; - - case sliderThree: - movingSliderThree = true; - return; - - case sliderFour: - movingSliderFour = true; - return; - - case sliderFive: - movingSliderFive = true; - return; - - case sliderSix: - movingSliderSix = true; - return; - - case sliderSeven: - movingSliderSeven = true; - return; - - case sliderEight: - movingSliderEight = true; - return; - - case sliderNine: - movingSliderNine = true; - return; - } - - if( INTERNAL_STATE===CONFIG_WALK_JOINTS || - INTERNAL_STATE===CONFIG_STANDING || - INTERNAL_STATE===CONFIG_FLYING || - INTERNAL_STATE===CONFIG_FLYING_UP || - INTERNAL_STATE===CONFIG_FLYING_DOWN || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) { - - // check for new joint selection and update display accordingly - var clickX = event.x - backgroundX - 75; - var clickY = event.y - backgroundY - 92; - - if(clickX>60&&clickX<120&&clickY>123&&clickY<155) { - initialiseJointsEditingPanel(0); - return; - } - else if(clickX>63&&clickX<132&&clickY>156&&clickY<202) { - initialiseJointsEditingPanel(1); - return; - } - else if(clickX>58&&clickX<137&&clickY>203&&clickY<250) { - initialiseJointsEditingPanel(2); - return; - } - else if(clickX>58&&clickX<137&&clickY>250&&clickY<265) { - initialiseJointsEditingPanel(3); - return; - } - else if(clickX>58&&clickX<137&&clickY>265&&clickY<280) { - initialiseJointsEditingPanel(4); - return; - } - else if(clickX>78&&clickX<121&&clickY>111&&clickY<128) { - initialiseJointsEditingPanel(5); - return; - } - else if(clickX>78&&clickX<128&&clickY>89&&clickY<111) { - initialiseJointsEditingPanel(6); - return; - } - else if(clickX>85&&clickX<118&&clickY>77&&clickY<94) { - initialiseJointsEditingPanel(7); - return; - } - else if(clickX>64&&clickX<125&&clickY>55&&clickY<77) { - initialiseJointsEditingPanel(8); - return; - } - else if((clickX>44&&clickX<73&&clickY>71&&clickY<94) - ||(clickX>125&&clickX<144&&clickY>71&&clickY<94)) { - initialiseJointsEditingPanel(9); - return; - } - else if((clickX>28&&clickX<57&&clickY>94&&clickY<119) - ||(clickX>137&&clickX<170&&clickY>97&&clickY<114)) { - initialiseJointsEditingPanel(10); - return; - } - else if((clickX>18&&clickX<37&&clickY>115&&clickY<136) - ||(clickX>157&&clickX<182&&clickY>115&&clickY<136)) { - initialiseJointsEditingPanel(11); - return; - } - else if(clickX>81&&clickX<116&&clickY>12&&clickY<53) { - initialiseJointsEditingPanel(12); - return; - } - } -} -function mouseMoveEvent(event) { - - // only need deal with slider changes - if(powerOn) { - - if( INTERNAL_STATE===CONFIG_WALK_JOINTS || - INTERNAL_STATE===CONFIG_STANDING || - INTERNAL_STATE===CONFIG_FLYING || - INTERNAL_STATE===CONFIG_FLYING_UP || - INTERNAL_STATE===CONFIG_FLYING_DOWN || - INTERNAL_STATE===CONFIG_SIDESTEP_LEFT || - INTERNAL_STATE===CONFIG_SIDESTEP_RIGHT ) { - - var thumbClickOffsetX = event.x - minSliderX; - var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; - if(thumbPositionNormalised<0) thumbPositionNormalised = 0; - if(thumbPositionNormalised>1) thumbPositionNormalised = 1; - var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range - - if(movingSliderOne) { // currently selected joint pitch - Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} ); - currentAnimation.joints[selectedJointIndex].pitch = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].pitchRange; - } - else if(movingSliderTwo) { // currently selected joint yaw - Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} ); - currentAnimation.joints[selectedJointIndex].yaw = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].yawRange; - } - else if(movingSliderThree) { // currently selected joint roll - Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} ); - currentAnimation.joints[selectedJointIndex].roll = thumbPositionNormalised * sliderRanges.joints[selectedJointIndex].rollRange; - } - else if(movingSliderFour) { // currently selected joint pitch phase - Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} ); - var newPhase = 360 * thumbPositionNormalised - 180; - currentAnimation.joints[selectedJointIndex].pitchPhase = newPhase; - } - else if(movingSliderFive) { // currently selected joint yaw phase; - Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} ); - var newPhase = 360 * thumbPositionNormalised - 180; - currentAnimation.joints[selectedJointIndex].yawPhase = newPhase; - } - else if(movingSliderSix) { // currently selected joint roll phase - Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} ); - var newPhase = 360 * thumbPositionNormalised - 180; - currentAnimation.joints[selectedJointIndex].rollPhase = newPhase; - } - else if(movingSliderSeven) { // currently selected joint pitch offset - Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} ); // currently selected joint pitch offset - var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].pitchOffsetRange; - currentAnimation.joints[selectedJointIndex].pitchOffset = newOffset; - } - else if(movingSliderEight) { // currently selected joint yaw offset - Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} ); // currently selected joint yaw offset - var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].yawOffsetRange; - currentAnimation.joints[selectedJointIndex].yawOffset = newOffset; - } - else if(movingSliderNine) { // currently selected joint roll offset - Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} ); // currently selected joint roll offset - var newOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[selectedJointIndex].rollOffsetRange; - currentAnimation.joints[selectedJointIndex].rollOffset = newOffset; - } - } - else if(INTERNAL_STATE===CONFIG_WALK_TWEAKS) { - - var thumbClickOffsetX = event.x - minSliderX; - var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; - if(thumbPositionNormalised<0) thumbPositionNormalised = 0; - if(thumbPositionNormalised>1) thumbPositionNormalised = 1; - var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range - - if(movingSliderOne) { // walk speed - paused = true; // avoid nasty jittering - Overlays.editOverlay(sliderOne, { x: sliderX + minSliderX} ); - currentAnimation.settings.baseFrequency = thumbPositionNormalised * MAX_WALK_SPEED; - } - else if(movingSliderTwo) { // take flight speed - Overlays.editOverlay(sliderTwo, { x: sliderX + minSliderX} ); - //currentAnimation.settings.takeFlightVelocity = thumbPositionNormalised * 300; - } - else if(movingSliderThree) { // hips sway - Overlays.editOverlay(sliderThree, { x: sliderX + minSliderX} ); - currentAnimation.joints[0].sway = thumbPositionNormalised * sliderRanges.joints[0].swayRange; - } - else if(movingSliderFour) { // hips bob - Overlays.editOverlay(sliderFour, { x: sliderX + minSliderX} ); - currentAnimation.joints[0].bob = thumbPositionNormalised * sliderRanges.joints[0].bobRange; - } - else if(movingSliderFive) { // hips thrust - Overlays.editOverlay(sliderFive, { x: sliderX + minSliderX} ); - currentAnimation.joints[0].thrust = thumbPositionNormalised * sliderRanges.joints[0].thrustRange; - } - else if(movingSliderSix) { // legs separation - Overlays.editOverlay(sliderSix, { x: sliderX + minSliderX} ); - currentAnimation.joints[1].rollOffset = (thumbPositionNormalised-0.5) * 2 * sliderRanges.joints[1].rollOffsetRange; - } - else if(movingSliderSeven) { // stride - Overlays.editOverlay(sliderSeven, { x: sliderX + minSliderX} ); - currentAnimation.joints[1].pitch = thumbPositionNormalised * sliderRanges.joints[1].pitchRange; - } - else if(movingSliderEight) { // arms swing = upper arms yaw - Overlays.editOverlay(sliderEight, { x: sliderX + minSliderX} ); - currentAnimation.joints[9].yaw = thumbPositionNormalised * sliderRanges.joints[9].yawRange; - } - else if(movingSliderNine) { // arms out = upper arms pitch offset - Overlays.editOverlay(sliderNine, { x: sliderX + minSliderX} ); - currentAnimation.joints[9].pitchOffset = (thumbPositionNormalised-0.5) * -2 * sliderRanges.joints[9].pitchOffsetRange; - } - } - } -} -function mouseReleaseEvent(event) { - - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - - if(paused) paused = false; - - if(clickedOverlay === offButton) { - powerOn = true; - Overlays.editOverlay(offButton, { visible: false } ); - Overlays.editOverlay(onButton, { visible: true } ); - stand(); - } - else if(clickedOverlay === hideButton || clickedOverlay === hideButtonSelected){ - Overlays.editOverlay(hideButton, { visible: true } ); - Overlays.editOverlay(hideButtonSelected, { visible: false } ); - minimised = true; - minimiseDialog(); - } - else if(clickedOverlay === controlsMinimisedTab) { - minimised = false; - minimiseDialog(); - } - else if(powerOn) { - - if(movingSliderOne) movingSliderOne = false; - else if(movingSliderTwo) movingSliderTwo = false; - else if(movingSliderThree) movingSliderThree = false; - else if(movingSliderFour) movingSliderFour = false; - else if(movingSliderFive) movingSliderFive = false; - else if(movingSliderSix) movingSliderSix = false; - else if(movingSliderSeven) movingSliderSeven = false; - else if(movingSliderEight) movingSliderEight = false; - else if(movingSliderNine) movingSliderNine = false; - else { - - switch(clickedOverlay) { - - case configWalkButtonSelected: - case configStandButtonSelected: - case configSideStepLeftButtonSelected: - case configSideStepRightButtonSelected: - case configFlyingButtonSelected: - case configFlyingUpButtonSelected: - case configFlyingDownButtonSelected: - case configWalkStylesButtonSelected: - case configWalkTweaksButtonSelected: - case configWalkJointsButtonSelected: - setInternalState(STANDING); - break; - - case onButton: - powerOn = false; - setInternalState(STANDING); - Overlays.editOverlay(offButton, { visible: true } ); - Overlays.editOverlay(onButton, { visible: false } ); - resetJoints(); - break; - - case backButton: - case backButtonSelected: - Overlays.editOverlay(backButton, { visible: false } ); - Overlays.editOverlay(backButtonSelected, { visible: false } ); - setInternalState(STANDING); - break; - - case configWalkStylesButton: - setInternalState(CONFIG_WALK_STYLES); - break; - - case configWalkTweaksButton: - setInternalState(CONFIG_WALK_TWEAKS); - break; - - case configWalkJointsButton: - setInternalState(CONFIG_WALK_JOINTS); - break; - - case configWalkButton: - setInternalState(CONFIG_WALK_STYLES); // set the default walk adjustment panel here (i.e. first panel shown when Walk button clicked) - break; - - case configStandButton: - setInternalState(CONFIG_STANDING); - break; - - case configSideStepLeftButton: - setInternalState(CONFIG_SIDESTEP_LEFT); - break; - - case configSideStepRightButton: - setInternalState(CONFIG_SIDESTEP_RIGHT); - break; - - case configFlyingButton: - setInternalState(CONFIG_FLYING); - break; - - case configFlyingUpButton: - setInternalState(CONFIG_FLYING_UP); - break; - - case configFlyingDownButton: - setInternalState(CONFIG_FLYING_DOWN); - break; - } - } - } -} -Controller.mouseMoveEvent.connect(mouseMoveEvent); -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - -// Script ending -Script.scriptEnding.connect(function() { - - // remove the background overlays - for(var i in backgroundOverlays) { - Overlays.deleteOverlay(backgroundOverlays[i]); - } - // remove the button overlays - for(var i in buttonOverlays) { - Overlays.deleteOverlay(buttonOverlays[i]); - } - // remove the slider thumb overlays - for(var i in sliderThumbOverlays) { - Overlays.deleteOverlay(sliderThumbOverlays[i]); - } - // remove the character joint control overlays - for(var i in bigButtonOverlays) { - Overlays.deleteOverlay(jointsControlOverlays[i]); - } - // remove the big button overlays - for(var i in bigButtonOverlays) { - Overlays.deleteOverlay(bigButtonOverlays[i]); - } - // remove the mimimised tab - Overlays.deleteOverlay(controlsMinimisedTab); - - // remove the walk wheel overlays - Overlays.deleteOverlay(walkWheelYLine); - Overlays.deleteOverlay(walkWheelZLine); - Overlays.deleteOverlay(walkWheelStats); - - // remove the debug stats overlays - Overlays.deleteOverlay(debugStats); - Overlays.deleteOverlay(debugStatsPeriodic); -}); - -var sideStep = 0.002; // i.e. 2mm increments whilst sidestepping - JS movement keys don't work well :-( -function keyPressEvent(event) { - - if (event.text == "q") { - // export currentAnimation as json string when q key is pressed. reformat result at http://www.freeformatter.com/json-formatter.html - print('\n'); - print('walk.js dumping animation: '+currentAnimation.name+'\n'); - print('\n'); - print(JSON.stringify(currentAnimation), null, '\t'); - } - else if (event.text == "t") { - statsOn = !statsOn; - Overlays.editOverlay(debugStats, {visible: statsOn}); - Overlays.editOverlay(debugStatsPeriodic, {visible: statsOn}); - Overlays.editOverlay(walkWheelStats, {visible: statsOn}); - Overlays.editOverlay(walkWheelYLine, {visible: statsOn}); - Overlays.editOverlay(walkWheelZLine, {visible: statsOn}); - } -} -Controller.keyPressEvent.connect(keyPressEvent); - -// get the list of joint names -var jointList = MyAvatar.getJointNames(); - -// clear the joint data so can calculate hips to feet distance -for(var i = 0 ; i < 5 ; i++) { - MyAvatar.setJointData(i, Quat.fromPitchYawRollDegrees(0,0,0)); -} -// used to position the visual representation of the walkwheel only -var hipsToFeetDistance = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightFoot").y; - -// This script is designed around the Finite State Machine (FSM) model, so to start things up we just select the STANDING state. -setInternalState(STANDING); \ No newline at end of file +// Begin by setting an internal state +state.setInternalState(state.STANDING); \ No newline at end of file diff --git a/interface/resources/shaders/deferred_light_limited.vert b/interface/resources/shaders/deferred_light_limited.vert index 849d14bda4..f360f0307d 100644 --- a/interface/resources/shaders/deferred_light_limited.vert +++ b/interface/resources/shaders/deferred_light_limited.vert @@ -14,5 +14,6 @@ void main(void) { gl_Position = ftransform(); vec4 projected = gl_Position / gl_Position.w; - gl_TexCoord[0] = vec4(dot(projected, gl_ObjectPlaneS[3]), dot(projected, gl_ObjectPlaneT[3]), 0.0, 1.0); + gl_TexCoord[0] = vec4(dot(projected, gl_ObjectPlaneS[3]) * gl_Position.w, + dot(projected, gl_ObjectPlaneT[3]) * gl_Position.w, 0.0, gl_Position.w); } diff --git a/interface/resources/shaders/point_light.frag b/interface/resources/shaders/point_light.frag index b9ba638885..1d1b4f4073 100644 --- a/interface/resources/shaders/point_light.frag +++ b/interface/resources/shaders/point_light.frag @@ -40,16 +40,17 @@ uniform float radius; void main(void) { // get the depth and exit early if it doesn't pass the test - float depth = texture2D(depthMap, gl_TexCoord[0].st).r; + vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q; + float depth = texture2D(depthMap, texCoord).r; if (depth < gl_FragCoord.z) { discard; } // compute the view space position using the depth float z = near / (depth * depthScale - 1.0); - vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + vec4 position = vec4((depthTexCoordOffset + texCoord * depthTexCoordScale) * z, z, 1.0); // get the normal from the map - vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + vec4 normal = texture2D(normalMap, texCoord); vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); // compute the base color based on OpenGL lighting model @@ -58,7 +59,7 @@ void main(void) { lightVector = lightVector / lightDistance; float diffuse = dot(normalizedNormal, lightVector); float facingLight = step(0.0, diffuse); - vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightProduct[1].ambient + + vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient + gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); // compute attenuation based on distance, etc. @@ -69,6 +70,6 @@ void main(void) { // add base to specular, modulate by attenuation float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), normalizedNormal)); - vec4 specularColor = texture2D(specularMap, gl_TexCoord[0].st); + vec4 specularColor = texture2D(specularMap, texCoord); gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); } diff --git a/interface/resources/shaders/spot_light.frag b/interface/resources/shaders/spot_light.frag index a496abd7b2..590ed8b6ac 100644 --- a/interface/resources/shaders/spot_light.frag +++ b/interface/resources/shaders/spot_light.frag @@ -40,16 +40,17 @@ uniform float radius; void main(void) { // get the depth and exit early if it doesn't pass the test - float depth = texture2D(depthMap, gl_TexCoord[0].st).r; + vec2 texCoord = gl_TexCoord[0].st / gl_TexCoord[0].q; + float depth = texture2D(depthMap, texCoord).r; if (depth < gl_FragCoord.z) { discard; } // compute the view space position using the depth float z = near / (depth * depthScale - 1.0); - vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0); + vec4 position = vec4((depthTexCoordOffset + texCoord * depthTexCoordScale) * z, z, 1.0); // get the normal from the map - vec4 normal = texture2D(normalMap, gl_TexCoord[0].st); + vec4 normal = texture2D(normalMap, texCoord); vec4 normalizedNormal = normalize(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0)); // compute the base color based on OpenGL lighting model @@ -58,7 +59,7 @@ void main(void) { lightVector = lightVector / lightDistance; float diffuse = dot(normalizedNormal, lightVector); float facingLight = step(0.0, diffuse); - vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightProduct[1].ambient + + vec4 baseColor = texture2D(diffuseMap, texCoord) * (gl_FrontLightProduct[1].ambient + gl_FrontLightProduct[1].diffuse * (diffuse * facingLight)); // compute attenuation based on spot angle, distance, etc. @@ -71,6 +72,6 @@ void main(void) { // add base to specular, modulate by attenuation float specular = facingLight * max(0.0, dot(normalize(lightVector - normalize(vec4(position.xyz, 0.0))), normalizedNormal)); - vec4 specularColor = texture2D(specularMap, gl_TexCoord[0].st); + vec4 specularColor = texture2D(specularMap, texCoord); gl_FragColor = vec4((baseColor.rgb + pow(specular, specularColor.a * 128.0) * specularColor.rgb) * attenuation, 0.0); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index df13e26eae..5abb826f45 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ #include "renderer/ProgramObject.h" #include "gpu/Batch.h" +#include "gpu/GLBackend.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" @@ -107,7 +109,7 @@ static unsigned STARFIELD_SEED = 1; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored -const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB +const qint64 MAXIMUM_CACHE_SIZE = 10737418240; // 10GB static QTimer* idleTimer = NULL; @@ -187,7 +189,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), - _isVSyncOn(true) + _isVSyncOn(true), + _aboutToQuit(false) { // read the ApplicationInfo.ini file for Name/Version/Domain information @@ -257,6 +260,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); + connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); @@ -391,6 +395,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript); connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts())); + connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit())); // check first run... QVariant firstRunValue = _settings->value("firstRun",QVariant(true)); @@ -415,6 +420,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon->show(); + // set the local loopback interface for local sounds from audio scripts + AudioScriptingInterface::getInstance().setLocalLoopbackInterface(&_audio); + #ifdef HAVE_RTMIDI // setup the MIDIManager MIDIManager& midiManagerInstance = MIDIManager::getInstance(); @@ -424,6 +432,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : this->installEventFilter(this); } +void Application::aboutToQuit() { + _aboutToQuit = true; +} + Application::~Application() { qInstallMessageHandler(NULL); @@ -716,11 +728,11 @@ void Application::paintGL() { displaySide(*whichCamera); glPopMatrix(); - if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - renderRearViewMirror(_mirrorViewRect); - - } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true); + + } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + renderRearViewMirror(_mirrorViewRect); } _glowEffect.render(); @@ -778,7 +790,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) // Tell our viewFrustum about this change, using the application camera if (updateViewFrustum) { loadViewFrustum(camera, _viewFrustum); - computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum @@ -1256,7 +1268,9 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { showMouse = false; } - _entities.mouseMoveEvent(event, deviceID); + if (!_aboutToQuit) { + _entities.mouseMoveEvent(event, deviceID); + } _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1279,7 +1293,9 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { - _entities.mousePressEvent(event, deviceID); + if (!_aboutToQuit) { + _entities.mousePressEvent(event, deviceID); + } _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts @@ -1320,7 +1336,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { - _entities.mouseReleaseEvent(event, deviceID); + if (!_aboutToQuit) { + _entities.mouseReleaseEvent(event, deviceID); + } _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts @@ -1995,25 +2013,17 @@ void Application::init() { void Application::closeMirrorView() { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - Menu::getInstance()->triggerOption(MenuOption::Mirror);; + Menu::getInstance()->triggerOption(MenuOption::Mirror); } } void Application::restoreMirrorView() { - if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - Menu::getInstance()->triggerOption(MenuOption::Mirror);; - } - if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } } void Application::shrinkMirrorView() { - if (!Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - Menu::getInstance()->triggerOption(MenuOption::Mirror);; - } - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } @@ -3567,9 +3577,8 @@ void Application::changeDomainHostname(const QString &newDomainHostname) { } } -void Application::domainChanged(const QString& domainHostname) { - updateWindowTitle(); - +void Application::clearDomainOctreeDetails() { + qDebug() << "Clearing domain octree details..."; // reset the environment so that we don't erroneously end up with multiple _environment.resetToDefault(); @@ -3578,7 +3587,13 @@ void Application::domainChanged(const QString& domainHostname) { _voxelServerJurisdictions.clear(); _voxelServerJurisdictions.unlock(); + _entityServerJurisdictions.lockForWrite(); + _entityServerJurisdictions.clear(); + _entityServerJurisdictions.unlock(); + + _octreeSceneStatsLock.lockForWrite(); _octreeServerSceneStats.clear(); + _octreeSceneStatsLock.unlock(); // reset the model renderer _entities.clear(); @@ -3587,6 +3602,11 @@ void Application::domainChanged(const QString& domainHostname) { _voxels.killLocalVoxels(); } +void Application::domainChanged(const QString& domainHostname) { + updateWindowTitle(); + clearDomainOctreeDetails(); +} + void Application::connectedToDomain(const QString& hostname) { AccountManager& accountManager = AccountManager::getInstance(); const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID(); @@ -4065,18 +4085,18 @@ void Application::openUrl(const QUrl& url) { void Application::updateMyAvatarTransform() { bumpSettings(); #ifdef USE_BULLET_PHYSICS - const float SIMULATION_OFFSET_QUANTIZATION = 8.0f; // meters + const float SIMULATION_OFFSET_QUANTIZATION = 16.0f; // meters glm::vec3 avatarPosition = _myAvatar->getPosition(); glm::vec3 physicsWorldOffset = _physicsWorld.getOriginOffset(); - if (glm::distance(avatarPosition, physicsWorldOffset) > HALF_SIMULATION_EXTENT) { + if (glm::distance(avatarPosition, physicsWorldOffset) > SIMULATION_OFFSET_QUANTIZATION) { //_entityCollisionSystem.forgetAllPhysics(); glm::vec3 newOriginOffset = avatarPosition; - int halfExtent = (int)HALF_SIMULATION_EXENT; + int halfExtent = (int)HALF_SIMULATION_EXTENT; for (int i = 0; i < 3; ++i) { newOriginOffset[i] = (float)(glm::max(halfExtent, ((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION)); } - _physicsWorld.setOriginOffset(newOrigin); + _physicsWorld.setOriginOffset(newOriginOffset); //_entityCollisionSystem.rememberAllPhysics(); } #endif // USE_BULLET_PHYSICS @@ -4310,8 +4330,6 @@ bool Application::isVSyncOn() const { if (wglewGetExtension("WGL_EXT_swap_control")) { int swapInterval = wglGetSwapIntervalEXT(); return (swapInterval > 0); - } else { - return true; } #elif defined(Q_OS_LINUX) // TODO: write the poper code for linux @@ -4322,10 +4340,9 @@ bool Application::isVSyncOn() const { } else { return true; } - */ -#else - return true; + */ #endif + return true; } bool Application::isVSyncEditable() const { @@ -4340,7 +4357,6 @@ bool Application::isVSyncEditable() const { return true; } */ -#else #endif return false; } diff --git a/interface/src/Application.h b/interface/src/Application.h index e9361a577a..fb8518b794 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -307,6 +307,7 @@ public: unsigned int getRenderTargetFramerate() const; bool isVSyncOn() const; bool isVSyncEditable() const; + bool isAboutToQuit() const { return _aboutToQuit; } void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); @@ -379,8 +380,10 @@ public slots: void resetSensors(); private slots: + void clearDomainOctreeDetails(); void timer(); void idle(); + void aboutToQuit(); void connectedToDomain(const QString& hostname); @@ -631,6 +634,8 @@ private: quint64 _lastSendDownstreamAudioStats; bool _isVSyncOn; + + bool _aboutToQuit; }; #endif // hifi_Application_h diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 7d039387bb..96121d9a8c 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -77,6 +77,9 @@ Audio::Audio(QObject* parent) : _isStereoInput(false), _averagedLatency(0.0), _lastInputLoudness(0), + _inputFrameCounter(0), + _quietestFrame(std::numeric_limits::max()), + _loudestFrame(0.0f), _timeSinceLastClip(-1.0), _dcOffset(0), _noiseGateMeasuredFloor(0), @@ -94,6 +97,9 @@ Audio::Audio(QObject* parent) : _muted(false), _reverb(false), _reverbOptions(&_scriptReverbOptions), + _gverb(NULL), + _iconColor(1.0f), + _iconPulseTimeReference(usecTimestampNow()), _processSpatialAudio(false), _spatialAudioStart(0), _spatialAudioFinish(0), @@ -181,9 +187,9 @@ QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& de QAudioDeviceInfo::defaultOutputDevice(); #else foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { - qDebug() << audioDevice.deviceName() << " " << deviceName; if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { result = audioDevice; + break; } } #endif @@ -510,6 +516,33 @@ void Audio::initGverb() { gverb_set_taillevel(_gverb, DB_CO(_reverbOptions->getTailLevel())); } +void Audio::updateGverbOptions() { + bool reverbChanged = false; + if (_receivedAudioStream.hasReverb()) { + + if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) { + _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); + reverbChanged = true; + } + if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { + _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); + reverbChanged = true; + } + + if (_reverbOptions != &_zoneReverbOptions) { + _reverbOptions = &_zoneReverbOptions; + reverbChanged = true; + } + } else if (_reverbOptions != &_scriptReverbOptions) { + _reverbOptions = &_scriptReverbOptions; + reverbChanged = true; + } + + if (reverbChanged) { + initGverb(); + } +} + void Audio::setReverbOptions(const AudioEffectOptions* options) { // Save the new options _scriptReverbOptions.setMaxRoomSize(options->getMaxRoomSize()); @@ -541,14 +574,14 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF gverb_do(_gverb, value, &lValue, &rValue); // Mix, accounting for clipping, the left and right channels. Ignore the rest. - for (unsigned int j = sample; j < sample + audioFormat.channelCount(); j++) { + for (int j = sample; j < sample + audioFormat.channelCount(); j++) { if (j == sample) { // left channel - int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), -32768, 32767); + int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); samplesData[j] = (int16_t)lResult; } else if (j == (sample + 1)) { // right channel - int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), -32768, 32767); + int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); samplesData[j] = (int16_t)rResult; } else { // ignore channels above 2 @@ -557,6 +590,60 @@ void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioF } } +void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) { + bool hasEcho = Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio); + // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here. + bool hasLocalReverb = (_reverb || _receivedAudioStream.hasReverb()) && + !Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio); + if (_muted || !_audioOutput || (!hasEcho && !hasLocalReverb)) { + return; + } + + // if this person wants local loopback add that to the locally injected audio + // if there is reverb apply it to local audio and substract the origin samples + + if (!_loopbackOutputDevice && _loopbackAudioOutput) { + // we didn't have the loopback output device going so set that up now + _loopbackOutputDevice = _loopbackAudioOutput->start(); + } + + QByteArray loopBackByteArray(inputByteArray); + if (_inputFormat != _outputFormat) { + float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) * + (_outputFormat.channelCount() / _inputFormat.channelCount()); + loopBackByteArray.resize(inputByteArray.size() * loopbackOutputToInputRatio); + loopBackByteArray.fill(0); + linearResampling(reinterpret_cast(inputByteArray.data()), + reinterpret_cast(loopBackByteArray.data()), + inputByteArray.size() / sizeof(int16_t), loopBackByteArray.size() / sizeof(int16_t), + _inputFormat, _outputFormat); + } + + if (hasLocalReverb) { + QByteArray loopbackCopy; + if (!hasEcho) { + loopbackCopy = loopBackByteArray; + } + + int16_t* loopbackSamples = reinterpret_cast(loopBackByteArray.data()); + int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t); + updateGverbOptions(); + addReverb(loopbackSamples, numLoopbackSamples, _outputFormat); + + if (!hasEcho) { + int16_t* loopbackCopySamples = reinterpret_cast(loopbackCopy.data()); + for (int i = 0; i < numLoopbackSamples; ++i) { + loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i], + MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); + } + } + } + + if (_loopbackOutputDevice) { + _loopbackOutputDevice->write(loopBackByteArray); + } +} + void Audio::handleAudioInput() { static char audioDataPacket[MAX_PACKET_SIZE]; @@ -601,34 +688,8 @@ void Audio::handleAudioInput() { _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); } - - if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) { - // if this person wants local loopback add that to the locally injected audio - - if (!_loopbackOutputDevice && _loopbackAudioOutput) { - // we didn't have the loopback output device going so set that up now - _loopbackOutputDevice = _loopbackAudioOutput->start(); - } - - if (_inputFormat == _outputFormat) { - if (_loopbackOutputDevice) { - _loopbackOutputDevice->write(inputByteArray); - } - } else { - float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate()) - * (_outputFormat.channelCount() / _inputFormat.channelCount()); - - QByteArray loopBackByteArray(inputByteArray.size() * loopbackOutputToInputRatio, 0); - - linearResampling((int16_t*) inputByteArray.data(), (int16_t*) loopBackByteArray.data(), - inputByteArray.size() / sizeof(int16_t), - loopBackByteArray.size() / sizeof(int16_t), _inputFormat, _outputFormat); - - if (_loopbackOutputDevice) { - _loopbackOutputDevice->write(loopBackByteArray); - } - } - } + + handleLocalEchoAndReverb(inputByteArray); _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); @@ -717,6 +778,20 @@ void Audio::handleAudioInput() { } _lastInputLoudness = fabs(loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + + if (_quietestFrame > _lastInputLoudness) { + _quietestFrame = _lastInputLoudness; + } + if (_loudestFrame < _lastInputLoudness) { + _loudestFrame = _lastInputLoudness; + } + + const int FRAMES_FOR_NOISE_DETECTION = 400; + if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) { + _quietestFrame = std::numeric_limits::max(); + _loudestFrame = 0.0f; + _inputFrameCounter = 0; + } // If Noise Gate is enabled, check and turn the gate on and off if (!_audioSourceInjectEnabled && _noiseGateEnabled) { @@ -951,30 +1026,7 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou _desiredOutputFormat, _outputFormat); if(_reverb || _receivedAudioStream.hasReverb()) { - bool reverbChanged = false; - if (_receivedAudioStream.hasReverb()) { - - if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) { - _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); - reverbChanged = true; - } - if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) { - _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); - reverbChanged = true; - } - - if (_reverbOptions != &_zoneReverbOptions) { - _reverbOptions = &_zoneReverbOptions; - reverbChanged = true; - } - } else if (_reverbOptions != &_scriptReverbOptions) { - _reverbOptions = &_scriptReverbOptions; - reverbChanged = true; - } - - if (reverbChanged) { - initGverb(); - } + updateGverbOptions(); addReverb((int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat); } } @@ -1314,9 +1366,25 @@ void Audio::startDrumSound(float volume, float frequency, float duration, float _drumSoundSample = 0; } -void Audio::handleAudioByteArray(const QByteArray& audioByteArray) { - // TODO: either create a new audio device (up to the limit of the sound card or a hard limit) - // or send to the mixer and use delayed loopback +void Audio::handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& injectorOptions) { + if (audioByteArray.size() > 0) { + QAudioFormat localFormat = _outputFormat; + + if (!injectorOptions.isStereo()) { + localFormat.setChannelCount(1); + } + + QAudioOutput* localSoundOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), localFormat, this); + + QIODevice* localIODevice = localSoundOutput->start(); + if (localIODevice) { + localIODevice->write(audioByteArray); + } else { + qDebug() << "Unable to handle audio byte array. Error:" << localSoundOutput->error(); + } + } else { + qDebug() << "Audio::handleAudioByteArray called with an empty byte array. Sound is likely still downloading."; + } } void Audio::renderToolBox(int x, int y, bool boxed) { @@ -1360,23 +1428,37 @@ void Audio::renderToolBox(int x, int y, bool boxed) { _iconBounds = QRect(x, y, MUTE_ICON_SIZE, MUTE_ICON_SIZE); if (!_muted) { glBindTexture(GL_TEXTURE_2D, _micTextureId); + _iconColor = 1.0f; } else { glBindTexture(GL_TEXTURE_2D, _muteTextureId); + + // Make muted icon pulsate + static const float PULSE_MIN = 0.4f; + static const float PULSE_MAX = 1.0f; + static const float PULSE_FREQUENCY = 1.0f; // in Hz + qint64 now = usecTimestampNow(); + if (now - _iconPulseTimeReference > USECS_PER_SECOND) { + // Prevents t from getting too big, which would diminish glm::cos precision + _iconPulseTimeReference = now - ((now - _iconPulseTimeReference) % USECS_PER_SECOND); + } + float t = (float)(now - _iconPulseTimeReference) / (float)USECS_PER_SECOND; + float pulseFactor = (glm::cos(t * PULSE_FREQUENCY * 2.0f * PI) + 1.0f) / 2.0f; + _iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; } - glColor3f(1,1,1); + glColor3f(_iconColor, _iconColor, _iconColor); glBegin(GL_QUADS); - glTexCoord2f(1, 1); + glTexCoord2f(1.0f, 1.0f); glVertex2f(_iconBounds.left(), _iconBounds.top()); - glTexCoord2f(0, 1); + glTexCoord2f(0.0f, 1.0f); glVertex2f(_iconBounds.right(), _iconBounds.top()); - glTexCoord2f(0, 0); + glTexCoord2f(0.0f, 0.0f); glVertex2f(_iconBounds.right(), _iconBounds.bottom()); - glTexCoord2f(1, 0); + glTexCoord2f(1.0f, 0.0f); glVertex2f(_iconBounds.left(), _iconBounds.bottom()); glEnd(); @@ -1886,6 +1968,7 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { } } } + return supportedFormat; } @@ -1944,6 +2027,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) supportedFormat = true; } } + return supportedFormat; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index fcbfb12761..cc0da78198 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -40,12 +40,12 @@ #include #include -#include +#include #include "MixedProcessedAudioStream.h" #include "AudioEffectOptions.h" #include -#include +#include extern "C" { #include @@ -155,7 +155,7 @@ public slots: void selectAudioFilterBassCut(); void selectAudioFilterSmiley(); - virtual void handleAudioByteArray(const QByteArray& audioByteArray); + virtual void handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& options); void sendDownstreamAudioStatsPacket(); @@ -213,6 +213,9 @@ private: QElapsedTimer _timeSinceLastReceived; float _averagedLatency; float _lastInputLoudness; + int _inputFrameCounter; + float _quietestFrame; + float _loudestFrame; float _timeSinceLastClip; float _dcOffset; float _noiseGateMeasuredFloor; @@ -245,11 +248,13 @@ private: AudioEffectOptions _scriptReverbOptions; AudioEffectOptions _zoneReverbOptions; AudioEffectOptions* _reverbOptions; - ty_gverb *_gverb; + ty_gverb* _gverb; GLuint _micTextureId; GLuint _muteTextureId; GLuint _boxTextureId; QRect _iconBounds; + float _iconColor; + qint64 _iconPulseTimeReference; /// Audio callback in class context. inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight); @@ -267,8 +272,11 @@ private: // Adds Reverb void initGverb(); + void updateGverbOptions(); void addReverb(int16_t* samples, int numSamples, QAudioFormat& format); + void handleLocalEchoAndReverb(QByteArray& inputByteArray); + // Add sounds that we want the user to not hear themselves, by adding on top of mic input signal void addProceduralSounds(int16_t* monoInput, int numSamples); diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index acc00c14b0..119f5a31d0 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -52,12 +52,10 @@ Hair::Hair(int strands, glm::vec3 thisVertex; for (int strand = 0; strand < _strands; strand++) { float strandAngle = randFloat() * PI; - float azimuth; - float elevation = - (randFloat() * PI); - azimuth = PI_OVER_TWO; - if (randFloat() < 0.5f) { - azimuth *= -1.0f; - } + + float azimuth = (float)strand / (float)_strands * PI * 2.0f; + float elevation = 0.0f; + glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); thisStrand *= _radius; @@ -92,6 +90,17 @@ Hair::Hair(int strands, } } +Hair::~Hair() { + delete[] _hairPosition; + delete[] _hairOriginalPosition; + delete[] _hairLastPosition; + delete[] _hairQuadDelta; + delete[] _hairNormals; + delete[] _hairColors; + delete[] _hairIsMoveable; + delete[] _hairConstraints; +} + const float SOUND_THRESHOLD = 40.0f; void Hair::simulate(float deltaTime) { @@ -115,11 +124,22 @@ void Hair::simulate(float deltaTime) { glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; _hairPosition[vertexIndex] += diff * HAIR_DAMPING; + /* // Resolve collisions with sphere if (glm::length(_hairPosition[vertexIndex]) < _radius) { _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * (_radius - glm::length(_hairPosition[vertexIndex])); + } */ + + // Collide with a conical body descending from the root of the hair + glm::vec3 thisVertex = _hairPosition[vertexIndex]; + float depth = -thisVertex.y; + thisVertex.y = 0.0f; + const float BODY_CONE_ANGLE = 0.30; + if (glm::length(thisVertex) < depth * BODY_CONE_ANGLE) { + _hairPosition[vertexIndex] += glm::normalize(thisVertex) * (depth * BODY_CONE_ANGLE - glm::length(thisVertex)); } + // Add random thing driven by loudness float loudnessFactor = (_loudness > SOUND_THRESHOLD) ? logf(_loudness - SOUND_THRESHOLD) / 2000.0f : 0.0f; diff --git a/interface/src/Hair.h b/interface/src/Hair.h index 94378dfd6a..436c6b836a 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -24,10 +24,10 @@ const int HAIR_CONSTRAINTS = 2; const int DEFAULT_HAIR_STRANDS = 20; -const int DEFAULT_HAIR_LINKS = 10; +const int DEFAULT_HAIR_LINKS = 11; const float DEFAULT_HAIR_RADIUS = 0.075f; -const float DEFAULT_HAIR_LINK_LENGTH = 0.06f; -const float DEFAULT_HAIR_THICKNESS = 0.025f; +const float DEFAULT_HAIR_LINK_LENGTH = 0.1f; +const float DEFAULT_HAIR_THICKNESS = 0.07f; const glm::vec3 DEFAULT_GRAVITY(0.0f, -9.8f, 0.0f); class Hair { @@ -37,6 +37,7 @@ public: float radius = DEFAULT_HAIR_RADIUS, float linkLength = DEFAULT_HAIR_LINK_LENGTH, float hairThickness = DEFAULT_HAIR_THICKNESS); + ~Hair(); void simulate(float deltaTime); void render(); void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 21c1cded88..cd074805b6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -386,7 +386,7 @@ Menu::Menu() : #if defined(Q_OS_MAC) #else - QAction* vsyncAction = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, this, SLOT(changeVSync())); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, this, SLOT(changeVSync())); #endif } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5329ff5c1d..9ceb073167 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -49,7 +49,7 @@ const float PITCH_SPEED = 100.0f; // degrees/sec const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALE = 0.125f; -const float MAX_WALKING_SPEED = 4.5f; +const float MAX_WALKING_SPEED = 2.5f; // human walking speed const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // keyboard motor gets additive boost below this speed const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index e447c703fb..2f8ddb1095 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -61,11 +61,13 @@ EntityTreeRenderer::~EntityTreeRenderer() { void EntityTreeRenderer::clear() { OctreeRenderer::clear(); + _entityScripts.clear(); } void EntityTreeRenderer::init() { OctreeRenderer::init(); - static_cast(_tree)->setFBXService(this); + EntityTree* entityTree = static_cast(_tree); + entityTree->setFBXService(this); if (_wantScripts) { _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", @@ -77,6 +79,8 @@ void EntityTreeRenderer::init() { // first chance, we'll check for enter/leave entity events. glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition(); _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f); + + connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity); } QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) { @@ -196,6 +200,7 @@ void EntityTreeRenderer::update() { void EntityTreeRenderer::checkEnterLeaveEntities() { if (_tree) { + _tree->lockForRead(); glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float) TREE_SCALE; if (avatarPosition != _lastAvatarPosition) { @@ -240,6 +245,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { _currentEntitiesInside = entitiesContainingAvatar; _lastAvatarPosition = avatarPosition; } + _tree->unlock(); } } @@ -760,4 +766,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI } } +void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { + _entityScripts.remove(entityID); +} diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index b8acb28c3b..ff9066dd6d 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -101,6 +101,9 @@ signals: void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); + +public slots: + void deletingEntity(const EntityItemID& entityID); private: QList _releasedModels; diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index ce8d497da3..3cee527273 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -62,10 +62,21 @@ void RenderableModelEntityItem::remapTextures() { return; // nothing to do if we don't have a model } + if (!_model->isLoadedWithTextures()) { + return; // nothing to do if the model has not yet loaded it's default textures + } + + if (!_originalTexturesRead && _model->isLoadedWithTextures()) { + const QSharedPointer& networkGeometry = _model->getGeometry(); + if (networkGeometry) { + _originalTextures = networkGeometry->getTextureNames(); + _originalTexturesRead = true; + } + } + if (_currentTextures == _textures) { return; // nothing to do if our recently mapped textures match our desired textures } - qDebug() << "void RenderableModelEntityItem::remapTextures()...."; // since we're changing here, we need to run through our current texture map // and any textures in the recently mapped texture, that is not in our desired @@ -234,6 +245,14 @@ bool RenderableModelEntityItem::needsSimulation() const { return _needsInitialSimulation || simulationState == Moving || simulationState == Changing; } +EntityItemProperties RenderableModelEntityItem::getProperties() const { + EntityItemProperties properties = ModelEntityItem::getProperties(); // get the properties from our base class + if (_originalTexturesRead) { + properties.setTextureNames(_originalTextures); + } + return properties; +} + diff --git a/interface/src/entities/RenderableModelEntityItem.h b/interface/src/entities/RenderableModelEntityItem.h index dbc77b7e48..48c9a26051 100644 --- a/interface/src/entities/RenderableModelEntityItem.h +++ b/interface/src/entities/RenderableModelEntityItem.h @@ -37,10 +37,12 @@ public: _model(NULL), _needsInitialSimulation(true), _needsModelReload(true), - _myRenderer(NULL) { } + _myRenderer(NULL), + _originalTexturesRead(false) { } virtual ~RenderableModelEntityItem(); + virtual EntityItemProperties getProperties() const; virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy); virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, @@ -59,6 +61,8 @@ private: bool _needsModelReload; EntityTreeRenderer* _myRenderer; QString _currentTextures; + QStringList _originalTextures; + bool _originalTexturesRead; }; #endif // hifi_RenderableModelEntityItem_h diff --git a/interface/src/gpu/Batch.cpp b/interface/src/gpu/Batch.cpp index 40b2646418..e176e1641a 100644 --- a/interface/src/gpu/Batch.cpp +++ b/interface/src/gpu/Batch.cpp @@ -12,7 +12,6 @@ #include -//#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandCalls.push_back(&gpu::Batch::do_##call); _commandOffsets.push_back(_params.size()); #define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); using namespace gpu; @@ -33,6 +32,7 @@ void Batch::clear() { _commandOffsets.clear(); _params.clear(); _resources.clear(); + _buffers.clear(); _data.clear(); } @@ -59,7 +59,7 @@ uint32 Batch::cacheData(uint32 size, const void* data) { return offset; } -void Batch::draw(Primitive primitiveType, int nbVertices, int startVertex) { +void Batch::draw(Primitive primitiveType, uint32 nbVertices, uint32 startVertex) { ADD_COMMAND(draw); _params.push_back(startVertex); @@ -67,7 +67,7 @@ void Batch::draw(Primitive primitiveType, int nbVertices, int startVertex) { _params.push_back(primitiveType); } -void Batch::drawIndexed(Primitive primitiveType, int nbIndices, int startIndex) { +void Batch::drawIndexed(Primitive primitiveType, uint32 nbIndices, uint32 startIndex) { ADD_COMMAND(drawIndexed); _params.push_back(startIndex); @@ -75,7 +75,7 @@ void Batch::drawIndexed(Primitive primitiveType, int nbIndices, int startIndex) _params.push_back(primitiveType); } -void Batch::drawInstanced(uint32 nbInstances, Primitive primitiveType, int nbVertices, int startVertex, int startInstance) { +void Batch::drawInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbVertices, uint32 startVertex, uint32 startInstance) { ADD_COMMAND(drawInstanced); _params.push_back(startInstance); @@ -85,7 +85,7 @@ void Batch::drawInstanced(uint32 nbInstances, Primitive primitiveType, int nbVer _params.push_back(nbInstances); } -void Batch::drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, int nbIndices, int startIndex, int startInstance) { +void Batch::drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbIndices, uint32 startIndex, uint32 startInstance) { ADD_COMMAND(drawIndexedInstanced); _params.push_back(startInstance); @@ -95,5 +95,36 @@ void Batch::drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, in _params.push_back(nbInstances); } +void Batch::setInputFormat(const Stream::FormatPointer& format) { + ADD_COMMAND(setInputFormat); + _params.push_back(_streamFormats.cache(format)); +} +void Batch::setInputStream(Slot startChannel, const BufferStream& stream) { + if (stream.getNumBuffers()) { + const Buffers& buffers = stream.getBuffers(); + const Offsets& offsets = stream.getOffsets(); + const Offsets& strides = stream.getStrides(); + for (unsigned int i = 0; i < buffers.size(); i++) { + setInputBuffer(startChannel + i, buffers[i], offsets[i], strides[i]); + } + } +} + +void Batch::setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride) { + ADD_COMMAND(setInputBuffer); + + _params.push_back(stride); + _params.push_back(offset); + _params.push_back(_buffers.cache(buffer)); + _params.push_back(channel); +} + +void Batch::setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset) { + ADD_COMMAND(setIndexBuffer); + + _params.push_back(offset); + _params.push_back(_buffers.cache(buffer)); + _params.push_back(type); +} diff --git a/interface/src/gpu/Batch.h b/interface/src/gpu/Batch.h index 8078d33d0c..f9db3b9a40 100644 --- a/interface/src/gpu/Batch.h +++ b/interface/src/gpu/Batch.h @@ -16,6 +16,10 @@ #include +#include "gpu/Format.h" +#include "gpu/Resource.h" +#include "gpu/Stream.h" + #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" class ProfileRange { @@ -35,23 +39,20 @@ namespace gpu { -class Buffer; -class Resource; -typedef int Stamp; -typedef unsigned int uint32; -typedef int int32; - enum Primitive { - PRIMITIVE_POINTS = 0, - PRIMITIVE_LINES, - PRIMITIVE_LINE_STRIP, - PRIMITIVE_TRIANGLES, - PRIMITIVE_TRIANGLE_STRIP, - PRIMITIVE_QUADS, + POINTS = 0, + LINES, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + QUADS, + + NUM_PRIMITIVES, }; class Batch { public: + typedef Stream::Slot Slot; Batch(); Batch(const Batch& batch); @@ -59,10 +60,18 @@ public: void clear(); - void draw(Primitive primitiveType, int nbVertices, int startVertex = 0); - void drawIndexed(Primitive primitiveType, int nbIndices, int startIndex = 0); - void drawInstanced(uint32 nbInstances, Primitive primitiveType, int nbVertices, int startVertex = 0, int startInstance = 0); - void drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, int nbIndices, int startIndex = 0, int startInstance = 0); + void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0); + void drawIndexed(Primitive primitiveType, uint32 nbIndices, uint32 startIndex = 0); + void drawInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbVertices, uint32 startVertex = 0, uint32 startInstance = 0); + void drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbIndices, uint32 startIndex = 0, uint32 startInstance = 0); + + void setInputFormat(const Stream::FormatPointer& format); + + void setInputStream(Slot startChannel, const BufferStream& stream); // not a command, just unroll into a loop of setInputBuffer + void setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride); + + void setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long @@ -127,13 +136,12 @@ public: COMMAND_drawIndexed, COMMAND_drawInstanced, COMMAND_drawIndexedInstanced, - - COMMAND_SET_PIPE_STATE, - COMMAND_SET_VIEWPORT, - COMMAND_SET_FRAMEBUFFER, - COMMAND_SET_RESOURCE, - COMMAND_SET_VERTEX_STREAM, - COMMAND_SET_INDEX_STREAM, + + COMMAND_setInputFormat, + + COMMAND_setInputBuffer, + + COMMAND_setIndexBuffer, // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long @@ -226,21 +234,64 @@ public: }; typedef std::vector Resources; + template + class Cache { + public: + typedef QSharedPointer Pointer; + Pointer _pointer; + Cache(const Pointer& pointer) : _pointer(pointer) {} + + class Vector { + public: + std::vector< Cache > _pointers; + + uint32 cache(const Pointer& pointer) { + uint32 offset = _pointers.size(); + _pointers.push_back(Cache(pointer)); + return offset; + } + + Pointer get(uint32 offset) { + if (offset >= _pointers.size()) { + return Pointer(); + } + return (_pointers.data() + offset)->_pointer; + } + + void clear() { + _pointers.clear(); + } + }; + }; + + typedef Cache::Vector BufferCaches; + typedef Cache::Vector StreamFormatCaches; + typedef unsigned char Byte; typedef std::vector Bytes; uint32 cacheResource(Resource* res); uint32 cacheResource(const void* pointer); ResourceCache* editResource(uint32 offset) { - if (offset >= _resources.size()) + if (offset >= _resources.size()) { return 0; + } return (_resources.data() + offset); } + template + T* editResourcePointer(uint32 offset) { + if (offset >= _resources.size()) { + return 0; + } + return reinterpret_cast((_resources.data() + offset)->_pointer); + } + uint32 cacheData(uint32 size, const void* data); Byte* editData(uint32 offset) { - if (offset >= _data.size()) + if (offset >= _data.size()) { return 0; + } return (_data.data() + offset); } @@ -248,6 +299,10 @@ public: CommandOffsets _commandOffsets; Params _params; Resources _resources; + + BufferCaches _buffers; + StreamFormatCaches _streamFormats; + Bytes _data; protected: }; diff --git a/interface/src/gpu/Format.h b/interface/src/gpu/Format.h new file mode 100644 index 0000000000..8faa995924 --- /dev/null +++ b/interface/src/gpu/Format.h @@ -0,0 +1,147 @@ +// +// Format.h +// interface/src/gpu +// +// Created by Sam Gateau on 10/29/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Format_h +#define hifi_gpu_Format_h + +#include +#include "InterfaceConfig.h" + + +namespace gpu { + +typedef unsigned int uint32; +typedef int int32; +typedef unsigned short uint16; +typedef short int16; +typedef unsigned char uint8; +typedef char int8; + +typedef uint32 Offset; + +// Description of a scalar type +enum Type { + + FLOAT = 0, + INT32, + UINT32, + HALF, + INT16, + UINT16, + INT8, + UINT8, + + NFLOAT, + NINT32, + NUINT32, + NHALF, + NINT16, + NUINT16, + NINT8, + NUINT8, + + NUM_TYPES, +}; +// Array providing the size in bytes for a given scalar type +static const int TYPE_SIZE[NUM_TYPES] = { + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1 +}; + + +// Dimension of an Element +enum Dimension { + SCALAR = 0, + VEC2, + VEC3, + VEC4, + MAT3, + MAT4, + + NUM_DIMENSIONS, +}; +// Count (of scalars) in an Element for a given Dimension +static const int DIMENSION_COUNT[NUM_DIMENSIONS] = { + 1, + 2, + 3, + 4, + 9, + 16 +}; + +// Semantic of an Element +// Provide information on how to use the element +enum Semantic { + RGB = 0, + RGBA, + XYZ, + XYZW, + POS_XYZ, + POS_XYZW, + QUAT, + DIR_XYZ, + UV, + R8, + + NUM_SEMANTICS, +}; + +// Element is a simple 16bit value that contains everything we need to know about an element +// of a buffer, a pixel of a texture, a varying input/output or uniform from a shader pipeline. +// Type and dimension of the element, and semantic +class Element { +public: + Element(Dimension dim, Type type, Semantic sem) : + _semantic(sem), + _dimension(dim), + _type(type) + {} + Element() : + _semantic(R8), + _dimension(SCALAR), + _type(INT8) + {} + + Semantic getSemantic() const { return (Semantic)_semantic; } + + Dimension getDimension() const { return (Dimension)_dimension; } + uint8 getDimensionCount() const { return DIMENSION_COUNT[(Dimension)_dimension]; } + + Type getType() const { return (Type)_type; } + bool isNormalized() const { return (getType() >= NFLOAT); } + + uint32 getSize() const { return DIMENSION_COUNT[_dimension] * TYPE_SIZE[_type]; } + +protected: + uint8 _semantic; + uint8 _dimension : 4; + uint8 _type : 4; +}; + + +}; + + +#endif diff --git a/interface/src/gpu/GLBackend.cpp b/interface/src/gpu/GLBackend.cpp index bf9298ad72..5c2451d81f 100644 --- a/interface/src/gpu/GLBackend.cpp +++ b/interface/src/gpu/GLBackend.cpp @@ -18,17 +18,28 @@ using namespace gpu; GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { + (&::gpu::GLBackend::do_draw), + (&::gpu::GLBackend::do_drawIndexed), + (&::gpu::GLBackend::do_drawInstanced), + (&::gpu::GLBackend::do_drawIndexedInstanced), + + (&::gpu::GLBackend::do_setInputFormat), + + (&::gpu::GLBackend::do_setInputBuffer), + + (&::gpu::GLBackend::do_setIndexBuffer), + (&::gpu::GLBackend::do_glEnable), (&::gpu::GLBackend::do_glDisable), - + (&::gpu::GLBackend::do_glEnableClientState), (&::gpu::GLBackend::do_glDisableClientState), (&::gpu::GLBackend::do_glCullFace), (&::gpu::GLBackend::do_glAlphaFunc), - (&::gpu::GLBackend::do_glDepthFunc), - (&::gpu::GLBackend::do_glDepthMask), + (&::gpu::GLBackend::do_glDepthFunc), + (&::gpu::GLBackend::do_glDepthMask), (&::gpu::GLBackend::do_glDepthRange), (&::gpu::GLBackend::do_glBindBuffer), @@ -46,18 +57,18 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glPushMatrix), (&::gpu::GLBackend::do_glPopMatrix), (&::gpu::GLBackend::do_glMultMatrixf), - (&::gpu::GLBackend::do_glLoadMatrixf), - (&::gpu::GLBackend::do_glLoadIdentity), - (&::gpu::GLBackend::do_glRotatef), - (&::gpu::GLBackend::do_glScalef), - (&::gpu::GLBackend::do_glTranslatef), + (&::gpu::GLBackend::do_glLoadMatrixf), + (&::gpu::GLBackend::do_glLoadIdentity), + (&::gpu::GLBackend::do_glRotatef), + (&::gpu::GLBackend::do_glScalef), + (&::gpu::GLBackend::do_glTranslatef), - (&::gpu::GLBackend::do_glDrawArrays), + (&::gpu::GLBackend::do_glDrawArrays), (&::gpu::GLBackend::do_glDrawRangeElements), - - (&::gpu::GLBackend::do_glColorPointer), - (&::gpu::GLBackend::do_glNormalPointer), - (&::gpu::GLBackend::do_glTexCoordPointer), + + (&::gpu::GLBackend::do_glColorPointer), + (&::gpu::GLBackend::do_glNormalPointer), + (&::gpu::GLBackend::do_glTexCoordPointer), (&::gpu::GLBackend::do_glVertexPointer), (&::gpu::GLBackend::do_glVertexAttribPointer), @@ -66,12 +77,53 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glColor4f), - (&::gpu::GLBackend::do_glMaterialf), + (&::gpu::GLBackend::do_glMaterialf), (&::gpu::GLBackend::do_glMaterialfv), }; +static const GLenum _primitiveToGLmode[NUM_PRIMITIVES] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_QUADS, +}; -GLBackend::GLBackend() { +static const GLenum _elementTypeToGLType[NUM_TYPES]= { + GL_FLOAT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE, + GL_FLOAT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_BYTE, + GL_UNSIGNED_BYTE +}; + + +GLBackend::GLBackend() : + + _needInputFormatUpdate(true), + _inputFormat(0), + + _inputBuffersState(0), + _inputBuffers(_inputBuffersState.size(), BufferPointer(0)), + _inputBufferOffsets(_inputBuffersState.size(), 0), + _inputBufferStrides(_inputBuffersState.size(), 0), + + _indexBuffer(0), + _indexBufferOffset(0), + _inputAttributeActivation(0) +{ } @@ -86,8 +138,8 @@ void GLBackend::renderBatch(Batch& batch) { GLBackend backend; - for (int i = 0; i < numCommands; i++) { - CommandCall call = _commandCalls[((*command) - Batch::COMMAND_glEnable)]; + for (unsigned int i = 0; i < numCommands; i++) { + CommandCall call = _commandCalls[(*command)]; (backend.*(call))(batch, *offset); command++; offset++; @@ -126,24 +178,265 @@ void GLBackend::checkGLError() { } } +//#define CHECK_GL_ERROR() ::gpu::GLBackend::checkGLError() +#define CHECK_GL_ERROR() + +void GLBackend::do_draw(Batch& batch, uint32 paramOffset) { + updateInput(); + + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = _primitiveToGLmode[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 1]._uint; + uint32 startVertex = batch._params[paramOffset + 0]._uint; + + glDrawArrays(mode, startVertex, numVertices); + CHECK_GL_ERROR(); +} + +void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { + updateInput(); + + Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; + GLenum mode = _primitiveToGLmode[primitiveType]; + uint32 numIndices = batch._params[paramOffset + 1]._uint; + uint32 startIndex = batch._params[paramOffset + 0]._uint; + + GLenum glType = _elementTypeToGLType[_indexBufferType]; + + glDrawElements(mode, numIndices, glType, reinterpret_cast(startIndex + _indexBufferOffset)); + CHECK_GL_ERROR(); +} + +void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) { + CHECK_GL_ERROR(); +} + +void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) { + CHECK_GL_ERROR(); +} + +void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) { + Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint); + + if (format != _inputFormat) { + _inputFormat = format; + _needInputFormatUpdate = true; + } +} + +void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) { + Offset stride = batch._params[paramOffset + 0]._uint; + Offset offset = batch._params[paramOffset + 1]._uint; + BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); + uint32 channel = batch._params[paramOffset + 3]._uint; + + if (channel < getNumInputBuffers()) { + _inputBuffers[channel] = buffer; + _inputBufferOffsets[channel] = offset; + _inputBufferStrides[channel] = stride; + _inputBuffersState.set(channel); + } +} + +#define SUPPORT_LEGACY_OPENGL +#if defined(SUPPORT_LEGACY_OPENGL) +static const int NUM_CLASSIC_ATTRIBS = Stream::TANGENT; +static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = { + GL_VERTEX_ARRAY, + GL_NORMAL_ARRAY, + GL_COLOR_ARRAY, + GL_TEXTURE_COORD_ARRAY +}; +#endif + +void GLBackend::updateInput() { + if (_needInputFormatUpdate || _inputBuffersState.any()) { + + if (_needInputFormatUpdate) { + InputActivationCache newActivation; + + // Check expected activation + if (_inputFormat) { + const Stream::Format::AttributeMap& attributes = _inputFormat->getAttributes(); + for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { + const Stream::Attribute& attrib = (*it).second; + newActivation.set(attrib._slot); + } + } + + // Manage Activation what was and what is expected now + for (unsigned int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _inputAttributeActivation[i]) { +#if defined(SUPPORT_LEGACY_OPENGL) + if (i < NUM_CLASSIC_ATTRIBS) { + if (newState) { + glEnableClientState(attributeSlotToClassicAttribName[i]); + } + else { + glDisableClientState(attributeSlotToClassicAttribName[i]); + } + } else { +#else + { +#endif + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + } + CHECK_GL_ERROR(); + + _inputAttributeActivation.flip(i); + } + } + } + + // now we need to bind the buffers and assign the attrib pointers + if (_inputFormat) { + const Buffers& buffers = _inputBuffers; + const Offsets& offsets = _inputBufferOffsets; + const Offsets& strides = _inputBufferStrides; + + const Stream::Format::AttributeMap& attributes = _inputFormat->getAttributes(); + + for (Stream::Format::ChannelMap::const_iterator channelIt = _inputFormat->getChannels().begin(); + channelIt != _inputFormat->getChannels().end(); + channelIt++) { + const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second; + if ((*channelIt).first < buffers.size()) { + int bufferNum = (*channelIt).first; + + if (_inputBuffersState.test(bufferNum) || _needInputFormatUpdate) { + GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum])); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + CHECK_GL_ERROR(); + _inputBuffersState[bufferNum] = false; + + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + GLuint slot = attrib._slot; + GLuint count = attrib._element.getDimensionCount(); + GLenum type = _elementTypeToGLType[attrib._element.getType()]; + GLuint stride = strides[bufferNum]; + GLuint pointer = attrib._offset + offsets[bufferNum]; + #if defined(SUPPORT_LEGACY_OPENGL) + if (slot < NUM_CLASSIC_ATTRIBS) { + switch (slot) { + case Stream::POSITION: + glVertexPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::NORMAL: + glNormalPointer(type, stride, reinterpret_cast(pointer)); + break; + case Stream::COLOR: + glColorPointer(count, type, stride, reinterpret_cast(pointer)); + break; + case Stream::TEXCOORD: + glTexCoordPointer(count, type, stride, reinterpret_cast(pointer)); + break; + }; + } else { + #else + { + #endif + GLboolean isNormalized = attrib._element.isNormalized(); + glVertexAttribPointer(slot, count, type, isNormalized, stride, + reinterpret_cast(pointer)); + } + CHECK_GL_ERROR(); + } + } + } + } + } + // everything format related should be in sync now + _needInputFormatUpdate = false; + } + +/* TODO: Fancy version GL4.4 + if (_needInputFormatUpdate) { + + InputActivationCache newActivation; + + // Assign the vertex format required + if (_inputFormat) { + const StreamFormat::AttributeMap& attributes = _inputFormat->getAttributes(); + for (StreamFormat::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) { + const StreamFormat::Attribute& attrib = (*it).second; + newActivation.set(attrib._slot); + glVertexAttribFormat( + attrib._slot, + attrib._element.getDimensionCount(), + _elementTypeToGLType[attrib._element.getType()], + attrib._element.isNormalized(), + attrib._stride); + } + CHECK_GL_ERROR(); + } + + // Manage Activation what was and what is expected now + for (int i = 0; i < newActivation.size(); i++) { + bool newState = newActivation[i]; + if (newState != _inputAttributeActivation[i]) { + if (newState) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + _inputAttributeActivation.flip(i); + } + } + CHECK_GL_ERROR(); + + _needInputFormatUpdate = false; + } + + if (_needInputStreamUpdate) { + if (_inputStream) { + const Stream::Buffers& buffers = _inputStream->getBuffers(); + const Stream::Offsets& offsets = _inputStream->getOffsets(); + const Stream::Strides& strides = _inputStream->getStrides(); + + for (int i = 0; i < buffers.size(); i++) { + GLuint vbo = gpu::GLBackend::getBufferID((*buffers[i])); + glBindVertexBuffer(i, vbo, offsets[i], strides[i]); + } + + CHECK_GL_ERROR(); + } + _needInputStreamUpdate = false; + } +*/ +} + + +void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) { + _indexBufferType = (Type) batch._params[paramOffset + 2]._uint; + BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint); + _indexBufferOffset = batch._params[paramOffset + 0]._uint; + _indexBuffer = indexBuffer; + if (indexBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer)); + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + CHECK_GL_ERROR(); +} // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API -//#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandCalls.push_back(&gpu::Batch::do_##call); _commandOffsets.push_back(_params.size()); -#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); -//#define ADD_COMMAND_GL(call) _commands.push_back(COMMAND_##call); _commandCalls.push_back(&gpu::GLBackend::do_##call); _commandOffsets.push_back(_params.size()); #define ADD_COMMAND_GL(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); //#define DO_IT_NOW(call, offset) runLastCommand(); #define DO_IT_NOW(call, offset) -//#define CHECK_GL_ERROR() ::gpu::backend::checkGLError() -#define CHECK_GL_ERROR() void Batch::_glEnable(GLenum cap) { - ADD_COMMAND(glEnable); + ADD_COMMAND_GL(glEnable); _params.push_back(cap); @@ -155,7 +448,7 @@ void GLBackend::do_glEnable(Batch& batch, uint32 paramOffset) { } void Batch::_glDisable(GLenum cap) { - ADD_COMMAND(glDisable); + ADD_COMMAND_GL(glDisable); _params.push_back(cap); @@ -167,7 +460,7 @@ void GLBackend::do_glDisable(Batch& batch, uint32 paramOffset) { } void Batch::_glEnableClientState(GLenum array) { - ADD_COMMAND(glEnableClientState); + ADD_COMMAND_GL(glEnableClientState); _params.push_back(array); @@ -179,7 +472,7 @@ void GLBackend::do_glEnableClientState(Batch& batch, uint32 paramOffset) { } void Batch::_glDisableClientState(GLenum array) { - ADD_COMMAND(glDisableClientState); + ADD_COMMAND_GL(glDisableClientState); _params.push_back(array); @@ -191,7 +484,7 @@ void GLBackend::do_glDisableClientState(Batch& batch, uint32 paramOffset) { } void Batch::_glCullFace(GLenum mode) { - ADD_COMMAND(glCullFace); + ADD_COMMAND_GL(glCullFace); _params.push_back(mode); @@ -203,7 +496,7 @@ void GLBackend::do_glCullFace(Batch& batch, uint32 paramOffset) { } void Batch::_glAlphaFunc(GLenum func, GLclampf ref) { - ADD_COMMAND(glAlphaFunc); + ADD_COMMAND_GL(glAlphaFunc); _params.push_back(ref); _params.push_back(func); @@ -218,7 +511,7 @@ void GLBackend::do_glAlphaFunc(Batch& batch, uint32 paramOffset) { } void Batch::_glDepthFunc(GLenum func) { - ADD_COMMAND(glDepthFunc); + ADD_COMMAND_GL(glDepthFunc); _params.push_back(func); @@ -230,7 +523,7 @@ void GLBackend::do_glDepthFunc(Batch& batch, uint32 paramOffset) { } void Batch::_glDepthMask(GLboolean flag) { - ADD_COMMAND(glDepthMask); + ADD_COMMAND_GL(glDepthMask); _params.push_back(flag); @@ -242,7 +535,7 @@ void GLBackend::do_glDepthMask(Batch& batch, uint32 paramOffset) { } void Batch::_glDepthRange(GLclampd zNear, GLclampd zFar) { - ADD_COMMAND(glDepthRange); + ADD_COMMAND_GL(glDepthRange); _params.push_back(zFar); _params.push_back(zNear); @@ -257,7 +550,7 @@ void GLBackend::do_glDepthRange(Batch& batch, uint32 paramOffset) { } void Batch::_glBindBuffer(GLenum target, GLuint buffer) { - ADD_COMMAND(glBindBuffer); + ADD_COMMAND_GL(glBindBuffer); _params.push_back(buffer); _params.push_back(target); @@ -272,7 +565,7 @@ void GLBackend::do_glBindBuffer(Batch& batch, uint32 paramOffset) { } void Batch::_glBindTexture(GLenum target, GLuint texture) { - ADD_COMMAND(glBindTexture); + ADD_COMMAND_GL(glBindTexture); _params.push_back(texture); _params.push_back(target); @@ -287,7 +580,7 @@ void GLBackend::do_glBindTexture(Batch& batch, uint32 paramOffset) { } void Batch::_glActiveTexture(GLenum texture) { - ADD_COMMAND(glActiveTexture); + ADD_COMMAND_GL(glActiveTexture); _params.push_back(texture); @@ -299,7 +592,7 @@ void GLBackend::do_glActiveTexture(Batch& batch, uint32 paramOffset) { } void Batch::_glDrawBuffers(GLsizei n, const GLenum* bufs) { - ADD_COMMAND(glDrawBuffers); + ADD_COMMAND_GL(glDrawBuffers); _params.push_back(cacheData(n * sizeof(GLenum), bufs)); _params.push_back(n); @@ -314,7 +607,7 @@ void GLBackend::do_glDrawBuffers(Batch& batch, uint32 paramOffset) { } void Batch::_glUseProgram(GLuint program) { - ADD_COMMAND(glUseProgram); + ADD_COMMAND_GL(glUseProgram); _params.push_back(program); @@ -326,7 +619,7 @@ void GLBackend::do_glUseProgram(Batch& batch, uint32 paramOffset) { } void Batch::_glUniform1f(GLint location, GLfloat v0) { - ADD_COMMAND(glUniform1f); + ADD_COMMAND_GL(glUniform1f); _params.push_back(v0); _params.push_back(location); @@ -341,7 +634,7 @@ void GLBackend::do_glUniform1f(Batch& batch, uint32 paramOffset) { } void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { - ADD_COMMAND(glUniformMatrix4fv); + ADD_COMMAND_GL(glUniformMatrix4fv); const int MATRIX4_SIZE = 16 * sizeof(float); _params.push_back(cacheData(count * MATRIX4_SIZE, value)); @@ -361,7 +654,7 @@ void GLBackend::do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset) { } void Batch::_glMatrixMode(GLenum mode) { - ADD_COMMAND(glMatrixMode); + ADD_COMMAND_GL(glMatrixMode); _params.push_back(mode); @@ -373,7 +666,7 @@ void GLBackend::do_glMatrixMode(Batch& batch, uint32 paramOffset) { } void Batch::_glPushMatrix() { - ADD_COMMAND(glPushMatrix); + ADD_COMMAND_GL(glPushMatrix); DO_IT_NOW(_glPushMatrix, 0); } @@ -383,7 +676,7 @@ void GLBackend::do_glPushMatrix(Batch& batch, uint32 paramOffset) { } void Batch::_glPopMatrix() { - ADD_COMMAND(glPopMatrix); + ADD_COMMAND_GL(glPopMatrix); DO_IT_NOW(_glPopMatrix, 0); } @@ -393,7 +686,7 @@ void GLBackend::do_glPopMatrix(Batch& batch, uint32 paramOffset) { } void Batch::_glMultMatrixf(const GLfloat *m) { - ADD_COMMAND(glMultMatrixf); + ADD_COMMAND_GL(glMultMatrixf); const int MATRIX4_SIZE = 16 * sizeof(float); _params.push_back(cacheData(MATRIX4_SIZE, m)); @@ -406,7 +699,7 @@ void GLBackend::do_glMultMatrixf(Batch& batch, uint32 paramOffset) { } void Batch::_glLoadMatrixf(const GLfloat *m) { - ADD_COMMAND(glLoadMatrixf); + ADD_COMMAND_GL(glLoadMatrixf); const int MATRIX4_SIZE = 16 * sizeof(float); _params.push_back(cacheData(MATRIX4_SIZE, m)); @@ -419,7 +712,7 @@ void GLBackend::do_glLoadMatrixf(Batch& batch, uint32 paramOffset) { } void Batch::_glLoadIdentity(void) { - ADD_COMMAND(glLoadIdentity); + ADD_COMMAND_GL(glLoadIdentity); DO_IT_NOW(_glLoadIdentity, 0); } @@ -429,7 +722,7 @@ void GLBackend::do_glLoadIdentity(Batch& batch, uint32 paramOffset) { } void Batch::_glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) { - ADD_COMMAND(glRotatef); + ADD_COMMAND_GL(glRotatef); _params.push_back(z); _params.push_back(y); @@ -448,7 +741,7 @@ void GLBackend::do_glRotatef(Batch& batch, uint32 paramOffset) { } void Batch::_glScalef(GLfloat x, GLfloat y, GLfloat z) { - ADD_COMMAND(glScalef); + ADD_COMMAND_GL(glScalef); _params.push_back(z); _params.push_back(y); @@ -465,7 +758,7 @@ void GLBackend::do_glScalef(Batch& batch, uint32 paramOffset) { } void Batch::_glTranslatef(GLfloat x, GLfloat y, GLfloat z) { - ADD_COMMAND(glTranslatef); + ADD_COMMAND_GL(glTranslatef); _params.push_back(z); _params.push_back(y); @@ -482,7 +775,7 @@ void GLBackend::do_glTranslatef(Batch& batch, uint32 paramOffset) { } void Batch::_glDrawArrays(GLenum mode, GLint first, GLsizei count) { - ADD_COMMAND(glDrawArrays); + ADD_COMMAND_GL(glDrawArrays); _params.push_back(count); _params.push_back(first); @@ -499,7 +792,7 @@ void GLBackend::do_glDrawArrays(Batch& batch, uint32 paramOffset) { } void Batch::_glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) { - ADD_COMMAND(glDrawRangeElements); + ADD_COMMAND_GL(glDrawRangeElements); _params.push_back(cacheResource(indices)); _params.push_back(type); @@ -522,7 +815,7 @@ void GLBackend::do_glDrawRangeElements(Batch& batch, uint32 paramOffset) { } void Batch::_glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { - ADD_COMMAND(glColorPointer); + ADD_COMMAND_GL(glColorPointer); _params.push_back(cacheResource(pointer)); _params.push_back(stride); @@ -541,7 +834,7 @@ void GLBackend::do_glColorPointer(Batch& batch, uint32 paramOffset) { } void Batch::_glNormalPointer(GLenum type, GLsizei stride, const void *pointer) { - ADD_COMMAND(glNormalPointer); + ADD_COMMAND_GL(glNormalPointer); _params.push_back(cacheResource(pointer)); _params.push_back(stride); @@ -558,7 +851,7 @@ void GLBackend::do_glNormalPointer(Batch& batch, uint32 paramOffset) { } void Batch::_glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { - ADD_COMMAND(glTexCoordPointer); + ADD_COMMAND_GL(glTexCoordPointer); _params.push_back(cacheResource(pointer)); _params.push_back(stride); @@ -577,7 +870,7 @@ void GLBackend::do_glTexCoordPointer(Batch& batch, uint32 paramOffset) { } void Batch::_glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) { - ADD_COMMAND(glVertexPointer); + ADD_COMMAND_GL(glVertexPointer); _params.push_back(cacheResource(pointer)); _params.push_back(stride); @@ -597,7 +890,7 @@ void GLBackend::do_glVertexPointer(Batch& batch, uint32 paramOffset) { void Batch::_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { - ADD_COMMAND(glVertexAttribPointer); + ADD_COMMAND_GL(glVertexAttribPointer); _params.push_back(cacheResource(pointer)); _params.push_back(stride); @@ -620,7 +913,7 @@ void GLBackend::do_glVertexAttribPointer(Batch& batch, uint32 paramOffset) { } void Batch::_glEnableVertexAttribArray(GLint location) { - ADD_COMMAND(glEnableVertexAttribArray); + ADD_COMMAND_GL(glEnableVertexAttribArray); _params.push_back(location); @@ -632,7 +925,7 @@ void GLBackend::do_glEnableVertexAttribArray(Batch& batch, uint32 paramOffset) { } void Batch::_glDisableVertexAttribArray(GLint location) { - ADD_COMMAND(glDisableVertexAttribArray); + ADD_COMMAND_GL(glDisableVertexAttribArray); _params.push_back(location); @@ -644,7 +937,7 @@ void GLBackend::do_glDisableVertexAttribArray(Batch& batch, uint32 paramOffset) } void Batch::_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { - ADD_COMMAND(glColor4f); + ADD_COMMAND_GL(glColor4f); _params.push_back(alpha); _params.push_back(blue); @@ -663,7 +956,7 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) { } void Batch::_glMaterialf(GLenum face, GLenum pname, GLfloat param) { - ADD_COMMAND(glMaterialf); + ADD_COMMAND_GL(glMaterialf); _params.push_back(param); _params.push_back(pname); @@ -680,7 +973,7 @@ void GLBackend::do_glMaterialf(Batch& batch, uint32 paramOffset) { } void Batch::_glMaterialfv(GLenum face, GLenum pname, const GLfloat *params) { - ADD_COMMAND(glMaterialfv); + ADD_COMMAND_GL(glMaterialfv); _params.push_back(cacheData(4 * sizeof(float), params)); _params.push_back(pname); @@ -719,6 +1012,7 @@ void GLBackend::syncGPUObject(const Buffer& buffer) { if (!object) { object = new GLBuffer(); glGenBuffers(1, &object->_buffer); + CHECK_GL_ERROR(); Backend::setGPUObject(buffer, object); } @@ -731,6 +1025,7 @@ void GLBackend::syncGPUObject(const Buffer& buffer) { object->_stamp = buffer.getSysmem().getStamp(); object->_size = buffer.getSysmem().getSize(); //} + CHECK_GL_ERROR(); } diff --git a/interface/src/gpu/GLBackend.h b/interface/src/gpu/GLBackend.h index a28ffb3dcc..2cce6cbedc 100644 --- a/interface/src/gpu/GLBackend.h +++ b/interface/src/gpu/GLBackend.h @@ -16,6 +16,8 @@ #include "gpu/Context.h" #include "gpu/Batch.h" +#include + namespace gpu { @@ -43,8 +45,42 @@ public: static GLuint getBufferID(const Buffer& buffer); + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; + static const int MAX_NUM_INPUT_BUFFERS = 16; + + uint32 getNumInputBuffers() const { return _inputBuffersState.size(); } + protected: + bool _needInputFormatUpdate; + Stream::FormatPointer _inputFormat; + + typedef std::bitset InputBuffersState; + InputBuffersState _inputBuffersState; + Buffers _inputBuffers; + Offsets _inputBufferOffsets; + Offsets _inputBufferStrides; + + BufferPointer _indexBuffer; + Offset _indexBufferOffset; + Type _indexBufferType; + + typedef std::bitset InputActivationCache; + InputActivationCache _inputAttributeActivation; + + void do_draw(Batch& batch, uint32 paramOffset); + void do_drawIndexed(Batch& batch, uint32 paramOffset); + void do_drawInstanced(Batch& batch, uint32 paramOffset); + void do_drawIndexedInstanced(Batch& batch, uint32 paramOffset); + + void updateInput(); + void do_setInputFormat(Batch& batch, uint32 paramOffset); + + void do_setInputBuffer(Batch& batch, uint32 paramOffset); + + void do_setVertexBuffer(Batch& batch, uint32 paramOffset); + void do_setIndexBuffer(Batch& batch, uint32 paramOffset); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 3579f7145a..3a6e47a7ba 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -8,6 +8,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include "Context.h" #include "Resource.h" #include @@ -120,7 +122,7 @@ Resource::Size Resource::Sysmem::resize(Size size) { Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { if (allocate(size) == size) { - if (bytes) { + if (size && bytes) { memcpy( _data, bytes, _size ); _stamp++; } @@ -129,7 +131,7 @@ Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { } Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { - if (((offset + size) <= getSize()) && bytes) { + if (size && ((offset + size) <= getSize()) && bytes) { memcpy( _data + offset, bytes, size ); _stamp++; return size; diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index d4527cac4f..52108c215a 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -14,6 +14,11 @@ #include #include "InterfaceConfig.h" +#include "gpu/Format.h" + +#include + +#include namespace gpu { @@ -142,6 +147,8 @@ protected: friend class Backend; }; +typedef QSharedPointer BufferPointer; +typedef std::vector< BufferPointer > Buffers; }; diff --git a/interface/src/gpu/Stream.cpp b/interface/src/gpu/Stream.cpp new file mode 100644 index 0000000000..099cbde24a --- /dev/null +++ b/interface/src/gpu/Stream.cpp @@ -0,0 +1,48 @@ +// +// Stream.cpp +// interface/src/gpu +// +// Created by Sam Gateau on 10/29/2014. +// Copyright 2014 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 "Stream.h" + +using namespace gpu; + +void Stream::Format::evaluateCache() { + _channels.clear(); + _elementTotalSize = 0; + for(AttributeMap::iterator it = _attributes.begin(); it != _attributes.end(); it++) { + Attribute& attrib = (*it).second; + Channel& channel = _channels[attrib._channel]; + channel._slots.push_back(attrib._slot); + channel._stride = std::max(channel._stride, attrib.getSize() + attrib._offset); + channel._netSize += attrib.getSize(); + _elementTotalSize += attrib.getSize(); + } +} + +bool Stream::Format::setAttribute(Slot slot, Slot channel, Element element, Offset offset, Frequency frequency) { + _attributes[slot] = Attribute((InputSlot) slot, channel, element, offset, frequency); + evaluateCache(); + return true; +} + +BufferStream::BufferStream() : + _buffers(), + _offsets(), + _strides() +{} + +BufferStream::~BufferStream() { +} + +void BufferStream::addBuffer(BufferPointer& buffer, Offset offset, Offset stride) { + _buffers.push_back(buffer); + _offsets.push_back(offset); + _strides.push_back(stride); +} diff --git a/interface/src/gpu/Stream.h b/interface/src/gpu/Stream.h new file mode 100644 index 0000000000..84b2edc5f3 --- /dev/null +++ b/interface/src/gpu/Stream.h @@ -0,0 +1,150 @@ +// +// Stream.h +// interface/src/gpu +// +// Created by Sam Gateau on 10/29/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Stream_h +#define hifi_gpu_Stream_h + +#include +#include "InterfaceConfig.h" + +#include "gpu/Resource.h" +#include "gpu/Format.h" +#include +#include + +namespace gpu { + +// Stream namespace class +class Stream { +public: + + // Possible input slots identifiers + enum InputSlot { + POSITION = 0, + NORMAL, + COLOR, + TEXCOORD, + TANGENT, + SKIN_CLUSTER_INDEX, + SKIN_CLUSTER_WEIGHT, + + NUM_INPUT_SLOTS, + }; + + typedef uint8 Slot; + + // Frequency describer + enum Frequency { + PER_VERTEX = 0, + PER_INSTANCE, + }; + + // The attribute description + // Every thing that is needed to detail a stream attribute and how to interpret it + class Attribute { + public: + Attribute(Slot slot, Slot channel, Element element, Offset offset = 0, Frequency frequency = PER_VERTEX) : + _slot(slot), + _channel(channel), + _element(element), + _offset(offset), + _frequency(frequency) + {} + Attribute() : + _slot(POSITION), + _channel(0), + _element(), + _offset(0), + _frequency(PER_VERTEX) + {} + + + Slot _slot; // Logical slot assigned to the attribute + Slot _channel; // index of the channel where to get the data from + Element _element; + + Offset _offset; + uint32 _frequency; + + // Size of the + uint32 getSize() const { return _element.getSize(); } + }; + + // Stream Format is describing how to feed a list of attributes from a bunch of stream buffer channels + class Format { + public: + typedef std::map< Slot, Attribute > AttributeMap; + + class Channel { + public: + std::vector< Slot > _slots; + std::vector< Offset > _offsets; + Offset _stride; + uint32 _netSize; + + Channel() : _stride(0), _netSize(0) {} + }; + typedef std::map< Slot, Channel > ChannelMap; + + Format() : + _attributes(), + _elementTotalSize(0) {} + ~Format() {} + + uint32 getNumAttributes() const { return _attributes.size(); } + const AttributeMap& getAttributes() const { return _attributes; } + + uint8 getNumChannels() const { return _channels.size(); } + const ChannelMap& getChannels() const { return _channels; } + + uint32 getElementTotalSize() const { return _elementTotalSize; } + + bool setAttribute(Slot slot, Slot channel, Element element, Offset offset = 0, Frequency frequency = PER_VERTEX); + + protected: + AttributeMap _attributes; + ChannelMap _channels; + uint32 _elementTotalSize; + + void evaluateCache(); + }; + + typedef QSharedPointer FormatPointer; +}; + +typedef std::vector< Offset > Offsets; + +// Buffer Stream is a container of N Buffers and their respective Offsets and Srides representing N consecutive channels. +// A Buffer Stream can be assigned to the Batch to set several stream channels in one call +class BufferStream { +public: + typedef Offsets Strides; + + BufferStream(); + ~BufferStream(); + + void addBuffer(BufferPointer& buffer, Offset offset, Offset stride); + + const Buffers& getBuffers() const { return _buffers; } + const Offsets& getOffsets() const { return _offsets; } + const Strides& getStrides() const { return _strides; } + uint8 getNumBuffers() const { return _buffers.size(); } + +protected: + Buffers _buffers; + Offsets _offsets; + Strides _strides; +}; +typedef QSharedPointer BufferStreamPointer; + +}; + + +#endif diff --git a/interface/src/renderer/DeferredLightingEffect.cpp b/interface/src/renderer/DeferredLightingEffect.cpp index 6e96d106cc..917a6de00e 100644 --- a/interface/src/renderer/DeferredLightingEffect.cpp +++ b/interface/src/renderer/DeferredLightingEffect.cpp @@ -230,9 +230,10 @@ void DeferredLightingEffect::render() { glTexGenfv(GL_T, GL_OBJECT_PLANE, (const GLfloat*)&tCoefficients); // enlarge the scales slightly to account for tesselation - const float SCALE_EXPANSION = 0.1f; + const float SCALE_EXPANSION = 0.05f; const glm::vec3& eyePoint = Application::getInstance()->getDisplayViewFrustum()->getPosition(); + float nearRadius = glm::distance(eyePoint, Application::getInstance()->getDisplayViewFrustum()->getNearTopLeft()); if (!_pointLights.isEmpty()) { _pointLight.bind(); @@ -247,14 +248,14 @@ void DeferredLightingEffect::render() { glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, light.constantAttenuation); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, light.linearAttenuation); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.f ? light.constantAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.f ? light.linearAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.f ? light.quadraticAttenuation : 0.f)); glPushMatrix(); float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION); - if (glm::distance(eyePoint, glm::vec3(light.position)) < expandedRadius) { + if (glm::distance(eyePoint, glm::vec3(light.position)) < expandedRadius + nearRadius) { glLoadIdentity(); glTranslatef(0.0f, 0.0f, -1.0f); @@ -269,7 +270,7 @@ void DeferredLightingEffect::render() { } else { glTranslatef(light.position.x, light.position.y, light.position.z); - Application::getInstance()->getGeometryCache()->renderSphere(expandedRadius, 64, 64); + Application::getInstance()->getGeometryCache()->renderSphere(expandedRadius, 32, 32); } glPopMatrix(); @@ -292,9 +293,9 @@ void DeferredLightingEffect::render() { glLightfv(GL_LIGHT1, GL_DIFFUSE, (const GLfloat*)&light.diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, (const GLfloat*)&light.specular); glLightfv(GL_LIGHT1, GL_POSITION, (const GLfloat*)&light.position); - glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, light.constantAttenuation); - glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, light.linearAttenuation); - glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light.quadraticAttenuation); + glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, (light.constantAttenuation > 0.f ? light.constantAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, (light.linearAttenuation > 0.f ? light.linearAttenuation : 0.f)); + glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, (light.quadraticAttenuation > 0.f ? light.quadraticAttenuation : 0.f)); glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, (const GLfloat*)&light.direction); glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, light.exponent); glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, glm::degrees(light.cutoff)); @@ -303,7 +304,7 @@ void DeferredLightingEffect::render() { float expandedRadius = light.radius * (1.0f + SCALE_EXPANSION); float edgeRadius = expandedRadius / glm::cos(light.cutoff); - if (glm::distance(eyePoint, glm::vec3(light.position)) < edgeRadius) { + if (glm::distance(eyePoint, glm::vec3(light.position)) < edgeRadius + nearRadius) { glLoadIdentity(); glTranslatef(0.0f, 0.0f, -1.0f); @@ -323,7 +324,7 @@ void DeferredLightingEffect::render() { glRotatef(glm::degrees(glm::angle(spotRotation)), axis.x, axis.y, axis.z); glTranslatef(0.0f, 0.0f, -light.radius * (1.0f + SCALE_EXPANSION * 0.5f)); Application::getInstance()->getGeometryCache()->renderCone(expandedRadius * glm::tan(light.cutoff), - expandedRadius, 64, 32); + expandedRadius, 32, 1); } glPopMatrix(); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 5aa4012cc3..1742afba05 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -173,12 +173,12 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks) { for (int j = 0; j < slices; j++) { int next = (j + 1) % slices; - *(index++) = bottom + j; *(index++) = top + next; + *(index++) = bottom + j; *(index++) = top + j; - *(index++) = bottom + j; *(index++) = bottom + next; + *(index++) = bottom + j; *(index++) = top + next; } } @@ -187,8 +187,8 @@ void GeometryCache::renderSphere(float radius, int slices, int stacks) { bottom = (stacks - 2) * slices + 1; top = bottom + slices; for (int i = 0; i < slices; i++) { - *(index++) = bottom + i; *(index++) = bottom + (i + 1) % slices; + *(index++) = bottom + i; *(index++) = top; } @@ -408,8 +408,8 @@ void GeometryCache::renderCone(float base, float height, int slices, int stacks) GLushort* index = indexData; for (int i = 0; i < baseTriangles; i++) { *(index++) = 0; - *(index++) = i + 1; *(index++) = i + 2; + *(index++) = i + 1; } for (int i = 1; i <= stacks; i++) { GLushort bottom = i * slices; @@ -734,6 +734,32 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u } } +QStringList NetworkGeometry::getTextureNames() const { + QStringList result; + for (int i = 0; i < _meshes.size(); i++) { + const NetworkMesh& mesh = _meshes[i]; + for (int j = 0; j < mesh.parts.size(); j++) { + const NetworkMeshPart& part = mesh.parts[j]; + + if (!part.diffuseTextureName.isEmpty()) { + QString textureURL = part.diffuseTexture->getURL().toString(); + result << part.diffuseTextureName + ":" + textureURL; + } + + if (!part.normalTextureName.isEmpty()) { + QString textureURL = part.normalTexture->getURL().toString(); + result << part.normalTextureName + ":" + textureURL; + } + + if (!part.specularTextureName.isEmpty()) { + QString textureURL = part.specularTexture->getURL().toString(); + result << part.specularTextureName + ":" + textureURL; + } + } + } + return result; +} + /// Reads geometry in a worker thread. class GeometryReader : public QRunnable { public: @@ -840,7 +866,7 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; foreach (const FBXMesh& mesh, _geometry.meshes) { - NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) }; + NetworkMesh networkMesh; int totalIndices = 0; foreach (const FBXMeshPart& part, mesh.parts) { @@ -870,67 +896,103 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); } - - networkMesh.indexBuffer.create(); - networkMesh.indexBuffer.bind(); - networkMesh.indexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); - networkMesh.indexBuffer.allocate(totalIndices * sizeof(int)); - int offset = 0; - foreach (const FBXMeshPart& part, mesh.parts) { - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int), - part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int), - part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); + + { + networkMesh._indexBuffer = gpu::BufferPointer(new gpu::Buffer()); + networkMesh._indexBuffer->resize(totalIndices * sizeof(int)); + int offset = 0; + foreach(const FBXMeshPart& part, mesh.parts) { + networkMesh._indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), + (gpu::Resource::Byte*) part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + networkMesh._indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), + (gpu::Resource::Byte*) part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } } - networkMesh.indexBuffer.release(); - - networkMesh.vertexBuffer.create(); - networkMesh.vertexBuffer.bind(); - networkMesh.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); - - // if we don't need to do any blending, the positions/normals can be static - if (mesh.blendshapes.isEmpty()) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh.vertexBuffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(normalsOffset, mesh.normals.constData(), mesh.normals.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(tangentsOffset, mesh.tangents.constData(), - mesh.tangents.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(), - mesh.texCoords.size() * sizeof(glm::vec2)); - networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(), - mesh.clusterIndices.size() * sizeof(glm::vec4)); - networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), - mesh.clusterWeights.size() * sizeof(glm::vec4)); - - // otherwise, at least the cluster indices/weights can be static - } else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3)); - networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(), - mesh.texCoords.size() * sizeof(glm::vec2)); - networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(), - mesh.clusterIndices.size() * sizeof(glm::vec4)); - networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), - mesh.clusterWeights.size() * sizeof(glm::vec4)); + + { + networkMesh._vertexBuffer = gpu::BufferPointer(new gpu::Buffer()); + // if we don't need to do any blending, the positions/normals can be static + if (mesh.blendshapes.isEmpty()) { + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + //networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + networkMesh._vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData()); + networkMesh._vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData()); + networkMesh._vertexBuffer->setSubData(tangentsOffset, + mesh.tangents.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.tangents.constData()); + networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData()); + networkMesh._vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData()); + networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData()); + networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterWeights.constData()); + + // otherwise, at least the cluster indices/weights can be static + networkMesh._vertexStream = gpu::BufferStreamPointer(new gpu::BufferStream()); + networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.normals.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, normalsOffset, sizeof(glm::vec3)); + if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, tangentsOffset, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh._vertexFormat = gpu::Stream::FormatPointer(new gpu::Stream::Format()); + networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::POS_XYZ), 0); + if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + } + else { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh._vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh._vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.tangents.constData()); + networkMesh._vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.colors.constData()); + networkMesh._vertexBuffer->setSubData(texCoordsOffset, + mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Resource::Byte*) mesh.texCoords.constData()); + networkMesh._vertexBuffer->setSubData(clusterIndicesOffset, + mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterIndices.constData()); + networkMesh._vertexBuffer->setSubData(clusterWeightsOffset, + mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Resource::Byte*) mesh.clusterWeights.constData()); + + networkMesh._vertexStream = gpu::BufferStreamPointer(new gpu::BufferStream()); + if (mesh.tangents.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, 0, sizeof(glm::vec3)); + if (mesh.colors.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, colorsOffset, sizeof(glm::vec3)); + if (mesh.texCoords.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); + if (mesh.clusterIndices.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); + if (mesh.clusterWeights.size()) networkMesh._vertexStream->addBuffer(networkMesh._vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); + + int channelNum = 0; + networkMesh._vertexFormat = gpu::Stream::FormatPointer(new gpu::Stream::Format()); + networkMesh._vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::POS_XYZ)); + if (mesh.normals.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.tangents.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + if (mesh.colors.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); + if (mesh.texCoords.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + if (mesh.clusterIndices.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + if (mesh.clusterWeights.size()) networkMesh._vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::NFLOAT, gpu::XYZW)); + + } } - - networkMesh.vertexBuffer.release(); - + _meshes.append(networkMesh); } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index bb6a19b1a9..28f1341b5f 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -24,6 +24,8 @@ #include +#include "gpu/Stream.h" + class Model; class NetworkGeometry; class NetworkMesh; @@ -110,6 +112,7 @@ public: virtual void clearLoadPriority(const QPointer& owner); void setTextureWithNameToURL(const QString& name, const QUrl& url); + QStringList getTextureNames() const; protected: @@ -155,9 +158,12 @@ public: /// The state associated with a single mesh. class NetworkMesh { public: - - QOpenGLBuffer indexBuffer; - QOpenGLBuffer vertexBuffer; + gpu::BufferPointer _indexBuffer; + gpu::BufferPointer _vertexBuffer; + + gpu::BufferStreamPointer _vertexStream; + + gpu::Stream::FormatPointer _vertexFormat; QVector parts; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 2248b377ae..8d426d067b 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -137,6 +137,10 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, int + glBindAttribLocation(program.programId(), gpu::Stream::TANGENT, "tangent"); + + glLinkProgram(program.programId()); + locations.tangent = program.attributeLocation("tangent"); locations.alphaThreshold = program.uniformLocation("alphaThreshold"); @@ -171,7 +175,13 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati #endif + // HACK: Assign explicitely the attribute channel to avoid a bug on Yosemite + glBindAttribLocation(program.programId(), gpu::Stream::SKIN_CLUSTER_INDEX, "clusterIndices"); + + glBindAttribLocation(program.programId(), gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights"); + + glLinkProgram(program.programId()); program.bind(); @@ -395,19 +405,15 @@ bool Model::updateGeometry() { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.append(state); - - QOpenGLBuffer buffer; + + gpu::BufferPointer buffer(new gpu::Buffer()); if (!mesh.blendshapes.isEmpty()) { - buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); - buffer.create(); - buffer.bind(); - buffer.allocate((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); - buffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3)); - buffer.write(mesh.vertices.size() * sizeof(glm::vec3), mesh.normals.constData(), - mesh.normals.size() * sizeof(glm::vec3)); - buffer.release(); + buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); + buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.vertices.constData()); + buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), + mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) mesh.normals.constData()); } - _blendedVertexBuffers.append(buffer); + _blendedVertexBuffers.push_back(buffer); } foreach (const FBXAttachment& attachment, fbxGeometry.attachments) { Model* model = new Model(this); @@ -473,9 +479,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float bestDistance = std::numeric_limits::max(); float distanceToSubMesh; BoxFace subMeshFace; - BoxFace bestSubMeshFace; int subMeshIndex = 0; - int bestSubMeshIndex = -1; // If we hit the models box, then consider the submeshes... foreach(const AABox& subMeshBox, _calculatedMeshBoxes) { @@ -483,10 +487,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (subMeshBox.findRayIntersection(origin, direction, distanceToSubMesh, subMeshFace)) { if (distanceToSubMesh < bestDistance) { - bestSubMeshIndex = subMeshIndex; bestDistance = distanceToSubMesh; - bestSubMeshFace = subMeshFace; intersectedSomething = true; + face = subMeshFace; extraInfo = geometry.getModelNameOfMesh(subMeshIndex); } } @@ -516,6 +519,7 @@ void Model::recalcuateMeshBoxes() { bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { PROFILE_RANGE(__FUNCTION__); + // render the attachments foreach (Model* attachment, _attachments) { attachment->render(alpha, mode); @@ -549,8 +553,6 @@ bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { // Let's introduce a gpu::Batch to capture all the calls to the graphics api gpu::Batch batch; - GLBATCH(glEnableClientState)(GL_VERTEX_ARRAY); - GLBATCH(glEnableClientState)(GL_NORMAL_ARRAY); GLBATCH(glDisable)(GL_COLOR_MATERIAL); @@ -665,6 +667,10 @@ bool Model::render(float alpha, RenderMode mode, RenderArgs* args) { GLBATCH(glDisableClientState)(GL_NORMAL_ARRAY); GLBATCH(glDisableClientState)(GL_VERTEX_ARRAY); GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY); + GLBATCH(glDisableClientState)(GL_COLOR_ARRAY); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::TANGENT); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_INDEX); + GLBATCH(glDisableVertexAttribArray)(gpu::Stream::SKIN_CLUSTER_WEIGHT); // bind with 0 to switch back to normal operation GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, 0); @@ -1391,7 +1397,7 @@ bool Model::maybeStartBlender() { void Model::setBlendedVertices(int blendNumber, const QWeakPointer& geometry, const QVector& vertices, const QVector& normals) { - if (_geometry != geometry || _blendedVertexBuffers.isEmpty() || blendNumber < _appliedBlendNumber) { + if (_geometry != geometry || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; @@ -1402,12 +1408,12 @@ void Model::setBlendedVertices(int blendNumber, const QWeakPointersetSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) vertices.constData() + index*sizeof(glm::vec3)); + buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), + mesh.normals.size() * sizeof(glm::vec3), (gpu::Resource::Byte*) normals.constData() + index*sizeof(glm::vec3)); + index += mesh.vertices.size(); } } @@ -1791,11 +1797,8 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl activeProgram->link(); } GLBATCH(glUseProgram)(activeProgram->programId()); - // activeProgram->setUniformValue(activeLocations->alphaThreshold, alphaThreshold); GLBATCH(glUniform1f)(activeLocations->alphaThreshold, alphaThreshold); - - // i is the "index" from the original networkMeshes QVector... foreach (int i, list) { @@ -1811,9 +1814,7 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl const NetworkMesh& networkMesh = networkMeshes.at(i); const FBXMesh& mesh = geometry.meshes.at(i); - //const_cast(networkMesh.indexBuffer).bind(); - GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, const_cast(networkMesh.indexBuffer).bufferId()); - + batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); int vertexCount = mesh.vertices.size(); if (vertexCount == 0) { // sanity check @@ -1846,9 +1847,6 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl continue; // skip this mesh } } - - //const_cast(networkMesh.vertexBuffer).bind(); - GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, const_cast(networkMesh.vertexBuffer).bufferId()); GLBATCH(glPushMatrix)(); //Application::getInstance()->loadTranslatedViewMatrix(_translation); @@ -1861,59 +1859,24 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl if (state.clusterMatrices.size() > 1) { GLBATCH(glUniformMatrix4fv)(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, (const float*)state.clusterMatrices.constData()); - int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + - mesh.texCoords.size() * sizeof(glm::vec2) + - (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); - //skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); - GLBATCH(glVertexAttribPointer)(skinLocations->clusterIndices, 4, GL_FLOAT, GL_TRUE, 0, - reinterpret_cast(offset)); - //skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, - // offset + vertexCount * sizeof(glm::vec4), 4); - GLBATCH(glVertexAttribPointer)(skinLocations->clusterWeights, 4, GL_FLOAT, GL_TRUE, 0, (const void*) (offset + vertexCount * sizeof(glm::vec4))); - //skinProgram->enableAttributeArray(skinLocations->clusterIndices); - GLBATCH(glEnableVertexAttribArray)(skinLocations->clusterIndices); - //skinProgram->enableAttributeArray(skinLocations->clusterWeights); - GLBATCH(glEnableVertexAttribArray)(skinLocations->clusterWeights); } else { GLBATCH(glMultMatrixf)((const GLfloat*)&state.clusterMatrices[0]); } if (mesh.blendshapes.isEmpty()) { - if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { - //activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); - GLBATCH(glVertexAttribPointer)(activeLocations->tangent, 3, GL_FLOAT, GL_TRUE, 0, (const void*)(vertexCount * 2 * sizeof(glm::vec3))); - //activeProgram->enableAttributeArray(activeLocations->tangent); - GLBATCH(glEnableVertexAttribArray)(activeLocations->tangent); - } - GLBATCH(glColorPointer)(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - mesh.tangents.size() * sizeof(glm::vec3))); - GLBATCH(glTexCoordPointer)(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - + batch.setInputFormat(networkMesh._vertexFormat); + batch.setInputStream(0, *networkMesh._vertexStream); } else { - if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { - //activeProgram->setAttributeBuffer(activeLocations->tangent, GL_FLOAT, 0, 3); - GLBATCH(glVertexAttribPointer)(activeLocations->tangent, 3, GL_FLOAT, GL_TRUE, 0, 0); - //activeProgram->enableAttributeArray(activeLocations->tangent); - GLBATCH(glEnableVertexAttribArray)(activeLocations->tangent); - } - GLBATCH(glColorPointer)(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); - GLBATCH(glTexCoordPointer)(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - // _blendedVertexBuffers[i].bind(); - GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, _blendedVertexBuffers[i].bufferId()); + batch.setInputFormat(networkMesh._vertexFormat); + batch.setInputBuffer(0, _blendedVertexBuffers[i], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _blendedVertexBuffers[i], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, *networkMesh._vertexStream); } - GLBATCH(glVertexPointer)(3, GL_FLOAT, 0, 0); - GLBATCH(glNormalPointer)(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); - - if (!mesh.colors.isEmpty()) { - GLBATCH(glEnableClientState)(GL_COLOR_ARRAY); - } else { + + if (mesh.colors.isEmpty()) { GLBATCH(glColor4f)(1.0f, 1.0f, 1.0f, 1.0f); } - if (!mesh.texCoords.isEmpty()) { - GLBATCH(glEnableClientState)(GL_TEXTURE_COORD_ARRAY); - } - + qint64 offset = 0; for (int j = 0; j < networkMesh.parts.size(); j++) { const NetworkMeshPart& networkPart = networkMesh.parts.at(j); @@ -1980,13 +1943,12 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl meshPartsRendered++; if (part.quadIndices.size() > 0) { - GLBATCH(glDrawRangeElements)(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); + batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); offset += part.quadIndices.size() * sizeof(int); } - + if (part.triangleIndices.size() > 0) { - GLBATCH(glDrawRangeElements)(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), - GL_UNSIGNED_INT, (void*)offset); + batch.drawIndexed(gpu::TRIANGLES, part.triangleIndices.size(), offset); offset += part.triangleIndices.size() * sizeof(int); } @@ -1997,39 +1959,23 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl args->_quadsRendered += part.quadIndices.size() / INDICES_PER_QUAD; } } - - if (!mesh.colors.isEmpty()) { - GLBATCH(glDisableClientState)(GL_COLOR_ARRAY); - } - if (!mesh.texCoords.isEmpty()) { - GLBATCH(glDisableClientState)(GL_TEXTURE_COORD_ARRAY); - } - + if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { GLBATCH(glActiveTexture)(GL_TEXTURE1); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glActiveTexture)(GL_TEXTURE0); - - // activeProgram->disableAttributeArray(activeLocations->tangent); - GLBATCH(glDisableVertexAttribArray)(activeLocations->tangent); } - + if (specularTextureUnit) { GLBATCH(glActiveTexture)(specularTextureUnit); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glActiveTexture)(GL_TEXTURE0); } - - if (state.clusterMatrices.size() > 1) { - // skinProgram->disableAttributeArray(skinLocations->clusterIndices); - GLBATCH(glDisableVertexAttribArray)(skinLocations->clusterIndices); - // skinProgram->disableAttributeArray(skinLocations->clusterWeights); - GLBATCH(glDisableVertexAttribArray)(skinLocations->clusterWeights); - } + GLBATCH(glPopMatrix)(); } - //activeProgram->release(); + GLBATCH(glUseProgram)(0); return meshPartsRendered; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 9892a12c9d..34d6a34c06 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -40,6 +40,8 @@ namespace gpu { class Batch; } +#include "gpu/Stream.h" + /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { Q_OBJECT @@ -279,8 +281,8 @@ private: QVector _blendshapeCoefficients; QUrl _url; - - QVector _blendedVertexBuffers; + + gpu::Buffers _blendedVertexBuffers; QVector > > _dilatedTextures; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 4d2c710be7..e0aa1f3020 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -984,7 +984,9 @@ void ApplicationOverlay::renderAudioMeter() { const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP; int audioMeterY; - if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { + bool boxed = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && + !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror); + if (boxed) { audioMeterY = MIRROR_VIEW_HEIGHT + AUDIO_METER_GAP + MUTE_ICON_PADDING; } else { audioMeterY = AUDIO_METER_GAP + MUTE_ICON_PADDING; @@ -1022,9 +1024,7 @@ void ApplicationOverlay::renderAudioMeter() { renderCollisionOverlay(glWidget->width(), glWidget->height(), magnitude, 1.0f); } - audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, - audioMeterY, - Menu::getInstance()->isOptionChecked(MenuOption::Mirror)); + audio->renderToolBox(MIRROR_VIEW_LEFT_PADDING + AUDIO_METER_GAP, audioMeterY, boxed); audio->renderScope(glWidget->width(), glWidget->height()); diff --git a/interface/src/ui/RearMirrorTools.cpp b/interface/src/ui/RearMirrorTools.cpp index f4b0bb82b5..fb6ef63647 100644 --- a/interface/src/ui/RearMirrorTools.cpp +++ b/interface/src/ui/RearMirrorTools.cpp @@ -156,6 +156,9 @@ void RearMirrorTools::displayIcon(QRect bounds, QRect iconBounds, GLuint texture } glEnd(); glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 0db1ab5f00..fed041d6ea 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -25,6 +25,7 @@ #include #include "gpu/GLBackend.h" +#include "gpu/Stream.h" // the width/height of the cached glyph textures @@ -128,16 +129,16 @@ int TextRenderer::draw(int x, int y, const char* str) { leftBottom.x, rightTop.y, ls, tt, }; const int NUM_COLOR_SCALARS_PER_GLYPH = 4; - unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; + int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; gpu::Buffer::Size offset = sizeof(vertexBuffer) * _numGlyphsBatched; gpu::Buffer::Size colorOffset = sizeof(colorBuffer) * _numGlyphsBatched; - if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { - _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer->getSize()) { + _glyphsBuffer->append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer->append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); } else { - _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + _glyphsBuffer->setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer->setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); } _numGlyphsBatched++; @@ -178,9 +179,22 @@ TextRenderer::TextRenderer(const Properties& properties) : _y(IMAGE_SIZE), _rowHeight(0), _color(properties.color), - _glyphsBuffer(), + _glyphsBuffer(new gpu::Buffer()), + _glyphsColorBuffer(new gpu::Buffer()), + _glyphsStreamFormat(new gpu::Stream::Format()), + _glyphsStream(new gpu::BufferStream()), _numGlyphsBatched(0) { + _glyphsStreamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::POS_XYZ), 0); + const int NUM_POS_COORDS = 2; + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + _glyphsStreamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); + + _glyphsStreamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA)); + + _glyphsStream->addBuffer(_glyphsBuffer, 0, _glyphsStreamFormat->getChannels().at(0)._stride); + _glyphsStream->addBuffer(_glyphsColorBuffer, 0, _glyphsStreamFormat->getChannels().at(1)._stride); + _font.setKerning(false); } @@ -295,30 +309,17 @@ void TextRenderer::drawBatch() { glLoadIdentity(); */ + gpu::Batch batch; + glEnable(GL_TEXTURE_2D); // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good glBindTexture(GL_TEXTURE_2D, _currentTextureID); - GLuint vbo = gpu::GLBackend::getBufferID(_glyphsBuffer); - GLuint colorvbo = gpu::GLBackend::getBufferID(_glyphsColorBuffer); + batch.setInputFormat(_glyphsStreamFormat); + batch.setInputStream(0, *_glyphsStream); + batch.draw(gpu::QUADS, _numGlyphsBatched * 4, 0); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - const int NUM_POS_COORDS = 2; - const int NUM_TEX_COORDS = 2; - const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); - - glBindBuffer(GL_ARRAY_BUFFER, colorvbo); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); - - glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + gpu::GLBackend::renderBatch(batch); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index 1953dda422..bcb0ce8890 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -20,6 +20,8 @@ #include #include "gpu/Resource.h" +#include "gpu/Stream.h" + #include "InterfaceConfig.h" @@ -105,8 +107,10 @@ private: QColor _color; // Graphics Buffer containing the current accumulated glyphs to render - gpu::Buffer _glyphsBuffer; - gpu::Buffer _glyphsColorBuffer; + gpu::BufferPointer _glyphsBuffer; + gpu::BufferPointer _glyphsColorBuffer; + gpu::Stream::FormatPointer _glyphsStreamFormat; + gpu::BufferStreamPointer _glyphsStream; int _numGlyphsBatched; static QHash _instances; diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index f950eefcbd..1ee71ee32d 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -14,6 +14,8 @@ #include +#include "AudioInjectorOptions.h" + class AbstractAudioInterface : public QObject { Q_OBJECT public: @@ -22,7 +24,7 @@ public: virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen) = 0; virtual void startDrumSound(float volume, float frequency, float duration, float decay) = 0; public slots: - virtual void handleAudioByteArray(const QByteArray& audioByteArray) = 0; + virtual void handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& options) = 0; }; Q_DECLARE_METATYPE(AbstractAudioInterface*) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 87de9ede8b..45e1eb57e1 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -26,6 +26,7 @@ AudioInjector::AudioInjector(QObject* parent) : _sound(NULL), _options(), _shouldStop(false), + _loudness(0.0f), _isFinished(false), _currentSendPosition(0) { @@ -35,6 +36,7 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _sound(sound), _options(injectorOptions), _shouldStop(false), + _loudness(0.0f), _isFinished(false), _currentSendPosition(0) { @@ -44,6 +46,10 @@ void AudioInjector::setOptions(AudioInjectorOptions& options) { _options = options; } +float AudioInjector::getLoudness() { + return _loudness; +} + const uchar MAX_INJECTOR_VOLUME = 0xFF; void AudioInjector::injectAudio() { @@ -56,14 +62,6 @@ void AudioInjector::injectAudio() { // make sure we actually have samples downloaded to inject if (soundByteArray.size()) { - // give our sample byte array to the local audio interface, if we have it, so it can be handled locally - if (_options.getLoopbackAudioInterface()) { - // assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly - QMetaObject::invokeMethod(_options.getLoopbackAudioInterface(), "handleAudioByteArray", - Qt::AutoConnection, - Q_ARG(QByteArray, soundByteArray)); - - } // setup the packet for injected audio QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio); @@ -80,7 +78,7 @@ void AudioInjector::injectAudio() { packetStream << _options.isStereo(); // pack the flag for loopback - uchar loopbackFlag = (uchar) (!_options.getLoopbackAudioInterface()); + uchar loopbackFlag = (uchar) true; packetStream << loopbackFlag; // pack the position for injected audio @@ -102,6 +100,8 @@ void AudioInjector::injectAudio() { quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume(); packetStream << volume; + packetStream << _options.ignorePenumbra(); + QElapsedTimer timer; timer.start(); int nextFrame = 0; @@ -115,6 +115,15 @@ void AudioInjector::injectAudio() { int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - _currentSendPosition); + + // Measure the loudness of this frame + _loudness = 0.0f; + for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { + _loudness += abs(*reinterpret_cast(soundByteArray.data() + _currentSendPosition + i)) / + (MAX_SAMPLE_VALUE / 2.0f); + } + _loudness /= (float)(bytesToCopy / sizeof(int16_t)); + memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.getPosition(), sizeof(_options.getPosition())); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 1f070a77b0..68acd3b887 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -34,14 +34,18 @@ public slots: void stop() { _shouldStop = true; } void setOptions(AudioInjectorOptions& options); void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } + float getLoudness(); + signals: void finished(); private: Sound* _sound; AudioInjectorOptions _options; bool _shouldStop; + float _loudness; bool _isFinished; int _currentSendPosition; + }; Q_DECLARE_METATYPE(AudioInjector*) diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index eab4eb3848..670bea2fef 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -18,7 +18,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) : _loop(false), _orientation(glm::vec3(0.0f, 0.0f, 0.0f)), _isStereo(false), - _loopbackAudioInterface(NULL) + _ignorePenumbra(false) { } @@ -28,7 +28,7 @@ AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) { _loop = other._loop; _orientation = other._orientation; _isStereo = other._isStereo; - _loopbackAudioInterface = other._loopbackAudioInterface; + _ignorePenumbra = other._ignorePenumbra; } void AudioInjectorOptions::operator=(const AudioInjectorOptions& other) { @@ -37,5 +37,5 @@ void AudioInjectorOptions::operator=(const AudioInjectorOptions& other) { _loop = other._loop; _orientation = other._orientation; _isStereo = other._isStereo; - _loopbackAudioInterface = other._loopbackAudioInterface; + _ignorePenumbra = other._ignorePenumbra; } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 66e8f1550b..1ccd85be7e 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -19,8 +19,6 @@ #include -#include "AbstractAudioInterface.h" - class AudioInjectorOptions : public QObject { Q_OBJECT @@ -29,6 +27,7 @@ class AudioInjectorOptions : public QObject { Q_PROPERTY(float volume READ getVolume WRITE setVolume) Q_PROPERTY(bool loop READ getLoop WRITE setLoop) Q_PROPERTY(bool isStereo READ isStereo WRITE setIsStereo) + Q_PROPERTY(bool ignorePenumbra READ ignorePenumbra WRITE setIgnorePenumbra) public: AudioInjectorOptions(QObject* parent = 0); AudioInjectorOptions(const AudioInjectorOptions& other); @@ -49,16 +48,18 @@ public: const bool isStereo() const { return _isStereo; } void setIsStereo(const bool isStereo) { _isStereo = isStereo; } - AbstractAudioInterface* getLoopbackAudioInterface() const { return _loopbackAudioInterface; } - void setLoopbackAudioInterface(AbstractAudioInterface* loopbackAudioInterface) - { _loopbackAudioInterface = loopbackAudioInterface; } + const bool ignorePenumbra() const {return _ignorePenumbra; } + void setIgnorePenumbra(bool ignorePenumbra) { _ignorePenumbra = ignorePenumbra; } + private: glm::vec3 _position; float _volume; bool _loop; glm::quat _orientation; bool _isStereo; - AbstractAudioInterface* _loopbackAudioInterface; + bool _ignorePenumbra; }; +Q_DECLARE_METATYPE(AudioInjectorOptions) + #endif // hifi_AudioInjectorOptions_h diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index f78bdb0614..33afcdb095 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -16,6 +16,12 @@ AudioScriptingInterface& AudioScriptingInterface::getInstance() { return staticInstance; } +AudioScriptingInterface::AudioScriptingInterface() : + _localLoopbackInterface(NULL) +{ + qRegisterMetaType("AudioInjectorOptions"); +} + void AudioScriptingInterface::stopAllInjectors() { QList >::iterator injector = _activeInjectors.begin(); while (injector != _activeInjectors.end()) { @@ -31,6 +37,18 @@ void AudioScriptingInterface::stopAllInjectors() { } } +void AudioScriptingInterface::playLocalSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { + if (sound->isStereo()) { + const_cast(injectorOptions)->setIsStereo(true); + } + + // assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly + QMetaObject::invokeMethod(_localLoopbackInterface, "handleAudioByteArray", + Qt::AutoConnection, + Q_ARG(QByteArray, sound->getByteArray()), + Q_ARG(const AudioInjectorOptions&, *injectorOptions)); +} + AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { if (sound->isStereo()) { @@ -68,6 +86,14 @@ bool AudioScriptingInterface::isInjectorPlaying(AudioInjector* injector) { return (injector != NULL); } +float AudioScriptingInterface::getLoudness(AudioInjector* injector) { + if (injector) { + return injector->getLoudness(); + } else { + return 0.0f; + } +} + void AudioScriptingInterface::injectorStopped() { _activeInjectors.removeAll(QPointer(reinterpret_cast(sender()))); -} \ No newline at end of file +} diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h index 6179111f59..5cd0f9e99a 100644 --- a/libraries/audio/src/AudioScriptingInterface.h +++ b/libraries/audio/src/AudioScriptingInterface.h @@ -14,6 +14,7 @@ #include +#include "AbstractAudioInterface.h" #include "AudioInjector.h" #include "Sound.h" @@ -25,16 +26,23 @@ public: static AudioScriptingInterface& getInstance(); void stopAllInjectors(); + + void setLocalLoopbackInterface(AbstractAudioInterface* audioInterface) { _localLoopbackInterface = audioInterface; } public slots: + + static float getLoudness(AudioInjector* injector); + + void playLocalSound(Sound *sound, const AudioInjectorOptions* injectorOptions = NULL); AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL); + void stopInjector(AudioInjector* injector); bool isInjectorPlaying(AudioInjector* injector); void injectorStopped(); private: - AudioScriptingInterface() {}; + AudioScriptingInterface(); QList< QPointer > _activeInjectors; - + AbstractAudioInterface* _localLoopbackInterface; }; #endif // hifi_AudioScriptingInterface_h diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index ad2b7266ed..4621de2899 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -18,7 +18,7 @@ #include "SequenceNumberStats.h" #include "AudioStreamStats.h" #include "PacketHeaders.h" -#include "StdDev.h" +#include "StDev.h" #include "TimeWeightedAvg.h" // This adds some number of frames to the desired jitter buffer frames target we use when we're dropping frames. @@ -113,10 +113,8 @@ public: bool lastPopSucceeded() const { return _lastPopSucceeded; }; const AudioRingBuffer::ConstIterator& getLastPopOutput() const { return _lastPopOutput; } - void setToStarved(); - void setSettings(const Settings& settings); void setMaxFramesOverDesired(int maxFramesOverDesired) { _maxFramesOverDesired = maxFramesOverDesired; } @@ -163,7 +161,7 @@ public: float getWetLevel() const { return _wetLevel; } void setReverb(float reverbTime, float wetLevel); void clearReverb() { _hasReverb = false; } - + public slots: /// This function should be called every second for all the stats to function properly. If dynamic jitter buffers /// is enabled, those stats are used to calculate _desiredJitterBufferFrames. diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index 873b32cc40..2811e857db 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -63,7 +63,9 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, quint8 attenuationByte = 0; packetStream >> attenuationByte; _attenuationRatio = attenuationByte / (float)MAX_INJECTOR_VOLUME; - + + packetStream >> _ignorePenumbra; + int numAudioBytes = packetAfterSeqNum.size() - packetStream.device()->pos(); numAudioSamples = numAudioBytes / sizeof(int16_t); diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index 6334ac0efb..c315c04026 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -29,8 +29,12 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b _orientation(0.0f, 0.0f, 0.0f, 0.0f), _shouldLoopbackForNode(false), _isStereo(isStereo), + _ignorePenumbra(false), _lastPopOutputTrailingLoudness(0.0f), - _lastPopOutputLoudness(0.0f) + _lastPopOutputLoudness(0.0f), + _quietestTrailingFrameLoudness(std::numeric_limits::max()), + _quietestFrameLoudness(0.0f), + _frameCounter(0) { } @@ -42,8 +46,9 @@ void PositionalAudioStream::resetStats() { void PositionalAudioStream::updateLastPopOutputLoudnessAndTrailingLoudness() { _lastPopOutputLoudness = _ringBuffer.getFrameLoudness(_lastPopOutput); - const int TRAILING_AVERAGE_FRAMES = 100; - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const int TRAILING_MUTE_THRESHOLD_FRAMES = 400; + const int TRAILING_LOUDNESS_FRAMES = 200; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_LOUDNESS_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; const float LOUDNESS_EPSILON = 0.000001f; @@ -56,6 +61,15 @@ void PositionalAudioStream::updateLastPopOutputLoudnessAndTrailingLoudness() { _lastPopOutputTrailingLoudness = 0; } } + if (_frameCounter++ == TRAILING_MUTE_THRESHOLD_FRAMES) { + _quietestFrameLoudness = _quietestTrailingFrameLoudness; + _frameCounter = 0; + _quietestTrailingFrameLoudness = std::numeric_limits::max(); + + } + if (_lastPopOutputLoudness < _quietestTrailingFrameLoudness) { + _quietestTrailingFrameLoudness = _lastPopOutputLoudness; + } } int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteArray) { diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index 6ac189352b..e641791ef5 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -36,12 +36,15 @@ public: void updateLastPopOutputLoudnessAndTrailingLoudness(); float getLastPopOutputTrailingLoudness() const { return _lastPopOutputTrailingLoudness; } float getLastPopOutputLoudness() const { return _lastPopOutputLoudness; } + float getQuietestFrameLoudness() const { return _quietestFrameLoudness; } bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; } bool isStereo() const { return _isStereo; } + bool ignorePenumbraFilter() { return _ignorePenumbra; } PositionalAudioStream::Type getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } + protected: // disallow copying of PositionalAudioStream objects @@ -57,9 +60,14 @@ protected: bool _shouldLoopbackForNode; bool _isStereo; + // Ignore penumbra filter + bool _ignorePenumbra; float _lastPopOutputTrailingLoudness; float _lastPopOutputLoudness; + float _quietestTrailingFrameLoudness; + float _quietestFrameLoudness; + int _frameCounter; }; #endif // hifi_PositionalAudioStream_h diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 47d1b04421..56f820ab97 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -297,7 +297,7 @@ void Player::play() { _injector->setOptions(_options); } -void Player::setCurrentFrame(unsigned int currentFrame) { +void Player::setCurrentFrame(int currentFrame) { if (_recording && currentFrame >= _recording->getFrameNumber()) { stopPlaying(); return; @@ -314,7 +314,7 @@ void Player::setCurrentFrame(unsigned int currentFrame) { } } -void Player::setCurrentTime(unsigned int currentTime) { +void Player::setCurrentTime(int currentTime) { if (currentTime >= _recording->getLength()) { stopPlaying(); return; @@ -393,7 +393,7 @@ bool Player::computeCurrentFrame() { _currentFrame = 0; } - quint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength()); + qint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength()); while(_currentFrame >= 0 && _recording->getFrameTimestamp(_currentFrame) > elapsed) { --_currentFrame; diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 043fcc4a25..7ed76fa495 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -44,8 +44,8 @@ public slots: void loadRecording(RecordingPointer recording); void play(); - void setCurrentFrame(unsigned int currentFrame); - void setCurrentTime(unsigned int currentTime); + void setCurrentFrame(int currentFrame); + void setCurrentTime(int currentTime); void setVolume(float volume); void setAudioOffset(int audioOffset); @@ -87,4 +87,4 @@ private: bool _useSkeletonURL; }; -#endif // hifi_Player_h \ No newline at end of file +#endif // hifi_Player_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9d32b17dfb..a65d7519a5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -60,6 +60,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _lastUpdated = 0; _created = 0; // TODO: when do we actually want to make this "now" + _changedOnServer = 0; _position = glm::vec3(0,0,0); _rotation = DEFAULT_ROTATION; @@ -91,6 +92,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) { #ifdef USE_BULLET_PHYSICS _motionState = NULL; #endif // USE_BULLET_PHYSICS + _changedOnServer = 0; initFromEntityItemID(entityItemID); } @@ -104,6 +106,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert #ifdef USE_BULLET_PHYSICS _motionState = NULL; #endif // USE_BULLET_PHYSICS + _changedOnServer = 0; initFromEntityItemID(entityItemID); setProperties(properties, true); // force copy } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 205dc2f165..d24e12434b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -74,10 +74,14 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } - void setLastEdited(quint64 lastEdited) { _lastEdited = _lastUpdated = lastEdited; } + void setLastEdited(quint64 lastEdited) + { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } float getEditedAgo() const /// Elapsed seconds since this entity was last edited { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } + void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); } + quint64 getLastChangedOnServer() const { return _changedOnServer; } + // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; @@ -276,6 +280,7 @@ protected: quint64 _lastEditedFromRemote; // this is the last time we received and edit from the server quint64 _lastEditedFromRemoteInRemoteTime; // time in server time space the last time we received and edit from the server quint64 _created; + quint64 _changedOnServer; glm::vec3 _position; glm::vec3 _dimensions; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 11661b02f4..cb6d9c7bcd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -243,6 +243,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons boundingBox.setProperty("dimensions", boundingBoxDimensions); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(boundingBox, boundingBox); // gettable, but not settable + QString textureNamesList = _textureNames.join(",\n"); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(originalTextures, textureNamesList); // gettable, but not settable + return properties; } @@ -693,6 +696,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt void EntityItemProperties::markAllChanged() { _positionChanged = true; + _dimensionsChanged = true; _rotationChanged = true; _massChanged = true; _velocityChanged = true; @@ -713,6 +717,8 @@ void EntityItemProperties::markAllChanged() { _glowLevelChanged = true; _localRenderAlphaChanged = true; _isSpotlightChanged = true; + _ignoreForCollisionsChanged = true; + _collisionsWillMoveChanged = true; _diffuseColorChanged = true; _ambientColorChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 92fa344286..6b22e8cba9 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -285,6 +285,9 @@ public: const QString& getTextures() const { return _textures; } void setTextures(const QString& value) { _textures = value; _texturesChanged = true; } + + const QStringList& getTextureNames() const { return _textureNames; } + void setTextureNames(const QStringList& value) { _textureNames = value; } void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } @@ -380,6 +383,7 @@ private: // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. QVector _sittingPoints; + QStringList _textureNames; glm::vec3 _naturalDimensions; }; Q_DECLARE_METATYPE(EntityItemProperties); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 61dbbda698..199bd92030 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -185,6 +185,8 @@ void EntityTree::trackDeletedEntity(const EntityItemID& entityID) { } void EntityTree::deleteEntity(const EntityItemID& entityID) { + emit deletingEntity(entityID); + // NOTE: callers must lock the tree before using this method DeleteEntityOperator theOperator(this, entityID); recurseTreeWithOperator(&theOperator); @@ -197,6 +199,7 @@ void EntityTree::deleteEntities(QSet entityIDs) { foreach(const EntityItemID& entityID, entityIDs) { // tell our delete operator about this entityID theOperator.addEntityIDToDeleteList(entityID); + emit deletingEntity(entityID); } recurseTreeWithOperator(&theOperator); @@ -497,6 +500,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char // if the entityItem exists, then update it if (existingEntity) { updateEntity(entityItemID, properties); + existingEntity->markAsChangedOnServer(); } else { qDebug() << "User attempted to edit an unknown entity. ID:" << entityItemID; } @@ -505,6 +509,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char entityItemID = assignEntityID(entityItemID); EntityItem* newEntity = addEntity(entityItemID, properties); if (newEntity) { + newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); } } @@ -1069,6 +1074,24 @@ void EntityTree::dumpTree() { recurseTreeWithOperator(&theOperator); } +class PruneOperator : public RecurseOctreeOperator { +public: + virtual bool preRecursion(OctreeElement* element) { return true; } + virtual bool postRecursion(OctreeElement* element); +}; + +bool PruneOperator::postRecursion(OctreeElement* element) { + EntityTreeElement* entityTreeElement = static_cast(element); + entityTreeElement->pruneChildren(); + return true; +} + +void EntityTree::pruneTree() { + // First, look for the existing entity in the tree.. + PruneOperator theOperator; + recurseTreeWithOperator(&theOperator); +} + void EntityTree::sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z) { SendEntitiesOperationArgs args; args.packetSender = packetSender; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 634a04a688..8d1acc0d01 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -131,6 +131,7 @@ public: void resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); void debugDumpMap(); virtual void dumpTree(); + virtual void pruneTree(); void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z); @@ -140,6 +141,9 @@ public: void trackDeletedEntity(const EntityItemID& entityID); QList& getMovingEntities() { return _movingEntities; } + +signals: + void deletingEntity(const EntityItemID& entityID); private: diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index da7d5db226..079fb1bba7 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -238,7 +238,7 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { - + OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best... // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element @@ -289,7 +289,8 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData for (uint16_t i = 0; i < _entityItems->size(); i++) { EntityItem* entity = (*_entityItems)[i]; bool includeThisEntity = true; - if (!params.forceSendScene && entity->getLastEdited() < params.lastViewFrustumSent) { + + if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) { includeThisEntity = false; } @@ -324,7 +325,6 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData if (successAppendEntityCount) { foreach (uint16_t i, indexesOfEntitiesToInclude) { EntityItem* entity = (*_entityItems)[i]; - LevelDetails entityLevel = packetData->startLevel(); OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData, params, entityTreeElementExtraEncodeData); @@ -812,11 +812,20 @@ bool EntityTreeElement::pruneChildren() { void EntityTreeElement::debugDump() { qDebug() << "EntityTreeElement..."; - qDebug() << "entity count:" << _entityItems->size(); - qDebug() << "cube:" << getAACube(); - for (uint16_t i = 0; i < _entityItems->size(); i++) { - EntityItem* entity = (*_entityItems)[i]; - entity->debugDump(); + AACube temp = getAACube(); + temp.scale((float)TREE_SCALE); + qDebug() << " cube:" << temp; + qDebug() << " has child elements:" << getChildCount(); + if (_entityItems->size()) { + qDebug() << " has entities:" << _entityItems->size(); + qDebug() << "--------------------------------------------------"; + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + entity->debugDump(); + } + qDebug() << "--------------------------------------------------"; + } else { + qDebug() << " NO entities!"; } } diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 043f0621bb..0c18c82962 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -209,8 +209,15 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) { if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) { return true; } else { - qDebug() << "Packet hash mismatch on" << checkType << "- Sender" + static QMultiMap hashDebugSuppressMap; + + QUuid senderUUID = uuidFromPacketHeader(packet); + if (!hashDebugSuppressMap.contains(senderUUID, checkType)) { + qDebug() << "Packet hash mismatch on" << checkType << "- Sender" << uuidFromPacketHeader(packet); + + hashDebugSuppressMap.insert(senderUUID, checkType); + } } } else { static QString repeatedMessage diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 73671295df..b5cf84ee28 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -54,6 +54,8 @@ PacketVersion versionForPacketType(PacketType type) { return 4; case PacketTypeMixedAudio: return 1; + case PacketTypeInjectAudio: + return 1; case PacketTypeAvatarData: return 3; case PacketTypeAvatarIdentity: diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d9520a4e68..496839fdcd 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -123,6 +123,8 @@ public: void setCache(ResourceCache* cache) { _cache = cache; } Q_INVOKABLE void allReferencesCleared(); + + const QUrl& getURL() const { return _url; } signals: diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index ceda91a441..596dd7536c 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -547,11 +547,12 @@ void Octree::deleteOctalCodeFromTreeRecursion(OctreeElement* element, void* extr void Octree::eraseAllOctreeElements(bool createNewRoot) { delete _rootElement; // this will recurse and delete all children + _rootElement = NULL; + if (createNewRoot) { _rootElement = createNewElement(); - } else { - _rootElement = NULL; } + _isDirty = true; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index da0e483e1e..42cbb3c38d 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -352,6 +352,7 @@ public: void setIsClient(bool isClient) { _isServer = !isClient; } virtual void dumpTree() { }; + virtual void pruneTree() { }; signals: void importSize(float x, float y, float z); diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 65308f906f..df52e85c5e 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -246,9 +246,7 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned ch // But we can't really do that with a packed message, since each edit message could be destined // for a different server... So we need to actually manage multiple queued packets... one // for each server - _packetsQueueLock.lock(); - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { // only send to the NodeTypes that are getMyNodeType() if (node->getActiveSocket() && node->getType() == getMyNodeType()) { @@ -277,12 +275,12 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, unsigned ch if ((type != packetBuffer._currentType && packetBuffer._currentSize > 0) || (packetBuffer._currentSize + length >= (size_t)_maxPacketSize)) { releaseQueuedPacket(packetBuffer); - initializePacket(packetBuffer, type); + initializePacket(packetBuffer, type, node->getClockSkewUsec()); } // If the buffer is empty and not correctly initialized for our type... if (type != packetBuffer._currentType && packetBuffer._currentSize == 0) { - initializePacket(packetBuffer, type); + initializePacket(packetBuffer, type, node->getClockSkewUsec()); } // This is really the first time we know which server/node this particular edit message @@ -330,14 +328,14 @@ void OctreeEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) _releaseQueuedPacketMutex.unlock(); } -void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type) { +void OctreeEditPacketSender::initializePacket(EditPacketBuffer& packetBuffer, PacketType type, int nodeClockSkew) { packetBuffer._currentSize = populatePacketHeader(reinterpret_cast(&packetBuffer._currentBuffer[0]), type); // skip over sequence number for now; will be packed when packet is ready to be sent out packetBuffer._currentSize += sizeof(quint16); // pack in timestamp - quint64 now = usecTimestampNow(); + quint64 now = usecTimestampNow() + nodeClockSkew; quint64* timeAt = (quint64*)&packetBuffer._currentBuffer[packetBuffer._currentSize]; *timeAt = now; packetBuffer._currentSize += sizeof(quint64); // nudge past timestamp diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 9ba6a0243c..79b7a43ac4 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -101,7 +101,7 @@ protected: void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, size_t length, qint64 satoshiCost = 0); void queuePendingPacketToNodes(PacketType type, unsigned char* buffer, size_t length, qint64 satoshiCost = 0); void queuePacketToNodes(unsigned char* buffer, size_t length, qint64 satoshiCost = 0); - void initializePacket(EditPacketBuffer& packetBuffer, PacketType type); + void initializePacket(EditPacketBuffer& packetBuffer, PacketType type, int nodeClockSkew); void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet void processPreServerExistsPackets(); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 271e885d17..a0a331beb3 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -68,7 +68,8 @@ void OctreeElement::init(unsigned char * octalCode) { // set up the _children union _childBitmask = 0; _childrenExternal = false; - + + #ifdef BLENDED_UNION_CHILDREN _children.external = NULL; _singleChildrenCount++; @@ -660,6 +661,11 @@ void OctreeElement::deleteAllChildren() { delete childAt; } } + + if (_childrenExternal) { + // if the children_t union represents _children.external we need to delete it here + delete[] _children.external; + } #ifdef BLENDED_UNION_CHILDREN // now, reset our internal state and ANY and all population data @@ -757,6 +763,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { memset(_children.external, 0, sizeof(OctreeElement*) * NUMBER_OF_CHILDREN); _children.external[firstIndex] = previousChild; _children.external[childIndex] = child; + + _childrenExternal = true; _externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(OctreeElement*); @@ -764,7 +772,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { assert(!child); // we are removing a child, so this must be true! OctreeElement* previousFirstChild = _children.external[firstIndex]; OctreeElement* previousSecondChild = _children.external[secondIndex]; + delete[] _children.external; + _childrenExternal = false; + _externalChildrenMemoryUsage -= NUMBER_OF_CHILDREN * sizeof(OctreeElement*); if (childIndex == firstIndex) { _children.single = previousSecondChild; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 7613e2332b..406681c2c5 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -36,6 +36,7 @@ bool OctreePersistThread::process() { { PerformanceWarning warn(true, "Loading Octree File", true); persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData()); + _tree->pruneTree(); } _tree->unlock(); @@ -80,10 +81,14 @@ bool OctreePersistThread::process() { // check the dirty bit and persist here... _lastCheck = usecTimestampNow(); if (_tree->isDirty()) { - qDebug() << "saving Octrees to file " << _filename << "..."; + qDebug() << "pruning Octree before saving..."; + _tree->pruneTree(); + qDebug() << "DONE pruning Octree before saving..."; + + qDebug() << "saving Octree to file " << _filename << "..."; _tree->writeToSVOFile(_filename.toLocal8Bit().constData()); _tree->clearDirtyBit(); // tree is clean after saving - qDebug("DONE saving Octrees to file..."); + qDebug("DONE saving Octree to file..."); } } } diff --git a/libraries/shared/src/StdDev.cpp b/libraries/shared/src/StDev.cpp similarity index 87% rename from libraries/shared/src/StdDev.cpp rename to libraries/shared/src/StDev.cpp index 387b67cf4f..23afd12b98 100644 --- a/libraries/shared/src/StdDev.cpp +++ b/libraries/shared/src/StDev.cpp @@ -1,5 +1,5 @@ // -// StdDev.cpp +// StDev.cpp // libraries/shared/src // // Created by Philip Rosedale on 3/12/13. @@ -11,16 +11,18 @@ #include #include -#include "StdDev.h" +#include -const int NUM_SAMPLES = 1000; +#include "StDev.h" -StDev::StDev() { - _data = new float[NUM_SAMPLES]; - _sampleCount = 0; +StDev::StDev() : + _sampleCount(0) +{ + reset(); } void StDev::reset() { + memset(&_data, 0, sizeof(_data)); _sampleCount = 0; } diff --git a/libraries/shared/src/StdDev.h b/libraries/shared/src/StDev.h similarity index 78% rename from libraries/shared/src/StdDev.h rename to libraries/shared/src/StDev.h index 40148007a9..ab48aae6b5 100644 --- a/libraries/shared/src/StdDev.h +++ b/libraries/shared/src/StDev.h @@ -1,5 +1,5 @@ // -// StdDev.h +// StDev.h // libraries/shared/src // // Created by Philip Rosedale on 3/12/13. @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_StdDev_h -#define hifi_StdDev_h +#ifndef hifi_StDev_h +#define hifi_StDev_h + +const int NUM_SAMPLES = 1000; class StDev { public: @@ -21,8 +23,8 @@ public: float getStDev() const; int getSamples() const { return _sampleCount; } private: - float* _data; + float _data[NUM_SAMPLES]; int _sampleCount; }; -#endif // hifi_StdDev_h +#endif // hifi_StDev_h diff --git a/libraries/voxels/src/VoxelTreeElement.h b/libraries/voxels/src/VoxelTreeElement.h index b1bff7dcc9..0ea91d4657 100644 --- a/libraries/voxels/src/VoxelTreeElement.h +++ b/libraries/voxels/src/VoxelTreeElement.h @@ -79,8 +79,8 @@ public: protected: - uint32_t _glBufferIndex : 24, /// Client only, vbo index for this voxel if being rendered, 3 bytes - _voxelSystemIndex : 8; /// Client only, index to the VoxelSystem rendering this voxel, 1 bytes + uint32_t _glBufferIndex : 24; /// Client only, vbo index for this voxel if being rendered, 3 bytes + uint32_t _voxelSystemIndex : 8; /// Client only, index to the VoxelSystem rendering this voxel, 1 bytes // Support for _voxelSystemIndex, we use these static member variables to track the VoxelSystems that are // in use by various voxel nodes. We map the VoxelSystem pointers into an 1 byte key, this limits us to at diff --git a/tests/jitter/src/main.cpp b/tests/jitter/src/main.cpp index 985ce99530..baaa2d08d6 100644 --- a/tests/jitter/src/main.cpp +++ b/tests/jitter/src/main.cpp @@ -20,7 +20,7 @@ #include #include // for usecTimestampNow #include -#include +#include const quint64 MSEC_TO_USEC = 1000; const quint64 LARGE_STATS_TIME = 500; // we don't expect stats calculation to take more than this many usecs