mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 14:33:31 +02:00
Merge branch 'atp' of github.com:birarda/hifi into atp-mappings
This commit is contained in:
commit
bfb9801b22
95 changed files with 2904 additions and 1951 deletions
|
@ -139,13 +139,13 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
httpStatusPort = parser.value(httpStatusPortOption).toUShort();
|
||||
}
|
||||
|
||||
QDir logDirectory { "." };
|
||||
QString logDirectory;
|
||||
|
||||
if (parser.isSet(logDirectoryOption)) {
|
||||
logDirectory = parser.value(logDirectoryOption);
|
||||
} else {
|
||||
logDirectory = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
}
|
||||
|
||||
|
||||
Assignment::Type requestAssignmentType = Assignment::AllTypes;
|
||||
if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) {
|
||||
requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt();
|
||||
|
|
|
@ -33,8 +33,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
const unsigned int maxAssignmentClientForks,
|
||||
Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QDir logDirectory) :
|
||||
_logDirectory(logDirectory),
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory) :
|
||||
_httpManager(QHostAddress::LocalHost, httpStatusServerPort, "", this),
|
||||
_numAssignmentClientForks(numAssignmentClientForks),
|
||||
_minAssignmentClientForks(minAssignmentClientForks),
|
||||
|
@ -48,6 +47,11 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
{
|
||||
qDebug() << "_requestAssignmentType =" << _requestAssignmentType;
|
||||
|
||||
if (!logDirectory.isEmpty()) {
|
||||
_wantsChildFileLogging = true;
|
||||
_logDirectory = QDir(logDirectory);
|
||||
}
|
||||
|
||||
// start the Logging class with the parent's target name
|
||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
||||
|
||||
|
@ -159,52 +163,61 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
|
||||
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
|
||||
|
||||
// Setup log files
|
||||
const QString DATETIME_FORMAT = "yyyyMMdd.hh.mm.ss.zzz";
|
||||
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;
|
||||
|
||||
if (!_logDirectory.exists()) {
|
||||
qDebug() << "Log directory (" << _logDirectory.absolutePath() << ") does not exist, creating.";
|
||||
_logDirectory.mkpath(_logDirectory.absolutePath());
|
||||
|
||||
if (_wantsChildFileLogging) {
|
||||
// Setup log files
|
||||
const QString DATETIME_FORMAT = "yyyyMMdd.hh.mm.ss.zzz";
|
||||
|
||||
if (!_logDirectory.exists()) {
|
||||
qDebug() << "Log directory (" << _logDirectory.absolutePath() << ") does not exist, creating.";
|
||||
_logDirectory.mkpath(_logDirectory.absolutePath());
|
||||
}
|
||||
|
||||
nowString = QDateTime::currentDateTime().toString(DATETIME_FORMAT);
|
||||
stdoutFilenameTemp = QString("ac-%1-stdout.txt").arg(nowString);
|
||||
stderrFilenameTemp = QString("ac-%1-stderr.txt").arg(nowString);
|
||||
stdoutPathTemp = _logDirectory.absoluteFilePath(stdoutFilenameTemp);
|
||||
stderrPathTemp = _logDirectory.absoluteFilePath(stderrFilenameTemp);
|
||||
|
||||
// reset our output and error files
|
||||
assignmentClient->setStandardOutputFile(stdoutPathTemp);
|
||||
assignmentClient->setStandardErrorFile(stderrPathTemp);
|
||||
}
|
||||
|
||||
auto nowString = QDateTime::currentDateTime().toString(DATETIME_FORMAT);
|
||||
auto stdoutFilenameTemp = QString("ac-%1-stdout.txt").arg(nowString);
|
||||
auto stderrFilenameTemp = QString("ac-%1-stderr.txt").arg(nowString);
|
||||
QString stdoutPathTemp = _logDirectory.absoluteFilePath(stdoutFilenameTemp);
|
||||
QString stderrPathTemp = _logDirectory.absoluteFilePath(stderrFilenameTemp);
|
||||
|
||||
// reset our output and error files
|
||||
assignmentClient->setStandardOutputFile(stdoutPathTemp);
|
||||
assignmentClient->setStandardErrorFile(stderrPathTemp);
|
||||
|
||||
// make sure that the output from the child process appears in our output
|
||||
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments);
|
||||
|
||||
// Update log path to use PID in filename
|
||||
auto stdoutFilename = QString("ac-%1_%2-stdout.txt").arg(nowString).arg(assignmentClient->processId());
|
||||
auto stderrFilename = QString("ac-%1_%2-stderr.txt").arg(nowString).arg(assignmentClient->processId());
|
||||
QString stdoutPath = _logDirectory.absoluteFilePath(stdoutFilename);
|
||||
QString stderrPath = _logDirectory.absoluteFilePath(stderrFilename);
|
||||
QString stdoutPath, stderrPath;
|
||||
|
||||
qDebug() << "Renaming " << stdoutPathTemp << " to " << stdoutPath;
|
||||
if (!_logDirectory.rename(stdoutFilenameTemp, stdoutFilename)) {
|
||||
qDebug() << "Failed to rename " << stdoutFilenameTemp;
|
||||
stdoutPath = stdoutPathTemp;
|
||||
stdoutFilename = stdoutFilenameTemp;
|
||||
if (_wantsChildFileLogging) {
|
||||
|
||||
// Update log path to use PID in filename
|
||||
auto stdoutFilename = QString("ac-%1_%2-stdout.txt").arg(nowString).arg(assignmentClient->processId());
|
||||
auto stderrFilename = QString("ac-%1_%2-stderr.txt").arg(nowString).arg(assignmentClient->processId());
|
||||
stdoutPath = _logDirectory.absoluteFilePath(stdoutFilename);
|
||||
stderrPath = _logDirectory.absoluteFilePath(stderrFilename);
|
||||
|
||||
qDebug() << "Renaming " << stdoutPathTemp << " to " << stdoutPath;
|
||||
if (!_logDirectory.rename(stdoutFilenameTemp, stdoutFilename)) {
|
||||
qDebug() << "Failed to rename " << stdoutFilenameTemp;
|
||||
stdoutPath = stdoutPathTemp;
|
||||
stdoutFilename = stdoutFilenameTemp;
|
||||
}
|
||||
|
||||
qDebug() << "Renaming " << stderrPathTemp << " to " << stderrPath;
|
||||
if (!QFile::rename(stderrPathTemp, stderrPath)) {
|
||||
qDebug() << "Failed to rename " << stderrFilenameTemp;
|
||||
stderrPath = stderrPathTemp;
|
||||
stderrFilename = stderrFilenameTemp;
|
||||
}
|
||||
|
||||
qDebug() << "Child stdout being written to: " << stdoutFilename;
|
||||
qDebug() << "Child stderr being written to: " << stderrFilename;
|
||||
}
|
||||
|
||||
qDebug() << "Renaming " << stderrPathTemp << " to " << stderrPath;
|
||||
if (!QFile::rename(stderrPathTemp, stderrPath)) {
|
||||
qDebug() << "Failed to rename " << stderrFilenameTemp;
|
||||
stderrPath = stderrPathTemp;
|
||||
stderrFilename = stderrFilenameTemp;
|
||||
}
|
||||
|
||||
qDebug() << "Child stdout being written to: " << stdoutFilename;
|
||||
qDebug() << "Child stderr being written to: " << stderrFilename;
|
||||
|
||||
if (assignmentClient->processId() > 0) {
|
||||
auto pid = assignmentClient->processId();
|
||||
// make sure we hear that this process has finished when it does
|
||||
|
@ -212,6 +225,7 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
this, [this, pid]() { childProcessFinished(pid); });
|
||||
|
||||
qDebug() << "Spawned a child client with PID" << assignmentClient->processId();
|
||||
|
||||
_childProcesses.insert(assignmentClient->processId(), { assignmentClient, stdoutPath, stderrPath });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks,
|
||||
const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType,
|
||||
QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname,
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QDir logDirectory);
|
||||
quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory);
|
||||
~AssignmentClientMonitor();
|
||||
|
||||
void stopChildProcesses();
|
||||
|
@ -73,6 +73,8 @@ private:
|
|||
quint16 _assignmentServerPort;
|
||||
|
||||
QMap<qint64, ACProcess> _childProcesses;
|
||||
|
||||
bool _wantsChildFileLogging { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentClientMonitor_h
|
||||
|
|
|
@ -181,7 +181,7 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer<ReceivedMessage> me
|
|||
AssetMappingOperationType operationType;
|
||||
message->readPrimitive(&operationType);
|
||||
|
||||
auto replyPacket = NLPacket::create(PacketType::AssetMappingOperationReply);
|
||||
auto replyPacket = NLPacketList::create(PacketType::AssetMappingOperationReply, QByteArray(), true, true);
|
||||
replyPacket->writePrimitive(messageID);
|
||||
|
||||
switch (operationType) {
|
||||
|
@ -189,6 +189,16 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer<ReceivedMessage> me
|
|||
handleGetMappingOperation(*message, senderNode, *replyPacket);
|
||||
break;
|
||||
}
|
||||
case AssetMappingOperationType::GetAll: {
|
||||
replyPacket->writePrimitive(AssetServerError::NoError);
|
||||
auto count = _fileMappings.size();
|
||||
replyPacket->writePrimitive(count);
|
||||
for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) {
|
||||
replyPacket->writeString(it.key());
|
||||
replyPacket->write(QByteArray::fromHex(it.value().toString().toLocal8Bit()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AssetMappingOperationType::Set: {
|
||||
handleSetMappingOperation(*message, senderNode, *replyPacket);
|
||||
break;
|
||||
|
@ -200,10 +210,10 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer<ReceivedMessage> me
|
|||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendPacket(std::move(replyPacket), *senderNode);
|
||||
nodeList->sendPacketList(std::move(replyPacket), *senderNode);
|
||||
}
|
||||
|
||||
void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket) {
|
||||
void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
||||
QString assetPath = message.readString();
|
||||
|
||||
auto it = _fileMappings.find(assetPath);
|
||||
|
@ -219,7 +229,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode
|
|||
}
|
||||
}
|
||||
|
||||
void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket) {
|
||||
void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
||||
if (senderNode->getCanRez()) {
|
||||
QString assetPath = message.readString();
|
||||
auto assetHash = message.read(SHA256_HASH_LENGTH).toHex();
|
||||
|
@ -234,7 +244,7 @@ void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNode
|
|||
}
|
||||
}
|
||||
|
||||
void AssetServer::handleDeleteMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket) {
|
||||
void AssetServer::handleDeleteMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) {
|
||||
if (senderNode->getCanRez()) {
|
||||
QString assetPath = message.readString();
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ private slots:
|
|||
private:
|
||||
using Mappings = QVariantHash;
|
||||
|
||||
void handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket);
|
||||
void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket);
|
||||
void handleDeleteMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacket& replyPacket);
|
||||
void handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
||||
void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
||||
void handleDeleteMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket);
|
||||
|
||||
// Mapping file operations must be called from main assignment thread only
|
||||
void loadMappingsFromFile();
|
||||
|
@ -63,6 +63,7 @@ private:
|
|||
void performMappingMigration();
|
||||
|
||||
Mappings _fileMappings;
|
||||
|
||||
QDir _resourcesDirectory;
|
||||
QDir _filesDirectory;
|
||||
QThreadPool _taskPool;
|
||||
|
|
|
@ -89,6 +89,9 @@ var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
|
|||
var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode";
|
||||
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
|
||||
|
||||
|
||||
// marketplace info, etc. not quite ready yet.
|
||||
var SHOULD_SHOW_PROPERTY_MENU = false;
|
||||
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
|
||||
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."
|
||||
|
||||
|
@ -1022,6 +1025,9 @@ function mouseClickEvent(event) {
|
|||
} else if (event.isRightButton) {
|
||||
var result = findClickedEntity(event);
|
||||
if (result) {
|
||||
if (SHOULD_SHOW_PROPERTY_MENU !== true) {
|
||||
return;
|
||||
}
|
||||
var properties = Entities.getEntityProperties(result.entityID);
|
||||
if (properties.marketplaceID) {
|
||||
propertyMenu.marketplaceID = properties.marketplaceID;
|
||||
|
@ -1895,9 +1901,11 @@ PopupMenu = function() {
|
|||
return this;
|
||||
};
|
||||
|
||||
|
||||
var propertyMenu = PopupMenu();
|
||||
|
||||
propertyMenu.onSelectMenuItem = function(name) {
|
||||
|
||||
if (propertyMenu.marketplaceID) {
|
||||
showMarketplace(propertyMenu.marketplaceID);
|
||||
}
|
||||
|
|
|
@ -104,8 +104,9 @@
|
|||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 2,
|
||||
exponent: 0.3,
|
||||
intensity: 1,
|
||||
falloffRadius:0.9,
|
||||
exponent: 0.5,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME,
|
||||
position: lightTransform.p,
|
||||
|
@ -128,6 +129,8 @@
|
|||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
intensity:1.0,
|
||||
falloffRadius:0.3,
|
||||
lifetime: LIFETIME,
|
||||
cutoff: 90, // in degrees
|
||||
position: glowLightTransform.p,
|
||||
|
|
|
@ -13,51 +13,61 @@ Script.include("../../libraries/utils.js");
|
|||
|
||||
var scriptURL = Script.resolvePath('pingPongGun.js');
|
||||
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj';
|
||||
var MODEL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.fbx'
|
||||
var COLLISION_HULL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.obj';
|
||||
var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav';
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var pingPongGun = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: scriptURL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.21,
|
||||
z: 0.47
|
||||
type: "Model",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: scriptURL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.125,
|
||||
y: 0.3875,
|
||||
z: 0.9931
|
||||
},
|
||||
dynamic: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
},
|
||||
dynamic: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
},
|
||||
wearable:{joints:{RightHand:[{x:0.1177130937576294,
|
||||
y:0.12922893464565277,
|
||||
z:0.08307232707738876},
|
||||
{x:0.4934672713279724,
|
||||
y:0.3605862259864807,
|
||||
z:0.6394805908203125,
|
||||
w:-0.4664038419723511}],
|
||||
LeftHand:[{x:0.09151676297187805,
|
||||
y:0.13639454543590546,
|
||||
z:0.09354984760284424},
|
||||
{x:-0.19628101587295532,
|
||||
y:0.6418180465698242,
|
||||
z:0.2830369472503662,
|
||||
w:0.6851521730422974}]}}
|
||||
})
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.1177130937576294,
|
||||
y: 0.12922893464565277,
|
||||
z: 0.08307232707738876
|
||||
}, {
|
||||
x: 0.4934672713279724,
|
||||
y: 0.3605862259864807,
|
||||
z: 0.6394805908203125,
|
||||
w: -0.4664038419723511
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: 0.09151676297187805,
|
||||
y: 0.13639454543590546,
|
||||
z: 0.09354984760284424
|
||||
}, {
|
||||
x: -0.19628101587295532,
|
||||
y: 0.6418180465698242,
|
||||
z: 0.2830369472503662,
|
||||
w: 0.6851521730422974
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function cleanUp() {
|
||||
Entities.deleteEntity(pingPongGun);
|
||||
Entities.deleteEntity(pingPongGun);
|
||||
}
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
//if the trigger value goes below this value, reload the gun.
|
||||
var RELOAD_THRESHOLD = 0.95;
|
||||
var GUN_TIP_FWD_OFFSET =-0.35;
|
||||
var GUN_TIP_UP_OFFSET = 0.040;
|
||||
var GUN_TIP_FWD_OFFSET = -0.35;
|
||||
var GUN_TIP_UP_OFFSET = 0.12;
|
||||
var GUN_FORCE = 9;
|
||||
var BALL_RESTITUTION = 0.6;
|
||||
var BALL_LINEAR_DAMPING = 0.4;
|
||||
|
@ -45,12 +45,12 @@
|
|||
green: 255,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
|
||||
|
||||
|
||||
PingPongGun.prototype = {
|
||||
hand: null,
|
||||
|
@ -98,8 +98,8 @@
|
|||
var properties = {
|
||||
// type: 'Model',
|
||||
// modelURL:PING_PONG_BALL_URL,
|
||||
shapeType:'sphere',
|
||||
type:'Sphere',
|
||||
shapeType: 'sphere',
|
||||
type: 'Sphere',
|
||||
color: BALL_COLOR,
|
||||
dimensions: BALL_DIMENSIONS,
|
||||
damping: BALL_LINEAR_DAMPING,
|
||||
|
@ -125,7 +125,7 @@
|
|||
|
||||
Audio.playSound(this.SHOOTING_SOUND, audioProperties);
|
||||
},
|
||||
|
||||
|
||||
getGunTipPosition: function(properties) {
|
||||
//the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
|
||||
var frontVector = Quat.getFront(properties.rotation);
|
||||
|
@ -148,4 +148,4 @@
|
|||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new PingPongGun();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// fireworksLaunchButtonEntityScript.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 1 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
Fireworks = function() {
|
||||
_this = this;
|
||||
};
|
||||
|
||||
Fireworks.prototype = {
|
||||
|
||||
preload: function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Fireworks();
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// fireworksLaunchButtonSpawner.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 1 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
var orientation = Camera.getOrientation();
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
|
||||
|
||||
var SCRIPT_URL = Script.resolvePath("fireworksLaunchButtonEntityScript.js");
|
||||
var MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/Launch-Button.fbx";
|
||||
var launchButton = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "hifi-launch-button",
|
||||
modelURL: MODEL_URL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.98,
|
||||
y: 1.16,
|
||||
z: 0.98
|
||||
},
|
||||
script: SCRIPT_URL,
|
||||
})
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(launchButton);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// fireworksLaunchButtonEntityScript.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 2 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
Fireworks = function() {
|
||||
_this = this;
|
||||
_this.launchSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/missle+launch.wav");
|
||||
};
|
||||
|
||||
Fireworks.prototype = {
|
||||
|
||||
startNearTrigger: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
startFarTrigger: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
clickReleaseOnEntity: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
|
||||
|
||||
shootFirework: function(launchPosition) {
|
||||
Audio.playSound(_this.launchSound, {
|
||||
position: launchPosition,
|
||||
volume: 0.5
|
||||
});
|
||||
|
||||
|
||||
var smoke = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
position: _this.position,
|
||||
velocity: {x: 0, y: 3, z: 0},
|
||||
lifespan: 10,
|
||||
lifetime: 20,
|
||||
isEmitting: true,
|
||||
name: "Smoke Trail",
|
||||
maxParticles: 3000,
|
||||
emitRate: 80,
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0,
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthStart: -3.14,
|
||||
azimuthFinish: 3.14,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0.01,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0.01,
|
||||
y: 0,
|
||||
z: 0.01
|
||||
},
|
||||
radiusSpread: 0.03,
|
||||
particleRadius: 0.3,
|
||||
radiusStart: 0.06,
|
||||
radiusFinish: 0.9,
|
||||
alpha: 0.1,
|
||||
alphaSpread: 0,
|
||||
alphaStart: 0.7,
|
||||
alphaFinish: 0,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
emitterShouldTrail: true,
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
_this.position = Entities.getEntityProperties(_this.entityID, "position").position;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Fireworks();
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// fireworksLaunchButtonSpawner.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 2 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
var orientation = Camera.getOrientation();
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
|
||||
|
||||
var SCRIPT_URL = Script.resolvePath("fireworksLaunchButtonEntityScript.js");
|
||||
var MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/Launch-Button.fbx";
|
||||
var launchButton = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "hifi-launch-button",
|
||||
modelURL: MODEL_URL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.98,
|
||||
y: 1.16,
|
||||
z: 0.98
|
||||
},
|
||||
script: SCRIPT_URL,
|
||||
})
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(launchButton);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// fireworksLaunchButtonEntityScript.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 3 entity script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
(function() {
|
||||
Script.include("../../libraries/utils.js");
|
||||
var _this;
|
||||
Fireworks = function() {
|
||||
_this = this;
|
||||
_this.launchSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/missle+launch.wav");
|
||||
_this.explosionSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/fireworksExplosion.wav");
|
||||
_this.TIME_TO_EXPLODE = 3000;
|
||||
};
|
||||
|
||||
Fireworks.prototype = {
|
||||
|
||||
startNearTrigger: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
startFarTrigger: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
clickReleaseOnEntity: function() {
|
||||
_this.shootFirework(_this.position);
|
||||
},
|
||||
|
||||
|
||||
|
||||
shootFirework: function(launchPosition) {
|
||||
Audio.playSound(_this.launchSound, {
|
||||
position: launchPosition,
|
||||
volume: 0.5
|
||||
});
|
||||
|
||||
|
||||
var smoke = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
position: _this.position,
|
||||
velocity: {x: 0, y: 3, z: 0},
|
||||
linearDamping: 0,
|
||||
lifespan: 10,
|
||||
lifetime: 20,
|
||||
isEmitting: true,
|
||||
name: "Smoke Trail",
|
||||
maxParticles: 3000,
|
||||
emitRate: 80,
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0,
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthStart: -3.14,
|
||||
azimuthFinish: 3.14,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0.01,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0.01,
|
||||
y: 0,
|
||||
z: 0.01
|
||||
},
|
||||
radiusSpread: 0.03,
|
||||
particleRadius: 0.3,
|
||||
radiusStart: 0.06,
|
||||
radiusFinish: 0.9,
|
||||
alpha: 0.1,
|
||||
alphaSpread: 0,
|
||||
alphaStart: 0.7,
|
||||
alphaFinish: 0,
|
||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
emitterShouldTrail: true,
|
||||
});
|
||||
|
||||
Script.setTimeout(function() {
|
||||
var explodePosition = Entities.getEntityProperties(smoke, "position").position;
|
||||
_this.explodeFirework(explodePosition);
|
||||
}, _this.TIME_TO_EXPLODE);
|
||||
|
||||
},
|
||||
|
||||
explodeFirework: function(explodePosition) {
|
||||
Audio.playSound(_this.explosionSound, {
|
||||
position: explodePosition
|
||||
});
|
||||
var firework = Entities.addEntity({
|
||||
name: "fireworks emitter",
|
||||
position: explodePosition,
|
||||
type: "ParticleEffect",
|
||||
colorStart: hslToRgb({
|
||||
h: Math.random(),
|
||||
s: 0.5,
|
||||
l: 0.7
|
||||
}),
|
||||
color: hslToRgb({
|
||||
h: Math.random(),
|
||||
s: 0.5,
|
||||
l: 0.5
|
||||
}),
|
||||
colorFinish: hslToRgb({
|
||||
h: Math.random(),
|
||||
s: 0.5,
|
||||
l: 0.7
|
||||
}),
|
||||
maxParticles: 10000,
|
||||
lifetime: 20,
|
||||
lifespan: randFloat(1.5, 3),
|
||||
emitRate: randInt(500, 5000),
|
||||
emitSpeed: randFloat(0.5, 2),
|
||||
speedSpread: 0.2,
|
||||
emitOrientation: Quat.fromPitchYawRollDegrees(randInt(0, 360), randInt(0, 360), randInt(0, 360)),
|
||||
polarStart: 1,
|
||||
polarFinish: randFloat(1.2, 3),
|
||||
azimuthStart: -Math.PI,
|
||||
azimuthFinish: Math.PI,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: randFloat(-1, -0.2),
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: Math.random(),
|
||||
y: 0,
|
||||
z: Math.random()
|
||||
},
|
||||
particleRadius: randFloat(0.001, 0.1),
|
||||
radiusSpread: Math.random() * 0.1,
|
||||
radiusStart: randFloat(0.001, 0.1),
|
||||
radiusFinish: randFloat(0.001, 0.1),
|
||||
alpha: randFloat(0.8, 1.0),
|
||||
alphaSpread: randFloat(0.1, 0.2),
|
||||
alphaStart: randFloat(0.7, 1.0),
|
||||
alphaFinish: randFloat(0.7, 1.0),
|
||||
textures: "http://ericrius1.github.io/PlatosCave/assets/star.png",
|
||||
});
|
||||
|
||||
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(firework, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, randInt(500, 1000));
|
||||
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
_this.position = Entities.getEntityProperties(_this.entityID, "position").position;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Fireworks();
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// fireworksLaunchButtonSpawner.js
|
||||
//
|
||||
// Created by Eric Levin on 3/7/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This is the chapter 3 interface script of the fireworks tutorial (https://docs.highfidelity.com/docs/fireworks-scripting-tutorial)
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
|
||||
var orientation = Camera.getOrientation();
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(orientation)));
|
||||
|
||||
var SCRIPT_URL = Script.resolvePath("fireworksLaunchButtonEntityScript.js");
|
||||
var MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/Launch-Button.fbx";
|
||||
var launchButton = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "hifi-launch-button",
|
||||
modelURL: MODEL_URL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.98,
|
||||
y: 1.16,
|
||||
z: 0.98
|
||||
},
|
||||
script: SCRIPT_URL,
|
||||
})
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(launchButton);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -50,7 +50,23 @@ Windows.Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle message traffic from the script that launched us to the loaded QML
|
||||
function fromScript(message) {
|
||||
if (root.dynamicContent && root.dynamicContent.fromScript) {
|
||||
root.dynamicContent.fromScript(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle message traffic from our loaded QML to the script that launched us
|
||||
signal sendToScript(var message);
|
||||
onDynamicContentChanged: {
|
||||
if (dynamicContent && dynamicContent.sendToScript) {
|
||||
dynamicContent.sendToScript.connect(sendToScript);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
|
|
|
@ -58,21 +58,12 @@ TreeView {
|
|||
text: styleData.isExpanded ? hifi.glyphs.caratDn : hifi.glyphs.caratR
|
||||
size: hifi.fontSizes.carat
|
||||
color: colorScheme == hifi.colorSchemes.light
|
||||
? (styleData.selected
|
||||
? hifi.colors.black
|
||||
: (iconArea.pressed ? hifi.colors.white : hifi.colors.baseGrayHighlight))
|
||||
: (styleData.selected
|
||||
? hifi.colors.black
|
||||
: (iconArea.pressed ? hifi.colors.white : hifi.colors.lightGrayText))
|
||||
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||
anchors {
|
||||
left: parent ? parent.left : undefined
|
||||
leftMargin: hifi.dimensions.tablePadding / 2
|
||||
}
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
}
|
||||
|
||||
handle: Item {
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
#include <recording/Recorder.h>
|
||||
#include <QmlWebWindowClass.h>
|
||||
#include <Preferences.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
||||
#include "AnimDebugDraw.h"
|
||||
#include "AudioClient.h"
|
||||
|
@ -376,6 +377,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
DependencyManager::set<EntityTreeRenderer>(true, qApp, qApp);
|
||||
DependencyManager::set<CompositorHelper>();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -770,7 +772,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
}
|
||||
|
||||
if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
|
||||
auto reticlePos = _compositor.getReticlePosition();
|
||||
auto reticlePos = getApplicationCompositor().getReticlePosition();
|
||||
QPoint globalPos(reticlePos.x, reticlePos.y);
|
||||
|
||||
// FIXME - it would be nice if this was self contained in the _compositor or Reticle class
|
||||
|
@ -793,14 +795,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
} else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {
|
||||
cycleCamera();
|
||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||
auto reticlePosition = _compositor.getReticlePosition();
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||
auto oldPos = _compositor.getReticlePosition();
|
||||
_compositor.setReticlePosition({ oldPos.x + state, oldPos.y });
|
||||
auto oldPos = getApplicationCompositor().getReticlePosition();
|
||||
getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });
|
||||
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
||||
auto oldPos = _compositor.getReticlePosition();
|
||||
_compositor.setReticlePosition({ oldPos.x, oldPos.y + state });
|
||||
auto oldPos = getApplicationCompositor().getReticlePosition();
|
||||
getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state });
|
||||
} else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) {
|
||||
toggleOverlays();
|
||||
}
|
||||
|
@ -1253,17 +1255,17 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
||||
rootContext->setContextProperty("Reticle", _compositor.getReticleInterface());
|
||||
rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
|
||||
|
||||
rootContext->setContextProperty("ApplicationCompositor", &_compositor);
|
||||
rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
|
||||
QPointF result = pt;
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
if (displayPlugin->isHmd()) {
|
||||
_compositor.handleRealMouseMoveEvent(false);
|
||||
auto resultVec = _compositor.getReticlePosition();
|
||||
getApplicationCompositor().handleRealMouseMoveEvent(false);
|
||||
auto resultVec = getApplicationCompositor().getReticlePosition();
|
||||
result = QPointF(resultVec.x, resultVec.y);
|
||||
}
|
||||
return result.toPoint();
|
||||
|
@ -1287,7 +1289,15 @@ void Application::initializeUi() {
|
|||
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
|
||||
}
|
||||
}
|
||||
Menu::setInstance();
|
||||
updateInputModes();
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] {
|
||||
if (isHMDMode()) {
|
||||
showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
|
@ -1381,9 +1391,10 @@ void Application::paintGL() {
|
|||
QSize size = getDeviceSize();
|
||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer();
|
||||
|
||||
|
||||
auto overlayTexture = _applicationOverlay.acquireOverlay();
|
||||
if (overlayTexture) {
|
||||
displayPlugin->submitOverlayTexture(overlayTexture);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 boomOffset;
|
||||
|
@ -1484,6 +1495,8 @@ void Application::paintGL() {
|
|||
myAvatar->endCapture();
|
||||
}
|
||||
|
||||
getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform());
|
||||
|
||||
// Primary rendering pass
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
const QSize size = framebufferCache->getFrameBufferSize();
|
||||
|
@ -1536,7 +1549,7 @@ void Application::paintGL() {
|
|||
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
|
||||
eyeOffsets[eye] = eyeOffsetTransform;
|
||||
|
||||
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
|
||||
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform));
|
||||
|
||||
eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection);
|
||||
});
|
||||
|
@ -1550,44 +1563,12 @@ void Application::paintGL() {
|
|||
renderArgs._context->enableStereo(false);
|
||||
}
|
||||
|
||||
// Overlay Composition, needs to occur after screen space effects have completed
|
||||
// FIXME migrate composition into the display plugins
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/compositor");
|
||||
PerformanceTimer perfTimer("compositor");
|
||||
|
||||
auto primaryFbo = finalFramebuffer;
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo));
|
||||
if (displayPlugin->isStereo()) {
|
||||
QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height()));
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
for_each_eye([&](Eye eye) {
|
||||
renderArgs._viewport = toGlm(currentViewport);
|
||||
if (displayPlugin->isHmd()) {
|
||||
_compositor.displayOverlayTextureHmd(&renderArgs, eye);
|
||||
} else {
|
||||
_compositor.displayOverlayTexture(&renderArgs);
|
||||
}
|
||||
}, [&] {
|
||||
currentViewport.moveLeft(currentViewport.width());
|
||||
});
|
||||
} else {
|
||||
glViewport(0, 0, size.width(), size.height());
|
||||
_compositor.displayOverlayTexture(&renderArgs);
|
||||
}
|
||||
}
|
||||
|
||||
// deliver final composited scene to the display plugin
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
|
||||
auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0);
|
||||
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
|
||||
Q_ASSERT(0 != finalTexture);
|
||||
|
||||
auto finalTexture = finalFramebuffer->getRenderBuffer(0);
|
||||
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
|
||||
_lockedFramebufferMap[finalTexture] = finalFramebuffer;
|
||||
|
||||
|
@ -1595,10 +1576,9 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
|
||||
PerformanceTimer perfTimer("pluginSubmitScene");
|
||||
displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
|
||||
displayPlugin->submitSceneTexture(_frameCount, finalTexture);
|
||||
}
|
||||
Q_ASSERT(isCurrentContext(_offscreenContext->getContext()));
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1795,7 +1775,7 @@ bool Application::event(QEvent* event) {
|
|||
bool Application::eventFilter(QObject* object, QEvent* event) {
|
||||
|
||||
if (event->type() == QEvent::Leave) {
|
||||
_compositor.handleLeaveEvent();
|
||||
getApplicationCompositor().handleLeaveEvent();
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
|
@ -2083,7 +2063,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto reticlePosition = _compositor.getReticlePosition();
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y)));
|
||||
}
|
||||
|
||||
|
@ -2172,13 +2152,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement
|
||||
void Application::fakeMouseEvent(QMouseEvent* event) {
|
||||
_fakedMouseEvent = true;
|
||||
sendEvent(_glWidget, event);
|
||||
_fakedMouseEvent = false;
|
||||
}
|
||||
|
||||
void Application::mouseMoveEvent(QMouseEvent* event) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
|
@ -2188,17 +2161,16 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
maybeToggleMenuVisible(event);
|
||||
|
||||
auto& compositor = getApplicationCompositor();
|
||||
// if this is a real mouse event, and we're in HMD mode, then we should use it to move the
|
||||
// compositor reticle
|
||||
if (!_fakedMouseEvent) {
|
||||
// handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
|
||||
if (_compositor.handleRealMouseMoveEvent()) {
|
||||
return; // bail
|
||||
}
|
||||
// handleRealMouseMoveEvent() will return true, if we shouldn't process the event further
|
||||
if (!compositor.fakeEventActive() && compositor.handleRealMouseMoveEvent()) {
|
||||
return; // bail
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||
auto eventPosition = compositor.getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
auto button = event->button();
|
||||
auto buttons = event->buttons();
|
||||
|
@ -2240,7 +2212,7 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
// will consume all keyboard events.
|
||||
offscreenUi->unfocusWindows();
|
||||
|
||||
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||
auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
|
@ -2286,7 +2258,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) {
|
|||
void Application::mouseReleaseEvent(QMouseEvent* event) {
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto eventPosition = _compositor.getMouseEventPosition(event);
|
||||
auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
|
@ -2569,7 +2541,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
|||
}
|
||||
|
||||
ivec2 Application::getMouse() {
|
||||
auto reticlePosition = _compositor.getReticlePosition();
|
||||
auto reticlePosition = getApplicationCompositor().getReticlePosition();
|
||||
|
||||
// in the HMD, the reticlePosition is the mouse position
|
||||
if (isHMDMode()) {
|
||||
|
@ -4208,7 +4180,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface());
|
||||
scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) const {
|
||||
|
@ -4709,7 +4681,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
|
|||
auto action = menu->addActionToQMenuAndActionHash(parent,
|
||||
name, 0, qApp,
|
||||
SLOT(updateDisplayMode()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu);
|
||||
QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu);
|
||||
|
||||
action->setCheckable(true);
|
||||
action->setChecked(active);
|
||||
|
@ -4814,6 +4786,7 @@ void Application::updateDisplayMode() {
|
|||
|
||||
oldDisplayPlugin = _displayPlugin;
|
||||
_displayPlugin = newDisplayPlugin;
|
||||
getApplicationCompositor().setDisplayPlugin(_displayPlugin);
|
||||
|
||||
// If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed
|
||||
// Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1
|
||||
|
@ -5031,3 +5004,7 @@ void Application::showDesktop() {
|
|||
_overlayConductor.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
CompositorHelper& Application::getApplicationCompositor() const {
|
||||
return *DependencyManager::get<CompositorHelper>();
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "render/Engine.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "scripting/DialogsManagerScriptingInterface.h"
|
||||
#include "ui/ApplicationCompositor.h"
|
||||
#include "ui/ApplicationOverlay.h"
|
||||
#include "ui/AudioStatsDialog.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
|
@ -67,6 +66,7 @@ class GLCanvas;
|
|||
class FaceTracker;
|
||||
class MainWindow;
|
||||
class AssetUpload;
|
||||
class CompositorHelper;
|
||||
|
||||
namespace controller {
|
||||
class StateController;
|
||||
|
@ -125,6 +125,7 @@ public:
|
|||
bool isThrottleRendering() const;
|
||||
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
const Camera* getCamera() const { return &_myCamera; }
|
||||
// Represents the current view frustum of the avatar.
|
||||
ViewFrustum* getViewFrustum();
|
||||
const ViewFrustum* getViewFrustum() const;
|
||||
|
@ -149,8 +150,7 @@ public:
|
|||
|
||||
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
|
||||
const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; }
|
||||
ApplicationCompositor& getApplicationCompositor() { return _compositor; }
|
||||
const ApplicationCompositor& getApplicationCompositor() const { return _compositor; }
|
||||
CompositorHelper& getApplicationCompositor() const;
|
||||
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
|
||||
|
@ -221,8 +221,6 @@ public:
|
|||
|
||||
float getAverageSimsPerSecond();
|
||||
|
||||
void fakeMouseEvent(QMouseEvent* event);
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
|
@ -390,7 +388,7 @@ private:
|
|||
InputPluginList _activeInputPlugins;
|
||||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
QMap<uint32_t, gpu::FramebufferPointer> _lockedFramebufferMap;
|
||||
QMap<gpu::TexturePointer, gpu::FramebufferPointer> _lockedFramebufferMap;
|
||||
|
||||
MainWindow* _window;
|
||||
|
||||
|
@ -484,7 +482,6 @@ private:
|
|||
|
||||
Overlays _overlays;
|
||||
ApplicationOverlay _applicationOverlay;
|
||||
ApplicationCompositor _compositor;
|
||||
OverlayConductor _overlayConductor;
|
||||
|
||||
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <VrMenu.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <MenuItemProperties.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
|
@ -45,11 +46,15 @@
|
|||
|
||||
#include "Menu.h"
|
||||
|
||||
// Fixme make static member of Menu
|
||||
static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu";
|
||||
|
||||
void Menu::setInstance() {
|
||||
globalInstance<Menu>(MENU_PROPERTY_NAME);
|
||||
}
|
||||
|
||||
Menu* Menu::getInstance() {
|
||||
static Menu* instance = globalInstance<Menu>(MENU_PROPERTY_NAME);
|
||||
return instance;
|
||||
return static_cast<Menu*>(ui::Menu::getInstance());
|
||||
}
|
||||
|
||||
Menu::Menu() {
|
||||
|
@ -621,418 +626,6 @@ Menu::Menu() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Menu::toggleAdvancedMenus() {
|
||||
setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced"));
|
||||
}
|
||||
|
||||
void Menu::toggleDeveloperMenus() {
|
||||
setGroupingIsVisible("Developer", !getGroupingIsVisible("Developer"));
|
||||
}
|
||||
|
||||
void Menu::loadSettings() {
|
||||
scanMenuBar(&Menu::loadAction);
|
||||
}
|
||||
|
||||
void Menu::saveSettings() {
|
||||
scanMenuBar(&Menu::saveAction);
|
||||
}
|
||||
|
||||
void Menu::loadAction(Settings& settings, QAction& action) {
|
||||
if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) {
|
||||
action.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::saveAction(Settings& settings, QAction& action) {
|
||||
settings.setValue(action.text(), action.isChecked());
|
||||
}
|
||||
|
||||
void Menu::scanMenuBar(settingsAction modifySetting) {
|
||||
Settings settings;
|
||||
foreach (QMenu* menu, findChildren<QMenu*>()) {
|
||||
scanMenu(*menu, modifySetting, settings);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings) {
|
||||
settings.beginGroup(menu.title());
|
||||
foreach (QAction* action, menu.actions()) {
|
||||
if (action->menu()) {
|
||||
scanMenu(*action->menu(), modifySetting, settings);
|
||||
} else if (action->isCheckable()) {
|
||||
modifySetting(settings, *action);
|
||||
}
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation, const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
QAction* separator;
|
||||
QAction* separatorText;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
if (actionBefore) {
|
||||
separator = new QAction("",destinationMenu);
|
||||
destinationMenu->insertAction(actionBefore, separator);
|
||||
separator->setSeparator(true);
|
||||
|
||||
separatorText = new QAction(actionName,destinationMenu);
|
||||
separatorText->setEnabled(false);
|
||||
destinationMenu->insertAction(actionBefore, separatorText);
|
||||
|
||||
} else {
|
||||
separator = destinationMenu->addSeparator();
|
||||
separatorText = destinationMenu->addAction(actionName);
|
||||
separatorText->setEnabled(false);
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << separator;
|
||||
_groupingActions[grouping] << separatorText;
|
||||
bool isVisible = getGroupingIsVisible(grouping);
|
||||
separator->setVisible(isVisible);
|
||||
separatorText->setVisible(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const QObject* receiver,
|
||||
const char* member,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* action = NULL;
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
|
||||
if (!actionBefore) {
|
||||
if (receiver && member) {
|
||||
action = destinationMenu->addAction(actionName, receiver, member, shortcut);
|
||||
} else {
|
||||
action = destinationMenu->addAction(actionName);
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
} else {
|
||||
action = new QAction(actionName, destinationMenu);
|
||||
action->setShortcut(shortcut);
|
||||
destinationMenu->insertAction(actionBefore, action);
|
||||
|
||||
if (receiver && member) {
|
||||
connect(action, SIGNAL(triggered()), receiver, member);
|
||||
}
|
||||
}
|
||||
action->setMenuRole(role);
|
||||
|
||||
_actionHash.insert(actionName, action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
|
||||
if (!actionName.isEmpty()) {
|
||||
action->setText(actionName);
|
||||
}
|
||||
|
||||
if (shortcut != 0) {
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
|
||||
if (role != QAction::NoRole) {
|
||||
action->setMenuRole(role);
|
||||
}
|
||||
|
||||
if (!actionBefore) {
|
||||
destinationMenu->addAction(action);
|
||||
} else {
|
||||
destinationMenu->insertAction(actionBefore, action);
|
||||
}
|
||||
|
||||
_actionHash.insert(action->text(), action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const bool checked,
|
||||
const QObject* receiver,
|
||||
const char* member,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
|
||||
QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member,
|
||||
QAction::NoRole, menuItemLocation);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(checked);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
|
||||
auto action = _actionHash.value(actionName);
|
||||
menu->removeAction(action);
|
||||
_actionHash.remove(actionName);
|
||||
for (auto& grouping : _groupingActions) {
|
||||
grouping.remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, menuOption),
|
||||
Q_ARG(bool, isChecked));
|
||||
return;
|
||||
}
|
||||
QAction* menu = _actionHash.value(menuOption);
|
||||
if (menu) {
|
||||
menu->setChecked(isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::isOptionChecked(const QString& menuOption) const {
|
||||
const QAction* menu = _actionHash.value(menuOption);
|
||||
if (menu) {
|
||||
return menu->isChecked();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::triggerOption(const QString& menuOption) {
|
||||
QAction* action = _actionHash.value(menuOption);
|
||||
if (action) {
|
||||
action->trigger();
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "NULL Action for menuOption '" << menuOption << "'";
|
||||
}
|
||||
}
|
||||
|
||||
QAction* Menu::getActionForOption(const QString& menuOption) {
|
||||
return _actionHash.value(menuOption);
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QList<QAction*> menuActions;
|
||||
if (menu) {
|
||||
menuActions = menu->actions();
|
||||
} else {
|
||||
menuActions = actions();
|
||||
}
|
||||
|
||||
foreach (QAction* menuAction, menuActions) {
|
||||
QString actionText = menuAction->text();
|
||||
if (menuName == menuAction->text()) {
|
||||
return menuAction;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QAction* action = getActionFromName(menuName, menu);
|
||||
if (action) {
|
||||
return MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
parent = menu;
|
||||
finalMenuPart = menuTreePart.trimmed();
|
||||
menu = getSubMenuFromName(finalMenuPart, parent);
|
||||
if (!menu) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getMenu(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
int item = 0;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), parent);
|
||||
if (!menu) {
|
||||
break;
|
||||
}
|
||||
parent = menu;
|
||||
item++;
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
QAction* Menu::getMenuAction(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
QAction* action = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
action = getActionFromName(menuTreePart.trimmed(), parent);
|
||||
if (!action) {
|
||||
break;
|
||||
}
|
||||
parent = MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) {
|
||||
int position = 0;
|
||||
foreach(QAction* action, menu->actions()) {
|
||||
if (action->text() == searchMenuItem) {
|
||||
return position;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
return UNSPECIFIED_POSITION; // not found
|
||||
}
|
||||
|
||||
int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) {
|
||||
QList<QAction*> menuActions = menu->actions();
|
||||
if (requestedPosition > 1 && requestedPosition < menuActions.size()) {
|
||||
QAction* beforeRequested = menuActions[requestedPosition - 1];
|
||||
if (beforeRequested->isSeparator()) {
|
||||
requestedPosition--;
|
||||
}
|
||||
}
|
||||
return requestedPosition;
|
||||
}
|
||||
|
||||
bool Menu::_isSomeSubmenuShown = false;
|
||||
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* addTo = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), addTo);
|
||||
if (!menu) {
|
||||
if (!addTo) {
|
||||
menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed()));
|
||||
} else {
|
||||
menu = addTo->addMenu(menuTreePart.trimmed());
|
||||
}
|
||||
}
|
||||
addTo = menu;
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
auto action = getMenuAction(menuName);
|
||||
if (action) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
|
||||
// hook our show/hide for popup menus, so we can keep track of whether or not one
|
||||
// of our submenus is currently showing.
|
||||
connect(menu->_realMenu, &QMenu::aboutToShow, []() { _isSomeSubmenuShown = true; });
|
||||
connect(menu->_realMenu, &QMenu::aboutToHide, []() { _isSomeSubmenuShown = false; });
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void Menu::removeMenu(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
QString finalMenuPart;
|
||||
MenuWrapper* parent = getMenuParent(menuName, finalMenuPart);
|
||||
if (parent) {
|
||||
parent->removeAction(action);
|
||||
} else {
|
||||
QMenuBar::removeAction(action);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) {
|
||||
MenuWrapper* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
addDisabledActionAndSeparator(menuObj, separatorName);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::removeSeparator(const QString& menuName, const QString& separatorName) {
|
||||
MenuWrapper* menu = getMenu(menuName);
|
||||
bool separatorRemoved = false;
|
||||
if (menu) {
|
||||
int textAt = findPositionOfMenuItem(menu, separatorName);
|
||||
QList<QAction*> menuActions = menu->actions();
|
||||
QAction* separatorText = menuActions[textAt];
|
||||
if (textAt > 0 && textAt < menuActions.size()) {
|
||||
QAction* separatorLine = menuActions[textAt - 1];
|
||||
if (separatorLine) {
|
||||
if (separatorLine->isSeparator()) {
|
||||
menu->removeAction(separatorText);
|
||||
menu->removeAction(separatorLine);
|
||||
separatorRemoved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (separatorRemoved) {
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::addMenuItem(const MenuItemProperties& properties) {
|
||||
MenuWrapper* menuObj = getMenu(properties.menuName);
|
||||
if (menuObj) {
|
||||
|
@ -1075,126 +668,3 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
||||
MenuWrapper* menuObj = getMenu(menu);
|
||||
if (menuObj) {
|
||||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
if (menuItemAction) {
|
||||
return (getMenu(menu) != NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Menu::getGroupingIsVisible(const QString& grouping) {
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return true;
|
||||
}
|
||||
if (_groupingVisible.contains(grouping)) {
|
||||
return _groupingVisible[grouping];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) {
|
||||
// NOTE: Default grouping always visible
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return;
|
||||
}
|
||||
_groupingVisible[grouping] = isVisible;
|
||||
|
||||
for (auto action: _groupingActions[grouping]) {
|
||||
action->setVisible(isVisible);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) {
|
||||
auto menu = addMenu(groupName);
|
||||
|
||||
QActionGroup* actionGroup = new QActionGroup(menu);
|
||||
actionGroup->setExclusive(true);
|
||||
|
||||
auto menuScriptingInterface = MenuScriptingInterface::getInstance();
|
||||
for (auto action : actionList) {
|
||||
auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected,
|
||||
menuScriptingInterface,
|
||||
SLOT(menuItemTriggered()));
|
||||
actionGroup->addAction(item);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::removeActionGroup(const QString& groupName) {
|
||||
removeMenu(groupName);
|
||||
}
|
||||
|
||||
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addMenu(menu);
|
||||
});
|
||||
_backMap[menu] = this;
|
||||
}
|
||||
|
||||
QList<QAction*> MenuWrapper::actions() {
|
||||
return _realMenu->actions();
|
||||
}
|
||||
|
||||
MenuWrapper* MenuWrapper::addMenu(const QString& menuName) {
|
||||
return new MenuWrapper(_realMenu->addMenu(menuName));
|
||||
}
|
||||
|
||||
void MenuWrapper::setEnabled(bool enabled) {
|
||||
_realMenu->setEnabled(enabled);
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addSeparator() {
|
||||
return _realMenu->addSeparator();
|
||||
}
|
||||
|
||||
void MenuWrapper::addAction(QAction* action) {
|
||||
_realMenu->addAction(action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName) {
|
||||
QAction* action = _realMenu->addAction(menuName);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
|
||||
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
void MenuWrapper::removeAction(QAction* action) {
|
||||
_realMenu->removeAction(action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->removeAction(action);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuWrapper::insertAction(QAction* before, QAction* action) {
|
||||
_realMenu->insertAction(before, action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->insertAction(before, action);
|
||||
});
|
||||
}
|
||||
|
||||
QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;
|
||||
|
|
|
@ -9,144 +9,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <ui/Menu.h>
|
||||
|
||||
#ifndef hifi_Menu_h
|
||||
#define hifi_Menu_h
|
||||
|
||||
#include <QDir>
|
||||
#include <QMenuBar>
|
||||
#include <QHash>
|
||||
#include <QKeySequence>
|
||||
#include <QPointer>
|
||||
#include <QStandardPaths>
|
||||
class MenuItemProperties;
|
||||
|
||||
#include <MenuItemProperties.h>
|
||||
|
||||
#include "DiscoverabilityManager.h"
|
||||
|
||||
class Settings;
|
||||
|
||||
class MenuWrapper : public QObject {
|
||||
public:
|
||||
QList<QAction*> actions();
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void setEnabled(bool enabled = true);
|
||||
QAction* addSeparator();
|
||||
void addAction(QAction* action);
|
||||
|
||||
QAction* addAction(const QString& menuName);
|
||||
void insertAction(QAction* before, QAction* menuName);
|
||||
|
||||
QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0);
|
||||
void removeAction(QAction* action);
|
||||
|
||||
QAction* newAction() {
|
||||
return new QAction(_realMenu);
|
||||
}
|
||||
|
||||
private:
|
||||
MenuWrapper(QMenu* menu);
|
||||
|
||||
static MenuWrapper* fromMenu(QMenu* menu) {
|
||||
return _backMap[menu];
|
||||
}
|
||||
|
||||
QMenu* const _realMenu;
|
||||
static QHash<QMenu*, MenuWrapper*> _backMap;
|
||||
friend class Menu;
|
||||
};
|
||||
|
||||
class Menu : public QMenuBar {
|
||||
class Menu : public ui::Menu {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Menu();
|
||||
static void setInstance();
|
||||
static Menu* getInstance();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName = QString(),
|
||||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const bool checked = false,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
public slots:
|
||||
MenuWrapper* addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping = QString());
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString());
|
||||
void removeActionGroup(const QString& groupName);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
bool getGroupingIsVisible(const QString& grouping);
|
||||
void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible
|
||||
|
||||
void toggleDeveloperMenus();
|
||||
void toggleAdvancedMenus();
|
||||
|
||||
static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; }
|
||||
|
||||
private:
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
static void saveAction(Settings& settings, QAction& action);
|
||||
void scanMenuBar(settingsAction modifySetting);
|
||||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
||||
QAction* getMenuAction(const QString& menuName);
|
||||
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
|
||||
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
||||
QHash<QString, bool> _groupingVisible;
|
||||
QHash<QString, QSet<QAction*>> _groupingActions;
|
||||
|
||||
static bool _isSomeSubmenuShown;
|
||||
Menu();
|
||||
Q_INVOKABLE void addMenuItem(const MenuItemProperties& properties);
|
||||
};
|
||||
|
||||
namespace MenuOption {
|
||||
|
@ -302,3 +179,4 @@ namespace MenuOption {
|
|||
}
|
||||
|
||||
#endif // hifi_Menu_h
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
#include "ModelSelector.h"
|
||||
|
||||
static const QString AVATAR_HEAD_STRING = "Avatar Head Only";
|
||||
static const QString AVATAR_BODY_STRING = "Avatar Body Only";
|
||||
static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head";
|
||||
static const QString AVATAR_ATTACHEMENT_STRING = "Avatar Attachment";
|
||||
static const QString ENTITY_MODEL_STRING = "Entity Model";
|
||||
|
@ -35,8 +33,7 @@ ModelSelector::ModelSelector() {
|
|||
form->addRow("Model File:", _browseButton);
|
||||
|
||||
_modelType = new QComboBox(this);
|
||||
_modelType->addItem(AVATAR_HEAD_STRING);
|
||||
_modelType->addItem(AVATAR_BODY_STRING);
|
||||
|
||||
_modelType->addItem(AVATAR_HEAD_AND_BODY_STRING);
|
||||
_modelType->addItem(AVATAR_ATTACHEMENT_STRING);
|
||||
_modelType->addItem(ENTITY_MODEL_STRING);
|
||||
|
@ -55,11 +52,7 @@ QFileInfo ModelSelector::getFileInfo() const {
|
|||
FSTReader::ModelType ModelSelector::getModelType() const {
|
||||
QString text = _modelType->currentText();
|
||||
|
||||
if (text == AVATAR_HEAD_STRING) {
|
||||
return FSTReader::HEAD_MODEL;
|
||||
} else if (text == AVATAR_BODY_STRING) {
|
||||
return FSTReader::BODY_ONLY_MODEL;
|
||||
} else if (text == AVATAR_HEAD_AND_BODY_STRING) {
|
||||
if (text == AVATAR_HEAD_AND_BODY_STRING) {
|
||||
return FSTReader::HEAD_AND_BODY_MODEL;
|
||||
} else if (text == AVATAR_ATTACHEMENT_STRING) {
|
||||
return FSTReader::ATTACHMENT_MODEL;
|
||||
|
|
|
@ -170,7 +170,7 @@ bool PluginContainerProxy::makeRenderingContextCurrent() {
|
|||
return qApp->_offscreenContext->makeCurrent();
|
||||
}
|
||||
|
||||
void PluginContainerProxy::releaseSceneTexture(uint32_t texture) {
|
||||
void PluginContainerProxy::releaseSceneTexture(const gpu::TexturePointer& texture) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
auto& framebufferMap = qApp->_lockedFramebufferMap;
|
||||
Q_ASSERT(framebufferMap.contains(texture));
|
||||
|
@ -180,8 +180,8 @@ void PluginContainerProxy::releaseSceneTexture(uint32_t texture) {
|
|||
framebufferCache->releaseFramebuffer(framebufferPointer);
|
||||
}
|
||||
|
||||
void PluginContainerProxy::releaseOverlayTexture(uint32_t texture) {
|
||||
// FIXME implement present thread compositing
|
||||
void PluginContainerProxy::releaseOverlayTexture(const gpu::TexturePointer& texture) {
|
||||
qApp->_applicationOverlay.releaseOverlay(texture);
|
||||
}
|
||||
|
||||
/// settings interface
|
||||
|
|
|
@ -25,8 +25,8 @@ class PluginContainerProxy : public QObject, PluginContainer {
|
|||
virtual void showDisplayPluginsTools() override;
|
||||
virtual void requestReset() override;
|
||||
virtual bool makeRenderingContextCurrent() override;
|
||||
virtual void releaseSceneTexture(uint32_t texture) override;
|
||||
virtual void releaseOverlayTexture(uint32_t texture) override;
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override;
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override;
|
||||
virtual GLWidget* getPrimaryWidget() override;
|
||||
virtual QWindow* getPrimaryWindow() override;
|
||||
virtual QOpenGLContext* getPrimaryContext() override;
|
||||
|
|
|
@ -1918,13 +1918,26 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co
|
|||
|
||||
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
||||
const float CYLINDER_RADIUS = 0.3f;
|
||||
|
||||
// -z axis of currentBodyMatrix in world space.
|
||||
glm::vec3 forward = glm::normalize(glm::vec3(-currentBodyMatrix[0][2], -currentBodyMatrix[1][2], -currentBodyMatrix[2][2]));
|
||||
// x axis of currentBodyMatrix in world space.
|
||||
glm::vec3 right = glm::normalize(glm::vec3(currentBodyMatrix[0][0], currentBodyMatrix[1][0], currentBodyMatrix[2][0]));
|
||||
glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix);
|
||||
glm::vec3 radialOffset(offset.x, 0.0f, offset.z);
|
||||
float radialDistance = glm::length(radialOffset);
|
||||
|
||||
return radialDistance > CYLINDER_RADIUS;
|
||||
float forwardLeanAmount = glm::dot(forward, offset);
|
||||
float lateralLeanAmount = glm::dot(right, offset);
|
||||
|
||||
const float MAX_LATERAL_LEAN = 0.3f;
|
||||
const float MAX_FORWARD_LEAN = 0.15f;
|
||||
const float MAX_BACKWARD_LEAN = 0.1f;
|
||||
|
||||
if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) {
|
||||
return true;
|
||||
} else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN;
|
||||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include "display-plugins/DisplayPlugin.h"
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include "Application.h"
|
||||
|
||||
|
|
|
@ -86,10 +86,13 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
|
|||
|
||||
void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList,
|
||||
const QString& selected) {
|
||||
static const char* slot = SLOT(menuItemTriggered());
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup",
|
||||
Q_ARG(const QString&, groupName),
|
||||
Q_ARG(const QStringList&, actionList),
|
||||
Q_ARG(const QString&, selected));
|
||||
Q_ARG(const QString&, selected),
|
||||
Q_ARG(QObject*, this),
|
||||
Q_ARG(const char*, slot));
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::removeActionGroup(const QString& groupName) {
|
||||
|
|
|
@ -1,704 +0,0 @@
|
|||
//
|
||||
// ApplicationCompositor.cpp
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Benjamin Arnold on 5/27/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ApplicationCompositor.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "CursorManager.h"
|
||||
#include "Tooltip.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include <controllers/InputDevice.h>
|
||||
|
||||
|
||||
// Used to animate the magnification windows
|
||||
|
||||
static const quint64 MSECS_TO_USECS = 1000ULL;
|
||||
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||
|
||||
static const float reticleSize = TWO_PI / 100.0f;
|
||||
|
||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||
|
||||
static gpu::BufferPointer _hemiVertices;
|
||||
static gpu::BufferPointer _hemiIndices;
|
||||
static int _hemiIndexCount{ 0 };
|
||||
EntityItemID ApplicationCompositor::_noItemId;
|
||||
static QString _tooltipId;
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
glm::vec3 getPoint(float yaw, float pitch) {
|
||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||
glm::sin(-pitch),
|
||||
glm::cos(-pitch) * (-glm::cos(yaw)));
|
||||
}
|
||||
|
||||
//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should
|
||||
//be multiplied by dir and added to origin to get the location of the collision
|
||||
bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result)
|
||||
{
|
||||
//Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
|
||||
|
||||
//Compute A, B and C coefficients
|
||||
float a = glm::dot(dir, dir);
|
||||
float b = 2 * glm::dot(dir, origin);
|
||||
float c = glm::dot(origin, origin) - (r * r);
|
||||
|
||||
//Find discriminant
|
||||
float disc = b * b - 4 * a * c;
|
||||
|
||||
// if discriminant is negative there are no real roots, so return
|
||||
// false as ray misses sphere
|
||||
if (disc < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute q as described above
|
||||
float distSqrt = sqrtf(disc);
|
||||
float q;
|
||||
if (b < 0) {
|
||||
q = (-b - distSqrt) / 2.0f;
|
||||
} else {
|
||||
q = (-b + distSqrt) / 2.0f;
|
||||
}
|
||||
|
||||
// compute t0 and t1
|
||||
float t0 = q / a;
|
||||
float t1 = c / q;
|
||||
|
||||
// make sure t0 is smaller than t1
|
||||
if (t0 > t1) {
|
||||
// if t0 is bigger than t1 swap them around
|
||||
float temp = t0;
|
||||
t0 = t1;
|
||||
t1 = temp;
|
||||
}
|
||||
|
||||
// if t1 is less than zero, the object is in the ray's negative direction
|
||||
// and consequently the ray misses the sphere
|
||||
if (t1 < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if t0 is less than zero, the intersection point is at t1
|
||||
if (t0 < 0) {
|
||||
*result = t1;
|
||||
return true;
|
||||
} else { // else the intersection point is at t0
|
||||
*result = t0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationCompositor::ApplicationCompositor() :
|
||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")),
|
||||
_reticleInterface(new ReticleInterface(this))
|
||||
|
||||
{
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
_reticleQuad = geometryCache->allocateID();
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_hoverItemId != entityItemID) {
|
||||
_hoverItemId = entityItemID;
|
||||
_hoverItemEnterUsecs = usecTimestampNow();
|
||||
auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);
|
||||
|
||||
// check the format of this href string before we parse it
|
||||
QString hrefString = properties.getHref();
|
||||
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
if (!hrefString.isEmpty()) {
|
||||
if (!hrefString.startsWith("hifi:")) {
|
||||
hrefString.prepend("hifi://");
|
||||
}
|
||||
|
||||
// parse out a QUrl from the hrefString
|
||||
QUrl href = QUrl(hrefString);
|
||||
|
||||
_hoverItemTitle = href.host();
|
||||
_hoverItemDescription = properties.getDescription();
|
||||
cursor->setIcon(Cursor::Icon::LINK);
|
||||
} else {
|
||||
_hoverItemTitle.clear();
|
||||
_hoverItemDescription.clear();
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
if (_hoverItemId == entityItemID) {
|
||||
_hoverItemId = _noItemId;
|
||||
|
||||
_hoverItemTitle.clear();
|
||||
_hoverItemDescription.clear();
|
||||
|
||||
auto cursor = Cursor::Manager::instance().getCursor();
|
||||
cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
if (!_tooltipId.isEmpty()) {
|
||||
qDebug() << "Closing tooltip " << _tooltipId;
|
||||
Tooltip::closeTip(_tooltipId);
|
||||
_tooltipId.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_alphaPropertyAnimation.reset(new QPropertyAnimation(this, "alpha"));
|
||||
}
|
||||
|
||||
ApplicationCompositor::~ApplicationCompositor() {
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
auto cursor = cursorManager.getCursor(cursorIndex);
|
||||
auto iconId = cursor->getIcon();
|
||||
if (!_cursors.count(iconId)) {
|
||||
auto iconPath = cursorManager.getIconImage(cursor->getIcon());
|
||||
_cursors[iconId] = DependencyManager::get<TextureCache>()->
|
||||
getImageTexture(iconPath);
|
||||
}
|
||||
batch.setResourceTexture(0, _cursors[iconId]);
|
||||
}
|
||||
|
||||
// Draws the FBO texture for the screen
|
||||
void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
if (_alpha <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer overlayFramebuffer = qApp->getApplicationOverlay().getOverlayFramebuffer();
|
||||
if (!overlayFramebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateTooltips();
|
||||
|
||||
//Handle fading and deactivation/activation of UI
|
||||
gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setModelTransform(Transform());
|
||||
batch.setViewTransform(Transform());
|
||||
batch.setProjectionTransform(mat4());
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha));
|
||||
|
||||
//draw the mouse pointer
|
||||
if (getReticleVisible()) {
|
||||
// Get the mouse coordinates and convert to NDC [-1, 1]
|
||||
vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas...
|
||||
vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize);
|
||||
// Invert the Y axis
|
||||
mousePosition.y *= -1.0f;
|
||||
|
||||
Transform model;
|
||||
model.setTranslation(vec3(mousePosition, 0));
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
model.setScale(vec3(mouseSize, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
bindCursorTexture(batch);
|
||||
geometryCache->renderUnitQuad(batch, vec4(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Draws the FBO texture for Oculus rift.
|
||||
void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
if (_alpha <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer overlayFramebuffer = qApp->getApplicationOverlay().getOverlayFramebuffer();
|
||||
if (!overlayFramebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateTooltips();
|
||||
|
||||
glm::uvec2 screenSize = qApp->getUiSize(); // HMD use virtual screen size
|
||||
vec2 canvasSize = screenSize;
|
||||
_textureAspectRatio = aspect(canvasSize);
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) {
|
||||
geometryCache->useSimpleDrawPipeline(batch);
|
||||
|
||||
batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0));
|
||||
|
||||
mat4 camMat;
|
||||
_cameraBaseTransform.getMatrix(camMat);
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye);
|
||||
camMat = (headPose * eyeToHead) * camMat; // FIXME - why are not all transforms are doing this aditioanl eyeToHead
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
batch.setViewTransform(camMat);
|
||||
batch.setProjectionTransform(qApp->getEyeProjection(eye));
|
||||
|
||||
#ifdef DEBUG_OVERLAY
|
||||
{
|
||||
batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2)));
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1));
|
||||
}
|
||||
#else
|
||||
{
|
||||
batch.setModelTransform(_modelTransform);
|
||||
drawSphereSection(batch);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
|
||||
bindCursorTexture(batch);
|
||||
|
||||
//Mouse Pointer
|
||||
if (getReticleVisible()) {
|
||||
if (getReticleDepth() != 1.0f) {
|
||||
// calculate the "apparent location" based on the depth and the current ray
|
||||
glm::vec3 origin, direction;
|
||||
auto reticlePosition = getReticlePosition();
|
||||
computeHmdPickRay(reticlePosition, origin, direction);
|
||||
auto apparentPosition = origin + (direction * getReticleDepth());
|
||||
|
||||
// same code as used to render for apparent location
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||
auto relativePosition4 = glm::inverse(UITransform) * vec4(apparentPosition, 1);
|
||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||
auto relativeDistance = glm::length(relativePosition);
|
||||
|
||||
// look at borrowed from overlays
|
||||
float elevation = -asinf(relativePosition.y / glm::length(relativePosition));
|
||||
float azimuth = atan2f(relativePosition.x, relativePosition.z);
|
||||
glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, -PI, 0)); // this extra *quat(vec3(0,-PI,0)) was required to get the quad to flip this seems like we could optimize
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(relativePosition);
|
||||
transform.setScale(reticleScale);
|
||||
transform.postScale(relativeDistance); // scale not quite working, distant things too large
|
||||
transform.setRotation(faceCamera);
|
||||
batch.setModelTransform(transform);
|
||||
} else {
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
|
||||
auto reticlePosition = getReticlePosition();
|
||||
glm::vec2 projection = overlayToSpherical(reticlePosition);
|
||||
mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
mat4 reticleXfm = overlayXfm * pointerXfm;
|
||||
reticleXfm = glm::scale(reticleXfm, reticleScale);
|
||||
batch.setModelTransform(reticleXfm);
|
||||
}
|
||||
geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) {
|
||||
if (qApp->isHMDMode()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||
}
|
||||
return event->localPos();
|
||||
}
|
||||
|
||||
bool ApplicationCompositor::shouldCaptureMouse() const {
|
||||
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
|
||||
return _allowMouseCapture && qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown();
|
||||
}
|
||||
|
||||
void ApplicationCompositor::setAllowMouseCapture(bool capture) {
|
||||
if (qApp->isHMDMode()) {
|
||||
if (capture) {
|
||||
qApp->showCursor(Qt::BlankCursor);
|
||||
} else {
|
||||
qApp->showCursor(Qt::ArrowCursor);
|
||||
}
|
||||
}
|
||||
_allowMouseCapture = capture;
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::handleLeaveEvent() {
|
||||
|
||||
if (shouldCaptureMouse()) {
|
||||
QWidget* mainWidget = (QWidget*)qApp->getWindow();
|
||||
QRect mainWidgetFrame = qApp->getRenderingGeometry();
|
||||
QRect uncoveredRect = mainWidgetFrame;
|
||||
foreach(QWidget* widget, QApplication::topLevelWidgets()) {
|
||||
if (widget->isWindow() && widget->isVisible() && widget != mainWidget) {
|
||||
QRect widgetFrame = widget->frameGeometry();
|
||||
if (widgetFrame.intersects(uncoveredRect)) {
|
||||
QRect intersection = uncoveredRect & widgetFrame;
|
||||
if (intersection.top() > uncoveredRect.top()) {
|
||||
uncoveredRect.setBottom(intersection.top() - 1);
|
||||
} else if (intersection.bottom() < uncoveredRect.bottom()) {
|
||||
uncoveredRect.setTop(intersection.bottom() + 1);
|
||||
}
|
||||
|
||||
if (intersection.left() > uncoveredRect.left()) {
|
||||
uncoveredRect.setRight(intersection.left() - 1);
|
||||
} else if (intersection.right() < uncoveredRect.right()) {
|
||||
uncoveredRect.setLeft(intersection.right() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ignoreMouseMove = true;
|
||||
auto sendToPos = uncoveredRect.center();
|
||||
QCursor::setPos(sendToPos);
|
||||
_lastKnownRealMouse = sendToPos;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) {
|
||||
|
||||
// If the mouse move came from a capture mouse related move, we completely ignore it.
|
||||
if (_ignoreMouseMove) {
|
||||
_ignoreMouseMove = false;
|
||||
return true; // swallow the event
|
||||
}
|
||||
|
||||
// If we're in HMD mode
|
||||
if (shouldCaptureMouse()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
auto newPosition = QCursor::pos();
|
||||
auto changeInRealMouse = newPosition - _lastKnownRealMouse;
|
||||
auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse);
|
||||
setReticlePosition(newReticlePosition, sendFakeEvent);
|
||||
_ignoreMouseMove = true;
|
||||
QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was
|
||||
return true; // swallow the event
|
||||
} else {
|
||||
_lastKnownRealMouse = QCursor::pos();
|
||||
}
|
||||
return false; // let the caller know to process the event
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::getReticlePosition() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
return _reticlePositionInHMD;
|
||||
}
|
||||
return toGlm(QCursor::pos());
|
||||
}
|
||||
|
||||
bool ApplicationCompositor::getReticleOverDesktop() const {
|
||||
// if the QML/Offscreen UI thinks we're over the desktop, then we are...
|
||||
// but... if we're outside of the overlay area, we also want to call ourselves
|
||||
// as being over the desktop.
|
||||
if (qApp->isHMDMode()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
glm::vec2 maxOverlayPosition = qApp->getUiSize();
|
||||
if (_reticlePositionInHMD.x < 0 || _reticlePositionInHMD.y < 0 ||
|
||||
_reticlePositionInHMD.x > maxOverlayPosition.x || _reticlePositionInHMD.y > maxOverlayPosition.y) {
|
||||
return true; // we are outside the overlay area, consider ourselves over the desktop
|
||||
}
|
||||
}
|
||||
return _isOverDesktop;
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::setReticlePosition(glm::vec2 position, bool sendFakeEvent) {
|
||||
if (qApp->isHMDMode()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
const float MOUSE_EXTENTS_VERT_ANGULAR_SIZE = 170.0f; // 5deg from poles
|
||||
const float MOUSE_EXTENTS_VERT_PIXELS = VIRTUAL_SCREEN_SIZE_Y * (MOUSE_EXTENTS_VERT_ANGULAR_SIZE / DEFAULT_HMD_UI_VERT_ANGULAR_SIZE);
|
||||
const float MOUSE_EXTENTS_HORZ_ANGULAR_SIZE = 360.0f; // full sphere
|
||||
const float MOUSE_EXTENTS_HORZ_PIXELS = VIRTUAL_SCREEN_SIZE_X * (MOUSE_EXTENTS_HORZ_ANGULAR_SIZE / DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE);
|
||||
|
||||
glm::vec2 maxOverlayPosition = qApp->getUiSize();
|
||||
float extaPixelsX = (MOUSE_EXTENTS_HORZ_PIXELS - maxOverlayPosition.x) / 2.0f;
|
||||
float extaPixelsY = (MOUSE_EXTENTS_VERT_PIXELS - maxOverlayPosition.y) / 2.0f;
|
||||
glm::vec2 mouseExtra { extaPixelsX, extaPixelsY };
|
||||
glm::vec2 minMouse = vec2(0) - mouseExtra;
|
||||
glm::vec2 maxMouse = maxOverlayPosition + mouseExtra;
|
||||
|
||||
_reticlePositionInHMD = glm::clamp(position, minMouse, maxMouse);
|
||||
|
||||
if (sendFakeEvent) {
|
||||
// in HMD mode we need to fake our mouse moves...
|
||||
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||
auto button = Qt::NoButton;
|
||||
auto buttons = QApplication::mouseButtons();
|
||||
auto modifiers = QApplication::keyboardModifiers();
|
||||
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
|
||||
qApp->fakeMouseEvent(&event);
|
||||
}
|
||||
} else {
|
||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||
// remove it after we're done
|
||||
const float REASONABLE_CHANGE = 50.0f;
|
||||
glm::vec2 oldPos = toGlm(QCursor::pos());
|
||||
auto distance = glm::distance(oldPos, position);
|
||||
if (distance > REASONABLE_CHANGE) {
|
||||
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position;
|
||||
}
|
||||
|
||||
QCursor::setPos(position.x, position.y);
|
||||
}
|
||||
}
|
||||
|
||||
#include <QDesktopWidget>
|
||||
|
||||
glm::vec2 ApplicationCompositor::getReticleMaximumPosition() const {
|
||||
glm::vec2 result;
|
||||
if (qApp->isHMDMode()) {
|
||||
result = glm::vec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y);
|
||||
} else {
|
||||
QRect rec = QApplication::desktop()->screenGeometry();
|
||||
result = glm::vec2(rec.right(), rec.bottom());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
||||
auto surfacePointAt = sphereSurfaceFromOverlay(cursorPos); // in world space
|
||||
glm::vec3 worldSpaceCameraPosition = qApp->getCamera()->getPosition();
|
||||
origin = worldSpaceCameraPosition;
|
||||
direction = glm::normalize(surfacePointAt - worldSpaceCameraPosition);
|
||||
}
|
||||
|
||||
//Finds the collision point of a world space ray
|
||||
bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||
auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1);
|
||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
|
||||
|
||||
float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
|
||||
|
||||
float instersectionDistance;
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){
|
||||
result = position + glm::normalize(direction) * instersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApplicationCompositor::buildHemiVertices(
|
||||
const float fov, const float aspectRatio, const int slices, const int stacks) {
|
||||
static float textureFOV = 0.0f, textureAspectRatio = 1.0f;
|
||||
if (textureFOV == fov && textureAspectRatio == aspectRatio) {
|
||||
return;
|
||||
}
|
||||
|
||||
textureFOV = fov;
|
||||
textureAspectRatio = aspectRatio;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
_hemiVertices = std::make_shared<gpu::Buffer>();
|
||||
_hemiIndices = std::make_shared<gpu::Buffer>();
|
||||
|
||||
|
||||
if (fov >= PI) {
|
||||
qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues";
|
||||
}
|
||||
|
||||
//UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm
|
||||
|
||||
vec3 pos;
|
||||
vec2 uv;
|
||||
// Compute vertices positions and texture UV coordinate
|
||||
// Create and write to buffer
|
||||
for (int i = 0; i < stacks; i++) {
|
||||
uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f
|
||||
// abs(theta) <= fov / 2.0f
|
||||
float pitch = -fov * (uv.y - 0.5f);
|
||||
for (int j = 0; j < slices; j++) {
|
||||
uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f
|
||||
// abs(phi) <= fov * aspectRatio / 2.0f
|
||||
float yaw = -fov * aspectRatio * (uv.x - 0.5f);
|
||||
pos = getPoint(yaw, pitch);
|
||||
static const vec4 color(1);
|
||||
_hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos);
|
||||
_hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv);
|
||||
_hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute number of indices needed
|
||||
static const int VERTEX_PER_TRANGLE = 3;
|
||||
static const int TRIANGLE_PER_RECTANGLE = 2;
|
||||
int numberOfRectangles = (slices - 1) * (stacks - 1);
|
||||
_hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
|
||||
|
||||
// Compute indices order
|
||||
std::vector<GLushort> indices;
|
||||
for (int i = 0; i < stacks - 1; i++) {
|
||||
for (int j = 0; j < slices - 1; j++) {
|
||||
GLushort bottomLeftIndex = i * slices + j;
|
||||
GLushort bottomRightIndex = bottomLeftIndex + 1;
|
||||
GLushort topLeftIndex = bottomLeftIndex + slices;
|
||||
GLushort topRightIndex = topLeftIndex + 1;
|
||||
// FIXME make a z-order curve for better vertex cache locality
|
||||
indices.push_back(topLeftIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(topRightIndex);
|
||||
|
||||
indices.push_back(topRightIndex);
|
||||
indices.push_back(bottomLeftIndex);
|
||||
indices.push_back(bottomRightIndex);
|
||||
}
|
||||
}
|
||||
_hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]);
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) {
|
||||
buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80);
|
||||
static const int VERTEX_DATA_SLOT = 0;
|
||||
static const int TEXTURE_DATA_SLOT = 1;
|
||||
static const int COLOR_DATA_SLOT = 2;
|
||||
auto streamFormat = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
|
||||
streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
|
||||
streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
|
||||
streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
|
||||
batch.setInputFormat(streamFormat);
|
||||
|
||||
static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4);
|
||||
|
||||
if (_prevAlpha != _alpha) {
|
||||
// adjust alpha by munging vertex color alpha.
|
||||
// FIXME we should probably just use a uniform for this.
|
||||
float* floatPtr = reinterpret_cast<float*>(_hemiVertices->editData());
|
||||
const auto ALPHA_FLOAT_OFFSET = (sizeof(vec3) + sizeof(vec2) + sizeof(vec3)) / sizeof(float);
|
||||
const auto VERTEX_FLOAT_STRIDE = (sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) / sizeof(float);
|
||||
const auto NUM_VERTS = _hemiVertices->getSize() / VERTEX_STRIDE;
|
||||
for (size_t i = 0; i < NUM_VERTS; i++) {
|
||||
floatPtr[i * VERTEX_FLOAT_STRIDE + ALPHA_FLOAT_OFFSET] = _alpha;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element);
|
||||
gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element);
|
||||
gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element);
|
||||
batch.setInputBuffer(VERTEX_DATA_SLOT, posView);
|
||||
batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView);
|
||||
batch.setInputBuffer(COLOR_DATA_SLOT, colView);
|
||||
batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount);
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0f;
|
||||
result /= _textureFov;
|
||||
result.x /= _textureAspectRatio;
|
||||
result += 0.5f;
|
||||
result *= qApp->getUiSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const {
|
||||
glm::vec2 result = overlayPos;
|
||||
result /= qApp->getUiSize();
|
||||
result -= 0.5f;
|
||||
result *= _textureFov;
|
||||
result.x *= _textureAspectRatio;
|
||||
result.x *= -1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const {
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||
auto relativePosition4 = glm::inverse(UITransform) * vec4(sphereSurfacePoint, 1);
|
||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||
auto center = vec3(0); // center of HUD in HUD space
|
||||
auto direction = relativePosition - center; // direction to relative position in HUD space
|
||||
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
|
||||
auto overlayPos = sphericalToOverlay(polar);
|
||||
return overlayPos;
|
||||
}
|
||||
|
||||
glm::vec3 ApplicationCompositor::sphereSurfaceFromOverlay(const glm::vec2& overlay) const {
|
||||
auto spherical = overlayToSpherical(overlay);
|
||||
auto sphereSurfacePoint = getPoint(spherical.x, spherical.y);
|
||||
auto headPose = qApp->getHMDSensorPose();
|
||||
auto myCamera = qApp->getCamera();
|
||||
mat4 cameraMat = myCamera->getTransform();
|
||||
auto UITransform = cameraMat * glm::inverse(headPose);
|
||||
auto position4 = UITransform * vec4(sphereSurfacePoint, 1);
|
||||
auto position = vec3(position4) / position4.w;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
void ApplicationCompositor::updateTooltips() {
|
||||
if (_hoverItemId != _noItemId) {
|
||||
quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||
if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemTitle.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
|
||||
// TODO Enable and position the tooltip
|
||||
_hoverItemEnterUsecs = UINT64_MAX;
|
||||
_tooltipId = Tooltip::showTip(_hoverItemTitle, _hoverItemDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const float FADE_DURATION = 500.0f;
|
||||
void ApplicationCompositor::fadeIn() {
|
||||
_fadeInAlpha = true;
|
||||
|
||||
_alphaPropertyAnimation->setDuration(FADE_DURATION);
|
||||
_alphaPropertyAnimation->setStartValue(_alpha);
|
||||
_alphaPropertyAnimation->setEndValue(1.0f);
|
||||
_alphaPropertyAnimation->start();
|
||||
}
|
||||
void ApplicationCompositor::fadeOut() {
|
||||
_fadeInAlpha = false;
|
||||
|
||||
_alphaPropertyAnimation->setDuration(FADE_DURATION);
|
||||
_alphaPropertyAnimation->setStartValue(_alpha);
|
||||
_alphaPropertyAnimation->setEndValue(0.0f);
|
||||
_alphaPropertyAnimation->start();
|
||||
}
|
||||
|
||||
void ApplicationCompositor::toggle() {
|
||||
if (_fadeInAlpha) {
|
||||
fadeOut();
|
||||
} else {
|
||||
fadeIn();
|
||||
}
|
||||
}
|
|
@ -244,34 +244,69 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr
|
|||
}
|
||||
}
|
||||
|
||||
static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||
|
||||
std::mutex _textureGuard;
|
||||
using Lock = std::unique_lock<std::mutex>;
|
||||
std::queue<gpu::TexturePointer> _availableTextures;
|
||||
|
||||
void ApplicationOverlay::buildFramebufferObject() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
auto uiSize = qApp->getUiSize();
|
||||
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
|
||||
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
}
|
||||
|
||||
if (_overlayFramebuffer && uiSize == _overlayFramebuffer->getSize()) {
|
||||
// Already built
|
||||
return;
|
||||
auto width = uiSize.x;
|
||||
auto height = uiSize.y;
|
||||
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
|
||||
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
|
||||
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
|
||||
}
|
||||
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
gpu::TexturePointer newColorAttachment;
|
||||
{
|
||||
Lock lock(_textureGuard);
|
||||
if (!_availableTextures.empty()) {
|
||||
newColorAttachment = _availableTextures.front();
|
||||
_availableTextures.pop();
|
||||
}
|
||||
}
|
||||
if (newColorAttachment) {
|
||||
newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples());
|
||||
_overlayFramebuffer->setRenderBuffer(0, newColorAttachment);
|
||||
}
|
||||
}
|
||||
|
||||
if (_overlayFramebuffer) {
|
||||
_overlayFramebuffer.reset();
|
||||
_overlayDepthTexture.reset();
|
||||
_overlayColorTexture.reset();
|
||||
// If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one
|
||||
if (!_overlayFramebuffer->getRenderBuffer(0)) {
|
||||
auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, DEFAULT_SAMPLER));
|
||||
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::TexturePointer ApplicationOverlay::acquireOverlay() {
|
||||
if (!_overlayFramebuffer) {
|
||||
return gpu::TexturePointer();
|
||||
}
|
||||
auto result = _overlayFramebuffer->getRenderBuffer(0);
|
||||
auto textureId = gpu::GLBackend::getTextureID(result, false);
|
||||
if (!textureId) {
|
||||
qDebug() << "Missing texture";
|
||||
}
|
||||
_overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer());
|
||||
return result;
|
||||
}
|
||||
|
||||
void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) {
|
||||
if (texture) {
|
||||
Lock lock(_textureGuard);
|
||||
_availableTextures.push(texture);
|
||||
} else {
|
||||
qWarning() << "Attempted to release null texture";
|
||||
}
|
||||
|
||||
_overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
|
||||
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
auto width = uiSize.x;
|
||||
auto height = uiSize.y;
|
||||
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
_overlayColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||
_overlayFramebuffer->setRenderBuffer(0, _overlayColorTexture);
|
||||
|
||||
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||
_overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));
|
||||
|
||||
_overlayFramebuffer->setDepthStencilBuffer(_overlayDepthTexture, depthFormat);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ public:
|
|||
|
||||
void renderOverlay(RenderArgs* renderArgs);
|
||||
|
||||
gpu::FramebufferPointer getOverlayFramebuffer() const { return _overlayFramebuffer; }
|
||||
gpu::TexturePointer acquireOverlay();
|
||||
void releaseOverlay(gpu::TexturePointer pointer);
|
||||
|
||||
private:
|
||||
void renderStatsAndLogs(RenderArgs* renderArgs);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//
|
||||
|
||||
#include <OffscreenUi.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <ScriptEngines.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <Preferences.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "DialogsManager.h"
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
set(TARGET_NAME display-plugins)
|
||||
setup_hifi_library(OpenGL)
|
||||
link_hifi_libraries(shared plugins gl)
|
||||
link_hifi_libraries(shared plugins gl ui)
|
||||
|
||||
target_opengl()
|
||||
|
||||
GroupSources("src/display-plugins")
|
||||
|
||||
target_oglplus()
|
||||
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(OpenVR)
|
||||
find_package(OpenVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
|
||||
endif()
|
||||
|
|
|
@ -35,9 +35,9 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
|||
updateFramerate();
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
_wantVsync = true; // always
|
||||
WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize);
|
||||
WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture);
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
virtual void activate() override;
|
||||
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
|
||||
virtual void internalPresent() override;
|
||||
|
||||
|
|
|
@ -0,0 +1,476 @@
|
|||
//
|
||||
// Created by Benjamin Arnold on 5/27/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "CompositorHelper.h"
|
||||
|
||||
#include <memory>
|
||||
#include <math.h>
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <ui/Menu.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <CursorManager.h>
|
||||
#include <gl/GLWidget.h>
|
||||
|
||||
// Used to animate the magnification windows
|
||||
|
||||
//static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||
|
||||
static const float reticleSize = TWO_PI / 100.0f;
|
||||
|
||||
//EntityItemID CompositorHelper::_noItemId;
|
||||
static QString _tooltipId;
|
||||
|
||||
const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(3960, 1188); // ~10% more pixel density than old version, 72dx240d FOV
|
||||
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
|
||||
const vec2 CompositorHelper::MOUSE_EXTENTS_PIXELS = vec2(VIRTUAL_SCREEN_SIZE) * (MOUSE_EXTENTS_ANGULAR_SIZE / VIRTUAL_UI_TARGET_FOV);
|
||||
|
||||
// Return a point's cartesian coordinates on a sphere from pitch and yaw
|
||||
glm::vec3 getPoint(float yaw, float pitch) {
|
||||
return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)),
|
||||
glm::sin(-pitch),
|
||||
glm::cos(-pitch) * (-glm::cos(yaw)));
|
||||
}
|
||||
|
||||
// FIXME move to GLMHelpers
|
||||
//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should
|
||||
//be multiplied by dir and added to origin to get the location of the collision
|
||||
bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result)
|
||||
{
|
||||
//Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
|
||||
|
||||
//Compute A, B and C coefficients
|
||||
float a = glm::dot(dir, dir);
|
||||
float b = 2 * glm::dot(dir, origin);
|
||||
float c = glm::dot(origin, origin) - (r * r);
|
||||
|
||||
//Find discriminant
|
||||
float disc = b * b - 4 * a * c;
|
||||
|
||||
// if discriminant is negative there are no real roots, so return
|
||||
// false as ray misses sphere
|
||||
if (disc < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute q as described above
|
||||
float distSqrt = sqrtf(disc);
|
||||
float q;
|
||||
if (b < 0) {
|
||||
q = (-b - distSqrt) / 2.0f;
|
||||
} else {
|
||||
q = (-b + distSqrt) / 2.0f;
|
||||
}
|
||||
|
||||
// compute t0 and t1
|
||||
float t0 = q / a;
|
||||
float t1 = c / q;
|
||||
|
||||
// make sure t0 is smaller than t1
|
||||
if (t0 > t1) {
|
||||
// if t0 is bigger than t1 swap them around
|
||||
float temp = t0;
|
||||
t0 = t1;
|
||||
t1 = temp;
|
||||
}
|
||||
|
||||
// if t1 is less than zero, the object is in the ray's negative direction
|
||||
// and consequently the ray misses the sphere
|
||||
if (t1 < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if t0 is less than zero, the intersection point is at t1
|
||||
if (t0 < 0) {
|
||||
*result = t1;
|
||||
return true;
|
||||
} else { // else the intersection point is at t0
|
||||
*result = t0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
CompositorHelper::CompositorHelper() :
|
||||
_alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")),
|
||||
_reticleInterface(new ReticleInterface(this))
|
||||
{
|
||||
// FIX in a separate PR addressing the current mouse over entity bug
|
||||
//auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
//connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
// if (_hoverItemId != entityItemID) {
|
||||
// _hoverItemId = entityItemID;
|
||||
// _hoverItemEnterUsecs = usecTimestampNow();
|
||||
// auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId);
|
||||
|
||||
// // check the format of this href string before we parse it
|
||||
// QString hrefString = properties.getHref();
|
||||
|
||||
// auto cursor = Cursor::Manager::instance().getCursor();
|
||||
// if (!hrefString.isEmpty()) {
|
||||
// if (!hrefString.startsWith("hifi:")) {
|
||||
// hrefString.prepend("hifi://");
|
||||
// }
|
||||
|
||||
// // parse out a QUrl from the hrefString
|
||||
// QUrl href = QUrl(hrefString);
|
||||
|
||||
// _hoverItemTitle = href.host();
|
||||
// _hoverItemDescription = properties.getDescription();
|
||||
// cursor->setIcon(Cursor::Icon::LINK);
|
||||
// } else {
|
||||
// _hoverItemTitle.clear();
|
||||
// _hoverItemDescription.clear();
|
||||
// cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
// }
|
||||
// }
|
||||
//});
|
||||
|
||||
//connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) {
|
||||
// if (_hoverItemId == entityItemID) {
|
||||
// _hoverItemId = _noItemId;
|
||||
|
||||
// _hoverItemTitle.clear();
|
||||
// _hoverItemDescription.clear();
|
||||
|
||||
// auto cursor = Cursor::Manager::instance().getCursor();
|
||||
// cursor->setIcon(Cursor::Icon::DEFAULT);
|
||||
// if (!_tooltipId.isEmpty()) {
|
||||
// qDebug() << "Closing tooltip " << _tooltipId;
|
||||
// Tooltip::closeTip(_tooltipId);
|
||||
// _tooltipId.clear();
|
||||
// }
|
||||
// }
|
||||
//});
|
||||
}
|
||||
|
||||
|
||||
bool CompositorHelper::isHMD() const {
|
||||
return _currentDisplayPlugin && _currentDisplayPlugin->isHmd();
|
||||
}
|
||||
|
||||
QPointF CompositorHelper::getMouseEventPosition(QMouseEvent* event) {
|
||||
if (isHMD()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||
}
|
||||
return event->localPos();
|
||||
}
|
||||
|
||||
bool CompositorHelper::shouldCaptureMouse() const {
|
||||
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
|
||||
return _allowMouseCapture && isHMD() && QApplication::activeWindow() && !ui::Menu::isSomeSubmenuShown();
|
||||
}
|
||||
|
||||
void CompositorHelper::setAllowMouseCapture(bool capture) {
|
||||
if (capture != _allowMouseCapture) {
|
||||
_allowMouseCapture = capture;
|
||||
emit allowMouseCaptureChanged();
|
||||
}
|
||||
_allowMouseCapture = capture;
|
||||
}
|
||||
|
||||
void CompositorHelper::handleLeaveEvent() {
|
||||
if (shouldCaptureMouse()) {
|
||||
|
||||
//QWidget* mainWidget = (QWidget*)qApp->getWindow();
|
||||
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
|
||||
static QWidget* mainWidget = nullptr;
|
||||
if (mainWidget == nullptr) {
|
||||
mainWidget = renderingWidget->parentWidget();
|
||||
}
|
||||
QRect mainWidgetFrame;
|
||||
{
|
||||
mainWidgetFrame = renderingWidget->geometry();
|
||||
auto topLeft = mainWidgetFrame.topLeft();
|
||||
auto topLeftScreen = renderingWidget->mapToGlobal(topLeft);
|
||||
mainWidgetFrame.moveTopLeft(topLeftScreen);
|
||||
}
|
||||
QRect uncoveredRect = mainWidgetFrame;
|
||||
foreach(QWidget* widget, QApplication::topLevelWidgets()) {
|
||||
if (widget->isWindow() && widget->isVisible() && widget != mainWidget) {
|
||||
QRect widgetFrame = widget->frameGeometry();
|
||||
if (widgetFrame.intersects(uncoveredRect)) {
|
||||
QRect intersection = uncoveredRect & widgetFrame;
|
||||
if (intersection.top() > uncoveredRect.top()) {
|
||||
uncoveredRect.setBottom(intersection.top() - 1);
|
||||
} else if (intersection.bottom() < uncoveredRect.bottom()) {
|
||||
uncoveredRect.setTop(intersection.bottom() + 1);
|
||||
}
|
||||
|
||||
if (intersection.left() > uncoveredRect.left()) {
|
||||
uncoveredRect.setRight(intersection.left() - 1);
|
||||
} else if (intersection.right() < uncoveredRect.right()) {
|
||||
uncoveredRect.setLeft(intersection.right() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ignoreMouseMove = true;
|
||||
auto sendToPos = uncoveredRect.center();
|
||||
QCursor::setPos(sendToPos);
|
||||
_lastKnownRealMouse = sendToPos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CompositorHelper::handleRealMouseMoveEvent(bool sendFakeEvent) {
|
||||
// If the mouse move came from a capture mouse related move, we completely ignore it.
|
||||
if (_ignoreMouseMove) {
|
||||
_ignoreMouseMove = false;
|
||||
return true; // swallow the event
|
||||
}
|
||||
|
||||
// If we're in HMD mode
|
||||
if (shouldCaptureMouse()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
auto newPosition = QCursor::pos();
|
||||
auto changeInRealMouse = newPosition - _lastKnownRealMouse;
|
||||
auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse);
|
||||
setReticlePosition(newReticlePosition, sendFakeEvent);
|
||||
_ignoreMouseMove = true;
|
||||
QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was
|
||||
return true; // swallow the event
|
||||
} else {
|
||||
_lastKnownRealMouse = QCursor::pos();
|
||||
}
|
||||
return false; // let the caller know to process the event
|
||||
}
|
||||
|
||||
glm::vec2 CompositorHelper::getReticlePosition() const {
|
||||
if (isHMD()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
return _reticlePositionInHMD;
|
||||
}
|
||||
return toGlm(QCursor::pos());
|
||||
}
|
||||
|
||||
bool CompositorHelper::getReticleOverDesktop() const {
|
||||
// if the QML/Offscreen UI thinks we're over the desktop, then we are...
|
||||
// but... if we're outside of the overlay area, we also want to call ourselves
|
||||
// as being over the desktop.
|
||||
if (isHMD()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
glm::vec2 maxOverlayPosition = _currentDisplayPlugin->getRecommendedUiSize();
|
||||
static const glm::vec2 minOverlayPosition;
|
||||
if (glm::any(glm::lessThan(_reticlePositionInHMD, minOverlayPosition)) ||
|
||||
glm::any(glm::greaterThan(_reticlePositionInHMD, maxOverlayPosition))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return _isOverDesktop;
|
||||
}
|
||||
|
||||
glm::vec2 CompositorHelper::getReticleMaximumPosition() const {
|
||||
glm::vec2 result;
|
||||
if (isHMD()) {
|
||||
result = VIRTUAL_SCREEN_SIZE;
|
||||
} else {
|
||||
QRect rec = QApplication::desktop()->screenGeometry();
|
||||
result = glm::vec2(rec.right(), rec.bottom());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) {
|
||||
if (isHMD()) {
|
||||
QMutexLocker locker(&_reticleLock);
|
||||
glm::vec2 maxOverlayPosition = _currentDisplayPlugin->getRecommendedUiSize();
|
||||
// FIXME don't allow negative mouseExtra
|
||||
glm::vec2 mouseExtra = (MOUSE_EXTENTS_PIXELS - maxOverlayPosition) / 2.0f;
|
||||
glm::vec2 minMouse = vec2(0) - mouseExtra;
|
||||
glm::vec2 maxMouse = maxOverlayPosition + mouseExtra;
|
||||
|
||||
_reticlePositionInHMD = glm::clamp(position, minMouse, maxMouse);
|
||||
|
||||
if (sendFakeEvent) {
|
||||
// in HMD mode we need to fake our mouse moves...
|
||||
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||
auto button = Qt::NoButton;
|
||||
auto buttons = QApplication::mouseButtons();
|
||||
auto modifiers = QApplication::keyboardModifiers();
|
||||
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
|
||||
if (qApp->thread() == QThread::currentThread()) {
|
||||
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
|
||||
_fakeMouseEvent = true;
|
||||
qApp->sendEvent(renderingWidget, &event);
|
||||
_fakeMouseEvent = false;
|
||||
} else {
|
||||
qApp->postEvent(renderingWidget, new QMouseEvent(QEvent::MouseMove, globalPos, button, buttons, modifiers));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||
// remove it after we're done
|
||||
const float REASONABLE_CHANGE = 50.0f;
|
||||
glm::vec2 oldPos = toGlm(QCursor::pos());
|
||||
auto distance = glm::distance(oldPos, position);
|
||||
if (distance > REASONABLE_CHANGE) {
|
||||
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" <<
|
||||
distance << " oldPos:" << oldPos.x << "," << oldPos.y << " newPos:" << position.x << "," << position.y;
|
||||
}
|
||||
|
||||
QCursor::setPos(position.x, position.y);
|
||||
}
|
||||
}
|
||||
|
||||
void CompositorHelper::computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& origin, glm::vec3& direction) const {
|
||||
auto surfacePointAt = sphereSurfaceFromOverlay(cursorPos); // in world space
|
||||
origin = vec3(_currentCamera[3]);
|
||||
direction = glm::normalize(surfacePointAt - origin);
|
||||
}
|
||||
|
||||
glm::mat4 CompositorHelper::getUiTransform() const {
|
||||
return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose(_currentFrame));
|
||||
}
|
||||
|
||||
//Finds the collision point of a world space ray
|
||||
bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const {
|
||||
auto UITransform = getUiTransform();
|
||||
auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1);
|
||||
auto relativePosition = vec3(relativePosition4) / relativePosition4.w;
|
||||
auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction;
|
||||
|
||||
float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale
|
||||
|
||||
float instersectionDistance;
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){
|
||||
result = position + glm::normalize(direction) * instersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec2 CompositorHelper::sphericalToOverlay(const glm::vec2& sphericalPos) const {
|
||||
glm::vec2 result = sphericalPos;
|
||||
result.x *= -1.0f;
|
||||
result /= _textureFov;
|
||||
result.x /= _textureAspectRatio;
|
||||
result += 0.5f;
|
||||
result *= _currentDisplayPlugin->getRecommendedUiSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 CompositorHelper::overlayToSpherical(const glm::vec2& overlayPos) const {
|
||||
glm::vec2 result = overlayPos;
|
||||
result /= _currentDisplayPlugin->getRecommendedUiSize();
|
||||
result -= 0.5f;
|
||||
result *= _textureFov;
|
||||
result.x *= _textureAspectRatio;
|
||||
result.x *= -1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec2 CompositorHelper::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const {
|
||||
auto UITransform = getUiTransform();
|
||||
auto relativePosition4 = glm::inverse(UITransform) * vec4(sphereSurfacePoint, 1);
|
||||
auto direction = vec3(relativePosition4) / relativePosition4.w;
|
||||
// FIXME use a GLMHelper cartesianToSpherical after fixing the rotation signs.
|
||||
glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f;
|
||||
auto overlayPos = sphericalToOverlay(polar);
|
||||
return overlayPos;
|
||||
}
|
||||
|
||||
glm::vec3 CompositorHelper::sphereSurfaceFromOverlay(const glm::vec2& overlay) const {
|
||||
auto spherical = overlayToSpherical(overlay);
|
||||
// FIXME use a GLMHelper sphericalToCartesian after fixing the rotation signs.
|
||||
auto sphereSurfacePoint = getPoint(spherical.x, spherical.y);
|
||||
auto UITransform = getUiTransform();
|
||||
auto position4 = UITransform * vec4(sphereSurfacePoint, 1);
|
||||
return vec3(position4) / position4.w;
|
||||
}
|
||||
|
||||
void CompositorHelper::updateTooltips() {
|
||||
//if (_hoverItemId != _noItemId) {
|
||||
// quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs;
|
||||
// if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemTitle.isEmpty() && hoverDuration > TOOLTIP_DELAY) {
|
||||
// // TODO Enable and position the tooltip
|
||||
// _hoverItemEnterUsecs = UINT64_MAX;
|
||||
// _tooltipId = Tooltip::showTip(_hoverItemTitle, _hoverItemDescription);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
static const float FADE_DURATION = 500.0f;
|
||||
void CompositorHelper::fadeIn() {
|
||||
_fadeInAlpha = true;
|
||||
|
||||
_alphaPropertyAnimation->setDuration(FADE_DURATION);
|
||||
_alphaPropertyAnimation->setStartValue(_alpha);
|
||||
_alphaPropertyAnimation->setEndValue(1.0f);
|
||||
_alphaPropertyAnimation->start();
|
||||
}
|
||||
|
||||
void CompositorHelper::fadeOut() {
|
||||
_fadeInAlpha = false;
|
||||
|
||||
_alphaPropertyAnimation->setDuration(FADE_DURATION);
|
||||
_alphaPropertyAnimation->setStartValue(_alpha);
|
||||
_alphaPropertyAnimation->setEndValue(0.0f);
|
||||
_alphaPropertyAnimation->start();
|
||||
}
|
||||
|
||||
void CompositorHelper::toggle() {
|
||||
if (_fadeInAlpha) {
|
||||
fadeOut();
|
||||
} else {
|
||||
fadeIn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::vec3& headPosition) const {
|
||||
glm::mat4 result;
|
||||
if (isHMD()) {
|
||||
vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize);
|
||||
auto reticlePosition = getReticlePosition();
|
||||
auto spherical = overlayToSpherical(reticlePosition);
|
||||
// The pointer transform relative to the sensor
|
||||
auto pointerTransform = glm::mat4_cast(quat(vec3(-spherical.y, spherical.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1));
|
||||
float reticleDepth = getReticleDepth();
|
||||
if (reticleDepth != 1.0f) {
|
||||
// Cursor position in UI space
|
||||
auto cursorPosition = vec3(pointerTransform[3]) / pointerTransform[3].w;
|
||||
// Ray to the cursor, in UI space
|
||||
auto cursorRay = glm::normalize(cursorPosition - headPosition) * reticleDepth;
|
||||
// Move the ray to be relative to the head pose
|
||||
pointerTransform[3] = vec4(cursorRay + headPosition, 1);
|
||||
// Scale up the cursor because of distance
|
||||
reticleScale *= reticleDepth;
|
||||
}
|
||||
glm::mat4 overlayXfm;
|
||||
_modelTransform.getMatrix(overlayXfm);
|
||||
pointerTransform = overlayXfm * pointerTransform;
|
||||
pointerTransform = glm::inverse(eyePose) * pointerTransform;
|
||||
result = glm::scale(pointerTransform, reticleScale);
|
||||
} else {
|
||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||
static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget();
|
||||
const auto canvasSize = vec2(toGlm(renderingWidget->size()));;
|
||||
vec2 mousePosition = toGlm(renderingWidget->mapFromGlobal(QCursor::pos()));
|
||||
mousePosition /= canvasSize;
|
||||
mousePosition *= 2.0;
|
||||
mousePosition -= 1.0;
|
||||
mousePosition.y *= -1.0f;
|
||||
|
||||
vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize;
|
||||
return glm::scale(glm::translate(glm::mat4(), vec3(mousePosition, 0.0f)), vec3(mouseSize, 1.0f));
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -6,59 +6,55 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ApplicationCompositor_h
|
||||
#define hifi_ApplicationCompositor_h
|
||||
#ifndef hifi_display_plugins_Compositor_h
|
||||
#define hifi_display_plugins_Compositor_h
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include <QCursor>
|
||||
#include <QMouseEvent>
|
||||
#include <QObject>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QPropertyAnimation>
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
#include <EntityItemID.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Texture.h>
|
||||
#include <Transform.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "DisplayPlugin.h"
|
||||
|
||||
class Camera;
|
||||
class PalmData;
|
||||
class RenderArgs;
|
||||
class ReticleInterface;
|
||||
|
||||
|
||||
const float DEFAULT_RETICLE_DEPTH = 1.0f; // FIXME - probably should be based on UI radius
|
||||
|
||||
const float MAGNIFY_WIDTH = 220.0f;
|
||||
const float MAGNIFY_HEIGHT = 100.0f;
|
||||
const float MAGNIFY_MULT = 2.0f;
|
||||
|
||||
const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV
|
||||
const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV
|
||||
const float DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE = 240.0f;
|
||||
const float DEFAULT_HMD_UI_VERT_ANGULAR_SIZE = DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE * (float)VIRTUAL_SCREEN_SIZE_Y / (float)VIRTUAL_SCREEN_SIZE_X;
|
||||
|
||||
// Handles the drawing of the overlays to the screen
|
||||
// TODO, move divide up the rendering, displaying and input handling
|
||||
// facilities of this class
|
||||
class ApplicationCompositor : public QObject {
|
||||
class CompositorHelper : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha)
|
||||
Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop)
|
||||
public:
|
||||
ApplicationCompositor();
|
||||
~ApplicationCompositor();
|
||||
static const uvec2 VIRTUAL_SCREEN_SIZE;
|
||||
static const float VIRTUAL_UI_ASPECT_RATIO;
|
||||
static const vec2 VIRTUAL_UI_TARGET_FOV;
|
||||
static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE;
|
||||
static const vec2 MOUSE_EXTENTS_PIXELS;
|
||||
|
||||
void displayOverlayTexture(RenderArgs* renderArgs);
|
||||
void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye);
|
||||
CompositorHelper();
|
||||
|
||||
bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const;
|
||||
|
||||
float getHmdUIAngularSize() const { return _hmdUIAngularSize; }
|
||||
void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; }
|
||||
bool isHMD() const;
|
||||
bool fakeEventActive() const { return _fakeMouseEvent; }
|
||||
|
||||
// Converter from one frame of reference to another.
|
||||
// Frame of reference:
|
||||
|
@ -66,8 +62,7 @@ public:
|
|||
// Overlay: Position on the overlay (x,y)
|
||||
glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||
glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||
void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||
uint32_t getOverlayTexture() const;
|
||||
void computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& origin, glm::vec3& direction) const;
|
||||
|
||||
glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const;
|
||||
glm::vec3 sphereSurfaceFromOverlay(const glm::vec2& overlay) const;
|
||||
|
@ -93,10 +88,12 @@ public:
|
|||
void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; }
|
||||
|
||||
glm::vec2 getReticlePosition() const;
|
||||
void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true);
|
||||
void setReticlePosition(const glm::vec2& position, bool sendFakeEvent = true);
|
||||
|
||||
glm::vec2 getReticleMaximumPosition() const;
|
||||
|
||||
glm::mat4 getReticleTransform(const glm::mat4& eyePose = glm::mat4(), const glm::vec3& headPosition = glm::vec3()) const;
|
||||
|
||||
ReticleInterface* getReticleInterface() { return _reticleInterface; }
|
||||
|
||||
/// return value - true means the caller should not process the event further
|
||||
|
@ -113,34 +110,38 @@ public:
|
|||
bool getReticleOverDesktop() const;
|
||||
void setReticleOverDesktop(bool value) { _isOverDesktop = value; }
|
||||
|
||||
private:
|
||||
bool _isOverDesktop { true };
|
||||
void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; }
|
||||
void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; _currentFrame = frame; }
|
||||
|
||||
void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov);
|
||||
void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0);
|
||||
void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks);
|
||||
void drawSphereSection(gpu::Batch& batch);
|
||||
signals:
|
||||
void allowMouseCaptureChanged();
|
||||
|
||||
private:
|
||||
glm::mat4 getUiTransform() const;
|
||||
void updateTooltips();
|
||||
|
||||
// Support for hovering and tooltips
|
||||
static EntityItemID _noItemId;
|
||||
EntityItemID _hoverItemId { _noItemId };
|
||||
QString _hoverItemTitle;
|
||||
QString _hoverItemDescription;
|
||||
quint64 _hoverItemEnterUsecs { 0 };
|
||||
DisplayPluginPointer _currentDisplayPlugin;
|
||||
glm::mat4 _currentCamera;
|
||||
uint32_t _currentFrame { 0 };
|
||||
|
||||
float _hmdUIAngularSize { DEFAULT_HMD_UI_VERT_ANGULAR_SIZE };
|
||||
float _textureFov { glm::radians(DEFAULT_HMD_UI_VERT_ANGULAR_SIZE) };
|
||||
float _textureAspectRatio { 1.0f };
|
||||
int _hemiVerticesID { GeometryCache::UNKNOWN_ID };
|
||||
//// Support for hovering and tooltips
|
||||
//static EntityItemID _noItemId;
|
||||
//EntityItemID _hoverItemId { _noItemId };
|
||||
|
||||
float _alpha { 0.0f }; // hidden by default
|
||||
//QString _hoverItemTitle;
|
||||
//QString _hoverItemDescription;
|
||||
//quint64 _hoverItemEnterUsecs { 0 };
|
||||
|
||||
bool _isOverDesktop { true };
|
||||
float _hmdUIAngularSize { glm::degrees(VIRTUAL_UI_TARGET_FOV.y) };
|
||||
float _textureFov { VIRTUAL_UI_TARGET_FOV.y };
|
||||
float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO };
|
||||
|
||||
float _alpha { 1.0f };
|
||||
float _prevAlpha { 1.0f };
|
||||
float _fadeInAlpha { true };
|
||||
float _oculusUIRadius { 1.0f };
|
||||
|
||||
QMap<uint16_t, gpu::TexturePointer> _cursors;
|
||||
|
||||
int _reticleQuad;
|
||||
|
||||
int _previousBorderWidth { -1 };
|
||||
|
@ -167,7 +168,9 @@ private:
|
|||
|
||||
bool _allowMouseCapture { true };
|
||||
|
||||
ReticleInterface* _reticleInterface;
|
||||
bool _fakeMouseEvent { false };
|
||||
|
||||
ReticleInterface* _reticleInterface { nullptr };
|
||||
};
|
||||
|
||||
// Scripting interface available to control the Reticle
|
||||
|
@ -182,7 +185,7 @@ class ReticleInterface : public QObject {
|
|||
Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay)
|
||||
|
||||
public:
|
||||
ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {}
|
||||
ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {}
|
||||
|
||||
Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); }
|
||||
|
||||
|
@ -203,9 +206,7 @@ public:
|
|||
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
|
||||
|
||||
private:
|
||||
ApplicationCompositor* _compositor;
|
||||
CompositorHelper* _compositor;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // hifi_ApplicationCompositor_h
|
||||
#endif // hifi_CompositorHelper_h
|
|
@ -22,11 +22,11 @@ bool NullDisplayPlugin::hasFocus() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
_container->releaseSceneTexture(sceneTexture);
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) {
|
||||
void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
|
||||
_container->releaseOverlayTexture(overlayTexture);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ public:
|
|||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
virtual bool hasFocus() const override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
|
||||
virtual QImage getScreenshot() const override;
|
||||
private:
|
||||
static const QString NAME;
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include <gl/Config.h>
|
||||
#include <gl/GLEscrow.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <CursorManager.h>
|
||||
#include "CompositorHelper.h"
|
||||
|
||||
#if THREADED_PRESENT
|
||||
|
||||
|
@ -184,13 +187,16 @@ private:
|
|||
#endif
|
||||
|
||||
OpenGLDisplayPlugin::OpenGLDisplayPlugin() {
|
||||
_sceneTextureEscrow.setRecycler([this](GLuint texture){
|
||||
_sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){
|
||||
cleanupForSceneTexture(texture);
|
||||
_container->releaseSceneTexture(texture);
|
||||
});
|
||||
_overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) {
|
||||
_container->releaseOverlayTexture(texture);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) {
|
||||
void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) {
|
||||
Lock lock(_mutex);
|
||||
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture));
|
||||
_sceneTextureToFrameIndexMap.remove(sceneTexture);
|
||||
|
@ -198,15 +204,26 @@ void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) {
|
|||
|
||||
|
||||
void OpenGLDisplayPlugin::activate() {
|
||||
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
|
||||
if (!_cursorsData.size()) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
for (const auto iconId : cursorManager.registeredIcons()) {
|
||||
auto& cursorData = _cursorsData[iconId];
|
||||
auto iconPath = cursorManager.getIconImage(iconId);
|
||||
auto image = QImage(iconPath);
|
||||
image = image.mirrored();
|
||||
image = image.convertToFormat(QImage::Format_RGBA8888);
|
||||
cursorData.image = image;
|
||||
cursorData.size = toGlm(image.size());
|
||||
cursorData.hotSpot = vec2(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
|
||||
#if THREADED_PRESENT
|
||||
// Start the present thread if necessary
|
||||
auto presentThread = DependencyManager::get<PresentThread>();
|
||||
if (!presentThread) {
|
||||
auto widget = _container->getPrimaryWidget();
|
||||
|
||||
|
||||
DependencyManager::set<PresentThread>();
|
||||
presentThread = DependencyManager::get<PresentThread>();
|
||||
presentThread->setObjectName("Presentation Thread");
|
||||
|
@ -242,6 +259,7 @@ void OpenGLDisplayPlugin::deactivate() {
|
|||
DisplayPlugin::deactivate();
|
||||
}
|
||||
|
||||
|
||||
void OpenGLDisplayPlugin::customizeContext() {
|
||||
#if THREADED_PRESENT
|
||||
_uncustomized = false;
|
||||
|
@ -250,6 +268,20 @@ void OpenGLDisplayPlugin::customizeContext() {
|
|||
#endif
|
||||
enableVsync();
|
||||
|
||||
for (auto& cursorValue : _cursorsData) {
|
||||
auto& cursorData = cursorValue.second;
|
||||
if (!cursorData.texture) {
|
||||
const auto& image = cursorData.image;
|
||||
glGenTextures(1, &cursorData.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
using namespace oglplus;
|
||||
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||
Context::Disable(Capability::Blend);
|
||||
|
@ -257,10 +289,26 @@ void OpenGLDisplayPlugin::customizeContext() {
|
|||
Context::Disable(Capability::CullFace);
|
||||
|
||||
_program = loadDefaultShader();
|
||||
|
||||
auto uniforms = _program->ActiveUniforms();
|
||||
while (!uniforms.Empty()) {
|
||||
auto uniform = uniforms.Front();
|
||||
if (uniform.Name() == "Projection") {
|
||||
_projectionUniform = uniform.Index();
|
||||
} else if (uniform.Name() == "ModelView") {
|
||||
_modelViewUniform = uniform.Index();
|
||||
}
|
||||
uniforms.Next();
|
||||
}
|
||||
|
||||
_plane = loadPlane(_program);
|
||||
|
||||
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||
_compositeFramebuffer->Init(getRecommendedRenderSize());
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::uncustomizeContext() {
|
||||
_compositeFramebuffer.reset();
|
||||
_program.reset();
|
||||
_plane.reset();
|
||||
}
|
||||
|
@ -308,7 +356,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) {
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
_sceneTextureToFrameIndexMap[sceneTexture] = frameIndex;
|
||||
|
@ -326,15 +374,27 @@ void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t scene
|
|||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::submitOverlayTexture(GLuint overlayTexture, const glm::uvec2& overlaySize) {
|
||||
void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) {
|
||||
// Submit it to the presentation thread via escrow
|
||||
_currentOverlayTexture = overlayTexture;
|
||||
_overlayTextureEscrow.submit(overlayTexture);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::updateTextures() {
|
||||
auto oldSceneTexture = _currentSceneTexture;
|
||||
_currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture);
|
||||
if (oldSceneTexture != _currentSceneTexture) {
|
||||
updateFrameData();
|
||||
}
|
||||
|
||||
_currentOverlayTexture = _overlayTextureEscrow.fetchAndRelease(_currentOverlayTexture);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::updateFrameData() {
|
||||
Lock lock(_mutex);
|
||||
_currentRenderFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
|
||||
}
|
||||
|
||||
|
||||
void OpenGLDisplayPlugin::updateFramerate() {
|
||||
uint64_t now = usecTimestampNow();
|
||||
static uint64_t lastSwapEnd { now };
|
||||
|
@ -346,14 +406,85 @@ void OpenGLDisplayPlugin::updateFramerate() {
|
|||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositeOverlay() {
|
||||
using namespace oglplus;
|
||||
// Overlay draw
|
||||
if (isStereo()) {
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(mat4());
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(mat4());
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
drawUnitQuad();
|
||||
});
|
||||
} else {
|
||||
// Overlay draw
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(mat4());
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(mat4());
|
||||
drawUnitQuad();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositePointer() {
|
||||
using namespace oglplus;
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(compositorHelper->getReticleTransform(glm::mat4()));
|
||||
if (isStereo()) {
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
drawUnitQuad();
|
||||
});
|
||||
} else {
|
||||
drawUnitQuad();
|
||||
}
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(mat4());
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(mat4());
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::compositeLayers() {
|
||||
using namespace oglplus;
|
||||
auto targetRenderSize = getRecommendedRenderSize();
|
||||
if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) {
|
||||
_compositeFramebuffer = std::make_shared<BasicFramebufferWrapper>();
|
||||
_compositeFramebuffer->Init(targetRenderSize);
|
||||
}
|
||||
_compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] {
|
||||
Context::Viewport(targetRenderSize.x, targetRenderSize.y);
|
||||
Context::Clear().DepthBuffer();
|
||||
glBindTexture(GL_TEXTURE_2D, getSceneTextureId());
|
||||
_program->Bind();
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(mat4());
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(mat4());
|
||||
drawUnitQuad();
|
||||
auto overlayTextureId = getOverlayTextureId();
|
||||
if (overlayTextureId) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBindTexture(GL_TEXTURE_2D, overlayTextureId);
|
||||
compositeOverlay();
|
||||
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
if (compositorHelper->getReticleVisible()) {
|
||||
auto& cursorManager = Cursor::Manager::instance();
|
||||
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
|
||||
glBindTexture(GL_TEXTURE_2D, cursorData.texture);
|
||||
compositePointer();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::internalPresent() {
|
||||
using namespace oglplus;
|
||||
uvec2 size = getSurfacePixels();
|
||||
Context::Viewport(size.x, size.y);
|
||||
Context::Clear().DepthBuffer();
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
drawUnitQuad();
|
||||
const uvec2& srcSize = _compositeFramebuffer->size;
|
||||
uvec2 dstSize = getSurfacePixels();
|
||||
_compositeFramebuffer->Bound(FramebufferTarget::Read, [&] {
|
||||
Context::BlitFramebuffer(
|
||||
0, 0, srcSize.x, srcSize.y,
|
||||
0, 0, dstSize.x, dstSize.y,
|
||||
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
||||
});
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
@ -361,6 +492,9 @@ void OpenGLDisplayPlugin::present() {
|
|||
incrementPresentCount();
|
||||
updateTextures();
|
||||
if (_currentSceneTexture) {
|
||||
// Write all layers to a local framebuffer
|
||||
compositeLayers();
|
||||
// Take the composite framebuffer and send it to the output device
|
||||
internalPresent();
|
||||
updateFramerate();
|
||||
}
|
||||
|
@ -379,6 +513,7 @@ float OpenGLDisplayPlugin::presentRate() {
|
|||
|
||||
void OpenGLDisplayPlugin::drawUnitQuad() {
|
||||
_program->Bind();
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
}
|
||||
|
||||
|
@ -436,3 +571,28 @@ void OpenGLDisplayPlugin::enableDeactivate() {
|
|||
_deactivateWait.notify_one();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t OpenGLDisplayPlugin::getSceneTextureId() const {
|
||||
if (!_currentSceneTexture) {
|
||||
return 0;
|
||||
}
|
||||
return gpu::GLBackend::getTextureID(_currentSceneTexture, false);
|
||||
}
|
||||
|
||||
uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const {
|
||||
if (!_currentOverlayTexture) {
|
||||
return 0;
|
||||
}
|
||||
return gpu::GLBackend::getTextureID(_currentOverlayTexture, false);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
|
||||
using namespace oglplus;
|
||||
uvec2 vpSize = _compositeFramebuffer->size;
|
||||
vpSize.x /= 2;
|
||||
uvec2 vpPos;
|
||||
if (eye == Eye::Right) {
|
||||
vpPos.x = vpSize.x;
|
||||
}
|
||||
Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <condition_variable>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
@ -25,6 +26,7 @@ protected:
|
|||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
using Condition = std::condition_variable;
|
||||
using TextureEscrow = GLEscrow<gpu::TexturePointer>;
|
||||
public:
|
||||
OpenGLDisplayPlugin();
|
||||
virtual void activate() override;
|
||||
|
@ -32,8 +34,8 @@ public:
|
|||
virtual void stop() override;
|
||||
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override;
|
||||
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override;
|
||||
virtual float presentRate() override;
|
||||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override {
|
||||
|
@ -50,7 +52,13 @@ protected:
|
|||
#if THREADED_PRESENT
|
||||
friend class PresentThread;
|
||||
#endif
|
||||
uint32_t getSceneTextureId() const;
|
||||
uint32_t getOverlayTextureId() const;
|
||||
|
||||
void compositeLayers();
|
||||
virtual void compositeOverlay();
|
||||
virtual void compositePointer();
|
||||
|
||||
virtual glm::uvec2 getSurfaceSize() const = 0;
|
||||
virtual glm::uvec2 getSurfacePixels() const = 0;
|
||||
|
||||
|
@ -61,32 +69,49 @@ protected:
|
|||
// These functions must only be called on the presentation thread
|
||||
virtual void customizeContext();
|
||||
virtual void uncustomizeContext();
|
||||
virtual void cleanupForSceneTexture(uint32_t sceneTexture);
|
||||
void withMainThreadContext(std::function<void()> f) const;
|
||||
virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture);
|
||||
// Plugin specific functionality to send the composed scene to the output window or device
|
||||
virtual void internalPresent();
|
||||
|
||||
void withMainThreadContext(std::function<void()> f) const;
|
||||
|
||||
void present();
|
||||
void updateTextures();
|
||||
void updateFramerate();
|
||||
void drawUnitQuad();
|
||||
void swapBuffers();
|
||||
// Plugin specific functionality to composite the scene and overlay and present the result
|
||||
virtual void internalPresent();
|
||||
void eyeViewport(Eye eye) const;
|
||||
|
||||
virtual void updateFrameData();
|
||||
|
||||
ProgramPtr _program;
|
||||
int32_t _modelViewUniform { -1 };
|
||||
int32_t _projectionUniform { -1 };
|
||||
ShapeWrapperPtr _plane;
|
||||
|
||||
mutable Mutex _mutex;
|
||||
SimpleMovingAverage _usecsPerFrame { 10 };
|
||||
QMap<uint32_t, uint32_t> _sceneTextureToFrameIndexMap;
|
||||
QMap<gpu::TexturePointer, uint32_t> _sceneTextureToFrameIndexMap;
|
||||
uint32_t _currentRenderFrameIndex { 0 };
|
||||
|
||||
GLuint _currentSceneTexture { 0 };
|
||||
GLuint _currentOverlayTexture { 0 };
|
||||
gpu::TexturePointer _currentSceneTexture;
|
||||
gpu::TexturePointer _currentOverlayTexture;
|
||||
|
||||
GLTextureEscrow _sceneTextureEscrow;
|
||||
TextureEscrow _sceneTextureEscrow;
|
||||
TextureEscrow _overlayTextureEscrow;
|
||||
|
||||
bool _vsyncSupported { false };
|
||||
|
||||
struct CursorData {
|
||||
QImage image;
|
||||
vec2 hotSpot;
|
||||
uvec2 size;
|
||||
uint32_t texture { 0 };
|
||||
};
|
||||
|
||||
std::map<uint16_t, CursorData> _cursorsData;
|
||||
BasicFramebufferWrapperPtr _compositeFramebuffer;
|
||||
|
||||
private:
|
||||
#if THREADED_PRESENT
|
||||
void enableDeactivate();
|
||||
|
|
|
@ -11,16 +11,25 @@
|
|||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <CursorManager.h>
|
||||
|
||||
#include "../Logging.h"
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
static const QString MONO_PREVIEW = "Mono Preview";
|
||||
static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate";
|
||||
static const bool DEFAULT_MONO_VIEW = true;
|
||||
|
||||
glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const {
|
||||
return CompositorHelper::VIRTUAL_SCREEN_SIZE;
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::activate() {
|
||||
_monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW);
|
||||
|
||||
|
@ -30,21 +39,59 @@ void HmdDisplayPlugin::activate() {
|
|||
_container->setBoolSetting("monoPreview", _monoPreview);
|
||||
}, true, _monoPreview);
|
||||
_container->removeMenu(FRAMERATE);
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
Parent::activate();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::deactivate() {
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
Parent::deactivate();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::customizeContext() {
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
Parent::customizeContext();
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
enableVsync(false);
|
||||
_enablePreview = !isVsyncEnabled();
|
||||
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::uncustomizeContext() {
|
||||
_sphereSection.reset();
|
||||
_compositeFramebuffer.reset();
|
||||
Parent::uncustomizeContext();
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::compositeOverlay() {
|
||||
using namespace oglplus;
|
||||
_sphereSection->Use();
|
||||
for_each_eye([&](Eye eye) {
|
||||
eyeViewport(eye);
|
||||
auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1));
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(modelView);
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(_eyeProjections[eye]);
|
||||
_sphereSection->Draw();
|
||||
});
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::compositePointer() {
|
||||
//Mouse Pointer
|
||||
auto compositorHelper = DependencyManager::get<CompositorHelper>();
|
||||
_plane->Use();
|
||||
// Reconstruct the headpose from the eye poses
|
||||
auto headPosition = (vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f;
|
||||
for_each_eye([&](Eye eye) {
|
||||
using namespace oglplus;
|
||||
eyeViewport(eye);
|
||||
auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], headPosition);
|
||||
Uniform<glm::mat4>(*_program, _modelViewUniform).Set(reticleTransform);
|
||||
Uniform<glm::mat4>(*_program, _projectionUniform).Set(_eyeProjections[eye]);
|
||||
_plane->Draw();
|
||||
});
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::internalPresent() {
|
||||
// Composite together the scene, overlay and mouse cursor
|
||||
hmdPresent();
|
||||
|
||||
// screen preview mirroring
|
||||
if (_enablePreview) {
|
||||
auto windowSize = toGlm(_window->size());
|
||||
|
@ -69,19 +116,30 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2;
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportSize.x * (_monoPreview ? 2 : 1), targetViewportSize.y);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportSize.x, targetViewportSize.y);
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
GLenum err = glGetError();
|
||||
Q_ASSERT(0 == err);
|
||||
drawUnitQuad();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
using namespace oglplus;
|
||||
Context::Clear().ColorBuffer();
|
||||
auto sourceSize = _compositeFramebuffer->size;
|
||||
if (_monoPreview) {
|
||||
sourceSize.x /= 2;
|
||||
}
|
||||
_compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] {
|
||||
Context::BlitFramebuffer(
|
||||
0, 0, sourceSize.x, sourceSize.y,
|
||||
targetViewportPosition.x, targetViewportPosition.y,
|
||||
targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y,
|
||||
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
||||
});
|
||||
swapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
Lock lock(_mutex);
|
||||
_renderEyePoses[frameIndex][eye] = pose;
|
||||
}
|
||||
|
||||
void HmdDisplayPlugin::updateFrameData() {
|
||||
Parent::updateFrameData();
|
||||
Lock lock(_mutex);
|
||||
_currentRenderEyePoses = _renderEyePoses[_currentRenderFrameIndex];
|
||||
}
|
|
@ -12,34 +12,41 @@
|
|||
#include "../WindowOpenGLDisplayPlugin.h"
|
||||
|
||||
class HmdDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
using Parent = WindowOpenGLDisplayPlugin;
|
||||
public:
|
||||
bool isHmd() const override final { return true; }
|
||||
float getIPD() const override final { return _ipd; }
|
||||
glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; }
|
||||
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override final { return _eyeProjections[eye]; }
|
||||
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; }
|
||||
glm::uvec2 getRecommendedUiSize() const override final {
|
||||
// FIXME - would be good to have these values sync with ApplicationCompositor in a better way.
|
||||
const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV
|
||||
const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV
|
||||
auto result = uvec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y);
|
||||
return result;
|
||||
}
|
||||
glm::uvec2 getRecommendedUiSize() const override final;
|
||||
glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; }
|
||||
void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
protected:
|
||||
virtual void hmdPresent() = 0;
|
||||
void compositeOverlay() override;
|
||||
void compositePointer() override;
|
||||
void internalPresent() override;
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
void updateFrameData() override;
|
||||
|
||||
std::array<glm::mat4, 2> _eyeOffsets;
|
||||
std::array<glm::mat4, 2> _eyeProjections;
|
||||
glm::mat4 _cullingProjection;
|
||||
glm::uvec2 _renderTargetSize;
|
||||
float _ipd { 0.064f };
|
||||
using EyePoses = std::array<glm::mat4, 2>;
|
||||
QMap<uint32_t, EyePoses> _renderEyePoses;
|
||||
EyePoses _currentRenderEyePoses;
|
||||
|
||||
private:
|
||||
bool _enablePreview { false };
|
||||
bool _monoPreview { true };
|
||||
ShapeWrapperPtr _sphereSection;
|
||||
};
|
||||
|
||||
|
|
|
@ -46,13 +46,15 @@ void main() {
|
|||
|
||||
const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved");
|
||||
|
||||
InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() {
|
||||
}
|
||||
|
||||
void InterleavedStereoDisplayPlugin::customizeContext() {
|
||||
StereoDisplayPlugin::customizeContext();
|
||||
// Set up the stencil buffers? Or use a custom shader?
|
||||
compileProgram(_program, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
|
||||
compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
|
||||
}
|
||||
|
||||
void InterleavedStereoDisplayPlugin::uncustomizeContext() {
|
||||
_interleavedProgram.reset();
|
||||
StereoDisplayPlugin::uncustomizeContext();
|
||||
}
|
||||
|
||||
glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
|
||||
|
@ -64,8 +66,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
|
|||
|
||||
void InterleavedStereoDisplayPlugin::internalPresent() {
|
||||
using namespace oglplus;
|
||||
_program->Bind();
|
||||
auto sceneSize = getRecommendedRenderSize();
|
||||
Uniform<ivec2>(*_program, "textureSize").SetValue(sceneSize);
|
||||
WindowOpenGLDisplayPlugin::internalPresent();
|
||||
_interleavedProgram->Bind();
|
||||
Uniform<ivec2>(*_interleavedProgram, "textureSize").SetValue(sceneSize);
|
||||
auto surfaceSize = getSurfacePixels();
|
||||
Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y);
|
||||
glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color));
|
||||
_plane->Use();
|
||||
_plane->Draw();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,17 +12,17 @@
|
|||
class InterleavedStereoDisplayPlugin : public StereoDisplayPlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InterleavedStereoDisplayPlugin();
|
||||
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual grouping getGrouping() const override { return ADVANCED; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
grouping getGrouping() const override { return ADVANCED; }
|
||||
glm::uvec2 getRecommendedRenderSize() const override;
|
||||
|
||||
protected:
|
||||
// initialize OpenGL context settings needed by the plugin
|
||||
virtual void customizeContext() override;
|
||||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
void internalPresent() override;
|
||||
|
||||
private:
|
||||
ProgramPtr _interleavedProgram;
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
#include "SideBySideStereoDisplayPlugin.h"
|
||||
#include <GLMHelpers.h>
|
||||
#include <CursorManager.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <gl/GLWidget.h>
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
|
||||
|
||||
|
@ -19,5 +23,3 @@ glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
|
|||
result.x *= 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ public:
|
|||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual grouping getGrouping() const override { return ADVANCED; }
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -8,19 +8,18 @@
|
|||
|
||||
#include "StereoDisplayPlugin.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QAction>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <MatrixStack.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
||||
StereoDisplayPlugin::StereoDisplayPlugin() {
|
||||
}
|
||||
#include <gl/GLWidget.h>
|
||||
#include <CursorManager.h>
|
||||
#include "../CompositorHelper.h"
|
||||
|
||||
bool StereoDisplayPlugin::isSupported() const {
|
||||
// FIXME this should attempt to do a scan for supported 3D output
|
||||
|
@ -76,14 +75,16 @@ void StereoDisplayPlugin::activate() {
|
|||
|
||||
_container->removeMenu(FRAMERATE);
|
||||
|
||||
_container->setFullscreen(qApp->primaryScreen());
|
||||
_screen = qApp->primaryScreen();
|
||||
_container->setFullscreen(_screen);
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
}
|
||||
|
||||
void StereoDisplayPlugin::updateScreen() {
|
||||
for (uint32_t i = 0; i < _screenActions.size(); ++i) {
|
||||
if (_screenActions[i]->isChecked()) {
|
||||
_container->setFullscreen(qApp->screens().at(i));
|
||||
_screen = qApp->screens().at(i);
|
||||
_container->setFullscreen(_screen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -100,3 +101,4 @@ void StereoDisplayPlugin::deactivate() {
|
|||
float StereoDisplayPlugin::getRecommendedAspectRatio() const {
|
||||
return aspect(WindowOpenGLDisplayPlugin::getRecommendedRenderSize());
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "../WindowOpenGLDisplayPlugin.h"
|
||||
class QScreen;
|
||||
|
||||
class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
Q_OBJECT
|
||||
using Parent = WindowOpenGLDisplayPlugin;
|
||||
public:
|
||||
StereoDisplayPlugin();
|
||||
virtual bool isStereo() const override final { return true; }
|
||||
virtual bool isSupported() const override final;
|
||||
|
||||
|
@ -33,4 +34,5 @@ public:
|
|||
protected:
|
||||
void updateScreen();
|
||||
float _ipd{ 0.064f };
|
||||
QScreen* _screen;
|
||||
};
|
||||
|
|
|
@ -336,6 +336,33 @@ bool RenderableModelEntityItem::getAnimationFrame() {
|
|||
return newFrame;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::updateModelBounds() {
|
||||
if (!hasModel() || !_model) {
|
||||
return;
|
||||
}
|
||||
bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething();
|
||||
if ((movingOrAnimating ||
|
||||
_needsInitialSimulation ||
|
||||
_model->getTranslation() != getPosition() ||
|
||||
_model->getRotation() != getRotation() ||
|
||||
_model->getRegistrationPoint() != getRegistrationPoint())
|
||||
&& _model->isActive() && _dimensionsInitialized) {
|
||||
_model->setScaleToFit(true, getDimensions());
|
||||
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
|
||||
_model->setRotation(getRotation());
|
||||
_model->setTranslation(getPosition());
|
||||
|
||||
// make sure to simulate so everything gets set up correctly for rendering
|
||||
{
|
||||
PerformanceTimer perfTimer("_model->simulate");
|
||||
_model->simulate(0.0f);
|
||||
}
|
||||
|
||||
_needsInitialSimulation = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items, and it handles
|
||||
// the per frame simulation/update that might be required if the models properties changed.
|
||||
void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||
|
@ -414,27 +441,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething();
|
||||
if ((movingOrAnimating ||
|
||||
_needsInitialSimulation ||
|
||||
_model->getTranslation() != getPosition() ||
|
||||
_model->getRotation() != getRotation() ||
|
||||
_model->getRegistrationPoint() != getRegistrationPoint())
|
||||
&& _model->isActive() && _dimensionsInitialized) {
|
||||
_model->setScaleToFit(true, getDimensions());
|
||||
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
|
||||
_model->setRotation(getRotation());
|
||||
_model->setTranslation(getPosition());
|
||||
|
||||
// make sure to simulate so everything gets set up correctly for rendering
|
||||
{
|
||||
PerformanceTimer perfTimer("_model->simulate");
|
||||
_model->simulate(0.0f);
|
||||
}
|
||||
|
||||
_needsInitialSimulation = false;
|
||||
}
|
||||
updateModelBounds();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -598,7 +605,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
if (type != SHAPE_TYPE_COMPOUND) {
|
||||
ModelEntityItem::computeShapeInfo(info);
|
||||
info.setParams(type, 0.5f * getDimensions());
|
||||
adjustShapeInfoByRegistration(info);
|
||||
} else {
|
||||
updateModelBounds();
|
||||
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
|
||||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
|
@ -690,10 +699,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
AABox box;
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
for (int j = 0; j < _points[i].size(); j++) {
|
||||
// compensate for registraion
|
||||
// compensate for registration
|
||||
_points[i][j] += _model->getOffset();
|
||||
// scale so the collision points match the model points
|
||||
_points[i][j] *= scale;
|
||||
// this next subtraction is done so we can give info the offset, which will cause
|
||||
// the shape-key to change.
|
||||
_points[i][j] -= _model->getOffset();
|
||||
box += _points[i][j];
|
||||
}
|
||||
}
|
||||
|
@ -701,6 +713,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
glm::vec3 collisionModelDimensions = box.getDimensions();
|
||||
info.setParams(type, collisionModelDimensions, _compoundShapeURL);
|
||||
info.setConvexHulls(_points);
|
||||
info.setOffset(_model->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
|
||||
|
||||
|
||||
void updateModelBounds();
|
||||
virtual void render(RenderArgs* args) override;
|
||||
virtual bool supportsDetailedRayIntersection() const override { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
|
|
@ -142,7 +142,7 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
|
|||
glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const {
|
||||
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
|
||||
bool success; // TODO -- Does this actually have to happen in world space?
|
||||
glm::vec3 center = getCenterPosition(success);
|
||||
glm::vec3 center = getCenterPosition(success); // this handles registrationPoint changes
|
||||
glm::vec3 position = getPosition(success);
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
|
||||
|
@ -430,6 +430,13 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const {
|
|||
return SHAPE_TYPE_COMPOUND;
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::updateRegistrationPoint(const glm::vec3& value) {
|
||||
if (value != _registrationPoint) {
|
||||
_meshDirty = true;
|
||||
EntityItem::updateRegistrationPoint(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::isReadyToComputeShape() {
|
||||
_meshLock.lockForRead();
|
||||
if (_meshDirty) {
|
||||
|
@ -1224,10 +1231,16 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorkerAsync() {
|
|||
}
|
||||
|
||||
glm::vec3 collisionModelDimensions = box.getDimensions();
|
||||
QByteArray b64 = _voxelData.toBase64();
|
||||
// include the registrationPoint in the shape key, because the offset is already
|
||||
// included in the points and the shapeManager wont know that the shape has changed.
|
||||
QString shapeKey = QString(_voxelData.toBase64()) + "," +
|
||||
QString::number(_registrationPoint.x) + "," +
|
||||
QString::number(_registrationPoint.y) + "," +
|
||||
QString::number(_registrationPoint.z);
|
||||
_shapeInfoLock.lockForWrite();
|
||||
_shapeInfo.setParams(SHAPE_TYPE_COMPOUND, collisionModelDimensions, QString(b64));
|
||||
_shapeInfo.setParams(SHAPE_TYPE_COMPOUND, collisionModelDimensions, shapeKey);
|
||||
_shapeInfo.setConvexHulls(points);
|
||||
// adjustShapeInfoByRegistration(_shapeInfo);
|
||||
_shapeInfoLock.unlock();
|
||||
|
||||
_meshLock.lockForWrite();
|
||||
|
|
|
@ -116,6 +116,8 @@ public:
|
|||
|
||||
virtual void rebakeMesh();
|
||||
|
||||
virtual void updateRegistrationPoint(const glm::vec3& value);
|
||||
|
||||
private:
|
||||
// The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions
|
||||
// may not match _voxelVolumeSize.
|
||||
|
|
|
@ -677,7 +677,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, updateRegistrationPoint);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
|
@ -1120,7 +1120,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
// these (along with "position" above) affect tree structure
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, updateDimensions);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, updateRegistrationPoint);
|
||||
|
||||
// these (along with all properties above) affect the simulation
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(density, updateDensity);
|
||||
|
@ -1340,6 +1340,15 @@ float EntityItem::getRadius() const {
|
|||
return 0.5f * glm::length(getDimensions());
|
||||
}
|
||||
|
||||
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const {
|
||||
if (_registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
|
||||
glm::mat4 scale = glm::scale(getDimensions());
|
||||
glm::mat4 registration = scale * glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
|
||||
glm::vec3 regTransVec = glm::vec3(registration[3]); // extract position component from matrix
|
||||
info.setOffset(regTransVec);
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::contains(const glm::vec3& point) const {
|
||||
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
|
||||
bool success;
|
||||
|
@ -1348,12 +1357,21 @@ bool EntityItem::contains(const glm::vec3& point) const {
|
|||
} else {
|
||||
ShapeInfo info;
|
||||
info.setParams(getShapeType(), glm::vec3(0.5f));
|
||||
adjustShapeInfoByRegistration(info);
|
||||
return info.contains(worldToEntity(point));
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||
info.setParams(getShapeType(), 0.5f * getDimensions());
|
||||
adjustShapeInfoByRegistration(info);
|
||||
}
|
||||
|
||||
void EntityItem::updateRegistrationPoint(const glm::vec3& value) {
|
||||
if (value != _registrationPoint) {
|
||||
setRegistrationPoint(value);
|
||||
_dirtyFlags |= Simulation::DIRTY_SHAPE;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::updatePosition(const glm::vec3& value) {
|
||||
|
|
|
@ -305,6 +305,7 @@ public:
|
|||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
virtual void adjustShapeInfoByRegistration(ShapeInfo& info) const;
|
||||
virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool isReadyToComputeShape() { return !isDead(); }
|
||||
|
@ -319,6 +320,7 @@ public:
|
|||
virtual void setRotation(glm::quat orientation) { setOrientation(orientation); }
|
||||
|
||||
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
||||
virtual void updateRegistrationPoint(const glm::vec3& value);
|
||||
void updatePosition(const glm::vec3& value);
|
||||
void updatePositionFromNetwork(const glm::vec3& value);
|
||||
void updateDimensions(const glm::vec3& value);
|
||||
|
|
|
@ -42,13 +42,19 @@
|
|||
// in use by the GPU. Fence sync objects are used to moderate the actual release of
|
||||
// resources in either direction.
|
||||
template <
|
||||
typename T,
|
||||
// Only accept numeric types
|
||||
typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type
|
||||
typename T
|
||||
//,
|
||||
//// Only accept numeric types
|
||||
//typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type
|
||||
>
|
||||
class GLEscrow {
|
||||
public:
|
||||
static const uint64_t MAX_UNSIGNALED_TIME = USECS_PER_SECOND / 2;
|
||||
|
||||
const T& invalid() const {
|
||||
static const T INVALID_RESULT;
|
||||
return INVALID_RESULT;
|
||||
}
|
||||
|
||||
struct Item {
|
||||
const T _value;
|
||||
|
@ -133,7 +139,7 @@ public:
|
|||
// or if none is available (which could mean either the submission
|
||||
// list is empty or that the first item on the list isn't yet signaled
|
||||
T fetch() {
|
||||
T result{0};
|
||||
T result = invalid();
|
||||
// On the one hand using try_lock() reduces the chance of blocking the consumer thread,
|
||||
// but if the produce thread is going fast enough, it could effectively
|
||||
// starve the consumer out of ever actually getting resources.
|
||||
|
@ -151,7 +157,7 @@ public:
|
|||
// or if none is available (which could mean either the submission
|
||||
// list is empty or that the first item on the list isn't yet signaled
|
||||
// Also releases any previous texture held by the caller
|
||||
T fetchAndRelease(T oldValue) {
|
||||
T fetchAndRelease(const T& oldValue) {
|
||||
T result = fetch();
|
||||
if (!result) {
|
||||
return oldValue;
|
||||
|
@ -164,7 +170,7 @@ public:
|
|||
|
||||
// If fetch returns a non-zero value, it's the responsibility of the
|
||||
// client to release it at some point
|
||||
void release(T t, GLsync readSync = 0) {
|
||||
void release(const T& t, GLsync readSync = 0) {
|
||||
if (!readSync) {
|
||||
// FIXME should the release and submit actually force the creation of a fence?
|
||||
readSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
@ -240,7 +246,7 @@ private:
|
|||
// inside the locked sections, so it cannot have any latency
|
||||
if (item.signaled()) {
|
||||
// if the sync is signaled, queue it for deletion
|
||||
_trash.push_front(Item(0, item._sync));
|
||||
_trash.push_front(Item(invalid(), item._sync));
|
||||
// And change the stored value to 0 so we don't check it again
|
||||
item._sync = 0;
|
||||
return true;
|
||||
|
@ -259,6 +265,12 @@ private:
|
|||
List _trash;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline const GLuint& GLEscrow<GLuint>::invalid() const {
|
||||
static const GLuint INVALID_RESULT { 0 };
|
||||
return INVALID_RESULT;
|
||||
}
|
||||
|
||||
using GLTextureEscrow = GLEscrow<GLuint>;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -485,7 +485,9 @@ void OffscreenQmlSurface::updateQuick() {
|
|||
if (_render) {
|
||||
QMutexLocker lock(&(_renderer->_mutex));
|
||||
_renderer->post(RENDER);
|
||||
_renderer->_cond.wait(&(_renderer->_mutex));
|
||||
while (!_renderer->_cond.wait(&(_renderer->_mutex), 100)) {
|
||||
qApp->processEvents();
|
||||
}
|
||||
_render = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,17 @@ using namespace oglplus::shapes;
|
|||
static const char * SIMPLE_TEXTURED_VS = R"VS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
uniform mat4 Projection = mat4(1);
|
||||
uniform mat4 ModelView = mat4(1);
|
||||
|
||||
in vec3 Position;
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(Position, 1);
|
||||
vTexCoord = TexCoord;
|
||||
gl_Position = Projection * ModelView * vec4(Position, 1);
|
||||
vTexCoord = TexCoord ;
|
||||
}
|
||||
|
||||
)VS";
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
~GLTexture();
|
||||
};
|
||||
static GLTexture* syncGPUObject(const Texture& texture);
|
||||
static GLuint getTextureID(const TexturePointer& texture);
|
||||
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
|
||||
|
||||
// very specific for now
|
||||
static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object);
|
||||
|
|
|
@ -578,11 +578,16 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
|
||||
|
||||
|
||||
GLuint GLBackend::getTextureID(const TexturePointer& texture) {
|
||||
GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) {
|
||||
if (!texture) {
|
||||
return 0;
|
||||
}
|
||||
GLTexture* object = GLBackend::syncGPUObject(*texture);
|
||||
GLTexture* object { nullptr };
|
||||
if (sync) {
|
||||
object = GLBackend::syncGPUObject(*texture);
|
||||
} else {
|
||||
object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
|
||||
}
|
||||
if (object) {
|
||||
return object->_texture;
|
||||
} else {
|
||||
|
|
0
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
Executable file → Normal file
0
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp
Executable file → Normal file
|
@ -30,38 +30,143 @@
|
|||
|
||||
MessageID AssetClient::_currentID = 0;
|
||||
|
||||
GetMappingRequest::GetMappingRequest(AssetPath path) : _path(path) {
|
||||
};
|
||||
|
||||
void GetMappingRequest::start() {
|
||||
void MappingRequest::start() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
|
||||
return;
|
||||
}
|
||||
doStart();
|
||||
};
|
||||
|
||||
GetMappingRequest::GetMappingRequest(AssetPath path) : _path(path) {
|
||||
};
|
||||
|
||||
void GetMappingRequest::doStart() {
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAssetMapping(_path, [this](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
// read message
|
||||
_error = error;
|
||||
if (!error) {
|
||||
|
||||
// Check cache
|
||||
auto it = assetClient->_mappingCache.constFind(_path);
|
||||
if (it != assetClient->_mappingCache.constEnd()) {
|
||||
_hash = it.value();
|
||||
emit finished(this);
|
||||
return;
|
||||
}
|
||||
|
||||
assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
switch (error) {
|
||||
case AssetServerError::NoError:
|
||||
_error = NoError;
|
||||
break;
|
||||
case AssetServerError::AssetNotFound:
|
||||
_error = NotFound;
|
||||
break;
|
||||
default:
|
||||
_error = UnknownError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_error) {
|
||||
_hash = message->read(SHA256_HASH_LENGTH).toHex();
|
||||
assetClient->_mappingCache[_path] = _hash;
|
||||
}
|
||||
emit finished(this);
|
||||
});
|
||||
};
|
||||
|
||||
GetAllMappingsRequest::GetAllMappingsRequest() {
|
||||
};
|
||||
|
||||
void GetAllMappingsRequest::doStart() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAllAssetMappings([this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
switch (error) {
|
||||
case AssetServerError::NoError:
|
||||
_error = NoError;
|
||||
break;
|
||||
default:
|
||||
_error = UnknownError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!error) {
|
||||
int numberOfMappings;
|
||||
message->readPrimitive(&numberOfMappings);
|
||||
assetClient->_mappingCache.clear();
|
||||
for (auto i = 0; i < numberOfMappings; ++i) {
|
||||
auto path = message->readString();
|
||||
auto hash = message->readString();
|
||||
_mappings[path] = hash;
|
||||
assetClient->_mappingCache[path] = hash;
|
||||
}
|
||||
}
|
||||
emit finished(this);
|
||||
});
|
||||
};
|
||||
|
||||
SetMappingRequest::SetMappingRequest(AssetPath path, AssetHash hash) : _path(path), _hash(hash) {
|
||||
};
|
||||
|
||||
void SetMappingRequest::start() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
void SetMappingRequest::doStart() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->setAssetMapping(_path, _hash, [this](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
// read message
|
||||
_error = error;
|
||||
assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
switch (error) {
|
||||
case AssetServerError::NoError:
|
||||
_error = NoError;
|
||||
break;
|
||||
case AssetServerError::PermissionDenied:
|
||||
_error = PermissionDenied;
|
||||
break;
|
||||
default:
|
||||
_error = UnknownError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
assetClient->_mappingCache[_path] = _hash;
|
||||
}
|
||||
emit finished(this);
|
||||
});
|
||||
};
|
||||
|
||||
DeleteMappingRequest::DeleteMappingRequest(AssetPath path) : _path(path) {
|
||||
};
|
||||
|
||||
void DeleteMappingRequest::doStart() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->deleteAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
switch (error) {
|
||||
case AssetServerError::NoError:
|
||||
_error = NoError;
|
||||
break;
|
||||
case AssetServerError::PermissionDenied:
|
||||
_error = PermissionDenied;
|
||||
break;
|
||||
default:
|
||||
_error = UnknownError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
assetClient->_mappingCache.remove(_path);
|
||||
}
|
||||
emit finished(this);
|
||||
});
|
||||
};
|
||||
|
@ -127,6 +232,8 @@ void AssetClient::clearCache() {
|
|||
return;
|
||||
}
|
||||
|
||||
_mappingCache.clear();
|
||||
|
||||
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||
qDebug() << "AssetClient::clearCache(): Clearing disk cache.";
|
||||
cache->clear();
|
||||
|
@ -174,11 +281,18 @@ bool haveAssetServer() {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
GetMappingRequest* AssetClient::createGetMappingRequest(const AssetPath& path) {
|
||||
return new GetMappingRequest(path);
|
||||
}
|
||||
|
||||
GetAllMappingsRequest* AssetClient::createGetAllMappingsRequest() {
|
||||
return new GetAllMappingsRequest();
|
||||
}
|
||||
|
||||
DeleteMappingRequest* AssetClient::createDeleteMappingRequest(const AssetPath& path) {
|
||||
return new DeleteMappingRequest(path);
|
||||
}
|
||||
|
||||
SetMappingRequest* AssetClient::createSetMappingRequest(const AssetPath& path, const AssetHash& hash) {
|
||||
return new SetMappingRequest(path, hash);
|
||||
}
|
||||
|
@ -370,7 +484,31 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
|
|||
}
|
||||
}
|
||||
|
||||
bool AssetClient::getAssetMapping(const AssetHash& hash, MappingOperationCallback callback) {
|
||||
bool AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (assetServer) {
|
||||
auto packetList = NLPacketList::create(PacketType::AssetMappingOperation, QByteArray(), true, true);
|
||||
|
||||
auto messageID = ++_currentID;
|
||||
packetList->writePrimitive(messageID);
|
||||
|
||||
packetList->writePrimitive(AssetMappingOperationType::Get);
|
||||
|
||||
packetList->writeString(path);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *assetServer);
|
||||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -380,9 +518,31 @@ bool AssetClient::getAssetMapping(const AssetHash& hash, MappingOperationCallbac
|
|||
auto messageID = ++_currentID;
|
||||
packetList->writePrimitive(messageID);
|
||||
|
||||
packetList->writePrimitive(AssetMappingOperationType::Get);
|
||||
packetList->writePrimitive(AssetMappingOperationType::GetAll);
|
||||
|
||||
packetList->writeString(hash.toUtf8());
|
||||
nodeList->sendPacketList(std::move(packetList), *assetServer);
|
||||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::deleteAssetMapping(const AssetPath& path, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
if (assetServer) {
|
||||
auto packetList = NLPacketList::create(PacketType::AssetMappingOperation, QByteArray(), true, true);
|
||||
|
||||
auto messageID = ++_currentID;
|
||||
packetList->writePrimitive(messageID);
|
||||
|
||||
packetList->writePrimitive(AssetMappingOperationType::Delete);
|
||||
|
||||
packetList->writeString(path);
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *assetServer);
|
||||
|
||||
|
@ -407,7 +567,7 @@ bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, Ma
|
|||
packetList->writePrimitive(AssetMappingOperationType::Set);
|
||||
|
||||
packetList->writeString(path.toUtf8());
|
||||
packetList->writeString(hash.toUtf8());
|
||||
packetList->write(QByteArray::fromHex(hash.toUtf8()));
|
||||
|
||||
nodeList->sendPacketList(std::move(packetList), *assetServer);
|
||||
|
||||
|
@ -518,6 +678,18 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) {
|
|||
messageMapIt->second.clear();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto messageMapIt = _pendingMappingRequests.find(node);
|
||||
if (messageMapIt != _pendingMappingRequests.end()) {
|
||||
for (const auto& value : messageMapIt->second) {
|
||||
value.second(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
}
|
||||
messageMapIt->second.clear();
|
||||
}
|
||||
}
|
||||
|
||||
_mappingCache.clear();
|
||||
}
|
||||
|
||||
void AssetScriptingInterface::uploadData(QString data, QString extension, QScriptValue callback) {
|
||||
|
@ -618,5 +790,41 @@ void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) {
|
|||
request->start();
|
||||
}
|
||||
|
||||
void AssetScriptingInterface::getAllMappings(QString path, QScriptValue callback) {
|
||||
void AssetScriptingInterface::deleteMapping(QString path, QScriptValue callback) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto request = assetClient->createDeleteMappingRequest(path);
|
||||
|
||||
connect(request, &DeleteMappingRequest::finished, this, [this, callback](DeleteMappingRequest* request) mutable {
|
||||
QScriptValueList args { uint8_t(request->getError()) };
|
||||
|
||||
callback.call(_engine->currentContext()->thisObject(), args);
|
||||
|
||||
request->deleteLater();
|
||||
|
||||
});
|
||||
|
||||
request->start();
|
||||
}
|
||||
|
||||
void AssetScriptingInterface::getAllMappings(QScriptValue callback) {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
auto request = assetClient->createGetAllMappingsRequest();
|
||||
|
||||
connect(request, &GetAllMappingsRequest::finished, this, [this, callback](GetAllMappingsRequest* request) mutable {
|
||||
auto mappings = request->getMappings();
|
||||
auto map = callback.engine()->newObject();
|
||||
|
||||
for (auto& kv : mappings ) {
|
||||
map.setProperty(kv.first, kv.second);
|
||||
}
|
||||
|
||||
QScriptValueList args { uint8_t(request->getError()), map };
|
||||
|
||||
callback.call(_engine->currentContext()->thisObject(), args);
|
||||
|
||||
request->deleteLater();
|
||||
|
||||
});
|
||||
|
||||
request->start();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <QString>
|
||||
#include <QScriptValue>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
|
@ -41,46 +43,95 @@ using GetInfoCallback = std::function<void(bool responseReceived, AssetServerErr
|
|||
using UploadResultCallback = std::function<void(bool responseReceived, AssetServerError serverError, const QString& hash)>;
|
||||
using ProgressCallback = std::function<void(qint64 totalReceived, qint64 total)>;
|
||||
|
||||
class MappingRequest : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Error {
|
||||
NoError,
|
||||
NotFound,
|
||||
NetworkError,
|
||||
PermissionDenied,
|
||||
UnknownError
|
||||
};
|
||||
|
||||
class GetMappingRequest : public QObject {
|
||||
Q_INVOKABLE void start();
|
||||
Error getError() const { return _error; }
|
||||
|
||||
protected:
|
||||
Error _error { NoError };
|
||||
|
||||
private:
|
||||
virtual void doStart() = 0;
|
||||
};
|
||||
|
||||
|
||||
class GetMappingRequest : public MappingRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GetMappingRequest(AssetPath path);
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
AssetHash getHash() { return _hash; }
|
||||
AssetServerError getError() { return _error; }
|
||||
AssetHash getHash() const { return _hash; }
|
||||
|
||||
signals:
|
||||
void finished(GetMappingRequest* thisRequest);
|
||||
|
||||
private:
|
||||
virtual void doStart() override;
|
||||
|
||||
AssetPath _path;
|
||||
AssetHash _hash;
|
||||
AssetServerError _error { AssetServerError::NoError };
|
||||
};
|
||||
|
||||
|
||||
class SetMappingRequest : public QObject {
|
||||
class SetMappingRequest : public MappingRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SetMappingRequest(AssetPath path, AssetHash hash);
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
AssetHash getHash() { return _hash; }
|
||||
AssetServerError getError() { return _error; }
|
||||
AssetHash getHash() const { return _hash; }
|
||||
|
||||
signals:
|
||||
void finished(SetMappingRequest* thisRequest);
|
||||
|
||||
private:
|
||||
virtual void doStart() override;
|
||||
|
||||
AssetPath _path;
|
||||
AssetHash _hash;
|
||||
AssetServerError _error { AssetServerError::NoError };
|
||||
};
|
||||
|
||||
class DeleteMappingRequest : public MappingRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DeleteMappingRequest(AssetPath path);
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
signals:
|
||||
void finished(DeleteMappingRequest* thisRequest);
|
||||
|
||||
private:
|
||||
virtual void doStart() override;
|
||||
|
||||
AssetPath _path;
|
||||
};
|
||||
|
||||
class GetAllMappingsRequest : public MappingRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GetAllMappingsRequest();
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
AssetMapping getMappings() const { return _mappings; }
|
||||
|
||||
signals:
|
||||
void finished(GetAllMappingsRequest* thisRequest);
|
||||
|
||||
private:
|
||||
virtual void doStart() override;
|
||||
|
||||
std::map<AssetPath, AssetHash> _mappings;
|
||||
};
|
||||
|
||||
class AssetClient : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -88,6 +139,8 @@ public:
|
|||
AssetClient();
|
||||
|
||||
Q_INVOKABLE GetMappingRequest* createGetMappingRequest(const AssetPath& path);
|
||||
Q_INVOKABLE GetAllMappingsRequest* createGetAllMappingsRequest();
|
||||
Q_INVOKABLE DeleteMappingRequest* createDeleteMappingRequest(const AssetPath& path);
|
||||
Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash);
|
||||
Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash);
|
||||
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
||||
|
@ -109,8 +162,9 @@ private slots:
|
|||
|
||||
private:
|
||||
bool getAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
|
||||
bool getAllAssetMappings(MappingOperationCallback callback);
|
||||
bool setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback);
|
||||
bool deleteAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
|
||||
bool deleteAssetMapping(const AssetPath& path, MappingOperationCallback callback);
|
||||
|
||||
bool getAssetInfo(const QString& hash, GetInfoCallback callback);
|
||||
bool getAsset(const QString& hash, DataOffset start, DataOffset end,
|
||||
|
@ -127,11 +181,15 @@ private:
|
|||
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetAssetCallbacks>> _pendingRequests;
|
||||
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, GetInfoCallback>> _pendingInfoRequests;
|
||||
std::unordered_map<SharedNodePointer, std::unordered_map<MessageID, UploadResultCallback>> _pendingUploads;
|
||||
|
||||
QHash<QString, QString> _mappingCache;
|
||||
|
||||
friend class AssetRequest;
|
||||
friend class AssetUpload;
|
||||
friend class GetMappingRequest;
|
||||
friend class GetAllMappingsRequest;
|
||||
friend class SetMappingRequest;
|
||||
friend class DeleteMappingRequest;
|
||||
};
|
||||
|
||||
|
||||
|
@ -144,7 +202,8 @@ public:
|
|||
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
|
||||
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
|
||||
Q_INVOKABLE void getMapping(QString path, QScriptValue callback);
|
||||
Q_INVOKABLE void getAllMappings(QString path, QScriptValue callback);
|
||||
Q_INVOKABLE void deleteMapping(QString path, QScriptValue callback);
|
||||
Q_INVOKABLE void getAllMappings(QScriptValue callback);
|
||||
protected:
|
||||
QSet<AssetRequest*> _pendingRequests;
|
||||
QScriptEngine* _engine;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
|
@ -22,6 +24,7 @@ using DataOffset = int64_t;
|
|||
|
||||
using AssetPath = QString;
|
||||
using AssetHash = QString;
|
||||
using AssetMapping = std::map<AssetPath, AssetHash>;
|
||||
|
||||
const size_t SHA256_HASH_LENGTH = 32;
|
||||
const size_t SHA256_HASH_HEX_LENGTH = 64;
|
||||
|
@ -38,6 +41,7 @@ enum AssetServerError : uint8_t {
|
|||
|
||||
enum AssetMappingOperationType : uint8_t {
|
||||
Get = 0,
|
||||
GetAll,
|
||||
Set,
|
||||
Delete
|
||||
};
|
||||
|
|
|
@ -79,20 +79,29 @@ void MessagesClient::handleMessagesPacket(QSharedPointer<ReceivedMessage> receiv
|
|||
QString channel, message;
|
||||
QUuid senderID;
|
||||
decodeMessagesPacket(receivedMessage, channel, message, senderID);
|
||||
emit messageReceived(channel, message, senderID);
|
||||
emit messageReceived(channel, message, senderID, false);
|
||||
}
|
||||
|
||||
void MessagesClient::sendMessage(QString channel, QString message) {
|
||||
void MessagesClient::sendMessage(QString channel, QString message, bool localOnly) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer);
|
||||
|
||||
if (messagesMixer) {
|
||||
if (localOnly) {
|
||||
QUuid senderID = nodeList->getSessionUUID();
|
||||
auto packetList = encodeMessagesPacket(channel, message, senderID);
|
||||
nodeList->sendPacketList(std::move(packetList), *messagesMixer);
|
||||
emit messageReceived(channel, message, senderID, true);
|
||||
} else {
|
||||
SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer);
|
||||
|
||||
if (messagesMixer) {
|
||||
QUuid senderID = nodeList->getSessionUUID();
|
||||
auto packetList = encodeMessagesPacket(channel, message, senderID);
|
||||
nodeList->sendPacketList(std::move(packetList), *messagesMixer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesClient::sendLocalMessage(QString channel, QString message) {
|
||||
sendMessage(channel, message, true);
|
||||
}
|
||||
|
||||
void MessagesClient::subscribe(QString channel) {
|
||||
_subscribedChannels << channel;
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -123,4 +132,4 @@ void MessagesClient::handleNodeActivated(SharedNodePointer node) {
|
|||
subscribe(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ public:
|
|||
|
||||
Q_INVOKABLE void init();
|
||||
|
||||
Q_INVOKABLE void sendMessage(QString channel, QString message);
|
||||
Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false);
|
||||
Q_INVOKABLE void sendLocalMessage(QString channel, QString message);
|
||||
Q_INVOKABLE void subscribe(QString channel);
|
||||
Q_INVOKABLE void unsubscribe(QString channel);
|
||||
|
||||
|
@ -38,7 +39,7 @@ public:
|
|||
|
||||
|
||||
signals:
|
||||
void messageReceived(QString channel, QString message, QUuid senderUUID);
|
||||
void messageReceived(QString channel, QString message, QUuid senderUUID, bool localOnly);
|
||||
|
||||
private slots:
|
||||
void handleMessagesPacket(QSharedPointer<ReceivedMessage> receivedMessage, SharedNodePointer senderNode);
|
||||
|
|
|
@ -157,7 +157,7 @@ qint64 BasePacket::writeString(const QString& string) {
|
|||
}
|
||||
|
||||
QString BasePacket::readString() {
|
||||
uint32_t size {};
|
||||
uint32_t size;
|
||||
readPrimitive(&size);
|
||||
auto string = QString::fromUtf8(getPayload() + pos(), size);
|
||||
seek(pos() + size);
|
||||
|
|
|
@ -106,11 +106,11 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (shape && type != SHAPE_TYPE_COMPOUND) {
|
||||
if (shape) {
|
||||
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||
// this shape has an offset, which we support by wrapping the true shape
|
||||
// in a btCompoundShape with a local transform
|
||||
auto compound = new btCompoundShape();
|
||||
auto compound = new btCompoundShape();
|
||||
btTransform trans;
|
||||
trans.setIdentity();
|
||||
trans.setOrigin(glmToBullet(info.getOffset()));
|
||||
|
|
|
@ -50,6 +50,11 @@ class QWindow;
|
|||
|
||||
#define AVERAGE_HUMAN_IPD 0.064f
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
using TexturePointer = std::shared_ptr<Texture>;
|
||||
}
|
||||
|
||||
class DisplayPlugin : public Plugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -70,12 +75,12 @@ public:
|
|||
/**
|
||||
* Sends the scene texture to the display plugin.
|
||||
*/
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) = 0;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0;
|
||||
|
||||
/**
|
||||
* Sends the scene texture to the display plugin.
|
||||
*/
|
||||
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) = 0;
|
||||
virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0;
|
||||
|
||||
// Does the rendering surface have current focus?
|
||||
virtual bool hasFocus() const = 0;
|
||||
|
|
|
@ -23,6 +23,11 @@ class QWindow;
|
|||
|
||||
class DisplayPlugin;
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
using TexturePointer = std::shared_ptr<Texture>;
|
||||
}
|
||||
|
||||
class PluginContainer {
|
||||
public:
|
||||
static PluginContainer& getInstance();
|
||||
|
@ -39,8 +44,8 @@ public:
|
|||
virtual void showDisplayPluginsTools() = 0;
|
||||
virtual void requestReset() = 0;
|
||||
virtual bool makeRenderingContextCurrent() = 0;
|
||||
virtual void releaseSceneTexture(uint32_t texture) = 0;
|
||||
virtual void releaseOverlayTexture(uint32_t texture) = 0;
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0;
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0;
|
||||
virtual GLWidget* getPrimaryWidget() = 0;
|
||||
virtual QWindow* getPrimaryWindow() = 0;
|
||||
virtual QOpenGLContext* getPrimaryContext() = 0;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
set(TARGET_NAME script-engine)
|
||||
setup_hifi_library(Gui Network Script WebSockets Widgets)
|
||||
link_hifi_libraries(shared networking octree gpu procedural model model-networking recording avatars fbx entities controllers animation audio physics)
|
||||
link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics)
|
||||
|
|
|
@ -13,20 +13,7 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
#include "MenuItemProperties.h"
|
||||
|
||||
MenuItemProperties::MenuItemProperties() :
|
||||
menuName(""),
|
||||
menuItemName(""),
|
||||
shortcutKey(""),
|
||||
shortcutKeyEvent(),
|
||||
shortcutKeySequence(),
|
||||
position(UNSPECIFIED_POSITION),
|
||||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(false),
|
||||
isChecked(false),
|
||||
isSeparator(false)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const QString& shortcutKey, bool checkable, bool checked, bool separator) :
|
||||
|
@ -35,9 +22,6 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m
|
|||
shortcutKey(shortcutKey),
|
||||
shortcutKeyEvent(),
|
||||
shortcutKeySequence(shortcutKey),
|
||||
position(UNSPECIFIED_POSITION),
|
||||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(checkable),
|
||||
isChecked(checked),
|
||||
isSeparator(separator)
|
||||
|
@ -48,12 +32,8 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m
|
|||
const KeyEvent& shortcutKeyEvent, bool checkable, bool checked, bool separator) :
|
||||
menuName(menuName),
|
||||
menuItemName(menuItemName),
|
||||
shortcutKey(""),
|
||||
shortcutKeyEvent(shortcutKeyEvent),
|
||||
shortcutKeySequence(shortcutKeyEvent),
|
||||
position(UNSPECIFIED_POSITION),
|
||||
beforeItem(""),
|
||||
afterItem(""),
|
||||
isCheckable(checkable),
|
||||
isChecked(checked),
|
||||
isSeparator(separator)
|
||||
|
|
|
@ -14,35 +14,35 @@
|
|||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <ui/Menu.h>
|
||||
#include "KeyEvent.h"
|
||||
|
||||
const int UNSPECIFIED_POSITION = -1;
|
||||
|
||||
class MenuItemProperties {
|
||||
public:
|
||||
MenuItemProperties();
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
MenuItemProperties() {}
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const QString& shortcutKey = QString(""), bool checkable = false, bool checked = false, bool separator = false);
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
MenuItemProperties(const QString& menuName, const QString& menuItemName,
|
||||
const KeyEvent& shortcutKeyEvent, bool checkable = false, bool checked = false, bool separator = false);
|
||||
|
||||
QString menuName;
|
||||
QString menuItemName;
|
||||
|
||||
|
||||
// Shortcut key items: in order of priority
|
||||
QString shortcutKey;
|
||||
KeyEvent shortcutKeyEvent;
|
||||
QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above
|
||||
|
||||
// location related items: in order of priority
|
||||
int position;
|
||||
int position { ui::Menu::UNSPECIFIED_POSITION };
|
||||
QString beforeItem;
|
||||
QString afterItem;
|
||||
|
||||
// other properties
|
||||
bool isCheckable;
|
||||
bool isChecked;
|
||||
bool isSeparator;
|
||||
bool isCheckable { false };
|
||||
bool isChecked { false };
|
||||
bool isSeparator { false };
|
||||
|
||||
QString grouping; /// Either: "", "Advanced", or "Developer"
|
||||
};
|
||||
|
|
|
@ -44,6 +44,10 @@ namespace Cursor {
|
|||
return instance;
|
||||
}
|
||||
|
||||
QList<uint16_t> Manager::registeredIcons() const {
|
||||
return ICONS.keys();
|
||||
}
|
||||
|
||||
uint8_t Manager::getCount() {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace Cursor {
|
|||
void setScale(float scale);
|
||||
Instance* getCursor(uint8_t index = 0);
|
||||
uint16_t registerIcon(const QString& path);
|
||||
QList<uint16_t> registeredIcons() const;
|
||||
const QString& getIconImage(uint16_t icon);
|
||||
private:
|
||||
float _scale{ 1.0f };
|
||||
|
|
|
@ -217,6 +217,13 @@ QmlWindowClass::QmlWindowClass(QObject* qmlWindow)
|
|||
qDebug() << "Created window with ID " << _windowId;
|
||||
Q_ASSERT(_qmlWindow);
|
||||
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow));
|
||||
// Forward messages received from QML on to the script
|
||||
connect(_qmlWindow, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(const QVariant&)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QmlWindowClass::sendToQml(const QVariant& message) {
|
||||
// Forward messages received from the script on to QML
|
||||
QMetaObject::invokeMethod(asQuickItem(), "fromScript", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||
}
|
||||
|
||||
QmlWindowClass::~QmlWindowClass() {
|
||||
|
|
|
@ -67,17 +67,23 @@ public slots:
|
|||
|
||||
void setTitle(const QString& title);
|
||||
|
||||
|
||||
// Ugh.... do not want to do
|
||||
Q_INVOKABLE void raise();
|
||||
Q_INVOKABLE void close();
|
||||
Q_INVOKABLE int getWindowId() const { return _windowId; };
|
||||
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
|
||||
|
||||
// Scripts can use this to send a message to the QML object
|
||||
void sendToQml(const QVariant& message);
|
||||
|
||||
signals:
|
||||
void visibilityChanged(bool visible); // Tool window
|
||||
void moved(glm::vec2 position);
|
||||
void resized(QSizeF size);
|
||||
void closed();
|
||||
// Scripts can connect to this signal to receive messages from the QML object
|
||||
void fromQml(const QVariant& message);
|
||||
|
||||
protected slots:
|
||||
void hasClosed();
|
||||
|
|
11
libraries/ui/src/ui/Logging.cpp
Normal file
11
libraries/ui/src/ui/Logging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2016/03/01
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(uiLogging, "hifi.ui")
|
16
libraries/ui/src/ui/Logging.h
Normal file
16
libraries/ui/src/ui/Logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Controllers_Logging_h
|
||||
#define hifi_Controllers_Logging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(uiLogging)
|
||||
|
||||
#endif
|
561
libraries/ui/src/ui/Menu.cpp
Normal file
561
libraries/ui/src/ui/Menu.cpp
Normal file
|
@ -0,0 +1,561 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtWidgets/QShortcut>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "../VrMenu.h"
|
||||
#include "Logging.h"
|
||||
|
||||
using namespace ui;
|
||||
static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu";
|
||||
|
||||
Menu* Menu::getInstance() {
|
||||
static Menu* instance = globalInstance<Menu>(MENU_PROPERTY_NAME);
|
||||
return instance;
|
||||
}
|
||||
|
||||
Menu::Menu() {
|
||||
}
|
||||
|
||||
void Menu::toggleAdvancedMenus() {
|
||||
setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced"));
|
||||
}
|
||||
|
||||
void Menu::toggleDeveloperMenus() {
|
||||
setGroupingIsVisible("Developer", !getGroupingIsVisible("Developer"));
|
||||
}
|
||||
|
||||
void Menu::loadSettings() {
|
||||
scanMenuBar(&Menu::loadAction);
|
||||
}
|
||||
|
||||
void Menu::saveSettings() {
|
||||
scanMenuBar(&Menu::saveAction);
|
||||
}
|
||||
|
||||
void Menu::loadAction(Settings& settings, QAction& action) {
|
||||
if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) {
|
||||
action.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::saveAction(Settings& settings, QAction& action) {
|
||||
settings.setValue(action.text(), action.isChecked());
|
||||
}
|
||||
|
||||
void Menu::scanMenuBar(settingsAction modifySetting) {
|
||||
Settings settings;
|
||||
foreach (QMenu* menu, findChildren<QMenu*>()) {
|
||||
scanMenu(*menu, modifySetting, settings);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings) {
|
||||
settings.beginGroup(menu.title());
|
||||
foreach (QAction* action, menu.actions()) {
|
||||
if (action->menu()) {
|
||||
scanMenu(*action->menu(), modifySetting, settings);
|
||||
} else if (action->isCheckable()) {
|
||||
modifySetting(settings, *action);
|
||||
}
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation, const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
QAction* separator;
|
||||
QAction* separatorText;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
if (actionBefore) {
|
||||
separator = new QAction("",destinationMenu);
|
||||
destinationMenu->insertAction(actionBefore, separator);
|
||||
separator->setSeparator(true);
|
||||
|
||||
separatorText = new QAction(actionName,destinationMenu);
|
||||
separatorText->setEnabled(false);
|
||||
destinationMenu->insertAction(actionBefore, separatorText);
|
||||
|
||||
} else {
|
||||
separator = destinationMenu->addSeparator();
|
||||
separatorText = destinationMenu->addAction(actionName);
|
||||
separatorText->setEnabled(false);
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << separator;
|
||||
_groupingActions[grouping] << separatorText;
|
||||
bool isVisible = getGroupingIsVisible(grouping);
|
||||
separator->setVisible(isVisible);
|
||||
separatorText->setVisible(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const QObject* receiver,
|
||||
const char* member,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* action = NULL;
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
|
||||
if (!actionBefore) {
|
||||
if (receiver && member) {
|
||||
action = destinationMenu->addAction(actionName, receiver, member, shortcut);
|
||||
} else {
|
||||
action = destinationMenu->addAction(actionName);
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
} else {
|
||||
action = new QAction(actionName, destinationMenu);
|
||||
action->setShortcut(shortcut);
|
||||
destinationMenu->insertAction(actionBefore, action);
|
||||
|
||||
if (receiver && member) {
|
||||
connect(action, SIGNAL(triggered()), receiver, member);
|
||||
}
|
||||
}
|
||||
action->setMenuRole(role);
|
||||
|
||||
_actionHash.insert(actionName, action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
QAction::MenuRole role,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
QAction* actionBefore = NULL;
|
||||
|
||||
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
|
||||
actionBefore = destinationMenu->actions()[menuItemLocation];
|
||||
}
|
||||
|
||||
if (!actionName.isEmpty()) {
|
||||
action->setText(actionName);
|
||||
}
|
||||
|
||||
if (shortcut != 0) {
|
||||
action->setShortcut(shortcut);
|
||||
}
|
||||
|
||||
if (role != QAction::NoRole) {
|
||||
action->setMenuRole(role);
|
||||
}
|
||||
|
||||
if (!actionBefore) {
|
||||
destinationMenu->addAction(action);
|
||||
} else {
|
||||
destinationMenu->insertAction(actionBefore, action);
|
||||
}
|
||||
|
||||
_actionHash.insert(action->text(), action);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut,
|
||||
const bool checked,
|
||||
const QObject* receiver,
|
||||
const char* member,
|
||||
int menuItemLocation,
|
||||
const QString& grouping) {
|
||||
|
||||
QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member,
|
||||
QAction::NoRole, menuItemLocation);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(checked);
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
|
||||
auto action = _actionHash.value(actionName);
|
||||
menu->removeAction(action);
|
||||
_actionHash.remove(actionName);
|
||||
for (auto& grouping : _groupingActions) {
|
||||
grouping.remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, menuOption),
|
||||
Q_ARG(bool, isChecked));
|
||||
return;
|
||||
}
|
||||
QAction* menu = _actionHash.value(menuOption);
|
||||
if (menu) {
|
||||
menu->setChecked(isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::isOptionChecked(const QString& menuOption) const {
|
||||
const QAction* menu = _actionHash.value(menuOption);
|
||||
if (menu) {
|
||||
return menu->isChecked();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::triggerOption(const QString& menuOption) {
|
||||
QAction* action = _actionHash.value(menuOption);
|
||||
if (action) {
|
||||
action->trigger();
|
||||
} else {
|
||||
qCDebug(uiLogging) << "NULL Action for menuOption '" << menuOption << "'";
|
||||
}
|
||||
}
|
||||
|
||||
QAction* Menu::getActionForOption(const QString& menuOption) {
|
||||
return _actionHash.value(menuOption);
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QList<QAction*> menuActions;
|
||||
if (menu) {
|
||||
menuActions = menu->actions();
|
||||
} else {
|
||||
menuActions = actions();
|
||||
}
|
||||
|
||||
foreach (QAction* menuAction, menuActions) {
|
||||
QString actionText = menuAction->text();
|
||||
if (menuName == menuAction->text()) {
|
||||
return menuAction;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) {
|
||||
QAction* action = getActionFromName(menuName, menu);
|
||||
if (action) {
|
||||
return MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
parent = menu;
|
||||
finalMenuPart = menuTreePart.trimmed();
|
||||
menu = getSubMenuFromName(finalMenuPart, parent);
|
||||
if (!menu) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
MenuWrapper* Menu::getMenu(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
int item = 0;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), parent);
|
||||
if (!menu) {
|
||||
break;
|
||||
}
|
||||
parent = menu;
|
||||
item++;
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
QAction* Menu::getMenuAction(const QString& menuName) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* parent = NULL;
|
||||
QAction* action = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
action = getActionFromName(menuTreePart.trimmed(), parent);
|
||||
if (!action) {
|
||||
break;
|
||||
}
|
||||
parent = MenuWrapper::fromMenu(action->menu());
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) {
|
||||
int position = 0;
|
||||
foreach(QAction* action, menu->actions()) {
|
||||
if (action->text() == searchMenuItem) {
|
||||
return position;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
return UNSPECIFIED_POSITION; // not found
|
||||
}
|
||||
|
||||
int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) {
|
||||
QList<QAction*> menuActions = menu->actions();
|
||||
if (requestedPosition > 1 && requestedPosition < menuActions.size()) {
|
||||
QAction* beforeRequested = menuActions[requestedPosition - 1];
|
||||
if (beforeRequested->isSeparator()) {
|
||||
requestedPosition--;
|
||||
}
|
||||
}
|
||||
return requestedPosition;
|
||||
}
|
||||
|
||||
bool Menu::_isSomeSubmenuShown = false;
|
||||
|
||||
MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) {
|
||||
QStringList menuTree = menuName.split(">");
|
||||
MenuWrapper* addTo = NULL;
|
||||
MenuWrapper* menu = NULL;
|
||||
foreach (QString menuTreePart, menuTree) {
|
||||
menu = getSubMenuFromName(menuTreePart.trimmed(), addTo);
|
||||
if (!menu) {
|
||||
if (!addTo) {
|
||||
menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed()));
|
||||
} else {
|
||||
menu = addTo->addMenu(menuTreePart.trimmed());
|
||||
}
|
||||
}
|
||||
addTo = menu;
|
||||
}
|
||||
|
||||
if (isValidGrouping(grouping)) {
|
||||
auto action = getMenuAction(menuName);
|
||||
if (action) {
|
||||
_groupingActions[grouping] << action;
|
||||
action->setVisible(getGroupingIsVisible(grouping));
|
||||
}
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
|
||||
// hook our show/hide for popup menus, so we can keep track of whether or not one
|
||||
// of our submenus is currently showing.
|
||||
connect(menu->_realMenu, &QMenu::aboutToShow, []() { _isSomeSubmenuShown = true; });
|
||||
connect(menu->_realMenu, &QMenu::aboutToHide, []() { _isSomeSubmenuShown = false; });
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void Menu::removeMenu(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
QString finalMenuPart;
|
||||
MenuWrapper* parent = getMenuParent(menuName, finalMenuPart);
|
||||
if (parent) {
|
||||
parent->removeAction(action);
|
||||
} else {
|
||||
QMenuBar::removeAction(action);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) {
|
||||
MenuWrapper* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
addDisabledActionAndSeparator(menuObj, separatorName);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::removeSeparator(const QString& menuName, const QString& separatorName) {
|
||||
MenuWrapper* menu = getMenu(menuName);
|
||||
bool separatorRemoved = false;
|
||||
if (menu) {
|
||||
int textAt = findPositionOfMenuItem(menu, separatorName);
|
||||
QList<QAction*> menuActions = menu->actions();
|
||||
QAction* separatorText = menuActions[textAt];
|
||||
if (textAt > 0 && textAt < menuActions.size()) {
|
||||
QAction* separatorLine = menuActions[textAt - 1];
|
||||
if (separatorLine) {
|
||||
if (separatorLine->isSeparator()) {
|
||||
menu->removeAction(separatorText);
|
||||
menu->removeAction(separatorLine);
|
||||
separatorRemoved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (separatorRemoved) {
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
||||
MenuWrapper* menuObj = getMenu(menu);
|
||||
if (menuObj) {
|
||||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
if (menuItemAction) {
|
||||
return (getMenu(menu) != NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Menu::getGroupingIsVisible(const QString& grouping) {
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return true;
|
||||
}
|
||||
if (_groupingVisible.contains(grouping)) {
|
||||
return _groupingVisible[grouping];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) {
|
||||
// NOTE: Default grouping always visible
|
||||
if (grouping.isEmpty() || grouping.isNull()) {
|
||||
return;
|
||||
}
|
||||
_groupingVisible[grouping] = isVisible;
|
||||
|
||||
for (auto action: _groupingActions[grouping]) {
|
||||
action->setVisible(isVisible);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected, QObject* receiver, const char* slot) {
|
||||
auto menu = addMenu(groupName);
|
||||
|
||||
QActionGroup* actionGroup = new QActionGroup(menu);
|
||||
actionGroup->setExclusive(true);
|
||||
|
||||
for (auto action : actionList) {
|
||||
auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, receiver, slot);
|
||||
actionGroup->addAction(item);
|
||||
}
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
|
||||
void Menu::removeActionGroup(const QString& groupName) {
|
||||
removeMenu(groupName);
|
||||
}
|
||||
|
||||
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addMenu(menu);
|
||||
});
|
||||
_backMap[menu] = this;
|
||||
}
|
||||
|
||||
QList<QAction*> MenuWrapper::actions() {
|
||||
return _realMenu->actions();
|
||||
}
|
||||
|
||||
MenuWrapper* MenuWrapper::addMenu(const QString& menuName) {
|
||||
return new MenuWrapper(_realMenu->addMenu(menuName));
|
||||
}
|
||||
|
||||
void MenuWrapper::setEnabled(bool enabled) {
|
||||
_realMenu->setEnabled(enabled);
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addSeparator() {
|
||||
return _realMenu->addSeparator();
|
||||
}
|
||||
|
||||
void MenuWrapper::addAction(QAction* action) {
|
||||
_realMenu->addAction(action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName) {
|
||||
QAction* action = _realMenu->addAction(menuName);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
|
||||
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->addAction(_realMenu, action);
|
||||
});
|
||||
return action;
|
||||
}
|
||||
|
||||
void MenuWrapper::removeAction(QAction* action) {
|
||||
_realMenu->removeAction(action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->removeAction(action);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuWrapper::insertAction(QAction* before, QAction* action) {
|
||||
_realMenu->insertAction(before, action);
|
||||
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
|
||||
vrMenu->insertAction(before, action);
|
||||
});
|
||||
}
|
||||
|
||||
QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;
|
155
libraries/ui/src/ui/Menu.h
Normal file
155
libraries/ui/src/ui/Menu.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ui_Menu_h
|
||||
#define hifi_ui_Menu_h
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtGui/QKeySequence>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
|
||||
class Settings;
|
||||
namespace ui {
|
||||
class Menu;
|
||||
}
|
||||
|
||||
class MenuWrapper : public QObject {
|
||||
public:
|
||||
|
||||
QList<QAction*> actions();
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void setEnabled(bool enabled = true);
|
||||
QAction* addSeparator();
|
||||
void addAction(QAction* action);
|
||||
|
||||
QAction* addAction(const QString& menuName);
|
||||
void insertAction(QAction* before, QAction* menuName);
|
||||
|
||||
QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0);
|
||||
void removeAction(QAction* action);
|
||||
|
||||
QAction* newAction() {
|
||||
return new QAction(_realMenu);
|
||||
}
|
||||
|
||||
private:
|
||||
MenuWrapper(QMenu* menu);
|
||||
|
||||
static MenuWrapper* fromMenu(QMenu* menu) {
|
||||
return _backMap[menu];
|
||||
}
|
||||
|
||||
QMenu* const _realMenu;
|
||||
static QHash<QMenu*, MenuWrapper*> _backMap;
|
||||
friend class ui::Menu;
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
||||
class Menu : public QMenuBar {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const int UNSPECIFIED_POSITION = -1;
|
||||
|
||||
Menu();
|
||||
static Menu* getInstance();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
QAction* action,
|
||||
const QString& actionName = QString(),
|
||||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
const bool checked = false,
|
||||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
public slots:
|
||||
MenuWrapper* addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping = QString());
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString(),
|
||||
QObject* receiver = nullptr, const char* slot = nullptr);
|
||||
void removeActionGroup(const QString& groupName);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
bool getGroupingIsVisible(const QString& grouping);
|
||||
void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible
|
||||
|
||||
void toggleDeveloperMenus();
|
||||
void toggleAdvancedMenus();
|
||||
|
||||
static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; }
|
||||
|
||||
protected:
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
static void saveAction(Settings& settings, QAction& action);
|
||||
void scanMenuBar(settingsAction modifySetting);
|
||||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION,
|
||||
const QString& grouping = QString());
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
||||
QAction* getMenuAction(const QString& menuName);
|
||||
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
|
||||
bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; }
|
||||
QHash<QString, bool> _groupingVisible;
|
||||
QHash<QString, QSet<QAction*>> _groupingActions;
|
||||
|
||||
static bool _isSomeSubmenuShown;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
#endif // hifi_Menu_h
|
|
@ -13,7 +13,7 @@ if (WIN32)
|
|||
|
||||
set(TARGET_NAME oculus)
|
||||
setup_hifi_plugin()
|
||||
link_hifi_libraries(shared gl plugins display-plugins)
|
||||
link_hifi_libraries(shared gl plugins gpu display-plugins input-plugins)
|
||||
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
|
|
|
@ -76,8 +76,6 @@ void OculusBaseDisplayPlugin::activate() {
|
|||
qFatal("Failed to acquire HMD");
|
||||
}
|
||||
|
||||
HmdDisplayPlugin::activate();
|
||||
|
||||
_hmdDesc = ovr_GetHmdDesc(_session);
|
||||
|
||||
_ipd = ovr_GetFloat(_session, OVR_KEY_IPD, _ipd);
|
||||
|
@ -123,6 +121,11 @@ void OculusBaseDisplayPlugin::activate() {
|
|||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||
qFatal("Could not attach to sensor device");
|
||||
}
|
||||
|
||||
// This must come after the initialization, so that the values calculated
|
||||
// above are available during the customizeContext call (when not running
|
||||
// in threaded present mode)
|
||||
HmdDisplayPlugin::activate();
|
||||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::deactivate() {
|
||||
|
|
|
@ -19,8 +19,3 @@ bool OculusDebugDisplayPlugin::isSupported() const {
|
|||
}
|
||||
return OculusBaseDisplayPlugin::isSupported();
|
||||
}
|
||||
|
||||
void OculusDebugDisplayPlugin::customizeContext() {
|
||||
OculusBaseDisplayPlugin::customizeContext();
|
||||
enableVsync(false);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
|
||||
class OculusDebugDisplayPlugin : public OculusBaseDisplayPlugin {
|
||||
public:
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual grouping getGrouping() const override { return DEVELOPER; }
|
||||
virtual bool isSupported() const override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
grouping getGrouping() const override { return DEVELOPER; }
|
||||
bool isSupported() const override;
|
||||
|
||||
protected:
|
||||
virtual void customizeContext() override;
|
||||
void hmdPresent() override {}
|
||||
|
||||
private:
|
||||
static const QString NAME;
|
||||
|
|
|
@ -37,57 +37,38 @@ void OculusDisplayPlugin::uncustomizeContext() {
|
|||
OculusBaseDisplayPlugin::uncustomizeContext();
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::internalPresent() {
|
||||
|
||||
template <typename SrcFbo, typename DstFbo>
|
||||
void blit(const SrcFbo& srcFbo, const DstFbo& dstFbo) {
|
||||
using namespace oglplus;
|
||||
srcFbo->Bound(FramebufferTarget::Read, [&] {
|
||||
dstFbo->Bound(FramebufferTarget::Draw, [&] {
|
||||
Context::BlitFramebuffer(
|
||||
0, 0, srcFbo->size.x, srcFbo->size.y,
|
||||
0, 0, dstFbo->size.x, dstFbo->size.y,
|
||||
BufferSelectBit::ColorBuffer, BlitFilter::Linear);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::updateFrameData() {
|
||||
Parent::updateFrameData();
|
||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentRenderEyePoses[Left]);
|
||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentRenderEyePoses[Right]);
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::hmdPresent() {
|
||||
if (!_currentSceneTexture) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace oglplus;
|
||||
const auto& size = _sceneFbo->size;
|
||||
_sceneFbo->Bound([&] {
|
||||
Context::Viewport(size.x, size.y);
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
//glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
GLenum err = glGetError();
|
||||
drawUnitQuad();
|
||||
//glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
});
|
||||
|
||||
uint32_t frameIndex { 0 };
|
||||
EyePoses eyePoses;
|
||||
blit(_compositeFramebuffer, _sceneFbo);
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
Q_ASSERT(_sceneTextureToFrameIndexMap.contains(_currentSceneTexture));
|
||||
frameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture];
|
||||
Q_ASSERT(_frameEyePoses.contains(frameIndex));
|
||||
eyePoses = _frameEyePoses[frameIndex];
|
||||
}
|
||||
|
||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = eyePoses.first;
|
||||
_sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = eyePoses.second;
|
||||
|
||||
{
|
||||
|
||||
ovrLayerHeader* layers = &_sceneLayer.Header;
|
||||
ovrResult result = ovr_SubmitFrame(_session, frameIndex, &_viewScaleDesc, &layers, 1);
|
||||
ovrResult result = ovr_SubmitFrame(_session, _currentRenderFrameIndex, &_viewScaleDesc, &layers, 1);
|
||||
if (!OVR_SUCCESS(result)) {
|
||||
qDebug() << result;
|
||||
}
|
||||
}
|
||||
_sceneFbo->Increment();
|
||||
|
||||
// Handle mirroring to screen in base class
|
||||
HmdDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
||||
void OculusDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) {
|
||||
auto ovrPose = ovrPoseFromGlm(pose);
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
if (eye == Eye::Left) {
|
||||
_frameEyePoses[frameIndex].first = ovrPose;
|
||||
} else {
|
||||
_frameEyePoses[frameIndex].second = ovrPose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,24 +15,23 @@ using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
|
|||
const float TARGET_RATE_Oculus = 75.0f;
|
||||
|
||||
class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
|
||||
using Parent = OculusBaseDisplayPlugin;
|
||||
public:
|
||||
virtual void activate() override;
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final;
|
||||
void activate() override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; }
|
||||
float getTargetFrameRate() override { return TARGET_RATE_Oculus; }
|
||||
|
||||
protected:
|
||||
virtual void internalPresent() override;
|
||||
virtual void customizeContext() override;
|
||||
virtual void uncustomizeContext() override;
|
||||
void hmdPresent() override;
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
void updateFrameData() override;
|
||||
|
||||
private:
|
||||
using EyePoses = std::pair<ovrPosef, ovrPosef>;
|
||||
static const QString NAME;
|
||||
bool _enablePreview { false };
|
||||
bool _monoPreview { true };
|
||||
QMap<uint32_t, EyePoses> _frameEyePoses;
|
||||
|
||||
SwapFboPtr _sceneFbo;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ if (NOT WIN32)
|
|||
|
||||
set(TARGET_NAME oculusLegacy)
|
||||
setup_hifi_plugin()
|
||||
link_hifi_libraries(shared gl plugins display-plugins)
|
||||
link_hifi_libraries(shared gl gpu plugins display-plugins input-plugins)
|
||||
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void customizeContext() override;
|
||||
void hmdPresent() override {}
|
||||
#if 0
|
||||
virtual void uncustomizeContext() override;
|
||||
virtual void internalPresent() override;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <PerfStat.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "OpenVrHelpers.h"
|
||||
|
||||
|
@ -128,19 +127,16 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
|||
return _trackedDevicePoseMat4[0];
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::internalPresent() {
|
||||
void OpenVrDisplayPlugin::hmdPresent() {
|
||||
// Flip y-axis since GL UV coords are backwards.
|
||||
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
||||
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
||||
|
||||
vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
|
||||
vr::Texture_t texture { (void*)oglplus::GetName(_compositeFramebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
|
||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||
|
||||
vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
_compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
||||
// Handle the mirroring in the base class
|
||||
HmdDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
|
|
@ -32,11 +32,10 @@ public:
|
|||
virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override;
|
||||
|
||||
protected:
|
||||
virtual void internalPresent() override;
|
||||
void hmdPresent() override;
|
||||
|
||||
private:
|
||||
vr::IVRSystem* _system { nullptr };
|
||||
static const QString NAME;
|
||||
mutable Mutex _poseMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -92,8 +92,8 @@ public:
|
|||
virtual void showDisplayPluginsTools() override {}
|
||||
virtual void requestReset() override {}
|
||||
virtual bool makeRenderingContextCurrent() override { return true; }
|
||||
virtual void releaseSceneTexture(uint32_t texture) override {}
|
||||
virtual void releaseOverlayTexture(uint32_t texture) override {}
|
||||
virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {}
|
||||
virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {}
|
||||
virtual GLWidget* getPrimaryWidget() override { return nullptr; }
|
||||
virtual QWindow* getPrimaryWindow() override { return nullptr; }
|
||||
virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }
|
||||
|
|
|
@ -973,6 +973,8 @@
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1001,6 +1003,8 @@
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1074,6 +1078,8 @@
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1103,6 +1109,8 @@
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1131,6 +1139,8 @@
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1241,8 +1251,9 @@
|
|||
}
|
||||
|
||||
function createPingPongBallGun() {
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj';
|
||||
|
||||
var MODEL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.obj';
|
||||
var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav';
|
||||
var position = {
|
||||
x: 548.6,
|
||||
|
@ -1267,9 +1278,9 @@
|
|||
},
|
||||
restitution: 0,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.21,
|
||||
z: 0.47
|
||||
x: 0.125,
|
||||
y: 0.3875,
|
||||
z: 0.9931
|
||||
},
|
||||
dynamic: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
|
|
|
@ -720,6 +720,7 @@ MasterReset = function() {
|
|||
|
||||
function createTargets() {
|
||||
|
||||
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj';
|
||||
|
||||
|
@ -960,6 +961,8 @@ MasterReset = function() {
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -988,6 +991,8 @@ MasterReset = function() {
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1061,6 +1066,8 @@ MasterReset = function() {
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1090,6 +1097,8 @@ MasterReset = function() {
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1118,6 +1127,8 @@ MasterReset = function() {
|
|||
y: 2.545,
|
||||
z: 2.545
|
||||
},
|
||||
intensity: 1.0,
|
||||
falloffRadius: 0.3,
|
||||
cutoff: 90,
|
||||
color: {
|
||||
red: 217,
|
||||
|
@ -1228,8 +1239,9 @@ MasterReset = function() {
|
|||
}
|
||||
|
||||
function createPingPongBallGun() {
|
||||
var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj';
|
||||
var MODEL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Pingpong-Gun-New.obj';
|
||||
|
||||
var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav';
|
||||
var position = {
|
||||
x: 548.6,
|
||||
|
@ -1254,9 +1266,9 @@ MasterReset = function() {
|
|||
},
|
||||
restitution: 0,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.21,
|
||||
z: 0.47
|
||||
x: 0.125,
|
||||
y: 0.3875,
|
||||
z: 0.9931
|
||||
},
|
||||
dynamic: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
|
|
Loading…
Reference in a new issue