mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 03:13:09 +02:00
Merge branch 'master' into 20872
This commit is contained in:
commit
54b032f612
76 changed files with 4992 additions and 663 deletions
|
@ -19,6 +19,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -577,15 +578,15 @@ void AudioMixer::domainSettingsRequestComplete() {
|
||||||
void AudioMixer::broadcastMixes() {
|
void AudioMixer::broadcastMixes() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
int64_t nextFrame = 0;
|
auto nextFrameTimestamp = p_high_resolution_clock::now();
|
||||||
QElapsedTimer timer;
|
auto timeToSleep = std::chrono::microseconds(0);
|
||||||
timer.start();
|
|
||||||
|
|
||||||
int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
|
|
||||||
|
|
||||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||||
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
||||||
|
|
||||||
|
int currentFrame { 1 };
|
||||||
|
int numFramesPerSecond { (int) ceil(AudioConstants::NETWORK_FRAMES_PER_SEC) };
|
||||||
|
|
||||||
while (!_isFinished) {
|
while (!_isFinished) {
|
||||||
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
||||||
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
|
const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;
|
||||||
|
@ -595,12 +596,12 @@ void AudioMixer::broadcastMixes() {
|
||||||
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
|
||||||
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
||||||
|
|
||||||
if (usecToSleep < 0) {
|
if (timeToSleep.count() < 0) {
|
||||||
usecToSleep = 0;
|
timeToSleep = std::chrono::microseconds(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
_trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
|
||||||
+ (usecToSleep * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS);
|
+ (timeToSleep.count() * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS);
|
||||||
|
|
||||||
float lastCutoffRatio = _performanceThrottlingRatio;
|
float lastCutoffRatio = _performanceThrottlingRatio;
|
||||||
bool hasRatioChanged = false;
|
bool hasRatioChanged = false;
|
||||||
|
@ -694,10 +695,11 @@ void AudioMixer::broadcastMixes() {
|
||||||
nodeList->sendPacket(std::move(mixPacket), *node);
|
nodeList->sendPacket(std::move(mixPacket), *node);
|
||||||
nodeData->incrementOutgoingMixedAudioSequenceNumber();
|
nodeData->incrementOutgoingMixedAudioSequenceNumber();
|
||||||
|
|
||||||
static const int FRAMES_PER_SECOND = int(ceilf(1.0f / AudioConstants::NETWORK_FRAME_SECS));
|
|
||||||
|
|
||||||
// send an audio stream stats packet to the client approximately every second
|
// send an audio stream stats packet to the client approximately every second
|
||||||
if (nextFrame % FRAMES_PER_SECOND == 0) {
|
++currentFrame;
|
||||||
|
currentFrame %= numFramesPerSecond;
|
||||||
|
|
||||||
|
if (nodeData->shouldSendStats(currentFrame)) {
|
||||||
nodeData->sendAudioStreamStatsPackets(node);
|
nodeData->sendAudioStreamStatsPackets(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,11 +720,14 @@ void AudioMixer::broadcastMixes() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - (timer.nsecsElapsed() / 1000);
|
// push the next frame timestamp to when we should send the next
|
||||||
|
nextFrameTimestamp += std::chrono::microseconds(AudioConstants::NETWORK_FRAME_USECS);
|
||||||
|
|
||||||
if (usecToSleep > 0) {
|
// sleep as long as we need until next frame, if we can
|
||||||
usleep(usecToSleep);
|
auto now = p_high_resolution_clock::now();
|
||||||
}
|
timeToSleep = std::chrono::duration_cast<std::chrono::microseconds>(nextFrameTimestamp - now);
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(timeToSleep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
@ -26,7 +28,14 @@ AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) :
|
||||||
_outgoingMixedAudioSequenceNumber(0),
|
_outgoingMixedAudioSequenceNumber(0),
|
||||||
_downstreamAudioStreamStats()
|
_downstreamAudioStreamStats()
|
||||||
{
|
{
|
||||||
|
// of the ~94 blocks in a second of audio sent from the AudioMixer, pick a random one to send out a stats packet on
|
||||||
|
// this ensures we send out stats to this client around every second
|
||||||
|
// but do not send all of the stats packets out at the same time
|
||||||
|
std::random_device randomDevice;
|
||||||
|
std::mt19937 numberGenerator { randomDevice() };
|
||||||
|
std::uniform_int_distribution<> distribution { 1, (int) ceil(1.0f / AudioConstants::NETWORK_FRAME_SECS) };
|
||||||
|
|
||||||
|
_frameToSendStats = distribution(numberGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() {
|
AvatarAudioStream* AudioMixerClientData::getAvatarAudioStream() {
|
||||||
|
@ -180,6 +189,10 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioMixerClientData::shouldSendStats(int frameNumber) {
|
||||||
|
return frameNumber == _frameToSendStats;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) {
|
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) {
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
|
void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
|
||||||
quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
|
quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
|
||||||
|
|
||||||
|
// uses randomization to have the AudioMixer send a stats packet to this node around every second
|
||||||
|
bool shouldSendStats(int frameNumber);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||||
|
|
||||||
|
@ -72,6 +75,8 @@ private:
|
||||||
quint16 _outgoingMixedAudioSequenceNumber;
|
quint16 _outgoingMixedAudioSequenceNumber;
|
||||||
|
|
||||||
AudioStreamStats _downstreamAudioStreamStats;
|
AudioStreamStats _downstreamAudioStreamStats;
|
||||||
|
|
||||||
|
int _frameToSendStats { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AudioMixerClientData_h
|
#endif // hifi_AudioMixerClientData_h
|
||||||
|
|
|
@ -36,14 +36,7 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXE
|
||||||
|
|
||||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
ThreadedAssignment(message),
|
ThreadedAssignment(message),
|
||||||
_broadcastThread(),
|
_broadcastThread()
|
||||||
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
|
||||||
_trailingSleepRatio(1.0f),
|
|
||||||
_performanceThrottlingRatio(0.0f),
|
|
||||||
_sumListeners(0),
|
|
||||||
_numStatFrames(0),
|
|
||||||
_sumBillboardPackets(0),
|
|
||||||
_sumIdentityPackets(0)
|
|
||||||
{
|
{
|
||||||
// make sure we hear about node kills so we can tell the other nodes
|
// make sure we hear about node kills so we can tell the other nodes
|
||||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
||||||
|
@ -51,7 +44,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket");
|
packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket");
|
||||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "handleAvatarBillboardPacket");
|
|
||||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +58,18 @@ AvatarMixer::~AvatarMixer() {
|
||||||
|
|
||||||
// An 80% chance of sending a identity packet within a 5 second interval.
|
// An 80% chance of sending a identity packet within a 5 second interval.
|
||||||
// assuming 60 htz update rate.
|
// assuming 60 htz update rate.
|
||||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
|
const float IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
|
||||||
|
|
||||||
// NOTE: some additional optimizations to consider.
|
// NOTE: some additional optimizations to consider.
|
||||||
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
||||||
// if the avatar is not in view or in the keyhole.
|
// if the avatar is not in view or in the keyhole.
|
||||||
void AvatarMixer::broadcastAvatarData() {
|
void AvatarMixer::broadcastAvatarData() {
|
||||||
int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;
|
int idleTime = AVATAR_DATA_SEND_INTERVAL_MSECS;
|
||||||
|
|
||||||
|
if (_lastFrameTimestamp.time_since_epoch().count() > 0) {
|
||||||
|
auto idleDuration = p_high_resolution_clock::now() - _lastFrameTimestamp;
|
||||||
|
idleTime = std::chrono::duration_cast<std::chrono::microseconds>(idleDuration).count();
|
||||||
|
}
|
||||||
|
|
||||||
++_numStatFrames;
|
++_numStatFrames;
|
||||||
|
|
||||||
|
@ -245,32 +242,13 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we send out identity and billboard packets to and from new arrivals.
|
// make sure we send out identity packets to and from new arrivals.
|
||||||
bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
|
bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
|
||||||
|
|
||||||
// we will also force a send of billboard or identity packet
|
if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0
|
||||||
// if either has changed in the last frame
|
|
||||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
|
||||||
&& (forceSend
|
|
||||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
|
||||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
|
||||||
|
|
||||||
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
|
|
||||||
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
|
|
||||||
|
|
||||||
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
|
|
||||||
billboardPacket->write(rfcUUID);
|
|
||||||
billboardPacket->write(billboard);
|
|
||||||
|
|
||||||
nodeList->sendPacket(std::move(billboardPacket), *node);
|
|
||||||
|
|
||||||
++_sumBillboardPackets;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
|
||||||
&& (forceSend
|
&& (forceSend
|
||||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
|| distribution(generator) < IDENTITY_SEND_PROBABILITY)) {
|
||||||
|
|
||||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||||
|
|
||||||
|
@ -385,7 +363,7 @@ void AvatarMixer::broadcastAvatarData() {
|
||||||
otherAvatar.doneEncoding(false);
|
otherAvatar.doneEncoding(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
|
_lastFrameTimestamp = p_high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
||||||
|
@ -438,26 +416,12 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> mes
|
||||||
// parse the identity packet and update the change timestamp if appropriate
|
// parse the identity packet and update the change timestamp if appropriate
|
||||||
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
|
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
|
||||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
||||||
nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
nodeData->flagIdentityChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixer::handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
|
||||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(senderNode->getLinkedData());
|
|
||||||
if (nodeData) {
|
|
||||||
AvatarData& avatar = nodeData->getAvatar();
|
|
||||||
|
|
||||||
// parse the billboard packet and update the change timestamp if appropriate
|
|
||||||
if (avatar.hasBillboardChangedAfterParsing(message->getMessage())) {
|
|
||||||
QMutexLocker nodeDataLocker(&nodeData->getMutex());
|
|
||||||
nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message) {
|
void AvatarMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message) {
|
||||||
DependencyManager::get<NodeList>()->processKillNode(*message);
|
DependencyManager::get<NodeList>()->processKillNode(*message);
|
||||||
}
|
}
|
||||||
|
@ -466,7 +430,6 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
QJsonObject statsObject;
|
QJsonObject statsObject;
|
||||||
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
|
||||||
|
|
||||||
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
|
|
||||||
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
|
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
|
||||||
|
|
||||||
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
||||||
|
@ -507,7 +470,6 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||||
|
|
||||||
_sumListeners = 0;
|
_sumListeners = 0;
|
||||||
_sumBillboardPackets = 0;
|
|
||||||
_sumIdentityPackets = 0;
|
_sumIdentityPackets = 0;
|
||||||
_numStatFrames = 0;
|
_numStatFrames = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#ifndef hifi_AvatarMixer_h
|
#ifndef hifi_AvatarMixer_h
|
||||||
#define hifi_AvatarMixer_h
|
#define hifi_AvatarMixer_h
|
||||||
|
|
||||||
|
#include <PortableHighResolutionClock.h>
|
||||||
|
|
||||||
#include <ThreadedAssignment.h>
|
#include <ThreadedAssignment.h>
|
||||||
|
|
||||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||||
|
@ -34,7 +36,6 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleAvatarBillboardPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
|
||||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message);
|
||||||
void domainSettingsRequestComplete();
|
void domainSettingsRequestComplete();
|
||||||
|
|
||||||
|
@ -44,15 +45,14 @@ private:
|
||||||
|
|
||||||
QThread _broadcastThread;
|
QThread _broadcastThread;
|
||||||
|
|
||||||
quint64 _lastFrameTimestamp;
|
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||||
|
|
||||||
float _trailingSleepRatio;
|
float _trailingSleepRatio { 1.0f };
|
||||||
float _performanceThrottlingRatio;
|
float _performanceThrottlingRatio { 0.0f };
|
||||||
|
|
||||||
int _sumListeners;
|
int _sumListeners { 0 };
|
||||||
int _numStatFrames;
|
int _numStatFrames { 0 };
|
||||||
int _sumBillboardPackets;
|
int _sumIdentityPackets { 0 };
|
||||||
int _sumIdentityPackets;
|
|
||||||
|
|
||||||
float _maxKbpsPerNode = 0.0f;
|
float _maxKbpsPerNode = 0.0f;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
#include <PortableHighResolutionClock.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <UUIDHasher.h>
|
#include <UUIDHasher.h>
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
||||||
class AvatarMixerClientData : public NodeData {
|
class AvatarMixerClientData : public NodeData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
using HRCTime = p_high_resolution_clock::time_point;
|
||||||
|
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
AvatarData& getAvatar() { return *_avatar; }
|
||||||
|
|
||||||
|
@ -45,11 +48,8 @@ public:
|
||||||
|
|
||||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||||
|
|
||||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); }
|
||||||
|
|
||||||
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
|
||||||
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
|
||||||
|
|
||||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||||
float getFullRateDistance() const { return _fullRateDistance; }
|
float getFullRateDistance() const { return _fullRateDistance; }
|
||||||
|
@ -86,8 +86,7 @@ private:
|
||||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||||
|
|
||||||
quint64 _billboardChangeTimestamp = 0;
|
HRCTime _identityChangeTimestamp;
|
||||||
quint64 _identityChangeTimestamp = 0;
|
|
||||||
|
|
||||||
float _fullRateDistance = FLT_MAX;
|
float _fullRateDistance = FLT_MAX;
|
||||||
float _maxAvatarDistance = FLT_MAX;
|
float _maxAvatarDistance = FLT_MAX;
|
||||||
|
|
|
@ -9,16 +9,20 @@
|
||||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
#
|
#
|
||||||
|
|
||||||
|
set(INTERFACE_DISPLAY_NAME "Interface")
|
||||||
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
|
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
|
||||||
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
|
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
|
||||||
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
||||||
|
set(CONSOLE_DISPLAY_NAME "Sandbox")
|
||||||
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
||||||
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
|
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
|
||||||
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
|
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
|
||||||
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
||||||
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@")
|
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@")
|
||||||
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@")
|
set(PRE_SANDBOX_CONSOLE_SHORTCUT_NAME "@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@")
|
||||||
|
set(DS_DISPLAY_NAME "Domain Server")
|
||||||
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
set(DS_EXEC_NAME "@DS_EXEC_NAME@")
|
||||||
|
set(AC_DISPLAY_NAME "Assignment Client")
|
||||||
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
||||||
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")
|
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")
|
||||||
set(PRODUCTION_BUILD "@PRODUCTION_BUILD@")
|
set(PRODUCTION_BUILD "@PRODUCTION_BUILD@")
|
||||||
|
|
|
@ -737,10 +737,10 @@ SectionEnd
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro CheckForRunningApplications action prompter
|
!macro CheckForRunningApplications action prompter
|
||||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_DISPLAY_NAME@" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_DISPLAY_NAME@" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "@DS_DISPLAY_NAME@" ${action} ${prompter}
|
||||||
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
|
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "@AC_DISPLAY_NAME@" ${action} ${prompter}
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|
|
@ -14,11 +14,9 @@ Script.load("edit.js");
|
||||||
Script.load("examples.js");
|
Script.load("examples.js");
|
||||||
Script.load("selectAudioDevice.js");
|
Script.load("selectAudioDevice.js");
|
||||||
Script.load("notifications.js");
|
Script.load("notifications.js");
|
||||||
Script.load("users.js");
|
|
||||||
Script.load("controllers/handControllerGrab.js");
|
Script.load("controllers/handControllerGrab.js");
|
||||||
Script.load("controllers/squeezeHands.js");
|
Script.load("controllers/squeezeHands.js");
|
||||||
Script.load("grab.js");
|
Script.load("grab.js");
|
||||||
Script.load("directory.js");
|
Script.load("directory.js");
|
||||||
Script.load("dialTone.js");
|
Script.load("dialTone.js");
|
||||||
// Script.load("attachedEntitiesManager.js");
|
|
||||||
Script.load("depthReticle.js");
|
Script.load("depthReticle.js");
|
||||||
|
|
50
examples/disableAvatarAnimations.js
Normal file
50
examples/disableAvatarAnimations.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// disableAvatarAnimations.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// When launched, it will replace all of the avatars animations with a single frame idle pose.
|
||||||
|
// full body IK and hand grabbing animations will still continue to function, but all other
|
||||||
|
// animations will be replaced.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
var skeletonModelURL = "";
|
||||||
|
var jointCount = 0;
|
||||||
|
|
||||||
|
var excludedRoles = ["rightHandGraspOpen", "rightHandGraspClosed", "leftHandGraspOpen", "leftHandGraspClosed"];
|
||||||
|
var IDLE_URL = "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/idle.fbx";
|
||||||
|
|
||||||
|
function overrideAnims() {
|
||||||
|
var roles = MyAvatar.getAnimationRoles();
|
||||||
|
var i, l = roles.length;
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
if (excludedRoles.indexOf(roles[i]) == -1) {
|
||||||
|
MyAvatar.overrideRoleAnimation(roles[i], IDLE_URL, 30, false, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreAnims() {
|
||||||
|
var roles = MyAvatar.getAnimationRoles();
|
||||||
|
var i, l = roles.length;
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
if (excludedRoles.indexOf(roles[i]) == -1) {
|
||||||
|
MyAvatar.restoreRoleAnimation(roles[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideAnims();
|
||||||
|
|
||||||
|
MyAvatar.onLoadComplete.connect(function () {
|
||||||
|
overrideAnims();
|
||||||
|
});
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
restoreAnims();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
56
examples/entityScripts/exampleSelfCallingTimeoutNoCleanup.js
Normal file
56
examples/entityScripts/exampleSelfCallingTimeoutNoCleanup.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// exampleSelfCallingTimeoutNoCleanup.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 4/18/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which hooks the update signal
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||||
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
|
ExampleUpdate = function() {
|
||||||
|
_this = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ExampleUpdate.prototype = {
|
||||||
|
|
||||||
|
timeOutFunction: function() {
|
||||||
|
var entityID = _this.entityID;
|
||||||
|
print("timeOutFunction in entityID:" + entityID);
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
_this.timeOutFunction();
|
||||||
|
}, 3000);
|
||||||
|
},
|
||||||
|
|
||||||
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
|
preload: function(entityID) {
|
||||||
|
print("preload - entityID:" + entityID);
|
||||||
|
this.entityID = entityID;
|
||||||
|
|
||||||
|
print("preload - entityID:" + entityID + "-- calling timeOutFunction()....");
|
||||||
|
_this.timeOutFunction();
|
||||||
|
},
|
||||||
|
|
||||||
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
|
// to the update signal
|
||||||
|
unload: function(entityID) {
|
||||||
|
print("unload - entityID:" + entityID);
|
||||||
|
print("NOTE --- WE DID NOT CALL clear our timeout");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
|
return new ExampleUpdate();
|
||||||
|
})
|
50
examples/entityScripts/exampleTimeoutNoCleanup.js
Normal file
50
examples/entityScripts/exampleTimeoutNoCleanup.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// exampleTimeoutNoCleanup.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 4/18/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which hooks the update signal
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||||
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
|
ExampleUpdate = function() {
|
||||||
|
_this = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ExampleUpdate.prototype = {
|
||||||
|
|
||||||
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
|
preload: function(entityID) {
|
||||||
|
print("preload - entityID:" + entityID);
|
||||||
|
this.entityID = entityID;
|
||||||
|
|
||||||
|
Script.setInterval(function() {
|
||||||
|
var entityID = _this.entityID;
|
||||||
|
print("timer interval in entityID:" + entityID);
|
||||||
|
}, 3000);
|
||||||
|
},
|
||||||
|
|
||||||
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
|
// to the update signal
|
||||||
|
unload: function(entityID) {
|
||||||
|
print("unload - entityID:" + entityID);
|
||||||
|
print("NOTE --- WE DID NOT CALL clear our timeout");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
|
return new ExampleUpdate();
|
||||||
|
})
|
54
examples/entityScripts/exampleUpdate.js
Normal file
54
examples/entityScripts/exampleUpdate.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// exampleUpdate.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 4/18/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which hooks the update signal
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||||
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
|
ExampleUpdate = function() {
|
||||||
|
_this = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ExampleUpdate.prototype = {
|
||||||
|
|
||||||
|
// update() will be called regulary, because we've hooked the update signal in our preload() function.
|
||||||
|
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
|
||||||
|
update: function() {
|
||||||
|
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||||
|
var entityID = _this.entityID;
|
||||||
|
print("update in entityID:" + entityID);
|
||||||
|
},
|
||||||
|
|
||||||
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
|
preload: function(entityID) {
|
||||||
|
print("preload - entityID:" + entityID);
|
||||||
|
this.entityID = entityID;
|
||||||
|
Script.update.connect(this.update);
|
||||||
|
},
|
||||||
|
|
||||||
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
|
// to the update signal
|
||||||
|
unload: function(entityID) {
|
||||||
|
print("unload - entityID:" + entityID);
|
||||||
|
Script.update.disconnect(this.update);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
|
return new ExampleUpdate();
|
||||||
|
})
|
54
examples/entityScripts/exampleUpdateNoDisconnect.js
Normal file
54
examples/entityScripts/exampleUpdateNoDisconnect.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// exampleUpdateNoDisconnect.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 4/18/16.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which hooks the update signal
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||||
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
|
ExampleUpdate = function() {
|
||||||
|
_this = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ExampleUpdate.prototype = {
|
||||||
|
|
||||||
|
// update() will be called regulary, because we've hooked the update signal in our preload() function.
|
||||||
|
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
|
||||||
|
update: function() {
|
||||||
|
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
||||||
|
var entityID = _this.entityID;
|
||||||
|
print("update in entityID:" + entityID);
|
||||||
|
},
|
||||||
|
|
||||||
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
|
// * connecting to the update signal so we can check our grabbed state
|
||||||
|
preload: function(entityID) {
|
||||||
|
print("preload - entityID:" + entityID);
|
||||||
|
this.entityID = entityID;
|
||||||
|
Script.update.connect(this.update);
|
||||||
|
},
|
||||||
|
|
||||||
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
|
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
||||||
|
// to the update signal
|
||||||
|
unload: function(entityID) {
|
||||||
|
print("unload - entityID:" + entityID);
|
||||||
|
print("NOTE --- WE DID NOT CALL Script.update.disconnect()");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
|
return new ExampleUpdate();
|
||||||
|
})
|
53
examples/libraries/jasmine/hifi-boot.js
Normal file
53
examples/libraries/jasmine/hifi-boot.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
function ConsoleReporter(options) {
|
||||||
|
this.jasmineStarted = function (obj) {
|
||||||
|
print("jasmineStarted: numSpecs = " + obj.totalSpecsDefined);
|
||||||
|
};
|
||||||
|
this.jasmineDone = function (obj) {
|
||||||
|
print("jasmineDone");
|
||||||
|
};
|
||||||
|
this.suiteStarted = function(obj) {
|
||||||
|
print("suiteStarted: \"" + obj.fullName + "\"");
|
||||||
|
};
|
||||||
|
this.suiteDone = function(obj) {
|
||||||
|
print("suiteDone: \"" + obj.fullName + "\" " + obj.status);
|
||||||
|
};
|
||||||
|
this.specStarted = function(obj) {
|
||||||
|
print("specStarted: \"" + obj.fullName + "\"");
|
||||||
|
};
|
||||||
|
this.specDone = function(obj) {
|
||||||
|
print("specDone: \"" + obj.fullName + "\" " + obj.status);
|
||||||
|
|
||||||
|
var i, l = obj.failedExpectations.length;
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
print(" " + obj.failedExpectations[i].message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout = Script.setTimeout;
|
||||||
|
setInterval = Script.setInterval;
|
||||||
|
clearTimeout = Script.clearTimeout;
|
||||||
|
clearInterval = Script.clearInterval;
|
||||||
|
|
||||||
|
var jasmine = jasmineRequire.core(jasmineRequire);
|
||||||
|
|
||||||
|
var env = jasmine.getEnv();
|
||||||
|
|
||||||
|
env.addReporter(new ConsoleReporter());
|
||||||
|
|
||||||
|
var jasmineInterface = jasmineRequire.interface(jasmine, env);
|
||||||
|
|
||||||
|
extend(this, jasmineInterface);
|
||||||
|
|
||||||
|
function extend(destination, source) {
|
||||||
|
for (var property in source) {
|
||||||
|
destination[property] = source[property];
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
3458
examples/libraries/jasmine/jasmine.js
Normal file
3458
examples/libraries/jasmine/jasmine.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -355,20 +355,36 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
that.windowDimensions = Controller.getViewportDimensions();
|
|
||||||
|
function clamp(value, min, max) {
|
||||||
|
return Math.min(Math.max(value, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var recommendedDimmensions = { x: recommendedRect.width, y: recommendedRect.height };
|
||||||
|
that.windowDimensions = recommendedDimmensions; // Controller.getViewportDimensions();
|
||||||
|
that.origin = { x: recommendedRect.x, y: recommendedRect.y };
|
||||||
// Maybe fixme: Keeping the same percent of the window size isn't always the right thing.
|
// Maybe fixme: Keeping the same percent of the window size isn't always the right thing.
|
||||||
// For example, maybe we want "keep the same percentage to whatever two edges are closest to the edge of screen".
|
// For example, maybe we want "keep the same percentage to whatever two edges are closest to the edge of screen".
|
||||||
// If we change that, the places to do so are onResizeViewport, save (maybe), and the initial move based on Settings, below.
|
// If we change that, the places to do so are onResizeViewport, save (maybe), and the initial move based on Settings, below.
|
||||||
that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients.
|
that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients.
|
||||||
var fractionX = that.x / that.windowDimensions.x;
|
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
var fractionY = that.y / that.windowDimensions.y;
|
var recommendedDimmensions = { x: recommendedRect.width, y: recommendedRect.height };
|
||||||
that.windowDimensions = newSize || Controller.getViewportDimensions();
|
var originRelativeX = (that.x - that.origin.x);
|
||||||
that.move(fractionX * that.windowDimensions.x, fractionY * that.windowDimensions.y);
|
var originRelativeY = (that.y - that.origin.y);
|
||||||
|
var fractionX = clamp(originRelativeX / that.windowDimensions.x, 0, 1);
|
||||||
|
var fractionY = clamp(originRelativeY / that.windowDimensions.y, 0, 1);
|
||||||
|
that.windowDimensions = newSize || recommendedDimmensions;
|
||||||
|
that.origin = { x: recommendedRect.x, y: recommendedRect.y };
|
||||||
|
var newX = (fractionX * that.windowDimensions.x) + recommendedRect.x;
|
||||||
|
var newY = (fractionY * that.windowDimensions.y) + recommendedRect.y;
|
||||||
|
that.move(newX, newY);
|
||||||
};
|
};
|
||||||
if (optionalPersistenceKey) {
|
if (optionalPersistenceKey) {
|
||||||
this.fractionKey = optionalPersistenceKey + '.fraction';
|
this.fractionKey = optionalPersistenceKey + '.fraction';
|
||||||
this.save = function () {
|
this.save = function () {
|
||||||
var screenSize = Controller.getViewportDimensions();
|
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var screenSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||||
if (screenSize.x > 0 && screenSize.y > 0) {
|
if (screenSize.x > 0 && screenSize.y > 0) {
|
||||||
// Guard against invalid screen size that can occur at shut-down.
|
// Guard against invalid screen size that can occur at shut-down.
|
||||||
var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y};
|
var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y};
|
||||||
|
@ -411,7 +427,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
||||||
that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
|
that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y);
|
||||||
};
|
};
|
||||||
that.checkResize = function () { // Can be overriden or extended, but usually not. See onResizeViewport.
|
that.checkResize = function () { // Can be overriden or extended, but usually not. See onResizeViewport.
|
||||||
var currentWindowSize = Controller.getViewportDimensions();
|
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var currentWindowSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||||
|
|
||||||
if ((currentWindowSize.x !== that.windowDimensions.x) || (currentWindowSize.y !== that.windowDimensions.y)) {
|
if ((currentWindowSize.x !== that.windowDimensions.x) || (currentWindowSize.y !== that.windowDimensions.y)) {
|
||||||
that.onResizeViewport(currentWindowSize);
|
that.onResizeViewport(currentWindowSize);
|
||||||
}
|
}
|
||||||
|
@ -434,7 +452,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
||||||
}
|
}
|
||||||
if (this.fractionKey || optionalInitialPositionFunction) {
|
if (this.fractionKey || optionalInitialPositionFunction) {
|
||||||
var savedFraction = JSON.parse(Settings.getValue(this.fractionKey) || '0'); // getValue can answer empty string
|
var savedFraction = JSON.parse(Settings.getValue(this.fractionKey) || '0'); // getValue can answer empty string
|
||||||
var screenSize = Controller.getViewportDimensions();
|
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var screenSize = { x: recommendedRect.width, y: recommendedRect.height };
|
||||||
if (savedFraction) {
|
if (savedFraction) {
|
||||||
// If we have saved data, keep the toolbar at the same proportion of the screen width/height.
|
// If we have saved data, keep the toolbar at the same proportion of the screen width/height.
|
||||||
that.move(savedFraction.x * screenSize.x, savedFraction.y * screenSize.y);
|
that.move(savedFraction.x * screenSize.x, savedFraction.y * screenSize.y);
|
||||||
|
|
59
examples/tests/avatarUnitTests.js
Normal file
59
examples/tests/avatarUnitTests.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
Script.include("../libraries/jasmine/jasmine.js");
|
||||||
|
Script.include("../libraries/jasmine/hifi-boot.js");
|
||||||
|
|
||||||
|
// Art3mis
|
||||||
|
var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758";
|
||||||
|
|
||||||
|
var ORIGIN = {x: 0, y: 0, z: 0};
|
||||||
|
var ONE_HUNDRED = {x: 100, y: 100, z: 100};
|
||||||
|
var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
|
||||||
|
|
||||||
|
describe("MyAvatar", function () {
|
||||||
|
|
||||||
|
// reload the avatar from scratch before each test.
|
||||||
|
beforeEach(function (done) {
|
||||||
|
MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL;
|
||||||
|
|
||||||
|
// wait until we are finished loading
|
||||||
|
var id = Script.setInterval(function () {
|
||||||
|
if (MyAvatar.jointNames.length == 72) {
|
||||||
|
// assume we are finished loading.
|
||||||
|
Script.clearInterval(id);
|
||||||
|
MyAvatar.position = ORIGIN;
|
||||||
|
MyAvatar.orientation = ROT_IDENT;
|
||||||
|
// give the avatar 1/2 a second to settle on the ground in the idle pose.
|
||||||
|
Script.setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// makes the assumption that there is solid ground somewhat underneath the avatar.
|
||||||
|
it("position and orientation getters", function () {
|
||||||
|
var pos = MyAvatar.position;
|
||||||
|
|
||||||
|
expect(Math.abs(pos.x)).toBeLessThan(0.1);
|
||||||
|
expect(Math.abs(pos.y)).toBeLessThan(1.0);
|
||||||
|
expect(Math.abs(pos.z)).toBeLessThan(0.1);
|
||||||
|
|
||||||
|
var rot = MyAvatar.orientation;
|
||||||
|
expect(Math.abs(rot.x)).toBeLessThan(0.01);
|
||||||
|
expect(Math.abs(rot.y)).toBeLessThan(0.01);
|
||||||
|
expect(Math.abs(rot.z)).toBeLessThan(0.01);
|
||||||
|
expect(Math.abs(1 - rot.w)).toBeLessThan(0.01);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("position and orientation setters", function (done) {
|
||||||
|
MyAvatar.position = ONE_HUNDRED;
|
||||||
|
Script.setTimeout(function () {
|
||||||
|
expect(Vec3.length(Vec3.subtract(MyAvatar.position, ONE_HUNDRED))).toBeLessThan(0.1);
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
jasmine.getEnv().execute();
|
||||||
|
|
|
@ -39,6 +39,7 @@ Column {
|
||||||
"Shadow",
|
"Shadow",
|
||||||
"Pyramid Depth",
|
"Pyramid Depth",
|
||||||
"Ambient Occlusion",
|
"Ambient Occlusion",
|
||||||
|
"Ambient Occlusion Blurred",
|
||||||
"Custom Shader"
|
"Custom Shader"
|
||||||
]
|
]
|
||||||
RadioButton {
|
RadioButton {
|
||||||
|
|
|
@ -80,11 +80,6 @@ Item {
|
||||||
label: "GPU",
|
label: "GPU",
|
||||||
color: "#1AC567"
|
color: "#1AC567"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
prop: "frameTextureCount",
|
|
||||||
label: "Frame",
|
|
||||||
color: "#E2334D"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: "textureGPUTransferCount",
|
prop: "textureGPUTransferCount",
|
||||||
label: "Transfer",
|
label: "Transfer",
|
||||||
|
@ -114,13 +109,7 @@ Item {
|
||||||
prop: "textureGPUVirtualMemoryUsage",
|
prop: "textureGPUVirtualMemoryUsage",
|
||||||
label: "GPU Virtual",
|
label: "GPU Virtual",
|
||||||
color: "#9495FF"
|
color: "#9495FF"
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: "frameTextureMemoryUsage",
|
|
||||||
label: "Frame",
|
|
||||||
color: "#E2334D"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +159,24 @@ Item {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlotPerf {
|
||||||
|
title: "State Changes"
|
||||||
|
height: parent.evalEvenHeight()
|
||||||
|
object: stats.config
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "frameTextureCount",
|
||||||
|
label: "Textures",
|
||||||
|
color: "#00B4EF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "frameSetPipelineCount",
|
||||||
|
label: "Pipelines",
|
||||||
|
color: "#E2334D"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
|
property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
|
||||||
property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
|
property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
|
||||||
property var drawLightConfig: Render.getConfig("DrawLight")
|
property var drawLightConfig: Render.getConfig("DrawLight")
|
||||||
|
@ -178,9 +185,10 @@ Item {
|
||||||
title: "Items"
|
title: "Items"
|
||||||
height: parent.evalEvenHeight()
|
height: parent.evalEvenHeight()
|
||||||
object: parent.drawOpaqueConfig
|
object: parent.drawOpaqueConfig
|
||||||
|
|
||||||
plots: [
|
plots: [
|
||||||
{
|
{
|
||||||
object: Render.getConfig("DrawOpaqueDeferred"),
|
object: parent.drawOpaqueConfig,
|
||||||
prop: "numDrawn",
|
prop: "numDrawn",
|
||||||
label: "Opaques",
|
label: "Opaques",
|
||||||
color: "#1AC567"
|
color: "#1AC567"
|
||||||
|
@ -198,7 +206,42 @@ Item {
|
||||||
color: "#FED959"
|
color: "#FED959"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlotPerf {
|
||||||
|
title: "Timing"
|
||||||
|
height: parent.evalEvenHeight()
|
||||||
|
object: parent.drawOpaqueConfig
|
||||||
|
valueUnit: "ms"
|
||||||
|
valueScale: 1000
|
||||||
|
valueNumDigits: "1"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
object: Render.getConfig("DrawOpaqueDeferred"),
|
||||||
|
prop: "cpuRunTime",
|
||||||
|
label: "Opaques",
|
||||||
|
color: "#1AC567"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: Render.getConfig("DrawTransparentDeferred"),
|
||||||
|
prop: "cpuRunTime",
|
||||||
|
label: "Translucents",
|
||||||
|
color: "#00B4EF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: Render.getConfig("RenderDeferred"),
|
||||||
|
prop: "cpuRunTime",
|
||||||
|
label: "Lighting",
|
||||||
|
color: "#FED959"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
object: Render.getConfig("RenderDeferredTask"),
|
||||||
|
prop: "cpuRunTime",
|
||||||
|
label: "RenderFrame",
|
||||||
|
color: "#E2334D"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ Hifi.AvatarInputs {
|
||||||
readonly property int mirrorWidth: 265
|
readonly property int mirrorWidth: 265
|
||||||
readonly property int iconSize: 24
|
readonly property int iconSize: 24
|
||||||
readonly property int iconPadding: 5
|
readonly property int iconPadding: 5
|
||||||
|
|
||||||
|
readonly property bool shouldReposition: true
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
category: "Overlay.AvatarInputs"
|
category: "Overlay.AvatarInputs"
|
||||||
|
|
|
@ -21,8 +21,11 @@ FocusScope {
|
||||||
objectName: "desktop"
|
objectName: "desktop"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onHeightChanged: d.repositionAll();
|
property rect recommendedRect: rect(0,0,0,0);
|
||||||
onWidthChanged: d.repositionAll();
|
|
||||||
|
onHeightChanged: d.handleSizeChanged();
|
||||||
|
|
||||||
|
onWidthChanged: d.handleSizeChanged();
|
||||||
|
|
||||||
// Controls and windows can trigger this signal to ensure the desktop becomes visible
|
// Controls and windows can trigger this signal to ensure the desktop becomes visible
|
||||||
// when they're opened.
|
// when they're opened.
|
||||||
|
@ -50,6 +53,20 @@ FocusScope {
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
|
|
||||||
|
function handleSizeChanged() {
|
||||||
|
var oldRecommendedRect = recommendedRect;
|
||||||
|
var newRecommendedRectJS = Controller.getRecommendedOverlayRect();
|
||||||
|
var newRecommendedRect = Qt.rect(newRecommendedRectJS.x, newRecommendedRectJS.y,
|
||||||
|
newRecommendedRectJS.width,
|
||||||
|
newRecommendedRectJS.height);
|
||||||
|
|
||||||
|
if (oldRecommendedRect != Qt.rect(0,0,0,0)
|
||||||
|
&& oldRecommendedRect != newRecommendedRect) {
|
||||||
|
d.repositionAll();
|
||||||
|
}
|
||||||
|
recommendedRect = newRecommendedRect;
|
||||||
|
}
|
||||||
|
|
||||||
function findChild(item, name) {
|
function findChild(item, name) {
|
||||||
for (var i = 0; i < item.children.length; ++i) {
|
for (var i = 0; i < item.children.length; ++i) {
|
||||||
if (item.children[i].objectName === name) {
|
if (item.children[i].objectName === name) {
|
||||||
|
@ -202,12 +219,42 @@ FocusScope {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRepositionChildren(predicate) {
|
||||||
|
var currentWindows = [];
|
||||||
|
if (!desktop) {
|
||||||
|
console.log("Could not find desktop");
|
||||||
|
return currentWindows;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < desktop.children.length; ++i) {
|
||||||
|
var child = desktop.children[i];
|
||||||
|
if (child.shouldReposition === true && (!predicate || predicate(child))) {
|
||||||
|
currentWindows.push(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentWindows;
|
||||||
|
}
|
||||||
|
|
||||||
function repositionAll() {
|
function repositionAll() {
|
||||||
|
var oldRecommendedRect = recommendedRect;
|
||||||
|
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
|
||||||
|
var newRecommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
|
||||||
var windows = d.getTopLevelWindows();
|
var windows = d.getTopLevelWindows();
|
||||||
for (var i = 0; i < windows.length; ++i) {
|
for (var i = 0; i < windows.length; ++i) {
|
||||||
reposition(windows[i]);
|
var targetWindow = windows[i];
|
||||||
|
if (targetWindow.visible) {
|
||||||
|
repositionWindow(targetWindow, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also reposition the other children that aren't top level windows but want to be repositioned
|
||||||
|
var otherChildren = d.getRepositionChildren();
|
||||||
|
for (var i = 0; i < otherChildren.length; ++i) {
|
||||||
|
var child = otherChildren[i];
|
||||||
|
repositionWindow(child, true, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,38 +279,56 @@ FocusScope {
|
||||||
targetWindow.focus = true;
|
targetWindow.focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
reposition(targetWindow);
|
var oldRecommendedRect = recommendedRect;
|
||||||
|
var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height };
|
||||||
|
var newRecommendedRect = Controller.getRecommendedOverlayRect();
|
||||||
|
var newRecommendedDimmensions = { x: newRecommendedRect.width, y: newRecommendedRect.height };
|
||||||
|
repositionWindow(targetWindow, false, oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions);
|
||||||
|
|
||||||
showDesktop();
|
showDesktop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reposition(item) {
|
function repositionWindow(targetWindow, forceReposition,
|
||||||
|
oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions) {
|
||||||
|
|
||||||
if (desktop.width === 0 || desktop.height === 0) {
|
if (desktop.width === 0 || desktop.height === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetWindow = d.getDesktopWindow(item);
|
|
||||||
if (!targetWindow) {
|
if (!targetWindow) {
|
||||||
console.warn("Could not find top level window for " + item);
|
console.warn("Could not find top level window for " + item);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var recommended = Controller.getRecommendedOverlayRect();
|
||||||
|
var maxX = recommended.x + recommended.width;
|
||||||
|
var maxY = recommended.y + recommended.height;
|
||||||
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
|
var newPosition = Qt.vector2d(targetWindow.x, targetWindow.y);
|
||||||
// If the window is completely offscreen, reposition it
|
|
||||||
if ((targetWindow.x > desktop.width || (targetWindow.x + targetWindow.width) < 0) ||
|
// if we asked to force reposition, or if the window is completely outside of the recommended rectangle, reposition it
|
||||||
(targetWindow.y > desktop.height || (targetWindow.y + targetWindow.height) < 0)) {
|
if (forceReposition || (targetWindow.x > maxX || (targetWindow.x + targetWindow.width) < recommended.x) ||
|
||||||
|
(targetWindow.y > maxY || (targetWindow.y + targetWindow.height) < recommended.y)) {
|
||||||
newPosition.x = -1
|
newPosition.x = -1
|
||||||
newPosition.y = -1
|
newPosition.y = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (newPosition.x === -1 && newPosition.y === -1) {
|
if (newPosition.x === -1 && newPosition.y === -1) {
|
||||||
// Set initial window position
|
var originRelativeX = (targetWindow.x - oldRecommendedRect.x);
|
||||||
// var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
|
var originRelativeY = (targetWindow.y - oldRecommendedRect.y);
|
||||||
// var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
|
if (isNaN(originRelativeX)) {
|
||||||
// newPosition = Utils.clampVector(newPosition, minPosition, maxPosition);
|
originRelativeX = 0;
|
||||||
// newPosition = Utils.randomPosition(minPosition, maxPosition);
|
}
|
||||||
newPosition = Qt.vector2d(desktop.width / 2 - targetWindow.width / 2,
|
if (isNaN(originRelativeY)) {
|
||||||
desktop.height / 2 - targetWindow.height / 2);
|
originRelativeY = 0;
|
||||||
|
}
|
||||||
|
var fractionX = Utils.clamp(originRelativeX / oldRecommendedDimmensions.x, 0, 1);
|
||||||
|
var fractionY = Utils.clamp(originRelativeY / oldRecommendedDimmensions.y, 0, 1);
|
||||||
|
|
||||||
|
var newX = (fractionX * newRecommendedDimmensions.x) + newRecommendedRect.x;
|
||||||
|
var newY = (fractionY * newRecommendedDimmensions.y) + newRecommendedRect.y;
|
||||||
|
|
||||||
|
newPosition = Qt.vector2d(newX, newY);
|
||||||
}
|
}
|
||||||
targetWindow.x = newPosition.x;
|
targetWindow.x = newPosition.x;
|
||||||
targetWindow.y = newPosition.y;
|
targetWindow.y = newPosition.y;
|
||||||
|
|
|
@ -195,12 +195,7 @@ static const uint32_t INVALID_FRAME = UINT32_MAX;
|
||||||
|
|
||||||
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
|
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||||
#else
|
|
||||||
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
|
|
||||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
|
Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
|
||||||
|
|
||||||
|
@ -1183,8 +1178,6 @@ void Application::cleanupBeforeQuit() {
|
||||||
}
|
}
|
||||||
_keyboardFocusHighlight = nullptr;
|
_keyboardFocusHighlight = nullptr;
|
||||||
|
|
||||||
getEntities()->clear(); // this will allow entity scripts to properly shutdown
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||||
|
@ -1195,6 +1188,7 @@ void Application::cleanupBeforeQuit() {
|
||||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||||
|
|
||||||
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||||
|
|
||||||
DependencyManager::get<ScriptEngines>()->saveScripts();
|
DependencyManager::get<ScriptEngines>()->saveScripts();
|
||||||
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
|
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
|
||||||
DependencyManager::destroy<ScriptEngines>();
|
DependencyManager::destroy<ScriptEngines>();
|
||||||
|
@ -2686,8 +2680,6 @@ void Application::idle(uint64_t now) {
|
||||||
_overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
|
_overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
||||||
|
@ -4191,8 +4183,13 @@ void Application::updateWindowTitle() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::clearDomainOctreeDetails() {
|
void Application::clearDomainOctreeDetails() {
|
||||||
|
|
||||||
|
// if we're about to quit, we really don't need to do any of these things...
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qCDebug(interfaceapp) << "Clearing domain octree details...";
|
qCDebug(interfaceapp) << "Clearing domain octree details...";
|
||||||
// reset the environment so that we don't erroneously end up with multiple
|
|
||||||
|
|
||||||
resetPhysicsReadyInformation();
|
resetPhysicsReadyInformation();
|
||||||
|
|
||||||
|
@ -4216,7 +4213,6 @@ void Application::clearDomainOctreeDetails() {
|
||||||
|
|
||||||
void Application::domainChanged(const QString& domainHostname) {
|
void Application::domainChanged(const QString& domainHostname) {
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
clearDomainOctreeDetails();
|
|
||||||
// disable physics until we have enough information about our new location to not cause craziness.
|
// disable physics until we have enough information about our new location to not cause craziness.
|
||||||
resetPhysicsReadyInformation();
|
resetPhysicsReadyInformation();
|
||||||
}
|
}
|
||||||
|
@ -4899,19 +4895,44 @@ QRect Application::getRenderingGeometry() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::uvec2 Application::getUiSize() const {
|
glm::uvec2 Application::getUiSize() const {
|
||||||
return getActiveDisplayPlugin()->getRecommendedUiSize();
|
static const uint MIN_SIZE = 1;
|
||||||
|
glm::uvec2 result(MIN_SIZE);
|
||||||
|
if (_displayPlugin) {
|
||||||
|
result = getActiveDisplayPlugin()->getRecommendedUiSize();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Application::getRecommendedOverlayRect() const {
|
||||||
|
auto uiSize = getUiSize();
|
||||||
|
QRect result(0, 0, uiSize.x, uiSize.y);
|
||||||
|
if (_displayPlugin) {
|
||||||
|
result = getActiveDisplayPlugin()->getRecommendedOverlayRect();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Application::getDeviceSize() const {
|
QSize Application::getDeviceSize() const {
|
||||||
return fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
static const int MIN_SIZE = 1;
|
||||||
|
QSize result(MIN_SIZE, MIN_SIZE);
|
||||||
|
if (_displayPlugin) {
|
||||||
|
result = fromGlm(getActiveDisplayPlugin()->getRecommendedRenderSize());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::isThrottleRendering() const {
|
bool Application::isThrottleRendering() const {
|
||||||
return getActiveDisplayPlugin()->isThrottled();
|
if (_displayPlugin) {
|
||||||
|
return getActiveDisplayPlugin()->isThrottled();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::hasFocus() const {
|
bool Application::hasFocus() const {
|
||||||
return getActiveDisplayPlugin()->hasFocus();
|
if (_displayPlugin) {
|
||||||
|
return getActiveDisplayPlugin()->hasFocus();
|
||||||
|
}
|
||||||
|
return (QApplication::activeWindow() != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 Application::getViewportDimensions() const {
|
glm::vec2 Application::getViewportDimensions() const {
|
||||||
|
|
|
@ -117,6 +117,7 @@ public:
|
||||||
QRect getRenderingGeometry() const;
|
QRect getRenderingGeometry() const;
|
||||||
|
|
||||||
glm::uvec2 getUiSize() const;
|
glm::uvec2 getUiSize() const;
|
||||||
|
QRect getRecommendedOverlayRect() const;
|
||||||
QSize getDeviceSize() const;
|
QSize getDeviceSize() const;
|
||||||
bool hasFocus() const;
|
bool hasFocus() const;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <gpu/Context.h>
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <GeometryCache.h>
|
#include <GeometryCache.h>
|
||||||
|
@ -42,26 +41,11 @@ static const float TAU = 6.28318530717958f;
|
||||||
//static const float MILKY_WAY_RATIO = 0.4f;
|
//static const float MILKY_WAY_RATIO = 0.4f;
|
||||||
static const char* UNIFORM_TIME_NAME = "iGlobalTime";
|
static const char* UNIFORM_TIME_NAME = "iGlobalTime";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Stars::Stars() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Stars::~Stars() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produce a random float value between 0 and 1
|
// Produce a random float value between 0 and 1
|
||||||
static float frand() {
|
static float frand() {
|
||||||
return (float)rand() / (float)RAND_MAX;
|
return (float)rand() / (float)RAND_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a random radian value between 0 and 2 PI (TAU)
|
|
||||||
/*
|
|
||||||
static float rrand() {
|
|
||||||
return frand() * TAU;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// http://mathworld.wolfram.com/SpherePointPicking.html
|
// http://mathworld.wolfram.com/SpherePointPicking.html
|
||||||
static vec2 randPolar() {
|
static vec2 randPolar() {
|
||||||
vec2 result(frand(), frand());
|
vec2 result(frand(), frand());
|
||||||
|
@ -115,59 +99,56 @@ struct StarVertex {
|
||||||
vec4 colorAndSize;
|
vec4 colorAndSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME star colors
|
static const int STARS_VERTICES_SLOT{ 0 };
|
||||||
void Stars::render(RenderArgs* renderArgs, float alpha) {
|
static const int STARS_COLOR_SLOT{ 1 };
|
||||||
static gpu::BufferPointer vertexBuffer;
|
|
||||||
static gpu::Stream::FormatPointer streamFormat;
|
|
||||||
static gpu::Element positionElement, colorElement;
|
|
||||||
static gpu::PipelinePointer _gridPipeline;
|
|
||||||
static gpu::PipelinePointer _starsPipeline;
|
|
||||||
static int32_t _timeSlot{ -1 };
|
|
||||||
static std::once_flag once;
|
|
||||||
|
|
||||||
const int VERTICES_SLOT = 0;
|
gpu::PipelinePointer Stars::_gridPipeline{};
|
||||||
const int COLOR_SLOT = 1;
|
gpu::PipelinePointer Stars::_starsPipeline{};
|
||||||
|
int32_t Stars::_timeSlot{ -1 };
|
||||||
|
|
||||||
std::call_once(once, [&] {
|
void Stars::init() {
|
||||||
{
|
if (!_gridPipeline) {
|
||||||
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
||||||
auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
|
auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
|
||||||
auto program = gpu::Shader::createProgram(vs, ps);
|
auto program = gpu::Shader::createProgram(vs, ps);
|
||||||
gpu::Shader::makeProgram((*program));
|
gpu::Shader::makeProgram((*program));
|
||||||
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
|
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
|
||||||
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
|
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
|
||||||
_timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
_timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME);
|
||||||
}
|
|
||||||
auto state = gpu::StatePointer(new gpu::State());
|
|
||||||
// enable decal blend
|
|
||||||
state->setDepthTest(gpu::State::DepthTest(false));
|
|
||||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
|
||||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
|
||||||
_gridPipeline = gpu::Pipeline::create(program, state);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto vs = gpu::Shader::createVertex(std::string(stars_vert));
|
|
||||||
auto ps = gpu::Shader::createPixel(std::string(stars_frag));
|
|
||||||
auto program = gpu::Shader::createProgram(vs, ps);
|
|
||||||
gpu::Shader::makeProgram((*program));
|
|
||||||
auto state = gpu::StatePointer(new gpu::State());
|
|
||||||
// enable decal blend
|
|
||||||
state->setDepthTest(gpu::State::DepthTest(false));
|
|
||||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
|
||||||
state->setAntialiasedLineEnable(true); // line smoothing also smooth points
|
|
||||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
|
||||||
_starsPipeline = gpu::Pipeline::create(program, state);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
auto state = gpu::StatePointer(new gpu::State());
|
||||||
|
// enable decal blend
|
||||||
|
state->setDepthTest(gpu::State::DepthTest(false));
|
||||||
|
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
|
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||||
|
_gridPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_starsPipeline) {
|
||||||
|
auto vs = gpu::Shader::createVertex(std::string(stars_vert));
|
||||||
|
auto ps = gpu::Shader::createPixel(std::string(stars_frag));
|
||||||
|
auto program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
gpu::Shader::makeProgram((*program));
|
||||||
|
auto state = gpu::StatePointer(new gpu::State());
|
||||||
|
// enable decal blend
|
||||||
|
state->setDepthTest(gpu::State::DepthTest(false));
|
||||||
|
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||||
|
state->setAntialiasedLineEnable(true); // line smoothing also smooth points
|
||||||
|
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||||
|
_starsPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned limit = STARFIELD_NUM_STARS;
|
||||||
|
std::vector<StarVertex> points;
|
||||||
|
points.resize(limit);
|
||||||
|
|
||||||
|
{ // generate stars
|
||||||
QElapsedTimer startTime;
|
QElapsedTimer startTime;
|
||||||
startTime.start();
|
startTime.start();
|
||||||
|
|
||||||
vertexBuffer.reset(new gpu::Buffer);
|
vertexBuffer.reset(new gpu::Buffer);
|
||||||
|
|
||||||
srand(STARFIELD_SEED);
|
srand(STARFIELD_SEED);
|
||||||
unsigned limit = STARFIELD_NUM_STARS;
|
|
||||||
std::vector<StarVertex> points;
|
|
||||||
points.resize(limit);
|
|
||||||
for (size_t star = 0; star < limit; ++star) {
|
for (size_t star = 0; star < limit; ++star) {
|
||||||
points[star].position = vec4(fromPolar(randPolar()), 1);
|
points[star].position = vec4(fromPolar(randPolar()), 1);
|
||||||
float size = frand() * 2.5f + 0.5f;
|
float size = frand() * 2.5f + 0.5f;
|
||||||
|
@ -179,16 +160,32 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
points[star].colorAndSize = vec4(color, size);
|
points[star].colorAndSize = vec4(color, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
|
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
|
||||||
qDebug() << "Total time to generate stars: " << timeDiff << " msec";
|
qDebug() << "Total time to generate stars: " << timeDiff << " msec";
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::Element positionElement, colorElement;
|
||||||
|
const size_t VERTEX_STRIDE = sizeof(StarVertex);
|
||||||
|
|
||||||
|
vertexBuffer->append(VERTEX_STRIDE * limit, (const gpu::Byte*)&points[0]);
|
||||||
|
streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
|
||||||
|
streamFormat->setAttribute(gpu::Stream::POSITION, STARS_VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0);
|
||||||
|
streamFormat->setAttribute(gpu::Stream::COLOR, STARS_COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
|
||||||
|
positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
|
||||||
|
colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
|
||||||
|
|
||||||
|
size_t offset = offsetof(StarVertex, position);
|
||||||
|
positionView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement);
|
||||||
|
|
||||||
|
offset = offsetof(StarVertex, colorAndSize);
|
||||||
|
colorView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME star colors
|
||||||
|
void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
|
std::call_once(once, [&]{ init(); });
|
||||||
|
|
||||||
vertexBuffer->append(sizeof(StarVertex) * limit, (const gpu::Byte*)&points[0]);
|
|
||||||
streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
|
|
||||||
streamFormat->setAttribute(gpu::Stream::POSITION, VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0);
|
|
||||||
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
|
|
||||||
positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
|
|
||||||
colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto modelCache = DependencyManager::get<ModelCache>();
|
auto modelCache = DependencyManager::get<ModelCache>();
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
@ -210,17 +207,10 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
batch._glUniform1f(_timeSlot, secs);
|
batch._glUniform1f(_timeSlot, secs);
|
||||||
geometryCache->renderCube(batch);
|
geometryCache->renderCube(batch);
|
||||||
|
|
||||||
static const size_t VERTEX_STRIDE = sizeof(StarVertex);
|
|
||||||
size_t offset = offsetof(StarVertex, position);
|
|
||||||
gpu::BufferView posView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement);
|
|
||||||
offset = offsetof(StarVertex, colorAndSize);
|
|
||||||
gpu::BufferView colView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement);
|
|
||||||
|
|
||||||
// Render the stars
|
// Render the stars
|
||||||
batch.setPipeline(_starsPipeline);
|
batch.setPipeline(_starsPipeline);
|
||||||
|
|
||||||
batch.setInputFormat(streamFormat);
|
batch.setInputFormat(streamFormat);
|
||||||
batch.setInputBuffer(VERTICES_SLOT, posView);
|
batch.setInputBuffer(STARS_VERTICES_SLOT, positionView);
|
||||||
batch.setInputBuffer(COLOR_SLOT, colView);
|
batch.setInputBuffer(STARS_COLOR_SLOT, colorView);
|
||||||
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,21 +12,37 @@
|
||||||
#ifndef hifi_Stars_h
|
#ifndef hifi_Stars_h
|
||||||
#define hifi_Stars_h
|
#define hifi_Stars_h
|
||||||
|
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
class RenderArgs;
|
class RenderArgs;
|
||||||
|
|
||||||
// Starfield rendering component.
|
// Starfield rendering component.
|
||||||
class Stars {
|
class Stars {
|
||||||
public:
|
public:
|
||||||
Stars();
|
Stars() = default;
|
||||||
~Stars();
|
~Stars() = default;
|
||||||
|
|
||||||
|
Stars(Stars const&) = delete;
|
||||||
|
Stars& operator=(Stars const&) = delete;
|
||||||
|
|
||||||
// Renders the starfield from a local viewer's perspective.
|
// Renders the starfield from a local viewer's perspective.
|
||||||
// The parameters specifiy the field of view.
|
// The parameters specifiy the field of view.
|
||||||
void render(RenderArgs* args, float alpha);
|
void render(RenderArgs* args, float alpha);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// don't copy/assign
|
// Pipelines
|
||||||
Stars(Stars const&); // = delete;
|
static gpu::PipelinePointer _gridPipeline;
|
||||||
Stars& operator=(Stars const&); // delete;
|
static gpu::PipelinePointer _starsPipeline;
|
||||||
|
static int32_t _timeSlot;
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
gpu::BufferPointer vertexBuffer;
|
||||||
|
gpu::Stream::FormatPointer streamFormat;
|
||||||
|
gpu::BufferView positionView;
|
||||||
|
gpu::BufferView colorView;
|
||||||
|
std::once_flag once;
|
||||||
|
|
||||||
|
void init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,8 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
||||||
_headData->setLookAtPosition(headData->getLookAtPosition());
|
_headData->setLookAtPosition(headData->getLookAtPosition());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
|
||||||
}
|
}
|
||||||
|
|
||||||
MyAvatar::~MyAvatar() {
|
MyAvatar::~MyAvatar() {
|
||||||
|
@ -349,6 +351,10 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
_skeletonModel->simulate(deltaTime);
|
_skeletonModel->simulate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we've achived our final adjusted position and rotation for the avatar
|
||||||
|
// and all of its joints, now update our attachements.
|
||||||
|
Avatar::simulateAttachments(deltaTime);
|
||||||
|
|
||||||
if (!_skeletonModel->hasSkeleton()) {
|
if (!_skeletonModel->hasSkeleton()) {
|
||||||
// All the simulation that can be done has been done
|
// All the simulation that can be done has been done
|
||||||
return;
|
return;
|
||||||
|
@ -1169,9 +1175,6 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
||||||
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||||
|
|
||||||
setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
||||||
|
|
||||||
// now that physics has adjusted our position, we can update attachements.
|
|
||||||
Avatar::simulateAttachments(deltaTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString MyAvatar::getScriptedMotorFrame() const {
|
QString MyAvatar::getScriptedMotorFrame() const {
|
||||||
|
@ -1575,6 +1578,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
||||||
float speedIncreaseFactor = 1.8f;
|
float speedIncreaseFactor = 1.8f;
|
||||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||||
const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED;
|
const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED;
|
||||||
|
|
||||||
if (motorSpeed < maxBoostSpeed) {
|
if (motorSpeed < maxBoostSpeed) {
|
||||||
// an active keyboard motor should never be slower than this
|
// an active keyboard motor should never be slower than this
|
||||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||||
|
@ -2089,7 +2093,7 @@ float MyAvatar::getAccelerationEnergy() {
|
||||||
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
||||||
float changeInEnergy = priorVelocity.length() * changeInVelocity * AVATAR_MOVEMENT_ENERGY_CONSTANT;
|
float changeInEnergy = priorVelocity.length() * changeInVelocity * AVATAR_MOVEMENT_ENERGY_CONSTANT;
|
||||||
priorVelocity = velocity;
|
priorVelocity = velocity;
|
||||||
|
|
||||||
return changeInEnergy;
|
return changeInEnergy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2111,4 +2115,3 @@ bool MyAvatar::didTeleport() {
|
||||||
lastPosition = pos;
|
lastPosition = pos;
|
||||||
return (changeInPosition.length() > MAX_AVATAR_MOVEMENT_PER_FRAME);
|
return (changeInPosition.length() > MAX_AVATAR_MOVEMENT_PER_FRAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ signals:
|
||||||
void collisionWithEntity(const Collision& collision);
|
void collisionWithEntity(const Collision& collision);
|
||||||
void energyChanged(float newEnergy);
|
void energyChanged(float newEnergy);
|
||||||
void positionGoneTo();
|
void positionGoneTo();
|
||||||
|
void onLoadComplete();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,11 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
|
||||||
return qApp->getUiSize();
|
return qApp->getUiSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
|
||||||
|
auto rect = qApp->getRecommendedOverlayRect();
|
||||||
|
return qRectToVariant(rect);
|
||||||
|
}
|
||||||
|
|
||||||
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
||||||
// This is where we retrieve the Device Tracker category and then the sub tracker within it
|
// This is where we retrieve the Device Tracker category and then the sub tracker within it
|
||||||
auto icIt = _inputControllers.find(0);
|
auto icIt = _inputControllers.find(0);
|
||||||
|
|
|
@ -96,6 +96,7 @@ public slots:
|
||||||
virtual void releaseJoystick(int joystickIndex);
|
virtual void releaseJoystick(int joystickIndex);
|
||||||
|
|
||||||
virtual glm::vec2 getViewportDimensions() const;
|
virtual glm::vec2 getViewportDimensions() const;
|
||||||
|
virtual QVariant getRecommendedOverlayRect() const;
|
||||||
|
|
||||||
/// Factory to create an InputController
|
/// Factory to create an InputController
|
||||||
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
|
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
|
||||||
|
|
|
@ -1161,6 +1161,7 @@ void Rig::initAnimGraph(const QUrl& url) {
|
||||||
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit onLoadComplete();
|
||||||
});
|
});
|
||||||
connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) {
|
connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) {
|
||||||
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
|
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
|
||||||
|
|
|
@ -32,6 +32,7 @@ typedef std::shared_ptr<Rig> RigPointer;
|
||||||
// However only specific methods thread-safe. Noted below.
|
// However only specific methods thread-safe. Noted below.
|
||||||
|
|
||||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
struct StateHandler {
|
struct StateHandler {
|
||||||
AnimVariantMap results;
|
AnimVariantMap results;
|
||||||
|
@ -223,7 +224,10 @@ public:
|
||||||
|
|
||||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||||
|
|
||||||
protected:
|
signals:
|
||||||
|
void onLoadComplete();
|
||||||
|
|
||||||
|
protected:
|
||||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||||
void updateAnimationStateHandlers();
|
void updateAnimationStateHandlers();
|
||||||
void applyOverridePoses();
|
void applyOverridePoses();
|
||||||
|
|
|
@ -388,7 +388,7 @@ void SwingTwistConstraint::dynamicallyAdjustLimits(const glm::quat& rotation) {
|
||||||
glm::vec3 swungY = swingRotation * Vectors::UNIT_Y;
|
glm::vec3 swungY = swingRotation * Vectors::UNIT_Y;
|
||||||
glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY);
|
glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY);
|
||||||
float theta = atan2f(-swingAxis.z, swingAxis.x);
|
float theta = atan2f(-swingAxis.z, swingAxis.x);
|
||||||
if (isnan(theta)) {
|
if (glm::isnan(theta)) {
|
||||||
// atan2f() will only return NaN if either of its arguments is NaN, which can only
|
// atan2f() will only return NaN if either of its arguments is NaN, which can only
|
||||||
// happen if we've been given a bad rotation. Since a NaN value here could potentially
|
// happen if we've been given a bad rotation. Since a NaN value here could potentially
|
||||||
// cause a crash (we use the value of theta to compute indices into a std::vector)
|
// cause a crash (we use the value of theta to compute indices into a std::vector)
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace AudioConstants {
|
||||||
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
||||||
const float NETWORK_FRAME_SECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / float(AudioConstants::SAMPLE_RATE));
|
const float NETWORK_FRAME_SECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / float(AudioConstants::SAMPLE_RATE));
|
||||||
const float NETWORK_FRAME_MSECS = NETWORK_FRAME_SECS * 1000.0f;
|
const float NETWORK_FRAME_MSECS = NETWORK_FRAME_SECS * 1000.0f;
|
||||||
|
const float NETWORK_FRAMES_PER_SEC = 1.0f / NETWORK_FRAME_SECS;
|
||||||
|
|
||||||
// be careful with overflows when using this constant
|
// be careful with overflows when using this constant
|
||||||
const int NETWORK_FRAME_USECS = static_cast<int>(NETWORK_FRAME_MSECS * 1000.0f);
|
const int NETWORK_FRAME_USECS = static_cast<int>(NETWORK_FRAME_MSECS * 1000.0f);
|
||||||
|
|
|
@ -34,6 +34,7 @@ static const float reticleSize = TWO_PI / 100.0f;
|
||||||
static QString _tooltipId;
|
static QString _tooltipId;
|
||||||
|
|
||||||
const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(3960, 1188); // ~10% more pixel density than old version, 72dx240d FOV
|
const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(3960, 1188); // ~10% more pixel density than old version, 72dx240d FOV
|
||||||
|
const QRect CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT = QRect(956, 0, 2048, 1188); // don't include entire width only center 2048
|
||||||
const float CompositorHelper::VIRTUAL_UI_ASPECT_RATIO = (float)VIRTUAL_SCREEN_SIZE.x / (float)VIRTUAL_SCREEN_SIZE.y;
|
const float CompositorHelper::VIRTUAL_UI_ASPECT_RATIO = (float)VIRTUAL_SCREEN_SIZE.x / (float)VIRTUAL_SCREEN_SIZE.y;
|
||||||
const vec2 CompositorHelper::VIRTUAL_UI_TARGET_FOV = vec2(PI * 3.0f / 2.0f, PI * 3.0f / 2.0f / VIRTUAL_UI_ASPECT_RATIO);
|
const vec2 CompositorHelper::VIRTUAL_UI_TARGET_FOV = vec2(PI * 3.0f / 2.0f, PI * 3.0f / 2.0f / VIRTUAL_UI_ASPECT_RATIO);
|
||||||
const vec2 CompositorHelper::MOUSE_EXTENTS_ANGULAR_SIZE = vec2(PI * 2.0f, PI * 0.95f); // horizontal: full sphere, vertical: ~5deg from poles
|
const vec2 CompositorHelper::MOUSE_EXTENTS_ANGULAR_SIZE = vec2(PI * 2.0f, PI * 0.95f); // horizontal: full sphere, vertical: ~5deg from poles
|
||||||
|
|
|
@ -42,6 +42,7 @@ class CompositorHelper : public QObject, public Dependency {
|
||||||
Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
|
Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
|
||||||
public:
|
public:
|
||||||
static const uvec2 VIRTUAL_SCREEN_SIZE;
|
static const uvec2 VIRTUAL_SCREEN_SIZE;
|
||||||
|
static const QRect VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
|
||||||
static const float VIRTUAL_UI_ASPECT_RATIO;
|
static const float VIRTUAL_UI_ASPECT_RATIO;
|
||||||
static const vec2 VIRTUAL_UI_TARGET_FOV;
|
static const vec2 VIRTUAL_UI_TARGET_FOV;
|
||||||
static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE;
|
static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE;
|
||||||
|
|
|
@ -34,6 +34,11 @@ glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
||||||
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect HmdDisplayPlugin::getRecommendedOverlayRect() const {
|
||||||
|
return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool HmdDisplayPlugin::internalActivate() {
|
bool HmdDisplayPlugin::internalActivate() {
|
||||||
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ public:
|
||||||
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
|
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
|
||||||
bool isDisplayVisible() const override { return isHmdMounted(); }
|
bool isDisplayVisible() const override { return isHmdMounted(); }
|
||||||
|
|
||||||
|
QRect getRecommendedOverlayRect() const override final;
|
||||||
|
|
||||||
virtual glm::mat4 getHeadPose() const override;
|
virtual glm::mat4 getHeadPose() const override;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,29 @@ EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
// it is registered with ScriptEngines, which will call deleteLater for us.
|
// it is registered with ScriptEngines, which will call deleteLater for us.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
|
||||||
|
|
||||||
|
void EntityTreeRenderer::setupEntitiesScriptEngine() {
|
||||||
|
QSharedPointer<ScriptEngine> oldEngine = _entitiesScriptEngine; // save the old engine through this function, so the EntityScriptingInterface doesn't have problems with it.
|
||||||
|
_entitiesScriptEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(NO_SCRIPT, QString("Entities %1").arg(++_entitiesScriptEngineCount)), &QObject::deleteLater);
|
||||||
|
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data());
|
||||||
|
_entitiesScriptEngine->runInThread();
|
||||||
|
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine.data());
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
leaveAllEntities();
|
leaveAllEntities();
|
||||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
if (_entitiesScriptEngine) {
|
||||||
|
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||||
|
_entitiesScriptEngine->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_wantScripts && !_shuttingDown) {
|
||||||
|
// NOTE: you can't actually need to delete it here because when we call setupEntitiesScriptEngine it will
|
||||||
|
// assign a new instance to our shared pointer, which will deref the old instance and ultimately call
|
||||||
|
// the custom deleter which calls deleteLater
|
||||||
|
setupEntitiesScriptEngine();
|
||||||
|
}
|
||||||
|
|
||||||
auto scene = _viewState->getMain3DScene();
|
auto scene = _viewState->getMain3DScene();
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
@ -94,7 +114,7 @@ void EntityTreeRenderer::reloadEntityScripts() {
|
||||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||||
foreach(auto entity, _entitiesInScene) {
|
foreach(auto entity, _entitiesInScene) {
|
||||||
if (!entity->getScript().isEmpty()) {
|
if (!entity->getScript().isEmpty()) {
|
||||||
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true);
|
ScriptEngine::loadEntityScript(_entitiesScriptEngine, entity->getEntityItemID(), entity->getScript(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,10 +125,7 @@ void EntityTreeRenderer::init() {
|
||||||
entityTree->setFBXService(this);
|
entityTree->setFBXService(this);
|
||||||
|
|
||||||
if (_wantScripts) {
|
if (_wantScripts) {
|
||||||
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities");
|
setupEntitiesScriptEngine();
|
||||||
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
|
||||||
_entitiesScriptEngine->runInThread();
|
|
||||||
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities
|
forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities
|
||||||
|
@ -122,6 +139,8 @@ void EntityTreeRenderer::init() {
|
||||||
void EntityTreeRenderer::shutdown() {
|
void EntityTreeRenderer::shutdown() {
|
||||||
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
||||||
_shuttingDown = true;
|
_shuttingDown = true;
|
||||||
|
|
||||||
|
clear(); // always clear() on shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::setTree(OctreePointer newTree) {
|
void EntityTreeRenderer::setTree(OctreePointer newTree) {
|
||||||
|
@ -763,7 +782,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const
|
||||||
if (entity && entity->shouldPreloadScript()) {
|
if (entity && entity->shouldPreloadScript()) {
|
||||||
QString scriptUrl = entity->getScript();
|
QString scriptUrl = entity->getScript();
|
||||||
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
|
ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload);
|
||||||
entity->scriptHasPreloaded();
|
entity->scriptHasPreloaded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,8 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setupEntitiesScriptEngine();
|
||||||
|
|
||||||
void addEntityToScene(EntityItemPointer entity);
|
void addEntityToScene(EntityItemPointer entity);
|
||||||
bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar);
|
bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar);
|
||||||
|
|
||||||
|
@ -155,7 +157,7 @@ private:
|
||||||
NetworkTexturePointer _ambientTexture;
|
NetworkTexturePointer _ambientTexture;
|
||||||
|
|
||||||
bool _wantScripts;
|
bool _wantScripts;
|
||||||
ScriptEngine* _entitiesScriptEngine;
|
QSharedPointer<ScriptEngine> _entitiesScriptEngine;
|
||||||
|
|
||||||
bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
|
bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
|
||||||
const EntityItemID& id, const Collision& collision);
|
const EntityItemID& id, const Collision& collision);
|
||||||
|
@ -196,6 +198,8 @@ private:
|
||||||
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
|
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
|
||||||
// For Scene.shouldRenderEntities
|
// For Scene.shouldRenderEntities
|
||||||
QList<EntityItemID> _entityIDsLastInScene;
|
QList<EntityItemID> _entityIDsLastInScene;
|
||||||
|
|
||||||
|
static int _entitiesScriptEngineCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
const QString SHADER_COMMON = R"SHADER(#version 410 core
|
const QString SHADER_COMMON = R"SHADER(
|
||||||
layout(location = 0) out vec4 _fragColor0;
|
layout(location = 0) out vec4 _fragColor0;
|
||||||
layout(location = 1) out vec4 _fragColor1;
|
layout(location = 1) out vec4 _fragColor1;
|
||||||
layout(location = 2) out vec4 _fragColor2;
|
layout(location = 2) out vec4 _fragColor2;
|
||||||
|
|
|
@ -414,7 +414,13 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) {
|
||||||
|
std::lock_guard<std::mutex> lock(_entitiesScriptEngineLock);
|
||||||
|
_entitiesScriptEngine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) {
|
void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) {
|
||||||
|
std::lock_guard<std::mutex> lock(_entitiesScriptEngineLock);
|
||||||
if (_entitiesScriptEngine) {
|
if (_entitiesScriptEngine) {
|
||||||
EntityItemID entityID{ id };
|
EntityItemID entityID{ id };
|
||||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params);
|
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params);
|
||||||
|
|
|
@ -71,7 +71,7 @@ public:
|
||||||
|
|
||||||
void setEntityTree(EntityTreePointer modelTree);
|
void setEntityTree(EntityTreePointer modelTree);
|
||||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||||
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; }
|
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine);
|
||||||
float calculateCost(float mass, float oldVelocity, float newVelocity);
|
float calculateCost(float mass, float oldVelocity, float newVelocity);
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -214,6 +214,8 @@ private:
|
||||||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
|
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
|
||||||
|
|
||||||
EntityTreePointer _entityTree;
|
EntityTreePointer _entityTree;
|
||||||
|
|
||||||
|
std::mutex _entitiesScriptEngineLock;
|
||||||
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr };
|
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr };
|
||||||
|
|
||||||
bool _bidOnSimulationOwnership { false };
|
bool _bidOnSimulationOwnership { false };
|
||||||
|
|
|
@ -83,11 +83,18 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
friend class OffscreenQmlSurface;
|
friend class OffscreenQmlSurface;
|
||||||
|
|
||||||
|
QJsonObject getGLContextData();
|
||||||
|
|
||||||
Queue _queue;
|
Queue _queue;
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
QWaitCondition _waitCondition;
|
QWaitCondition _waitCondition;
|
||||||
std::atomic<bool> _rendering { false };
|
std::atomic<bool> _rendering { false };
|
||||||
|
|
||||||
|
QJsonObject _glData;
|
||||||
|
QMutex _glMutex;
|
||||||
|
QWaitCondition _glWait;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Event-driven methods
|
// Event-driven methods
|
||||||
void init();
|
void init();
|
||||||
|
@ -211,22 +218,31 @@ void OffscreenQmlRenderThread::setupFbo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonObject OffscreenQmlRenderThread::getGLContextData() {
|
||||||
|
_glMutex.lock();
|
||||||
|
if (_glData.isEmpty()) {
|
||||||
|
_glWait.wait(&_glMutex);
|
||||||
|
}
|
||||||
|
_glMutex.unlock();
|
||||||
|
return _glData;
|
||||||
|
}
|
||||||
|
|
||||||
void OffscreenQmlRenderThread::init() {
|
void OffscreenQmlRenderThread::init() {
|
||||||
qDebug() << "Initializing QML Renderer";
|
qDebug() << "Initializing QML Renderer";
|
||||||
|
|
||||||
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
|
|
||||||
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
|
|
||||||
|
|
||||||
if (!_canvas.makeCurrent()) {
|
if (!_canvas.makeCurrent()) {
|
||||||
qWarning("Failed to make context current on QML Renderer Thread");
|
qWarning("Failed to make context current on QML Renderer Thread");
|
||||||
_quit = true;
|
_quit = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose GL data to QML
|
_glMutex.lock();
|
||||||
auto glData = getGLContextData();
|
_glData = ::getGLContextData();
|
||||||
auto setGL = [=]{ _surface->getRootContext()->setContextProperty("GL", glData); };
|
_glMutex.unlock();
|
||||||
_surface->executeOnUiThread(setGL);
|
_glWait.wakeAll();
|
||||||
|
|
||||||
|
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
|
||||||
|
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
|
||||||
|
|
||||||
_renderControl->initialize(_canvas.getContext());
|
_renderControl->initialize(_canvas.getContext());
|
||||||
setupFbo();
|
setupFbo();
|
||||||
|
@ -386,14 +402,16 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
|
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_qmlEngine->rootContext()->setContextProperty("GL", _renderer->getGLContextData());
|
||||||
|
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||||
|
_qmlComponent = new QQmlComponent(_qmlEngine);
|
||||||
|
|
||||||
// When Quick says there is a need to render, we will not render immediately. Instead,
|
// When Quick says there is a need to render, we will not render immediately. Instead,
|
||||||
// a timer with a small interval is used to get better performance.
|
// a timer with a small interval is used to get better performance.
|
||||||
_updateTimer.setInterval(MIN_TIMER_MS);
|
|
||||||
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
|
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
|
||||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit);
|
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit);
|
||||||
|
_updateTimer.setInterval(MIN_TIMER_MS);
|
||||||
_updateTimer.start();
|
_updateTimer.start();
|
||||||
_qmlComponent = new QQmlComponent(_qmlEngine);
|
|
||||||
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
|
|
||||||
<@if GLPROFILE == PC_GL @>
|
<@if GLPROFILE == PC_GL @>
|
||||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||||
<@def VERSION_HEADER #version 410 core@>
|
<@def VERSION_HEADER //PC 410 core@>
|
||||||
<@elif GLPROFILE == MAC_GL @>
|
<@elif GLPROFILE == MAC_GL @>
|
||||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||||
<@def VERSION_HEADER #version 410 core@>
|
<@def VERSION_HEADER //MAC 410 core@>
|
||||||
<@else@>
|
<@else@>
|
||||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||||
<@def VERSION_HEADER #version 410 core@>
|
<@def VERSION_HEADER //410 core@>
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
int _DSNumAPIDrawcalls = 0;
|
int _DSNumAPIDrawcalls = 0;
|
||||||
int _DSNumDrawcalls = 0;
|
int _DSNumDrawcalls = 0;
|
||||||
int _DSNumTriangles = 0;
|
int _DSNumTriangles = 0;
|
||||||
|
|
||||||
|
int _PSNumSetPipelines = 0;
|
||||||
|
|
||||||
ContextStats() {}
|
ContextStats() {}
|
||||||
ContextStats(const ContextStats& stats) = default;
|
ContextStats(const ContextStats& stats) = default;
|
||||||
|
|
|
@ -461,8 +461,10 @@ void GLBackend::resetStages() {
|
||||||
|
|
||||||
#define ADD_COMMAND_GL(call) _commands.push_back(COMMAND_##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 GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc
|
||||||
#define DO_IT_NOW(call, offset)
|
// THis will be used in the next PR
|
||||||
|
// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc)
|
||||||
|
|
||||||
|
|
||||||
void Batch::_glActiveBindTexture(GLenum unit, GLenum target, GLuint texture) {
|
void Batch::_glActiveBindTexture(GLenum unit, GLenum target, GLuint texture) {
|
||||||
// clean the cache on the texture unit we are going to use so the next call to setResourceTexture() at the same slot works fine
|
// clean the cache on the texture unit we are going to use so the next call to setResourceTexture() at the same slot works fine
|
||||||
|
@ -472,14 +474,11 @@ void Batch::_glActiveBindTexture(GLenum unit, GLenum target, GLuint texture) {
|
||||||
_params.push_back(texture);
|
_params.push_back(texture);
|
||||||
_params.push_back(target);
|
_params.push_back(target);
|
||||||
_params.push_back(unit);
|
_params.push_back(unit);
|
||||||
|
|
||||||
|
|
||||||
DO_IT_NOW(_glActiveBindTexture, 3);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
|
||||||
glActiveTexture(batch._params[paramOffset + 2]._uint);
|
glActiveTexture(batch._params[paramOffset + 2]._uint);
|
||||||
glBindTexture(
|
glBindTexture(
|
||||||
batch._params[paramOffset + 1]._uint,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._uint),
|
||||||
batch._params[paramOffset + 0]._uint);
|
batch._params[paramOffset + 0]._uint);
|
||||||
|
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
|
@ -492,8 +491,6 @@ void Batch::_glUniform1i(GLint location, GLint v0) {
|
||||||
ADD_COMMAND_GL(glUniform1i);
|
ADD_COMMAND_GL(glUniform1i);
|
||||||
_params.push_back(v0);
|
_params.push_back(v0);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform1i, 1);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -503,7 +500,7 @@ void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform1f(
|
glUniform1f(
|
||||||
batch._params[paramOffset + 1]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int),
|
||||||
batch._params[paramOffset + 0]._int);
|
batch._params[paramOffset + 0]._int);
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
@ -515,8 +512,6 @@ void Batch::_glUniform1f(GLint location, GLfloat v0) {
|
||||||
ADD_COMMAND_GL(glUniform1f);
|
ADD_COMMAND_GL(glUniform1f);
|
||||||
_params.push_back(v0);
|
_params.push_back(v0);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform1f, 1);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -527,7 +522,7 @@ void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
|
|
||||||
glUniform1f(
|
glUniform1f(
|
||||||
batch._params[paramOffset + 1]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int),
|
||||||
batch._params[paramOffset + 0]._float);
|
batch._params[paramOffset + 0]._float);
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
@ -538,8 +533,6 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) {
|
||||||
_params.push_back(v1);
|
_params.push_back(v1);
|
||||||
_params.push_back(v0);
|
_params.push_back(v0);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform2f, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
|
||||||
|
@ -550,7 +543,7 @@ void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform2f(
|
glUniform2f(
|
||||||
batch._params[paramOffset + 2]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||||
batch._params[paramOffset + 1]._float,
|
batch._params[paramOffset + 1]._float,
|
||||||
batch._params[paramOffset + 0]._float);
|
batch._params[paramOffset + 0]._float);
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
|
@ -563,8 +556,6 @@ void Batch::_glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
|
||||||
_params.push_back(v1);
|
_params.push_back(v1);
|
||||||
_params.push_back(v0);
|
_params.push_back(v0);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform3f, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
|
||||||
|
@ -575,7 +566,7 @@ void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform3f(
|
glUniform3f(
|
||||||
batch._params[paramOffset + 3]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
|
||||||
batch._params[paramOffset + 2]._float,
|
batch._params[paramOffset + 2]._float,
|
||||||
batch._params[paramOffset + 1]._float,
|
batch._params[paramOffset + 1]._float,
|
||||||
batch._params[paramOffset + 0]._float);
|
batch._params[paramOffset + 0]._float);
|
||||||
|
@ -591,8 +582,6 @@ void Batch::_glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLf
|
||||||
_params.push_back(v1);
|
_params.push_back(v1);
|
||||||
_params.push_back(v0);
|
_params.push_back(v0);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform4f, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -604,7 +593,7 @@ void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform4f(
|
glUniform4f(
|
||||||
batch._params[paramOffset + 4]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 4]._int),
|
||||||
batch._params[paramOffset + 3]._float,
|
batch._params[paramOffset + 3]._float,
|
||||||
batch._params[paramOffset + 2]._float,
|
batch._params[paramOffset + 2]._float,
|
||||||
batch._params[paramOffset + 1]._float,
|
batch._params[paramOffset + 1]._float,
|
||||||
|
@ -619,8 +608,6 @@ void Batch::_glUniform3fv(GLint location, GLsizei count, const GLfloat* value) {
|
||||||
_params.push_back(cacheData(count * VEC3_SIZE, value));
|
_params.push_back(cacheData(count * VEC3_SIZE, value));
|
||||||
_params.push_back(count);
|
_params.push_back(count);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform3fv, 3);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -630,7 +617,7 @@ void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform3fv(
|
glUniform3fv(
|
||||||
batch._params[paramOffset + 2]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||||
batch._params[paramOffset + 1]._uint,
|
batch._params[paramOffset + 1]._uint,
|
||||||
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
|
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
|
||||||
|
|
||||||
|
@ -645,8 +632,6 @@ void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) {
|
||||||
_params.push_back(cacheData(count * VEC4_SIZE, value));
|
_params.push_back(cacheData(count * VEC4_SIZE, value));
|
||||||
_params.push_back(count);
|
_params.push_back(count);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform4fv, 3);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -656,7 +641,7 @@ void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
|
|
||||||
GLint location = batch._params[paramOffset + 2]._int;
|
GLint location = GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int);
|
||||||
GLsizei count = batch._params[paramOffset + 1]._uint;
|
GLsizei count = batch._params[paramOffset + 1]._uint;
|
||||||
const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
|
const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
|
||||||
glUniform4fv(location, count, value);
|
glUniform4fv(location, count, value);
|
||||||
|
@ -671,8 +656,6 @@ void Batch::_glUniform4iv(GLint location, GLsizei count, const GLint* value) {
|
||||||
_params.push_back(cacheData(count * VEC4_SIZE, value));
|
_params.push_back(cacheData(count * VEC4_SIZE, value));
|
||||||
_params.push_back(count);
|
_params.push_back(count);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniform4iv, 3);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -682,7 +665,7 @@ void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniform4iv(
|
glUniform4iv(
|
||||||
batch._params[paramOffset + 2]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||||
batch._params[paramOffset + 1]._uint,
|
batch._params[paramOffset + 1]._uint,
|
||||||
(const GLint*)batch.editData(batch._params[paramOffset + 0]._uint));
|
(const GLint*)batch.editData(batch._params[paramOffset + 0]._uint));
|
||||||
|
|
||||||
|
@ -697,8 +680,6 @@ void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpo
|
||||||
_params.push_back(transpose);
|
_params.push_back(transpose);
|
||||||
_params.push_back(count);
|
_params.push_back(count);
|
||||||
_params.push_back(location);
|
_params.push_back(location);
|
||||||
|
|
||||||
DO_IT_NOW(_glUniformMatrix4fv, 4);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
|
||||||
if (_pipeline._program == 0) {
|
if (_pipeline._program == 0) {
|
||||||
|
@ -708,7 +689,7 @@ void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
updatePipeline();
|
updatePipeline();
|
||||||
glUniformMatrix4fv(
|
glUniformMatrix4fv(
|
||||||
batch._params[paramOffset + 3]._int,
|
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
|
||||||
batch._params[paramOffset + 2]._uint,
|
batch._params[paramOffset + 2]._uint,
|
||||||
batch._params[paramOffset + 1]._uint,
|
batch._params[paramOffset + 1]._uint,
|
||||||
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
|
(const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint));
|
||||||
|
@ -722,8 +703,6 @@ void Batch::_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
|
||||||
_params.push_back(blue);
|
_params.push_back(blue);
|
||||||
_params.push_back(green);
|
_params.push_back(green);
|
||||||
_params.push_back(red);
|
_params.push_back(red);
|
||||||
|
|
||||||
DO_IT_NOW(_glColor4f, 4);
|
|
||||||
}
|
}
|
||||||
void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) {
|
void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) {
|
||||||
|
|
||||||
|
|
|
@ -153,17 +153,42 @@ public:
|
||||||
|
|
||||||
class GLShader : public GPUObject {
|
class GLShader : public GPUObject {
|
||||||
public:
|
public:
|
||||||
GLuint _shader;
|
enum Version {
|
||||||
GLuint _program;
|
Mono = 0,
|
||||||
|
|
||||||
|
NumVersions
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderObject {
|
||||||
|
GLuint glshader{ 0 };
|
||||||
|
GLuint glprogram{ 0 };
|
||||||
|
GLint transformCameraSlot{ -1 };
|
||||||
|
GLint transformObjectSlot{ -1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
using ShaderObjects = std::array< ShaderObject, NumVersions >;
|
||||||
|
using UniformMapping = std::map<GLint, GLint>;
|
||||||
|
using UniformMappingVersions = std::vector<UniformMapping>;
|
||||||
|
|
||||||
GLint _transformCameraSlot = -1;
|
|
||||||
GLint _transformObjectSlot = -1;
|
|
||||||
|
|
||||||
GLShader();
|
GLShader();
|
||||||
~GLShader();
|
~GLShader();
|
||||||
|
|
||||||
|
ShaderObjects _shaderObjects;
|
||||||
|
UniformMappingVersions _uniformMappings;
|
||||||
|
|
||||||
|
GLuint getProgram() const {
|
||||||
|
return _shaderObjects[Mono].glprogram;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint getUniformLocation(GLint srcLoc) {
|
||||||
|
return srcLoc;
|
||||||
|
// THIS will be used in the next PR
|
||||||
|
// return _uniformMappings[Mono][srcLoc];
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
static GLShader* syncGPUObject(const Shader& shader);
|
static GLShader* syncGPUObject(const Shader& shader);
|
||||||
static GLuint getShaderID(const ShaderPointer& shader);
|
|
||||||
|
|
||||||
class GLState : public GPUObject {
|
class GLState : public GPUObject {
|
||||||
public:
|
public:
|
||||||
|
@ -464,6 +489,7 @@ protected:
|
||||||
PipelinePointer _pipeline;
|
PipelinePointer _pipeline;
|
||||||
|
|
||||||
GLuint _program;
|
GLuint _program;
|
||||||
|
GLShader* _programShader;
|
||||||
bool _invalidProgram;
|
bool _invalidProgram;
|
||||||
|
|
||||||
State::Data _stateCache;
|
State::Data _stateCache;
|
||||||
|
@ -475,6 +501,7 @@ protected:
|
||||||
PipelineStageState() :
|
PipelineStageState() :
|
||||||
_pipeline(),
|
_pipeline(),
|
||||||
_program(0),
|
_program(0),
|
||||||
|
_programShader(nullptr),
|
||||||
_invalidProgram(false),
|
_invalidProgram(false),
|
||||||
_stateCache(State::DEFAULT),
|
_stateCache(State::DEFAULT),
|
||||||
_stateSignatureCache(0),
|
_stateSignatureCache(0),
|
||||||
|
|
|
@ -64,11 +64,15 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A true new Pipeline
|
||||||
|
_stats._PSNumSetPipelines++;
|
||||||
|
|
||||||
// null pipeline == reset
|
// null pipeline == reset
|
||||||
if (!pipeline) {
|
if (!pipeline) {
|
||||||
_pipeline._pipeline.reset();
|
_pipeline._pipeline.reset();
|
||||||
|
|
||||||
_pipeline._program = 0;
|
_pipeline._program = 0;
|
||||||
|
_pipeline._programShader = nullptr;
|
||||||
_pipeline._invalidProgram = true;
|
_pipeline._invalidProgram = true;
|
||||||
|
|
||||||
_pipeline._state = nullptr;
|
_pipeline._state = nullptr;
|
||||||
|
@ -80,8 +84,10 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the program cache
|
// check the program cache
|
||||||
if (_pipeline._program != pipelineObject->_program->_program) {
|
GLuint glprogram = pipelineObject->_program->getProgram();
|
||||||
_pipeline._program = pipelineObject->_program->_program;
|
if (_pipeline._program != glprogram) {
|
||||||
|
_pipeline._program = glprogram;
|
||||||
|
_pipeline._programShader = pipelineObject->_program;
|
||||||
_pipeline._invalidProgram = true;
|
_pipeline._invalidProgram = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +148,7 @@ void GLBackend::resetPipelineStage() {
|
||||||
// Second the shader side
|
// Second the shader side
|
||||||
_pipeline._invalidProgram = false;
|
_pipeline._invalidProgram = false;
|
||||||
_pipeline._program = 0;
|
_pipeline._program = 0;
|
||||||
|
_pipeline._programShader = nullptr;
|
||||||
_pipeline._pipeline.reset();
|
_pipeline._pipeline.reset();
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,27 +13,205 @@
|
||||||
|
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
GLBackend::GLShader::GLShader() :
|
GLBackend::GLShader::GLShader()
|
||||||
_shader(0),
|
{
|
||||||
_program(0)
|
}
|
||||||
{}
|
|
||||||
|
|
||||||
GLBackend::GLShader::~GLShader() {
|
GLBackend::GLShader::~GLShader() {
|
||||||
if (_shader != 0) {
|
for (auto& so : _shaderObjects) {
|
||||||
glDeleteShader(_shader);
|
if (so.glshader != 0) {
|
||||||
}
|
glDeleteShader(so.glshader);
|
||||||
if (_program != 0) {
|
}
|
||||||
glDeleteProgram(_program);
|
if (so.glprogram != 0) {
|
||||||
|
glDeleteProgram(so.glprogram);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeBindings(GLBackend::GLShader* shader) {
|
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject) {
|
||||||
if(!shader || !shader->_program) {
|
if (shaderSource.empty()) {
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the shader object
|
||||||
|
GLuint glshader = glCreateShader(shaderDomain);
|
||||||
|
if (!glshader) {
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader object";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the source
|
||||||
|
const int NUM_SOURCE_STRINGS = 2;
|
||||||
|
const GLchar* srcstr[] = { defines.c_str(), shaderSource.c_str() };
|
||||||
|
glShaderSource(glshader, NUM_SOURCE_STRINGS, srcstr, NULL);
|
||||||
|
|
||||||
|
// Compile !
|
||||||
|
glCompileShader(glshader);
|
||||||
|
|
||||||
|
// check if shader compiled
|
||||||
|
GLint compiled = 0;
|
||||||
|
glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled);
|
||||||
|
|
||||||
|
// if compilation fails
|
||||||
|
if (!compiled) {
|
||||||
|
// save the source code to a temp file so we can debug easily
|
||||||
|
/* std::ofstream filestream;
|
||||||
|
filestream.open("debugshader.glsl");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << shaderSource->source;
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
GLint infoLength = 0;
|
||||||
|
glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength);
|
||||||
|
|
||||||
|
char* temp = new char[infoLength];
|
||||||
|
glGetShaderInfoLog(glshader, infoLength, NULL, temp);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
filestream.open("debugshader.glsl.info.txt");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << std::string(temp);
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
||||||
|
for (auto s : srcstr) {
|
||||||
|
qCWarning(gpulogging) << s;
|
||||||
|
}
|
||||||
|
qCWarning(gpulogging) << "GLShader::compileShader - errors:";
|
||||||
|
qCWarning(gpulogging) << temp;
|
||||||
|
delete[] temp;
|
||||||
|
|
||||||
|
glDeleteShader(glshader);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint glprogram = 0;
|
||||||
|
#ifdef SEPARATE_PROGRAM
|
||||||
|
// so far so good, program is almost done, need to link:
|
||||||
|
GLuint glprogram = glCreateProgram();
|
||||||
|
if (!glprogram) {
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader & gl program object";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||||
|
glAttachShader(glprogram, glshader);
|
||||||
|
glLinkProgram(glprogram);
|
||||||
|
|
||||||
|
GLint linked = 0;
|
||||||
|
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
||||||
|
|
||||||
|
if (!linked) {
|
||||||
|
/*
|
||||||
|
// save the source code to a temp file so we can debug easily
|
||||||
|
std::ofstream filestream;
|
||||||
|
filestream.open("debugshader.glsl");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << shaderSource->source;
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
GLint infoLength = 0;
|
||||||
|
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
|
||||||
|
|
||||||
|
char* temp = new char[infoLength];
|
||||||
|
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
|
||||||
|
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileShader - failed to LINK the gl program object :";
|
||||||
|
qCDebug(gpulogging) << temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
filestream.open("debugshader.glsl.info.txt");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << String(temp);
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
delete[] temp;
|
||||||
|
|
||||||
|
glDeleteShader(glshader);
|
||||||
|
glDeleteProgram(glprogram);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
shaderObject = glshader;
|
||||||
|
programObject = glprogram;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint compileProgram(const std::vector<GLuint>& glshaders) {
|
||||||
|
// A brand new program:
|
||||||
|
GLuint glprogram = glCreateProgram();
|
||||||
|
if (!glprogram) {
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileProgram - failed to create the gl program object";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE);
|
||||||
|
// Create the program from the sub shaders
|
||||||
|
for (auto so : glshaders) {
|
||||||
|
glAttachShader(glprogram, so);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link!
|
||||||
|
glLinkProgram(glprogram);
|
||||||
|
|
||||||
|
GLint linked = 0;
|
||||||
|
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
||||||
|
|
||||||
|
if (!linked) {
|
||||||
|
/*
|
||||||
|
// save the source code to a temp file so we can debug easily
|
||||||
|
std::ofstream filestream;
|
||||||
|
filestream.open("debugshader.glsl");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << shaderSource->source;
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
GLint infoLength = 0;
|
||||||
|
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
|
||||||
|
|
||||||
|
char* temp = new char[infoLength];
|
||||||
|
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
|
||||||
|
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileProgram - failed to LINK the gl program object :";
|
||||||
|
qCDebug(gpulogging) << temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
filestream.open("debugshader.glsl.info.txt");
|
||||||
|
if (filestream.is_open()) {
|
||||||
|
filestream << std::string(temp);
|
||||||
|
filestream.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
delete[] temp;
|
||||||
|
|
||||||
|
glDeleteProgram(glprogram);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glprogram;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void makeProgramBindings(GLBackend::GLShader::ShaderObject& shaderObject) {
|
||||||
|
if (!shaderObject.glprogram) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GLuint glprogram = shader->_program;
|
GLuint glprogram = shaderObject.glprogram;
|
||||||
GLint loc = -1;
|
GLint loc = -1;
|
||||||
|
|
||||||
//Check for gpu specific attribute slotBindings
|
//Check for gpu specific attribute slotBindings
|
||||||
loc = glGetAttribLocation(glprogram, "inPosition");
|
loc = glGetAttribLocation(glprogram, "inPosition");
|
||||||
if (loc >= 0 && loc != gpu::Stream::POSITION) {
|
if (loc >= 0 && loc != gpu::Stream::POSITION) {
|
||||||
|
@ -96,226 +274,111 @@ void makeBindings(GLBackend::GLShader* shader) {
|
||||||
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
|
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
|
||||||
if (loc >= 0) {
|
if (loc >= 0) {
|
||||||
glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
||||||
shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
|
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
|
||||||
if (loc >= 0) {
|
if (loc >= 0) {
|
||||||
glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
||||||
shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
|
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
|
||||||
if (loc >= 0) {
|
if (loc >= 0) {
|
||||||
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
|
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
|
||||||
shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::GLShader* compileShader(const Shader& shader) {
|
GLBackend::GLShader* compileBackendShader(const Shader& shader) {
|
||||||
// Any GLSLprogram ? normally yes...
|
// Any GLSLprogram ? normally yes...
|
||||||
const std::string& shaderSource = shader.getSource().getCode();
|
const std::string& shaderSource = shader.getSource().getCode();
|
||||||
if (shaderSource.empty()) {
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
|
// GLSL version
|
||||||
return nullptr;
|
const std::string glslVersion = {
|
||||||
}
|
"#version 410 core"
|
||||||
|
};
|
||||||
|
|
||||||
// Shader domain
|
// Shader domain
|
||||||
const GLenum SHADER_DOMAINS[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER };
|
const int NUM_SHADER_DOMAINS = 2;
|
||||||
|
const GLenum SHADER_DOMAINS[NUM_SHADER_DOMAINS] = {
|
||||||
|
GL_VERTEX_SHADER,
|
||||||
|
GL_FRAGMENT_SHADER
|
||||||
|
};
|
||||||
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
|
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
|
||||||
|
|
||||||
// Create the shader object
|
// Domain specific defines
|
||||||
GLuint glshader = glCreateShader(shaderDomain);
|
const std::string domainDefines[NUM_SHADER_DOMAINS] = {
|
||||||
if (!glshader) {
|
"#define VERTEX_SHADER",
|
||||||
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader object";
|
"#define PIXEL_SHADER"
|
||||||
return nullptr;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Assign the source
|
|
||||||
const GLchar* srcstr = shaderSource.c_str();
|
|
||||||
glShaderSource(glshader, 1, &srcstr, NULL);
|
|
||||||
|
|
||||||
// Compile !
|
|
||||||
glCompileShader(glshader);
|
|
||||||
|
|
||||||
// check if shader compiled
|
// Versions specific of the shader
|
||||||
GLint compiled = 0;
|
const std::string versionDefines[GLBackend::GLShader::NumVersions] = {
|
||||||
glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled);
|
""
|
||||||
|
};
|
||||||
|
|
||||||
// if compilation fails
|
GLBackend::GLShader::ShaderObjects shaderObjects;
|
||||||
if (!compiled) {
|
|
||||||
// save the source code to a temp file so we can debug easily
|
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||||
/* std::ofstream filestream;
|
auto& shaderObject = shaderObjects[version];
|
||||||
filestream.open("debugshader.glsl");
|
|
||||||
if (filestream.is_open()) {
|
std::string shaderDefines = glslVersion + "\n" + domainDefines[shader.getType()] + "\n" + versionDefines[version];
|
||||||
filestream << shaderSource->source;
|
|
||||||
filestream.close();
|
bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram);
|
||||||
|
if (!result) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
GLint infoLength = 0;
|
|
||||||
glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength);
|
|
||||||
|
|
||||||
char* temp = new char[infoLength] ;
|
|
||||||
glGetShaderInfoLog(glshader, infoLength, NULL, temp);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
filestream.open("debugshader.glsl.info.txt");
|
|
||||||
if (filestream.is_open()) {
|
|
||||||
filestream << std::string(temp);
|
|
||||||
filestream.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
qCWarning(gpulogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
|
||||||
qCWarning(gpulogging) << srcstr;
|
|
||||||
qCWarning(gpulogging) << "GLShader::compileShader - errors:";
|
|
||||||
qCWarning(gpulogging) << temp;
|
|
||||||
delete[] temp;
|
|
||||||
|
|
||||||
glDeleteShader(glshader);
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint glprogram = 0;
|
|
||||||
#ifdef SEPARATE_PROGRAM
|
|
||||||
// so far so good, program is almost done, need to link:
|
|
||||||
GLuint glprogram = glCreateProgram();
|
|
||||||
if (!glprogram) {
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader & gl program object";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
|
||||||
glAttachShader(glprogram, glshader);
|
|
||||||
glLinkProgram(glprogram);
|
|
||||||
|
|
||||||
GLint linked = 0;
|
|
||||||
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
|
||||||
|
|
||||||
if (!linked) {
|
|
||||||
/*
|
|
||||||
// save the source code to a temp file so we can debug easily
|
|
||||||
std::ofstream filestream;
|
|
||||||
filestream.open("debugshader.glsl");
|
|
||||||
if (filestream.is_open()) {
|
|
||||||
filestream << shaderSource->source;
|
|
||||||
filestream.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
GLint infoLength = 0;
|
|
||||||
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
|
|
||||||
|
|
||||||
char* temp = new char[infoLength] ;
|
|
||||||
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
|
|
||||||
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileShader - failed to LINK the gl program object :";
|
|
||||||
qCDebug(gpulogging) << temp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
filestream.open("debugshader.glsl.info.txt");
|
|
||||||
if (filestream.is_open()) {
|
|
||||||
filestream << String(temp);
|
|
||||||
filestream.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
delete[] temp;
|
|
||||||
|
|
||||||
glDeleteShader(glshader);
|
|
||||||
glDeleteProgram(glprogram);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// So far so good, the shader is created successfully
|
// So far so good, the shader is created successfully
|
||||||
GLBackend::GLShader* object = new GLBackend::GLShader();
|
GLBackend::GLShader* object = new GLBackend::GLShader();
|
||||||
object->_shader = glshader;
|
object->_shaderObjects = shaderObjects;
|
||||||
object->_program = glprogram;
|
|
||||||
|
|
||||||
makeBindings(object);
|
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLBackend::GLShader* compileProgram(const Shader& program) {
|
GLBackend::GLShader* compileBackendProgram(const Shader& program) {
|
||||||
if(!program.isProgram()) {
|
if (!program.isProgram()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's go through every shaders and make sure they are ready to go
|
GLBackend::GLShader::ShaderObjects programObjects;
|
||||||
std::vector< GLuint > shaderObjects;
|
|
||||||
for (auto subShader : program.getShaders()) {
|
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||||
GLuint so = GLBackend::getShaderID(subShader);
|
auto& programObject = programObjects[version];
|
||||||
if (!so) {
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileProgram - One of the shaders of the program is not compiled?";
|
// Let's go through every shaders and make sure they are ready to go
|
||||||
|
std::vector< GLuint > shaderGLObjects;
|
||||||
|
for (auto subShader : program.getShaders()) {
|
||||||
|
auto object = GLBackend::syncGPUObject(*subShader);
|
||||||
|
if (object) {
|
||||||
|
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
|
||||||
|
} else {
|
||||||
|
qCDebug(gpulogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint glprogram = compileProgram(shaderGLObjects);
|
||||||
|
if (glprogram == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
shaderObjects.push_back(so);
|
|
||||||
|
programObject.glprogram = glprogram;
|
||||||
|
|
||||||
|
makeProgramBindings(programObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// so far so good, program is almost done, need to link:
|
|
||||||
GLuint glprogram = glCreateProgram();
|
|
||||||
if (!glprogram) {
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileProgram - failed to create the gl program object";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE);
|
// So far so good, the program versions have all been created successfully
|
||||||
// Create the program from the sub shaders
|
|
||||||
for (auto so : shaderObjects) {
|
|
||||||
glAttachShader(glprogram, so);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link!
|
|
||||||
glLinkProgram(glprogram);
|
|
||||||
|
|
||||||
GLint linked = 0;
|
|
||||||
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
|
||||||
|
|
||||||
if (!linked) {
|
|
||||||
/*
|
|
||||||
// save the source code to a temp file so we can debug easily
|
|
||||||
std::ofstream filestream;
|
|
||||||
filestream.open("debugshader.glsl");
|
|
||||||
if (filestream.is_open()) {
|
|
||||||
filestream << shaderSource->source;
|
|
||||||
filestream.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
GLint infoLength = 0;
|
|
||||||
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
|
|
||||||
|
|
||||||
char* temp = new char[infoLength] ;
|
|
||||||
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
|
|
||||||
|
|
||||||
qCDebug(gpulogging) << "GLShader::compileProgram - failed to LINK the gl program object :";
|
|
||||||
qCDebug(gpulogging) << temp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
filestream.open("debugshader.glsl.info.txt");
|
|
||||||
if (filestream.is_open()) {
|
|
||||||
filestream << std::string(temp);
|
|
||||||
filestream.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
delete[] temp;
|
|
||||||
|
|
||||||
glDeleteProgram(glprogram);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// So far so good, the program is created successfully
|
|
||||||
GLBackend::GLShader* object = new GLBackend::GLShader();
|
GLBackend::GLShader* object = new GLBackend::GLShader();
|
||||||
object->_shader = 0;
|
object->_shaderObjects = programObjects;
|
||||||
object->_program = glprogram;
|
|
||||||
|
|
||||||
makeBindings(object);
|
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
@ -329,14 +392,14 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
|
||||||
}
|
}
|
||||||
// need to have a gpu object?
|
// need to have a gpu object?
|
||||||
if (shader.isProgram()) {
|
if (shader.isProgram()) {
|
||||||
GLShader* tempObject = compileProgram(shader);
|
GLShader* tempObject = compileBackendProgram(shader);
|
||||||
if (tempObject) {
|
if (tempObject) {
|
||||||
object = tempObject;
|
object = tempObject;
|
||||||
Backend::setGPUObject(shader, object);
|
Backend::setGPUObject(shader, object);
|
||||||
}
|
}
|
||||||
} else if (shader.isDomain()) {
|
} else if (shader.isDomain()) {
|
||||||
GLShader* tempObject = compileShader(shader);
|
GLShader* tempObject = compileBackendShader(shader);
|
||||||
if (tempObject) {
|
if (tempObject) {
|
||||||
object = tempObject;
|
object = tempObject;
|
||||||
Backend::setGPUObject(shader, object);
|
Backend::setGPUObject(shader, object);
|
||||||
}
|
}
|
||||||
|
@ -345,23 +408,6 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GLuint GLBackend::getShaderID(const ShaderPointer& shader) {
|
|
||||||
if (!shader) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
GLShader* object = GLBackend::syncGPUObject(*shader);
|
|
||||||
if (object) {
|
|
||||||
if (shader->isProgram()) {
|
|
||||||
return object->_program;
|
|
||||||
} else {
|
|
||||||
return object->_shader;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ElementResource {
|
class ElementResource {
|
||||||
public:
|
public:
|
||||||
gpu::Element _element;
|
gpu::Element _element;
|
||||||
|
@ -714,27 +760,38 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object->_program) {
|
// Apply bindings to all program versions and generate list of slots from default version
|
||||||
Shader::SlotSet buffers;
|
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||||
makeUniformBlockSlots(object->_program, slotBindings, buffers);
|
auto& shaderObject = object->_shaderObjects[version];
|
||||||
|
if (shaderObject.glprogram) {
|
||||||
|
Shader::SlotSet buffers;
|
||||||
|
makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
|
||||||
|
|
||||||
Shader::SlotSet uniforms;
|
Shader::SlotSet uniforms;
|
||||||
Shader::SlotSet textures;
|
Shader::SlotSet textures;
|
||||||
Shader::SlotSet samplers;
|
Shader::SlotSet samplers;
|
||||||
makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers);
|
makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
|
||||||
|
|
||||||
Shader::SlotSet inputs;
|
|
||||||
makeInputSlots(object->_program, slotBindings, inputs);
|
|
||||||
|
|
||||||
Shader::SlotSet outputs;
|
Shader::SlotSet inputs;
|
||||||
makeOutputSlots(object->_program, slotBindings, outputs);
|
makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
|
||||||
|
|
||||||
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
|
Shader::SlotSet outputs;
|
||||||
|
makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
|
||||||
} else if (object->_shader) {
|
|
||||||
|
|
||||||
|
// Define the public slots only from the default version
|
||||||
|
if (version == 0) {
|
||||||
|
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
|
||||||
|
} else {
|
||||||
|
GLShader::UniformMapping mapping;
|
||||||
|
for (auto srcUniform : shader.getUniforms()) {
|
||||||
|
mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name);
|
||||||
|
}
|
||||||
|
object->_uniformMappings.push_back(mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -325,3 +325,26 @@ Buffer::Size Buffer::append(Size size, const Byte* data) {
|
||||||
return newSize;
|
return newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW );
|
||||||
|
|
||||||
|
BufferView::BufferView() :
|
||||||
|
BufferView(DEFAULT_ELEMENT) {}
|
||||||
|
|
||||||
|
BufferView::BufferView(const Element& element) :
|
||||||
|
BufferView(BufferPointer(), element) {}
|
||||||
|
|
||||||
|
BufferView::BufferView(Buffer* newBuffer, const Element& element) :
|
||||||
|
BufferView(BufferPointer(newBuffer), element) {}
|
||||||
|
|
||||||
|
BufferView::BufferView(const BufferPointer& buffer, const Element& element) :
|
||||||
|
BufferView(buffer, DEFAULT_OFFSET, buffer ? buffer->getSize() : 0, element.getSize(), element) {}
|
||||||
|
|
||||||
|
BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element) :
|
||||||
|
BufferView(buffer, offset, size, element.getSize(), element) {}
|
||||||
|
|
||||||
|
BufferView::BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element) :
|
||||||
|
_buffer(buffer),
|
||||||
|
_offset(offset),
|
||||||
|
_size(size),
|
||||||
|
_element(element),
|
||||||
|
_stride(stride) {}
|
||||||
|
|
|
@ -174,73 +174,30 @@ typedef std::vector< BufferPointer > Buffers;
|
||||||
|
|
||||||
|
|
||||||
class BufferView {
|
class BufferView {
|
||||||
protected:
|
protected:
|
||||||
void initFromBuffer(const BufferPointer& buffer) {
|
static const Resource::Size DEFAULT_OFFSET{ 0 };
|
||||||
_buffer = (buffer);
|
static const Element DEFAULT_ELEMENT;
|
||||||
if (_buffer) {
|
|
||||||
_size = (buffer->getSize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
typedef Resource::Size Size;
|
using Size = Resource::Size;
|
||||||
typedef int Index;
|
using Index = int;
|
||||||
|
|
||||||
BufferPointer _buffer;
|
BufferPointer _buffer;
|
||||||
Size _offset{ 0 };
|
Size _offset;
|
||||||
Size _size{ 0 };
|
Size _size;
|
||||||
Element _element;
|
Element _element;
|
||||||
uint16 _stride{ 1 };
|
uint16 _stride;
|
||||||
|
|
||||||
BufferView() :
|
|
||||||
_buffer(NULL),
|
|
||||||
_offset(0),
|
|
||||||
_size(0),
|
|
||||||
_element(gpu::SCALAR, gpu::UINT8, gpu::RAW),
|
|
||||||
_stride(1)
|
|
||||||
{};
|
|
||||||
|
|
||||||
BufferView(const Element& element) :
|
|
||||||
_buffer(NULL),
|
|
||||||
_offset(0),
|
|
||||||
_size(0),
|
|
||||||
_element(element),
|
|
||||||
_stride(uint16(element.getSize()))
|
|
||||||
{};
|
|
||||||
|
|
||||||
// create the BufferView and own the Buffer
|
|
||||||
BufferView(Buffer* newBuffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
|
||||||
_offset(0),
|
|
||||||
_element(element),
|
|
||||||
_stride(uint16(element.getSize()))
|
|
||||||
{
|
|
||||||
initFromBuffer(BufferPointer(newBuffer));
|
|
||||||
};
|
|
||||||
BufferView(const BufferPointer& buffer, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
|
||||||
_offset(0),
|
|
||||||
_element(element),
|
|
||||||
_stride(uint16(element.getSize()))
|
|
||||||
{
|
|
||||||
initFromBuffer(buffer);
|
|
||||||
};
|
|
||||||
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
|
||||||
_buffer(buffer),
|
|
||||||
_offset(offset),
|
|
||||||
_size(size),
|
|
||||||
_element(element),
|
|
||||||
_stride(uint16(element.getSize()))
|
|
||||||
{};
|
|
||||||
BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = Element(gpu::SCALAR, gpu::UINT8, gpu::RAW)) :
|
|
||||||
_buffer(buffer),
|
|
||||||
_offset(offset),
|
|
||||||
_size(size),
|
|
||||||
_element(element),
|
|
||||||
_stride(stride)
|
|
||||||
{};
|
|
||||||
|
|
||||||
~BufferView() {}
|
|
||||||
BufferView(const BufferView& view) = default;
|
BufferView(const BufferView& view) = default;
|
||||||
BufferView& operator=(const BufferView& view) = default;
|
BufferView& operator=(const BufferView& view) = default;
|
||||||
|
|
||||||
|
BufferView();
|
||||||
|
BufferView(const Element& element);
|
||||||
|
BufferView(Buffer* newBuffer, const Element& element = DEFAULT_ELEMENT);
|
||||||
|
BufferView(const BufferPointer& buffer, const Element& element = DEFAULT_ELEMENT);
|
||||||
|
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT);
|
||||||
|
BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT);
|
||||||
|
|
||||||
Size getNumElements() const { return _size / _element.getSize(); }
|
Size getNumElements() const { return _size / _element.getSize(); }
|
||||||
|
|
||||||
//Template iterator with random access on the buffer sysmem
|
//Template iterator with random access on the buffer sysmem
|
||||||
|
|
|
@ -155,9 +155,12 @@ void ResourceCache::clearATPAssets() {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&_resourcesToBeGottenLock);
|
QWriteLocker locker(&_resourcesToBeGottenLock);
|
||||||
for (auto& url : _resourcesToBeGotten) {
|
auto it = _resourcesToBeGotten.begin();
|
||||||
if (url.scheme() == URL_SCHEME_ATP) {
|
while (it != _resourcesToBeGotten.end()) {
|
||||||
_resourcesToBeGotten.removeAll(url);
|
if (it->scheme() == URL_SCHEME_ATP) {
|
||||||
|
it = _resourcesToBeGotten.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,10 +329,21 @@ void SendQueue::run() {
|
||||||
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
||||||
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
||||||
|
|
||||||
// sleep as long as we need until next packet send, if we can
|
// sleep as long as we need for next packet send, if we can
|
||||||
auto now = p_high_resolution_clock::now();
|
auto now = p_high_resolution_clock::now();
|
||||||
|
|
||||||
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
|
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
|
||||||
|
|
||||||
|
// we use nextPacketTimestamp so that we don't fall behind, not to force long sleeps
|
||||||
|
// we'll never allow nextPacketTimestamp to force us to sleep for more than nextPacketDelta
|
||||||
|
// so cap it to that value
|
||||||
|
if (timeToSleep > std::chrono::microseconds(nextPacketDelta)) {
|
||||||
|
// reset the nextPacketTimestamp so that it is correct next time we come around
|
||||||
|
nextPacketTimestamp = now + std::chrono::microseconds(nextPacketDelta);
|
||||||
|
|
||||||
|
timeToSleep = std::chrono::microseconds(nextPacketDelta);
|
||||||
|
}
|
||||||
|
|
||||||
// we're seeing SendQueues sleep for a long period of time here,
|
// we're seeing SendQueues sleep for a long period of time here,
|
||||||
// which can lock the NodeList if it's attempting to clear connections
|
// which can lock the NodeList if it's attempting to clear connections
|
||||||
// for now we guard this by capping the time this thread and sleep for
|
// for now we guard this by capping the time this thread and sleep for
|
||||||
|
|
|
@ -72,6 +72,17 @@ CharacterController::CharacterController() {
|
||||||
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
_pendingFlags = PENDING_FLAG_UPDATE_SHAPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CharacterController::~CharacterController() {
|
||||||
|
if (_rigidBody) {
|
||||||
|
btCollisionShape* shape = _rigidBody->getCollisionShape();
|
||||||
|
if (shape) {
|
||||||
|
delete shape;
|
||||||
|
}
|
||||||
|
delete _rigidBody;
|
||||||
|
_rigidBody = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CharacterController::needsRemoval() const {
|
bool CharacterController::needsRemoval() const {
|
||||||
return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class btDynamicsWorld;
|
||||||
class CharacterController : public btCharacterControllerInterface {
|
class CharacterController : public btCharacterControllerInterface {
|
||||||
public:
|
public:
|
||||||
CharacterController();
|
CharacterController();
|
||||||
virtual ~CharacterController() {}
|
virtual ~CharacterController();
|
||||||
|
|
||||||
bool needsRemoval() const;
|
bool needsRemoval() const;
|
||||||
bool needsAddition() const;
|
bool needsAddition() const;
|
||||||
|
|
|
@ -105,6 +105,12 @@ public:
|
||||||
return aspect(getRecommendedRenderSize());
|
return aspect(getRecommendedRenderSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The recommended bounds for primary overlay placement
|
||||||
|
virtual QRect getRecommendedOverlayRect() const {
|
||||||
|
auto recommendedSize = getRecommendedUiSize();
|
||||||
|
return QRect(0, 0, recommendedSize.x, recommendedSize.y);
|
||||||
|
}
|
||||||
|
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
|
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||||
return baseProjection;
|
return baseProjection;
|
||||||
|
|
|
@ -46,6 +46,7 @@ const float DEFAULT_METALLIC = 0;
|
||||||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||||
const vec3 DEFAULT_EMISSIVE = vec3(0.0);
|
const vec3 DEFAULT_EMISSIVE = vec3(0.0);
|
||||||
const float DEFAULT_OCCLUSION = 1.0;
|
const float DEFAULT_OCCLUSION = 1.0;
|
||||||
|
const vec3 DEFAULT_FRESNEL = DEFAULT_EMISSIVE;
|
||||||
|
|
||||||
|
|
||||||
void packDeferredFragment(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 emissive, float occlusion) {
|
void packDeferredFragment(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 emissive, float occlusion) {
|
||||||
|
|
|
@ -90,6 +90,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
||||||
|
|
||||||
// Specular highlight from ambient
|
// Specular highlight from ambient
|
||||||
vec3 direction = -reflect(fragEyeDir, fragNormal);
|
vec3 direction = -reflect(fragEyeDir, fragNormal);
|
||||||
|
|
||||||
float levels = getLightAmbientMapNumMips(light);
|
float levels = getLightAmbientMapNumMips(light);
|
||||||
float lod = min(floor((roughness) * levels), levels);
|
float lod = min(floor((roughness) * levels), levels);
|
||||||
vec4 skyboxLight = evalSkyboxLight(direction, lod);
|
vec4 skyboxLight = evalSkyboxLight(direction, lod);
|
||||||
|
|
|
@ -96,7 +96,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
||||||
addJob<PrepareDeferred>("PrepareDeferred");
|
addJob<PrepareDeferred>("PrepareDeferred");
|
||||||
|
|
||||||
// Render opaque objects in DeferredBuffer
|
// Render opaque objects in DeferredBuffer
|
||||||
addJob<DrawDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
|
addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
|
||||||
|
|
||||||
// Once opaque is all rendered create stencil background
|
// Once opaque is all rendered create stencil background
|
||||||
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
|
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
|
||||||
|
@ -185,9 +185,6 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
|
||||||
batch.setViewportTransform(args->_viewport);
|
batch.setViewportTransform(args->_viewport);
|
||||||
batch.setStateScissorRect(args->_viewport);
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
config->setNumDrawn((int)inItems.size());
|
|
||||||
emit config->numDrawnChanged();
|
|
||||||
|
|
||||||
glm::mat4 projMat;
|
glm::mat4 projMat;
|
||||||
Transform viewMat;
|
Transform viewMat;
|
||||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||||
|
@ -199,6 +196,40 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
|
||||||
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
||||||
args->_batch = nullptr;
|
args->_batch = nullptr;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
config->setNumDrawn((int)inItems.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStateSortDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
|
||||||
|
assert(renderContext->args);
|
||||||
|
assert(renderContext->args->_viewFrustum);
|
||||||
|
|
||||||
|
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
|
||||||
|
|
||||||
|
RenderArgs* args = renderContext->args;
|
||||||
|
|
||||||
|
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||||
|
args->_batch = &batch;
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||||
|
args->_viewFrustum->evalViewTransform(viewMat);
|
||||||
|
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
|
||||||
|
if (_stateSort) {
|
||||||
|
renderStateSortShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
||||||
|
} else {
|
||||||
|
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
||||||
|
}
|
||||||
|
args->_batch = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
config->setNumDrawn((int)inItems.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawOverlay3D::DrawOverlay3D(bool opaque) :
|
DrawOverlay3D::DrawOverlay3D(bool opaque) :
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
using JobModel = render::Job::Model<SetupDeferred>;
|
using JobModel = render::Job::Model<SetupDeferred>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class PrepareDeferred {
|
class PrepareDeferred {
|
||||||
public:
|
public:
|
||||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||||
|
@ -29,32 +30,31 @@ public:
|
||||||
using JobModel = render::Job::Model<PrepareDeferred>;
|
using JobModel = render::Job::Model<PrepareDeferred>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class RenderDeferred {
|
class RenderDeferred {
|
||||||
public:
|
public:
|
||||||
using JobModel = render::Job::Model<RenderDeferred>;
|
using JobModel = render::Job::Model<RenderDeferred>;
|
||||||
|
|
||||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DrawConfig : public render::Job::Config {
|
class DrawConfig : public render::Job::Config {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
|
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY newStats)
|
||||||
|
|
||||||
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
|
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
|
||||||
public:
|
public:
|
||||||
|
|
||||||
int getNumDrawn() { return numDrawn; }
|
int getNumDrawn() { return _numDrawn; }
|
||||||
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
|
void setNumDrawn(int numDrawn) { _numDrawn = numDrawn; emit newStats(); }
|
||||||
|
|
||||||
int maxDrawn{ -1 };
|
int maxDrawn{ -1 };
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void numDrawnChanged();
|
void newStats();
|
||||||
void dirty();
|
void dirty();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int numDrawn{ 0 };
|
int _numDrawn{ 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class DrawDeferred {
|
class DrawDeferred {
|
||||||
|
@ -72,6 +72,44 @@ protected:
|
||||||
int _maxDrawn; // initialized by Config
|
int _maxDrawn; // initialized by Config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DrawStateSortConfig : public render::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
|
||||||
|
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty)
|
||||||
|
public:
|
||||||
|
|
||||||
|
int getNumDrawn() { return numDrawn; }
|
||||||
|
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
|
||||||
|
|
||||||
|
int maxDrawn{ -1 };
|
||||||
|
bool stateSort{ true };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void numDrawnChanged();
|
||||||
|
void dirty();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int numDrawn{ 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class DrawStateSortDeferred {
|
||||||
|
public:
|
||||||
|
using Config = DrawStateSortConfig;
|
||||||
|
using JobModel = render::Job::ModelI<DrawStateSortDeferred, render::ItemBounds, Config>;
|
||||||
|
|
||||||
|
DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||||
|
|
||||||
|
void configure(const Config& config) { _maxDrawn = config.maxDrawn; _stateSort = config.stateSort; }
|
||||||
|
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
render::ShapePlumberPointer _shapePlumber;
|
||||||
|
int _maxDrawn; // initialized by Config
|
||||||
|
bool _stateSort;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class DrawStencilDeferred {
|
class DrawStencilDeferred {
|
||||||
public:
|
public:
|
||||||
using JobModel = render::Job::Model<DrawStencilDeferred>;
|
using JobModel = render::Job::Model<DrawStencilDeferred>;
|
||||||
|
|
|
@ -27,7 +27,7 @@ ToneMappingEffect::ToneMappingEffect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToneMappingEffect::init() {
|
void ToneMappingEffect::init() {
|
||||||
const char BlitTextureGamma_frag[] = R"SCRIBE(#version 410 core
|
const char BlitTextureGamma_frag[] = R"SCRIBE(
|
||||||
// Generated on Sat Oct 24 09:34:37 2015
|
// Generated on Sat Oct 24 09:34:37 2015
|
||||||
//
|
//
|
||||||
// Draw texture 0 fetched at texcoord.xy
|
// Draw texture 0 fetched at texcoord.xy
|
||||||
|
|
|
@ -41,20 +41,15 @@ void main() {
|
||||||
float w = clamp( s, 0.0, 0.5);
|
float w = clamp( s, 0.0, 0.5);
|
||||||
float a = smoothstep(0.5 - w, 0.5 + w, sdf);
|
float a = smoothstep(0.5 - w, 0.5 + w, sdf);
|
||||||
|
|
||||||
// gamma correction for linear attenuation
|
|
||||||
a = pow(a, 1.0 / gamma);
|
|
||||||
|
|
||||||
// discard if unvisible
|
// discard if unvisible
|
||||||
if (a < 0.01) {
|
if (a < 0.01) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
packDeferredFragmentLightmap(
|
packDeferredFragmentTranslucent(
|
||||||
normalize(_normal),
|
normalize(_normal),
|
||||||
1.0,
|
a,
|
||||||
vec3(1.0),
|
Color.rgb,
|
||||||
DEFAULT_ROUGHNESS,
|
DEFAULT_FRESNEL,
|
||||||
DEFAULT_METALLIC,
|
DEFAULT_ROUGHNESS);
|
||||||
DEFAULT_SPECULAR,
|
|
||||||
Color.rgb);
|
|
||||||
}
|
}
|
|
@ -65,6 +65,57 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void render::renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
|
||||||
|
const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) {
|
||||||
|
auto& scene = sceneContext->_scene;
|
||||||
|
RenderArgs* args = renderContext->args;
|
||||||
|
|
||||||
|
int numItemsToDraw = (int)inItems.size();
|
||||||
|
if (maxDrawnItems != -1) {
|
||||||
|
numItemsToDraw = glm::min(numItemsToDraw, maxDrawnItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
using SortedPipelines = std::vector<render::ShapeKey>;
|
||||||
|
using SortedShapes = std::unordered_map<render::ShapeKey, std::vector<Item>, render::ShapeKey::Hash, render::ShapeKey::KeyEqual>;
|
||||||
|
SortedPipelines sortedPipelines;
|
||||||
|
SortedShapes sortedShapes;
|
||||||
|
std::vector<Item> ownPipelineBucket;
|
||||||
|
|
||||||
|
|
||||||
|
for (auto i = 0; i < numItemsToDraw; ++i) {
|
||||||
|
auto item = scene->getItem(inItems[i].id);
|
||||||
|
|
||||||
|
{
|
||||||
|
assert(item.getKey().isShape());
|
||||||
|
const auto& key = item.getShapeKey();
|
||||||
|
if (key.isValid() && !key.hasOwnPipeline()) {
|
||||||
|
auto& bucket = sortedShapes[key];
|
||||||
|
if (bucket.empty()) {
|
||||||
|
sortedPipelines.push_back(key);
|
||||||
|
}
|
||||||
|
bucket.push_back(item);
|
||||||
|
} else if (key.hasOwnPipeline()) {
|
||||||
|
ownPipelineBucket.push_back(item);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Item could not be rendered: invalid key ?" << key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then render
|
||||||
|
for (auto& pipelineKey : sortedPipelines) {
|
||||||
|
auto& bucket = sortedShapes[pipelineKey];
|
||||||
|
args->_pipeline = shapeContext->pickPipeline(args, pipelineKey);
|
||||||
|
for (auto& item : bucket) {
|
||||||
|
item.render(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args->_pipeline = nullptr;
|
||||||
|
for (auto& item : ownPipelineBucket) {
|
||||||
|
item.render(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights) {
|
void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inLights) {
|
||||||
assert(renderContext->args);
|
assert(renderContext->args);
|
||||||
assert(renderContext->args->_viewFrustum);
|
assert(renderContext->args->_viewFrustum);
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace render {
|
||||||
|
|
||||||
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
||||||
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
||||||
|
void renderStateSortShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,5 +49,7 @@ void EngineStats::run(const SceneContextPointer& sceneContext, const RenderConte
|
||||||
config->frameTextureRate = config->frameTextureCount * frequency;
|
config->frameTextureRate = config->frameTextureCount * frequency;
|
||||||
config->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded;
|
config->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded;
|
||||||
|
|
||||||
|
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines - gpuStats._PSNumSetPipelines;
|
||||||
|
|
||||||
config->emitDirty();
|
config->emitDirty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@ namespace render {
|
||||||
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
|
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
|
||||||
Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
|
Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY dirty)
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EngineStatsConfig() : Job::Config(true) {}
|
EngineStatsConfig() : Job::Config(true) {}
|
||||||
|
|
||||||
|
@ -73,6 +76,10 @@ namespace render {
|
||||||
quint32 frameTextureRate{ 0 };
|
quint32 frameTextureRate{ 0 };
|
||||||
qint64 frameTextureMemoryUsage{ 0 };
|
qint64 frameTextureMemoryUsage{ 0 };
|
||||||
|
|
||||||
|
quint32 frameSetPipelineCount{ 0 };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void emitDirty() { emit dirty(); }
|
void emitDirty() { emit dirty(); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -127,6 +127,9 @@ protected:
|
||||||
// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
|
// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
|
||||||
class JobConfig : public QObject {
|
class JobConfig : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(quint64 cpuRunTime READ getCPUTRunTime NOTIFY newStats())
|
||||||
|
|
||||||
|
quint64 _CPURunTime{ 0 };
|
||||||
public:
|
public:
|
||||||
using Persistent = PersistentConfig<JobConfig>;
|
using Persistent = PersistentConfig<JobConfig>;
|
||||||
|
|
||||||
|
@ -151,11 +154,17 @@ public:
|
||||||
Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); }
|
Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); }
|
||||||
Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); }
|
Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); }
|
||||||
|
|
||||||
|
// Running Time measurement
|
||||||
|
// The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated
|
||||||
|
void setCPURunTime(quint64 ustime) { _CPURunTime = ustime; emit newStats(); }
|
||||||
|
quint64 getCPUTRunTime() const { return _CPURunTime; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
|
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loaded();
|
void loaded();
|
||||||
|
void newStats();
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskConfig : public JobConfig {
|
class TaskConfig : public JobConfig {
|
||||||
|
@ -223,7 +232,11 @@ public:
|
||||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
|
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void setCPURunTime(quint64 ustime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(ustime); }
|
||||||
|
|
||||||
QConfigPointer _config;
|
QConfigPointer _config;
|
||||||
|
|
||||||
|
friend class Job;
|
||||||
};
|
};
|
||||||
using ConceptPointer = std::shared_ptr<Concept>;
|
using ConceptPointer = std::shared_ptr<Concept>;
|
||||||
|
|
||||||
|
@ -278,8 +291,11 @@ public:
|
||||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||||
PerformanceTimer perfTimer(_name.c_str());
|
PerformanceTimer perfTimer(_name.c_str());
|
||||||
PROFILE_RANGE(_name.c_str());
|
PROFILE_RANGE(_name.c_str());
|
||||||
|
auto start = usecTimestampNow();
|
||||||
|
|
||||||
_concept->run(sceneContext, renderContext);
|
_concept->run(sceneContext, renderContext);
|
||||||
|
|
||||||
|
_concept->setCPURunTime(usecTimestampNow() - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
|
#include <QtScript/QScriptValueIterator>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
|
@ -143,7 +144,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
||||||
|
|
||||||
ScriptEngine::~ScriptEngine() {
|
ScriptEngine::~ScriptEngine() {
|
||||||
qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
|
qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
|
||||||
|
|
||||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||||
if (scriptEngines) {
|
if (scriptEngines) {
|
||||||
scriptEngines->removeScriptEngine(this);
|
scriptEngines->removeScriptEngine(this);
|
||||||
|
@ -1047,39 +1047,25 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
|
||||||
|
|
||||||
// since all of these operations can be asynch we will always do the actual work in the response handler
|
// since all of these operations can be asynch we will always do the actual work in the response handler
|
||||||
// for the download
|
// for the download
|
||||||
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||||
if (QThread::currentThread() != thread()) {
|
// NOTE: If the script content is not currently in the cache, the LAMBDA here will be called on the Main Thread
|
||||||
|
// which means we're guaranteed that it's not the correct thread for the ScriptEngine. This means
|
||||||
|
// when we get into entityScriptContentAvailable() we will likely invokeMethod() to get it over
|
||||||
|
// to the "Entities" ScriptEngine thread.
|
||||||
|
DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [theEngine, entityID](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
|
||||||
|
QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();
|
||||||
|
if (strongEngine) {
|
||||||
#ifdef THREAD_DEBUGGING
|
#ifdef THREAD_DEBUGGING
|
||||||
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread ["
|
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
|
||||||
<< QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
<< QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
|
||||||
"entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;
|
|
||||||
#endif
|
#endif
|
||||||
|
strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
|
||||||
QMetaObject::invokeMethod(this, "loadEntityScript",
|
} else {
|
||||||
Q_ARG(const EntityItemID&, entityID),
|
// FIXME - I'm leaving this in for testing, so that QA can confirm that sometimes the script contents
|
||||||
Q_ARG(const QString&, entityScript),
|
// returns after the ScriptEngine has been deleted, we can remove this after QA verifies the
|
||||||
Q_ARG(bool, forceRedownload));
|
// repro case.
|
||||||
return;
|
qDebug() << "ScriptCache::getScriptContents() returned after our ScriptEngine was deleted... script:" << scriptOrURL;
|
||||||
}
|
}
|
||||||
#ifdef THREAD_DEBUGGING
|
|
||||||
qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "
|
|
||||||
"entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// If we've been called our known entityScripts should not know about us..
|
|
||||||
assert(!_entityScripts.contains(entityID));
|
|
||||||
|
|
||||||
#ifdef THREAD_DEBUGGING
|
|
||||||
qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread ["
|
|
||||||
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
|
|
||||||
#endif
|
|
||||||
DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [=](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
|
|
||||||
#ifdef THREAD_DEBUGGING
|
|
||||||
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
|
|
||||||
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
|
|
||||||
}, forceRedownload);
|
}, forceRedownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,6 +1199,16 @@ void ScriptEngine::unloadAllEntityScripts() {
|
||||||
callEntityScriptMethod(entityID, "unload");
|
callEntityScriptMethod(entityID, "unload");
|
||||||
}
|
}
|
||||||
_entityScripts.clear();
|
_entityScripts.clear();
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENGINE_STATE
|
||||||
|
qDebug() << "---- CURRENT STATE OF ENGINE: --------------------------";
|
||||||
|
QScriptValueIterator it(globalObject());
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
qDebug() << it.name() << ":" << it.value().toString();
|
||||||
|
}
|
||||||
|
qDebug() << "--------------------------------------------------------";
|
||||||
|
#endif // DEBUG_ENGINE_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {
|
||||||
|
|
|
@ -124,7 +124,7 @@ public:
|
||||||
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
||||||
|
|
||||||
// Entity Script Related methods
|
// Entity Script Related methods
|
||||||
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload = false); // will call the preload method once loaded
|
static void loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
|
||||||
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method
|
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method
|
||||||
Q_INVOKABLE void unloadAllEntityScripts();
|
Q_INVOKABLE void unloadAllEntityScripts();
|
||||||
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList());
|
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList());
|
||||||
|
|
|
@ -22,12 +22,7 @@
|
||||||
#define __STR1__(x) __STR2__(x)
|
#define __STR1__(x) __STR2__(x)
|
||||||
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||||
#else
|
|
||||||
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
|
|
||||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ScriptsModel& getScriptsModel() {
|
ScriptsModel& getScriptsModel() {
|
||||||
static ScriptsModel scriptsModel;
|
static ScriptsModel scriptsModel;
|
||||||
|
|
|
@ -55,8 +55,11 @@ public:
|
||||||
}
|
}
|
||||||
~Counter() { log(); }
|
~Counter() { log(); }
|
||||||
|
|
||||||
// Increase the count for key.
|
// Increase the count for key (by inc).
|
||||||
void add(const K& key);
|
void add(const K& key, size_t inc = 1);
|
||||||
|
|
||||||
|
// Decrease the count for key (by dec).
|
||||||
|
void sub(const K& key, size_t dec = 1);
|
||||||
|
|
||||||
// Log current counts (called on destruction).
|
// Log current counts (called on destruction).
|
||||||
void log();
|
void log();
|
||||||
|
@ -123,20 +126,25 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class K>
|
template<class K>
|
||||||
void Counter<K>::add(const K& k) {
|
void Counter<K>::add(const K& k, size_t inc) {
|
||||||
std::lock_guard<std::mutex> lock(_mutex);
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
auto& it = _map.find(k);
|
auto& it = _map.find(k);
|
||||||
|
|
||||||
if (it == _map.end()) {
|
if (it == _map.end()) {
|
||||||
// No entry for k; add it
|
// No entry for k; add it
|
||||||
_map.insert(std::pair<K, size_t>(k, 1));
|
_map.insert(std::pair<K, size_t>(k, inc));
|
||||||
} else {
|
} else {
|
||||||
// Entry for k; update it
|
// Entry for k; update it
|
||||||
it->second++;
|
it->second += inc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class K>
|
||||||
|
void Counter<K>::sub(const K& k, size_t dec) {
|
||||||
|
add(k, -dec);
|
||||||
|
}
|
||||||
|
|
||||||
template <class K>
|
template <class K>
|
||||||
void Counter<K>::log() {
|
void Counter<K>::log() {
|
||||||
// Avoid logging nothing
|
// Avoid logging nothing
|
||||||
|
|
|
@ -34,7 +34,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator
|
||||||
if (getIsLogging()) { // if logging/reporting
|
if (getIsLogging()) { // if logging/reporting
|
||||||
updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue);
|
updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue);
|
||||||
}
|
}
|
||||||
Q_ASSERT(!isnan(computedValue));
|
Q_ASSERT(!glm::isnan(computedValue));
|
||||||
|
|
||||||
// update state for next time
|
// update state for next time
|
||||||
_lastError = error;
|
_lastError = error;
|
||||||
|
@ -75,4 +75,4 @@ void PIDController::reportHistory() {
|
||||||
qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() <<
|
qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() <<
|
||||||
"controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() <<
|
"controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() <<
|
||||||
"kp/ki/kd" << getKP() << getKI() << getKD();
|
"kp/ki/kd" << getKP() << getKI() << getKD();
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
|
||||||
vec3.z = object.property("z").toVariant().toFloat();
|
vec3.z = object.property("z").toVariant().toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant vec3toVariant(const glm::vec3 &vec3) {
|
QVariant vec3toVariant(const glm::vec3& vec3) {
|
||||||
if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) {
|
if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) {
|
||||||
// if vec3 contains a NaN don't try to convert it
|
// if vec3 contains a NaN don't try to convert it
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
@ -140,6 +140,18 @@ QVariant vec3toVariant(const glm::vec3 &vec3) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant vec4toVariant(const glm::vec4& vec4) {
|
||||||
|
if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) {
|
||||||
|
// if vec4 contains a NaN don't try to convert it
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariantMap result;
|
||||||
|
result["x"] = vec4.x;
|
||||||
|
result["y"] = vec4.y;
|
||||||
|
result["z"] = vec4.z;
|
||||||
|
result["w"] = vec4.w;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector) {
|
QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::vec3>& vector) {
|
||||||
QScriptValue array = engine->newArray();
|
QScriptValue array = engine->newArray();
|
||||||
|
@ -150,7 +162,7 @@ QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector<glm::
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 vec3FromVariant(const QVariant &object, bool& valid) {
|
glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) {
|
||||||
glm::vec3 v;
|
glm::vec3 v;
|
||||||
valid = false;
|
valid = false;
|
||||||
if (!object.isValid() || object.isNull()) {
|
if (!object.isValid() || object.isNull()) {
|
||||||
|
@ -189,12 +201,49 @@ glm::vec3 vec3FromVariant(const QVariant &object, bool& valid) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 vec3FromVariant(const QVariant &object) {
|
glm::vec3 vec3FromVariant(const QVariant& object) {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
return vec3FromVariant(object, valid);
|
return vec3FromVariant(object, valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat &quat) {
|
glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) {
|
||||||
|
glm::vec4 v;
|
||||||
|
valid = false;
|
||||||
|
if (!object.isValid() || object.isNull()) {
|
||||||
|
return v;
|
||||||
|
} else if (object.canConvert<float>()) {
|
||||||
|
v = glm::vec4(object.toFloat());
|
||||||
|
valid = true;
|
||||||
|
} else if (object.canConvert<QVector4D>()) {
|
||||||
|
auto qvec4 = qvariant_cast<QVector4D>(object);
|
||||||
|
v.x = qvec4.x();
|
||||||
|
v.y = qvec4.y();
|
||||||
|
v.z = qvec4.z();
|
||||||
|
v.w = qvec4.w();
|
||||||
|
valid = true;
|
||||||
|
} else {
|
||||||
|
auto map = object.toMap();
|
||||||
|
auto x = map["x"];
|
||||||
|
auto y = map["y"];
|
||||||
|
auto z = map["z"];
|
||||||
|
auto w = map["w"];
|
||||||
|
if (x.canConvert<float>() && y.canConvert<float>() && z.canConvert<float>() && w.canConvert<float>()) {
|
||||||
|
v.x = x.toFloat();
|
||||||
|
v.y = y.toFloat();
|
||||||
|
v.z = z.toFloat();
|
||||||
|
v.w = w.toFloat();
|
||||||
|
valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec4 vec4FromVariant(const QVariant& object) {
|
||||||
|
bool valid = false;
|
||||||
|
return vec4FromVariant(object, valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) {
|
||||||
QScriptValue obj = engine->newObject();
|
QScriptValue obj = engine->newObject();
|
||||||
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) {
|
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z || quat.w != quat.w) {
|
||||||
// if quat contains a NaN don't try to convert it
|
// if quat contains a NaN don't try to convert it
|
||||||
|
@ -207,7 +256,7 @@ QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat &quat) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void quatFromScriptValue(const QScriptValue &object, glm::quat &quat) {
|
void quatFromScriptValue(const QScriptValue& object, glm::quat &quat) {
|
||||||
quat.x = object.property("x").toVariant().toFloat();
|
quat.x = object.property("x").toVariant().toFloat();
|
||||||
quat.y = object.property("y").toVariant().toFloat();
|
quat.y = object.property("y").toVariant().toFloat();
|
||||||
quat.z = object.property("z").toVariant().toFloat();
|
quat.z = object.property("z").toVariant().toFloat();
|
||||||
|
@ -245,12 +294,12 @@ glm::quat quatFromVariant(const QVariant &object, bool& isValid) {
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::quat quatFromVariant(const QVariant &object) {
|
glm::quat quatFromVariant(const QVariant& object) {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
return quatFromVariant(object, valid);
|
return quatFromVariant(object, valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant quatToVariant(const glm::quat &quat) {
|
QVariant quatToVariant(const glm::quat& quat) {
|
||||||
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) {
|
if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) {
|
||||||
// if vec3 contains a NaN don't try to convert it
|
// if vec3 contains a NaN don't try to convert it
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
|
@ -43,12 +43,15 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4);
|
||||||
// Vec4
|
// Vec4
|
||||||
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
|
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4);
|
||||||
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
|
void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4);
|
||||||
|
QVariant vec4toVariant(const glm::vec4& vec4);
|
||||||
|
glm::vec4 vec4FromVariant(const QVariant &object, bool& valid);
|
||||||
|
glm::vec4 vec4FromVariant(const QVariant &object);
|
||||||
|
|
||||||
// Vec3
|
// Vec3
|
||||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3);
|
||||||
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3);
|
||||||
|
|
||||||
QVariant vec3toVariant(const glm::vec3 &vec3);
|
QVariant vec3toVariant(const glm::vec3& vec3);
|
||||||
glm::vec3 vec3FromVariant(const QVariant &object, bool& valid);
|
glm::vec3 vec3FromVariant(const QVariant &object, bool& valid);
|
||||||
glm::vec3 vec3FromVariant(const QVariant &object);
|
glm::vec3 vec3FromVariant(const QVariant &object);
|
||||||
|
|
||||||
|
@ -71,9 +74,10 @@ glm::quat quatFromVariant(const QVariant &object);
|
||||||
// Rect
|
// Rect
|
||||||
QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect);
|
QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect);
|
||||||
void qRectFromScriptValue(const QScriptValue& object, QRect& rect);
|
void qRectFromScriptValue(const QScriptValue& object, QRect& rect);
|
||||||
|
|
||||||
QVariant qRectToVariant(const QRect& rect);
|
|
||||||
QRect qRectFromVariant(const QVariant& object, bool& isValid);
|
QRect qRectFromVariant(const QVariant& object, bool& isValid);
|
||||||
|
QRect qRectFromVariant(const QVariant& object);
|
||||||
|
QVariant qRectToVariant(const QRect& rect);
|
||||||
|
|
||||||
|
|
||||||
// xColor
|
// xColor
|
||||||
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
||||||
|
|
|
@ -18,7 +18,7 @@ void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
|
||||||
const QVariant badDefaultValue = NAN;
|
const QVariant badDefaultValue = NAN;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
float tempFloat = value(name, badDefaultValue).toFloat(&ok);
|
float tempFloat = value(name, badDefaultValue).toFloat(&ok);
|
||||||
if (ok && !isnan(tempFloat)) {
|
if (ok && !glm::isnan(tempFloat)) {
|
||||||
floatValue = tempFloat;
|
floatValue = tempFloat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ void Settings::getVec3ValueIfValid(const QString& name, glm::vec3& vecValue) {
|
||||||
float x = value(QString("x"), badDefaultValue).toFloat(&ok);
|
float x = value(QString("x"), badDefaultValue).toFloat(&ok);
|
||||||
float y = value(QString("y"), badDefaultValue).toFloat(&ok);
|
float y = value(QString("y"), badDefaultValue).toFloat(&ok);
|
||||||
float z = value(QString("z"), badDefaultValue).toFloat(&ok);
|
float z = value(QString("z"), badDefaultValue).toFloat(&ok);
|
||||||
if (ok && (!isnan(x) && !isnan(y) && !isnan(z))) {
|
if (ok && (!glm::isnan(x) && !glm::isnan(y) && !glm::isnan(z))) {
|
||||||
vecValue = glm::vec3(x, y, z);
|
vecValue = glm::vec3(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ void Settings::getQuatValueIfValid(const QString& name, glm::quat& quatValue) {
|
||||||
float y = value(QString("y"), badDefaultValue).toFloat(&ok);
|
float y = value(QString("y"), badDefaultValue).toFloat(&ok);
|
||||||
float z = value(QString("z"), badDefaultValue).toFloat(&ok);
|
float z = value(QString("z"), badDefaultValue).toFloat(&ok);
|
||||||
float w = value(QString("w"), badDefaultValue).toFloat(&ok);
|
float w = value(QString("w"), badDefaultValue).toFloat(&ok);
|
||||||
if (ok && (!isnan(x) && !isnan(y) && !isnan(z) && !isnan(w))) {
|
if (ok && (!glm::isnan(x) && !glm::isnan(y) && !glm::isnan(z) && !glm::isnan(w))) {
|
||||||
quatValue = glm::quat(w, x, y, z);
|
quatValue = glm::quat(w, x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue