mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 10:55:37 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19507
This commit is contained in:
commit
852756e84d
153 changed files with 7791 additions and 1595 deletions
assignment-client
domain-server/src
examples
dialogExample.jseditModelExample.jseditVoxels.jsfileBrowserExample.jslocationExample.jsplaceModelsWithHands.jsrayPickExample.jsswissArmyJetpack.jswindowExample.js
interface
CMakeLists.txtinterface_en.ts
resources
src
Application.cppApplication.hAudio.cppAudio.hDatagramProcessor.cppGLCanvas.cppGLCanvas.hMainWindow.cppMainWindow.hMenu.cppMenu.hModelUploader.cppModelUploader.hScriptHighlighting.cppScriptHighlighting.h
avatar
location
main.cppmodels
particles
renderer
scripting
LocationScriptingInterface.cppLocationScriptingInterface.hWindowScriptingInterface.cppWindowScriptingInterface.h
ui
ChatInputArea.cppChatInputArea.hChatMessageArea.cppChatMessageArea.hChatWindow.cppChatWindow.hFramelessDialog.cppFramelessDialog.hLoginDialog.cppLoginDialog.hOctreeStatsDialog.cppPreferencesDialog.cppRunningScriptsWidget.cppRunningScriptsWidget.hScriptEditBox.cppScriptEditBox.hScriptEditorWidget.cppScriptEditorWidget.hScriptEditorWindow.cppScriptEditorWindow.hScriptLineNumberArea.cppScriptLineNumberArea.hScriptsTableWidget.cppScriptsTableWidget.hStats.cpp
voxels
ui
libraries
|
@ -30,6 +30,7 @@ link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
|||
link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#include <ResourceCache.h>
|
||||
#include <UUID.h>
|
||||
#include <VoxelConstants.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
|
||||
#include <ParticlesScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
#include <ModelsScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
|
@ -33,6 +35,7 @@ Agent::Agent(const QByteArray& packet) :
|
|||
ThreadedAssignment(packet),
|
||||
_voxelEditSender(),
|
||||
_particleEditSender(),
|
||||
_modelEditSender(),
|
||||
_receivedAudioBuffer(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO),
|
||||
_avatarHashMap()
|
||||
{
|
||||
|
@ -41,6 +44,7 @@ Agent::Agent(const QByteArray& packet) :
|
|||
|
||||
_scriptEngine.getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||
_scriptEngine.getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||
_scriptEngine.getModelsScriptingInterface()->setPacketSender(&_modelEditSender);
|
||||
}
|
||||
|
||||
void Agent::readPendingDatagrams() {
|
||||
|
@ -68,6 +72,10 @@ void Agent::readPendingDatagrams() {
|
|||
_scriptEngine.getParticlesScriptingInterface()->getJurisdictionListener()->
|
||||
queueReceivedPacket(matchedNode, receivedPacket);
|
||||
break;
|
||||
case NodeType::ModelServer:
|
||||
_scriptEngine.getModelsScriptingInterface()->getJurisdictionListener()->
|
||||
queueReceivedPacket(matchedNode, receivedPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,10 +90,23 @@ void Agent::readPendingDatagrams() {
|
|||
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
} else if (datagramPacketType == PacketTypeModelAddResponse) {
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
ModelItem::handleAddModelResponse(receivedPacket);
|
||||
|
||||
// also give our local particle tree a chance to remap any internal locally created particles
|
||||
_modelViewer.getTree()->handleAddModelResponse(receivedPacket);
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
} else if (datagramPacketType == PacketTypeParticleData
|
||||
|| datagramPacketType == PacketTypeParticleErase
|
||||
|| datagramPacketType == PacketTypeOctreeStats
|
||||
|| datagramPacketType == PacketTypeVoxelData
|
||||
|| datagramPacketType == PacketTypeModelData
|
||||
|| datagramPacketType == PacketTypeModelErase
|
||||
) {
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
|
@ -117,6 +138,10 @@ void Agent::readPendingDatagrams() {
|
|||
_particleViewer.processDatagram(mutablePacket, sourceNode);
|
||||
}
|
||||
|
||||
if (datagramPacketType == PacketTypeModelData || datagramPacketType == PacketTypeModelErase) {
|
||||
_modelViewer.processDatagram(mutablePacket, sourceNode);
|
||||
}
|
||||
|
||||
if (datagramPacketType == PacketTypeVoxelData) {
|
||||
_voxelViewer.processDatagram(mutablePacket, sourceNode);
|
||||
}
|
||||
|
@ -159,7 +184,9 @@ void Agent::run() {
|
|||
<< NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer
|
||||
<< NodeType::VoxelServer
|
||||
<< NodeType::ParticleServer);
|
||||
<< NodeType::ParticleServer
|
||||
<< NodeType::ModelServer
|
||||
);
|
||||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QUrl scriptURL;
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
#include <AvatarHashMap.h>
|
||||
#include <MixedAudioRingBuffer.h>
|
||||
#include <ModelEditPacketSender.h>
|
||||
#include <ModelTree.h>
|
||||
#include <ModelTreeHeadlessViewer.h>
|
||||
#include <ParticleEditPacketSender.h>
|
||||
#include <ParticleTree.h>
|
||||
#include <ParticleTreeHeadlessViewer.h>
|
||||
|
@ -61,9 +64,11 @@ private:
|
|||
ScriptEngine _scriptEngine;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
ParticleEditPacketSender _particleEditSender;
|
||||
ModelEditPacketSender _modelEditSender;
|
||||
|
||||
ParticleTreeHeadlessViewer _particleViewer;
|
||||
VoxelTreeHeadlessViewer _voxelViewer;
|
||||
ModelTreeHeadlessViewer _modelViewer;
|
||||
|
||||
MixedAudioRingBuffer _receivedAudioBuffer;
|
||||
AvatarHashMap _avatarHashMap;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "audio/AudioMixer.h"
|
||||
#include "avatars/AvatarMixer.h"
|
||||
#include "metavoxels/MetavoxelServer.h"
|
||||
#include "models/ModelServer.h"
|
||||
#include "particles/ParticleServer.h"
|
||||
#include "voxels/VoxelServer.h"
|
||||
|
||||
|
@ -41,6 +42,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet
|
|||
return new ParticleServer(packet);
|
||||
case Assignment::MetavoxelServerType:
|
||||
return new MetavoxelServer(packet);
|
||||
case Assignment::ModelServerType:
|
||||
return new ModelServer(packet);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
#include "AssignmentClientMonitor.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
34
assignment-client/src/models/ModelNodeData.h
Normal file
34
assignment-client/src/models/ModelNodeData.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ModelNodeData.h
|
||||
// assignment-client/src/models
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/29/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ModelNodeData_h
|
||||
#define hifi_ModelNodeData_h
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "../octree/OctreeQueryNode.h"
|
||||
|
||||
class ModelNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
ModelNodeData() :
|
||||
OctreeQueryNode(),
|
||||
_lastDeletedModelsSentAt(0) { };
|
||||
|
||||
virtual PacketType getMyPacketType() const { return PacketTypeModelData; }
|
||||
|
||||
quint64 getLastDeletedModelsSentAt() const { return _lastDeletedModelsSentAt; }
|
||||
void setLastDeletedModelsSentAt(quint64 sentAt) { _lastDeletedModelsSentAt = sentAt; }
|
||||
|
||||
private:
|
||||
quint64 _lastDeletedModelsSentAt;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelNodeData_h
|
137
assignment-client/src/models/ModelServer.cpp
Normal file
137
assignment-client/src/models/ModelServer.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// ModelServer.cpp
|
||||
// assignment-client/src/models
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/29/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 <QTimer>
|
||||
#include <ModelTree.h>
|
||||
|
||||
#include "ModelServer.h"
|
||||
#include "ModelServerConsts.h"
|
||||
#include "ModelNodeData.h"
|
||||
|
||||
const char* MODEL_SERVER_NAME = "Model";
|
||||
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "model-server";
|
||||
const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo";
|
||||
|
||||
ModelServer::ModelServer(const QByteArray& packet) : OctreeServer(packet) {
|
||||
// nothing special to do here...
|
||||
}
|
||||
|
||||
ModelServer::~ModelServer() {
|
||||
ModelTree* tree = (ModelTree*)_tree;
|
||||
tree->removeNewlyCreatedHook(this);
|
||||
}
|
||||
|
||||
OctreeQueryNode* ModelServer::createOctreeQueryNode() {
|
||||
return new ModelNodeData();
|
||||
}
|
||||
|
||||
Octree* ModelServer::createTree() {
|
||||
ModelTree* tree = new ModelTree(true);
|
||||
tree->addNewlyCreatedHook(this);
|
||||
return tree;
|
||||
}
|
||||
|
||||
void ModelServer::beforeRun() {
|
||||
QTimer* pruneDeletedModelsTimer = new QTimer(this);
|
||||
connect(pruneDeletedModelsTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedModels()));
|
||||
const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second
|
||||
pruneDeletedModelsTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
void ModelServer::modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(outputBuffer), PacketTypeModelAddResponse);
|
||||
int packetLength = numBytesPacketHeader;
|
||||
copyAt += numBytesPacketHeader;
|
||||
|
||||
// encode the creatorTokenID
|
||||
uint32_t creatorTokenID = newModel.getCreatorTokenID();
|
||||
memcpy(copyAt, &creatorTokenID, sizeof(creatorTokenID));
|
||||
copyAt += sizeof(creatorTokenID);
|
||||
packetLength += sizeof(creatorTokenID);
|
||||
|
||||
// encode the model ID
|
||||
uint32_t modelID = newModel.getID();
|
||||
memcpy(copyAt, &modelID, sizeof(modelID));
|
||||
copyAt += sizeof(modelID);
|
||||
packetLength += sizeof(modelID);
|
||||
|
||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, senderNode);
|
||||
}
|
||||
|
||||
|
||||
// ModelServer will use the "special packets" to send list of recently deleted models
|
||||
bool ModelServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
||||
bool shouldSendDeletedModels = false;
|
||||
|
||||
// check to see if any new models have been added since we last sent to this node...
|
||||
ModelNodeData* nodeData = static_cast<ModelNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
quint64 deletedModelsSentAt = nodeData->getLastDeletedModelsSentAt();
|
||||
|
||||
ModelTree* tree = static_cast<ModelTree*>(_tree);
|
||||
shouldSendDeletedModels = tree->hasModelsDeletedSince(deletedModelsSentAt);
|
||||
}
|
||||
|
||||
return shouldSendDeletedModels;
|
||||
}
|
||||
|
||||
int ModelServer::sendSpecialPacket(const SharedNodePointer& node) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
size_t packetLength = 0;
|
||||
|
||||
ModelNodeData* nodeData = static_cast<ModelNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
quint64 deletedModelsSentAt = nodeData->getLastDeletedModelsSentAt();
|
||||
quint64 deletePacketSentAt = usecTimestampNow();
|
||||
|
||||
ModelTree* tree = static_cast<ModelTree*>(_tree);
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models?
|
||||
while (hasMoreToSend) {
|
||||
hasMoreToSend = tree->encodeModelsDeletedSince(deletedModelsSentAt,
|
||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||
|
||||
//qDebug() << "sending PacketType_MODEL_ERASE packetLength:" << packetLength;
|
||||
|
||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
||||
}
|
||||
|
||||
nodeData->setLastDeletedModelsSentAt(deletePacketSentAt);
|
||||
}
|
||||
|
||||
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
||||
return packetLength;
|
||||
}
|
||||
|
||||
void ModelServer::pruneDeletedModels() {
|
||||
ModelTree* tree = static_cast<ModelTree*>(_tree);
|
||||
if (tree->hasAnyDeletedModels()) {
|
||||
|
||||
//qDebug() << "there are some deleted models to consider...";
|
||||
quint64 earliestLastDeletedModelsSent = usecTimestampNow() + 1; // in the future
|
||||
foreach (const SharedNodePointer& otherNode, NodeList::getInstance()->getNodeHash()) {
|
||||
if (otherNode->getLinkedData()) {
|
||||
ModelNodeData* nodeData = static_cast<ModelNodeData*>(otherNode->getLinkedData());
|
||||
quint64 nodeLastDeletedModelsSentAt = nodeData->getLastDeletedModelsSentAt();
|
||||
if (nodeLastDeletedModelsSentAt < earliestLastDeletedModelsSent) {
|
||||
earliestLastDeletedModelsSent = nodeLastDeletedModelsSentAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug() << "earliestLastDeletedModelsSent=" << earliestLastDeletedModelsSent;
|
||||
tree->forgetModelsDeletedBefore(earliestLastDeletedModelsSent);
|
||||
}
|
||||
}
|
||||
|
50
assignment-client/src/models/ModelServer.h
Normal file
50
assignment-client/src/models/ModelServer.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// ModelServer.h
|
||||
// assignment-client/src/models
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/29/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ModelServer_h
|
||||
#define hifi_ModelServer_h
|
||||
|
||||
#include "../octree/OctreeServer.h"
|
||||
|
||||
#include "ModelItem.h"
|
||||
#include "ModelServerConsts.h"
|
||||
#include "ModelTree.h"
|
||||
|
||||
/// Handles assignments of type ModelServer - sending models to various clients.
|
||||
class ModelServer : public OctreeServer, public NewlyCreatedModelHook {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ModelServer(const QByteArray& packet);
|
||||
~ModelServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual char getMyNodeType() const { return NodeType::ModelServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeModelQuery; }
|
||||
virtual const char* getMyServerName() const { return MODEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return MODEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_MODELS_PERSIST_FILE; }
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
||||
|
||||
virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode);
|
||||
|
||||
public slots:
|
||||
void pruneDeletedModels();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // hifi_ModelServer_h
|
19
assignment-client/src/models/ModelServerConsts.h
Normal file
19
assignment-client/src/models/ModelServerConsts.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ModelServerConsts.h
|
||||
// assignment-client/src/models
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 4/29/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ModelServerConsts_h
|
||||
#define hifi_ModelServerConsts_h
|
||||
|
||||
extern const char* MODEL_SERVER_NAME;
|
||||
extern const char* MODEL_SERVER_LOGGING_TARGET_NAME;
|
||||
extern const char* LOCAL_MODELS_PERSIST_FILE;
|
||||
|
||||
#endif // hifi_ModelServerConsts_h
|
|
@ -833,7 +833,7 @@ void OctreeServer::readPendingDatagrams() {
|
|||
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
if (packetType == getMyQueryMessageType()) {
|
||||
|
||||
// If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we
|
||||
// If we got a query packet, then we're talking to an agent, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket);
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode() = 0;
|
||||
virtual Octree* createTree() = 0;
|
||||
virtual unsigned char getMyNodeType() const = 0;
|
||||
virtual char getMyNodeType() const = 0;
|
||||
virtual PacketType getMyQueryMessageType() const = 0;
|
||||
virtual const char* getMyServerName() const = 0;
|
||||
virtual const char* getMyLoggingServerTargetName() const = 0;
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NodeType::ParticleServer; }
|
||||
virtual char getMyNodeType() const { return NodeType::ParticleServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; }
|
||||
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode();
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||
virtual char getMyNodeType() const { return NodeType::VoxelServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeVoxelQuery; }
|
||||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
|
|
|
@ -341,7 +341,7 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
|
||||
<< NodeType::MetavoxelServer;
|
||||
|
||||
void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "DomainServer.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
7
examples/dialogExample.js
Normal file
7
examples/dialogExample.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
Window.alert("This is an alert box");
|
||||
|
||||
var confirmed = Window.confirm("This is a confirmation dialog")
|
||||
Window.alert("Your response was: " + confirmed);
|
||||
|
||||
var prompt = Window.prompt("This is a prompt dialog", "This is the default text");
|
||||
Window.alert("Your response was: " + prompt);
|
93
examples/editModelExample.js
Normal file
93
examples/editModelExample.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// editModelExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/31/13.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates creating and editing a model
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var count = 0;
|
||||
var moveUntil = 2000;
|
||||
var stopAfter = moveUntil + 100;
|
||||
|
||||
var pitch = 90.0;
|
||||
var yaw = 0.0;
|
||||
var roll = 180.0;
|
||||
var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll)
|
||||
|
||||
var originalProperties = {
|
||||
position: { x: 10,
|
||||
y: 0,
|
||||
z: 0 },
|
||||
|
||||
radius : 0.1,
|
||||
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 },
|
||||
|
||||
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo",
|
||||
//modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/minotaur/mino_full.fbx",
|
||||
modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
|
||||
|
||||
modelRotation: rotation
|
||||
};
|
||||
|
||||
var positionDelta = { x: 0, y: 0, z: 0 };
|
||||
|
||||
|
||||
var modelID = Models.addModel(originalProperties);
|
||||
|
||||
function moveModel(deltaTime) {
|
||||
if (count >= moveUntil) {
|
||||
|
||||
// delete it...
|
||||
if (count == moveUntil) {
|
||||
print("calling Models.deleteModel()");
|
||||
Models.deleteModel(modelID);
|
||||
}
|
||||
|
||||
// stop it...
|
||||
if (count >= stopAfter) {
|
||||
print("calling Script.stop()");
|
||||
Script.stop();
|
||||
}
|
||||
|
||||
count++;
|
||||
return; // break early
|
||||
}
|
||||
|
||||
//print("count =" + count);
|
||||
count++;
|
||||
|
||||
//print("modelID.creatorTokenID = " + modelID.creatorTokenID);
|
||||
|
||||
var newProperties = {
|
||||
position: {
|
||||
x: originalProperties.position.x + (count * positionDelta.x),
|
||||
y: originalProperties.position.y + (count * positionDelta.y),
|
||||
z: originalProperties.position.z + (count * positionDelta.z)
|
||||
},
|
||||
radius : 0.25,
|
||||
|
||||
};
|
||||
|
||||
|
||||
//print("modelID = " + modelID);
|
||||
//print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z);
|
||||
|
||||
Models.editModel(modelID, newProperties);
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(moveModel);
|
||||
|
|
@ -1088,7 +1088,6 @@ function keyPressEvent(event) {
|
|||
red: colors[whichColor].red,
|
||||
green: colors[whichColor].green,
|
||||
blue: colors[whichColor].blue };
|
||||
Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
setAudioPosition();
|
||||
initialVoxelSound.playRandom();
|
||||
|
@ -1394,7 +1393,6 @@ function checkControllers() {
|
|||
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
|
||||
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
|
||||
|
||||
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
|
||||
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
|
||||
newColor.red, newColor.green, newColor.blue);
|
||||
|
||||
|
|
13
examples/fileBrowserExample.js
Normal file
13
examples/fileBrowserExample.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
var file = Window.browse("File Browser Example", "/");
|
||||
if (file === null) {
|
||||
Window.alert("No file was selected");
|
||||
} else {
|
||||
Window.alert("Selected file: " + file);
|
||||
}
|
||||
|
||||
file = Window.browse("Relative Directory Example", "./images", "PNG or JPG files(*.png *.jpg);;SVG files (*.svg)");
|
||||
if (file === null) {
|
||||
Window.alert("No file was selected");
|
||||
} else {
|
||||
Window.alert("Selected file: " + file);
|
||||
}
|
11
examples/locationExample.js
Normal file
11
examples/locationExample.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
var goto = Window.prompt("Where would you like to go? (ex. @username, #location, 1.0,500.3,100.2)");
|
||||
var url = "hifi://" + goto;
|
||||
print("Going to: " + url);
|
||||
location = url;
|
||||
|
||||
// If the destination location is a user or named location the new location may not be set by the time execution reaches here
|
||||
// because it requires an asynchronous lookup. Coordinate changes should be be reflected immediately, though. (ex: hifi://0,0,0)
|
||||
print("URL: " + location.href);
|
||||
print("Protocol: " + location.protocol);
|
||||
print("Hostname: " + location.hostname);
|
||||
print("Pathname: " + location.pathname);
|
220
examples/placeModelsWithHands.js
Normal file
220
examples/placeModelsWithHands.js
Normal file
|
@ -0,0 +1,220 @@
|
|||
//
|
||||
// placeModelsWithHands.js
|
||||
// examples
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 1/20/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
|
||||
//
|
||||
|
||||
// maybe we should make these constants...
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_TIP = 1;
|
||||
var LEFT_BUTTON_FWD = 5;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_TIP = 3;
|
||||
var RIGHT_BUTTON_FWD = 11;
|
||||
var RIGHT_BUTTON_3 = 9;
|
||||
|
||||
var leftModelAlreadyInHand = false;
|
||||
var rightModelAlreadyInHand = false;
|
||||
var leftHandModel;
|
||||
var rightHandModel;
|
||||
|
||||
//var throwSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw");
|
||||
//var catchSound = new Sound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw");
|
||||
var targetRadius = 0.25;
|
||||
|
||||
|
||||
var modelURLs = [
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/minotaur/mino_full.fbx",
|
||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
|
||||
];
|
||||
|
||||
var currentModelURL = 0;
|
||||
|
||||
var wantDebugging = false;
|
||||
function debugPrint(message) {
|
||||
if (wantDebugging) {
|
||||
print(message);
|
||||
}
|
||||
}
|
||||
|
||||
function getModelHoldPosition(whichSide) {
|
||||
var normal;
|
||||
var tipPosition;
|
||||
if (whichSide == LEFT_PALM) {
|
||||
normal = Controller.getSpatialControlNormal(LEFT_PALM);
|
||||
tipPosition = Controller.getSpatialControlPosition(LEFT_TIP);
|
||||
} else {
|
||||
normal = Controller.getSpatialControlNormal(RIGHT_PALM);
|
||||
tipPosition = Controller.getSpatialControlPosition(RIGHT_TIP);
|
||||
}
|
||||
|
||||
var MODEL_FORWARD_OFFSET = 0.08; // put the model a bit forward of fingers
|
||||
position = { x: MODEL_FORWARD_OFFSET * normal.x,
|
||||
y: MODEL_FORWARD_OFFSET * normal.y,
|
||||
z: MODEL_FORWARD_OFFSET * normal.z };
|
||||
|
||||
position.x += tipPosition.x;
|
||||
position.y += tipPosition.y;
|
||||
position.z += tipPosition.z;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
function checkControllerSide(whichSide) {
|
||||
|
||||
var BUTTON_FWD;
|
||||
var BUTTON_3;
|
||||
var palmPosition;
|
||||
var modelAlreadyInHand;
|
||||
var handMessage;
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
BUTTON_FWD = LEFT_BUTTON_FWD;
|
||||
BUTTON_3 = LEFT_BUTTON_3;
|
||||
palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
|
||||
modelAlreadyInHand = leftModelAlreadyInHand;
|
||||
handMessage = "LEFT";
|
||||
} else {
|
||||
BUTTON_FWD = RIGHT_BUTTON_FWD;
|
||||
BUTTON_3 = RIGHT_BUTTON_3;
|
||||
palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
|
||||
modelAlreadyInHand = rightModelAlreadyInHand;
|
||||
handMessage = "RIGHT";
|
||||
}
|
||||
|
||||
//print("checkControllerSide..." + handMessage);
|
||||
|
||||
var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3));
|
||||
|
||||
// If I don't currently have a model in my hand, then try to grab closest one
|
||||
if (!modelAlreadyInHand && grabButtonPressed) {
|
||||
var closestModel = Models.findClosestModel(palmPosition, targetRadius);
|
||||
|
||||
if (closestModel.isKnownID) {
|
||||
|
||||
debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!");
|
||||
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftModelAlreadyInHand = true;
|
||||
leftHandModel = closestModel;
|
||||
} else {
|
||||
rightModelAlreadyInHand = true;
|
||||
rightHandModel = closestModel;
|
||||
}
|
||||
var modelPosition = getModelHoldPosition(whichSide);
|
||||
var properties = { position: { x: modelPosition.x,
|
||||
y: modelPosition.y,
|
||||
z: modelPosition.z },
|
||||
};
|
||||
Models.editModel(closestModel, properties);
|
||||
|
||||
/*
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = modelPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(catchSound, options);
|
||||
*/
|
||||
|
||||
return; // exit early
|
||||
}
|
||||
}
|
||||
|
||||
// If '3' is pressed, and not holding a model, make a new one
|
||||
if (Controller.isButtonPressed(BUTTON_3) && !modelAlreadyInHand) {
|
||||
var modelPosition = getModelHoldPosition(whichSide);
|
||||
var properties = { position: { x: modelPosition.x,
|
||||
y: modelPosition.y,
|
||||
z: modelPosition.z },
|
||||
radius: 0.25,
|
||||
modelURL: modelURLs[currentModelURL]
|
||||
};
|
||||
|
||||
newModel = Models.addModel(properties);
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftModelAlreadyInHand = true;
|
||||
leftHandModel = newModel;
|
||||
} else {
|
||||
rightModelAlreadyInHand = true;
|
||||
rightHandModel = newModel;
|
||||
}
|
||||
|
||||
// Play a new model sound
|
||||
/*
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = modelPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(catchSound, options);
|
||||
*/
|
||||
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
if (modelAlreadyInHand) {
|
||||
if (whichSide == LEFT_PALM) {
|
||||
handModel = leftHandModel;
|
||||
whichTip = LEFT_TIP;
|
||||
} else {
|
||||
handModel = rightHandModel;
|
||||
whichTip = RIGHT_TIP;
|
||||
}
|
||||
|
||||
// If holding the model keep it in the palm
|
||||
if (grabButtonPressed) {
|
||||
debugPrint(">>>>> " + handMessage + "-MODEL IN HAND, grabbing, hold and move");
|
||||
var modelPosition = getModelHoldPosition(whichSide);
|
||||
var properties = { position: { x: modelPosition.x,
|
||||
y: modelPosition.y,
|
||||
z: modelPosition.z },
|
||||
};
|
||||
Models.editModel(handModel, properties);
|
||||
} else {
|
||||
debugPrint(">>>>> " + handMessage + "-MODEL IN HAND, not grabbing, RELEASE!!!");
|
||||
if (whichSide == LEFT_PALM) {
|
||||
leftModelAlreadyInHand = false;
|
||||
leftHandModel = false;
|
||||
} else {
|
||||
rightModelAlreadyInHand = false;
|
||||
rightHandModel = false;
|
||||
}
|
||||
|
||||
/*
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = modelPosition;
|
||||
options.volume = 1.0;
|
||||
Audio.playSound(throwSound, options);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkController(deltaTime) {
|
||||
var numberOfButtons = Controller.getNumberOfButtons();
|
||||
var numberOfTriggers = Controller.getNumberOfTriggers();
|
||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||
|
||||
// this is expected for hydras
|
||||
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
||||
debugPrint("no hydra connected?");
|
||||
return; // bail if no hydra
|
||||
}
|
||||
|
||||
checkControllerSide(LEFT_PALM);
|
||||
checkControllerSide(RIGHT_PALM);
|
||||
}
|
||||
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(checkController);
|
|
@ -19,6 +19,12 @@ function mouseMoveEvent(event) {
|
|||
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
if (!intersection.accurate) {
|
||||
print(">>> NOTE: intersection not accurate. will try calling Voxels.findRayIntersectionBlocking()");
|
||||
intersection = Voxels.findRayIntersectionBlocking(pickRay);
|
||||
print(">>> AFTER BLOCKING CALL intersection.accurate=" + intersection.accurate);
|
||||
}
|
||||
|
||||
if (intersection.intersects) {
|
||||
print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", "
|
||||
+ intersection.voxel.green + ", " + intersection.voxel.blue);
|
||||
|
|
291
examples/swissArmyJetpack.js
Normal file
291
examples/swissArmyJetpack.js
Normal file
|
@ -0,0 +1,291 @@
|
|||
//
|
||||
// swissArmyJetpack.js
|
||||
// examples
|
||||
//
|
||||
// Created by Andrew Meadows 2014.04.24
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is a work in progress. It will eventually be able to move the avatar around,
|
||||
// toggle collision groups, modify avatar movement options, and other stuff (maybe trigger animations).
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// misc global constants
|
||||
var NUMBER_OF_COLLISION_BUTTONS = 3;
|
||||
var NUMBER_OF_BUTTONS = 4;
|
||||
var DOWN = { x: 0.0, y: -1.0, z: 0.0 };
|
||||
var MAX_VOXEL_SCAN_DISTANCE = 30.0;
|
||||
|
||||
// behavior transition thresholds
|
||||
var MIN_FLYING_SPEED = 3.0;
|
||||
var MIN_COLLISIONLESS_SPEED = 5.0;
|
||||
var MAX_WALKING_SPEED = 30.0;
|
||||
var MAX_COLLIDABLE_SPEED = 35.0;
|
||||
|
||||
// button URL and geometry/UI tuning
|
||||
var BUTTON_IMAGE_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg";
|
||||
var DISABLED_OFFSET_Y = 12;
|
||||
var ENABLED_OFFSET_Y = 55 + 12;
|
||||
var UI_BUFFER = 1;
|
||||
var OFFSET_X = UI_BUFFER;
|
||||
var OFFSET_Y = 200;
|
||||
var BUTTON_WIDTH = 30;
|
||||
var BUTTON_HEIGHT = 30;
|
||||
var TEXT_OFFSET_X = OFFSET_X + BUTTON_WIDTH + UI_BUFFER;
|
||||
var TEXT_HEIGHT = BUTTON_HEIGHT;
|
||||
var TEXT_WIDTH = 210;
|
||||
|
||||
var MSEC_PER_SECOND = 1000;
|
||||
var RAYCAST_EXPIRY_PERIOD = MSEC_PER_SECOND / 16;
|
||||
var COLLISION_EXPIRY_PERIOD = 2 * MSEC_PER_SECOND;
|
||||
var GRAVITY_ON_EXPIRY_PERIOD = MSEC_PER_SECOND / 2;
|
||||
var GRAVITY_OFF_EXPIRY_PERIOD = MSEC_PER_SECOND / 8;
|
||||
|
||||
var dater = new Date();
|
||||
var raycastExpiry = dater.getTime() + RAYCAST_EXPIRY_PERIOD;
|
||||
var gravityOnExpiry = dater.getTime() + GRAVITY_ON_EXPIRY_PERIOD;
|
||||
var gravityOffExpiry = dater.getTime() + GRAVITY_OFF_EXPIRY_PERIOD;
|
||||
var collisionOnExpiry = dater.getTime() + COLLISION_EXPIRY_PERIOD;
|
||||
|
||||
// avatar state
|
||||
var velocity = { x: 0.0, y: 0.0, z: 0.0 };
|
||||
var standing = false;
|
||||
|
||||
// speedometer globals
|
||||
var speed = 0.0;
|
||||
var lastPosition = MyAvatar.position;
|
||||
var speedometer = Overlays.addOverlay("text", {
|
||||
x: OFFSET_X,
|
||||
y: OFFSET_Y - BUTTON_HEIGHT,
|
||||
width: BUTTON_WIDTH + UI_BUFFER + TEXT_WIDTH,
|
||||
height: TEXT_HEIGHT,
|
||||
color: { red: 0, green: 0, blue: 0 },
|
||||
textColor: { red: 255, green: 0, blue: 0},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: "Speed: 0.0"
|
||||
});
|
||||
|
||||
// collision group buttons
|
||||
var buttons = new Array();
|
||||
var labels = new Array();
|
||||
|
||||
var labelContents = new Array();
|
||||
labelContents[0] = "Collide with Avatars";
|
||||
labelContents[1] = "Collide with Voxels";
|
||||
labelContents[2] = "Collide with Particles";
|
||||
labelContents[3] = "Use local gravity";
|
||||
var groupBits = 0;
|
||||
|
||||
var enabledColors = new Array();
|
||||
enabledColors[0] = { red: 255, green: 0, blue: 0};
|
||||
enabledColors[1] = { red: 0, green: 255, blue: 0};
|
||||
enabledColors[2] = { red: 0, green: 0, blue: 255};
|
||||
enabledColors[3] = { red: 255, green: 255, blue: 0};
|
||||
|
||||
var disabledColors = new Array();
|
||||
disabledColors[0] = { red: 90, green: 75, blue: 75};
|
||||
disabledColors[1] = { red: 75, green: 90, blue: 75};
|
||||
disabledColors[2] = { red: 75, green: 75, blue: 90};
|
||||
disabledColors[3] = { red: 90, green: 90, blue: 75};
|
||||
|
||||
var buttonStates = new Array();
|
||||
|
||||
for (i = 0; i < NUMBER_OF_BUTTONS; i++) {
|
||||
var offsetS = 12
|
||||
var offsetT = DISABLED_OFFSET_Y;
|
||||
|
||||
buttons[i] = Overlays.addOverlay("image", {
|
||||
x: OFFSET_X,
|
||||
y: OFFSET_Y + (BUTTON_HEIGHT * i),
|
||||
width: BUTTON_WIDTH,
|
||||
height: BUTTON_HEIGHT,
|
||||
subImage: { x: offsetS, y: offsetT, width: BUTTON_WIDTH, height: BUTTON_HEIGHT },
|
||||
imageURL: BUTTON_IMAGE_URL,
|
||||
color: disabledColors[i],
|
||||
alpha: 1,
|
||||
});
|
||||
|
||||
labels[i] = Overlays.addOverlay("text", {
|
||||
x: TEXT_OFFSET_X,
|
||||
y: OFFSET_Y + (BUTTON_HEIGHT * i),
|
||||
width: TEXT_WIDTH,
|
||||
height: TEXT_HEIGHT,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 0, blue: 0},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: labelContents[i]
|
||||
});
|
||||
|
||||
buttonStates[i] = false;
|
||||
}
|
||||
|
||||
|
||||
// functions
|
||||
|
||||
function updateButton(i, enabled) {
|
||||
var offsetY = DISABLED_OFFSET_Y;
|
||||
var buttonColor = disabledColors[i];
|
||||
if (enabled) {
|
||||
offsetY = ENABLED_OFFSET_Y;
|
||||
buttonColor = enabledColors[i];
|
||||
if (i == 0) {
|
||||
groupBits |= COLLISION_GROUP_AVATARS;
|
||||
} else if (i == 1) {
|
||||
groupBits |= COLLISION_GROUP_VOXELS;
|
||||
} else if (i == 2) {
|
||||
groupBits |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
groupBits &= ~COLLISION_GROUP_AVATARS;
|
||||
} else if (i == 1) {
|
||||
groupBits &= ~COLLISION_GROUP_VOXELS;
|
||||
} else if (i == 2) {
|
||||
groupBits &= ~COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
if (groupBits != MyAvatar.collisionGroups) {
|
||||
MyAvatar.collisionGroups = groupBits;
|
||||
}
|
||||
|
||||
Overlays.editOverlay(buttons[i], { subImage: { y: offsetY } } );
|
||||
Overlays.editOverlay(buttons[i], { color: buttonColor } );
|
||||
buttonStates[i] = enabled;
|
||||
}
|
||||
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
for (i = 0; i < NUMBER_OF_BUTTONS; i++) {
|
||||
Overlays.deleteOverlay(buttons[i]);
|
||||
Overlays.deleteOverlay(labels[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(speedometer);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
function updateSpeedometerDisplay() {
|
||||
Overlays.editOverlay(speedometer, { text: "Speed: " + speed.toFixed(2) });
|
||||
}
|
||||
Script.setInterval(updateSpeedometerDisplay, 100);
|
||||
|
||||
function disableArtificialGravity() {
|
||||
MyAvatar.motionBehaviors = MyAvatar.motionBehaviors & ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
updateButton(3, false);
|
||||
}
|
||||
|
||||
function enableArtificialGravity() {
|
||||
// NOTE: setting the gravity automatically sets the AVATAR_MOTION_OBEY_LOCAL_GRAVITY behavior bit.
|
||||
MyAvatar.gravity = DOWN;
|
||||
updateButton(3, true);
|
||||
// also enable collisions with voxels
|
||||
groupBits |= COLLISION_GROUP_VOXELS;
|
||||
updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
|
||||
}
|
||||
|
||||
// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays
|
||||
function update(deltaTime) {
|
||||
if (groupBits != MyAvatar.collisionGroups) {
|
||||
groupBits = MyAvatar.collisionGroups;
|
||||
updateButton(0, groupBits & COLLISION_GROUP_AVATARS);
|
||||
updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
|
||||
updateButton(2, groupBits & COLLISION_GROUP_PARTICLES);
|
||||
}
|
||||
|
||||
// measure speed
|
||||
var distance = Vec3.distance(MyAvatar.position, lastPosition);
|
||||
speed = 0.8 * speed + 0.2 * distance / deltaTime;
|
||||
lastPosition = MyAvatar.position;
|
||||
|
||||
dater = new Date();
|
||||
var now = dater.getTime();
|
||||
|
||||
// transition gravity
|
||||
if (raycastExpiry < now) {
|
||||
// scan for landing platform
|
||||
ray = { origin: MyAvatar.position, direction: DOWN };
|
||||
var intersection = Voxels.findRayIntersection(ray);
|
||||
// NOTE: it is possible for intersection.intersects to be false when it should be true
|
||||
// (perhaps the raycast failed to lock the octree thread?). To workaround this problem
|
||||
// we only transition on repeated failures.
|
||||
|
||||
if (intersection.intersects) {
|
||||
// compute distance to voxel
|
||||
var v = intersection.voxel;
|
||||
var maxCorner = Vec3.sum({ x: v.x, y: v.y, z: v.z }, {x: v.s, y: v.s, z: v.s });
|
||||
var distance = lastPosition.y - maxCorner.y;
|
||||
|
||||
if (distance < MAX_VOXEL_SCAN_DISTANCE) {
|
||||
if (speed < MIN_FLYING_SPEED &&
|
||||
gravityOnExpiry < now &&
|
||||
!(MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY)) {
|
||||
enableArtificialGravity();
|
||||
}
|
||||
if (speed < MAX_WALKING_SPEED) {
|
||||
gravityOffExpiry = now + GRAVITY_OFF_EXPIRY_PERIOD;
|
||||
} else if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) {
|
||||
disableArtificialGravity();
|
||||
}
|
||||
} else {
|
||||
// distance too far
|
||||
if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) {
|
||||
disableArtificialGravity();
|
||||
}
|
||||
gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD;
|
||||
}
|
||||
} else {
|
||||
// no intersection
|
||||
if (gravityOffExpiry < now && MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) {
|
||||
disableArtificialGravity();
|
||||
}
|
||||
gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD;
|
||||
}
|
||||
}
|
||||
if (speed > MAX_WALKING_SPEED && gravityOffExpiry < now) {
|
||||
if (MyAvatar.motionBehaviors & AVATAR_MOTION_OBEY_LOCAL_GRAVITY) {
|
||||
// turn off gravity
|
||||
MyAvatar.motionBehaviors = MyAvatar.motionBehaviors & ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
updateButton(3, false);
|
||||
}
|
||||
gravityOnExpiry = now + GRAVITY_ON_EXPIRY_PERIOD;
|
||||
}
|
||||
|
||||
// transition collidability with voxels
|
||||
if (speed < MIN_COLLISIONLESS_SPEED) {
|
||||
if (collisionOnExpiry < now && !(MyAvatar.collisionGroups & COLLISION_GROUP_VOXELS)) {
|
||||
// TODO: check to make sure not already colliding
|
||||
// enable collision with voxels
|
||||
groupBits |= COLLISION_GROUP_VOXELS;
|
||||
updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
|
||||
}
|
||||
} else {
|
||||
collisionOnExpiry = now + COLLISION_EXPIRY_PERIOD;
|
||||
}
|
||||
if (speed > MAX_COLLIDABLE_SPEED) {
|
||||
if (MyAvatar.collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
// disable collisions with voxels
|
||||
groupBits &= ~COLLISION_GROUP_VOXELS;
|
||||
updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
|
||||
}
|
||||
}
|
||||
}
|
||||
Script.update.connect(update);
|
||||
|
||||
|
||||
// we also handle click detection in our mousePressEvent()
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
for (i = 0; i < NUMBER_OF_COLLISION_BUTTONS; i++) {
|
||||
if (clickedOverlay == buttons[i]) {
|
||||
var enabled = !(buttonStates[i]);
|
||||
updateButton(i, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
12
examples/windowExample.js
Normal file
12
examples/windowExample.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var width = 0,
|
||||
height = 0;
|
||||
|
||||
function onUpdate(dt) {
|
||||
if (width != Window.innerWidth || height != Window.innerHeight) {
|
||||
width = Window.innerWidth;
|
||||
height = Window.innerHeight;
|
||||
print("New window dimensions: " + width + ", " + height);
|
||||
}
|
||||
}
|
||||
|
||||
Script.update.connect(onUpdate);
|
|
@ -49,7 +49,7 @@ configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVe
|
|||
|
||||
# grab the implementation and header files from src dirs
|
||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels)
|
||||
foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels particles models)
|
||||
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}")
|
||||
endforeach(SUBDIR)
|
||||
|
@ -124,6 +124,7 @@ link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
|
|||
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
|
|
@ -4,22 +4,22 @@
|
|||
<context>
|
||||
<name>Application</name>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1380"/>
|
||||
<location filename="src/Application.cpp" line="1481"/>
|
||||
<source>Export Voxels</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1381"/>
|
||||
<location filename="src/Application.cpp" line="1482"/>
|
||||
<source>Sparse Voxel Octree Files (*.svo)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3608"/>
|
||||
<location filename="src/Application.cpp" line="3465"/>
|
||||
<source>Open Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3609"/>
|
||||
<location filename="src/Application.cpp" line="3466"/>
|
||||
<source>JavaScript Files (*.js)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -113,18 +113,18 @@
|
|||
<context>
|
||||
<name>Menu</name>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="460"/>
|
||||
<location filename="src/Menu.cpp" line="554"/>
|
||||
<source>Open .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="462"/>
|
||||
<location filename="src/Menu.cpp" line="474"/>
|
||||
<location filename="src/Menu.cpp" line="556"/>
|
||||
<location filename="src/Menu.cpp" line="568"/>
|
||||
<source>Text files (*.ini)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="472"/>
|
||||
<location filename="src/Menu.cpp" line="566"/>
|
||||
<source>Save .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -271,4 +271,57 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RunningScriptsWidget</name>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="14"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="140"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="39"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="141"/>
|
||||
<source><html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="63"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="142"/>
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Currently running</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="89"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="143"/>
|
||||
<source>Reload all</source>
|
||||
<oldsource>Reload All</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="116"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="144"/>
|
||||
<source>Stop all</source>
|
||||
<oldsource>Stop All</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="137"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="145"/>
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="154"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="146"/>
|
||||
<source>(click a script or use the 1-9 keys to load and run it)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="202"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="148"/>
|
||||
<source>There are no scripts currently running.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
58
interface/resources/images/hifi-logo.svg
Normal file
58
interface/resources/images/hifi-logo.svg
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 55.5 55.5" enable-background="new 0 0 55.5 55.5" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M47.4,8.1C42.1,2.9,35.2,0,27.8,0C20.3,0,13.4,2.9,8.1,8.1C2.9,13.4,0,20.3,0,27.8
|
||||
c0,7.4,2.9,14.4,8.1,19.6c5.2,5.2,12.2,8.1,19.6,8.1c7.4,0,14.4-2.9,19.6-8.1c5.2-5.2,8.1-12.2,8.1-19.6
|
||||
C55.5,20.3,52.6,13.4,47.4,8.1z M45.4,45.4c-4.7,4.7-11,7.3-17.7,7.3c-6.7,0-12.9-2.6-17.7-7.3c-4.7-4.7-7.3-11-7.3-17.7
|
||||
c0-6.7,2.6-12.9,7.3-17.7c4.7-4.7,11-7.3,17.7-7.3c6.7,0,12.9,2.6,17.7,7.3c4.7,4.7,7.3,11,7.3,17.7
|
||||
C52.7,34.4,50.1,40.7,45.4,45.4z"/>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M20.3,37.6c-0.6,0-1.2-0.5-1.2-1.2V15c0-0.6,0.5-1.2,1.2-1.2c0.6,0,1.2,0.5,1.2,1.2v21.5
|
||||
C21.5,37.1,21,37.6,20.3,37.6z"/>
|
||||
<path fill="#0E7077" d="M21.7,14.4c-0.8,0.8-2,0.8-2.7,0c-0.8-0.8-0.8-2,0-2.7c0.8-0.8,2-0.8,2.7,0
|
||||
C22.5,12.5,22.5,13.7,21.7,14.4"/>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M20.3,16.2c-0.8,0-1.6-0.3-2.2-0.9c-1.2-1.2-1.2-3.2,0-4.4c0.6-0.6,1.4-0.9,2.2-0.9
|
||||
c0.8,0,1.6,0.3,2.2,0.9c0.6,0.6,0.9,1.4,0.9,2.2c0,0.8-0.3,1.6-0.9,2.2C21.9,15.8,21.2,16.2,20.3,16.2z M20.3,12.3
|
||||
c-0.2,0-0.4,0.1-0.6,0.2c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6
|
||||
C20.7,12.4,20.5,12.3,20.3,12.3z"/>
|
||||
</g>
|
||||
<path fill="#0E7077" d="M19,37c0.8-0.8,2-0.8,2.7,0c0.8,0.8,0.8,2,0,2.7c-0.8,0.8-2,0.8-2.7,0C18.2,39,18.2,37.8,19,37"/>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M20.3,41.5c-0.8,0-1.6-0.3-2.2-0.9c-0.6-0.6-0.9-1.4-0.9-2.2c0-0.8,0.3-1.6,0.9-2.2
|
||||
c0.6-0.6,1.4-0.9,2.2-0.9c0.8,0,1.6,0.3,2.2,0.9c0.6,0.6,0.9,1.4,0.9,2.2c0,0.8-0.3,1.6-0.9,2.2C21.9,41.2,21.2,41.5,20.3,41.5z
|
||||
M20.3,37.6c-0.2,0-0.4,0.1-0.6,0.2c-0.1,0.1-0.2,0.3-0.2,0.6c0,0.2,0.1,0.4,0.2,0.6c0.3,0.3,0.8,0.3,1.1,0
|
||||
c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6C20.7,37.7,20.5,37.6,20.3,37.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M35.2,41.7c-0.6,0-1.2-0.5-1.2-1.2V19c0-0.6,0.5-1.2,1.2-1.2c0.6,0,1.2,0.5,1.2,1.2v21.5
|
||||
C36.4,41.2,35.8,41.7,35.2,41.7z"/>
|
||||
</g>
|
||||
<path fill="#0E7077" d="M36.6,18.5c-0.8,0.8-2,0.8-2.7,0c-0.8-0.8-0.8-2,0-2.7c0.8-0.8,2-0.8,2.7,0
|
||||
C37.3,16.5,37.3,17.7,36.6,18.5"/>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M35.2,20.2c-0.8,0-1.6-0.3-2.2-0.9c-1.2-1.2-1.2-3.2,0-4.4c0.6-0.6,1.4-0.9,2.2-0.9
|
||||
c0.8,0,1.6,0.3,2.2,0.9c0.6,0.6,0.9,1.4,0.9,2.2c0,0.8-0.3,1.6-0.9,2.2C36.8,19.9,36,20.2,35.2,20.2z M35.2,16.3
|
||||
c-0.2,0-0.4,0.1-0.6,0.2c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.1-0.1,0.2-0.3,0.2-0.6c0-0.2-0.1-0.4-0.2-0.6
|
||||
C35.6,16.4,35.4,16.3,35.2,16.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M35.2,45.6c-0.8,0-1.6-0.3-2.2-0.9c-1.2-1.2-1.2-3.2,0-4.4c0.6-0.6,1.4-0.9,2.2-0.9
|
||||
c0.8,0,1.6,0.3,2.2,0.9c0.6,0.6,0.9,1.4,0.9,2.2c0,0.8-0.3,1.6-0.9,2.2C36.8,45.2,36,45.6,35.2,45.6z M35.2,41.7
|
||||
c-0.2,0-0.4,0.1-0.6,0.2c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0c0.1-0.1,0.2-0.3,0.2-0.6s-0.1-0.4-0.2-0.6
|
||||
C35.6,41.8,35.4,41.7,35.2,41.7z"/>
|
||||
</g>
|
||||
<path fill="#0E7077" d="M33.8,41.1c0.8-0.8,2-0.8,2.7,0c0.8,0.8,0.8,2,0,2.7c-0.8,0.8-2,0.8-2.7,0C33.1,43.1,33.1,41.8,33.8,41.1
|
||||
"/>
|
||||
<g>
|
||||
|
||||
<rect x="19.6" y="26.2" transform="matrix(0.9064 0.4224 -0.4224 0.9064 14.1626 -9.1647)" fill="#0E7077" width="16.4" height="2.3"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 3.6 KiB |
17
interface/resources/images/login.svg
Normal file
17
interface/resources/images/login.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 23.6 22.3" enable-background="new 0 0 23.6 22.3" xml:space="preserve">
|
||||
<g>
|
||||
<g id="Your_Icon_8_">
|
||||
<g>
|
||||
<polygon fill="#E7EEEE" points="6.8,7.8 0,7.8 0,14.1 6.8,14.1 6.8,19 14.4,11 6.8,3.1 "/>
|
||||
<polygon fill="#E7EEEE" points="12.9,3.1 14.3,3.1 14.3,1.4 12.9,1.4 12.9,1.4 11.2,1.4 11.2,6.5 12.9,8.2 "/>
|
||||
<polygon fill="#E7EEEE" points="12.9,13.7 11.2,15.5 11.2,20.7 14.3,20.7 14.3,19.1 12.9,19.1 "/>
|
||||
<path fill="#E7EEEE" d="M15,0v22.3l8.5-2.4V2.4L15,0z M17.8,12.2h-0.3c-0.4,0-0.7-0.4-0.8-0.9h-0.3v-0.7h0.3
|
||||
c0.1-0.5,0.4-0.9,0.8-0.9h0.3c0.5,0,0.8,0.5,0.8,1.2C18.7,11.6,18.3,12.2,17.8,12.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 1 KiB |
|
@ -1,98 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="44px"
|
||||
height="44px"
|
||||
viewBox="0 0 44 44"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="close.svg">
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Slice 1</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview14"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.7926636"
|
||||
inkscape:cx="57.156875"
|
||||
inkscape:cy="33.978935"
|
||||
inkscape:window-x="536"
|
||||
inkscape:window-y="258"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<title
|
||||
id="title4">Slice 1</title>
|
||||
<description
|
||||
id="description6">Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<g
|
||||
id="Page-1"
|
||||
sketch:type="MSPage"
|
||||
transform="translate(0.52733383,0.52733392)"
|
||||
style="fill:none;stroke:none" />
|
||||
<rect
|
||||
id="rect2993"
|
||||
width="5.7796612"
|
||||
height="26.101694"
|
||||
x="19.730724"
|
||||
y="16.374792"
|
||||
ry="3.371469" />
|
||||
<rect
|
||||
id="rect2995"
|
||||
width="35.423729"
|
||||
height="2.9830508"
|
||||
x="4.6290293"
|
||||
y="2.3917408"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
id="rect2999"
|
||||
width="31.135593"
|
||||
height="21.067797"
|
||||
x="6.6798768"
|
||||
y="4.0697069"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
style="fill:#ff0000"
|
||||
id="rect3003"
|
||||
width="27.779657"
|
||||
height="17.711861"
|
||||
x="8.171401"
|
||||
y="5.3747911"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
style="fill:#999999"
|
||||
id="rect3011"
|
||||
width="3.1694915"
|
||||
height="13.983051"
|
||||
x="21.035809"
|
||||
y="25.883266"
|
||||
ry="0.46610171" />
|
||||
</svg>
|
Before (image error) Size: 2.5 KiB |
|
@ -1,106 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="44px"
|
||||
height="44px"
|
||||
viewBox="0 0 44 44"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="pin.svg">
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Slice 1</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1058"
|
||||
id="namedview14"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.7926636"
|
||||
inkscape:cx="39.754857"
|
||||
inkscape:cy="33.978935"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<title
|
||||
id="title4">Slice 1</title>
|
||||
<description
|
||||
id="description6">Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<g
|
||||
id="Page-1"
|
||||
sketch:type="MSPage"
|
||||
transform="translate(0.52733383,0.52733392)"
|
||||
style="fill:none;stroke:none" />
|
||||
<rect
|
||||
style="fill:#b3b3b3"
|
||||
id="rect3013"
|
||||
width="43.768692"
|
||||
height="44.296078"
|
||||
x="0.26366693"
|
||||
y="-0.032378189"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
id="rect2993"
|
||||
width="5.7796612"
|
||||
height="26.101694"
|
||||
x="19.730724"
|
||||
y="16.374792"
|
||||
ry="3.371469" />
|
||||
<rect
|
||||
id="rect2995"
|
||||
width="35.423729"
|
||||
height="2.9830508"
|
||||
x="4.6290293"
|
||||
y="2.3917408"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
id="rect2999"
|
||||
width="31.135593"
|
||||
height="21.067797"
|
||||
x="6.6798768"
|
||||
y="4.0697069"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
style="fill:#ff0000"
|
||||
id="rect3003"
|
||||
width="27.779657"
|
||||
height="17.711861"
|
||||
x="8.171401"
|
||||
y="5.3747911"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
style="fill:#999999"
|
||||
id="rect3011"
|
||||
width="3.1694915"
|
||||
height="13.983051"
|
||||
x="21.035809"
|
||||
y="25.883266"
|
||||
ry="0.46610171" />
|
||||
</svg>
|
Before (image error) Size: 2.7 KiB |
7
interface/resources/images/plus-white.svg
Normal file
7
interface/resources/images/plus-white.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Your_Icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="23 23 54 54" enable-background="new 23 23 54 54" xml:space="preserve">
|
||||
<polygon fill="#FFFFFF" points="77,41 59,41 59,23 41,23 41,41 23,41 23,59 41,59 41,77 59,77 59,59 77,59 "/>
|
||||
</svg>
|
After (image error) Size: 565 B |
|
@ -33,6 +33,7 @@ QPushButton#searchButton {
|
|||
}
|
||||
|
||||
QPushButton#revealLogButton {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background: url(styles/txt-file.svg);
|
||||
background-repeat: none;
|
||||
background-position: left center;
|
||||
|
@ -50,9 +51,9 @@ QCheckBox {
|
|||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
image: url(:/styles/unchecked.svg);
|
||||
image: url(styles/unchecked.svg);
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
image: url(:/styles/checked.svg);
|
||||
image: url(styles/checked.svg);
|
||||
}
|
|
@ -30,7 +30,6 @@
|
|||
#include <QImage>
|
||||
#include <QInputDialog>
|
||||
#include <QKeyEvent>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QMouseEvent>
|
||||
#include <QNetworkAccessManager>
|
||||
|
@ -55,6 +54,7 @@
|
|||
#include <AccountManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <Logging.h>
|
||||
#include <ModelsScriptingInterface.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
|
@ -77,6 +77,8 @@
|
|||
#include "scripting/ClipboardScriptingInterface.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
#include "scripting/SettingsScriptingInterface.h"
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
#include "scripting/LocationScriptingInterface.h"
|
||||
|
||||
#include "ui/InfoView.h"
|
||||
#include "ui/Snapshot.h"
|
||||
|
@ -132,7 +134,7 @@ QString& Application::resourcesPath() {
|
|||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||
QApplication(argc, argv),
|
||||
_window(new QMainWindow(desktop())),
|
||||
_window(new MainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
_nodeThread(new QThread(this)),
|
||||
_datagramProcessor(),
|
||||
|
@ -164,7 +166,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_packetsPerSecond(0),
|
||||
_bytesPerSecond(0),
|
||||
_previousScriptLocation(),
|
||||
_logger(new FileLogger(this))
|
||||
_logger(new FileLogger(this)),
|
||||
_runningScriptsWidget(new RunningScriptsWidget(_window)),
|
||||
_runningScriptsWidgetWasVisible(false)
|
||||
{
|
||||
// init GnuTLS for DTLS with domain-servers
|
||||
DTLSClientSession::globalInit();
|
||||
|
@ -263,12 +267,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
|
||||
// tell the NodeList instance who to tell the domain server we care about
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::VoxelServer << NodeType::ParticleServer
|
||||
<< NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
|
||||
<< NodeType::MetavoxelServer);
|
||||
|
||||
// connect to the packet sent signal of the _voxelEditSender and the _particleEditSender
|
||||
connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
connect(&_modelEditSender, &ModelEditPacketSender::packetSent, this, &Application::packetSent);
|
||||
|
||||
// move the silentNodeTimer to the _nodeThread
|
||||
QTimer* silentNodeTimer = new QTimer();
|
||||
|
@ -312,14 +317,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// Tell our voxel edit sender about our known jurisdictions
|
||||
_voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions);
|
||||
_particleEditSender.setServerJurisdictions(&_particleServerJurisdictions);
|
||||
_modelEditSender.setServerJurisdictions(&_modelServerJurisdictions);
|
||||
|
||||
Particle::setVoxelEditPacketSender(&_voxelEditSender);
|
||||
Particle::setParticleEditPacketSender(&_particleEditSender);
|
||||
|
||||
// when -url in command line, teleport to location
|
||||
urlGoTo(argc, constArgv);
|
||||
|
||||
// For now we're going to set the PPS for outbound packets to be super high, this is
|
||||
// probably not the right long term solution. But for now, we're going to do this to
|
||||
// allow you to move a particle around in your hand
|
||||
_particleEditSender.setPacketsPerSecond(3000); // super high!!
|
||||
_modelEditSender.setPacketsPerSecond(3000); // super high!!
|
||||
|
||||
// Set the sixense filtering
|
||||
_sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
|
||||
|
@ -331,7 +341,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree());
|
||||
LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard);
|
||||
|
||||
_window->addDockWidget(Qt::NoDockWidgetArea, _runningScriptsWidget = new RunningScriptsWidget());
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript);
|
||||
|
||||
|
@ -352,8 +361,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
QMutexLocker locker(&_settingsMutex);
|
||||
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
|
||||
}
|
||||
//When -url in command line, teleport to location
|
||||
urlGoTo(argc, constArgv);
|
||||
|
||||
connect(_window, &MainWindow::windowGeometryChanged,
|
||||
_runningScriptsWidget, &RunningScriptsWidget::setBoundary);
|
||||
|
||||
//When -url in command line, teleport to location
|
||||
urlGoTo(argc, constArgv);
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -387,6 +400,7 @@ Application::~Application() {
|
|||
_voxelHideShowThread.terminate();
|
||||
_voxelEditSender.terminate();
|
||||
_particleEditSender.terminate();
|
||||
_modelEditSender.terminate();
|
||||
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
|
@ -487,6 +501,8 @@ void Application::initializeGL() {
|
|||
_voxelEditSender.initialize(_enableProcessVoxelsThread);
|
||||
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
|
||||
_particleEditSender.initialize(_enableProcessVoxelsThread);
|
||||
_modelEditSender.initialize(_enableProcessVoxelsThread);
|
||||
|
||||
if (_enableProcessVoxelsThread) {
|
||||
qDebug("Voxel parsing thread created.");
|
||||
}
|
||||
|
@ -757,6 +773,8 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod
|
|||
channel = BandwidthMeter::AVATARS;
|
||||
break;
|
||||
case NodeType::VoxelServer:
|
||||
case NodeType::ParticleServer:
|
||||
case NodeType::ModelServer:
|
||||
channel = BandwidthMeter::VOXELS;
|
||||
break;
|
||||
default:
|
||||
|
@ -857,7 +875,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_G:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Gravity);
|
||||
Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1263,8 +1281,8 @@ void Application::dropEvent(QDropEvent *event) {
|
|||
|
||||
void Application::sendPingPackets() {
|
||||
QByteArray pingPacket = NodeList::getInstance()->constructPingPacket();
|
||||
controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer
|
||||
<< NodeType::ParticleServer
|
||||
controlledBroadcastToNodes(pingPacket, NodeSet()
|
||||
<< NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer
|
||||
<< NodeType::AudioMixer << NodeType::AvatarMixer
|
||||
<< NodeType::MetavoxelServer);
|
||||
}
|
||||
|
@ -1656,6 +1674,9 @@ void Application::init() {
|
|||
_particles.init();
|
||||
_particles.setViewFrustum(getViewFrustum());
|
||||
|
||||
_models.init();
|
||||
_models.setViewFrustum(getViewFrustum());
|
||||
|
||||
_metavoxels.init();
|
||||
|
||||
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager);
|
||||
|
@ -1798,35 +1819,63 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
|
||||
|
||||
FaceTracker* tracker = getActiveFaceTracker();
|
||||
|
||||
bool isLookingAtSomeone = false;
|
||||
glm::vec3 lookAtSpot;
|
||||
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
// When I am in mirror mode, just look right at the camera (myself)
|
||||
lookAtSpot = _myCamera.getPosition();
|
||||
|
||||
} else {
|
||||
// look in direction of the mouse ray, but use distance from intersection, if any
|
||||
float distance = TREE_SCALE;
|
||||
if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) {
|
||||
distance = glm::distance(_mouseRayOrigin,
|
||||
static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition());
|
||||
isLookingAtSomeone = true;
|
||||
// If I am looking at someone else, look directly at one of their eyes
|
||||
if (tracker) {
|
||||
// If tracker active, look at the eye for the side my gaze is biased toward
|
||||
if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) {
|
||||
// Look at their right eye
|
||||
lookAtSpot = static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->getRightEyePosition();
|
||||
} else {
|
||||
// Look at their left eye
|
||||
lookAtSpot = static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->getLeftEyePosition();
|
||||
}
|
||||
} else {
|
||||
// Need to add randomly looking back and forth between left and right eye for case with no tracker
|
||||
lookAtSpot = static_cast<Avatar*>(_myAvatar->getLookAtTargetAvatar())->getHead()->getEyePosition();
|
||||
}
|
||||
} else {
|
||||
// I am not looking at anyone else, so just look forward
|
||||
lookAtSpot = _myAvatar->getHead()->calculateAverageEyePosition() + (_myAvatar->getHead()->getFinalOrientation() * glm::vec3(0.f, 0.f, -TREE_SCALE));
|
||||
}
|
||||
// TODO: Add saccade to mouse pointer when stable, IF not looking at someone (since we know we are looking at it)
|
||||
/*
|
||||
const float FIXED_MIN_EYE_DISTANCE = 0.3f;
|
||||
float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f :
|
||||
glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition()));
|
||||
lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance);
|
||||
*/
|
||||
|
||||
}
|
||||
FaceTracker* tracker = getActiveFaceTracker();
|
||||
//
|
||||
// Deflect the eyes a bit to match the detected Gaze from 3D camera if active
|
||||
//
|
||||
if (tracker) {
|
||||
float eyePitch = tracker->getEstimatedEyePitch();
|
||||
float eyeYaw = tracker->getEstimatedEyeYaw();
|
||||
|
||||
const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f;
|
||||
// deflect using Faceshift gaze data
|
||||
glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition();
|
||||
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
|
||||
float deflection = Menu::getInstance()->getFaceshiftEyeDeflection();
|
||||
if (isLookingAtSomeone) {
|
||||
deflection *= GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT;
|
||||
}
|
||||
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
|
||||
eyePitch * pitchSign * deflection, eyeYaw * deflection, 0.0f))) *
|
||||
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
|
||||
}
|
||||
|
||||
_myAvatar->getHead()->setLookAtPosition(lookAtSpot);
|
||||
}
|
||||
|
||||
|
@ -1868,6 +1917,7 @@ void Application::updateThreads(float deltaTime) {
|
|||
_voxelHideShowThread.threadRoutine();
|
||||
_voxelEditSender.threadRoutine();
|
||||
_particleEditSender.threadRoutine();
|
||||
_modelEditSender.threadRoutine();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1986,6 +2036,8 @@ void Application::update(float deltaTime) {
|
|||
_particles.update(); // update the particles...
|
||||
_particleCollisionSystem.update(); // collide the particles...
|
||||
|
||||
_models.update(); // update the models...
|
||||
|
||||
_overlays.update(deltaTime);
|
||||
|
||||
// let external parties know we're updating
|
||||
|
@ -2024,6 +2076,7 @@ void Application::updateMyAvatar(float deltaTime) {
|
|||
_lastQueriedTime = now;
|
||||
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
|
||||
queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
|
||||
queryOctree(NodeType::ModelServer, PacketTypeModelQuery, _modelServerJurisdictions);
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
}
|
||||
}
|
||||
|
@ -2325,6 +2378,7 @@ void Application::updateShadowMap() {
|
|||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render();
|
||||
_models.render();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
@ -2446,7 +2500,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
"Application::displaySide() ... atmosphere...");
|
||||
_environment.renderAtmospheres(whichCamera);
|
||||
}
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
|
@ -2491,6 +2544,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
_particles.render();
|
||||
}
|
||||
|
||||
// render models...
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... models...");
|
||||
_models.render();
|
||||
}
|
||||
|
||||
// render the ambient occlusion effect if enabled
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
|
@ -2630,7 +2690,6 @@ void Application::displayOverlay() {
|
|||
if (audioLevel > AUDIO_METER_SCALE_WIDTH) {
|
||||
audioLevel = AUDIO_METER_SCALE_WIDTH;
|
||||
}
|
||||
|
||||
bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME));
|
||||
|
||||
if ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) {
|
||||
|
@ -2703,7 +2762,7 @@ void Application::displayOverlay() {
|
|||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) {
|
||||
_myAvatar->renderHeadMouse();
|
||||
_myAvatar->renderHeadMouse(_glWidget->width(), _glWidget->height());
|
||||
}
|
||||
|
||||
// Display stats and log text onscreen
|
||||
|
@ -3086,6 +3145,9 @@ void Application::domainChanged(const QString& domainHostname) {
|
|||
// reset the particle renderer
|
||||
_particles.clear();
|
||||
|
||||
// reset the model renderer
|
||||
_models.clear();
|
||||
|
||||
// reset the voxels renderer
|
||||
_voxels.killLocalVoxels();
|
||||
}
|
||||
|
@ -3162,7 +3224,7 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
_voxelFades.push_back(fade);
|
||||
}
|
||||
|
||||
// If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
// If the particle server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
_particleServerJurisdictions.erase(_particleServerJurisdictions.find(nodeUUID));
|
||||
}
|
||||
|
||||
|
@ -3173,6 +3235,38 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
}
|
||||
_octreeSceneStatsLock.unlock();
|
||||
|
||||
} else if (node->getType() == NodeType::ModelServer) {
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_modelServerJurisdictions.find(nodeUUID) != _modelServerJurisdictions.end()) {
|
||||
unsigned char* rootCode = _modelServerJurisdictions[nodeUUID].getRootOctalCode();
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode, rootDetails);
|
||||
|
||||
qDebug("model server going away...... v[%f, %f, %f, %f]",
|
||||
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
|
||||
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
|
||||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFades.push_back(fade);
|
||||
}
|
||||
|
||||
// If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
_modelServerJurisdictions.erase(_modelServerJurisdictions.find(nodeUUID));
|
||||
}
|
||||
|
||||
// also clean up scene stats for that server
|
||||
_octreeSceneStatsLock.lockForWrite();
|
||||
if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {
|
||||
_octreeServerSceneStats.erase(nodeUUID);
|
||||
}
|
||||
_octreeSceneStatsLock.unlock();
|
||||
|
||||
} else if (node->getType() == NodeType::AvatarMixer) {
|
||||
// our avatar mixer has gone away - clear the hash of avatars
|
||||
_avatarManager.clearOtherAvatars();
|
||||
|
@ -3196,7 +3290,6 @@ void Application::trackIncomingVoxelPacket(const QByteArray& packet, const Share
|
|||
}
|
||||
|
||||
int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
|
||||
// parse the incoming stats datas stick it in a temporary object for now, while we
|
||||
|
@ -3223,16 +3316,21 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
|
|||
|
||||
// see if this is the first we've heard of this node...
|
||||
NodeToJurisdictionMap* jurisdiction = NULL;
|
||||
QString serverType;
|
||||
if (sendingNode->getType() == NodeType::VoxelServer) {
|
||||
jurisdiction = &_voxelServerJurisdictions;
|
||||
} else {
|
||||
serverType = "Voxel";
|
||||
} else if (sendingNode->getType() == NodeType::ParticleServer) {
|
||||
jurisdiction = &_particleServerJurisdictions;
|
||||
serverType = "Particle";
|
||||
} else {
|
||||
jurisdiction = &_modelServerJurisdictions;
|
||||
serverType = "Model";
|
||||
}
|
||||
|
||||
|
||||
if (jurisdiction->find(nodeUUID) == jurisdiction->end()) {
|
||||
qDebug("stats from new server... v[%f, %f, %f, %f]",
|
||||
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
qDebug("stats from new %s server... [%f, %f, %f, %f]",
|
||||
qPrintable(serverType), rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
|
||||
|
||||
// Add the jurisditionDetails object to the list of "fade outs"
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
|
||||
|
@ -3325,31 +3423,23 @@ void Application::reloadAllScripts() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::toggleRunningScriptsWidget()
|
||||
{
|
||||
if (!_runningScriptsWidget->toggleViewAction()->isChecked()) {
|
||||
_runningScriptsWidget->move(_window->geometry().topLeft().x(), _window->geometry().topLeft().y());
|
||||
_runningScriptsWidget->resize(0, _window->height());
|
||||
_runningScriptsWidget->toggleViewAction()->trigger();
|
||||
_runningScriptsWidget->grabKeyboard();
|
||||
void Application::manageRunningScriptsWidgetVisibility(bool shown) {
|
||||
if (_runningScriptsWidgetWasVisible && shown) {
|
||||
_runningScriptsWidget->show();
|
||||
} else if (_runningScriptsWidgetWasVisible && !shown) {
|
||||
_runningScriptsWidget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget);
|
||||
slideAnimation->setStartValue(_runningScriptsWidget->geometry());
|
||||
slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(),
|
||||
310, _runningScriptsWidget->height()));
|
||||
slideAnimation->setDuration(250);
|
||||
slideAnimation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
void Application::toggleRunningScriptsWidget() {
|
||||
if (_runningScriptsWidgetWasVisible) {
|
||||
_runningScriptsWidget->hide();
|
||||
_runningScriptsWidgetWasVisible = false;
|
||||
} else {
|
||||
_runningScriptsWidget->releaseKeyboard();
|
||||
|
||||
QPropertyAnimation* slideAnimation = new QPropertyAnimation(_runningScriptsWidget, "geometry", _runningScriptsWidget);
|
||||
slideAnimation->setStartValue(_runningScriptsWidget->geometry());
|
||||
slideAnimation->setEndValue(QRect(_window->geometry().topLeft().x(), _window->geometry().topLeft().y(),
|
||||
0, _runningScriptsWidget->height()));
|
||||
slideAnimation->setDuration(250);
|
||||
slideAnimation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
|
||||
QTimer::singleShot(260, _runningScriptsWidget->toggleViewAction(), SLOT(trigger()));
|
||||
_runningScriptsWidget->setBoundary(QRect(_window->geometry().topLeft(),
|
||||
_window->size()));
|
||||
_runningScriptsWidget->show();
|
||||
_runningScriptsWidgetWasVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3394,6 +3484,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
|
||||
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
|
||||
|
||||
scriptEngine->getModelsScriptingInterface()->setPacketSender(&_modelEditSender);
|
||||
scriptEngine->getModelsScriptingInterface()->setModelTree(_models.getTree());
|
||||
|
||||
// hook our avatar object into this script engine
|
||||
scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
|
||||
|
||||
|
@ -3406,6 +3499,15 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
|
||||
|
||||
scriptEngine->registerGlobalObject("Overlays", &_overlays);
|
||||
|
||||
QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", WindowScriptingInterface::getInstance());
|
||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||
LocationScriptingInterface::locationSetter, windowValue);
|
||||
|
||||
// register `location` on the global object.
|
||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||
LocationScriptingInterface::locationSetter);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
|
@ -3438,9 +3540,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
return scriptEngine;
|
||||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
QString Application::getPreviousScriptLocation() {
|
||||
QString suggestedName;
|
||||
|
||||
if (_previousScriptLocation.isEmpty()) {
|
||||
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
|
||||
|
@ -3450,14 +3551,22 @@ void Application::loadDialog() {
|
|||
} else {
|
||||
suggestedName = _previousScriptLocation;
|
||||
}
|
||||
return suggestedName;
|
||||
}
|
||||
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName,
|
||||
void Application::setPreviousScriptLocation(const QString& previousScriptLocation) {
|
||||
_previousScriptLocation = previousScriptLocation;
|
||||
QMutexLocker locker(&_settingsMutex);
|
||||
_settings->setValue("LastScriptLocation", _previousScriptLocation);
|
||||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"),
|
||||
getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!fileNameString.isEmpty()) {
|
||||
_previousScriptLocation = fileNameString;
|
||||
QMutexLocker locker(&_settingsMutex);
|
||||
_settings->setValue("LastScriptLocation", _previousScriptLocation);
|
||||
|
||||
setPreviousScriptLocation(fileNameString);
|
||||
loadScript(fileNameString);
|
||||
}
|
||||
}
|
||||
|
@ -3520,35 +3629,30 @@ void Application::parseVersionXml() {
|
|||
QString operatingSystem("ubuntu");
|
||||
#endif
|
||||
|
||||
QString releaseDate;
|
||||
QString releaseNotes;
|
||||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QString releaseNotes("Unavailable");
|
||||
QObject* sender = QObject::sender();
|
||||
|
||||
QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender));
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
QXmlStreamReader::TokenType token = xml.readNext();
|
||||
|
||||
if (token == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() == "ReleaseDate") {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
|
||||
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
xml.readNext();
|
||||
releaseDate = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "ReleaseNotes") {
|
||||
xml.readNext();
|
||||
releaseNotes = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "Version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == operatingSystem) {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
|
||||
new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl);
|
||||
}
|
||||
|
@ -3591,34 +3695,33 @@ void Application::takeSnapshot() {
|
|||
void Application::urlGoTo(int argc, const char * constArgv[]) {
|
||||
//Gets the url (hifi://domain/destination/orientation)
|
||||
QString customUrl = getCmdOption(argc, constArgv, "-url");
|
||||
|
||||
if (customUrl.startsWith("hifi://")) {
|
||||
if(customUrl.startsWith(CUSTOM_URL_SCHEME + "//")) {
|
||||
QStringList urlParts = customUrl.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
|
||||
if (urlParts.count() > 1) {
|
||||
if (urlParts.count() == 1) {
|
||||
// location coordinates or place name
|
||||
QString domain = urlParts[0];
|
||||
Menu::goToDomain(domain);
|
||||
} else if (urlParts.count() > 1) {
|
||||
// if url has 2 or more parts, the first one is domain name
|
||||
QString domain = urlParts[0];
|
||||
|
||||
|
||||
// second part is either a destination coordinate or
|
||||
// a place name
|
||||
QString destination = urlParts[1];
|
||||
|
||||
|
||||
// any third part is an avatar orientation.
|
||||
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
|
||||
|
||||
|
||||
Menu::goToDomain(domain);
|
||||
|
||||
|
||||
// goto either @user, #place, or x-xx,y-yy,z-zz
|
||||
// style co-ordinate.
|
||||
Menu::goTo(destination);
|
||||
|
||||
|
||||
if (!orientation.isEmpty()) {
|
||||
// location orientation
|
||||
Menu::goToOrientation(orientation);
|
||||
}
|
||||
} else if (urlParts.count() == 1) {
|
||||
// location coordinates or place name
|
||||
QString destination = urlParts[0];
|
||||
Menu::goTo(destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
|
@ -24,9 +25,11 @@
|
|||
#include <QSet>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QTouchEvent>
|
||||
#include <QUndoStack>
|
||||
|
||||
#include <ModelEditPacketSender.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
@ -37,6 +40,7 @@
|
|||
#include <ViewFrustum.h>
|
||||
#include <VoxelEditPacketSender.h>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Audio.h"
|
||||
#include "AudioReflector.h"
|
||||
#include "BuckyBalls.h"
|
||||
|
@ -48,7 +52,6 @@
|
|||
#include "Menu.h"
|
||||
#include "MetavoxelSystem.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "ParticleTreeRenderer.h"
|
||||
#include "Stars.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
@ -57,6 +60,8 @@
|
|||
#include "devices/Faceshift.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "devices/Visage.h"
|
||||
#include "models/ModelTreeRenderer.h"
|
||||
#include "particles/ParticleTreeRenderer.h"
|
||||
#include "renderer/AmbientOcclusionEffect.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
#include "renderer/GlowEffect.h"
|
||||
|
@ -85,7 +90,6 @@ class QAction;
|
|||
class QActionGroup;
|
||||
class QGLWidget;
|
||||
class QKeyEvent;
|
||||
class QMainWindow;
|
||||
class QMouseEvent;
|
||||
class QNetworkAccessManager;
|
||||
class QSettings;
|
||||
|
@ -124,6 +128,8 @@ public:
|
|||
void restoreSizeAndPosition();
|
||||
ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false);
|
||||
void loadScripts();
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
void storeSizeAndPosition();
|
||||
void clearScriptsBeforeRunning();
|
||||
void saveScripts();
|
||||
|
@ -136,6 +142,7 @@ public:
|
|||
void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
void focusOutEvent(QFocusEvent* event);
|
||||
void focusInEvent(QFocusEvent* event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
|
@ -173,6 +180,7 @@ public:
|
|||
VoxelTree* getVoxelTree() { return _voxels.getTree(); }
|
||||
ParticleTreeRenderer* getParticles() { return &_particles; }
|
||||
MetavoxelSystem* getMetavoxels() { return &_metavoxels; }
|
||||
ModelTreeRenderer* getModels() { return &_models; }
|
||||
bool getImportSucceded() { return _importSucceded; }
|
||||
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
|
||||
VoxelTree* getClipboard() { return &_clipboard; }
|
||||
|
@ -192,10 +200,10 @@ public:
|
|||
/// if you need to access the application settings, use lockSettings()/unlockSettings()
|
||||
QSettings* lockSettings() { _settingsMutex.lock(); return _settings; }
|
||||
void unlockSettings() { _settingsMutex.unlock(); }
|
||||
|
||||
|
||||
void saveSettings();
|
||||
|
||||
QMainWindow* getWindow() { return _window; }
|
||||
MainWindow* getWindow() { return _window; }
|
||||
NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; }
|
||||
void lockOctreeSceneStats() { _octreeSceneStatsLock.lockForRead(); }
|
||||
void unlockOctreeSceneStats() { _octreeSceneStatsLock.unlock(); }
|
||||
|
@ -243,6 +251,7 @@ public:
|
|||
glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
|
||||
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
|
||||
NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; }
|
||||
NodeToJurisdictionMap& getModelServerJurisdictions() { return _modelServerJurisdictions; }
|
||||
void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination);
|
||||
|
||||
void skipVersion(QString latestVersion);
|
||||
|
@ -312,6 +321,8 @@ private slots:
|
|||
|
||||
void parseVersionXml();
|
||||
|
||||
void manageRunningScriptsWidgetVisibility(bool shown);
|
||||
|
||||
private:
|
||||
void resetCamerasOnResizeGL(Camera& camera, int width, int height);
|
||||
void updateProjectionMatrix();
|
||||
|
@ -372,7 +383,7 @@ private:
|
|||
|
||||
void displayRearMirrorTools();
|
||||
|
||||
QMainWindow* _window;
|
||||
MainWindow* _window;
|
||||
GLCanvas* _glWidget; // our GLCanvas has a couple extra features
|
||||
|
||||
BandwidthMeter _bandwidthMeter;
|
||||
|
@ -386,7 +397,7 @@ private:
|
|||
int _numChangedSettings;
|
||||
|
||||
QUndoStack _undoStack;
|
||||
|
||||
|
||||
glm::vec3 _gravity;
|
||||
|
||||
// Frame Rate Measurement
|
||||
|
@ -411,6 +422,8 @@ private:
|
|||
ParticleTreeRenderer _particles;
|
||||
ParticleCollisionSystem _particleCollisionSystem;
|
||||
|
||||
ModelTreeRenderer _models;
|
||||
|
||||
QByteArray _voxelsFilename;
|
||||
bool _wantToKillLocalVoxels;
|
||||
|
||||
|
@ -431,7 +444,7 @@ private:
|
|||
Faceplus _faceplus;
|
||||
Faceshift _faceshift;
|
||||
Visage _visage;
|
||||
|
||||
|
||||
SixenseManager _sixenseManager;
|
||||
|
||||
Camera _myCamera; // My view onto the world
|
||||
|
@ -488,6 +501,7 @@ private:
|
|||
VoxelHideShowThread _voxelHideShowThread;
|
||||
VoxelEditPacketSender _voxelEditSender;
|
||||
ParticleEditPacketSender _particleEditSender;
|
||||
ModelEditPacketSender _modelEditSender;
|
||||
|
||||
int _packetsPerSecond;
|
||||
int _bytesPerSecond;
|
||||
|
@ -500,6 +514,7 @@ private:
|
|||
|
||||
NodeToJurisdictionMap _voxelServerJurisdictions;
|
||||
NodeToJurisdictionMap _particleServerJurisdictions;
|
||||
NodeToJurisdictionMap _modelServerJurisdictions;
|
||||
NodeToOctreeSceneStats _octreeServerSceneStats;
|
||||
QReadWriteLock _octreeSceneStatsLock;
|
||||
|
||||
|
@ -520,9 +535,11 @@ private:
|
|||
TouchEvent _lastTouchEvent;
|
||||
|
||||
Overlays _overlays;
|
||||
|
||||
AudioReflector _audioReflector;
|
||||
RunningScriptsWidget* _runningScriptsWidget;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
bool _runningScriptsWidgetWasVisible;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -644,10 +644,10 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
const int NUM_INITIAL_PACKETS_DISCARD = 3;
|
||||
const int STANDARD_DEVIATION_SAMPLE_COUNT = 500;
|
||||
|
||||
_timeSinceLastRecieved.start();
|
||||
_totalPacketsReceived++;
|
||||
|
||||
double timeDiff = (double)_timeSinceLastRecieved.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
double timeDiff = (double)_timeSinceLastReceived.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
// Discard first few received packets for computing jitter (often they pile up on start)
|
||||
if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) {
|
||||
|
@ -1265,7 +1265,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
|
|||
// setup a procedural audio output device
|
||||
_proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
_timeSinceLastRecieved.start();
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
// setup spatial audio ringbuffer
|
||||
int numFrameSamples = _outputFormat.sampleRate() * _desiredOutputFormat.channelCount();
|
||||
|
|
|
@ -129,7 +129,7 @@ private:
|
|||
QString _outputAudioDeviceName;
|
||||
|
||||
StDev _stdev;
|
||||
QElapsedTimer _timeSinceLastRecieved;
|
||||
QElapsedTimer _timeSinceLastReceived;
|
||||
float _averagedLatency;
|
||||
float _measuredJitter;
|
||||
int16_t _jitterBufferSamples;
|
||||
|
|
|
@ -56,9 +56,17 @@ void DatagramProcessor::processDatagrams() {
|
|||
Particle::handleAddParticleResponse(incomingPacket);
|
||||
application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket);
|
||||
break;
|
||||
|
||||
case PacketTypeModelAddResponse:
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
ModelItem::handleAddModelResponse(incomingPacket);
|
||||
application->getModels()->getTree()->handleAddModelResponse(incomingPacket);
|
||||
break;
|
||||
|
||||
case PacketTypeParticleData:
|
||||
case PacketTypeParticleErase:
|
||||
case PacketTypeModelData:
|
||||
case PacketTypeModelErase:
|
||||
case PacketTypeVoxelData:
|
||||
case PacketTypeVoxelErase:
|
||||
case PacketTypeOctreeStats:
|
||||
|
|
|
@ -24,8 +24,8 @@ GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer)),
|
|||
{
|
||||
}
|
||||
|
||||
bool GLCanvas::isThrottleRendering() const {
|
||||
return _throttleRendering || Application::getInstance()->getWindow()->isMinimized();
|
||||
bool GLCanvas::isThrottleRendering() const {
|
||||
return _throttleRendering || Application::getInstance()->getWindow()->isMinimized();
|
||||
}
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
|
@ -34,7 +34,7 @@ void GLCanvas::initializeGL() {
|
|||
setAcceptDrops(true);
|
||||
connect(Application::getInstance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(activeChanged(Qt::ApplicationState)));
|
||||
connect(&_frameTimer, SIGNAL(timeout()), this, SLOT(throttleRender()));
|
||||
|
||||
|
||||
// Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate.
|
||||
setAutoBufferSwap(false);
|
||||
}
|
||||
|
@ -81,14 +81,14 @@ void GLCanvas::activeChanged(Qt::ApplicationState state) {
|
|||
_frameTimer.stop();
|
||||
_throttleRendering = false;
|
||||
break;
|
||||
|
||||
|
||||
case Qt::ApplicationSuspended:
|
||||
case Qt::ApplicationHidden:
|
||||
// If we're hidden or are about to suspend, don't render anything.
|
||||
_throttleRendering = false;
|
||||
_frameTimer.stop();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Otherwise, throttle.
|
||||
if (!_throttleRendering) {
|
||||
|
|
|
@ -22,31 +22,31 @@ public:
|
|||
GLCanvas();
|
||||
bool isThrottleRendering() const;
|
||||
protected:
|
||||
|
||||
|
||||
QTimer _frameTimer;
|
||||
bool _throttleRendering;
|
||||
int _idleRenderInterval;
|
||||
|
||||
|
||||
virtual void initializeGL();
|
||||
virtual void paintGL();
|
||||
virtual void resizeGL(int width, int height);
|
||||
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void keyReleaseEvent(QKeyEvent* event);
|
||||
|
||||
|
||||
virtual void focusOutEvent(QFocusEvent* event);
|
||||
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent* event);
|
||||
virtual void mousePressEvent(QMouseEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
|
||||
virtual bool event(QEvent* event);
|
||||
|
||||
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event);
|
||||
virtual void dropEvent(QDropEvent* event);
|
||||
|
||||
|
||||
private slots:
|
||||
void activeChanged(Qt::ApplicationState state);
|
||||
void throttleRender();
|
||||
|
|
67
interface/src/MainWindow.cpp
Normal file
67
interface/src/MainWindow.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// MainWindow.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/06/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMoveEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
#include <QHideEvent>
|
||||
#include <QWindowStateChangeEvent>
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) :
|
||||
QMainWindow(parent) {
|
||||
}
|
||||
|
||||
void MainWindow::moveEvent(QMoveEvent* event) {
|
||||
emit windowGeometryChanged(QRect(event->pos(), size()));
|
||||
QMainWindow::moveEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::resizeEvent(QResizeEvent* event) {
|
||||
emit windowGeometryChanged(QRect(QPoint(x(), y()), event->size()));
|
||||
QMainWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::showEvent(QShowEvent* event) {
|
||||
if (event->spontaneous()) {
|
||||
emit windowShown(true);
|
||||
}
|
||||
QMainWindow::showEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::hideEvent(QHideEvent* event) {
|
||||
if (event->spontaneous()) {
|
||||
emit windowShown(false);
|
||||
}
|
||||
QMainWindow::hideEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::WindowStateChange) {
|
||||
QWindowStateChangeEvent* stateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
|
||||
if ((stateChangeEvent->oldState() == Qt::WindowNoState ||
|
||||
stateChangeEvent->oldState() == Qt::WindowMaximized) &&
|
||||
windowState() == Qt::WindowMinimized) {
|
||||
emit windowShown(false);
|
||||
} else {
|
||||
emit windowShown(true);
|
||||
}
|
||||
} else if (event->type() == QEvent::ActivationChange) {
|
||||
if (isActiveWindow()) {
|
||||
emit windowShown(true);
|
||||
} else {
|
||||
emit windowShown(false);
|
||||
}
|
||||
}
|
||||
QMainWindow::changeEvent(event);
|
||||
}
|
35
interface/src/MainWindow.h
Normal file
35
interface/src/MainWindow.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// MainWindow.h
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/06/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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__MainWindow__
|
||||
#define __hifi__MainWindow__
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = NULL);
|
||||
|
||||
signals:
|
||||
void windowGeometryChanged(QRect geometry);
|
||||
void windowShown(bool shown);
|
||||
|
||||
protected:
|
||||
virtual void moveEvent(QMoveEvent* event);
|
||||
virtual void resizeEvent(QResizeEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
virtual void hideEvent(QHideEvent* event);
|
||||
virtual void changeEvent(QEvent* event);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__MainWindow__) */
|
|
@ -27,6 +27,7 @@
|
|||
#include <QSlider>
|
||||
#include <QUuid>
|
||||
#include <QHBoxLayout>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <XmppClient.h>
|
||||
|
@ -39,6 +40,7 @@
|
|||
#include "ui/InfoView.h"
|
||||
#include "ui/MetavoxelEditor.h"
|
||||
#include "ui/ModelsBrowser.h"
|
||||
#include "ui/LoginDialog.h"
|
||||
|
||||
|
||||
Menu* Menu::_instance = NULL;
|
||||
|
@ -168,12 +170,12 @@ Menu::Menu() :
|
|||
|
||||
|
||||
QMenu* editMenu = addMenu("Edit");
|
||||
|
||||
|
||||
QUndoStack* undoStack = Application::getInstance()->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
undoAction->setShortcut(Qt::CTRL | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, undoAction);
|
||||
|
||||
|
||||
QAction* redoAction = undoStack->createRedoAction(editMenu);
|
||||
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, redoAction);
|
||||
|
@ -186,9 +188,9 @@ Menu::Menu() :
|
|||
QAction::PreferencesRole);
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false);
|
||||
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, true,
|
||||
avatar, SLOT(updateMotionBehaviors()));
|
||||
|
||||
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
@ -278,8 +280,9 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");
|
||||
|
@ -319,7 +322,7 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true,
|
||||
appInstance->getVisage(), SLOT(updateEnabled()));
|
||||
#endif
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::GlowWhenSpeaking, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
|
||||
|
@ -507,7 +510,7 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
|
||||
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
myAvatar->updateCollisionFlags();
|
||||
myAvatar->updateCollisionGroups();
|
||||
|
||||
if (lockedSettings) {
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
@ -722,31 +725,31 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
QAction::MenuRole role,
|
||||
int menuItemLocation) {
|
||||
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);
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
|
@ -812,42 +815,12 @@ void sendFakeEnterEvent() {
|
|||
QCoreApplication::sendEvent(glWidget, &enterEvent);
|
||||
}
|
||||
|
||||
const int QLINE_MINIMUM_WIDTH = 400;
|
||||
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
||||
|
||||
void Menu::loginForCurrentDomain() {
|
||||
QDialog loginDialog(Application::getInstance()->getWindow());
|
||||
loginDialog.setWindowTitle("Login");
|
||||
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
loginDialog.setLayout(layout);
|
||||
loginDialog.setWindowFlags(Qt::Sheet);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* loginLineEdit = new QLineEdit();
|
||||
loginLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Login:", loginLineEdit);
|
||||
|
||||
QLineEdit* passwordLineEdit = new QLineEdit();
|
||||
passwordLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
passwordLineEdit->setEchoMode(QLineEdit::Password);
|
||||
form->addRow("Password:", passwordLineEdit);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
loginDialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
loginDialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
int dialogReturn = loginDialog.exec();
|
||||
|
||||
if (dialogReturn == QDialog::Accepted && !loginLineEdit->text().isEmpty() && !passwordLineEdit->text().isEmpty()) {
|
||||
// attempt to get an access token given this username and password
|
||||
AccountManager::getInstance().requestAccessToken(loginLineEdit->text(), passwordLineEdit->text());
|
||||
}
|
||||
|
||||
sendFakeEnterEvent();
|
||||
LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow());
|
||||
loginDialog->show();
|
||||
loginDialog->resizeAndPosition(false);
|
||||
}
|
||||
|
||||
void Menu::editPreferences() {
|
||||
|
@ -926,50 +899,70 @@ void Menu::goTo() {
|
|||
int dialogReturn = gotoDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted && !gotoDialog.textValue().isEmpty()) {
|
||||
QString desiredDestination = gotoDialog.textValue();
|
||||
|
||||
if (desiredDestination.startsWith(CUSTOM_URL_SCHEME + "//")) {
|
||||
QStringList urlParts = desiredDestination.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
|
||||
|
||||
if (urlParts.count() > 1) {
|
||||
// if url has 2 or more parts, the first one is domain name
|
||||
QString domain = urlParts[0];
|
||||
|
||||
// second part is either a destination coordinate or
|
||||
// a place name
|
||||
QString destination = urlParts[1];
|
||||
|
||||
// any third part is an avatar orientation.
|
||||
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
|
||||
|
||||
goToDomain(domain);
|
||||
|
||||
// goto either @user, #place, or x-xx,y-yy,z-zz
|
||||
// style co-ordinate.
|
||||
goTo(destination);
|
||||
|
||||
if (!orientation.isEmpty()) {
|
||||
// location orientation
|
||||
goToOrientation(orientation);
|
||||
}
|
||||
} else if (urlParts.count() == 1) {
|
||||
// location coordinates or place name
|
||||
QString destination = urlParts[0];
|
||||
goTo(destination);
|
||||
}
|
||||
|
||||
} else {
|
||||
goToUser(gotoDialog.textValue());
|
||||
if (!goToURL(desiredDestination)) {;
|
||||
goTo(desiredDestination);
|
||||
}
|
||||
}
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
bool Menu::goToURL(QString location) {
|
||||
if (location.startsWith(CUSTOM_URL_SCHEME + "//")) {
|
||||
QStringList urlParts = location.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
|
||||
|
||||
if (urlParts.count() > 1) {
|
||||
// if url has 2 or more parts, the first one is domain name
|
||||
QString domain = urlParts[0];
|
||||
|
||||
// second part is either a destination coordinate or
|
||||
// a place name
|
||||
QString destination = urlParts[1];
|
||||
|
||||
// any third part is an avatar orientation.
|
||||
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
|
||||
|
||||
goToDomain(domain);
|
||||
|
||||
// goto either @user, #place, or x-xx,y-yy,z-zz
|
||||
// style co-ordinate.
|
||||
goTo(destination);
|
||||
|
||||
if (!orientation.isEmpty()) {
|
||||
// location orientation
|
||||
goToOrientation(orientation);
|
||||
}
|
||||
} else if (urlParts.count() == 1) {
|
||||
QString destination = urlParts[0];
|
||||
|
||||
// If this starts with # or @, treat it as a user/location, otherwise treat it as a domain
|
||||
if (destination[0] == '#' || destination[0] == '@') {
|
||||
goTo(destination);
|
||||
} else {
|
||||
goToDomain(destination);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::goToUser(const QString& user) {
|
||||
LocationManager* manager = &LocationManager::getInstance();
|
||||
manager->goTo(user);
|
||||
connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
|
||||
}
|
||||
|
||||
/// Open a url, shortcutting any "hifi" scheme URLs to the local application.
|
||||
void Menu::openUrl(const QUrl& url) {
|
||||
if (url.scheme() == "hifi") {
|
||||
QString path = url.toString(QUrl::RemoveScheme);
|
||||
path = path.remove(QRegExp("^:?/*"));
|
||||
goTo(path);
|
||||
} else {
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("Both user and location exists with same name");
|
||||
|
@ -1145,23 +1138,22 @@ void Menu::showScriptEditor() {
|
|||
void Menu::showChat() {
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
if (!_chatWindow) {
|
||||
mainWindow->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow());
|
||||
_chatWindow = new ChatWindow(mainWindow);
|
||||
}
|
||||
if (!_chatWindow->toggleViewAction()->isChecked()) {
|
||||
const QRect& windowGeometry = mainWindow->geometry();
|
||||
_chatWindow->move(windowGeometry.topRight().x() - _chatWindow->width(),
|
||||
windowGeometry.topRight().y() + (windowGeometry.height() / 2) - (_chatWindow->height() / 2));
|
||||
|
||||
_chatWindow->resize(0, _chatWindow->height());
|
||||
_chatWindow->toggleViewAction()->trigger();
|
||||
if (_chatWindow->isHidden()) {
|
||||
_chatWindow->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggleChat() {
|
||||
#ifdef HAVE_QXMPP
|
||||
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
|
||||
if (!_chatAction->isEnabled() && _chatWindow && _chatWindow->toggleViewAction()->isChecked()) {
|
||||
_chatWindow->toggleViewAction()->trigger();
|
||||
if (!_chatAction->isEnabled() && _chatWindow) {
|
||||
if (_chatWindow->isHidden()) {
|
||||
_chatWindow->show();
|
||||
} else {
|
||||
_chatWindow->hide();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1263,7 +1255,7 @@ void Menu::autoAdjustLOD(float currentFPS) {
|
|||
_avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool changed = false;
|
||||
quint64 elapsed = now - _lastAdjust;
|
||||
|
||||
|
@ -1370,13 +1362,13 @@ void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
|
|||
Application* appInstance = Application::getInstance();
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||
const float ADJUST_LOD_UP_FPS = 55.0;
|
||||
const float DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f;
|
||||
const float DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f;
|
||||
|
||||
const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 5;
|
||||
const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2;
|
||||
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
|
||||
float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; }
|
||||
void setAudioJitterBufferSamples(float audioJitterBufferSamples) { _audioJitterBufferSamples = audioJitterBufferSamples; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
|
@ -133,7 +133,7 @@ public:
|
|||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
void removeAction(QMenu* menu, const QString& actionName);
|
||||
|
||||
bool static goToDestination(QString destination);
|
||||
|
@ -152,8 +152,10 @@ public slots:
|
|||
void importSettings();
|
||||
void exportSettings();
|
||||
void goTo();
|
||||
bool goToURL(QString location);
|
||||
void goToUser(const QString& user);
|
||||
void pasteToVoxel();
|
||||
void openUrl(const QUrl& url);
|
||||
|
||||
void toggleLoginMenuItem();
|
||||
|
||||
|
@ -275,9 +277,6 @@ namespace MenuOption {
|
|||
const QString AudioSpatialProcessingWithDiffusions = "With Diffusions";
|
||||
const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation";
|
||||
const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation";
|
||||
|
||||
|
||||
|
||||
const QString Avatars = "Avatars";
|
||||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
|
@ -314,7 +313,7 @@ namespace MenuOption {
|
|||
const QString GoTo = "Go To...";
|
||||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
|
@ -329,6 +328,7 @@ namespace MenuOption {
|
|||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString Models = "Models";
|
||||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString NameLocation = "Name this location";
|
||||
|
|
|
@ -9,21 +9,29 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QImage>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextStream>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVariant>
|
||||
|
||||
#include <AccountManager.h>
|
||||
|
||||
#include <FBXReader.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ModelUploader.h"
|
||||
|
||||
|
@ -32,6 +40,10 @@ static const QString NAME_FIELD = "name";
|
|||
static const QString FILENAME_FIELD = "filename";
|
||||
static const QString TEXDIR_FIELD = "texdir";
|
||||
static const QString LOD_FIELD = "lod";
|
||||
static const QString JOINT_INDEX_FIELD = "jointIndex";
|
||||
static const QString SCALE_FIELD = "scale";
|
||||
static const QString JOINT_FIELD = "joint";
|
||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||
|
||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||
static const QString DATA_SERVER_URL = "https://data-web.highfidelity.io";
|
||||
|
@ -40,6 +52,7 @@ static const QString MODEL_URL = "/api/v1/models";
|
|||
static const QString SETTING_NAME = "LastModelUploadLocation";
|
||||
|
||||
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
static const int MAX_TEXTURE_SIZE = 1024;
|
||||
static const int TIMEOUT = 1000;
|
||||
static const int MAX_CHECK = 30;
|
||||
|
||||
|
@ -76,7 +89,8 @@ bool ModelUploader::zip() {
|
|||
}
|
||||
|
||||
|
||||
QString filename = QFileDialog::getOpenFileName(NULL, "Select your .fst file ...", lastLocation, "*.fst");
|
||||
QString filename = QFileDialog::getOpenFileName(NULL, "Select your model file ...",
|
||||
lastLocation, "Model files (*.fst *.fbx)");
|
||||
if (filename == "") {
|
||||
// If the user canceled we return.
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
@ -85,89 +99,160 @@ bool ModelUploader::zip() {
|
|||
settings->setValue(SETTING_NAME, filename);
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
||||
bool _nameIsPresent = false;
|
||||
QString texDir;
|
||||
// First we check the FST file (if any)
|
||||
QFile* fst;
|
||||
QVariantHash mapping;
|
||||
QString basePath;
|
||||
QString fbxFile;
|
||||
if (filename.toLower().endsWith(".fst")) {
|
||||
fst = new QFile(filename, this);
|
||||
if (!fst->open(QFile::ReadOnly | QFile::Text)) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Could not open FST file."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Could not open FST file.");
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Reading FST file : " << QFileInfo(*fst).filePath();
|
||||
mapping = readMapping(fst->readAll());
|
||||
basePath = QFileInfo(*fst).path();
|
||||
fbxFile = basePath + "/" + mapping.value(FILENAME_FIELD).toString();
|
||||
QFileInfo fbxInfo(fbxFile);
|
||||
if (!fbxInfo.exists() || !fbxInfo.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("FBX file %1 could not be found.").arg(fbxInfo.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbxInfo.fileName());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
fst = new QTemporaryFile(this);
|
||||
fst->open(QFile::WriteOnly);
|
||||
fbxFile = filename;
|
||||
basePath = QFileInfo(filename).path();
|
||||
mapping.insert(FILENAME_FIELD, QFileInfo(filename).fileName());
|
||||
}
|
||||
|
||||
// open the fbx file
|
||||
QFile fbx(fbxFile);
|
||||
if (!fbx.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
|
||||
|
||||
// First we check the FST file
|
||||
QFile fst(filename);
|
||||
if (!fst.open(QFile::ReadOnly | QFile::Text)) {
|
||||
// make sure we have some basic mappings
|
||||
if (!mapping.contains(NAME_FIELD)) {
|
||||
mapping.insert(NAME_FIELD, QFileInfo(filename).baseName());
|
||||
}
|
||||
if (!mapping.contains(TEXDIR_FIELD)) {
|
||||
mapping.insert(TEXDIR_FIELD, ".");
|
||||
}
|
||||
|
||||
// mixamo/autodesk defaults
|
||||
if (!mapping.contains(SCALE_FIELD)) {
|
||||
mapping.insert(SCALE_FIELD, 10.0);
|
||||
}
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
if (!joints.contains("jointEyeLeft")) {
|
||||
joints.insert("jointEyeLeft", "LeftEye");
|
||||
}
|
||||
if (!joints.contains("jointEyeRight")) {
|
||||
joints.insert("jointEyeRight", "RightEye");
|
||||
}
|
||||
if (!joints.contains("jointNeck")) {
|
||||
joints.insert("jointNeck", "Neck");
|
||||
}
|
||||
if (!joints.contains("jointRoot")) {
|
||||
joints.insert("jointRoot", "Hips");
|
||||
}
|
||||
if (!joints.contains("jointLean")) {
|
||||
joints.insert("jointLean", "Spine");
|
||||
}
|
||||
if (!joints.contains("jointHead")) {
|
||||
joints.insert("jointHead", geometry.applicationName == "mixamo.com" ? "HeadTop_End" : "HeadEnd");
|
||||
}
|
||||
if (!joints.contains("jointLeftHand")) {
|
||||
joints.insert("jointLeftHand", "LeftHand");
|
||||
}
|
||||
if (!joints.contains("jointRightHand")) {
|
||||
joints.insert("jointRightHand", "RightHand");
|
||||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
if (!mapping.contains(FREE_JOINT_FIELD)) {
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightArm");
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
||||
}
|
||||
|
||||
// open the dialog to configure the rest
|
||||
ModelPropertiesDialog properties(_isHead, mapping, basePath, geometry);
|
||||
if (properties.exec() == QDialog::Rejected) {
|
||||
return false;
|
||||
}
|
||||
mapping = properties.getMapping();
|
||||
|
||||
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
|
||||
if (!nameField.isEmpty()) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
||||
textPart.setBody(nameField);
|
||||
_dataMultiPart->append(textPart);
|
||||
_url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + nameField + ".fst";
|
||||
} else {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Could not open FST file."),
|
||||
QString("Model name is missing in the .fst file."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Could not open FST file.");
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Reading FST file : " << QFileInfo(fst).filePath();
|
||||
|
||||
// Compress and copy the fst
|
||||
if (!addPart(QFileInfo(fst).filePath(), QString("fst"))) {
|
||||
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's read through the FST file
|
||||
QTextStream stream(&fst);
|
||||
QList<QString> line;
|
||||
while (!stream.atEnd()) {
|
||||
line = stream.readLine().split(QRegExp("[ =]"), QString::SkipEmptyParts);
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// according to what is read, we modify the command
|
||||
if (line[0] == NAME_FIELD) {
|
||||
QHttpPart textPart;
|
||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||
" name=\"model_name\"");
|
||||
textPart.setBody(line[1].toUtf8());
|
||||
_dataMultiPart->append(textPart);
|
||||
_url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + line[1].toUtf8() + ".fst";
|
||||
_nameIsPresent = true;
|
||||
} else if (line[0] == FILENAME_FIELD) {
|
||||
fbxFile = QFileInfo(fst).path() + "/" + line[1];
|
||||
QFileInfo fbxInfo(fbxFile);
|
||||
if (!fbxInfo.exists() || !fbxInfo.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("FBX file %1 could not be found.").arg(fbxInfo.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbxInfo.fileName());
|
||||
return false;
|
||||
}
|
||||
// Compress and copy
|
||||
if (!addPart(fbxInfo.filePath(), "fbx")) {
|
||||
return false;
|
||||
}
|
||||
} else if (line[0] == TEXDIR_FIELD) { // Check existence
|
||||
texDir = QFileInfo(fst).path() + "/" + line[1];
|
||||
QFileInfo texInfo(texDir);
|
||||
if (!texInfo.exists() || !texInfo.isDir()) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Texture directory could not be found."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
|
||||
return false;
|
||||
}
|
||||
} else if (line[0] == LOD_FIELD) {
|
||||
QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
|
||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("LOD file %1 could not be found.").arg(lod.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
|
||||
}
|
||||
// Compress and copy
|
||||
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
|
||||
return false;
|
||||
}
|
||||
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
|
||||
QString texDir;
|
||||
if (!texdirField.isEmpty()) {
|
||||
texDir = basePath + "/" + texdirField;
|
||||
QFileInfo texInfo(texDir);
|
||||
if (!texInfo.exists() || !texInfo.isDir()) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Texture directory could not be found."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!addTextures(texDir, fbxFile)) {
|
||||
QVariantHash lodField = mapping.value(LOD_FIELD).toHash();
|
||||
for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) {
|
||||
QFileInfo lod(basePath + "/" + it.key());
|
||||
if (!lod.exists() || !lod.isFile()) { // Check existence
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("LOD file %1 could not be found.").arg(lod.fileName()),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
|
||||
}
|
||||
// Compress and copy
|
||||
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write out, compress and copy the fst
|
||||
if (!addPart(*fst, writeMapping(mapping), QString("fst"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compress and copy the fbx
|
||||
if (!addPart(fbx, fbxContents, "fbx")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addTextures(texDir, geometry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -181,15 +266,6 @@ bool ModelUploader::zip() {
|
|||
}
|
||||
_dataMultiPart->append(textPart);
|
||||
|
||||
if (!_nameIsPresent) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
QString("Model name is missing in the .fst file."),
|
||||
QMessageBox::Ok);
|
||||
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_readyToSend = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -350,27 +426,18 @@ void ModelUploader::processCheck() {
|
|||
delete reply;
|
||||
}
|
||||
|
||||
bool ModelUploader::addTextures(const QString& texdir, const QString fbxFile) {
|
||||
QFile fbx(fbxFile);
|
||||
if (!fbx.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray buffer = fbx.readAll();
|
||||
QVariantHash variantHash = readMapping(buffer);
|
||||
FBXGeometry geometry = readFBX(buffer, variantHash);
|
||||
|
||||
bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) {
|
||||
foreach (FBXMesh mesh, geometry.meshes) {
|
||||
foreach (FBXMeshPart part, mesh.parts) {
|
||||
if (!part.diffuseTexture.filename.isEmpty()) {
|
||||
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount))) {
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!part.normalTexture.filename.isEmpty()) {
|
||||
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.normalTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount))) {
|
||||
QString("texture%1").arg(++_texturesCount), true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +447,7 @@ bool ModelUploader::addTextures(const QString& texdir, const QString fbxFile) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ModelUploader::addPart(const QString &path, const QString& name) {
|
||||
bool ModelUploader::addPart(const QString &path, const QString& name, bool isTexture) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::warning(NULL,
|
||||
|
@ -390,7 +457,29 @@ bool ModelUploader::addPart(const QString &path, const QString& name) {
|
|||
qDebug() << "[Warning] " << QString("Could not open %1").arg(path);
|
||||
return false;
|
||||
}
|
||||
QByteArray buffer = qCompress(file.readAll());
|
||||
return addPart(file, file.readAll(), name, isTexture);
|
||||
}
|
||||
|
||||
bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture) {
|
||||
QFileInfo fileInfo(file);
|
||||
QByteArray recodedContents = contents;
|
||||
if (isTexture) {
|
||||
QString extension = fileInfo.suffix().toLower();
|
||||
bool isJpeg = (extension == "jpg");
|
||||
bool mustRecode = !(isJpeg || extension == "png");
|
||||
QImage image = QImage::fromData(contents);
|
||||
if (image.width() > MAX_TEXTURE_SIZE || image.height() > MAX_TEXTURE_SIZE) {
|
||||
image = image.scaled(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, Qt::KeepAspectRatio);
|
||||
mustRecode = true;
|
||||
}
|
||||
if (mustRecode) {
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, isJpeg ? "JPG" : "PNG");
|
||||
recodedContents = buffer.data();
|
||||
}
|
||||
}
|
||||
QByteArray buffer = qCompress(recodedContents);
|
||||
|
||||
// Qt's qCompress() default compression level (-1) is the standard zLib compression.
|
||||
// Here remove Qt's custom header that prevent the data server from uncompressing the files with zLib.
|
||||
|
@ -406,7 +495,7 @@ bool ModelUploader::addPart(const QString &path, const QString& name) {
|
|||
|
||||
|
||||
qDebug() << "File " << QFileInfo(file).fileName() << " added to model.";
|
||||
_totalSize += file.size();
|
||||
_totalSize += recodedContents.size();
|
||||
if (_totalSize > MAX_SIZE) {
|
||||
QMessageBox::warning(NULL,
|
||||
QString("ModelUploader::zip()"),
|
||||
|
@ -420,8 +509,165 @@ bool ModelUploader::addPart(const QString &path, const QString& name) {
|
|||
return true;
|
||||
}
|
||||
|
||||
ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping,
|
||||
const QString& basePath, const FBXGeometry& geometry) :
|
||||
_isHead(isHead),
|
||||
_originalMapping(originalMapping),
|
||||
_basePath(basePath),
|
||||
_geometry(geometry) {
|
||||
|
||||
setWindowTitle("Set Model Properties");
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
setLayout(form);
|
||||
|
||||
form->addRow("Name:", _name = new QLineEdit());
|
||||
|
||||
form->addRow("Texture Directory:", _textureDirectory = new QPushButton());
|
||||
connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory()));
|
||||
|
||||
form->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setSingleStep(0.01);
|
||||
|
||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
||||
if (!isHead) {
|
||||
form->addRow("Root Joint:", _rootJoint = createJointBox());
|
||||
form->addRow("Lean Joint:", _leanJoint = createJointBox());
|
||||
form->addRow("Head Joint:", _headJoint = createJointBox());
|
||||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||
|
||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
||||
_freeJoints->addWidget(newFreeJoint);
|
||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
||||
}
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), SLOT(reset()));
|
||||
|
||||
form->addRow(buttons);
|
||||
|
||||
// reset to initialize the fields
|
||||
reset();
|
||||
}
|
||||
|
||||
QVariantHash ModelPropertiesDialog::getMapping() const {
|
||||
QVariantHash mapping = _originalMapping;
|
||||
mapping.insert(NAME_FIELD, _name->text());
|
||||
mapping.insert(TEXDIR_FIELD, _textureDirectory->text());
|
||||
mapping.insert(SCALE_FIELD, QString::number(_scale->value()));
|
||||
|
||||
// update the joint indices
|
||||
QVariantHash jointIndices;
|
||||
for (int i = 0; i < _geometry.joints.size(); i++) {
|
||||
jointIndices.insert(_geometry.joints.at(i).name, QString::number(i));
|
||||
}
|
||||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||
|
||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
if (!_isHead) {
|
||||
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
||||
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
||||
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
||||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||
|
||||
mapping.remove(FREE_JOINT_FIELD);
|
||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
||||
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
|
||||
}
|
||||
}
|
||||
mapping.insert(JOINT_FIELD, joints);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static void setJointText(QComboBox* box, const QString& text) {
|
||||
box->setCurrentIndex(qMax(box->findText(text), 0));
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::reset() {
|
||||
_name->setText(_originalMapping.value(NAME_FIELD).toString());
|
||||
_textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString());
|
||||
_scale->setValue(_originalMapping.value(SCALE_FIELD, 1.0).toDouble());
|
||||
|
||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
if (!_isHead) {
|
||||
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
||||
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||
|
||||
while (_freeJoints->count() > 1) {
|
||||
delete _freeJoints->itemAt(0)->widget();
|
||||
}
|
||||
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
|
||||
QString jointName = joint.toString();
|
||||
if (_geometry.jointIndices.contains(jointName)) {
|
||||
createNewFreeJoint(jointName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::chooseTextureDirectory() {
|
||||
QString directory = QFileDialog::getExistingDirectory(this, "Choose Texture Directory",
|
||||
_basePath + "/" + _textureDirectory->text());
|
||||
if (directory.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!directory.startsWith(_basePath)) {
|
||||
QMessageBox::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path.");
|
||||
return;
|
||||
}
|
||||
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
|
||||
QWidget* freeJoint = new QWidget();
|
||||
QHBoxLayout* freeJointLayout = new QHBoxLayout();
|
||||
freeJointLayout->setContentsMargins(QMargins());
|
||||
freeJoint->setLayout(freeJointLayout);
|
||||
QComboBox* jointBox = createJointBox(false);
|
||||
jointBox->setCurrentText(joint);
|
||||
freeJointLayout->addWidget(jointBox, 1);
|
||||
QPushButton* deleteJoint = new QPushButton("Delete");
|
||||
freeJointLayout->addWidget(deleteJoint);
|
||||
freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||
_freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint);
|
||||
}
|
||||
|
||||
QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
|
||||
QComboBox* box = new QComboBox();
|
||||
if (withNone) {
|
||||
box->addItem("(none)");
|
||||
}
|
||||
foreach (const FBXJoint& joint, _geometry.joints) {
|
||||
box->addItem(joint.name);
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
void ModelPropertiesDialog::insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const {
|
||||
if (_geometry.jointIndices.contains(name)) {
|
||||
joints.insert(joint, name);
|
||||
} else {
|
||||
joints.remove(joint);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,19 @@
|
|||
#ifndef hifi_ModelUploader_h
|
||||
#define hifi_ModelUploader_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTimer>
|
||||
|
||||
class QDialog;
|
||||
#include <FBXReader.h>
|
||||
|
||||
class QComboBox;
|
||||
class QDoubleSpinBox;
|
||||
class QFileInfo;
|
||||
class QHttpMultiPart;
|
||||
class QLineEdit;
|
||||
class QProgressBar;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
|
||||
class ModelUploader : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -56,8 +63,46 @@ private:
|
|||
|
||||
|
||||
bool zip();
|
||||
bool addTextures(const QString& texdir, const QString fbxFile);
|
||||
bool addPart(const QString& path, const QString& name);
|
||||
bool addTextures(const QString& texdir, const FBXGeometry& geometry);
|
||||
bool addPart(const QString& path, const QString& name, bool isTexture = false);
|
||||
bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false);
|
||||
};
|
||||
|
||||
/// A dialog that allows customization of various model properties.
|
||||
class ModelPropertiesDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping,
|
||||
const QString& basePath, const FBXGeometry& geometry);
|
||||
|
||||
QVariantHash getMapping() const;
|
||||
|
||||
private slots:
|
||||
void reset();
|
||||
void chooseTextureDirectory();
|
||||
void createNewFreeJoint(const QString& joint = QString());
|
||||
|
||||
private:
|
||||
QComboBox* createJointBox(bool withNone = true) const;
|
||||
void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const;
|
||||
|
||||
bool _isHead;
|
||||
QVariantHash _originalMapping;
|
||||
QString _basePath;
|
||||
FBXGeometry _geometry;
|
||||
QLineEdit* _name;
|
||||
QPushButton* _textureDirectory;
|
||||
QDoubleSpinBox* _scale;
|
||||
QComboBox* _leftEyeJoint;
|
||||
QComboBox* _rightEyeJoint;
|
||||
QComboBox* _neckJoint;
|
||||
QComboBox* _rootJoint;
|
||||
QComboBox* _leanJoint;
|
||||
QComboBox* _headJoint;
|
||||
QComboBox* _leftHandJoint;
|
||||
QComboBox* _rightHandJoint;
|
||||
QVBoxLayout* _freeJoints;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelUploader_h
|
||||
|
|
|
@ -16,12 +16,13 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) :
|
|||
QSyntaxHighlighter(parent)
|
||||
{
|
||||
_keywordRegex = QRegExp("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b");
|
||||
_qoutedTextRegex = QRegExp("\".*\"");
|
||||
_qoutedTextRegex = QRegExp("\"[^\"]*(\"){0,1}");
|
||||
_multiLineCommentBegin = QRegExp("/\\*");
|
||||
_multiLineCommentEnd = QRegExp("\\*/");
|
||||
_numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}");
|
||||
_singleLineComment = QRegExp("//[^\n]*");
|
||||
_truefalseRegex = QRegExp("\\b(true|false)\\b");
|
||||
_alphacharRegex = QRegExp("[A-Za-z]");
|
||||
}
|
||||
|
||||
void ScriptHighlighting::highlightBlock(const QString& text) {
|
||||
|
@ -60,7 +61,19 @@ void ScriptHighlighting::formatComments(const QString& text) {
|
|||
int index = _singleLineComment.indexIn(text);
|
||||
while (index >= 0) {
|
||||
int length = _singleLineComment.matchedLength();
|
||||
setFormat(index, length, Qt::lightGray);
|
||||
int quoted_index = _qoutedTextRegex.indexIn(text);
|
||||
bool valid = true;
|
||||
while (quoted_index >= 0 && valid) {
|
||||
int quoted_length = _qoutedTextRegex.matchedLength();
|
||||
if (quoted_index <= index && index <= (quoted_index + quoted_length)) {
|
||||
valid = false;
|
||||
}
|
||||
quoted_index = _qoutedTextRegex.indexIn(text, quoted_index + quoted_length);
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
setFormat(index, length, Qt::lightGray);
|
||||
}
|
||||
index = _singleLineComment.indexIn(text, index + length);
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +91,9 @@ void ScriptHighlighting::formatNumbers(const QString& text){
|
|||
int index = _numberRegex.indexIn(text);
|
||||
while (index >= 0) {
|
||||
int length = _numberRegex.matchedLength();
|
||||
setFormat(index, length, Qt::green);
|
||||
if (index == 0 || _alphacharRegex.indexIn(text, index - 1) != (index - 1)) {
|
||||
setFormat(index, length, Qt::green);
|
||||
}
|
||||
index = _numberRegex.indexIn(text, index + length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
void formatTrueFalse(const QString& text);
|
||||
|
||||
private:
|
||||
QRegExp _alphacharRegex;
|
||||
QRegExp _keywordRegex;
|
||||
QRegExp _qoutedTextRegex;
|
||||
QRegExp _multiLineCommentBegin;
|
||||
|
|
|
@ -47,16 +47,14 @@ Avatar::Avatar() :
|
|||
AvatarData(),
|
||||
_skeletonModel(this),
|
||||
_bodyYawDelta(0.0f),
|
||||
_mode(AVATAR_MODE_STANDING),
|
||||
_velocity(0.0f, 0.0f, 0.0f),
|
||||
_thrust(0.0f, 0.0f, 0.0f),
|
||||
_leanScale(0.5f),
|
||||
_scale(1.0f),
|
||||
_worldUpDirection(DEFAULT_UP_DIRECTION),
|
||||
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
|
||||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_moving(false),
|
||||
_collisionFlags(0),
|
||||
_collisionGroups(0),
|
||||
_initialized(false),
|
||||
_shouldRenderBillboard(true)
|
||||
{
|
||||
|
@ -138,20 +136,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
head->simulate(deltaTime, false, _shouldRenderBillboard);
|
||||
}
|
||||
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed + fabs(_bodyYawDelta) > 0.2) {
|
||||
_mode = AVATAR_MODE_WALKING;
|
||||
} else {
|
||||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
// update animation for display name fade in/out
|
||||
if ( _displayNameTargetAlpha != _displayNameAlpha) {
|
||||
// the alpha function is
|
||||
|
@ -166,7 +153,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
// Fading in
|
||||
_displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;
|
||||
}
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,13 +206,13 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
GLOW_FROM_AVERAGE_LOUDNESS = 0.0f;
|
||||
}
|
||||
|
||||
Glower glower(_moving && distanceToTarget > GLOW_DISTANCE && renderMode == NORMAL_RENDER_MODE
|
||||
float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderMode == NORMAL_RENDER_MODE
|
||||
? 1.0f
|
||||
: GLOW_FROM_AVERAGE_LOUDNESS);
|
||||
: GLOW_FROM_AVERAGE_LOUDNESS;
|
||||
|
||||
// render body
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(renderMode);
|
||||
renderBody(renderMode, glowLevel);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
|
||||
_skeletonModel.updateShapePositions();
|
||||
|
@ -339,17 +326,21 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
return glm::angleAxis(angle * proportion, axis);
|
||||
}
|
||||
|
||||
void Avatar::renderBody(RenderMode renderMode) {
|
||||
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
// render the billboard until both models are loaded
|
||||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
{
|
||||
Glower glower(glowLevel);
|
||||
|
||||
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
// render the billboard until both models are loaded
|
||||
renderBillboard();
|
||||
return;
|
||||
}
|
||||
_skeletonModel.render(1.0f, modelRenderMode);
|
||||
getHand()->render(false);
|
||||
}
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
getHand()->render(false);
|
||||
}
|
||||
|
||||
bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const {
|
||||
|
@ -464,7 +455,7 @@ void Avatar::renderDisplayName() {
|
|||
// The up vector must be relative to the rotation current rotation matrix:
|
||||
// we set the identity
|
||||
glm::dvec3 testPoint0 = glm::dvec3(textPosition);
|
||||
glm::dvec3 testPoint1 = glm::dvec3(textPosition) + glm::dvec3(IDENTITY_UP);
|
||||
glm::dvec3 testPoint1 = glm::dvec3(textPosition) + glm::dvec3(Application::getInstance()->getCamera()->getRotation() * IDENTITY_UP);
|
||||
|
||||
bool success;
|
||||
success = gluProject(testPoint0.x, testPoint0.y, testPoint0.z,
|
||||
|
@ -479,7 +470,7 @@ void Avatar::renderDisplayName() {
|
|||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
float scaleFactor = (textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f;
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
|
||||
int text_x = -_displayNameBoundingRect.width() / 2;
|
||||
|
@ -563,7 +554,7 @@ bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList&
|
|||
}
|
||||
|
||||
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
if (_collisionFlags & COLLISION_GROUP_PARTICLES) {
|
||||
if (_collisionGroups & COLLISION_GROUP_PARTICLES) {
|
||||
return false;
|
||||
}
|
||||
bool collided = false;
|
||||
|
@ -753,19 +744,19 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void Avatar::updateCollisionFlags() {
|
||||
_collisionFlags = 0;
|
||||
void Avatar::updateCollisionGroups() {
|
||||
_collisionGroups = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithEnvironment)) {
|
||||
_collisionFlags |= COLLISION_GROUP_ENVIRONMENT;
|
||||
_collisionGroups |= COLLISION_GROUP_ENVIRONMENT;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
|
||||
_collisionFlags |= COLLISION_GROUP_AVATARS;
|
||||
_collisionGroups |= COLLISION_GROUP_AVATARS;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
|
||||
_collisionFlags |= COLLISION_GROUP_VOXELS;
|
||||
_collisionGroups |= COLLISION_GROUP_VOXELS;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
_collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
_collisionGroups |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,13 +46,6 @@ enum DriveKeys {
|
|||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
enum AvatarMode {
|
||||
AVATAR_MODE_STANDING = 0,
|
||||
AVATAR_MODE_WALKING,
|
||||
AVATAR_MODE_INTERACTING,
|
||||
NUM_AVATAR_MODES
|
||||
};
|
||||
|
||||
enum ScreenTintLayer {
|
||||
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
|
||||
SCREEN_TINT_BEFORE_AVATARS,
|
||||
|
@ -70,6 +63,7 @@ class Texture;
|
|||
|
||||
class Avatar : public AvatarData {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint32 collisionGroups READ getCollisionGroups WRITE setCollisionGroups)
|
||||
|
||||
public:
|
||||
Avatar();
|
||||
|
@ -155,8 +149,11 @@ public:
|
|||
virtual float getBoundingRadius() const;
|
||||
void updateShapePositions();
|
||||
|
||||
quint32 getCollisionGroups() const { return _collisionGroups; }
|
||||
virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
void updateCollisionGroups();
|
||||
|
||||
signals:
|
||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
@ -164,9 +161,7 @@ signals:
|
|||
protected:
|
||||
SkeletonModel _skeletonModel;
|
||||
float _bodyYawDelta;
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _thrust;
|
||||
float _leanScale;
|
||||
float _scale;
|
||||
glm::vec3 _worldUpDirection;
|
||||
|
@ -175,7 +170,7 @@ protected:
|
|||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
uint32_t _collisionFlags;
|
||||
quint32 _collisionGroups;
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
|
@ -190,7 +185,7 @@ protected:
|
|||
float getPelvisToHeadLength() const;
|
||||
|
||||
void renderDisplayName();
|
||||
virtual void renderBody(RenderMode renderMode);
|
||||
virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f);
|
||||
virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const;
|
||||
|
||||
virtual void updateJointMappings();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
@ -54,16 +55,13 @@ MyAvatar::MyAvatar() :
|
|||
_shouldJump(false),
|
||||
_gravity(0.0f, -1.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_elapsedTimeMoving(0.0f),
|
||||
_elapsedTimeStopped(0.0f),
|
||||
_elapsedTimeSinceCollision(0.0f),
|
||||
_lastCollisionPosition(0, 0, 0),
|
||||
_speedBrakes(false),
|
||||
_thrust(0.0f),
|
||||
_isThrustOn(false),
|
||||
_thrustMultiplier(1.0f),
|
||||
_moveTarget(0,0,0),
|
||||
_motionBehaviors(0),
|
||||
_lastBodyPenetration(0.0f),
|
||||
_moveTargetStepCounter(0),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
_billboardValid(false),
|
||||
|
@ -84,11 +82,8 @@ MyAvatar::~MyAvatar() {
|
|||
}
|
||||
|
||||
void MyAvatar::reset() {
|
||||
// TODO? resurrect headMouse stuff?
|
||||
//_headMouseX = _glWidget->width() / 2;
|
||||
//_headMouseY = _glWidget->height() / 2;
|
||||
_skeletonModel.reset();
|
||||
getHead()->reset();
|
||||
getHead()->reset();
|
||||
getHand()->reset();
|
||||
_oculusYawOffset = 0.0f;
|
||||
|
||||
|
@ -97,11 +92,6 @@ void MyAvatar::reset() {
|
|||
setOrientation(glm::quat(glm::vec3(0.0f)));
|
||||
}
|
||||
|
||||
void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
|
||||
_moveTarget = moveTarget;
|
||||
_moveTargetStepCounter = 0;
|
||||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
Head* head = getHead();
|
||||
head->relaxLean(deltaTime);
|
||||
|
@ -110,32 +100,14 @@ void MyAvatar::update(float deltaTime) {
|
|||
// Faceshift drive is enabled, set the avatar drive based on the head position
|
||||
moveWithLean();
|
||||
}
|
||||
|
||||
// Update head mouse from faceshift if active
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
if (faceshift->isActive()) {
|
||||
// TODO? resurrect headMouse stuff?
|
||||
//glm::vec3 headVelocity = faceshift->getHeadAngularVelocity();
|
||||
//// sets how quickly head angular rotation moves the head mouse
|
||||
//const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.0f;
|
||||
//const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.0f;
|
||||
//_headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE;
|
||||
//_headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE;
|
||||
//
|
||||
//// Constrain head-driven mouse to edges of screen
|
||||
//_headMouseX = glm::clamp(_headMouseX, 0, _glWidget->width());
|
||||
//_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height());
|
||||
}
|
||||
|
||||
|
||||
// Get audio loudness data from audio input device
|
||||
Audio* audio = Application::getInstance()->getAudio();
|
||||
head->setAudioLoudness(audio->getLastInputLoudness());
|
||||
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
|
||||
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
} else {
|
||||
setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
simulate(deltaTime);
|
||||
|
@ -145,17 +117,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
glm::quat orientation = getOrientation();
|
||||
|
||||
// Update movement timers
|
||||
_elapsedTimeSinceCollision += deltaTime;
|
||||
const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f;
|
||||
if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) {
|
||||
_elapsedTimeMoving = 0.0f;
|
||||
_elapsedTimeStopped += deltaTime;
|
||||
} else {
|
||||
_elapsedTimeStopped = 0.0f;
|
||||
_elapsedTimeMoving += deltaTime;
|
||||
}
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
setScale(scale);
|
||||
|
@ -269,34 +230,11 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// update the euler angles
|
||||
setOrientation(orientation);
|
||||
|
||||
const float WALKING_SPEED_THRESHOLD = 0.2f;
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) {
|
||||
_mode = AVATAR_MODE_WALKING;
|
||||
} else {
|
||||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
float speed = glm::length(_velocity);
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
// If a move target is set, update position explicitly
|
||||
const float MOVE_FINISHED_TOLERANCE = 0.1f;
|
||||
const float MOVE_SPEED_FACTOR = 2.0f;
|
||||
const int MOVE_TARGET_MAX_STEPS = 250;
|
||||
if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) {
|
||||
if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) {
|
||||
_position += (_moveTarget - _position) * (deltaTime * MOVE_SPEED_FACTOR);
|
||||
_moveTargetStepCounter++;
|
||||
} else {
|
||||
// Move completed
|
||||
_moveTarget = glm::vec3(0,0,0);
|
||||
_moveTargetStepCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateChatCircle(deltaTime);
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
|
@ -324,10 +262,10 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
head->simulate(deltaTime, true);
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0.0f);
|
||||
_thrust *= glm::vec3(0.0f);
|
||||
|
||||
// now that we're done stepping the avatar forward in time, compute new collisions
|
||||
if (_collisionFlags != 0) {
|
||||
if (_collisionGroups != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
|
@ -335,15 +273,15 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
if (_collisionFlags) {
|
||||
if (_collisionGroups) {
|
||||
updateShapePositions();
|
||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -451,8 +389,6 @@ void MyAvatar::renderDebugBodyPoints() {
|
|||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(0.15, 10, 10);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -467,32 +403,41 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::renderHeadMouse() const {
|
||||
// TODO? resurrect headMouse stuff?
|
||||
/*
|
||||
void MyAvatar::renderHeadMouse(int screenWidth, int screenHeight) const {
|
||||
|
||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||
|
||||
// Display small target box at center or head mouse target that can also be used to measure LOD
|
||||
float headPitch = getHead()->getFinalPitch();
|
||||
float headYaw = getHead()->getFinalYaw();
|
||||
//
|
||||
// It should be noted that the following constant is a function
|
||||
// how far the viewer's head is away from both the screen and the size of the screen,
|
||||
// which are both things we cannot know without adding a calibration phase.
|
||||
//
|
||||
const float PIXELS_PER_VERTICAL_DEGREE = 20.0f;
|
||||
float aspectRatio = (float) screenWidth / (float) screenHeight;
|
||||
int headMouseX = screenWidth / 2.f - headYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
||||
int headMouseY = screenHeight / 2.f - headPitch * PIXELS_PER_VERTICAL_DEGREE;
|
||||
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
const int PIXEL_BOX = 16;
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY);
|
||||
glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY);
|
||||
glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2);
|
||||
glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2);
|
||||
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY);
|
||||
glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY);
|
||||
glVertex2f(headMouseX, headMouseY - PIXEL_BOX/2);
|
||||
glVertex2f(headMouseX, headMouseY + PIXEL_BOX/2);
|
||||
glEnd();
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glPointSize(3.0f);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glBegin(GL_POINTS);
|
||||
glVertex2f(_headMouseX - 1, _headMouseY + 1);
|
||||
glEnd();
|
||||
// If Faceshift is active, show eye pitch and yaw as separate pointer
|
||||
if (_faceshift.isActive()) {
|
||||
const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0;
|
||||
int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE;
|
||||
int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE;
|
||||
if (faceshift->isActive()) {
|
||||
|
||||
float avgEyePitch = faceshift->getEstimatedEyePitch();
|
||||
float avgEyeYaw = faceshift->getEstimatedEyeYaw();
|
||||
int eyeTargetX = (screenWidth / 2) - avgEyeYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
||||
int eyeTargetY = (screenHeight / 2) - avgEyePitch * PIXELS_PER_VERTICAL_DEGREE;
|
||||
|
||||
glColor3f(0.0f, 1.0f, 1.0f);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
glBegin(GL_LINES);
|
||||
|
@ -503,7 +448,27 @@ void MyAvatar::renderHeadMouse() const {
|
|||
glEnd();
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void MyAvatar::setLocalGravity(glm::vec3 gravity) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
// Environmental and Local gravities are incompatible. Since Local is being set here
|
||||
// the environmental setting must be removed.
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
setGravity(gravity);
|
||||
}
|
||||
|
||||
void MyAvatar::setGravity(const glm::vec3& gravity) {
|
||||
_gravity = gravity;
|
||||
getHead()->setGravity(_gravity);
|
||||
|
||||
// use the gravity to determine the new world up direction, if possible
|
||||
float gravityLength = glm::length(gravity);
|
||||
if (gravityLength > EPSILON) {
|
||||
_worldUpDirection = _gravity / -gravityLength;
|
||||
} else {
|
||||
_worldUpDirection = DEFAULT_UP_DIRECTION;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::saveData(QSettings* settings) {
|
||||
|
@ -552,7 +517,7 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
setScale(_scale);
|
||||
Application::getInstance()->getCamera()->setScale(_scale);
|
||||
|
||||
setFaceModelURL(settings->value("faceModelURL").toUrl());
|
||||
setFaceModelURL(settings->value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
|
||||
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
||||
setDisplayName(settings->value("displayName").toString());
|
||||
|
||||
|
@ -590,23 +555,24 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
|||
}
|
||||
|
||||
void MyAvatar::updateLookAtTargetAvatar() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
|
||||
if (!applicationInstance->isMousePressed()) {
|
||||
glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin();
|
||||
glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection();
|
||||
|
||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
float distance;
|
||||
if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) {
|
||||
//
|
||||
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
||||
//
|
||||
_lookAtTargetAvatar.clear();
|
||||
_targetAvatarPosition = glm::vec3(0, 0, 0);
|
||||
const float MIN_LOOKAT_ANGLE = PI / 4.0f; // Smallest angle between face and person where we will look at someone
|
||||
float smallestAngleTo = MIN_LOOKAT_ANGLE;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
if (!avatar->isMyAvatar()) {
|
||||
float angleTo = glm::angle(getHead()->getFinalOrientation() * glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition()));
|
||||
if (angleTo < smallestAngleTo) {
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
_targetAvatarPosition = avatarPointer->getPosition();
|
||||
return;
|
||||
smallestAngleTo = angleTo;
|
||||
}
|
||||
}
|
||||
_lookAtTargetAvatar.clear();
|
||||
_targetAvatarPosition = glm::vec3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,7 +608,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_billboardValid = false;
|
||||
}
|
||||
|
||||
void MyAvatar::renderBody(RenderMode renderMode) {
|
||||
void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
return; // wait until both models are loaded
|
||||
}
|
||||
|
@ -830,7 +796,6 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
|
|||
// cancel out the velocity component in the direction of penetration
|
||||
float penetrationLength = glm::length(penetration);
|
||||
if (penetrationLength > EPSILON) {
|
||||
_elapsedTimeSinceCollision = 0.0f;
|
||||
glm::vec3 direction = penetration / penetrationLength;
|
||||
_velocity -= glm::dot(_velocity, direction) * direction * (1.0f + elasticity);
|
||||
_velocity *= glm::clamp(1.0f - damping, 0.0f, 1.0f);
|
||||
|
@ -867,7 +832,7 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
|
|||
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.0f),
|
||||
frequency * (1.0f + velocityTangentToCollision / velocityTowardCollision),
|
||||
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.0f),
|
||||
1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
|
||||
1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1090,19 +1055,6 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
sendBillboardPacket();
|
||||
}
|
||||
|
||||
void MyAvatar::setGravity(glm::vec3 gravity) {
|
||||
_gravity = gravity;
|
||||
getHead()->setGravity(_gravity);
|
||||
|
||||
// use the gravity to determine the new world up direction, if possible
|
||||
float gravityLength = glm::length(gravity);
|
||||
if (gravityLength > EPSILON) {
|
||||
_worldUpDirection = _gravity / -gravityLength;
|
||||
} else {
|
||||
_worldUpDirection = DEFAULT_UP_DIRECTION;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::goHome() {
|
||||
qDebug("Going Home!");
|
||||
setPosition(START_LOCATION);
|
||||
|
@ -1184,8 +1136,43 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
|||
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
setPosition(newPosition);
|
||||
emit transformChanged();
|
||||
} else {
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotionBehaviors() {
|
||||
_motionBehaviors = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
|
||||
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
}
|
||||
if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
|
||||
setGravity(glm::vec3(0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionGroups(quint32 collisionGroups) {
|
||||
Avatar::setCollisionGroups(collisionGroups & VALID_COLLISION_GROUPS);
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithEnvironment, (bool)(_collisionGroups & COLLISION_GROUP_ENVIRONMENT));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES));
|
||||
}
|
||||
|
||||
void MyAvatar::setMotionBehaviors(quint32 flags) {
|
||||
_motionBehaviors = flags;
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->setIsOptionChecked(MenuOption::ObeyEnvironmentalGravity, (bool)(_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY));
|
||||
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
|
||||
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
|
||||
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
} else if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
|
||||
setGravity(glm::vec3(0.0f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) {
|
||||
|
|
|
@ -28,6 +28,8 @@ enum AvatarHandState
|
|||
class MyAvatar : public Avatar {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
|
||||
Q_PROPERTY(quint32 motionBehaviors READ getMotionBehaviors WRITE setMotionBehaviors)
|
||||
Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setLocalGravity)
|
||||
|
||||
public:
|
||||
MyAvatar();
|
||||
|
@ -40,24 +42,20 @@ public:
|
|||
void moveWithLean();
|
||||
|
||||
void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE);
|
||||
void renderBody(RenderMode renderMode);
|
||||
void renderBody(RenderMode renderMode, float glowLevel = 0.0f);
|
||||
bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const;
|
||||
void renderDebugBodyPoints();
|
||||
void renderHeadMouse() const;
|
||||
void renderHeadMouse(int screenWidth, int screenHeight) const;
|
||||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setGravity(glm::vec3 gravity);
|
||||
void setMoveTarget(const glm::vec3 moveTarget);
|
||||
void setLocalGravity(glm::vec3 gravity);
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
|
||||
// getters
|
||||
AvatarMode getMode() const { return _mode; }
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
|
||||
float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
|
@ -91,6 +89,10 @@ public:
|
|||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual void setCollisionGroups(quint32 collisionGroups);
|
||||
void setMotionBehaviors(quint32 flags);
|
||||
quint32 getMotionBehaviors() const { return _motionBehaviors; }
|
||||
|
||||
void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration);
|
||||
|
||||
public slots:
|
||||
|
@ -107,6 +109,8 @@ public slots:
|
|||
glm::vec3 getThrust() { return _thrust; };
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
void updateMotionBehaviors();
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
|
||||
|
@ -117,17 +121,19 @@ private:
|
|||
bool _shouldJump;
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
glm::vec3 _environmentGravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
|
||||
float _elapsedTimeStopped;
|
||||
float _elapsedTimeSinceCollision;
|
||||
|
||||
// motion stuff
|
||||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
glm::vec3 _thrust; // final acceleration for the current frame
|
||||
bool _isThrustOn;
|
||||
float _thrustMultiplier;
|
||||
glm::vec3 _moveTarget;
|
||||
|
||||
quint32 _motionBehaviors;
|
||||
|
||||
glm::vec3 _lastBodyPenetration;
|
||||
int _moveTargetStepCounter;
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
bool _shouldRender;
|
||||
|
@ -144,6 +150,7 @@ private:
|
|||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void updateChatCircle(float deltaTime);
|
||||
void maybeUpdateBillboard();
|
||||
void setGravity(const glm::vec3& gravity);
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "Application.h"
|
||||
#include "LocationManager.h"
|
||||
|
||||
|
@ -118,6 +120,8 @@ void LocationManager::checkForMultipleDestinations() {
|
|||
Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData);
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <SharedUtil.h>
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
QElapsedTimer startupTime;
|
||||
startupTime.start();
|
||||
|
||||
|
|
123
interface/src/models/ModelTreeRenderer.cpp
Normal file
123
interface/src/models/ModelTreeRenderer.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// ModelTreeRenderer.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/6/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 <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include "ModelTreeRenderer.h"
|
||||
|
||||
ModelTreeRenderer::ModelTreeRenderer() :
|
||||
OctreeRenderer() {
|
||||
}
|
||||
|
||||
ModelTreeRenderer::~ModelTreeRenderer() {
|
||||
// delete the models in _modelsItemModels
|
||||
foreach(Model* model, _modelsItemModels) {
|
||||
delete model;
|
||||
}
|
||||
_modelsItemModels.clear();
|
||||
}
|
||||
|
||||
void ModelTreeRenderer::init() {
|
||||
OctreeRenderer::init();
|
||||
}
|
||||
|
||||
|
||||
void ModelTreeRenderer::update() {
|
||||
if (_tree) {
|
||||
ModelTree* tree = static_cast<ModelTree*>(_tree);
|
||||
tree->update();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelTreeRenderer::render() {
|
||||
OctreeRenderer::render();
|
||||
}
|
||||
|
||||
Model* ModelTreeRenderer::getModel(const QString& url) {
|
||||
Model* model = NULL;
|
||||
|
||||
// if we don't already have this model then create it and initialize it
|
||||
if (_modelsItemModels.find(url) == _modelsItemModels.end()) {
|
||||
model = new Model();
|
||||
model->init();
|
||||
model->setURL(QUrl(url));
|
||||
_modelsItemModels[url] = model;
|
||||
} else {
|
||||
model = _modelsItemModels[url];
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
|
||||
// actually render it here...
|
||||
// we need to iterate the actual modelItems of the element
|
||||
ModelTreeElement* modelTreeElement = (ModelTreeElement*)element;
|
||||
|
||||
const QList<ModelItem>& modelItems = modelTreeElement->getModels();
|
||||
|
||||
uint16_t numberOfModels = modelItems.size();
|
||||
|
||||
for (uint16_t i = 0; i < numberOfModels; i++) {
|
||||
const ModelItem& modelItem = modelItems[i];
|
||||
// render modelItem aspoints
|
||||
glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE;
|
||||
glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]);
|
||||
float radius = modelItem.getRadius() * (float)TREE_SCALE;
|
||||
//glm::vec3 center = position + glm::vec3(radius, radius, radius); // center it around the position
|
||||
|
||||
bool drawAsModel = modelItem.hasModel();
|
||||
|
||||
args->_renderedItems++;
|
||||
|
||||
if (drawAsModel) {
|
||||
glPushMatrix();
|
||||
const float alpha = 1.0f;
|
||||
|
||||
Model* model = getModel(modelItem.getModelURL());
|
||||
|
||||
model->setScaleToFit(true, radius * 2.0f);
|
||||
model->setSnapModelToCenter(true);
|
||||
|
||||
// set the rotation
|
||||
glm::quat rotation = modelItem.getModelRotation();
|
||||
model->setRotation(rotation);
|
||||
|
||||
// set the position
|
||||
model->setTranslation(position);
|
||||
|
||||
model->simulate(0.0f);
|
||||
|
||||
|
||||
model->render(alpha); // TODO: should we allow modelItems to have alpha on their models?
|
||||
|
||||
const bool wantDebugSphere = false;
|
||||
if (wantDebugSphere) {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutWireSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
} else {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(radius, 15, 15);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
|
||||
static_cast<ModelTree*>(_tree)->processEraseMessage(dataByteArray, sourceNode);
|
||||
}
|
55
interface/src/models/ModelTreeRenderer.h
Normal file
55
interface/src/models/ModelTreeRenderer.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ModelTreeRenderer.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/6/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_ModelTreeRenderer_h
|
||||
#define hifi_ModelTreeRenderer_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ModelTree.h>
|
||||
#include <Octree.h>
|
||||
#include <OctreePacketData.h>
|
||||
#include <OctreeRenderer.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "renderer/Model.h"
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class ModelTreeRenderer : public OctreeRenderer {
|
||||
public:
|
||||
ModelTreeRenderer();
|
||||
virtual ~ModelTreeRenderer();
|
||||
|
||||
virtual Octree* createTree() { return new ModelTree(true); }
|
||||
virtual char getMyNodeType() const { return NodeType::ModelServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeModelQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketTypeModelData; }
|
||||
virtual void renderElement(OctreeElement* element, RenderArgs* args);
|
||||
|
||||
void update();
|
||||
|
||||
ModelTree* getTree() { return (ModelTree*)_tree; }
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
||||
virtual void init();
|
||||
virtual void render();
|
||||
|
||||
protected:
|
||||
Model* getModel(const QString& url);
|
||||
|
||||
QMap<QString, Model*> _modelsItemModels;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelTreeRenderer_h
|
|
@ -31,7 +31,7 @@ public:
|
|||
virtual ~ParticleTreeRenderer();
|
||||
|
||||
virtual Octree* createTree() { return new ParticleTree(true); }
|
||||
virtual NodeType_t getMyNodeType() const { return NodeType::ParticleServer; }
|
||||
virtual char getMyNodeType() const { return NodeType::ParticleServer; }
|
||||
virtual PacketType getMyQueryMessageType() const { return PacketTypeParticleQuery; }
|
||||
virtual PacketType getExpectedPacketType() const { return PacketTypeParticleData; }
|
||||
virtual void renderElement(OctreeElement* element, RenderArgs* args);
|
|
@ -305,7 +305,6 @@ void GeometryCache::setBlendedVertices(const QPointer<Model>& model, const QWeak
|
|||
|
||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||
|
||||
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad),
|
||||
&Resource::allReferencesCleared);
|
||||
geometry->setLODParent(geometry);
|
||||
|
@ -320,6 +319,20 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGe
|
|||
_mapping(mapping),
|
||||
_textureBase(textureBase.isValid() ? textureBase : url),
|
||||
_fallback(fallback) {
|
||||
|
||||
if (url.isEmpty()) {
|
||||
// make the minimal amount of dummy geometry to satisfy Model
|
||||
FBXJoint joint = { false, QVector<int>(), -1 };
|
||||
_geometry.joints.append(joint);
|
||||
_geometry.leftEyeJointIndex = -1;
|
||||
_geometry.rightEyeJointIndex = -1;
|
||||
_geometry.neckJointIndex = -1;
|
||||
_geometry.rootJointIndex = -1;
|
||||
_geometry.leanJointIndex = -1;
|
||||
_geometry.headJointIndex = -1;
|
||||
_geometry.leftHandJointIndex = -1;
|
||||
_geometry.rightHandJointIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkGeometry::isLoadedWithTextures() const {
|
||||
|
|
|
@ -34,12 +34,18 @@ static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
|||
Model::Model(QObject* parent) :
|
||||
QObject(parent),
|
||||
_scale(1.0f, 1.0f, 1.0f),
|
||||
_scaleToFit(false),
|
||||
_scaleToFitLargestDimension(0.0f),
|
||||
_scaledToFit(false),
|
||||
_snapModelToCenter(false),
|
||||
_snappedToCenter(false),
|
||||
_shapesAreDirty(true),
|
||||
_boundingRadius(0.f),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.f),
|
||||
_lodDistance(0.0f),
|
||||
_pupilDilation(0.0f) {
|
||||
_pupilDilation(0.0f),
|
||||
_url("http://invalid.com") {
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
}
|
||||
|
@ -60,6 +66,13 @@ Model::SkinLocations Model::_skinNormalMapLocations;
|
|||
Model::SkinLocations Model::_skinShadowLocations;
|
||||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
setScaleInternal(scale);
|
||||
// if anyone sets scale manually, then we are no longer scaled to fit
|
||||
_scaleToFit = false;
|
||||
_scaledToFit = false;
|
||||
}
|
||||
|
||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
float scaleLength = glm::length(_scale);
|
||||
float relativeDeltaScale = glm::length(_scale - scale) / scaleLength;
|
||||
|
||||
|
@ -70,6 +83,15 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setOffset(const glm::vec3& offset) {
|
||||
_offset = offset;
|
||||
|
||||
// if someone manually sets our offset, then we are no longer snapped to center
|
||||
_snapModelToCenter = false;
|
||||
_snappedToCenter = false;
|
||||
}
|
||||
|
||||
|
||||
void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) {
|
||||
program.bind();
|
||||
locations.clusterMatrices = program.uniformLocation("clusterMatrices");
|
||||
|
@ -357,6 +379,23 @@ Extents Model::getBindExtents() const {
|
|||
return scaledExtents;
|
||||
}
|
||||
|
||||
Extents Model::getMeshExtents() const {
|
||||
if (!isActive()) {
|
||||
return Extents();
|
||||
}
|
||||
const Extents& extents = _geometry->getFBXGeometry().meshExtents;
|
||||
Extents scaledExtents = { extents.minimum * _scale, extents.maximum * _scale };
|
||||
return scaledExtents;
|
||||
}
|
||||
|
||||
Extents Model::getUnscaledMeshExtents() const {
|
||||
if (!isActive()) {
|
||||
return Extents();
|
||||
}
|
||||
const Extents& extents = _geometry->getFBXGeometry().meshExtents;
|
||||
return extents;
|
||||
}
|
||||
|
||||
bool Model::getJointState(int index, glm::quat& rotation) const {
|
||||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
|
@ -770,9 +809,52 @@ void Blender::run() {
|
|||
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
|
||||
}
|
||||
|
||||
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
|
||||
if (_scaleToFit != scaleToFit || _scaleToFitLargestDimension != largestDimension) {
|
||||
_scaleToFit = scaleToFit;
|
||||
_scaleToFitLargestDimension = largestDimension;
|
||||
_scaledToFit = false; // force rescaling
|
||||
}
|
||||
}
|
||||
|
||||
void Model::scaleToFit() {
|
||||
Extents modelMeshExtents = getMeshExtents();
|
||||
|
||||
// size is our "target size in world space"
|
||||
// we need to set our model scale so that the extents of the mesh, fit in a cube that size...
|
||||
glm::vec3 dimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
|
||||
float maxDimension = glm::max(glm::max(dimensions.x, dimensions.y), dimensions.z);
|
||||
float maxScale = _scaleToFitLargestDimension / maxDimension;
|
||||
glm::vec3 scale(maxScale, maxScale, maxScale);
|
||||
setScaleInternal(scale);
|
||||
_scaledToFit = true;
|
||||
}
|
||||
|
||||
void Model::setSnapModelToCenter(bool snapModelToCenter) {
|
||||
if (_snapModelToCenter != snapModelToCenter) {
|
||||
_snapModelToCenter = snapModelToCenter;
|
||||
_snappedToCenter = false; // force re-centering
|
||||
}
|
||||
}
|
||||
|
||||
void Model::snapToCenter() {
|
||||
Extents modelMeshExtents = getUnscaledMeshExtents();
|
||||
glm::vec3 halfDimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum) * 0.5f;
|
||||
glm::vec3 offset = -modelMeshExtents.minimum - halfDimensions;
|
||||
_offset = offset;
|
||||
_snappedToCenter = true;
|
||||
}
|
||||
|
||||
void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||
fullUpdate = updateGeometry() || fullUpdate;
|
||||
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToCenter && !_snappedToCenter);
|
||||
if (isActive() && fullUpdate) {
|
||||
// check for scale to fit
|
||||
if (_scaleToFit && !_scaledToFit) {
|
||||
scaleToFit();
|
||||
}
|
||||
if (_snapModelToCenter && !_snappedToCenter) {
|
||||
snapToCenter();
|
||||
}
|
||||
simulateInternal(deltaTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,19 @@ public:
|
|||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
|
||||
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
|
||||
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
|
||||
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
|
||||
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
|
||||
bool getScaleToFitDimension() const { return _scaleToFitLargestDimension; } /// the dimension model is scaled to
|
||||
|
||||
void setSnapModelToCenter(bool snapModelToCenter);
|
||||
bool getSnapModelToCenter() { return _snapModelToCenter; }
|
||||
|
||||
void setScale(const glm::vec3& scale);
|
||||
const glm::vec3& getScale() const { return _scale; }
|
||||
|
||||
void setOffset(const glm::vec3& offset) { _offset = offset; }
|
||||
void setOffset(const glm::vec3& offset);
|
||||
const glm::vec3& getOffset() const { return _offset; }
|
||||
|
||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
||||
|
@ -53,7 +62,7 @@ public:
|
|||
|
||||
bool isActive() const { return _geometry && _geometry->isLoaded(); }
|
||||
|
||||
bool isRenderable() const { return !_meshStates.isEmpty(); }
|
||||
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); }
|
||||
|
||||
bool isLoadedWithTextures() const { return _geometry && _geometry->isLoadedWithTextures(); }
|
||||
|
||||
|
@ -80,6 +89,12 @@ public:
|
|||
/// Returns the extents of the model in its bind pose.
|
||||
Extents getBindExtents() const;
|
||||
|
||||
/// Returns the extents of the model's mesh
|
||||
Extents getMeshExtents() const;
|
||||
|
||||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||
|
||||
|
@ -203,6 +218,13 @@ protected:
|
|||
glm::quat _rotation;
|
||||
glm::vec3 _scale;
|
||||
glm::vec3 _offset;
|
||||
|
||||
bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents
|
||||
float _scaleToFitLargestDimension; /// this is the dimension that scale to fit will use
|
||||
bool _scaledToFit; /// have we scaled to fit
|
||||
|
||||
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
|
||||
bool _snappedToCenter; /// are we currently snapped to center
|
||||
|
||||
class JointState {
|
||||
public:
|
||||
|
@ -229,6 +251,10 @@ protected:
|
|||
|
||||
// returns 'true' if needs fullUpdate after geometry change
|
||||
bool updateGeometry();
|
||||
|
||||
void setScaleInternal(const glm::vec3& scale);
|
||||
void scaleToFit();
|
||||
void snapToCenter();
|
||||
|
||||
void simulateInternal(float deltaTime);
|
||||
|
||||
|
|
49
interface/src/scripting/LocationScriptingInterface.cpp
Normal file
49
interface/src/scripting/LocationScriptingInterface.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// LocationScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Ryan Huffman on 4/29/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 <glm/glm.hpp>
|
||||
|
||||
#include "NodeList.h"
|
||||
|
||||
#include "LocationScriptingInterface.h"
|
||||
|
||||
LocationScriptingInterface* LocationScriptingInterface::getInstance() {
|
||||
static LocationScriptingInterface sharedInstance;
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
QString LocationScriptingInterface::getHref() {
|
||||
return getProtocol() + "//" + getHostname() + getPathname();
|
||||
}
|
||||
|
||||
QString LocationScriptingInterface::getPathname() {
|
||||
const glm::vec3& position = Application::getInstance()->getAvatar()->getPosition();
|
||||
QString path;
|
||||
path.sprintf("/%.4f,%.4f,%.4f", position.x, position.y, position.z);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString LocationScriptingInterface::getHostname() {
|
||||
return NodeList::getInstance()->getDomainHandler().getHostname();
|
||||
}
|
||||
|
||||
void LocationScriptingInterface::assign(const QString& url) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "goToURL", Q_ARG(const QString&, url));
|
||||
}
|
||||
|
||||
QScriptValue LocationScriptingInterface::locationGetter(QScriptContext* context, QScriptEngine* engine) {
|
||||
return engine->newQObject(getInstance());
|
||||
}
|
||||
|
||||
QScriptValue LocationScriptingInterface::locationSetter(QScriptContext* context, QScriptEngine* engine) {
|
||||
LocationScriptingInterface::getInstance()->assign(context->argument(0).toString());
|
||||
return QScriptValue::UndefinedValue;
|
||||
}
|
46
interface/src/scripting/LocationScriptingInterface.h
Normal file
46
interface/src/scripting/LocationScriptingInterface.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// LocationScriptingInterface.h
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Ryan Huffman on 4/29/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
|
||||
//
|
||||
|
||||
#ifndef hifi_LocationScriptingInterface_h
|
||||
#define hifi_LocationScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptContext>
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValue>
|
||||
#include <QString>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
class LocationScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString href READ getHref)
|
||||
Q_PROPERTY(QString protocol READ getProtocol)
|
||||
Q_PROPERTY(QString hostname READ getHostname)
|
||||
Q_PROPERTY(QString pathname READ getPathname)
|
||||
LocationScriptingInterface() { };
|
||||
public:
|
||||
static LocationScriptingInterface* getInstance();
|
||||
|
||||
QString getHref();
|
||||
QString getProtocol() { return CUSTOM_URL_SCHEME; };
|
||||
QString getPathname();
|
||||
QString getHostname();
|
||||
|
||||
static QScriptValue locationGetter(QScriptContext* context, QScriptEngine* engine);
|
||||
static QScriptValue locationSetter(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
void assign(const QString& url);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LocationScriptingInterface_h
|
119
interface/src/scripting/WindowScriptingInterface.cpp
Normal file
119
interface/src/scripting/WindowScriptingInterface.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// WindowScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Ryan Huffman on 4/29/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 <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
|
||||
#include "WindowScriptingInterface.h"
|
||||
|
||||
WindowScriptingInterface* WindowScriptingInterface::getInstance() {
|
||||
static WindowScriptingInterface sharedInstance;
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::alert(const QString& message) {
|
||||
QScriptValue retVal;
|
||||
QMetaObject::invokeMethod(this, "showAlert", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(const QString&, message));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::confirm(const QString& message) {
|
||||
QScriptValue retVal;
|
||||
QMetaObject::invokeMethod(this, "showConfirm", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(const QString&, message));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) {
|
||||
QScriptValue retVal;
|
||||
QMetaObject::invokeMethod(this, "showPrompt", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, retVal),
|
||||
Q_ARG(const QString&, message), Q_ARG(const QString&, defaultText));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
QScriptValue retVal;
|
||||
QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, retVal),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// Display an alert box
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
QScriptValue WindowScriptingInterface::showAlert(const QString& message) {
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", message);
|
||||
return QScriptValue::UndefinedValue;
|
||||
}
|
||||
|
||||
/// Display a confirmation box with the options 'Yes' and 'No'
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
||||
QScriptValue WindowScriptingInterface::showConfirm(const QString& message) {
|
||||
QMessageBox::StandardButton response = QMessageBox::question(Application::getInstance()->getWindow(), "", message);
|
||||
return QScriptValue(response == QMessageBox::Yes);
|
||||
}
|
||||
|
||||
/// Display a prompt with a text box
|
||||
/// \param const QString& message message to display
|
||||
/// \param const QString& defaultText default text in the text box
|
||||
/// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise.
|
||||
QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) {
|
||||
QInputDialog promptDialog(Application::getInstance()->getWindow());
|
||||
promptDialog.setWindowTitle("");
|
||||
promptDialog.setLabelText(message);
|
||||
promptDialog.setTextValue(defaultText);
|
||||
|
||||
if (promptDialog.exec() == QDialog::Accepted) {
|
||||
return QScriptValue(promptDialog.textValue());
|
||||
}
|
||||
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
/// Display a file dialog. If `directory` is an invalid file or directory the browser will start at the current
|
||||
/// working directory.
|
||||
/// \param const QString& title title of the window
|
||||
/// \param const QString& directory directory to start the file browser at
|
||||
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
|
||||
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
|
||||
QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QString& directory, const QString& nameFilter) {
|
||||
// On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus
|
||||
// filename if the directory is valid.
|
||||
QString path = "";
|
||||
QFileInfo fileInfo = QFileInfo(directory);
|
||||
if (fileInfo.isDir()) {
|
||||
fileInfo.setFile(directory, "__HIFI_INVALID_FILE__");
|
||||
path = fileInfo.filePath();
|
||||
}
|
||||
|
||||
QFileDialog fileDialog(Application::getInstance()->getWindow(), title, path, nameFilter);
|
||||
fileDialog.setFileMode(QFileDialog::ExistingFile);
|
||||
if (fileDialog.exec()) {
|
||||
return QScriptValue(fileDialog.selectedFiles().first());
|
||||
}
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
int WindowScriptingInterface::getInnerWidth() {
|
||||
return Application::getInstance()->getWindow()->geometry().width();
|
||||
}
|
||||
|
||||
int WindowScriptingInterface::getInnerHeight() {
|
||||
return Application::getInstance()->getWindow()->geometry().height();
|
||||
}
|
42
interface/src/scripting/WindowScriptingInterface.h
Normal file
42
interface/src/scripting/WindowScriptingInterface.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// WindowScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Ryan Huffman on 4/29/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
|
||||
//
|
||||
|
||||
#ifndef hifi_WindowScriptingInterface_h
|
||||
#define hifi_WindowScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptValue>
|
||||
#include <QString>
|
||||
|
||||
class WindowScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int innerWidth READ getInnerWidth)
|
||||
Q_PROPERTY(int innerHeight READ getInnerHeight)
|
||||
WindowScriptingInterface() { };
|
||||
public:
|
||||
static WindowScriptingInterface* getInstance();
|
||||
int getInnerWidth();
|
||||
int getInnerHeight();
|
||||
|
||||
public slots:
|
||||
QScriptValue alert(const QString& message = "");
|
||||
QScriptValue confirm(const QString& message = "");
|
||||
QScriptValue prompt(const QString& message = "", const QString& defaultText = "");
|
||||
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
|
||||
private slots:
|
||||
QScriptValue showAlert(const QString& message);
|
||||
QScriptValue showConfirm(const QString& message);
|
||||
QScriptValue showPrompt(const QString& message, const QString& defaultText);
|
||||
QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter);
|
||||
};
|
||||
|
||||
#endif // hifi_WindowScriptingInterface_h
|
19
interface/src/ui/ChatInputArea.cpp
Normal file
19
interface/src/ui/ChatInputArea.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ChatInputArea.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 4/24/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 "ChatInputArea.h"
|
||||
|
||||
ChatInputArea::ChatInputArea(QWidget* parent) : QTextEdit(parent) {
|
||||
};
|
||||
|
||||
void ChatInputArea::insertFromMimeData(const QMimeData* source) {
|
||||
insertPlainText(source->text());
|
||||
};
|
27
interface/src/ui/ChatInputArea.h
Normal file
27
interface/src/ui/ChatInputArea.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// ChatInputArea.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 4/11/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ChatInputArea_h
|
||||
#define hifi_ChatInputArea_h
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include <QMimeData>
|
||||
|
||||
class ChatInputArea : public QTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatInputArea(QWidget* parent);
|
||||
|
||||
protected:
|
||||
void insertFromMimeData(const QMimeData* source);
|
||||
};
|
||||
|
||||
#endif // hifi_ChatInputArea_h
|
|
@ -9,13 +9,18 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Application.h"
|
||||
#include "ChatMessageArea.h"
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QWheelEvent>
|
||||
|
||||
ChatMessageArea::ChatMessageArea() : QTextBrowser() {
|
||||
ChatMessageArea::ChatMessageArea(bool useFixedHeight) : QTextBrowser(), _useFixedHeight(useFixedHeight) {
|
||||
setOpenLinks(false);
|
||||
|
||||
connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
|
||||
this, &ChatMessageArea::updateLayout);
|
||||
connect(this, &QTextBrowser::anchorClicked,
|
||||
Menu::getInstance(), &Menu::openUrl);
|
||||
}
|
||||
|
||||
void ChatMessageArea::setHtml(const QString& html) {
|
||||
|
@ -34,7 +39,15 @@ void ChatMessageArea::setHtml(const QString& html) {
|
|||
}
|
||||
|
||||
void ChatMessageArea::updateLayout() {
|
||||
setFixedHeight(document()->size().height());
|
||||
if (_useFixedHeight) {
|
||||
setFixedHeight(document()->size().height());
|
||||
updateGeometry();
|
||||
emit sizeChanged(size());
|
||||
}
|
||||
}
|
||||
|
||||
void ChatMessageArea::setSize(const QSize& size) {
|
||||
setFixedHeight(size.height());
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,15 +19,19 @@ const int CHAT_MESSAGE_LINE_HEIGHT = 130;
|
|||
class ChatMessageArea : public QTextBrowser {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatMessageArea();
|
||||
ChatMessageArea(bool useFixedHeight = true);
|
||||
virtual void setHtml(const QString& html);
|
||||
|
||||
public slots:
|
||||
void updateLayout();
|
||||
void setSize(const QSize& size);
|
||||
|
||||
signals:
|
||||
void sizeChanged(QSize newSize);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
|
||||
bool _useFixedHeight;
|
||||
};
|
||||
|
||||
#endif // hifi_ChatMessageArea_h
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
#include <QGridLayout>
|
||||
#include <QFrame>
|
||||
#include <QLayoutItem>
|
||||
#include <QMainWindow>
|
||||
#include <QPalette>
|
||||
#include <QScrollBar>
|
||||
#include <QSizePolicy>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "Application.h"
|
||||
#include "FlowLayout.h"
|
||||
|
@ -31,32 +29,40 @@
|
|||
const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
||||
|
||||
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
|
||||
const QRegularExpression regexHifiLinks("([#@]\\S+)");
|
||||
|
||||
ChatWindow::ChatWindow() :
|
||||
ChatWindow::ChatWindow(QWidget* parent) :
|
||||
FramelessDialog(parent, 0, POSITION_RIGHT),
|
||||
ui(new Ui::ChatWindow),
|
||||
numMessagesAfterLastTimeStamp(0),
|
||||
_mousePressed(false),
|
||||
_mouseStartPosition()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
|
||||
// remove the title bar (see the Qt docs on setTitleBarWidget), but we keep it for undocking
|
||||
//
|
||||
titleBar = titleBarWidget();
|
||||
setTitleBarWidget(new QWidget());
|
||||
ui->setupUi(this);
|
||||
|
||||
FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
|
||||
ui->usersWidget->setLayout(flowLayout);
|
||||
|
||||
ui->messagesGridLayout->setColumnStretch(0, 1);
|
||||
ui->messagesGridLayout->setColumnStretch(1, 3);
|
||||
|
||||
ui->messagePlainTextEdit->installEventFilter(this);
|
||||
ui->messagePlainTextEdit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
|
||||
QTextCursor cursor(ui->messagePlainTextEdit->textCursor());
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
|
||||
QTextBlockFormat format = cursor.blockFormat();
|
||||
format.setLineHeight(130, QTextBlockFormat::ProportionalHeight);
|
||||
|
||||
cursor.setBlockFormat(format);
|
||||
|
||||
ui->messagePlainTextEdit->setTextCursor(cursor);
|
||||
|
||||
if (!AccountManager::getInstance().isLoggedIn()) {
|
||||
ui->connectingToXMPPLabel->setText(tr("You must be logged in to chat with others."));
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
|
||||
if (xmppClient.isConnected()) {
|
||||
|
@ -89,36 +95,17 @@ ChatWindow::~ChatWindow() {
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void ChatWindow::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton && isFloating()) {
|
||||
_mousePressed = true;
|
||||
_mouseStartPosition = e->pos();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (_mousePressed) {
|
||||
move(mapToParent(e->pos() - _mouseStartPosition));
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::mouseReleaseEvent( QMouseEvent *e ) {
|
||||
if ( e->button() == Qt::LeftButton ) {
|
||||
_mousePressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::keyPressEvent(QKeyEvent* event) {
|
||||
QDockWidget::keyPressEvent(event);
|
||||
if (event->key() == Qt::Key_Escape) {
|
||||
hide();
|
||||
} else {
|
||||
FramelessDialog::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatWindow::showEvent(QShowEvent* event) {
|
||||
QDockWidget::showEvent(event);
|
||||
FramelessDialog::showEvent(event);
|
||||
if (!event->spontaneous()) {
|
||||
activateWindow();
|
||||
ui->messagePlainTextEdit->setFocus();
|
||||
}
|
||||
}
|
||||
|
@ -141,18 +128,20 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
|
|||
message.setBody(messageText);
|
||||
XmppClient::getInstance().getXMPPClient().sendPacket(message);
|
||||
#endif
|
||||
ui->messagePlainTextEdit->document()->clear();
|
||||
QTextCursor cursor = ui->messagePlainTextEdit->textCursor();
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.removeSelectedText();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (event->type() != QEvent::MouseButtonRelease) {
|
||||
return false;
|
||||
} else if (event->type() == QEvent::MouseButtonRelease) {
|
||||
QVariant userVar = sender->property("user");
|
||||
if (userVar.isValid()) {
|
||||
Menu::getInstance()->goToUser("@" + userVar.toString());
|
||||
return true;
|
||||
}
|
||||
QString user = sender->property("user").toString();
|
||||
Menu::getInstance()->goToUser(user);
|
||||
}
|
||||
return false;
|
||||
return FramelessDialog::eventFilter(sender, event);
|
||||
}
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
|
@ -175,16 +164,17 @@ void ChatWindow::addTimeStamp() {
|
|||
timeString.chop(1);
|
||||
if (!timeString.isEmpty()) {
|
||||
QLabel* timeLabel = new QLabel(timeString);
|
||||
timeLabel->setStyleSheet("color: palette(shadow);"
|
||||
"background-color: palette(highlight);"
|
||||
timeLabel->setStyleSheet("color: #333333;"
|
||||
"background-color: white;"
|
||||
"font-size: 14pt;"
|
||||
"padding: 4px;");
|
||||
timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
timeLabel->setAlignment(Qt::AlignHCenter);
|
||||
timeLabel->setAlignment(Qt::AlignLeft);
|
||||
|
||||
bool atBottom = isAtBottom();
|
||||
bool atBottom = isNearBottom();
|
||||
|
||||
ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2);
|
||||
ui->messagesGridLayout->parentWidget()->updateGeometry();
|
||||
ui->messagesVBoxLayout->addWidget(timeLabel);
|
||||
ui->messagesVBoxLayout->parentWidget()->updateGeometry();
|
||||
|
||||
Application::processEvents();
|
||||
numMessagesAfterLastTimeStamp = 0;
|
||||
|
@ -249,6 +239,7 @@ void ChatWindow::participantsChanged() {
|
|||
"padding-bottom: 2px;"
|
||||
"padding-left: 2px;"
|
||||
"border: 1px solid palette(shadow);"
|
||||
"font-size: 14pt;"
|
||||
"font-weight: bold");
|
||||
userLabel->setProperty("user", participantName);
|
||||
userLabel->setCursor(Qt::PointingHandCursor);
|
||||
|
@ -262,15 +253,11 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
|||
return;
|
||||
}
|
||||
|
||||
QLabel* userLabel = new QLabel(getParticipantName(message.from()));
|
||||
userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
userLabel->setStyleSheet("padding: 2px; font-weight: bold");
|
||||
userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
||||
// Update background if this is a message from the current user
|
||||
bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername();
|
||||
|
||||
ChatMessageArea* messageArea = new ChatMessageArea();
|
||||
|
||||
messageArea->setOpenLinks(true);
|
||||
messageArea->setOpenExternalLinks(true);
|
||||
// Create message area
|
||||
ChatMessageArea* messageArea = new ChatMessageArea(true);
|
||||
messageArea->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
messageArea->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
messageArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
@ -281,22 +268,30 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
|||
"padding-left: 2px;"
|
||||
"padding-top: 2px;"
|
||||
"padding-right: 20px;"
|
||||
"margin: 0px;"
|
||||
"color: #333333;"
|
||||
"font-size: 14pt;"
|
||||
"background-color: rgba(0, 0, 0, 0%);"
|
||||
"border: 0;");
|
||||
|
||||
bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername();
|
||||
QString userLabel = getParticipantName(message.from());
|
||||
if (fromSelf) {
|
||||
userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea");
|
||||
messageArea->setStyleSheet(messageArea->styleSheet() + "; background-color: #e1e8ea");
|
||||
userLabel = "<b style=\"color: #4a6f91\">" + userLabel + ": </b>";
|
||||
messageArea->setStyleSheet(messageArea->styleSheet() + "background-color: #e1e8ea");
|
||||
} else {
|
||||
userLabel = "<b>" + userLabel + ": </b>";
|
||||
}
|
||||
|
||||
messageArea->setHtml(message.body().replace(regexLinks, "<a href=\"\\1\">\\1</a>"));
|
||||
messageArea->document()->setDefaultStyleSheet("a { text-decoration: none; font-weight: bold; color: #267077;}");
|
||||
QString messageText = message.body().toHtmlEscaped();
|
||||
messageText = messageText.replace(regexLinks, "<a href=\"\\1\">\\1</a>");
|
||||
messageText = messageText.replace(regexHifiLinks, "<a href=\"hifi://\\1\">\\1</a>");
|
||||
messageArea->setHtml(userLabel + messageText);
|
||||
|
||||
bool atBottom = isAtBottom();
|
||||
ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0);
|
||||
ui->messagesGridLayout->addWidget(messageArea, ui->messagesGridLayout->rowCount() - 1, 1);
|
||||
bool atBottom = isNearBottom();
|
||||
|
||||
ui->messagesGridLayout->parentWidget()->updateGeometry();
|
||||
ui->messagesVBoxLayout->addWidget(messageArea);
|
||||
ui->messagesVBoxLayout->parentWidget()->updateGeometry();
|
||||
Application::processEvents();
|
||||
|
||||
if (atBottom || fromSelf) {
|
||||
|
@ -313,26 +308,13 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
|||
|
||||
#endif
|
||||
|
||||
bool ChatWindow::isAtBottom() {
|
||||
bool ChatWindow::isNearBottom() {
|
||||
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
|
||||
return verticalScrollBar->sliderPosition() == verticalScrollBar->maximum();
|
||||
return verticalScrollBar->value() >= verticalScrollBar->maximum() - Ui::AUTO_SCROLL_THRESHOLD;
|
||||
}
|
||||
|
||||
// Scroll chat message area to bottom.
|
||||
void ChatWindow::scrollToBottom() {
|
||||
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
|
||||
verticalScrollBar->setSliderPosition(verticalScrollBar->maximum());
|
||||
}
|
||||
|
||||
void ChatWindow::togglePinned() {
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
mainWindow->removeDockWidget(this);
|
||||
if (ui->togglePinnedButton->isChecked()) {
|
||||
mainWindow->addDockWidget(ui->togglePinnedButton->isChecked() ? Qt::RightDockWidgetArea : Qt::NoDockWidgetArea, this);
|
||||
}
|
||||
if (!this->toggleViewAction()->isChecked()) {
|
||||
this->toggleViewAction()->trigger();
|
||||
}
|
||||
this->setFloating(!ui->togglePinnedButton->isChecked());
|
||||
setTitleBarWidget(ui->togglePinnedButton->isChecked()?new QWidget():titleBar);
|
||||
verticalScrollBar->setValue(verticalScrollBar->maximum());
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QTimer>
|
||||
|
||||
#include <Application.h>
|
||||
#include "FramelessDialog.h"
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
|
||||
|
@ -26,37 +27,38 @@
|
|||
#endif
|
||||
|
||||
namespace Ui {
|
||||
|
||||
|
||||
// Maximum amount the chat can be scrolled up in order to auto scroll.
|
||||
const int AUTO_SCROLL_THRESHOLD = 20;
|
||||
|
||||
|
||||
class ChatWindow;
|
||||
}
|
||||
|
||||
class ChatWindow : public QDockWidget {
|
||||
class ChatWindow : public FramelessDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChatWindow();
|
||||
ChatWindow(QWidget* parent);
|
||||
~ChatWindow();
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent *event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *e);
|
||||
virtual void mouseMoveEvent(QMouseEvent *e);
|
||||
virtual void mouseReleaseEvent(QMouseEvent *e);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* sender, QEvent* event);
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent *event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_QXMPP
|
||||
QString getParticipantName(const QString& participant);
|
||||
#endif
|
||||
void startTimerForTimeStamps();
|
||||
void addTimeStamp();
|
||||
bool isAtBottom();
|
||||
bool isNearBottom();
|
||||
void scrollToBottom();
|
||||
|
||||
Ui::ChatWindow* ui;
|
||||
QWidget* titleBar;
|
||||
int numMessagesAfterLastTimeStamp;
|
||||
QDateTime lastMessageStamp;
|
||||
bool _mousePressed;
|
||||
|
@ -65,7 +67,6 @@ private:
|
|||
private slots:
|
||||
void connected();
|
||||
void timeout();
|
||||
void togglePinned();
|
||||
#ifdef HAVE_QXMPP
|
||||
void error(QXmppClient::Error error);
|
||||
void participantsChanged();
|
||||
|
|
|
@ -14,8 +14,15 @@
|
|||
|
||||
const int RESIZE_HANDLE_WIDTH = 7;
|
||||
|
||||
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags) :
|
||||
QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) {
|
||||
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Position position) :
|
||||
QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
|
||||
_allowResize(true),
|
||||
_isResizing(false),
|
||||
_resizeInitialWidth(0),
|
||||
_selfHidden(false),
|
||||
_position(position),
|
||||
_hideOnBlur(true) {
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// handle rezize and move events
|
||||
|
@ -29,29 +36,37 @@ bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
|
|||
switch (event->type()) {
|
||||
case QEvent::Move:
|
||||
if (sender == parentWidget()) {
|
||||
// move to upper left corner on app move
|
||||
move(parentWidget()->geometry().topLeft());
|
||||
resizeAndPosition(false);
|
||||
}
|
||||
break;
|
||||
case QEvent::Resize:
|
||||
if (sender == parentWidget()) {
|
||||
// keep full app height on resizing the app
|
||||
setFixedHeight(parentWidget()->size().height());
|
||||
resizeAndPosition(false);
|
||||
}
|
||||
break;
|
||||
case QEvent::WindowStateChange:
|
||||
if (parentWidget()->isMinimized()) {
|
||||
setHidden(true);
|
||||
} else {
|
||||
if (_hideOnBlur && parentWidget()->isMinimized()) {
|
||||
if (isVisible()) {
|
||||
_selfHidden = true;
|
||||
setHidden(true);
|
||||
}
|
||||
} else if (_selfHidden) {
|
||||
_selfHidden = false;
|
||||
setHidden(false);
|
||||
}
|
||||
break;
|
||||
case QEvent::ApplicationDeactivate:
|
||||
// hide on minimize and focus lost
|
||||
setHidden(true);
|
||||
if (_hideOnBlur && isVisible()) {
|
||||
_selfHidden = true;
|
||||
setHidden(true);
|
||||
}
|
||||
break;
|
||||
case QEvent::ApplicationActivate:
|
||||
setHidden(false);
|
||||
if (_selfHidden) {
|
||||
_selfHidden = false;
|
||||
setHidden(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -70,21 +85,53 @@ void FramelessDialog::setStyleSheetFile(const QString& fileName) {
|
|||
}
|
||||
|
||||
void FramelessDialog::showEvent(QShowEvent* event) {
|
||||
// move to upper left corner
|
||||
move(parentWidget()->geometry().topLeft());
|
||||
resizeAndPosition();
|
||||
QDialog::showEvent(event);
|
||||
}
|
||||
|
||||
// keep full app height
|
||||
setFixedHeight(parentWidget()->size().height());
|
||||
void FramelessDialog::resizeAndPosition(bool resizeParent) {
|
||||
// keep full app height or width depending on position
|
||||
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
|
||||
setFixedHeight(parentWidget()->size().height());
|
||||
} else {
|
||||
setFixedWidth(parentWidget()->size().width());
|
||||
}
|
||||
|
||||
// resize parrent if width is smaller than this dialog
|
||||
if (parentWidget()->size().width() < size().width()) {
|
||||
if (resizeParent && parentWidget()->size().width() < size().width()) {
|
||||
parentWidget()->resize(size().width(), parentWidget()->size().height());
|
||||
}
|
||||
|
||||
if (_position == POSITION_LEFT || _position == POSITION_TOP) {
|
||||
// move to upper left corner
|
||||
move(parentWidget()->geometry().topLeft());
|
||||
} else if (_position == POSITION_RIGHT) {
|
||||
// move to upper right corner
|
||||
QPoint pos = parentWidget()->geometry().topRight();
|
||||
pos.setX(pos.x() - size().width());
|
||||
move(pos);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
|
||||
if (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH && mouseEvent->button() == Qt::LeftButton) {
|
||||
_isResizing = true;
|
||||
QApplication::setOverrideCursor(Qt::SizeHorCursor);
|
||||
if (_allowResize && mouseEvent->button() == Qt::LeftButton) {
|
||||
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
|
||||
bool hitLeft = (_position == POSITION_LEFT) && (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH);
|
||||
bool hitRight = (_position == POSITION_RIGHT) && (mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH);
|
||||
if (hitLeft || hitRight) {
|
||||
_isResizing = true;
|
||||
_resizeInitialWidth = size().width();
|
||||
QApplication::setOverrideCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
} else {
|
||||
bool hitTop = (_position == POSITION_TOP) && (abs(mouseEvent->pos().y() - size().height()) < RESIZE_HANDLE_WIDTH);
|
||||
if (hitTop) {
|
||||
_isResizing = true;
|
||||
_resizeInitialWidth = size().height();
|
||||
QApplication::setOverrideCursor(Qt::SizeHorCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +142,16 @@ void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) {
|
|||
|
||||
void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
|
||||
if (_isResizing) {
|
||||
resize(mouseEvent->pos().x(), size().height());
|
||||
if (_position == POSITION_LEFT) {
|
||||
resize(mouseEvent->pos().x(), size().height());
|
||||
} else if (_position == POSITION_RIGHT) {
|
||||
setUpdatesEnabled(false);
|
||||
resize(_resizeInitialWidth - mouseEvent->pos().x(), size().height());
|
||||
resizeAndPosition();
|
||||
_resizeInitialWidth = size().width();
|
||||
setUpdatesEnabled(true);
|
||||
} else if (_position == POSITION_TOP) {
|
||||
resize(size().width(), mouseEvent->pos().y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,17 @@
|
|||
|
||||
class FramelessDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0);
|
||||
enum Position { POSITION_LEFT, POSITION_RIGHT, POSITION_TOP };
|
||||
|
||||
FramelessDialog(QWidget* parent, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
|
||||
void setStyleSheetFile(const QString& fileName);
|
||||
void setAllowResize(bool allowResize) { _allowResize = allowResize; }
|
||||
bool getAllowResize() { return _allowResize; }
|
||||
void setHideOnBlur(bool hideOnBlur) { _hideOnBlur = hideOnBlur; }
|
||||
bool getHideOnBlur() { return _hideOnBlur; }
|
||||
void resizeAndPosition(bool resizeParent = true);
|
||||
|
||||
protected:
|
||||
virtual void mouseMoveEvent(QMouseEvent* mouseEvent);
|
||||
|
@ -31,7 +38,12 @@ protected:
|
|||
bool eventFilter(QObject* sender, QEvent* event);
|
||||
|
||||
private:
|
||||
bool _allowResize;
|
||||
bool _isResizing;
|
||||
int _resizeInitialWidth;
|
||||
bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization)
|
||||
Position _position;
|
||||
bool _hideOnBlur;
|
||||
|
||||
};
|
||||
|
||||
|
|
92
interface/src/ui/LoginDialog.cpp
Normal file
92
interface/src/ui/LoginDialog.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// LoginDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 4/23/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 <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
#include "AccountManager.h"
|
||||
#include "ui_loginDialog.h"
|
||||
|
||||
#include "LoginDialog.h"
|
||||
|
||||
const QString FORGOT_PASSWORD_URL = "https://data-web.highfidelity.io/password/new";
|
||||
|
||||
LoginDialog::LoginDialog(QWidget* parent) :
|
||||
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
|
||||
_ui(new Ui::LoginDialog) {
|
||||
|
||||
_ui->setupUi(this);
|
||||
_ui->errorLabel->hide();
|
||||
_ui->emailLineEdit->setFocus();
|
||||
_ui->logoLabel->setPixmap(QPixmap(Application::resourcesPath() + "images/hifi-logo.svg"));
|
||||
_ui->loginButton->setIcon(QIcon(Application::resourcesPath() + "images/login.svg"));
|
||||
_ui->infoLabel->setVisible(false);
|
||||
_ui->errorLabel->setVisible(false);
|
||||
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setHideOnBlur(false);
|
||||
|
||||
connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
|
||||
this, &LoginDialog::handleLoginCompleted);
|
||||
connect(&AccountManager::getInstance(), &AccountManager::loginFailed,
|
||||
this, &LoginDialog::handleLoginFailed);
|
||||
connect(_ui->loginButton, &QPushButton::clicked,
|
||||
this, &LoginDialog::handleLoginClicked);
|
||||
connect(_ui->closeButton, &QPushButton::clicked,
|
||||
this, &LoginDialog::close);
|
||||
};
|
||||
|
||||
LoginDialog::~LoginDialog() {
|
||||
delete _ui;
|
||||
};
|
||||
|
||||
void LoginDialog::handleLoginCompleted(const QUrl& authURL) {
|
||||
_ui->infoLabel->setVisible(false);
|
||||
_ui->errorLabel->setVisible(false);
|
||||
close();
|
||||
};
|
||||
|
||||
void LoginDialog::handleLoginFailed() {
|
||||
_ui->infoLabel->setVisible(false);
|
||||
_ui->errorLabel->setVisible(true);
|
||||
|
||||
_ui->errorLabel->show();
|
||||
_ui->loginArea->setDisabled(false);
|
||||
|
||||
// Move focus to password and select the entire line
|
||||
_ui->passwordLineEdit->setFocus();
|
||||
_ui->passwordLineEdit->setSelection(0, _ui->emailLineEdit->maxLength());
|
||||
};
|
||||
|
||||
void LoginDialog::handleLoginClicked() {
|
||||
// If the email or password inputs are empty, move focus to them, otherwise attempt to login.
|
||||
if (_ui->emailLineEdit->text().isEmpty()) {
|
||||
_ui->emailLineEdit->setFocus();
|
||||
} else if (_ui->passwordLineEdit->text().isEmpty()) {
|
||||
_ui->passwordLineEdit->setFocus();
|
||||
} else {
|
||||
_ui->infoLabel->setVisible(true);
|
||||
_ui->errorLabel->setVisible(false);
|
||||
|
||||
_ui->loginArea->setDisabled(true);
|
||||
AccountManager::getInstance().requestAccessToken(_ui->emailLineEdit->text(), _ui->passwordLineEdit->text());
|
||||
}
|
||||
};
|
||||
|
||||
void LoginDialog::moveEvent(QMoveEvent* event) {
|
||||
// Modal dialogs seemed to get repositioned automatically. Combat this by moving the window if needed.
|
||||
resizeAndPosition();
|
||||
};
|
42
interface/src/ui/LoginDialog.h
Normal file
42
interface/src/ui/LoginDialog.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// LoginDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 4/23/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
|
||||
//
|
||||
|
||||
#ifndef hifi_LoginDialog_h
|
||||
#define hifi_LoginDialog_h
|
||||
|
||||
#include <QObject>
|
||||
#include "FramelessDialog.h"
|
||||
|
||||
namespace Ui {
|
||||
class LoginDialog;
|
||||
}
|
||||
|
||||
class LoginDialog : public FramelessDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LoginDialog(QWidget* parent);
|
||||
~LoginDialog();
|
||||
|
||||
public slots:
|
||||
void handleLoginClicked();
|
||||
void handleLoginCompleted(const QUrl& authURL);
|
||||
void handleLoginFailed();
|
||||
|
||||
protected:
|
||||
void moveEvent(QMoveEvent* event);
|
||||
|
||||
private:
|
||||
Ui::LoginDialog* _ui;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LoginDialog_h
|
|
@ -232,6 +232,9 @@ void OctreeStatsDialog::showAllOctreeServers() {
|
|||
showOctreeServersOfType(serverCount, NodeType::ParticleServer, "Particle",
|
||||
Application::getInstance()->getParticleServerJurisdictions());
|
||||
|
||||
showOctreeServersOfType(serverCount, NodeType::ModelServer, "Model",
|
||||
Application::getInstance()->getModelServerJurisdictions());
|
||||
|
||||
if (_voxelServerLabelsCount > serverCount) {
|
||||
for (int i = serverCount; i < _voxelServerLabelsCount; i++) {
|
||||
int serverLabel = _voxelServerLables[i];
|
||||
|
|
|
@ -19,7 +19,7 @@ const int SCROLL_PANEL_BOTTOM_MARGIN = 30;
|
|||
const int OK_BUTTON_RIGHT_MARGIN = 30;
|
||||
const int BUTTONS_TOP_MARGIN = 24;
|
||||
|
||||
PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags) {
|
||||
PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags, POSITION_LEFT) {
|
||||
|
||||
ui.setupUi(this);
|
||||
setStyleSheetFile("styles/preferences.qss");
|
||||
|
@ -38,26 +38,34 @@ void PreferencesDialog::accept() {
|
|||
|
||||
void PreferencesDialog::setHeadUrl(QString modelUrl) {
|
||||
ui.faceURLEdit->setText(modelUrl);
|
||||
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
}
|
||||
|
||||
void PreferencesDialog::setSkeletonUrl(QString modelUrl) {
|
||||
ui.skeletonURLEdit->setText(modelUrl);
|
||||
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
}
|
||||
|
||||
void PreferencesDialog::openHeadModelBrowser() {
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
||||
show();
|
||||
|
||||
ModelsBrowser modelBrowser(Head);
|
||||
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl);
|
||||
modelBrowser.browse();
|
||||
|
||||
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
show();
|
||||
}
|
||||
|
||||
void PreferencesDialog::openBodyModelBrowser() {
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
||||
show();
|
||||
|
||||
ModelsBrowser modelBrowser(Skeleton);
|
||||
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl);
|
||||
modelBrowser.browse();
|
||||
|
||||
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||
show();
|
||||
}
|
||||
|
||||
void PreferencesDialog::openSnapshotLocationBrowser() {
|
||||
|
@ -176,6 +184,7 @@ void PreferencesDialog::savePreferences() {
|
|||
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
||||
|
||||
Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value());
|
||||
Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value());
|
||||
|
||||
Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(),
|
||||
Application::getInstance()->getGLWidget()->height());
|
||||
|
|
|
@ -12,39 +12,35 @@
|
|||
#include "ui_runningScriptsWidget.h"
|
||||
#include "RunningScriptsWidget.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QTableWidgetItem>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
RunningScriptsWidget::RunningScriptsWidget(QDockWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
ui(new Ui::RunningScriptsWidget)
|
||||
{
|
||||
RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
||||
FramelessDialog(parent, 0, POSITION_LEFT),
|
||||
ui(new Ui::RunningScriptsWidget) {
|
||||
ui->setupUi(this);
|
||||
|
||||
// remove the title bar (see the Qt docs on setTitleBarWidget)
|
||||
setTitleBarWidget(new QWidget());
|
||||
setAllowResize(false);
|
||||
|
||||
ui->runningScriptsTableWidget->setColumnCount(2);
|
||||
ui->runningScriptsTableWidget->verticalHeader()->setVisible(false);
|
||||
ui->runningScriptsTableWidget->horizontalHeader()->setVisible(false);
|
||||
ui->runningScriptsTableWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
ui->runningScriptsTableWidget->setShowGrid(false);
|
||||
ui->runningScriptsTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ui->runningScriptsTableWidget->setColumnWidth(0, 235);
|
||||
ui->runningScriptsTableWidget->setColumnWidth(1, 25);
|
||||
connect(ui->runningScriptsTableWidget, &QTableWidget::cellClicked, this, &RunningScriptsWidget::stopScript);
|
||||
ui->hideWidgetButton->setIcon(QIcon(Application::resourcesPath() + "images/close.svg"));
|
||||
ui->reloadAllButton->setIcon(QIcon(Application::resourcesPath() + "images/reload.svg"));
|
||||
ui->stopAllButton->setIcon(QIcon(Application::resourcesPath() + "images/stop.svg"));
|
||||
ui->loadScriptButton->setIcon(QIcon(Application::resourcesPath() + "images/plus-white.svg"));
|
||||
|
||||
ui->recentlyLoadedScriptsTableWidget->setColumnCount(2);
|
||||
ui->recentlyLoadedScriptsTableWidget->verticalHeader()->setVisible(false);
|
||||
ui->recentlyLoadedScriptsTableWidget->horizontalHeader()->setVisible(false);
|
||||
ui->recentlyLoadedScriptsTableWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
ui->recentlyLoadedScriptsTableWidget->setShowGrid(false);
|
||||
ui->recentlyLoadedScriptsTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ui->recentlyLoadedScriptsTableWidget->setColumnWidth(0, 25);
|
||||
ui->recentlyLoadedScriptsTableWidget->setColumnWidth(1, 235);
|
||||
connect(ui->recentlyLoadedScriptsTableWidget, &QTableWidget::cellClicked,
|
||||
_runningScriptsTable = new ScriptsTableWidget(ui->runningScriptsTableWidget);
|
||||
_runningScriptsTable->setColumnCount(2);
|
||||
_runningScriptsTable->setColumnWidth(0, 245);
|
||||
_runningScriptsTable->setColumnWidth(1, 22);
|
||||
connect(_runningScriptsTable, &QTableWidget::cellClicked, this, &RunningScriptsWidget::stopScript);
|
||||
|
||||
_recentlyLoadedScriptsTable = new ScriptsTableWidget(ui->recentlyLoadedScriptsTableWidget);
|
||||
_recentlyLoadedScriptsTable->setColumnCount(1);
|
||||
_recentlyLoadedScriptsTable->setColumnWidth(0, 265);
|
||||
connect(_recentlyLoadedScriptsTable, &QTableWidget::cellClicked,
|
||||
this, &RunningScriptsWidget::loadScript);
|
||||
|
||||
connect(ui->hideWidgetButton, &QPushButton::clicked,
|
||||
|
@ -53,118 +49,126 @@ RunningScriptsWidget::RunningScriptsWidget(QDockWidget *parent) :
|
|||
Application::getInstance(), &Application::reloadAllScripts);
|
||||
connect(ui->stopAllButton, &QPushButton::clicked,
|
||||
this, &RunningScriptsWidget::allScriptsStopped);
|
||||
connect(ui->loadScriptButton, &QPushButton::clicked,
|
||||
Application::getInstance(), &Application::loadDialog);
|
||||
}
|
||||
|
||||
RunningScriptsWidget::~RunningScriptsWidget()
|
||||
{
|
||||
RunningScriptsWidget::~RunningScriptsWidget() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::setRunningScripts(const QStringList& list)
|
||||
{
|
||||
ui->runningScriptsTableWidget->setRowCount(list.size());
|
||||
void RunningScriptsWidget::setBoundary(const QRect& rect) {
|
||||
_boundary = rect;
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
||||
_runningScriptsTable->setRowCount(list.size());
|
||||
|
||||
ui->noRunningScriptsLabel->setVisible(list.isEmpty());
|
||||
ui->currentlyRunningLabel->setVisible(!list.isEmpty());
|
||||
ui->line1->setVisible(!list.isEmpty());
|
||||
ui->runningScriptsTableWidget->setVisible(!list.isEmpty());
|
||||
ui->reloadAllButton->setVisible(!list.isEmpty());
|
||||
ui->stopAllButton->setVisible(!list.isEmpty());
|
||||
|
||||
const int CLOSE_ICON_HEIGHT = 12;
|
||||
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
QTableWidgetItem *scriptName = new QTableWidgetItem;
|
||||
scriptName->setText(list.at(i));
|
||||
scriptName->setText(QFileInfo(list.at(i)).fileName());
|
||||
scriptName->setToolTip(list.at(i));
|
||||
scriptName->setTextAlignment(Qt::AlignCenter);
|
||||
scriptName->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
QTableWidgetItem *closeIcon = new QTableWidgetItem;
|
||||
closeIcon->setIcon(QIcon(Application::resourcesPath() + "/images/kill-script.svg"));
|
||||
closeIcon->setIcon(QIcon(QPixmap(Application::resourcesPath() + "images/kill-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT)));
|
||||
|
||||
ui->runningScriptsTableWidget->setItem(i, 0, scriptName);
|
||||
ui->runningScriptsTableWidget->setItem(i, 1, closeIcon);
|
||||
_runningScriptsTable->setItem(i, 0, scriptName);
|
||||
_runningScriptsTable->setItem(i, 1, closeIcon);
|
||||
}
|
||||
|
||||
const int RUNNING_SCRIPTS_TABLE_LEFT_MARGIN = 12;
|
||||
const int RECENTLY_LOADED_TOP_MARGIN = 61;
|
||||
const int RECENTLY_LOADED_LABEL_TOP_MARGIN = 19;
|
||||
|
||||
int y = ui->runningScriptsTableWidget->y() + RUNNING_SCRIPTS_TABLE_LEFT_MARGIN;
|
||||
for (int i = 0; i < _runningScriptsTable->rowCount(); ++i) {
|
||||
y += _runningScriptsTable->rowHeight(i);
|
||||
}
|
||||
|
||||
ui->runningScriptsTableWidget->resize(ui->runningScriptsTableWidget->width(), y - RUNNING_SCRIPTS_TABLE_LEFT_MARGIN);
|
||||
_runningScriptsTable->resize(_runningScriptsTable->width(), y - RUNNING_SCRIPTS_TABLE_LEFT_MARGIN);
|
||||
ui->reloadAllButton->move(ui->reloadAllButton->x(), y);
|
||||
ui->stopAllButton->move(ui->stopAllButton->x(), y);
|
||||
ui->recentlyLoadedLabel->move(ui->recentlyLoadedLabel->x(),
|
||||
ui->stopAllButton->y() + ui->stopAllButton->height() + RECENTLY_LOADED_TOP_MARGIN);
|
||||
ui->recentlyLoadedScriptsTableWidget->move(ui->recentlyLoadedScriptsTableWidget->x(),
|
||||
ui->recentlyLoadedLabel->y() + RECENTLY_LOADED_LABEL_TOP_MARGIN);
|
||||
|
||||
|
||||
createRecentlyLoadedScriptsTable();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::keyPressEvent(QKeyEvent *e)
|
||||
void RunningScriptsWidget::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
switch(e->key()) {
|
||||
int loadScriptNumber = -1;
|
||||
switch(event->key()) {
|
||||
case Qt::Key_Escape:
|
||||
Application::getInstance()->toggleRunningScriptsWidget();
|
||||
break;
|
||||
|
||||
case Qt::Key_1:
|
||||
if (_recentlyLoadedScripts.size() > 0) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(0));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_2:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 2) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(1));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_3:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 3) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(2));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_4:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 4) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(3));
|
||||
}
|
||||
break;
|
||||
case Qt::Key_5:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 5) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(4));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_6:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 6) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(5));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_7:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 7) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(6));
|
||||
}
|
||||
break;
|
||||
case Qt::Key_8:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 8) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(7));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_9:
|
||||
if (_recentlyLoadedScripts.size() > 0 && _recentlyLoadedScripts.size() >= 9) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(8));
|
||||
}
|
||||
loadScriptNumber = event->key() - Qt::Key_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (loadScriptNumber >= 0) {
|
||||
if (_recentlyLoadedScripts.size() > loadScriptNumber) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(loadScriptNumber));
|
||||
}
|
||||
}
|
||||
|
||||
FramelessDialog::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::stopScript(int row, int column)
|
||||
{
|
||||
void RunningScriptsWidget::paintEvent(QPaintEvent* event) {
|
||||
QPainter painter(this);
|
||||
painter.setPen(QColor::fromRgb(225, 225, 225)); // #e1e1e1
|
||||
|
||||
if (ui->currentlyRunningLabel->isVisible()) {
|
||||
// line below the 'Currently Running' label
|
||||
painter.drawLine(36, ui->currentlyRunningLabel->y() + ui->currentlyRunningLabel->height(),
|
||||
300, ui->currentlyRunningLabel->y() + ui->currentlyRunningLabel->height());
|
||||
}
|
||||
|
||||
if (ui->recentlyLoadedLabel->isVisible()) {
|
||||
// line below the 'Recently loaded' label
|
||||
painter.drawLine(36, ui->recentlyLoadedLabel->y() + ui->recentlyLoadedLabel->height(),
|
||||
300, ui->recentlyLoadedLabel->y() + ui->recentlyLoadedLabel->height());
|
||||
}
|
||||
|
||||
painter.end();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::stopScript(int row, int column) {
|
||||
if (column == 1) { // make sure the user has clicked on the close icon
|
||||
_lastStoppedScript = ui->runningScriptsTableWidget->item(row, 0)->text();
|
||||
emit stopScriptName(ui->runningScriptsTableWidget->item(row, 0)->text());
|
||||
_lastStoppedScript = _runningScriptsTable->item(row, 0)->toolTip();
|
||||
emit stopScriptName(_runningScriptsTable->item(row, 0)->toolTip());
|
||||
}
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::loadScript(int row, int column)
|
||||
{
|
||||
Application::getInstance()->loadScript(ui->recentlyLoadedScriptsTableWidget->item(row, column)->text());
|
||||
void RunningScriptsWidget::loadScript(int row, int column) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScriptsTable->item(row, column)->toolTip());
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::allScriptsStopped()
|
||||
{
|
||||
void RunningScriptsWidget::allScriptsStopped() {
|
||||
QStringList list = Application::getInstance()->getRunningScripts();
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
_recentlyLoadedScripts.prepend(list.at(i));
|
||||
|
@ -173,8 +177,7 @@ void RunningScriptsWidget::allScriptsStopped()
|
|||
Application::getInstance()->stopAllScripts();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::createRecentlyLoadedScriptsTable()
|
||||
{
|
||||
void RunningScriptsWidget::createRecentlyLoadedScriptsTable() {
|
||||
if (!_recentlyLoadedScripts.contains(_lastStoppedScript) && !_lastStoppedScript.isEmpty()) {
|
||||
_recentlyLoadedScripts.prepend(_lastStoppedScript);
|
||||
_lastStoppedScript = "";
|
||||
|
@ -187,21 +190,28 @@ void RunningScriptsWidget::createRecentlyLoadedScriptsTable()
|
|||
}
|
||||
|
||||
ui->recentlyLoadedLabel->setVisible(!_recentlyLoadedScripts.isEmpty());
|
||||
ui->line2->setVisible(!_recentlyLoadedScripts.isEmpty());
|
||||
ui->recentlyLoadedScriptsTableWidget->setVisible(!_recentlyLoadedScripts.isEmpty());
|
||||
ui->recentlyLoadedInstruction->setVisible(!_recentlyLoadedScripts.isEmpty());
|
||||
|
||||
int limit = _recentlyLoadedScripts.size() > 9 ? 9 : _recentlyLoadedScripts.size();
|
||||
ui->recentlyLoadedScriptsTableWidget->setRowCount(limit);
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
_recentlyLoadedScriptsTable->setRowCount(limit);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
QTableWidgetItem *scriptName = new QTableWidgetItem;
|
||||
scriptName->setText(_recentlyLoadedScripts.at(i));
|
||||
scriptName->setText(QString::number(i + 1) + ". " + QFileInfo(_recentlyLoadedScripts.at(i)).fileName());
|
||||
scriptName->setToolTip(_recentlyLoadedScripts.at(i));
|
||||
scriptName->setTextAlignment(Qt::AlignCenter);
|
||||
QTableWidgetItem *number = new QTableWidgetItem;
|
||||
number->setText(QString::number(i+1) + ".");
|
||||
scriptName->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
||||
ui->recentlyLoadedScriptsTableWidget->setItem(i, 0, number);
|
||||
ui->recentlyLoadedScriptsTableWidget->setItem(i, 1, scriptName);
|
||||
_recentlyLoadedScriptsTable->setItem(i, 0, scriptName);
|
||||
}
|
||||
|
||||
int y = ui->recentlyLoadedScriptsTableWidget->y() + 15;
|
||||
for (int i = 0; i < _recentlyLoadedScriptsTable->rowCount(); ++i) {
|
||||
y += _recentlyLoadedScriptsTable->rowHeight(i);
|
||||
}
|
||||
|
||||
ui->recentlyLoadedInstruction->setGeometry(36, y,
|
||||
ui->recentlyLoadedInstruction->width(),
|
||||
ui->recentlyLoadedInstruction->height());
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
|
|
@ -12,18 +12,18 @@
|
|||
#ifndef hifi_RunningScriptsWidget_h
|
||||
#define hifi_RunningScriptsWidget_h
|
||||
|
||||
// Qt
|
||||
#include <QDockWidget>
|
||||
#include "FramelessDialog.h"
|
||||
#include "ScriptsTableWidget.h"
|
||||
|
||||
namespace Ui {
|
||||
class RunningScriptsWidget;
|
||||
}
|
||||
|
||||
class RunningScriptsWidget : public QDockWidget
|
||||
class RunningScriptsWidget : public FramelessDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit RunningScriptsWidget(QDockWidget *parent = 0);
|
||||
explicit RunningScriptsWidget(QWidget* parent = NULL);
|
||||
~RunningScriptsWidget();
|
||||
|
||||
void setRunningScripts(const QStringList& list);
|
||||
|
@ -32,7 +32,11 @@ signals:
|
|||
void stopScriptName(const QString& name);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e);
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void paintEvent(QPaintEvent* event);
|
||||
|
||||
public slots:
|
||||
void setBoundary(const QRect& rect);
|
||||
|
||||
private slots:
|
||||
void stopScript(int row, int column);
|
||||
|
@ -40,9 +44,12 @@ private slots:
|
|||
void allScriptsStopped();
|
||||
|
||||
private:
|
||||
Ui::RunningScriptsWidget *ui;
|
||||
Ui::RunningScriptsWidget* ui;
|
||||
ScriptsTableWidget* _runningScriptsTable;
|
||||
ScriptsTableWidget* _recentlyLoadedScriptsTable;
|
||||
QStringList _recentlyLoadedScripts;
|
||||
QString _lastStoppedScript;
|
||||
QRect _boundary;
|
||||
|
||||
void createRecentlyLoadedScriptsTable();
|
||||
};
|
||||
|
|
108
interface/src/ui/ScriptEditBox.cpp
Normal file
108
interface/src/ui/ScriptEditBox.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// ScriptEditBox.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/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 "ScriptEditBox.h"
|
||||
#include "ScriptLineNumberArea.h"
|
||||
#include "Application.h"
|
||||
|
||||
ScriptEditBox::ScriptEditBox(QWidget* parent) :
|
||||
QPlainTextEdit(parent)
|
||||
{
|
||||
_scriptLineNumberArea = new ScriptLineNumberArea(this);
|
||||
|
||||
connect(this, &ScriptEditBox::blockCountChanged, this, &ScriptEditBox::updateLineNumberAreaWidth);
|
||||
connect(this, &ScriptEditBox::updateRequest, this, &ScriptEditBox::updateLineNumberArea);
|
||||
connect(this, &ScriptEditBox::cursorPositionChanged, this, &ScriptEditBox::highlightCurrentLine);
|
||||
|
||||
updateLineNumberAreaWidth(0);
|
||||
highlightCurrentLine();
|
||||
}
|
||||
|
||||
int ScriptEditBox::lineNumberAreaWidth() {
|
||||
int digits = 1;
|
||||
const int SPACER_PIXELS = 3;
|
||||
const int BASE_TEN = 10;
|
||||
int max = qMax(1, blockCount());
|
||||
while (max >= BASE_TEN) {
|
||||
max /= BASE_TEN;
|
||||
digits++;
|
||||
}
|
||||
return SPACER_PIXELS + fontMetrics().width(QLatin1Char('H')) * digits;
|
||||
}
|
||||
|
||||
void ScriptEditBox::updateLineNumberAreaWidth(int blockCount) {
|
||||
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
|
||||
}
|
||||
|
||||
void ScriptEditBox::updateLineNumberArea(const QRect& rect, int deltaY) {
|
||||
if (deltaY) {
|
||||
_scriptLineNumberArea->scroll(0, deltaY);
|
||||
} else {
|
||||
_scriptLineNumberArea->update(0, rect.y(), _scriptLineNumberArea->width(), rect.height());
|
||||
}
|
||||
|
||||
if (rect.contains(viewport()->rect())) {
|
||||
updateLineNumberAreaWidth(0);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditBox::resizeEvent(QResizeEvent* event) {
|
||||
QPlainTextEdit::resizeEvent(event);
|
||||
|
||||
QRect localContentsRect = contentsRect();
|
||||
_scriptLineNumberArea->setGeometry(QRect(localContentsRect.left(), localContentsRect.top(), lineNumberAreaWidth(),
|
||||
localContentsRect.height()));
|
||||
}
|
||||
|
||||
void ScriptEditBox::highlightCurrentLine() {
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
|
||||
if (!isReadOnly()) {
|
||||
QTextEdit::ExtraSelection selection;
|
||||
|
||||
QColor lineColor = QColor(Qt::gray).lighter();
|
||||
|
||||
selection.format.setBackground(lineColor);
|
||||
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
extraSelections.append(selection);
|
||||
}
|
||||
|
||||
setExtraSelections(extraSelections);
|
||||
}
|
||||
|
||||
void ScriptEditBox::lineNumberAreaPaintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(_scriptLineNumberArea);
|
||||
painter.fillRect(event->rect(), Qt::lightGray);
|
||||
QTextBlock block = firstVisibleBlock();
|
||||
int blockNumber = block.blockNumber();
|
||||
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
|
||||
int bottom = top + (int) blockBoundingRect(block).height();
|
||||
|
||||
while (block.isValid() && top <= event->rect().bottom()) {
|
||||
if (block.isVisible() && bottom >= event->rect().top()) {
|
||||
QFont font = painter.font();
|
||||
font.setBold(this->textCursor().blockNumber() == block.blockNumber());
|
||||
painter.setFont(font);
|
||||
QString number = QString::number(blockNumber + 1);
|
||||
painter.setPen(Qt::black);
|
||||
painter.drawText(0, top, _scriptLineNumberArea->width(), fontMetrics().height(),
|
||||
Qt::AlignRight, number);
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
top = bottom;
|
||||
bottom = top + (int) blockBoundingRect(block).height();
|
||||
blockNumber++;
|
||||
}
|
||||
}
|
38
interface/src/ui/ScriptEditBox.h
Normal file
38
interface/src/ui/ScriptEditBox.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// ScriptEditBox.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptEditBox_h
|
||||
#define hifi_ScriptEditBox_h
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
class ScriptEditBox : public QPlainTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptEditBox(QWidget* parent = NULL);
|
||||
|
||||
void lineNumberAreaPaintEvent(QPaintEvent* event);
|
||||
int lineNumberAreaWidth();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
|
||||
private slots:
|
||||
void updateLineNumberAreaWidth(int blockCount);
|
||||
void highlightCurrentLine();
|
||||
void updateLineNumberArea(const QRect& rect, int deltaY);
|
||||
|
||||
private:
|
||||
QWidget* _scriptLineNumberArea;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEditBox_h
|
|
@ -32,8 +32,10 @@ ScriptEditorWidget::ScriptEditorWidget() :
|
|||
{
|
||||
_scriptEditorWidgetUI->setupUi(this);
|
||||
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), SIGNAL(modificationChanged(bool)), this, SIGNAL(scriptModified()));
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), SIGNAL(contentsChanged()), this, SLOT(onScriptModified()));
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::modificationChanged, this,
|
||||
&ScriptEditorWidget::scriptModified);
|
||||
connect(_scriptEditorWidgetUI->scriptEdit->document(), &QTextDocument::contentsChanged, this,
|
||||
&ScriptEditorWidget::onScriptModified);
|
||||
|
||||
// remove the title bar (see the Qt docs on setTitleBarWidget)
|
||||
setTitleBarWidget(new QWidget());
|
||||
|
@ -68,16 +70,19 @@ bool ScriptEditorWidget::setRunning(bool run) {
|
|||
return false;
|
||||
}
|
||||
// Clean-up old connections.
|
||||
disconnect(this, SLOT(onScriptError(const QString&)));
|
||||
disconnect(this, SLOT(onScriptPrint(const QString&)));
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
}
|
||||
|
||||
if (run) {
|
||||
_scriptEngine = Application::getInstance()->loadScript(_currentScript, true);
|
||||
connect(_scriptEngine, SIGNAL(runningStateChanged()), this, SIGNAL(runningStateChanged()));
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
|
||||
// Make new connections.
|
||||
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(onScriptError(const QString&)));
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(onScriptPrint(const QString&)));
|
||||
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
} else {
|
||||
Application::getInstance()->stopScript(_currentScript);
|
||||
_scriptEngine = NULL;
|
||||
|
@ -101,25 +106,44 @@ bool ScriptEditorWidget::saveFile(const QString &scriptPath) {
|
|||
}
|
||||
|
||||
void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath).arg(file.errorString()));
|
||||
return;
|
||||
QUrl url(scriptPath);
|
||||
|
||||
// if the scheme length is one or lower, maybe they typed in a file, let's try
|
||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath)
|
||||
.arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
QTextStream in(&file);
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(in.readAll());
|
||||
setScriptFile(scriptPath);
|
||||
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << scriptPath;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll());
|
||||
if (!saveAs()) {
|
||||
static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent())->terminateCurrentTab();
|
||||
}
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(in.readAll());
|
||||
|
||||
setScriptFile(scriptPath);
|
||||
|
||||
disconnect(this, SLOT(onScriptError(const QString&)));
|
||||
disconnect(this, SLOT(onScriptPrint(const QString&)));
|
||||
|
||||
_scriptEngine = Application::getInstance()->getScriptEngine(scriptPath);
|
||||
_scriptEngine = Application::getInstance()->getScriptEngine(_currentScript);
|
||||
if (_scriptEngine != NULL) {
|
||||
connect(_scriptEngine, SIGNAL(runningStateChanged()), this, SIGNAL(runningStateChanged()));
|
||||
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(onScriptError(const QString&)));
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(onScriptPrint(const QString&)));
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,8 +152,15 @@ bool ScriptEditorWidget::save() {
|
|||
}
|
||||
|
||||
bool ScriptEditorWidget::saveAs() {
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), QString(), tr("Javascript (*.js)"));
|
||||
return !fileName.isEmpty() ? saveFile(fileName) : false;
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"),
|
||||
Application::getInstance()->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
Application::getInstance()->setPreviousScriptLocation(fileName);
|
||||
return saveFile(fileName);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::setScriptFile(const QString& scriptPath) {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_ScriptEditorWidget_h
|
||||
|
||||
#include <QDockWidget>
|
||||
#include "ScriptEditorWidget.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
namespace Ui {
|
||||
|
|
|
@ -34,19 +34,20 @@ ScriptEditorWindow::ScriptEditorWindow() :
|
|||
_saveMenu(new QMenu)
|
||||
{
|
||||
_ScriptEditorWindowUI->setupUi(this);
|
||||
this->setWindowFlags(Qt::Tool);
|
||||
show();
|
||||
addScriptEditorWidget("New script");
|
||||
connect(_loadMenu, SIGNAL(aboutToShow()), this, SLOT(loadMenuAboutToShow()));
|
||||
connect(_loadMenu, &QMenu::aboutToShow, this, &ScriptEditorWindow::loadMenuAboutToShow);
|
||||
_ScriptEditorWindowUI->loadButton->setMenu(_loadMenu);
|
||||
|
||||
_saveMenu->addAction("Save as..", this, SLOT(saveScriptAsClicked()), Qt::CTRL | Qt::SHIFT | Qt::Key_S);
|
||||
|
||||
_ScriptEditorWindowUI->saveButton->setMenu(_saveMenu);
|
||||
|
||||
connect(new QShortcut(QKeySequence("Ctrl+N"), this), SIGNAL(activated()), this, SLOT(newScriptClicked()));
|
||||
connect(new QShortcut(QKeySequence("Ctrl+S"), this), SIGNAL(activated()), this, SLOT(saveScriptClicked()));
|
||||
connect(new QShortcut(QKeySequence("Ctrl+O"), this), SIGNAL(activated()), this, SLOT(loadScriptClicked()));
|
||||
connect(new QShortcut(QKeySequence("F5"), this), SIGNAL(activated()), this, SLOT(toggleRunScriptClicked()));
|
||||
connect(new QShortcut(QKeySequence("Ctrl+N"), this), &QShortcut::activated, this, &ScriptEditorWindow::newScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("Ctrl+S"), this), &QShortcut::activated, this,&ScriptEditorWindow::saveScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("Ctrl+O"), this), &QShortcut::activated, this, &ScriptEditorWindow::loadScriptClicked);
|
||||
connect(new QShortcut(QKeySequence("F5"), this), &QShortcut::activated, this, &ScriptEditorWindow::toggleRunScriptClicked);
|
||||
}
|
||||
|
||||
ScriptEditorWindow::~ScriptEditorWindow() {
|
||||
|
@ -73,8 +74,11 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
|
|||
}
|
||||
|
||||
void ScriptEditorWindow::loadScriptClicked() {
|
||||
QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), QString(), tr("Javascript (*.js)"));
|
||||
QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"),
|
||||
Application::getInstance()->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!scriptName.isEmpty()) {
|
||||
Application::getInstance()->setPreviousScriptLocation(scriptName);
|
||||
addScriptEditorWidget("loading...")->loadFile(scriptName);
|
||||
updateButtons();
|
||||
}
|
||||
|
@ -126,9 +130,9 @@ void ScriptEditorWindow::saveScriptAsClicked() {
|
|||
|
||||
ScriptEditorWidget* ScriptEditorWindow::addScriptEditorWidget(QString title) {
|
||||
ScriptEditorWidget* newScriptEditorWidget = new ScriptEditorWidget();
|
||||
connect(newScriptEditorWidget, SIGNAL(scriptnameChanged()), this, SLOT(updateScriptNameOrStatus()));
|
||||
connect(newScriptEditorWidget, SIGNAL(scriptModified()), this, SLOT(updateScriptNameOrStatus()));
|
||||
connect(newScriptEditorWidget, SIGNAL(runningStateChanged()), this, SLOT(updateButtons()));
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::scriptnameChanged, this, &ScriptEditorWindow::updateScriptNameOrStatus);
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::scriptModified, this, &ScriptEditorWindow::updateScriptNameOrStatus);
|
||||
connect(newScriptEditorWidget, &ScriptEditorWidget::runningStateChanged, this, &ScriptEditorWindow::updateButtons);
|
||||
_ScriptEditorWindowUI->tabWidget->addTab(newScriptEditorWidget, title);
|
||||
_ScriptEditorWindowUI->tabWidget->setCurrentWidget(newScriptEditorWidget);
|
||||
newScriptEditorWidget->setUpdatesEnabled(true);
|
||||
|
@ -198,3 +202,10 @@ void ScriptEditorWindow::updateScriptNameOrStatus() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWindow::terminateCurrentTab() {
|
||||
if (_ScriptEditorWindowUI->tabWidget->currentIndex() != -1) {
|
||||
_ScriptEditorWindowUI->tabWidget->removeTab(_ScriptEditorWindowUI->tabWidget->currentIndex());
|
||||
this->raise();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ public:
|
|||
ScriptEditorWindow();
|
||||
~ScriptEditorWindow();
|
||||
|
||||
void terminateCurrentTab();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event);
|
||||
|
||||
|
|
28
interface/src/ui/ScriptLineNumberArea.cpp
Normal file
28
interface/src/ui/ScriptLineNumberArea.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ScriptLineNumberArea.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/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 "ScriptLineNumberArea.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) :
|
||||
QWidget(scriptEditBox)
|
||||
{
|
||||
_scriptEditBox = scriptEditBox;
|
||||
}
|
||||
|
||||
QSize ScriptLineNumberArea::sizeHint() {
|
||||
return QSize(_scriptEditBox->lineNumberAreaWidth(), 0);
|
||||
}
|
||||
|
||||
void ScriptLineNumberArea::paintEvent(QPaintEvent* event) {
|
||||
_scriptEditBox->lineNumberAreaPaintEvent(event);
|
||||
}
|
31
interface/src/ui/ScriptLineNumberArea.h
Normal file
31
interface/src/ui/ScriptLineNumberArea.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// ScriptLineNumberArea.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Thijs Wenker on 4/30/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
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptLineNumberArea_h
|
||||
#define hifi_ScriptLineNumberArea_h
|
||||
|
||||
#include <QWidget>
|
||||
#include "ScriptEditBox.h"
|
||||
|
||||
class ScriptLineNumberArea : public QWidget {
|
||||
|
||||
public:
|
||||
ScriptLineNumberArea(ScriptEditBox* scriptEditBox);
|
||||
QSize sizeHint();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
|
||||
private:
|
||||
ScriptEditBox* _scriptEditBox;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptLineNumberArea_h
|
49
interface/src/ui/ScriptsTableWidget.cpp
Normal file
49
interface/src/ui/ScriptsTableWidget.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// ScriptsTableWidget.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/03/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHeaderView>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#include "ScriptsTableWidget.h"
|
||||
|
||||
ScriptsTableWidget::ScriptsTableWidget(QWidget* parent) :
|
||||
QTableWidget(parent) {
|
||||
verticalHeader()->setVisible(false);
|
||||
horizontalHeader()->setVisible(false);
|
||||
setShowGrid(false);
|
||||
setSelectionMode(QAbstractItemView::NoSelection);
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setStyleSheet("QTableWidget { background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }");
|
||||
setToolTipDuration(200);
|
||||
setWordWrap(true);
|
||||
setGeometry(0, 0, parent->width(), parent->height());
|
||||
}
|
||||
|
||||
void ScriptsTableWidget::paintEvent(QPaintEvent* event) {
|
||||
QPainter painter(viewport());
|
||||
painter.setPen(QColor::fromRgb(225, 225, 225)); // #e1e1e1
|
||||
|
||||
int y = 0;
|
||||
for (int i = 0; i < rowCount(); i++) {
|
||||
painter.drawLine(5, rowHeight(i) + y, width(), rowHeight(i) + y);
|
||||
y += rowHeight(i);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
QTableWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void ScriptsTableWidget::keyPressEvent(QKeyEvent* event) {
|
||||
// Ignore keys so they will propagate correctly
|
||||
event->ignore();
|
||||
}
|
28
interface/src/ui/ScriptsTableWidget.h
Normal file
28
interface/src/ui/ScriptsTableWidget.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// ScriptsTableWidget.h
|
||||
// interface
|
||||
//
|
||||
// Created by Mohammed Nafees on 04/03/2014.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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__ScriptsTableWidget_h
|
||||
#define hifi__ScriptsTableWidget_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTableWidget>
|
||||
|
||||
class ScriptsTableWidget : public QTableWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScriptsTableWidget(QWidget* parent);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* event);
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
};
|
||||
|
||||
#endif // hifi__ScriptsTableWidget_h
|
|
@ -237,6 +237,7 @@ void Stats::display(
|
|||
int voxelServerCount = 0;
|
||||
|
||||
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
|
||||
// TODO: this should also support particles and models
|
||||
if (node->getType() == NodeType::VoxelServer) {
|
||||
totalPingVoxel += node->getPingMs();
|
||||
voxelServerCount++;
|
||||
|
|
|
@ -44,7 +44,6 @@ void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, c
|
|||
// immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (voxelPacketType == PacketTypeOctreeStats) {
|
||||
|
||||
int statsMessageLength = app->parseOctreeStats(mutablePacket, sendingNode);
|
||||
wasStatsPacket = true;
|
||||
if (messageLength > statsMessageLength) {
|
||||
|
@ -77,6 +76,14 @@ void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, c
|
|||
app->_particles.processDatagram(mutablePacket, sendingNode);
|
||||
} break;
|
||||
|
||||
case PacketTypeModelErase: {
|
||||
app->_models.processEraseMessage(mutablePacket, sendingNode);
|
||||
} break;
|
||||
|
||||
case PacketTypeModelData: {
|
||||
app->_models.processDatagram(mutablePacket, sendingNode);
|
||||
} break;
|
||||
|
||||
case PacketTypeEnvironmentData: {
|
||||
app->_environment.parseData(*sendingNode->getActiveSocket(), mutablePacket);
|
||||
} break;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ChatWindow</class>
|
||||
<widget class="QDockWidget" name="ChatWindow">
|
||||
<widget class="QDialog" name="ChatWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>608</height>
|
||||
<height>440</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
|
@ -16,127 +16,95 @@
|
|||
<height>238</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<property name="features">
|
||||
<set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::NoDockWidgetArea</set>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Chat</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="connectingToXMPPLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connecting to XMPP...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="numOnlineLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> online now:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="togglePinnedButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/pin.svg</normaloff>
|
||||
<normalon>../resources/images/pinned.svg</normalon>../resources/images/pin.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="connectingToXMPPLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connecting to XMPP...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="numOnlineLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> online now:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
background-color: rgba( 0, 0, 0, 0% );
|
||||
border: none;
|
||||
image: url(../resources/images/close.svg)
|
||||
|
@ -148,50 +116,104 @@ QPushButton:pressed {
|
|||
border: none;
|
||||
image: url(../resources/images/close_down.svg)
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="messagesScrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 12px;</string>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#usersWidget {
|
||||
margin-right: 20px;
|
||||
}</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="messagesScrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 12px;</string>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 0px;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="messagesVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="chatFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 0px;</string>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>78</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="messagesGridLayout">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#chatFrame {
|
||||
border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -204,69 +226,65 @@ QPushButton:pressed {
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ChatInputArea" name="messagePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ChatInputArea</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>ui/ChatInputArea.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>messagePlainTextEdit</tabstop>
|
||||
<tabstop>messagesScrollArea</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>togglePinnedButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ChatWindow</receiver>
|
||||
<slot>togglePinned()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>390</x>
|
||||
<y>42</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>550</x>
|
||||
<y>42</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>closeButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
|
|
491
interface/ui/loginDialog.ui
Normal file
491
interface/ui/loginDialog.ui
Normal file
|
@ -0,0 +1,491 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoginDialog</class>
|
||||
<widget class="QDialog" name="LoginDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1003</width>
|
||||
<height>130</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>130</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="statusArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="errorMessages" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>825</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="infoLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>17</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Authenticating...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>17</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #992800;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
Invalid username or password. <a href="https://data-web.highfidelity.io/password/new">Recover?</a></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>SplitHCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/close.svg</normaloff>../resources/images/close.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="loginAreaContainer" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>102</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="spacerLeft">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="logoArea" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="logoLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>../resources/images/hifi-logo.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="loginArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>931</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLineEdit {
|
||||
padding: 10px;
|
||||
border-width: 1px; border-style: solid; border-radius: 3px; border-color: #aaa;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="emailLineEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>54</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">padding-top: 14px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Username or Email</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>300</width>
|
||||
<height>54</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">padding-top: 14px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loginButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>141</width>
|
||||
<height>54</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">
|
||||
background: #0e7077;
|
||||
color: #e7eeee;
|
||||
border-radius: 4px; padding-top: 1px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Login</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/login.svg</normaloff>../resources/images/login.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>17</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
<a href="https://data-web.highfidelity.io/password/new">Recover password?</a></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacerRight">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -6,44 +6,58 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>310</width>
|
||||
<height>651</height>
|
||||
<width>323</width>
|
||||
<height>894</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #f7f7f7;
|
||||
font-family: Helvetica, Arial, "DejaVu Sans"; </string>
|
||||
<string notr="true">* {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
QWidget {
|
||||
background: #f7f7f7;
|
||||
}</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="widgetTitle">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>10</y>
|
||||
<width>221</width>
|
||||
<height>31</height>
|
||||
<x>37</x>
|
||||
<y>29</y>
|
||||
<width>251</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #0e7077;</string>
|
||||
<string notr="true">color: #0e7077;
|
||||
font-size: 20pt;
|
||||
background: transparent;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="currentlyRunningLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>40</y>
|
||||
<width>301</width>
|
||||
<x>36</x>
|
||||
<y>110</y>
|
||||
<width>270</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #0e7077;</string>
|
||||
<string notr="true">color: #0e7077;
|
||||
font: bold 14pt;
|
||||
background: transparent;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">Currently running</span></p></body></html></string>
|
||||
|
@ -52,22 +66,27 @@ font-family: Helvetica, Arial, "DejaVu Sans"; </string>
|
|||
<widget class="QPushButton" name="reloadAllButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>230</y>
|
||||
<x>36</x>
|
||||
<y>270</y>
|
||||
<width>111</width>
|
||||
<height>31</height>
|
||||
<height>35</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #0e7077;
|
||||
color: #fff;
|
||||
border-radius: 6px;</string>
|
||||
border-radius: 4px;
|
||||
font: bold 14pt;
|
||||
padding-top: 3px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reload All</string>
|
||||
<string>Reload all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
|
@ -78,9 +97,9 @@ border-radius: 6px;</string>
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>160</x>
|
||||
<y>230</y>
|
||||
<width>101</width>
|
||||
<height>31</height>
|
||||
<y>270</y>
|
||||
<width>93</width>
|
||||
<height>35</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
|
@ -89,10 +108,12 @@ border-radius: 6px;</string>
|
|||
<property name="styleSheet">
|
||||
<string notr="true">background: #0e7077;
|
||||
color: #fff;
|
||||
border-radius: 6px;</string>
|
||||
border-radius: 4px;
|
||||
font: bold 14pt;
|
||||
padding-top: 3px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop All</string>
|
||||
<string>Stop all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
|
@ -102,46 +123,32 @@ border-radius: 6px;</string>
|
|||
<widget class="QLabel" name="recentlyLoadedLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>280</y>
|
||||
<width>301</width>
|
||||
<x>36</x>
|
||||
<y>320</y>
|
||||
<width>265</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #0e7077;</string>
|
||||
<string notr="true">color: #0e7077;
|
||||
font: bold 14pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="Line" name="line2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>300</y>
|
||||
<width>271</width>
|
||||
<height>8</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="recentlyLoadedInstruction">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>590</y>
|
||||
<width>271</width>
|
||||
<x>36</x>
|
||||
<y>630</y>
|
||||
<width>211</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #95a5a6;</string>
|
||||
<string notr="true">color: #95a5a6;
|
||||
font-size: 14pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(click a script or use the 1-9 keys to load and run it)</string>
|
||||
|
@ -153,10 +160,10 @@ border-radius: 6px;</string>
|
|||
<widget class="QPushButton" name="hideWidgetButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>270</x>
|
||||
<y>10</y>
|
||||
<width>31</width>
|
||||
<height>31</height>
|
||||
<x>285</x>
|
||||
<y>29</y>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
|
@ -165,81 +172,101 @@ border-radius: 6px;</string>
|
|||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/close.svg</normaloff>../resources/images/close.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTableWidget" name="runningScriptsTableWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>70</y>
|
||||
<width>271</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: transparent;</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="Line" name="line1">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>60</y>
|
||||
<width>271</width>
|
||||
<height>8</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTableWidget" name="recentlyLoadedScriptsTableWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>310</y>
|
||||
<width>271</width>
|
||||
<height>281</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: transparent;</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="noRunningScriptsLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>40</y>
|
||||
<x>36</x>
|
||||
<y>110</y>
|
||||
<width>271</width>
|
||||
<height>51</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 14px;</string>
|
||||
<string notr="true">font: 14pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>There are no scripts currently running.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="recentlyLoadedScriptsTableWidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>340</y>
|
||||
<width>272</width>
|
||||
<height>280</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: transparent;
|
||||
font-size: 14pt;</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="runningScriptsTableWidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>128</y>
|
||||
<width>272</width>
|
||||
<height>161</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: transparent;
|
||||
font-size: 14pt;</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="loadScriptButton">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>36</x>
|
||||
<y>70</y>
|
||||
<width>111</width>
|
||||
<height>35</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #0e7077;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
font: bold 14pt;
|
||||
padding-top: 3px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load script</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/plus.svg</normaloff>../resources/images/plus.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>widgetTitle</zorder>
|
||||
<zorder>currentlyRunningLabel</zorder>
|
||||
<zorder>recentlyLoadedLabel</zorder>
|
||||
<zorder>recentlyLoadedInstruction</zorder>
|
||||
<zorder>hideWidgetButton</zorder>
|
||||
<zorder>recentlyLoadedScriptsTableWidget</zorder>
|
||||
<zorder>runningScriptsTableWidget</zorder>
|
||||
<zorder>noRunningScriptsLabel</zorder>
|
||||
<zorder>reloadAllButton</zorder>
|
||||
<zorder>stopAllButton</zorder>
|
||||
<zorder>loadScriptButton</zorder>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>541</width>
|
||||
<height>238</height>
|
||||
<width>690</width>
|
||||
<height>328</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
|
@ -35,7 +35,7 @@
|
|||
<string>Edit Script</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="5,0,1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -52,18 +52,18 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="scriptEdit">
|
||||
<widget class="ScriptEditBox" name="scriptEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
<pointsize>9</pointsize>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 9pt "Courier";</string>
|
||||
<string notr="true">font: 16px "Courier";</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -86,6 +86,9 @@
|
|||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 13px "Helvetica","Arial","sans-serif";</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Debug Log:</string>
|
||||
</property>
|
||||
|
@ -93,8 +96,20 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="onTheFlyCheckBox">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 13px "Helvetica","Arial","sans-serif";</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run on the fly (Careful: Any valid change made to the code will run immediately)</string>
|
||||
<string>Run on the fly (Careful: Any valid change made to the code will run immediately) </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -115,17 +130,33 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="debugText">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font: 8pt "Courier";</string>
|
||||
<string notr="true">font: 15px "Courier";</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="plainText">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ScriptEditBox</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>ui/ScriptEditBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<class>ScriptEditorWindow</class>
|
||||
<widget class="QWidget" name="ScriptEditorWindow">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>706</width>
|
||||
<height>682</height>
|
||||
<width>780</width>
|
||||
<height>717</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
|
@ -29,16 +29,7 @@
|
|||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
|
|
|
@ -46,7 +46,8 @@ AvatarData::AvatarData() :
|
|||
_isChatCirclingEnabled(false),
|
||||
_hasNewJointRotations(true),
|
||||
_headData(NULL),
|
||||
_handData(NULL),
|
||||
_handData(NULL),
|
||||
_faceModelURL("http://invalid.com"),
|
||||
_displayNameBoundingRect(),
|
||||
_displayNameTargetAlpha(0.0f),
|
||||
_displayNameAlpha(0.0f),
|
||||
|
@ -640,7 +641,7 @@ bool AvatarData::hasBillboardChangedAfterParsing(const QByteArray& packet) {
|
|||
}
|
||||
|
||||
void AvatarData::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
_faceModelURL = faceModelURL.isEmpty() ? DEFAULT_HEAD_MODEL_URL : faceModelURL;
|
||||
_faceModelURL = faceModelURL;
|
||||
|
||||
qDebug() << "Changing face model for avatar to" << _faceModelURL.toString();
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ typedef unsigned long long quint64;
|
|||
#include "HeadData.h"
|
||||
#include "HandData.h"
|
||||
|
||||
// avatar motion behaviors
|
||||
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 0;
|
||||
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 1;
|
||||
|
||||
// First bitset
|
||||
const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits
|
||||
const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits
|
||||
|
|
|
@ -584,13 +584,16 @@ public:
|
|||
};
|
||||
|
||||
glm::mat4 getGlobalTransform(const QMultiHash<QString, QString>& parentMap,
|
||||
const QHash<QString, FBXModel>& models, QString nodeID) {
|
||||
const QHash<QString, FBXModel>& models, QString nodeID, bool mixamoHack) {
|
||||
glm::mat4 globalTransform;
|
||||
while (!nodeID.isNull()) {
|
||||
const FBXModel& model = models.value(nodeID);
|
||||
globalTransform = glm::translate(model.translation) * model.preTransform * glm::mat4_cast(model.preRotation *
|
||||
model.rotation * model.postRotation) * model.postTransform * globalTransform;
|
||||
|
||||
if (mixamoHack) {
|
||||
// there's something weird about the models from Mixamo Fuse; they don't skin right with the full transform
|
||||
return globalTransform;
|
||||
}
|
||||
QList<QString> parentIDs = parentMap.values(nodeID);
|
||||
nodeID = QString();
|
||||
foreach (const QString& parentID, parentIDs) {
|
||||
|
@ -1006,9 +1009,25 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
}
|
||||
QMultiHash<QString, WeightedIndex> blendshapeChannelIndices;
|
||||
|
||||
|
||||
FBXGeometry geometry;
|
||||
foreach (const FBXNode& child, node.children) {
|
||||
if (child.name == "Objects") {
|
||||
if (child.name == "FBXHeaderExtension") {
|
||||
foreach (const FBXNode& object, child.children) {
|
||||
if (object.name == "SceneInfo") {
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
if (subobject.name == "Properties70") {
|
||||
foreach (const FBXNode& subsubobject, subobject.children) {
|
||||
if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 &&
|
||||
subsubobject.properties.at(0) == "Original|ApplicationName") {
|
||||
geometry.applicationName = subsubobject.properties.at(4).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (child.name == "Objects") {
|
||||
foreach (const FBXNode& object, child.children) {
|
||||
if (object.name == "Geometry") {
|
||||
if (object.properties.at(2) == "Mesh") {
|
||||
|
@ -1317,7 +1336,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
|
||||
// get offset transform from mapping
|
||||
FBXGeometry geometry;
|
||||
float offsetScale = mapping.value("scale", 1.0f).toFloat();
|
||||
glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(),
|
||||
mapping.value("ry").toFloat(), mapping.value("rz").toFloat())));
|
||||
|
@ -1466,7 +1484,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
|
||||
// accumulate local transforms
|
||||
QString modelID = models.contains(it.key()) ? it.key() : parentMap.value(it.key());
|
||||
glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID);
|
||||
glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID, geometry.applicationName == "mixamo.com");
|
||||
|
||||
// compute the mesh extents from the transformed vertices
|
||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||
|
@ -1803,6 +1821,33 @@ QVariantHash readMapping(const QByteArray& data) {
|
|||
return parseMapping(&buffer);
|
||||
}
|
||||
|
||||
QByteArray writeMapping(const QVariantHash& mapping) {
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
for (QVariantHash::const_iterator first = mapping.constBegin(); first != mapping.constEnd(); first++) {
|
||||
QByteArray key = first.key().toUtf8() + " = ";
|
||||
QVariantHash hashValue = first.value().toHash();
|
||||
if (hashValue.isEmpty()) {
|
||||
buffer.write(key + first.value().toByteArray() + "\n");
|
||||
continue;
|
||||
}
|
||||
for (QVariantHash::const_iterator second = hashValue.constBegin(); second != hashValue.constEnd(); second++) {
|
||||
QByteArray extendedKey = key + second.key().toUtf8();
|
||||
QVariantList listValue = second.value().toList();
|
||||
if (listValue.isEmpty()) {
|
||||
buffer.write(extendedKey + " = " + second.value().toByteArray() + "\n");
|
||||
continue;
|
||||
}
|
||||
buffer.write(extendedKey);
|
||||
for (QVariantList::const_iterator third = listValue.constBegin(); third != listValue.constEnd(); third++) {
|
||||
buffer.write(" = " + third->toByteArray());
|
||||
}
|
||||
buffer.write("\n");
|
||||
}
|
||||
}
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&model));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
|
|
@ -174,6 +174,8 @@ public:
|
|||
class FBXGeometry {
|
||||
public:
|
||||
|
||||
QString applicationName; ///< the name of the application that generated the model
|
||||
|
||||
QVector<FBXJoint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
|
||||
|
@ -218,6 +220,9 @@ Q_DECLARE_METATYPE(FBXGeometry)
|
|||
/// Reads an FST mapping from the supplied data.
|
||||
QVariantHash readMapping(const QByteArray& data);
|
||||
|
||||
/// Writes an FST mapping to a byte array.
|
||||
QByteArray writeMapping(const QVariantHash& mapping);
|
||||
|
||||
/// Reads FBX geometry from the supplied model and mapping data.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping);
|
||||
|
|
45
libraries/models/CMakeLists.txt
Normal file
45
libraries/models/CMakeLists.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
if (WIN32)
|
||||
cmake_policy (SET CMP0020 NEW)
|
||||
endif (WIN32)
|
||||
|
||||
set(ROOT_DIR ../..)
|
||||
set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
||||
|
||||
# setup for find modules
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
|
||||
|
||||
set(TARGET_NAME models)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/AutoMTC.cmake)
|
||||
auto_mtc(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME} "${AUTOMTC_SRC}")
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
|
||||
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# for streamable
|
||||
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
|
||||
|
||||
# link ZLIB and GnuTLS
|
||||
find_package(ZLIB)
|
||||
find_package(GnuTLS REQUIRED)
|
||||
|
||||
# add a definition for ssize_t so that windows doesn't bail on gnutls.h
|
||||
if (WIN32)
|
||||
add_definitions(-Dssize_t=long)
|
||||
endif ()
|
||||
|
||||
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}")
|
||||
target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" Qt5::Widgets "${GNUTLS_LIBRARY}")
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue