mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-30 04:23:04 +02:00
merge upstream/master into andrew/inertia
Conflicts: libraries/entities/src/EntityItem.cpp
This commit is contained in:
commit
fa48da6c98
86 changed files with 8084 additions and 4420 deletions
assignment-client/src
audio
entities
octree
voxels
domain-server/resources
examples
interface
resources/shaders
src
libraries
audio/src
AbstractAudioInterface.hAudioInjector.cppAudioInjector.hAudioInjectorOptions.cppAudioInjectorOptions.hAudioScriptingInterface.cppAudioScriptingInterface.hInboundAudioStream.hInjectedAudioStream.cppPositionalAudioStream.cppPositionalAudioStream.h
avatars/src
entities/src
EntityItem.cppEntityItem.hEntityItemProperties.cppEntityItemProperties.hEntityTree.cppEntityTree.hEntityTreeElement.cpp
networking/src
octree/src
Octree.cppOctree.hOctreeEditPacketSender.cppOctreeEditPacketSender.hOctreeElement.cppOctreePersistThread.cpp
shared/src
voxels/src
tests/jitter/src
|
@ -48,7 +48,7 @@
|
|||
#include <OctreeConstants.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
#include <StDev.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#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<AudioMixerClientData*>(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<AudioMixerClientData*>(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<AudioMixerClientData*>(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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
164
examples/birdSongs.js
Normal file
164
examples/birdSongs.js
Normal file
|
@ -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]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
412
examples/libraries/walkApi.js
Normal file
412
examples/libraries/walkApi.js
Normal file
|
@ -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,
|
||||
}
|
||||
})();
|
225
examples/libraries/walkFilters.js
Normal file
225
examples/libraries/walkFilters.js
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
2690
examples/libraries/walkInterface.js
Normal file
2690
examples/libraries/walkInterface.js
Normal file
File diff suppressed because it is too large
Load diff
46
examples/lightExample.js
Normal file
46
examples/lightExample.js
Normal file
|
@ -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);
|
||||
});
|
6177
examples/walk.js
6177
examples/walk.js
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include <OctreeSceneStats.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PhysicsWorld.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
#include <StDev.h>
|
||||
#include <UUID.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -77,6 +77,9 @@ Audio::Audio(QObject* parent) :
|
|||
_isStereoInput(false),
|
||||
_averagedLatency(0.0),
|
||||
_lastInputLoudness(0),
|
||||
_inputFrameCounter(0),
|
||||
_quietestFrame(std::numeric_limits<float>::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<int16_t*>(inputByteArray.data()),
|
||||
reinterpret_cast<int16_t*>(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<int16_t*>(loopBackByteArray.data());
|
||||
int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
|
||||
updateGverbOptions();
|
||||
addReverb(loopbackSamples, numLoopbackSamples, _outputFormat);
|
||||
|
||||
if (!hasEcho) {
|
||||
int16_t* loopbackCopySamples = reinterpret_cast<int16_t*>(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<float>::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;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@
|
|||
#include <QByteArray>
|
||||
|
||||
#include <AbstractAudioInterface.h>
|
||||
#include <StdDev.h>
|
||||
#include <StDev.h>
|
||||
|
||||
#include "MixedProcessedAudioStream.h"
|
||||
#include "AudioEffectOptions.h"
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <StdDev.h>
|
||||
#include <StDev.h>
|
||||
|
||||
extern "C" {
|
||||
#include <gverb.h>
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -61,11 +61,13 @@ EntityTreeRenderer::~EntityTreeRenderer() {
|
|||
|
||||
void EntityTreeRenderer::clear() {
|
||||
OctreeRenderer::clear();
|
||||
_entityScripts.clear();
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::init() {
|
||||
OctreeRenderer::init();
|
||||
static_cast<EntityTree*>(_tree)->setFBXService(this);
|
||||
EntityTree* entityTree = static_cast<EntityTree*>(_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);
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,9 @@ signals:
|
|||
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
|
||||
public slots:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
|
||||
private:
|
||||
QList<Model*> _releasedModels;
|
||||
|
|
|
@ -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>& 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
//#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);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#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<ResourceCache> Resources;
|
||||
|
||||
template <typename T>
|
||||
class Cache {
|
||||
public:
|
||||
typedef QSharedPointer<T> Pointer;
|
||||
Pointer _pointer;
|
||||
Cache<T>(const Pointer& pointer) : _pointer(pointer) {}
|
||||
|
||||
class Vector {
|
||||
public:
|
||||
std::vector< Cache<T> > _pointers;
|
||||
|
||||
uint32 cache(const Pointer& pointer) {
|
||||
uint32 offset = _pointers.size();
|
||||
_pointers.push_back(Cache<T>(pointer));
|
||||
return offset;
|
||||
}
|
||||
|
||||
Pointer get(uint32 offset) {
|
||||
if (offset >= _pointers.size()) {
|
||||
return Pointer();
|
||||
}
|
||||
return (_pointers.data() + offset)->_pointer;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_pointers.clear();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
typedef Cache<Buffer>::Vector BufferCaches;
|
||||
typedef Cache<Stream::Format>::Vector StreamFormatCaches;
|
||||
|
||||
typedef unsigned char Byte;
|
||||
typedef std::vector<Byte> 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 <typename T>
|
||||
T* editResourcePointer(uint32 offset) {
|
||||
if (offset >= _resources.size()) {
|
||||
return 0;
|
||||
}
|
||||
return reinterpret_cast<T*>((_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:
|
||||
};
|
||||
|
|
147
interface/src/gpu/Format.h
Normal file
147
interface/src/gpu/Format.h
Normal file
|
@ -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 <assert.h>
|
||||
#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
|
|
@ -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<GLvoid*>(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<GLvoid*>(pointer));
|
||||
break;
|
||||
case Stream::NORMAL:
|
||||
glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||
break;
|
||||
case Stream::COLOR:
|
||||
glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||
break;
|
||||
case Stream::TEXCOORD:
|
||||
glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
|
||||
break;
|
||||
};
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
GLboolean isNormalized = attrib._element.isNormalized();
|
||||
glVertexAttribPointer(slot, count, type, isNormalized, stride,
|
||||
reinterpret_cast<GLvoid*>(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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/Batch.h"
|
||||
#include <bitset>
|
||||
|
||||
|
||||
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<MAX_NUM_INPUT_BUFFERS> InputBuffersState;
|
||||
InputBuffersState _inputBuffersState;
|
||||
Buffers _inputBuffers;
|
||||
Offsets _inputBufferOffsets;
|
||||
Offsets _inputBufferStrides;
|
||||
|
||||
BufferPointer _indexBuffer;
|
||||
Offset _indexBufferOffset;
|
||||
Type _indexBufferType;
|
||||
|
||||
typedef std::bitset<MAX_NUM_ATTRIBUTES> 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
|
||||
|
|
|
@ -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 <QDebug>
|
||||
|
@ -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;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include <assert.h>
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "gpu/Format.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
namespace gpu {
|
||||
|
||||
|
@ -142,6 +147,8 @@ protected:
|
|||
friend class Backend;
|
||||
};
|
||||
|
||||
typedef QSharedPointer<Buffer> BufferPointer;
|
||||
typedef std::vector< BufferPointer > Buffers;
|
||||
};
|
||||
|
||||
|
||||
|
|
48
interface/src/gpu/Stream.cpp
Normal file
48
interface/src/gpu/Stream.cpp
Normal file
|
@ -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);
|
||||
}
|
150
interface/src/gpu/Stream.h
Normal file
150
interface/src/gpu/Stream.h
Normal file
|
@ -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 <assert.h>
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "gpu/Resource.h"
|
||||
#include "gpu/Format.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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<Format> 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<BufferStream> BufferStreamPointer;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <AnimationCache.h>
|
||||
|
||||
#include "gpu/Stream.h"
|
||||
|
||||
class Model;
|
||||
class NetworkGeometry;
|
||||
class NetworkMesh;
|
||||
|
@ -110,6 +112,7 @@ public:
|
|||
virtual void clearLoadPriority(const QPointer<QObject>& 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<NetworkMeshPart> parts;
|
||||
|
||||
|
|
|
@ -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<float>::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<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& 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 QWeakPointer<NetworkGeomet
|
|||
if (mesh.blendshapes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
QOpenGLBuffer& buffer = _blendedVertexBuffers[i];
|
||||
buffer.bind();
|
||||
buffer.write(0, vertices.constData() + index, mesh.vertices.size() * sizeof(glm::vec3));
|
||||
buffer.write(mesh.vertices.size() * sizeof(glm::vec3), normals.constData() + index,
|
||||
mesh.normals.size() * sizeof(glm::vec3));
|
||||
buffer.release();
|
||||
|
||||
gpu::BufferPointer& buffer = _blendedVertexBuffers[i];
|
||||
buffer->setSubData(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<QOpenGLBuffer&>(networkMesh.indexBuffer).bind();
|
||||
GLBATCH(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, const_cast<QOpenGLBuffer&>(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<QOpenGLBuffer&>(networkMesh.vertexBuffer).bind();
|
||||
GLBATCH(glBindBuffer)(GL_ARRAY_BUFFER, const_cast<QOpenGLBuffer&>(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<const void*>(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;
|
||||
|
|
|
@ -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<float> _blendshapeCoefficients;
|
||||
|
||||
QUrl _url;
|
||||
|
||||
QVector<QOpenGLBuffer> _blendedVertexBuffers;
|
||||
|
||||
gpu::Buffers _blendedVertexBuffers;
|
||||
|
||||
QVector<QVector<QSharedPointer<Texture> > > _dilatedTextures;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <QVector>
|
||||
|
||||
#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<Properties, TextRenderer*> _instances;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#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*)
|
||||
|
|
|
@ -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<int16_t*>(soundByteArray.data() + _currentSendPosition + i)) /
|
||||
(MAX_SAMPLE_VALUE / 2.0f);
|
||||
}
|
||||
_loudness /= (float)(bytesToCopy / sizeof(int16_t));
|
||||
|
||||
memcpy(injectAudioPacket.data() + positionOptionOffset,
|
||||
&_options.getPosition(),
|
||||
sizeof(_options.getPosition()));
|
||||
|
|
|
@ -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*)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -16,6 +16,12 @@ AudioScriptingInterface& AudioScriptingInterface::getInstance() {
|
|||
return staticInstance;
|
||||
}
|
||||
|
||||
AudioScriptingInterface::AudioScriptingInterface() :
|
||||
_localLoopbackInterface(NULL)
|
||||
{
|
||||
qRegisterMetaType<AudioInjectorOptions>("AudioInjectorOptions");
|
||||
}
|
||||
|
||||
void AudioScriptingInterface::stopAllInjectors() {
|
||||
QList<QPointer<AudioInjector> >::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<AudioInjectorOptions*>(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<AudioInjector>(reinterpret_cast<AudioInjector*>(sender())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <qpointer.h>
|
||||
|
||||
#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<AudioInjector> > _activeInjectors;
|
||||
|
||||
AbstractAudioInterface* _localLoopbackInterface;
|
||||
};
|
||||
#endif // hifi_AudioScriptingInterface_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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<float>::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<float>::max();
|
||||
|
||||
}
|
||||
if (_lastPopOutputLoudness < _quietestTrailingFrameLoudness) {
|
||||
_quietestTrailingFrameLoudness = _lastPopOutputLoudness;
|
||||
}
|
||||
}
|
||||
|
||||
int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteArray) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
#endif // hifi_Player_h
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<SittingPoint> _sittingPoints;
|
||||
QStringList _textureNames;
|
||||
glm::vec3 _naturalDimensions;
|
||||
};
|
||||
Q_DECLARE_METATYPE(EntityItemProperties);
|
||||
|
|
|
@ -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<EntityItemID> 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<EntityTreeElement*>(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;
|
||||
|
|
|
@ -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<EntityItem*>& getMovingEntities() { return _movingEntities; }
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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!";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<QUuid, PacketType> 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
|
||||
|
|
|
@ -54,6 +54,8 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 4;
|
||||
case PacketTypeMixedAudio:
|
||||
return 1;
|
||||
case PacketTypeInjectAudio:
|
||||
return 1;
|
||||
case PacketTypeAvatarData:
|
||||
return 3;
|
||||
case PacketTypeAvatarIdentity:
|
||||
|
|
|
@ -123,6 +123,8 @@ public:
|
|||
void setCache(ResourceCache* cache) { _cache = cache; }
|
||||
|
||||
Q_INVOKABLE void allReferencesCleared();
|
||||
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
signals:
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<char*>(&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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// StdDev.cpp
|
||||
// StDev.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Philip Rosedale on 3/12/13.
|
||||
|
@ -11,16 +11,18 @@
|
|||
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include "StdDev.h"
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <SequenceNumberStats.h>
|
||||
#include <SharedUtil.h> // for usecTimestampNow
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <StdDev.h>
|
||||
#include <StDev.h>
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue