mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 00:33:11 +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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <math.h>
|
||||
|
@ -577,15 +578,15 @@ void AudioMixer::domainSettingsRequestComplete() {
|
|||
void AudioMixer::broadcastMixes() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int64_t nextFrame = 0;
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS;
|
||||
auto nextFrameTimestamp = p_high_resolution_clock::now();
|
||||
auto timeToSleep = std::chrono::microseconds(0);
|
||||
|
||||
const int TRAILING_AVERAGE_FRAMES = 100;
|
||||
int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;
|
||||
|
||||
int currentFrame { 1 };
|
||||
int numFramesPerSecond { (int) ceil(AudioConstants::NETWORK_FRAMES_PER_SEC) };
|
||||
|
||||
while (!_isFinished) {
|
||||
const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
|
||||
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 PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
|
||||
|
||||
if (usecToSleep < 0) {
|
||||
usecToSleep = 0;
|
||||
if (timeToSleep.count() < 0) {
|
||||
timeToSleep = std::chrono::microseconds(0);
|
||||
}
|
||||
|
||||
_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;
|
||||
bool hasRatioChanged = false;
|
||||
|
@ -694,10 +695,11 @@ void AudioMixer::broadcastMixes() {
|
|||
nodeList->sendPacket(std::move(mixPacket), *node);
|
||||
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
|
||||
if (nextFrame % FRAMES_PER_SECOND == 0) {
|
||||
++currentFrame;
|
||||
currentFrame %= numFramesPerSecond;
|
||||
|
||||
if (nodeData->shouldSendStats(currentFrame)) {
|
||||
nodeData->sendAudioStreamStatsPackets(node);
|
||||
}
|
||||
|
||||
|
@ -718,11 +720,14 @@ void AudioMixer::broadcastMixes() {
|
|||
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) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
// sleep as long as we need until next frame, if we can
|
||||
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
|
||||
//
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonArray>
|
||||
|
||||
|
@ -26,7 +28,14 @@ AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) :
|
|||
_outgoingMixedAudioSequenceNumber(0),
|
||||
_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() {
|
||||
|
@ -180,6 +189,10 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AudioMixerClientData::shouldSendStats(int frameNumber) {
|
||||
return frameNumber == _frameToSendStats;
|
||||
}
|
||||
|
||||
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) {
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
|
|
@ -58,6 +58,9 @@ public:
|
|||
void incrementOutgoingMixedAudioSequenceNumber() { _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:
|
||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||
|
||||
|
@ -72,6 +75,8 @@ private:
|
|||
quint16 _outgoingMixedAudioSequenceNumber;
|
||||
|
||||
AudioStreamStats _downstreamAudioStreamStats;
|
||||
|
||||
int _frameToSendStats { 0 };
|
||||
};
|
||||
|
||||
#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) :
|
||||
ThreadedAssignment(message),
|
||||
_broadcastThread(),
|
||||
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
|
||||
_trailingSleepRatio(1.0f),
|
||||
_performanceThrottlingRatio(0.0f),
|
||||
_sumListeners(0),
|
||||
_numStatFrames(0),
|
||||
_sumBillboardPackets(0),
|
||||
_sumIdentityPackets(0)
|
||||
_broadcastThread()
|
||||
{
|
||||
// make sure we hear about node kills so we can tell the other nodes
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
|
||||
|
@ -51,7 +44,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AvatarData, this, "handleAvatarDataPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "handleAvatarBillboardPacket");
|
||||
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.
|
||||
// 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.
|
||||
// 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.
|
||||
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;
|
||||
|
||||
|
@ -245,32 +242,13 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
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());
|
||||
|
||||
// we will also force a send of billboard or identity packet
|
||||
// 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
|
||||
if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|| distribution(generator) < IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
|
||||
|
@ -385,7 +363,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
otherAvatar.doneEncoding(false);
|
||||
});
|
||||
|
||||
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
_lastFrameTimestamp = p_high_resolution_clock::now();
|
||||
}
|
||||
|
||||
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
|
||||
if (avatar.hasIdentityChangedAfterParsing(message->getMessage())) {
|
||||
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) {
|
||||
DependencyManager::get<NodeList>()->processKillNode(*message);
|
||||
}
|
||||
|
@ -466,7 +430,6 @@ void AvatarMixer::sendStatsPacket() {
|
|||
QJsonObject statsObject;
|
||||
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["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
|
||||
|
@ -507,7 +470,6 @@ void AvatarMixer::sendStatsPacket() {
|
|||
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
|
||||
|
||||
_sumListeners = 0;
|
||||
_sumBillboardPackets = 0;
|
||||
_sumIdentityPackets = 0;
|
||||
_numStatFrames = 0;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#ifndef hifi_AvatarMixer_h
|
||||
#define hifi_AvatarMixer_h
|
||||
|
||||
#include <PortableHighResolutionClock.h>
|
||||
|
||||
#include <ThreadedAssignment.h>
|
||||
|
||||
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
|
||||
|
@ -34,7 +36,6 @@ public slots:
|
|||
private slots:
|
||||
void handleAvatarDataPacket(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 domainSettingsRequestComplete();
|
||||
|
||||
|
@ -44,15 +45,14 @@ private:
|
|||
|
||||
QThread _broadcastThread;
|
||||
|
||||
quint64 _lastFrameTimestamp;
|
||||
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||
|
||||
float _trailingSleepRatio;
|
||||
float _performanceThrottlingRatio;
|
||||
float _trailingSleepRatio { 1.0f };
|
||||
float _performanceThrottlingRatio { 0.0f };
|
||||
|
||||
int _sumListeners;
|
||||
int _numStatFrames;
|
||||
int _sumBillboardPackets;
|
||||
int _sumIdentityPackets;
|
||||
int _sumListeners { 0 };
|
||||
int _numStatFrames { 0 };
|
||||
int _sumIdentityPackets { 0 };
|
||||
|
||||
float _maxKbpsPerNode = 0.0f;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <NodeData.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <PortableHighResolutionClock.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <UUIDHasher.h>
|
||||
|
||||
|
@ -33,6 +34,8 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
|||
class AvatarMixerClientData : public NodeData {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using HRCTime = p_high_resolution_clock::time_point;
|
||||
|
||||
int parseData(ReceivedMessage& message) override;
|
||||
AvatarData& getAvatar() { return *_avatar; }
|
||||
|
||||
|
@ -45,11 +48,8 @@ public:
|
|||
|
||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||
|
||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||
|
||||
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
||||
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||
void flagIdentityChange() { _identityChangeTimestamp = p_high_resolution_clock::now(); }
|
||||
|
||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||
float getFullRateDistance() const { return _fullRateDistance; }
|
||||
|
@ -86,8 +86,7 @@ private:
|
|||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||
|
||||
quint64 _billboardChangeTimestamp = 0;
|
||||
quint64 _identityChangeTimestamp = 0;
|
||||
HRCTime _identityChangeTimestamp;
|
||||
|
||||
float _fullRateDistance = 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
|
||||
#
|
||||
|
||||
set(INTERFACE_DISPLAY_NAME "Interface")
|
||||
set(INTERFACE_SHORTCUT_NAME "@INTERFACE_SHORTCUT_NAME@")
|
||||
set(INTERFACE_HF_SHORTCUT_NAME "@INTERFACE_HF_SHORTCUT_NAME@")
|
||||
set(INTERFACE_WIN_EXEC_NAME "@INTERFACE_EXEC_PREFIX@.exe")
|
||||
set(CONSOLE_DISPLAY_NAME "Sandbox")
|
||||
set(CONSOLE_INSTALL_SUBDIR "@CONSOLE_INSTALL_DIR@")
|
||||
set(CONSOLE_SHORTCUT_NAME "@CONSOLE_SHORTCUT_NAME@")
|
||||
set(CONSOLE_HF_SHORTCUT_NAME "@CONSOLE_HF_SHORTCUT_NAME@")
|
||||
set(CONSOLE_WIN_EXEC_NAME "@CONSOLE_EXEC_NAME@")
|
||||
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "@PRE_SANDBOX_INTERFACE_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(AC_DISPLAY_NAME "Assignment Client")
|
||||
set(AC_EXEC_NAME "@AC_EXEC_NAME@")
|
||||
set(HIGH_FIDELITY_PROTOCOL "@HIGH_FIDELITY_PROTOCOL@")
|
||||
set(PRODUCTION_BUILD "@PRODUCTION_BUILD@")
|
||||
|
|
|
@ -737,10 +737,10 @@ SectionEnd
|
|||
!macroend
|
||||
|
||||
!macro CheckForRunningApplications action prompter
|
||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@CONSOLE_SHORTCUT_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@INTERFACE_SHORTCUT_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "Domain Server" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "Assignment Client" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@INTERFACE_WIN_EXEC_NAME@" "@INTERFACE_DISPLAY_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@CONSOLE_WIN_EXEC_NAME@" "@CONSOLE_DISPLAY_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@DS_EXEC_NAME@" "@DS_DISPLAY_NAME@" ${action} ${prompter}
|
||||
!insertmacro PromptForRunningApplication "@AC_EXEC_NAME@" "@AC_DISPLAY_NAME@" ${action} ${prompter}
|
||||
!macroend
|
||||
|
||||
;--------------------------------
|
||||
|
|
|
@ -14,11 +14,9 @@ Script.load("edit.js");
|
|||
Script.load("examples.js");
|
||||
Script.load("selectAudioDevice.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("users.js");
|
||||
Script.load("controllers/handControllerGrab.js");
|
||||
Script.load("controllers/squeezeHands.js");
|
||||
Script.load("grab.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
// Script.load("attachedEntitiesManager.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.
|
||||
// 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.
|
||||
that.onResizeViewport = function (newSize) { // Can be overridden or extended by clients.
|
||||
var fractionX = that.x / that.windowDimensions.x;
|
||||
var fractionY = that.y / that.windowDimensions.y;
|
||||
that.windowDimensions = newSize || Controller.getViewportDimensions();
|
||||
that.move(fractionX * that.windowDimensions.x, fractionY * that.windowDimensions.y);
|
||||
var recommendedRect = Controller.getRecommendedOverlayRect();
|
||||
var recommendedDimmensions = { x: recommendedRect.width, y: recommendedRect.height };
|
||||
var originRelativeX = (that.x - that.origin.x);
|
||||
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) {
|
||||
this.fractionKey = optionalPersistenceKey + '.fraction';
|
||||
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) {
|
||||
// Guard against invalid screen size that can occur at shut-down.
|
||||
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.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)) {
|
||||
that.onResizeViewport(currentWindowSize);
|
||||
}
|
||||
|
@ -434,7 +452,8 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
}
|
||||
if (this.fractionKey || optionalInitialPositionFunction) {
|
||||
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 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);
|
||||
|
|
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",
|
||||
"Pyramid Depth",
|
||||
"Ambient Occlusion",
|
||||
"Ambient Occlusion Blurred",
|
||||
"Custom Shader"
|
||||
]
|
||||
RadioButton {
|
||||
|
|
|
@ -80,11 +80,6 @@ Item {
|
|||
label: "GPU",
|
||||
color: "#1AC567"
|
||||
},
|
||||
{
|
||||
prop: "frameTextureCount",
|
||||
label: "Frame",
|
||||
color: "#E2334D"
|
||||
},
|
||||
{
|
||||
prop: "textureGPUTransferCount",
|
||||
label: "Transfer",
|
||||
|
@ -114,13 +109,7 @@ Item {
|
|||
prop: "textureGPUVirtualMemoryUsage",
|
||||
label: "GPU Virtual",
|
||||
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 drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
|
||||
property var drawLightConfig: Render.getConfig("DrawLight")
|
||||
|
@ -178,9 +185,10 @@ Item {
|
|||
title: "Items"
|
||||
height: parent.evalEvenHeight()
|
||||
object: parent.drawOpaqueConfig
|
||||
|
||||
plots: [
|
||||
{
|
||||
object: Render.getConfig("DrawOpaqueDeferred"),
|
||||
object: parent.drawOpaqueConfig,
|
||||
prop: "numDrawn",
|
||||
label: "Opaques",
|
||||
color: "#1AC567"
|
||||
|
@ -198,7 +206,42 @@ Item {
|
|||
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 iconSize: 24
|
||||
readonly property int iconPadding: 5
|
||||
|
||||
readonly property bool shouldReposition: true
|
||||
|
||||
Settings {
|
||||
category: "Overlay.AvatarInputs"
|
||||
|
|
|
@ -21,8 +21,11 @@ FocusScope {
|
|||
objectName: "desktop"
|
||||
anchors.fill: parent
|
||||
|
||||
onHeightChanged: d.repositionAll();
|
||||
onWidthChanged: d.repositionAll();
|
||||
property rect recommendedRect: rect(0,0,0,0);
|
||||
|
||||
onHeightChanged: d.handleSizeChanged();
|
||||
|
||||
onWidthChanged: d.handleSizeChanged();
|
||||
|
||||
// Controls and windows can trigger this signal to ensure the desktop becomes visible
|
||||
// when they're opened.
|
||||
|
@ -50,6 +53,20 @@ FocusScope {
|
|||
QtObject {
|
||||
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) {
|
||||
for (var i = 0; i < item.children.length; ++i) {
|
||||
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() {
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
function reposition(item) {
|
||||
function repositionWindow(targetWindow, forceReposition,
|
||||
oldRecommendedRect, oldRecommendedDimmensions, newRecommendedRect, newRecommendedDimmensions) {
|
||||
|
||||
if (desktop.width === 0 || desktop.height === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var targetWindow = d.getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
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);
|
||||
// If the window is completely offscreen, reposition it
|
||||
if ((targetWindow.x > desktop.width || (targetWindow.x + targetWindow.width) < 0) ||
|
||||
(targetWindow.y > desktop.height || (targetWindow.y + targetWindow.height) < 0)) {
|
||||
|
||||
// if we asked to force reposition, or if the window is completely outside of the recommended rectangle, reposition it
|
||||
if (forceReposition || (targetWindow.x > maxX || (targetWindow.x + targetWindow.width) < recommended.x) ||
|
||||
(targetWindow.y > maxY || (targetWindow.y + targetWindow.height) < recommended.y)) {
|
||||
newPosition.x = -1
|
||||
newPosition.y = -1
|
||||
}
|
||||
|
||||
|
||||
if (newPosition.x === -1 && newPosition.y === -1) {
|
||||
// Set initial window position
|
||||
// var minPosition = Qt.vector2d(-windowRect.x, -windowRect.y);
|
||||
// var maxPosition = Qt.vector2d(desktop.width - windowRect.width, desktop.height - windowRect.height);
|
||||
// newPosition = Utils.clampVector(newPosition, minPosition, maxPosition);
|
||||
// newPosition = Utils.randomPosition(minPosition, maxPosition);
|
||||
newPosition = Qt.vector2d(desktop.width / 2 - targetWindow.width / 2,
|
||||
desktop.height / 2 - targetWindow.height / 2);
|
||||
var originRelativeX = (targetWindow.x - oldRecommendedRect.x);
|
||||
var originRelativeY = (targetWindow.y - oldRecommendedRect.y);
|
||||
if (isNaN(originRelativeX)) {
|
||||
originRelativeX = 0;
|
||||
}
|
||||
if (isNaN(originRelativeY)) {
|
||||
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.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
|
||||
|
||||
#ifndef __APPLE__
|
||||
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);
|
||||
|
||||
|
@ -1183,8 +1178,6 @@ void Application::cleanupBeforeQuit() {
|
|||
}
|
||||
_keyboardFocusHighlight = nullptr;
|
||||
|
||||
getEntities()->clear(); // this will allow entity scripts to properly shutdown
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||
|
@ -1195,6 +1188,7 @@ void Application::cleanupBeforeQuit() {
|
|||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||
|
||||
DependencyManager::get<ScriptEngines>()->saveScripts();
|
||||
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
@ -2686,8 +2680,6 @@ void Application::idle(uint64_t now) {
|
|||
_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.
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
||||
|
@ -4191,8 +4183,13 @@ void Application::updateWindowTitle() const {
|
|||
}
|
||||
|
||||
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...";
|
||||
// reset the environment so that we don't erroneously end up with multiple
|
||||
|
||||
resetPhysicsReadyInformation();
|
||||
|
||||
|
@ -4216,7 +4213,6 @@ void Application::clearDomainOctreeDetails() {
|
|||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
updateWindowTitle();
|
||||
clearDomainOctreeDetails();
|
||||
// disable physics until we have enough information about our new location to not cause craziness.
|
||||
resetPhysicsReadyInformation();
|
||||
}
|
||||
|
@ -4899,19 +4895,44 @@ QRect Application::getRenderingGeometry() 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 {
|
||||
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 {
|
||||
return getActiveDisplayPlugin()->isThrottled();
|
||||
if (_displayPlugin) {
|
||||
return getActiveDisplayPlugin()->isThrottled();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Application::hasFocus() const {
|
||||
return getActiveDisplayPlugin()->hasFocus();
|
||||
if (_displayPlugin) {
|
||||
return getActiveDisplayPlugin()->hasFocus();
|
||||
}
|
||||
return (QApplication::activeWindow() != nullptr);
|
||||
}
|
||||
|
||||
glm::vec2 Application::getViewportDimensions() const {
|
||||
|
|
|
@ -117,6 +117,7 @@ public:
|
|||
QRect getRenderingGeometry() const;
|
||||
|
||||
glm::uvec2 getUiSize() const;
|
||||
QRect getRecommendedOverlayRect() const;
|
||||
QSize getDeviceSize() const;
|
||||
bool hasFocus() const;
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <mutex>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <gpu/Context.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
|
@ -42,26 +41,11 @@ static const float TAU = 6.28318530717958f;
|
|||
//static const float MILKY_WAY_RATIO = 0.4f;
|
||||
static const char* UNIFORM_TIME_NAME = "iGlobalTime";
|
||||
|
||||
|
||||
|
||||
Stars::Stars() {
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
}
|
||||
|
||||
// Produce a random float value between 0 and 1
|
||||
static float frand() {
|
||||
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
|
||||
static vec2 randPolar() {
|
||||
vec2 result(frand(), frand());
|
||||
|
@ -115,59 +99,56 @@ struct StarVertex {
|
|||
vec4 colorAndSize;
|
||||
};
|
||||
|
||||
// FIXME star colors
|
||||
void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||
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;
|
||||
static const int STARS_VERTICES_SLOT{ 0 };
|
||||
static const int STARS_COLOR_SLOT{ 1 };
|
||||
|
||||
const int VERTICES_SLOT = 0;
|
||||
const int COLOR_SLOT = 1;
|
||||
gpu::PipelinePointer Stars::_gridPipeline{};
|
||||
gpu::PipelinePointer Stars::_starsPipeline{};
|
||||
int32_t Stars::_timeSlot{ -1 };
|
||||
|
||||
std::call_once(once, [&] {
|
||||
{
|
||||
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
|
||||
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
|
||||
_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);
|
||||
|
||||
void Stars::init() {
|
||||
if (!_gridPipeline) {
|
||||
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
|
||||
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
|
||||
_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);
|
||||
}
|
||||
|
||||
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;
|
||||
startTime.start();
|
||||
|
||||
vertexBuffer.reset(new gpu::Buffer);
|
||||
|
||||
srand(STARFIELD_SEED);
|
||||
unsigned limit = STARFIELD_NUM_STARS;
|
||||
std::vector<StarVertex> points;
|
||||
points.resize(limit);
|
||||
for (size_t star = 0; star < limit; ++star) {
|
||||
points[star].position = vec4(fromPolar(randPolar()), 1);
|
||||
float size = frand() * 2.5f + 0.5f;
|
||||
|
@ -179,16 +160,32 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
points[star].colorAndSize = vec4(color, size);
|
||||
}
|
||||
}
|
||||
|
||||
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
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 textureCache = DependencyManager::get<TextureCache>();
|
||||
|
@ -210,17 +207,10 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
batch._glUniform1f(_timeSlot, secs);
|
||||
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
|
||||
batch.setPipeline(_starsPipeline);
|
||||
|
||||
batch.setInputFormat(streamFormat);
|
||||
batch.setInputBuffer(VERTICES_SLOT, posView);
|
||||
batch.setInputBuffer(COLOR_SLOT, colView);
|
||||
batch.setInputBuffer(STARS_VERTICES_SLOT, positionView);
|
||||
batch.setInputBuffer(STARS_COLOR_SLOT, colorView);
|
||||
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
||||
}
|
||||
|
|
|
@ -12,21 +12,37 @@
|
|||
#ifndef hifi_Stars_h
|
||||
#define hifi_Stars_h
|
||||
|
||||
#include <gpu/Context.h>
|
||||
|
||||
class RenderArgs;
|
||||
|
||||
// Starfield rendering component.
|
||||
class Stars {
|
||||
public:
|
||||
Stars();
|
||||
~Stars();
|
||||
Stars() = default;
|
||||
~Stars() = default;
|
||||
|
||||
Stars(Stars const&) = delete;
|
||||
Stars& operator=(Stars const&) = delete;
|
||||
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
// The parameters specifiy the field of view.
|
||||
void render(RenderArgs* args, float alpha);
|
||||
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
// Pipelines
|
||||
static gpu::PipelinePointer _gridPipeline;
|
||||
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());
|
||||
}
|
||||
});
|
||||
|
||||
connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));
|
||||
}
|
||||
|
||||
MyAvatar::~MyAvatar() {
|
||||
|
@ -349,6 +351,10 @@ void MyAvatar::simulate(float 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()) {
|
||||
// All the simulation that can be done has been done
|
||||
return;
|
||||
|
@ -1169,9 +1175,6 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
|
|||
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
|
||||
|
||||
setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity());
|
||||
|
||||
// now that physics has adjusted our position, we can update attachements.
|
||||
Avatar::simulateAttachments(deltaTime);
|
||||
}
|
||||
|
||||
QString MyAvatar::getScriptedMotorFrame() const {
|
||||
|
@ -1575,6 +1578,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
float speedIncreaseFactor = 1.8f;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED;
|
||||
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
|
@ -2089,7 +2093,7 @@ float MyAvatar::getAccelerationEnergy() {
|
|||
int changeInVelocity = abs(velocity.length() - priorVelocity.length());
|
||||
float changeInEnergy = priorVelocity.length() * changeInVelocity * AVATAR_MOVEMENT_ENERGY_CONSTANT;
|
||||
priorVelocity = velocity;
|
||||
|
||||
|
||||
return changeInEnergy;
|
||||
}
|
||||
|
||||
|
@ -2111,4 +2115,3 @@ bool MyAvatar::didTeleport() {
|
|||
lastPosition = pos;
|
||||
return (changeInPosition.length() > MAX_AVATAR_MOVEMENT_PER_FRAME);
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ signals:
|
|||
void collisionWithEntity(const Collision& collision);
|
||||
void energyChanged(float newEnergy);
|
||||
void positionGoneTo();
|
||||
|
||||
void onLoadComplete();
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -80,6 +80,11 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
|
|||
return qApp->getUiSize();
|
||||
}
|
||||
|
||||
QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
|
||||
auto rect = qApp->getRecommendedOverlayRect();
|
||||
return qRectToVariant(rect);
|
||||
}
|
||||
|
||||
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
|
||||
auto icIt = _inputControllers.find(0);
|
||||
|
|
|
@ -96,6 +96,7 @@ public slots:
|
|||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
virtual glm::vec2 getViewportDimensions() const;
|
||||
virtual QVariant getRecommendedOverlayRect() const;
|
||||
|
||||
/// Factory to create an InputController
|
||||
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);
|
||||
}
|
||||
|
||||
emit onLoadComplete();
|
||||
});
|
||||
connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString 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.
|
||||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct StateHandler {
|
||||
AnimVariantMap results;
|
||||
|
@ -223,7 +224,10 @@ public:
|
|||
|
||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||
|
||||
protected:
|
||||
signals:
|
||||
void onLoadComplete();
|
||||
|
||||
protected:
|
||||
bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }
|
||||
void updateAnimationStateHandlers();
|
||||
void applyOverridePoses();
|
||||
|
|
|
@ -388,7 +388,7 @@ void SwingTwistConstraint::dynamicallyAdjustLimits(const glm::quat& rotation) {
|
|||
glm::vec3 swungY = swingRotation * Vectors::UNIT_Y;
|
||||
glm::vec3 swingAxis = glm::cross(Vectors::UNIT_Y, swungY);
|
||||
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
|
||||
// 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)
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace AudioConstants {
|
|||
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_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
|
||||
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;
|
||||
|
||||
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 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
|
||||
|
|
|
@ -42,6 +42,7 @@ class CompositorHelper : public QObject, public Dependency {
|
|||
Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
|
||||
public:
|
||||
static const uvec2 VIRTUAL_SCREEN_SIZE;
|
||||
static const QRect VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
|
||||
static const float VIRTUAL_UI_ASPECT_RATIO;
|
||||
static const vec2 VIRTUAL_UI_TARGET_FOV;
|
||||
static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE;
|
||||
|
|
|
@ -34,6 +34,11 @@ glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
|||
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
||||
}
|
||||
|
||||
QRect HmdDisplayPlugin::getRecommendedOverlayRect() const {
|
||||
return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT;
|
||||
}
|
||||
|
||||
|
||||
bool HmdDisplayPlugin::internalActivate() {
|
||||
_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;
|
||||
bool isDisplayVisible() const override { return isHmdMounted(); }
|
||||
|
||||
QRect getRecommendedOverlayRect() const override final;
|
||||
|
||||
virtual glm::mat4 getHeadPose() const override;
|
||||
|
||||
|
||||
|
|
|
@ -75,9 +75,29 @@ EntityTreeRenderer::~EntityTreeRenderer() {
|
|||
// 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() {
|
||||
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();
|
||||
render::PendingChanges pendingChanges;
|
||||
|
@ -94,7 +114,7 @@ void EntityTreeRenderer::reloadEntityScripts() {
|
|||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
foreach(auto entity, _entitiesInScene) {
|
||||
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);
|
||||
|
||||
if (_wantScripts) {
|
||||
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities");
|
||||
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
||||
_entitiesScriptEngine->runInThread();
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
|
||||
setupEntitiesScriptEngine();
|
||||
}
|
||||
|
||||
forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities
|
||||
|
@ -122,6 +139,8 @@ void EntityTreeRenderer::init() {
|
|||
void EntityTreeRenderer::shutdown() {
|
||||
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
|
||||
_shuttingDown = true;
|
||||
|
||||
clear(); // always clear() on shutdown
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::setTree(OctreePointer newTree) {
|
||||
|
@ -763,7 +782,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const
|
|||
if (entity && entity->shouldPreloadScript()) {
|
||||
QString scriptUrl = entity->getScript();
|
||||
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
|
||||
ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload);
|
||||
entity->scriptHasPreloaded();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,6 +126,8 @@ protected:
|
|||
}
|
||||
|
||||
private:
|
||||
void setupEntitiesScriptEngine();
|
||||
|
||||
void addEntityToScene(EntityItemPointer entity);
|
||||
bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar);
|
||||
|
||||
|
@ -155,7 +157,7 @@ private:
|
|||
NetworkTexturePointer _ambientTexture;
|
||||
|
||||
bool _wantScripts;
|
||||
ScriptEngine* _entitiesScriptEngine;
|
||||
QSharedPointer<ScriptEngine> _entitiesScriptEngine;
|
||||
|
||||
bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
|
||||
const EntityItemID& id, const Collision& collision);
|
||||
|
@ -196,6 +198,8 @@ private:
|
|||
QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
|
||||
// For Scene.shouldRenderEntities
|
||||
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 = 1) out vec4 _fragColor1;
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(_entitiesScriptEngineLock);
|
||||
if (_entitiesScriptEngine) {
|
||||
EntityItemID entityID{ id };
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params);
|
||||
|
|
|
@ -71,7 +71,7 @@ public:
|
|||
|
||||
void setEntityTree(EntityTreePointer modelTree);
|
||||
EntityTreePointer getEntityTree() { return _entityTree; }
|
||||
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; }
|
||||
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine);
|
||||
float calculateCost(float mass, float oldVelocity, float newVelocity);
|
||||
public slots:
|
||||
|
||||
|
@ -214,6 +214,8 @@ private:
|
|||
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
|
||||
|
||||
EntityTreePointer _entityTree;
|
||||
|
||||
std::mutex _entitiesScriptEngineLock;
|
||||
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr };
|
||||
|
||||
bool _bidOnSimulationOwnership { false };
|
||||
|
|
|
@ -83,11 +83,18 @@ protected:
|
|||
};
|
||||
|
||||
friend class OffscreenQmlSurface;
|
||||
|
||||
QJsonObject getGLContextData();
|
||||
|
||||
Queue _queue;
|
||||
QMutex _mutex;
|
||||
QWaitCondition _waitCondition;
|
||||
std::atomic<bool> _rendering { false };
|
||||
|
||||
QJsonObject _glData;
|
||||
QMutex _glMutex;
|
||||
QWaitCondition _glWait;
|
||||
|
||||
private:
|
||||
// Event-driven methods
|
||||
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() {
|
||||
qDebug() << "Initializing QML Renderer";
|
||||
|
||||
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
|
||||
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
|
||||
|
||||
if (!_canvas.makeCurrent()) {
|
||||
qWarning("Failed to make context current on QML Renderer Thread");
|
||||
_quit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Expose GL data to QML
|
||||
auto glData = getGLContextData();
|
||||
auto setGL = [=]{ _surface->getRootContext()->setContextProperty("GL", glData); };
|
||||
_surface->executeOnUiThread(setGL);
|
||||
_glMutex.lock();
|
||||
_glData = ::getGLContextData();
|
||||
_glMutex.unlock();
|
||||
_glWait.wakeAll();
|
||||
|
||||
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
|
||||
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
|
||||
|
||||
_renderControl->initialize(_canvas.getContext());
|
||||
setupFbo();
|
||||
|
@ -386,14 +402,16 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
|||
_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,
|
||||
// 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(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit);
|
||||
_updateTimer.setInterval(MIN_TIMER_MS);
|
||||
_updateTimer.start();
|
||||
_qmlComponent = new QQmlComponent(_qmlEngine);
|
||||
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
|
||||
}
|
||||
|
||||
void OffscreenQmlSurface::resize(const QSize& newSize_) {
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
<@if GLPROFILE == PC_GL @>
|
||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||
<@def VERSION_HEADER #version 410 core@>
|
||||
<@def VERSION_HEADER //PC 410 core@>
|
||||
<@elif GLPROFILE == MAC_GL @>
|
||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||
<@def VERSION_HEADER #version 410 core@>
|
||||
<@def VERSION_HEADER //MAC 410 core@>
|
||||
<@else@>
|
||||
<@def GPU_FEATURE_PROFILE GPU_CORE@>
|
||||
<@def VERSION_HEADER #version 410 core@>
|
||||
<@def VERSION_HEADER //410 core@>
|
||||
<@endif@>
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
int _DSNumAPIDrawcalls = 0;
|
||||
int _DSNumDrawcalls = 0;
|
||||
int _DSNumTriangles = 0;
|
||||
|
||||
int _PSNumSetPipelines = 0;
|
||||
|
||||
ContextStats() {}
|
||||
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 DO_IT_NOW(call, offset) runLastCommand();
|
||||
#define DO_IT_NOW(call, offset)
|
||||
#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc
|
||||
// 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) {
|
||||
// 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(target);
|
||||
_params.push_back(unit);
|
||||
|
||||
|
||||
DO_IT_NOW(_glActiveBindTexture, 3);
|
||||
}
|
||||
void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
|
||||
glActiveTexture(batch._params[paramOffset + 2]._uint);
|
||||
glBindTexture(
|
||||
batch._params[paramOffset + 1]._uint,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._uint),
|
||||
batch._params[paramOffset + 0]._uint);
|
||||
|
||||
(void) CHECK_GL_ERROR();
|
||||
|
@ -492,8 +491,6 @@ void Batch::_glUniform1i(GLint location, GLint v0) {
|
|||
ADD_COMMAND_GL(glUniform1i);
|
||||
_params.push_back(v0);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform1i, 1);
|
||||
}
|
||||
void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -503,7 +500,7 @@ void GLBackend::do_glUniform1i(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform1f(
|
||||
batch._params[paramOffset + 1]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int),
|
||||
batch._params[paramOffset + 0]._int);
|
||||
(void) CHECK_GL_ERROR();
|
||||
}
|
||||
|
@ -515,8 +512,6 @@ void Batch::_glUniform1f(GLint location, GLfloat v0) {
|
|||
ADD_COMMAND_GL(glUniform1f);
|
||||
_params.push_back(v0);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform1f, 1);
|
||||
}
|
||||
void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -527,7 +522,7 @@ void GLBackend::do_glUniform1f(Batch& batch, size_t paramOffset) {
|
|||
updatePipeline();
|
||||
|
||||
glUniform1f(
|
||||
batch._params[paramOffset + 1]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int),
|
||||
batch._params[paramOffset + 0]._float);
|
||||
(void) CHECK_GL_ERROR();
|
||||
}
|
||||
|
@ -538,8 +533,6 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) {
|
|||
_params.push_back(v1);
|
||||
_params.push_back(v0);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform2f, 1);
|
||||
}
|
||||
|
||||
void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
|
||||
|
@ -550,7 +543,7 @@ void GLBackend::do_glUniform2f(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform2f(
|
||||
batch._params[paramOffset + 2]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||
batch._params[paramOffset + 1]._float,
|
||||
batch._params[paramOffset + 0]._float);
|
||||
(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(v0);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform3f, 1);
|
||||
}
|
||||
|
||||
void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
|
||||
|
@ -575,7 +566,7 @@ void GLBackend::do_glUniform3f(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform3f(
|
||||
batch._params[paramOffset + 3]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
|
||||
batch._params[paramOffset + 2]._float,
|
||||
batch._params[paramOffset + 1]._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(v0);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform4f, 1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -604,7 +593,7 @@ void GLBackend::do_glUniform4f(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform4f(
|
||||
batch._params[paramOffset + 4]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 4]._int),
|
||||
batch._params[paramOffset + 3]._float,
|
||||
batch._params[paramOffset + 2]._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(count);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform3fv, 3);
|
||||
}
|
||||
void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -630,7 +617,7 @@ void GLBackend::do_glUniform3fv(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform3fv(
|
||||
batch._params[paramOffset + 2]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||
batch._params[paramOffset + 1]._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(count);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform4fv, 3);
|
||||
}
|
||||
void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -656,7 +641,7 @@ void GLBackend::do_glUniform4fv(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
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;
|
||||
const GLfloat* value = (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint);
|
||||
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(count);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniform4iv, 3);
|
||||
}
|
||||
void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -682,7 +665,7 @@ void GLBackend::do_glUniform4iv(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniform4iv(
|
||||
batch._params[paramOffset + 2]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 2]._int),
|
||||
batch._params[paramOffset + 1]._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(count);
|
||||
_params.push_back(location);
|
||||
|
||||
DO_IT_NOW(_glUniformMatrix4fv, 4);
|
||||
}
|
||||
void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
|
||||
if (_pipeline._program == 0) {
|
||||
|
@ -708,7 +689,7 @@ void GLBackend::do_glUniformMatrix4fv(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
updatePipeline();
|
||||
glUniformMatrix4fv(
|
||||
batch._params[paramOffset + 3]._int,
|
||||
GET_UNIFORM_LOCATION(batch._params[paramOffset + 3]._int),
|
||||
batch._params[paramOffset + 2]._uint,
|
||||
batch._params[paramOffset + 1]._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(green);
|
||||
_params.push_back(red);
|
||||
|
||||
DO_IT_NOW(_glColor4f, 4);
|
||||
}
|
||||
void GLBackend::do_glColor4f(Batch& batch, size_t paramOffset) {
|
||||
|
||||
|
|
|
@ -153,17 +153,42 @@ public:
|
|||
|
||||
class GLShader : public GPUObject {
|
||||
public:
|
||||
GLuint _shader;
|
||||
GLuint _program;
|
||||
enum Version {
|
||||
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();
|
||||
|
||||
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 GLuint getShaderID(const ShaderPointer& shader);
|
||||
|
||||
class GLState : public GPUObject {
|
||||
public:
|
||||
|
@ -464,6 +489,7 @@ protected:
|
|||
PipelinePointer _pipeline;
|
||||
|
||||
GLuint _program;
|
||||
GLShader* _programShader;
|
||||
bool _invalidProgram;
|
||||
|
||||
State::Data _stateCache;
|
||||
|
@ -475,6 +501,7 @@ protected:
|
|||
PipelineStageState() :
|
||||
_pipeline(),
|
||||
_program(0),
|
||||
_programShader(nullptr),
|
||||
_invalidProgram(false),
|
||||
_stateCache(State::DEFAULT),
|
||||
_stateSignatureCache(0),
|
||||
|
|
|
@ -64,11 +64,15 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
|
|||
return;
|
||||
}
|
||||
|
||||
// A true new Pipeline
|
||||
_stats._PSNumSetPipelines++;
|
||||
|
||||
// null pipeline == reset
|
||||
if (!pipeline) {
|
||||
_pipeline._pipeline.reset();
|
||||
|
||||
_pipeline._program = 0;
|
||||
_pipeline._programShader = nullptr;
|
||||
_pipeline._invalidProgram = true;
|
||||
|
||||
_pipeline._state = nullptr;
|
||||
|
@ -80,8 +84,10 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
|
||||
// check the program cache
|
||||
if (_pipeline._program != pipelineObject->_program->_program) {
|
||||
_pipeline._program = pipelineObject->_program->_program;
|
||||
GLuint glprogram = pipelineObject->_program->getProgram();
|
||||
if (_pipeline._program != glprogram) {
|
||||
_pipeline._program = glprogram;
|
||||
_pipeline._programShader = pipelineObject->_program;
|
||||
_pipeline._invalidProgram = true;
|
||||
}
|
||||
|
||||
|
@ -142,6 +148,7 @@ void GLBackend::resetPipelineStage() {
|
|||
// Second the shader side
|
||||
_pipeline._invalidProgram = false;
|
||||
_pipeline._program = 0;
|
||||
_pipeline._programShader = nullptr;
|
||||
_pipeline._pipeline.reset();
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
|
|
@ -13,27 +13,205 @@
|
|||
|
||||
using namespace gpu;
|
||||
|
||||
GLBackend::GLShader::GLShader() :
|
||||
_shader(0),
|
||||
_program(0)
|
||||
{}
|
||||
GLBackend::GLShader::GLShader()
|
||||
{
|
||||
}
|
||||
|
||||
GLBackend::GLShader::~GLShader() {
|
||||
if (_shader != 0) {
|
||||
glDeleteShader(_shader);
|
||||
}
|
||||
if (_program != 0) {
|
||||
glDeleteProgram(_program);
|
||||
for (auto& so : _shaderObjects) {
|
||||
if (so.glshader != 0) {
|
||||
glDeleteShader(so.glshader);
|
||||
}
|
||||
if (so.glprogram != 0) {
|
||||
glDeleteProgram(so.glprogram);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void makeBindings(GLBackend::GLShader* shader) {
|
||||
if(!shader || !shader->_program) {
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject) {
|
||||
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;
|
||||
}
|
||||
GLuint glprogram = shader->_program;
|
||||
GLuint glprogram = shaderObject.glprogram;
|
||||
GLint loc = -1;
|
||||
|
||||
|
||||
//Check for gpu specific attribute slotBindings
|
||||
loc = glGetAttribLocation(glprogram, "inPosition");
|
||||
if (loc >= 0 && loc != gpu::Stream::POSITION) {
|
||||
|
@ -96,226 +274,111 @@ void makeBindings(GLBackend::GLShader* shader) {
|
|||
loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer");
|
||||
if (loc >= 0) {
|
||||
glShaderStorageBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
||||
shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||
}
|
||||
#else
|
||||
loc = glGetUniformLocation(glprogram, "transformObjectBuffer");
|
||||
if (loc >= 0) {
|
||||
glProgramUniform1i(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
|
||||
shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||
shaderObject.transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
|
||||
}
|
||||
#endif
|
||||
|
||||
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
|
||||
if (loc >= 0) {
|
||||
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
|
||||
shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
||||
shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT;
|
||||
}
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
GLBackend::GLShader* compileShader(const Shader& shader) {
|
||||
GLBackend::GLShader* compileBackendShader(const Shader& shader) {
|
||||
// Any GLSLprogram ? normally yes...
|
||||
const std::string& shaderSource = shader.getSource().getCode();
|
||||
if (shaderSource.empty()) {
|
||||
qCDebug(gpulogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// GLSL version
|
||||
const std::string glslVersion = {
|
||||
"#version 410 core"
|
||||
};
|
||||
|
||||
// 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()];
|
||||
|
||||
// Create the shader object
|
||||
GLuint glshader = glCreateShader(shaderDomain);
|
||||
if (!glshader) {
|
||||
qCDebug(gpulogging) << "GLShader::compileShader - failed to create the gl shader object";
|
||||
return nullptr;
|
||||
}
|
||||
// Domain specific defines
|
||||
const std::string domainDefines[NUM_SHADER_DOMAINS] = {
|
||||
"#define VERTEX_SHADER",
|
||||
"#define PIXEL_SHADER"
|
||||
};
|
||||
|
||||
// Assign the source
|
||||
const GLchar* srcstr = shaderSource.c_str();
|
||||
glShaderSource(glshader, 1, &srcstr, NULL);
|
||||
|
||||
// Compile !
|
||||
glCompileShader(glshader);
|
||||
|
||||
// check if shader compiled
|
||||
GLint compiled = 0;
|
||||
glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled);
|
||||
// Versions specific of the shader
|
||||
const std::string versionDefines[GLBackend::GLShader::NumVersions] = {
|
||||
""
|
||||
};
|
||||
|
||||
// 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();
|
||||
GLBackend::GLShader::ShaderObjects shaderObjects;
|
||||
|
||||
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||
auto& shaderObject = shaderObjects[version];
|
||||
|
||||
std::string shaderDefines = glslVersion + "\n" + domainDefines[shader.getType()] + "\n" + versionDefines[version];
|
||||
|
||||
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
|
||||
GLBackend::GLShader* object = new GLBackend::GLShader();
|
||||
object->_shader = glshader;
|
||||
object->_program = glprogram;
|
||||
|
||||
makeBindings(object);
|
||||
object->_shaderObjects = shaderObjects;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
GLBackend::GLShader* compileProgram(const Shader& program) {
|
||||
if(!program.isProgram()) {
|
||||
GLBackend::GLShader* compileBackendProgram(const Shader& program) {
|
||||
if (!program.isProgram()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Let's go through every shaders and make sure they are ready to go
|
||||
std::vector< GLuint > shaderObjects;
|
||||
for (auto subShader : program.getShaders()) {
|
||||
GLuint so = GLBackend::getShaderID(subShader);
|
||||
if (!so) {
|
||||
qCDebug(gpulogging) << "GLShader::compileProgram - One of the shaders of the program is not compiled?";
|
||||
GLBackend::GLShader::ShaderObjects programObjects;
|
||||
|
||||
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||
auto& programObject = programObjects[version];
|
||||
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
// 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
|
||||
// So far so good, the program versions have all been created successfully
|
||||
GLBackend::GLShader* object = new GLBackend::GLShader();
|
||||
object->_shader = 0;
|
||||
object->_program = glprogram;
|
||||
|
||||
makeBindings(object);
|
||||
object->_shaderObjects = programObjects;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
@ -329,14 +392,14 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
|
|||
}
|
||||
// need to have a gpu object?
|
||||
if (shader.isProgram()) {
|
||||
GLShader* tempObject = compileProgram(shader);
|
||||
if (tempObject) {
|
||||
GLShader* tempObject = compileBackendProgram(shader);
|
||||
if (tempObject) {
|
||||
object = tempObject;
|
||||
Backend::setGPUObject(shader, object);
|
||||
}
|
||||
} else if (shader.isDomain()) {
|
||||
GLShader* tempObject = compileShader(shader);
|
||||
if (tempObject) {
|
||||
GLShader* tempObject = compileBackendShader(shader);
|
||||
if (tempObject) {
|
||||
object = tempObject;
|
||||
Backend::setGPUObject(shader, object);
|
||||
}
|
||||
|
@ -345,23 +408,6 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
|
|||
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 {
|
||||
public:
|
||||
gpu::Element _element;
|
||||
|
@ -714,27 +760,38 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
|
|||
return false;
|
||||
}
|
||||
|
||||
if (object->_program) {
|
||||
Shader::SlotSet buffers;
|
||||
makeUniformBlockSlots(object->_program, slotBindings, buffers);
|
||||
// Apply bindings to all program versions and generate list of slots from default version
|
||||
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
|
||||
auto& shaderObject = object->_shaderObjects[version];
|
||||
if (shaderObject.glprogram) {
|
||||
Shader::SlotSet buffers;
|
||||
makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
|
||||
|
||||
Shader::SlotSet uniforms;
|
||||
Shader::SlotSet textures;
|
||||
Shader::SlotSet samplers;
|
||||
makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers);
|
||||
|
||||
Shader::SlotSet inputs;
|
||||
makeInputSlots(object->_program, slotBindings, inputs);
|
||||
Shader::SlotSet uniforms;
|
||||
Shader::SlotSet textures;
|
||||
Shader::SlotSet samplers;
|
||||
makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
|
||||
|
||||
Shader::SlotSet outputs;
|
||||
makeOutputSlots(object->_program, slotBindings, outputs);
|
||||
Shader::SlotSet inputs;
|
||||
makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
|
||||
|
||||
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
|
||||
|
||||
} else if (object->_shader) {
|
||||
Shader::SlotSet outputs;
|
||||
makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -325,3 +325,26 @@ Buffer::Size Buffer::append(Size size, const Byte* data) {
|
|||
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 {
|
||||
protected:
|
||||
void initFromBuffer(const BufferPointer& buffer) {
|
||||
_buffer = (buffer);
|
||||
if (_buffer) {
|
||||
_size = (buffer->getSize());
|
||||
}
|
||||
}
|
||||
protected:
|
||||
static const Resource::Size DEFAULT_OFFSET{ 0 };
|
||||
static const Element DEFAULT_ELEMENT;
|
||||
|
||||
public:
|
||||
typedef Resource::Size Size;
|
||||
typedef int Index;
|
||||
using Size = Resource::Size;
|
||||
using Index = int;
|
||||
|
||||
BufferPointer _buffer;
|
||||
Size _offset{ 0 };
|
||||
Size _size{ 0 };
|
||||
Size _offset;
|
||||
Size _size;
|
||||
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& 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(); }
|
||||
|
||||
//Template iterator with random access on the buffer sysmem
|
||||
|
|
|
@ -155,9 +155,12 @@ void ResourceCache::clearATPAssets() {
|
|||
}
|
||||
{
|
||||
QWriteLocker locker(&_resourcesToBeGottenLock);
|
||||
for (auto& url : _resourcesToBeGotten) {
|
||||
if (url.scheme() == URL_SCHEME_ATP) {
|
||||
_resourcesToBeGotten.removeAll(url);
|
||||
auto it = _resourcesToBeGotten.begin();
|
||||
while (it != _resourcesToBeGotten.end()) {
|
||||
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;
|
||||
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 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,
|
||||
// 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
|
||||
|
|
|
@ -72,6 +72,17 @@ CharacterController::CharacterController() {
|
|||
_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 {
|
||||
return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class btDynamicsWorld;
|
|||
class CharacterController : public btCharacterControllerInterface {
|
||||
public:
|
||||
CharacterController();
|
||||
virtual ~CharacterController() {}
|
||||
virtual ~CharacterController();
|
||||
|
||||
bool needsRemoval() const;
|
||||
bool needsAddition() const;
|
||||
|
|
|
@ -105,6 +105,12 @@ public:
|
|||
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
|
||||
virtual glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
return baseProjection;
|
||||
|
|
|
@ -46,6 +46,7 @@ const float DEFAULT_METALLIC = 0;
|
|||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||
const vec3 DEFAULT_EMISSIVE = vec3(0.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) {
|
||||
|
|
|
@ -90,6 +90,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
|
|||
|
||||
// Specular highlight from ambient
|
||||
vec3 direction = -reflect(fragEyeDir, fragNormal);
|
||||
|
||||
float levels = getLightAmbientMapNumMips(light);
|
||||
float lod = min(floor((roughness) * levels), levels);
|
||||
vec4 skyboxLight = evalSkyboxLight(direction, lod);
|
||||
|
|
|
@ -96,7 +96,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
addJob<PrepareDeferred>("PrepareDeferred");
|
||||
|
||||
// Render opaque objects in DeferredBuffer
|
||||
addJob<DrawDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
|
||||
addJob<DrawStateSortDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
|
||||
|
||||
// Once opaque is all rendered create stencil background
|
||||
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
|
||||
|
@ -185,9 +185,6 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
|
|||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setStateScissorRect(args->_viewport);
|
||||
|
||||
config->setNumDrawn((int)inItems.size());
|
||||
emit config->numDrawnChanged();
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||
|
@ -199,6 +196,40 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
|
|||
renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn);
|
||||
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) :
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
using JobModel = render::Job::Model<SetupDeferred>;
|
||||
};
|
||||
|
||||
|
||||
class PrepareDeferred {
|
||||
public:
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
@ -29,32 +30,31 @@ public:
|
|||
using JobModel = render::Job::Model<PrepareDeferred>;
|
||||
};
|
||||
|
||||
|
||||
class RenderDeferred {
|
||||
public:
|
||||
using JobModel = render::Job::Model<RenderDeferred>;
|
||||
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
||||
};
|
||||
|
||||
class DrawConfig : public render::Job::Config {
|
||||
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)
|
||||
public:
|
||||
|
||||
int getNumDrawn() { return numDrawn; }
|
||||
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
|
||||
int getNumDrawn() { return _numDrawn; }
|
||||
void setNumDrawn(int numDrawn) { _numDrawn = numDrawn; emit newStats(); }
|
||||
|
||||
int maxDrawn{ -1 };
|
||||
|
||||
signals:
|
||||
void numDrawnChanged();
|
||||
void newStats();
|
||||
void dirty();
|
||||
|
||||
protected:
|
||||
int numDrawn{ 0 };
|
||||
int _numDrawn{ 0 };
|
||||
};
|
||||
|
||||
class DrawDeferred {
|
||||
|
@ -72,6 +72,44 @@ protected:
|
|||
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 {
|
||||
public:
|
||||
using JobModel = render::Job::Model<DrawStencilDeferred>;
|
||||
|
|
|
@ -27,7 +27,7 @@ ToneMappingEffect::ToneMappingEffect() {
|
|||
}
|
||||
|
||||
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
|
||||
//
|
||||
// Draw texture 0 fetched at texcoord.xy
|
||||
|
|
|
@ -41,20 +41,15 @@ void main() {
|
|||
float w = clamp( s, 0.0, 0.5);
|
||||
float a = smoothstep(0.5 - w, 0.5 + w, sdf);
|
||||
|
||||
// gamma correction for linear attenuation
|
||||
a = pow(a, 1.0 / gamma);
|
||||
|
||||
// discard if unvisible
|
||||
if (a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
packDeferredFragmentLightmap(
|
||||
normalize(_normal),
|
||||
1.0,
|
||||
vec3(1.0),
|
||||
DEFAULT_ROUGHNESS,
|
||||
DEFAULT_METALLIC,
|
||||
DEFAULT_SPECULAR,
|
||||
Color.rgb);
|
||||
packDeferredFragmentTranslucent(
|
||||
normalize(_normal),
|
||||
a,
|
||||
Color.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_ROUGHNESS);
|
||||
}
|
|
@ -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) {
|
||||
assert(renderContext->args);
|
||||
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 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->frameTextureMemoryUsage = _gpuStats._RSAmountTextureMemoryBounded - gpuStats._RSAmountTextureMemoryBounded;
|
||||
|
||||
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines - gpuStats._PSNumSetPipelines;
|
||||
|
||||
config->emitDirty();
|
||||
}
|
||||
|
|
|
@ -47,6 +47,9 @@ namespace render {
|
|||
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
|
||||
Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
|
||||
|
||||
Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY dirty)
|
||||
|
||||
|
||||
public:
|
||||
EngineStatsConfig() : Job::Config(true) {}
|
||||
|
||||
|
@ -73,6 +76,10 @@ namespace render {
|
|||
quint32 frameTextureRate{ 0 };
|
||||
qint64 frameTextureMemoryUsage{ 0 };
|
||||
|
||||
quint32 frameSetPipelineCount{ 0 };
|
||||
|
||||
|
||||
|
||||
void emitDirty() { emit dirty(); }
|
||||
|
||||
signals:
|
||||
|
|
|
@ -127,6 +127,9 @@ protected:
|
|||
// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
|
||||
class JobConfig : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint64 cpuRunTime READ getCPUTRunTime NOTIFY newStats())
|
||||
|
||||
quint64 _CPURunTime{ 0 };
|
||||
public:
|
||||
using Persistent = PersistentConfig<JobConfig>;
|
||||
|
||||
|
@ -151,11 +154,17 @@ public:
|
|||
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(); }
|
||||
|
||||
// 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:
|
||||
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
void newStats();
|
||||
};
|
||||
|
||||
class TaskConfig : public JobConfig {
|
||||
|
@ -223,7 +232,11 @@ public:
|
|||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
|
||||
|
||||
protected:
|
||||
void setCPURunTime(quint64 ustime) { std::static_pointer_cast<Config>(_config)->setCPURunTime(ustime); }
|
||||
|
||||
QConfigPointer _config;
|
||||
|
||||
friend class Job;
|
||||
};
|
||||
using ConceptPointer = std::shared_ptr<Concept>;
|
||||
|
||||
|
@ -278,8 +291,11 @@ public:
|
|||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
PerformanceTimer perfTimer(_name.c_str());
|
||||
PROFILE_RANGE(_name.c_str());
|
||||
auto start = usecTimestampNow();
|
||||
|
||||
_concept->run(sceneContext, renderContext);
|
||||
|
||||
_concept->setCPURunTime(usecTimestampNow() - start);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <AudioConstants.h>
|
||||
|
@ -143,7 +144,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
|
||||
ScriptEngine::~ScriptEngine() {
|
||||
qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
if (scriptEngines) {
|
||||
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
|
||||
// for the download
|
||||
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
|
||||
// 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
|
||||
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread ["
|
||||
<< QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
|
||||
"entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;
|
||||
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
|
||||
<< QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
|
||||
#endif
|
||||
|
||||
QMetaObject::invokeMethod(this, "loadEntityScript",
|
||||
Q_ARG(const EntityItemID&, entityID),
|
||||
Q_ARG(const QString&, entityScript),
|
||||
Q_ARG(bool, forceRedownload));
|
||||
return;
|
||||
}
|
||||
#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);
|
||||
strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
|
||||
} else {
|
||||
// FIXME - I'm leaving this in for testing, so that QA can confirm that sometimes the script contents
|
||||
// returns after the ScriptEngine has been deleted, we can remove this after QA verifies the
|
||||
// repro case.
|
||||
qDebug() << "ScriptCache::getScriptContents() returned after our ScriptEngine was deleted... script:" << scriptOrURL;
|
||||
}
|
||||
}, forceRedownload);
|
||||
}
|
||||
|
||||
|
@ -1213,6 +1199,16 @@ void ScriptEngine::unloadAllEntityScripts() {
|
|||
callEntityScriptMethod(entityID, "unload");
|
||||
}
|
||||
_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) {
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
Q_INVOKABLE QUrl resolvePath(const QString& path) const;
|
||||
|
||||
// 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 unloadAllEntityScripts();
|
||||
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList());
|
||||
|
|
|
@ -22,12 +22,7 @@
|
|||
#define __STR1__(x) __STR2__(x)
|
||||
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
||||
|
||||
#ifndef __APPLE__
|
||||
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() {
|
||||
static ScriptsModel scriptsModel;
|
||||
|
|
|
@ -55,8 +55,11 @@ public:
|
|||
}
|
||||
~Counter() { log(); }
|
||||
|
||||
// Increase the count for key.
|
||||
void add(const K& key);
|
||||
// Increase the count for key (by inc).
|
||||
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).
|
||||
void log();
|
||||
|
@ -123,20 +126,25 @@ private:
|
|||
};
|
||||
|
||||
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);
|
||||
|
||||
auto& it = _map.find(k);
|
||||
|
||||
if (it == _map.end()) {
|
||||
// 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 {
|
||||
// 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>
|
||||
void Counter<K>::log() {
|
||||
// Avoid logging nothing
|
||||
|
|
|
@ -34,7 +34,7 @@ float PIDController::update(float measuredValue, float dt, bool resetAccumulator
|
|||
if (getIsLogging()) { // if logging/reporting
|
||||
updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue);
|
||||
}
|
||||
Q_ASSERT(!isnan(computedValue));
|
||||
Q_ASSERT(!glm::isnan(computedValue));
|
||||
|
||||
// update state for next time
|
||||
_lastError = error;
|
||||
|
@ -75,4 +75,4 @@ void PIDController::reportHistory() {
|
|||
qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() <<
|
||||
"controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() <<
|
||||
"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();
|
||||
}
|
||||
|
||||
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 contains a NaN don't try to convert it
|
||||
return QVariant();
|
||||
|
@ -140,6 +140,18 @@ QVariant vec3toVariant(const glm::vec3 &vec3) {
|
|||
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 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;
|
||||
valid = false;
|
||||
if (!object.isValid() || object.isNull()) {
|
||||
|
@ -189,12 +201,49 @@ glm::vec3 vec3FromVariant(const QVariant &object, bool& valid) {
|
|||
return v;
|
||||
}
|
||||
|
||||
glm::vec3 vec3FromVariant(const QVariant &object) {
|
||||
glm::vec3 vec3FromVariant(const QVariant& object) {
|
||||
bool valid = false;
|
||||
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();
|
||||
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
|
||||
|
@ -207,7 +256,7 @@ QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat &quat) {
|
|||
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.y = object.property("y").toVariant().toFloat();
|
||||
quat.z = object.property("z").toVariant().toFloat();
|
||||
|
@ -245,12 +294,12 @@ glm::quat quatFromVariant(const QVariant &object, bool& isValid) {
|
|||
return q;
|
||||
}
|
||||
|
||||
glm::quat quatFromVariant(const QVariant &object) {
|
||||
glm::quat quatFromVariant(const QVariant& object) {
|
||||
bool valid = false;
|
||||
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 vec3 contains a NaN don't try to convert it
|
||||
return QVariant();
|
||||
|
|
|
@ -43,12 +43,15 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4);
|
|||
// Vec4
|
||||
QScriptValue vec4toScriptValue(QScriptEngine* engine, const 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
|
||||
QScriptValue vec3toScriptValue(QScriptEngine* engine, const 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);
|
||||
|
||||
|
@ -71,9 +74,10 @@ glm::quat quatFromVariant(const QVariant &object);
|
|||
// Rect
|
||||
QScriptValue qRectToScriptValue(QScriptEngine* engine, const 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);
|
||||
QVariant qRectToVariant(const QRect& rect);
|
||||
|
||||
|
||||
// xColor
|
||||
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);
|
||||
|
|
|
@ -18,7 +18,7 @@ void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
|
|||
const QVariant badDefaultValue = NAN;
|
||||
bool ok = true;
|
||||
float tempFloat = value(name, badDefaultValue).toFloat(&ok);
|
||||
if (ok && !isnan(tempFloat)) {
|
||||
if (ok && !glm::isnan(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 y = value(QString("y"), 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);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ void Settings::getQuatValueIfValid(const QString& name, glm::quat& quatValue) {
|
|||
float y = value(QString("y"), badDefaultValue).toFloat(&ok);
|
||||
float z = value(QString("z"), 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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue