mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
merge andrew/inertia into andrew/ragdoll
Conflicts: interface/src/avatar/Avatar.cpp interface/src/renderer/Model.h
This commit is contained in:
commit
a62a270004
72 changed files with 2769 additions and 448 deletions
|
@ -86,7 +86,7 @@ bool ModelServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
|||
return shouldSendDeletedModels;
|
||||
}
|
||||
|
||||
int ModelServer::sendSpecialPacket(const SharedNodePointer& node) {
|
||||
int ModelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
size_t packetLength = 0;
|
||||
|
||||
|
@ -100,12 +100,13 @@ int ModelServer::sendSpecialPacket(const SharedNodePointer& node) {
|
|||
|
||||
// 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,
|
||||
hasMoreToSend = tree->encodeModelsDeletedSince(queryNode->getSequenceNumber(), deletedModelsSentAt,
|
||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||
|
||||
//qDebug() << "sending PacketType_MODEL_ERASE packetLength:" << packetLength;
|
||||
|
||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
||||
queryNode->incrementSequenceNumber();
|
||||
}
|
||||
|
||||
nodeData->setLastDeletedModelsSentAt(deletePacketSentAt);
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
||||
|
||||
virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode);
|
||||
|
||||
|
|
|
@ -362,7 +362,3 @@ void OctreeQueryNode::dumpOutOfView() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::incrementSequenceNumber() {
|
||||
_sequenceNumber++;
|
||||
}
|
|
@ -100,7 +100,9 @@ public:
|
|||
void forceNodeShutdown();
|
||||
bool isShuttingDown() const { return _isShuttingDown; }
|
||||
|
||||
void incrementSequenceNumber();
|
||||
void incrementSequenceNumber() { _sequenceNumber++; }
|
||||
|
||||
OCTREE_PACKET_SEQUENCE getSequenceNumber() const { return _sequenceNumber; }
|
||||
|
||||
private slots:
|
||||
void sendThreadFinished();
|
||||
|
|
|
@ -141,14 +141,6 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
return packetsSent; // without sending...
|
||||
}
|
||||
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
|
||||
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) {
|
||||
// Send the stats message to the client
|
||||
|
@ -170,7 +162,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" statsMessageLength: " << statsMessageLength <<
|
||||
" original size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
|
@ -192,7 +194,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += statsMessageLength;
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << statsMessageLength << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
@ -211,7 +223,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
@ -230,7 +252,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
@ -529,7 +561,8 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketToSend(_node) && !nodeData->isShuttingDown()) {
|
||||
trueBytesSent += _myServer->sendSpecialPacket(_node);
|
||||
trueBytesSent += _myServer->sendSpecialPacket(nodeData, _node);
|
||||
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
// subclass may implement these method
|
||||
virtual void beforeRun() { };
|
||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node) { return false; }
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node) { return 0; }
|
||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) { return 0; }
|
||||
|
||||
static void attachQueryNodeToNode(Node* newNode);
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ bool ParticleServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
|||
return shouldSendDeletedParticles;
|
||||
}
|
||||
|
||||
int ParticleServer::sendSpecialPacket(const SharedNodePointer& node) {
|
||||
int ParticleServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
size_t packetLength = 0;
|
||||
|
||||
|
@ -100,12 +100,13 @@ int ParticleServer::sendSpecialPacket(const SharedNodePointer& node) {
|
|||
|
||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles?
|
||||
while (hasMoreToSend) {
|
||||
hasMoreToSend = tree->encodeParticlesDeletedSince(deletedParticlesSentAt,
|
||||
hasMoreToSend = tree->encodeParticlesDeletedSince(queryNode->getSequenceNumber(), deletedParticlesSentAt,
|
||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||
|
||||
//qDebug() << "sending PacketType_PARTICLE_ERASE packetLength:" << packetLength;
|
||||
|
||||
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, SharedNodePointer(node));
|
||||
queryNode->incrementSequenceNumber();
|
||||
}
|
||||
|
||||
nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt);
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
||||
|
||||
virtual void particleCreated(const Particle& newParticle, const SharedNodePointer& senderNode);
|
||||
|
||||
|
|
|
@ -40,9 +40,34 @@ bool VoxelServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
|
|||
return shouldSendEnvironments;
|
||||
}
|
||||
|
||||
int VoxelServer::sendSpecialPacket(const SharedNodePointer& node) {
|
||||
int VoxelServer::sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node) {
|
||||
|
||||
unsigned char* copyAt = _tempOutputBuffer;
|
||||
|
||||
int numBytesPacketHeader = populatePacketHeader(reinterpret_cast<char*>(_tempOutputBuffer), PacketTypeEnvironmentData);
|
||||
copyAt += numBytesPacketHeader;
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)copyAt;
|
||||
*flagsAt = flags;
|
||||
copyAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
envPacketLength += sizeof(OCTREE_PACKET_FLAGS);
|
||||
|
||||
// pack in sequence number
|
||||
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)copyAt;
|
||||
*sequenceAt = queryNode->getSequenceNumber();
|
||||
copyAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
envPacketLength += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)copyAt;
|
||||
*timeAt = now;
|
||||
copyAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
envPacketLength += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount();
|
||||
|
||||
for (int i = 0; i < environmentsToSend; i++) {
|
||||
|
@ -50,6 +75,8 @@ int VoxelServer::sendSpecialPacket(const SharedNodePointer& node) {
|
|||
}
|
||||
|
||||
NodeList::getInstance()->writeDatagram((char*) _tempOutputBuffer, envPacketLength, SharedNodePointer(node));
|
||||
queryNode->incrementSequenceNumber();
|
||||
|
||||
return envPacketLength;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(const SharedNodePointer& node);
|
||||
virtual int sendSpecialPacket(OctreeQueryNode* queryNode, const SharedNodePointer& node);
|
||||
|
||||
private:
|
||||
bool _sendEnvironments;
|
||||
|
|
42
examples/currentAPI.js
Normal file
42
examples/currentAPI.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// currentAPI.js
|
||||
// examples
|
||||
//
|
||||
// Created by Clément Brisset on 5/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
|
||||
//
|
||||
|
||||
var array = [];
|
||||
var buffer = "\n\n\n\n\n======= JS API list =======";
|
||||
function listKeys(string, object) {
|
||||
if (string == "listKeys" || string == "array" || string == "buffer" || string == "i") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof(object) != "object") {
|
||||
array.push(string + " " + typeof(object));
|
||||
return;
|
||||
}
|
||||
|
||||
var keys = Object.keys(object);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
if (string == "") {
|
||||
listKeys(keys[i], object[keys[i]]);
|
||||
} else {
|
||||
listKeys(string + "." + keys[i], object[keys[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listKeys("", this);
|
||||
array.sort();
|
||||
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
buffer = buffer + "\n" + array[i];
|
||||
}
|
||||
buffer = buffer + "\n========= API END =========\n\n\n\n\n";
|
||||
|
||||
print(buffer);
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
Script.include("lookWithTouch.js");
|
||||
Script.include("editVoxels.js");
|
||||
Script.include("editModels.js");
|
||||
Script.include("selectAudioDevice.js");
|
||||
Script.include("hydraMove.js");
|
||||
Script.include("inspect.js");
|
|
@ -40,6 +40,8 @@ var modelURLs = [
|
|||
|
||||
var toolBar;
|
||||
|
||||
var jointList = MyAvatar.getJointNames();
|
||||
|
||||
function isLocked(properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (location.hostname == "hq.highfidelity.io" &&
|
||||
|
@ -81,10 +83,13 @@ function controller(wichSide) {
|
|||
|
||||
this.grabbing = false;
|
||||
this.modelID = { isKnownID: false };
|
||||
this.modelURL = "";
|
||||
this.oldModelRotation;
|
||||
this.oldModelPosition;
|
||||
this.oldModelRadius;
|
||||
|
||||
this.jointsIntersectingFromStart = [];
|
||||
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
position: { x: 0, y: 0, z: 0 },
|
||||
end: { x: 0, y: 0, z: 0 },
|
||||
|
@ -134,16 +139,63 @@ function controller(wichSide) {
|
|||
|
||||
this.grabbing = true;
|
||||
this.modelID = modelID;
|
||||
this.modelURL = properties.modelURL;
|
||||
|
||||
this.oldModelPosition = properties.position;
|
||||
this.oldModelRotation = properties.modelRotation;
|
||||
this.oldModelRadius = properties.radius;
|
||||
|
||||
this.jointsIntersectingFromStart = [];
|
||||
for (var i = 0; i < jointList.length; i++) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
|
||||
if (distance < this.oldModelRadius) {
|
||||
this.jointsIntersectingFromStart.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.release = function () {
|
||||
if (this.grabbing) {
|
||||
jointList = MyAvatar.getJointNames();
|
||||
|
||||
var closestJointIndex = -1;
|
||||
var closestJointDistance = 10;
|
||||
for (var i = 0; i < jointList.length; i++) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
|
||||
if (distance < closestJointDistance) {
|
||||
closestJointDistance = distance;
|
||||
closestJointIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
print("closestJoint: " + jointList[closestJointIndex]);
|
||||
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")");
|
||||
|
||||
if (closestJointDistance < this.oldModelRadius) {
|
||||
|
||||
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1) {
|
||||
// Do nothing
|
||||
} else {
|
||||
print("Attaching to " + jointList[closestJointIndex]);
|
||||
var jointPosition = MyAvatar.getJointPosition(jointList[closestJointIndex]);
|
||||
var jointRotation = MyAvatar.getJointCombinedRotation(jointList[closestJointIndex]);
|
||||
|
||||
var attachmentOffset = Vec3.subtract(this.oldModelPosition, jointPosition);
|
||||
attachmentOffset = Vec3.multiplyQbyV(Quat.inverse(jointRotation), attachmentOffset);
|
||||
var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation);
|
||||
|
||||
MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
|
||||
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius,
|
||||
true, false);
|
||||
Models.deleteModel(this.modelID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.grabbing = false;
|
||||
this.modelID.isKnownID = false;
|
||||
this.jointsIntersectingFromStart = [];
|
||||
}
|
||||
|
||||
this.checkTrigger = function () {
|
||||
|
@ -251,14 +303,21 @@ function controller(wichSide) {
|
|||
position: newPosition,
|
||||
modelRotation: newRotation
|
||||
});
|
||||
// print("Moving " + this.modelID.id);
|
||||
// Vec3.print("Old Position: ", this.oldModelPosition);
|
||||
// Vec3.print("Sav Position: ", newPosition);
|
||||
// Quat.print("Old Rotation: ", this.oldModelRotation);
|
||||
// Quat.print("New Rotation: ", newRotation);
|
||||
|
||||
this.oldModelRotation = newRotation;
|
||||
this.oldModelPosition = newPosition;
|
||||
|
||||
var indicesToRemove = [];
|
||||
for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) {
|
||||
var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition);
|
||||
if (distance >= this.oldModelRadius) {
|
||||
indicesToRemove.push(this.jointsIntersectingFromStart[i]);
|
||||
}
|
||||
|
||||
}
|
||||
for (var i = 0; i < indicesToRemove.length; ++i) {
|
||||
this.jointsIntersectingFromStart.splice(this.jointsIntersectingFromStart.indexOf(indicesToRemove[i], 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,6 +351,43 @@ function controller(wichSide) {
|
|||
}
|
||||
|
||||
if (this.pressing) {
|
||||
// Checking for attachments intersecting
|
||||
var attachments = MyAvatar.getAttachmentData();
|
||||
var attachmentIndex = -1;
|
||||
var attachmentX = LASER_LENGTH_FACTOR;
|
||||
|
||||
for (var i = 0; i < attachments.length; ++i) {
|
||||
var position = Vec3.sum(MyAvatar.getJointPosition(attachments[i].jointName), attachments[i].translation);
|
||||
var scale = attachments[i].scale;
|
||||
|
||||
var A = this.palmPosition;
|
||||
var B = this.front;
|
||||
var P = position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
|
||||
if (d < scale / 2.0 && 0 < x && x < attachmentX) {
|
||||
attachmentIndex = i;
|
||||
attachmentX = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (attachmentIndex != -1) {
|
||||
MyAvatar.detachOne(attachments[attachmentIndex].modelURL, attachments[attachmentIndex].jointName);
|
||||
Models.addModel({
|
||||
position: Vec3.sum(MyAvatar.getJointPosition(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].translation),
|
||||
modelRotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
|
||||
attachments[attachmentIndex].rotation),
|
||||
radius: attachments[attachmentIndex].scale / 2.0,
|
||||
modelURL: attachments[attachmentIndex].modelURL
|
||||
});
|
||||
}
|
||||
|
||||
// There is none so ...
|
||||
// Checking model tree
|
||||
Vec3.print("Looking at: ", this.palmPosition);
|
||||
var pickRay = { origin: this.palmPosition,
|
||||
direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) };
|
||||
|
@ -306,7 +402,7 @@ function controller(wichSide) {
|
|||
var identify = Models.identifyModel(foundModel);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
foundModel = identify;
|
||||
}
|
||||
|
@ -410,8 +506,6 @@ function checkController(deltaTime) {
|
|||
moveOverlays();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function initToolBar() {
|
||||
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
|
||||
// New Model
|
||||
|
@ -474,10 +568,15 @@ function mousePressEvent(event) {
|
|||
}
|
||||
|
||||
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
|
||||
Models.addModel({ position: position,
|
||||
radius: radiusDefault,
|
||||
modelURL: url
|
||||
});
|
||||
|
||||
if (position.x > 0 && position.y > 0 && position.z > 0) {
|
||||
Models.addModel({ position: position,
|
||||
radius: radiusDefault,
|
||||
modelURL: url
|
||||
});
|
||||
} else {
|
||||
print("Can't create model: Model would be out of bounds.");
|
||||
}
|
||||
|
||||
} else {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
@ -493,7 +592,7 @@ function mousePressEvent(event) {
|
|||
var identify = Models.identifyModel(foundModel);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
foundModel = identify;
|
||||
}
|
||||
|
@ -667,18 +766,30 @@ function mouseReleaseEvent(event) {
|
|||
glowedModelID.isKnownID = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already
|
||||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var modelMenuAddedDelete = false;
|
||||
function setupModelMenus() {
|
||||
print("setupModelMenus()");
|
||||
// add our menuitems
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete Model", shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
||||
if (!Menu.menuItemExists("Edit","Delete")) {
|
||||
print("no delete... adding ours");
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
||||
shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
||||
modelMenuAddedDelete = true;
|
||||
} else {
|
||||
print("delete exists... don't add ours");
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupModelMenus() {
|
||||
// delete our menuitems
|
||||
Menu.removeSeparator("Edit", "Models");
|
||||
Menu.removeMenuItem("Edit", "Delete Model");
|
||||
if (modelMenuAddedDelete) {
|
||||
// delete our menuitems
|
||||
Menu.removeSeparator("Edit", "Models");
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
|
@ -698,7 +809,7 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|||
setupModelMenus();
|
||||
Menu.menuItemEvent.connect(function(menuItem){
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Delete Model") {
|
||||
if (menuItem == "Delete") {
|
||||
if (leftController.grabbing) {
|
||||
print(" Delete Model.... leftController.modelID="+ leftController.modelID);
|
||||
Models.deleteModel(leftController.modelID);
|
||||
|
|
|
@ -28,8 +28,10 @@ var NEW_VOXEL_SIZE = 1.0;
|
|||
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
||||
var PIXELS_PER_EXTRUDE_VOXEL = 16;
|
||||
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
|
||||
var MAX_VOXEL_SCALE = 16.0;
|
||||
var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0);
|
||||
var MAX_VOXEL_SCALE_POWER = 4;
|
||||
var MIN_VOXEL_SCALE_POWER = -8;
|
||||
var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER);
|
||||
var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER);
|
||||
var WHITE_COLOR = { red: 255, green: 255, blue: 255 };
|
||||
|
||||
var MAX_PASTE_VOXEL_SCALE = 256;
|
||||
|
@ -330,6 +332,13 @@ function ScaleSelector() {
|
|||
visible: false
|
||||
});
|
||||
this.setScale = function(scale) {
|
||||
if (scale > MAX_VOXEL_SCALE) {
|
||||
scale = MAX_VOXEL_SCALE;
|
||||
}
|
||||
if (scale < MIN_VOXEL_SCALE) {
|
||||
scale = MIN_VOXEL_SCALE;
|
||||
}
|
||||
|
||||
this.scale = scale;
|
||||
this.power = Math.floor(Math.log(scale) / Math.log(2));
|
||||
rescaleImport();
|
||||
|
@ -391,12 +400,9 @@ function ScaleSelector() {
|
|||
|
||||
this.incrementScale = function() {
|
||||
copyScale = false;
|
||||
if (this.power < 13) {
|
||||
if (this.power < MAX_VOXEL_SCALE_POWER) {
|
||||
++this.power;
|
||||
this.scale *= 2.0;
|
||||
if (this.scale > MAX_VOXEL_SCALE) {
|
||||
this.scale = MAX_VOXEL_SCALE;
|
||||
}
|
||||
this.update();
|
||||
rescaleImport();
|
||||
resizeVoxelSound.play(voxelSizePlus);
|
||||
|
@ -405,7 +411,7 @@ function ScaleSelector() {
|
|||
|
||||
this.decrementScale = function() {
|
||||
copyScale = false;
|
||||
if (-4 < this.power) {
|
||||
if (MIN_VOXEL_SCALE_POWER < this.power) {
|
||||
--this.power;
|
||||
this.scale /= 2.0;
|
||||
this.update();
|
||||
|
@ -1111,7 +1117,12 @@ function keyReleaseEvent(event) {
|
|||
trackKeyReleaseEvent(event); // used by preview support
|
||||
}
|
||||
|
||||
function setupMenus() {
|
||||
|
||||
// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already
|
||||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var voxelMenuAddedDelete = false;
|
||||
function setupVoxelMenus() {
|
||||
// hook up menus
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
|
@ -1121,7 +1132,13 @@ function setupMenus() {
|
|||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
||||
|
||||
|
||||
if (!Menu.menuItemExists("Edit","Delete")) {
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
||||
shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
||||
voxelMenuAddedDelete = true;
|
||||
}
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Voxels", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
|
||||
|
@ -1135,7 +1152,9 @@ function cleanupMenus() {
|
|||
Menu.removeMenuItem("Edit", "Copy");
|
||||
Menu.removeMenuItem("Edit", "Paste");
|
||||
Menu.removeMenuItem("Edit", "Nudge");
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
if (voxelMenuAddedDelete) {
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
Menu.removeSeparator("File", "Voxels");
|
||||
Menu.removeMenuItem("File", "Export Voxels");
|
||||
Menu.removeMenuItem("File", "Import Voxels");
|
||||
|
@ -1476,4 +1495,4 @@ Script.scriptEnding.connect(scriptEnding);
|
|||
|
||||
Script.update.connect(update);
|
||||
|
||||
setupMenus();
|
||||
setupVoxelMenus();
|
||||
|
|
302
examples/locationsMenu.js
Normal file
302
examples/locationsMenu.js
Normal file
|
@ -0,0 +1,302 @@
|
|||
//
|
||||
// locationsMenu.js
|
||||
// examples
|
||||
//
|
||||
// Created by Ryan Huffman on 5/28/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
|
||||
//
|
||||
|
||||
var scriptUrl = "https://script.google.com/macros/s/AKfycbwIo4lmF-qUwX1Z-9eA_P-g2gse9oFhNcjVyyksGukyDDEFXgU/exec?action=listOwners&domain=alpha.highfidelity.io";
|
||||
|
||||
var LocationMenu = function(opts) {
|
||||
var self = this;
|
||||
|
||||
var pageSize = opts.pageSize || 10;
|
||||
var menuWidth = opts.menuWidth || 150;
|
||||
var menuHeight = opts.menuItemHeight || 24;
|
||||
|
||||
var inactiveColor = { red: 51, green: 102, blue: 102 };
|
||||
var activeColor = { red: 18, green: 66, blue: 66 };
|
||||
var prevNextColor = { red: 192, green: 192, blue: 192 };
|
||||
var disabledColor = { red: 64, green: 64, blue: 64};
|
||||
var position = { x: 0, y: 0 };
|
||||
|
||||
var locationIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/location.svg";
|
||||
var toolHeight = 50;
|
||||
var toolWidth = 50;
|
||||
var visible = false;
|
||||
var menuItemOffset = {
|
||||
x: 55,
|
||||
y: 0,
|
||||
};
|
||||
var menuItemPadding = 5;
|
||||
var margin = 7;
|
||||
var fullMenuHeight = (2 * menuItemOffset.y) + (menuHeight * (pageSize + 1));
|
||||
var menuOffset = -fullMenuHeight + toolHeight;
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
|
||||
this.locations = [];
|
||||
this.numPages = 1;
|
||||
this.page = 0;
|
||||
|
||||
this.menuToggleButton = Overlays.addOverlay("image", {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
width: toolWidth, height: toolHeight,
|
||||
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||
imageURL: locationIconUrl,
|
||||
alpha: 0.9
|
||||
});
|
||||
|
||||
this.background = Overlays.addOverlay("text", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: menuWidth + 10,
|
||||
height: (menuHeight * (pageSize + 1)) + 10,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: "",
|
||||
visible: visible,
|
||||
});
|
||||
|
||||
this.menuItems = [];
|
||||
for (var i = 0; i < pageSize; i++) {
|
||||
var menuItem = Overlays.addOverlay("text", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: menuWidth,
|
||||
height: menuHeight,
|
||||
color: inactiveColor,
|
||||
topMargin: margin,
|
||||
leftMargin: margin,
|
||||
text: (i == 0) ? "Loading..." : "",
|
||||
visible: visible,
|
||||
});
|
||||
this.menuItems.push({ overlay: menuItem, location: null });
|
||||
}
|
||||
|
||||
this.previousButton = Overlays.addOverlay("text", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: menuWidth / 2,
|
||||
height: menuHeight,
|
||||
color: disabledColor,
|
||||
topMargin: margin,
|
||||
leftMargin: margin,
|
||||
text: "Previous",
|
||||
visible: visible,
|
||||
});
|
||||
|
||||
this.nextButton = Overlays.addOverlay("text", {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: menuWidth / 2,
|
||||
height: menuHeight,
|
||||
color: disabledColor,
|
||||
topMargin: margin,
|
||||
leftMargin: margin,
|
||||
text: "Next",
|
||||
visible: visible,
|
||||
});
|
||||
|
||||
this.reposition = function(force) {
|
||||
var newWindowDimensions = Controller.getViewportDimensions();
|
||||
if (force || newWindowDimensions.y != windowDimensions.y) {
|
||||
windowDimensions = newWindowDimensions;
|
||||
|
||||
position.x = 8;
|
||||
position.y = Math.floor(windowDimensions.y / 2) + 25 + 50 + 8;
|
||||
|
||||
Overlays.editOverlay(self.menuToggleButton, {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
});
|
||||
Overlays.editOverlay(self.background, {
|
||||
x: position.x + menuItemOffset.x,
|
||||
y: position.y + menuItemOffset.y - 2 * menuItemPadding + menuOffset,
|
||||
});
|
||||
for (var i = 0; i < pageSize; i++) {
|
||||
Overlays.editOverlay(self.menuItems[i].overlay, {
|
||||
x: position.x + menuItemOffset.x + menuItemPadding,
|
||||
y: position.y + menuItemOffset.y - menuItemPadding + (i * menuHeight) + menuOffset,
|
||||
});
|
||||
}
|
||||
Overlays.editOverlay(self.previousButton, {
|
||||
x: position.x + menuItemOffset.x + menuItemPadding,
|
||||
y: position.y + menuItemOffset.y - menuItemPadding + (pageSize * menuHeight) + menuOffset,
|
||||
});
|
||||
Overlays.editOverlay(self.nextButton, {
|
||||
x: position.x + menuItemOffset.x + menuItemPadding + (menuWidth / 2),
|
||||
y: position.y + menuItemOffset.y - menuItemPadding + (pageSize * menuHeight) + menuOffset,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.updateLocations = function(locations) {
|
||||
this.locations = locations;
|
||||
this.numPages = Math.ceil(locations.length / pageSize);
|
||||
this.goToPage(0);
|
||||
}
|
||||
|
||||
this.setError = function() {
|
||||
Overlays.editOverlay(this.menuItems[0].overlay, { text: "Error loading data" });
|
||||
}
|
||||
|
||||
this.toggleMenu = function() {
|
||||
visible = !visible;
|
||||
for (var i = 0; i < this.menuItems.length; i++) {
|
||||
Overlays.editOverlay(this.menuItems[i].overlay, { visible: visible});
|
||||
}
|
||||
Overlays.editOverlay(this.previousButton, { visible: visible});
|
||||
Overlays.editOverlay(this.nextButton, { visible: visible});
|
||||
Overlays.editOverlay(this.background, { visible: visible});
|
||||
if (visible) {
|
||||
Overlays.editOverlay(this.menuToggleButton, { subImage: { x: 0, y: 0, width: toolWidth, height: toolHeight } }),
|
||||
} else {
|
||||
Overlays.editOverlay(this.menuToggleButton, { subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight } }),
|
||||
}
|
||||
}
|
||||
|
||||
this.goToPage = function(pageNumber) {
|
||||
if (pageNumber < 0 || pageNumber >= this.numPages) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.page = pageNumber;
|
||||
var start = pageNumber * pageSize;
|
||||
for (var i = 0; i < pageSize; i++) {
|
||||
var update = {};
|
||||
var location = null;
|
||||
if (start + i < this.locations.length) {
|
||||
location = this.locations[start + i];
|
||||
update.text = (start + i + 1) + ". " + location.username;
|
||||
update.color = inactiveColor;
|
||||
} else {
|
||||
update.text = "";
|
||||
update.color = disabledColor;
|
||||
}
|
||||
Overlays.editOverlay(this.menuItems[i].overlay, update);
|
||||
this.menuItems[i].location = location;
|
||||
}
|
||||
|
||||
this.previousEnabled = pageNumber > 0;
|
||||
this.nextEnabled = pageNumber < (this.numPages - 1);
|
||||
|
||||
Overlays.editOverlay(this.previousButton, { color: this.previousEnabled ? prevNextColor : disabledColor});
|
||||
Overlays.editOverlay(this.nextButton, { color: this.nextEnabled ? prevNextColor : disabledColor });
|
||||
}
|
||||
|
||||
this.mousePressEvent = function(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
|
||||
if (clickedOverlay == self.menuToggleButton) {
|
||||
self.toggleMenu();
|
||||
} else if (clickedOverlay == self.previousButton) {
|
||||
if (self.previousEnabled) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: activeColor });
|
||||
}
|
||||
} else if (clickedOverlay == self.nextButton) {
|
||||
if (self.nextEnabled) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: activeColor });
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < self.menuItems.length; i++) {
|
||||
if (clickedOverlay == self.menuItems[i].overlay) {
|
||||
if (self.menuItems[i].location != null) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: activeColor });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.mouseReleaseEvent = function(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
|
||||
if (clickedOverlay == self.previousButton) {
|
||||
if (self.previousEnabled) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
|
||||
self.goToPage(self.page - 1);
|
||||
}
|
||||
} else if (clickedOverlay == self.nextButton) {
|
||||
if (self.nextEnabled) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
|
||||
self.goToPage(self.page + 1);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < self.menuItems.length; i++) {
|
||||
if (clickedOverlay == self.menuItems[i].overlay) {
|
||||
if (self.menuItems[i].location != null) {
|
||||
Overlays.editOverlay(clickedOverlay, { color: inactiveColor });
|
||||
var location = self.menuItems[i].location;
|
||||
Window.location = "hifi://" + location.domain + "/"
|
||||
+ location.x + "," + location.y + "," + location.z;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanup = function() {
|
||||
for (var i = 0; i < self.menuItems.length; i++) {
|
||||
Overlays.deleteOverlay(self.menuItems[i].overlay);
|
||||
}
|
||||
Overlays.deleteOverlay(self.menuToggleButton);
|
||||
Overlays.deleteOverlay(self.previousButton);
|
||||
Overlays.deleteOverlay(self.nextButton);
|
||||
Overlays.deleteOverlay(self.background);
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(this.mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(this.mouseReleaseEvent);
|
||||
Script.update.connect(this.reposition);
|
||||
Script.scriptEnding.connect(this.cleanup);
|
||||
|
||||
this.reposition(true);
|
||||
};
|
||||
|
||||
var locationMenu = new LocationMenu({ pageSize: 8 });
|
||||
|
||||
print("Loading strip data from " + scriptUrl);
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.responseType = 'json';
|
||||
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState == req.DONE) {
|
||||
if (req.status == 200 && req.response != null) {
|
||||
for (var domain in req.response) {
|
||||
var locations = req.response[domain];
|
||||
var users = [];
|
||||
for (var i = 0; i < locations.length; i++) {
|
||||
var loc = locations[i];
|
||||
var x1 = loc[1],
|
||||
x2 = loc[2],
|
||||
y1 = loc[3],
|
||||
y2 = loc[4];
|
||||
users.push({
|
||||
domain: domain,
|
||||
username: loc[0],
|
||||
x: x1,
|
||||
y: 300,
|
||||
z: y1,
|
||||
});
|
||||
}
|
||||
locationMenu.updateLocations(users);
|
||||
}
|
||||
} else {
|
||||
print("Error loading data: " + req.status + " " + req.statusText + ", " + req.errorCode + ": " + req.responseText);
|
||||
locationMenu.setError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.open("GET", scriptUrl);
|
||||
req.send();
|
|
@ -13,68 +13,88 @@
|
|||
|
||||
|
||||
function setupMenus() {
|
||||
Menu.addMenu("Foo");
|
||||
Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 3", "META+F" );
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 4",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
if (!Menu.menuExists("Foo")) {
|
||||
Window.alert("Adding Menu Foo!");
|
||||
Menu.addMenu("Foo");
|
||||
Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 3", "META+F" );
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 5",
|
||||
shortcutKey: "ALT+F",
|
||||
isCheckable: true
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 4",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 5",
|
||||
shortcutKey: "ALT+F",
|
||||
isCheckable: true
|
||||
});
|
||||
|
||||
|
||||
Menu.addSeparator("Foo","Removable Tools");
|
||||
Menu.addMenuItem("Foo","Remove Foo item 4");
|
||||
Menu.addMenuItem("Foo","Remove Foo");
|
||||
Menu.addMenuItem("Foo","Remove Bar-Spam");
|
||||
Menu.addMenu("Bar");
|
||||
Menu.addSeparator("Foo","Removable Tools");
|
||||
Menu.addMenuItem("Foo","Remove Foo item 4");
|
||||
Menu.addMenuItem("Foo","Remove Foo");
|
||||
Menu.addMenuItem("Foo","Remove Bar-Spam");
|
||||
|
||||
Menu.addMenuItem("Bar","Bar item 1", "b");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar",
|
||||
menuItemName: "Bar item 2",
|
||||
shortcutKeyEvent: { text: "B", isControl: true }
|
||||
});
|
||||
Menu.addMenuItem("Foo","Remove Spam item 2");
|
||||
|
||||
Menu.addMenu("Bar > Spam");
|
||||
Menu.addMenuItem("Bar > Spam","Spam item 1");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar > Spam",
|
||||
menuItemName: "Spam item 2",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Remove Spam item 2"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Foo already exists!");
|
||||
}
|
||||
|
||||
if (!Menu.menuExists("Bar")) {
|
||||
Window.alert("Adding Menu Bar!");
|
||||
Menu.addMenu("Bar");
|
||||
Menu.addMenuItem("Bar","Bar item 1", "b");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar",
|
||||
menuItemName: "Bar item 2",
|
||||
shortcutKeyEvent: { text: "B", isControl: true }
|
||||
});
|
||||
|
||||
Menu.addMenu("Bar > Spam");
|
||||
Menu.addMenuItem("Bar > Spam","Spam item 1");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar > Spam",
|
||||
menuItemName: "Spam item 2",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
|
||||
Menu.addSeparator("Bar > Spam","Other Items");
|
||||
Menu.addMenuItem("Bar > Spam","Remove Spam item 2");
|
||||
Menu.addMenuItem("Foo","Remove Spam item 2");
|
||||
Menu.addSeparator("Bar > Spam","Other Items");
|
||||
Menu.addMenuItem("Bar > Spam","Remove Spam item 2");
|
||||
}
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Remove Spam item 2"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "before Cut",
|
||||
beforeItem: "Cut"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "after Nudge",
|
||||
afterItem: "Nudge"
|
||||
});
|
||||
if (Menu.menuItemExists("Edit","Cut")) {
|
||||
Window.alert("Menu Item Cut exist adding 'before Cut'.");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "before Cut",
|
||||
beforeItem: "Cut"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Item Cut doesn't exist!");
|
||||
}
|
||||
|
||||
if (Menu.menuItemExists("Edit","Nudge")) {
|
||||
Window.alert("Menu Item Nudge exist adding 'after Nudge'.");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "after Nudge",
|
||||
afterItem: "Nudge"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Item Nudge doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
|
@ -82,6 +102,10 @@ function scriptEnding() {
|
|||
|
||||
Menu.removeMenu("Foo");
|
||||
Menu.removeMenu("Bar");
|
||||
|
||||
Menu.removeMenuItem("Edit", "before Cut");
|
||||
Menu.removeMenuItem("Edit", "after Nudge");
|
||||
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
|
|
43
examples/playSoundLoop.js
Normal file
43
examples/playSoundLoop.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// playSoundLoop.js
|
||||
// examples
|
||||
//
|
||||
// Created by David Rowe on 5/29/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This example script plays a sound in a continuous loop.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guitars/Guitar+-+Nylon+A.raw");
|
||||
|
||||
var soundPlaying = false;
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text === "1") {
|
||||
if (!Audio.isInjectorPlaying(soundPlaying)) {
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = MyAvatar.position;
|
||||
options.volume = 0.5;
|
||||
options.loop = true;
|
||||
soundPlaying = Audio.playSound(sound, options);
|
||||
print("Started sound loop");
|
||||
} else {
|
||||
Audio.stopInjector(soundPlaying);
|
||||
print("Stopped sound loop");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
if (Audio.isInjectorPlaying(soundPlaying)) {
|
||||
Audio.stopInjector(soundPlaying);
|
||||
print("Stopped sound loop");
|
||||
}
|
||||
}
|
||||
|
||||
// Connect a call back that happens every frame
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
|
@ -40,6 +40,8 @@ var passedTime = 0.0;
|
|||
var startPosition = null;
|
||||
var animationLenght = 2.0;
|
||||
|
||||
var sitting = false;
|
||||
|
||||
// This is the pose we would like to end up
|
||||
var pose = [
|
||||
{joint:"RightUpLeg", rotation: {x:100.0, y:15.0, z:0.0}},
|
||||
|
@ -101,31 +103,41 @@ var standingUpAnimation = function(deltaTime){
|
|||
}
|
||||
}
|
||||
|
||||
function sitDown() {
|
||||
sitting = true;
|
||||
passedTime = 0.0;
|
||||
startPosition = MyAvatar.position;
|
||||
storeStartPoseAndTransition();
|
||||
try{
|
||||
Script.update.disconnect(standingUpAnimation);
|
||||
} catch(e){
|
||||
// no need to handle. if it wasn't connected no harm done
|
||||
}
|
||||
Script.update.connect(sittingDownAnimation);
|
||||
Overlays.editOverlay(sitDownButton, { visible: false });
|
||||
Overlays.editOverlay(standUpButton, { visible: true });
|
||||
}
|
||||
|
||||
function standUp() {
|
||||
sitting = false;
|
||||
passedTime = 0.0;
|
||||
startPosition = MyAvatar.position;
|
||||
try{
|
||||
Script.update.disconnect(sittingDownAnimation);
|
||||
} catch (e){}
|
||||
Script.update.connect(standingUpAnimation);
|
||||
Overlays.editOverlay(standUpButton, { visible: false });
|
||||
Overlays.editOverlay(sitDownButton, { visible: true });
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(function(event){
|
||||
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
|
||||
if (clickedOverlay == sitDownButton) {
|
||||
passedTime = 0.0;
|
||||
startPosition = MyAvatar.position;
|
||||
storeStartPoseAndTransition();
|
||||
try{
|
||||
Script.update.disconnect(standingUpAnimation);
|
||||
} catch(e){
|
||||
// no need to handle. if it wasn't connected no harm done
|
||||
}
|
||||
Script.update.connect(sittingDownAnimation);
|
||||
Overlays.editOverlay(sitDownButton, { visible: false });
|
||||
Overlays.editOverlay(standUpButton, { visible: true });
|
||||
sitDown();
|
||||
} else if (clickedOverlay == standUpButton) {
|
||||
passedTime = 0.0;
|
||||
startPosition = MyAvatar.position;
|
||||
try{
|
||||
Script.update.disconnect(sittingDownAnimation);
|
||||
} catch (e){}
|
||||
Script.update.connect(standingUpAnimation);
|
||||
Overlays.editOverlay(standUpButton, { visible: false });
|
||||
Overlays.editOverlay(sitDownButton, { visible: true });
|
||||
standUp();
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -140,7 +152,19 @@ function update(deltaTime){
|
|||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text === ".") {
|
||||
if (sitting) {
|
||||
standUp();
|
||||
} else {
|
||||
sitDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Script.update.connect(update);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
|
||||
|
|
40
interface/resources/shaders/cascaded_shadow_map.frag
Normal file
40
interface/resources/shaders/cascaded_shadow_map.frag
Normal file
|
@ -0,0 +1,40 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// cascaded_shadow_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
// the distances to the cascade sections
|
||||
uniform vec3 shadowDistances;
|
||||
|
||||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the color in shadow
|
||||
varying vec4 shadowColor;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
void main(void) {
|
||||
// compute the index of the cascade to use and the corresponding texture coordinates
|
||||
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
|
||||
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
|
||||
dot(gl_EyePlaneR[shadowIndex], position));
|
||||
|
||||
gl_FragColor = mix(shadowColor, gl_Color, 0.25 *
|
||||
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r));
|
||||
}
|
33
interface/resources/shaders/cascaded_shadow_map.vert
Normal file
33
interface/resources/shaders/cascaded_shadow_map.vert
Normal file
|
@ -0,0 +1,33 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// cascaded_shadow_map.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the color in shadow
|
||||
varying vec4 shadowColor;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
void main(void) {
|
||||
// the shadow color includes only the ambient terms
|
||||
shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient);
|
||||
|
||||
// the normal color includes diffuse
|
||||
vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
|
||||
gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
|
||||
|
||||
// generate the shadow texture coordinates using the eye position
|
||||
position = gl_ModelViewMatrix * gl_Vertex;
|
||||
|
||||
// use the fixed function transform
|
||||
gl_Position = ftransform();
|
||||
}
|
56
interface/resources/shaders/model_cascaded_shadow_map.frag
Normal file
56
interface/resources/shaders/model_cascaded_shadow_map.frag
Normal file
|
@ -0,0 +1,56 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_cascaded_shadow_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
// the distances to the cascade sections
|
||||
uniform vec3 shadowDistances;
|
||||
|
||||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
// compute the index of the cascade to use and the corresponding texture coordinates
|
||||
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
|
||||
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
|
||||
dot(gl_EyePlaneR[shadowIndex], position));
|
||||
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 normalizedNormal = normalize(normal);
|
||||
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
|
||||
float facingLight = step(0.0, diffuse) * 0.25 *
|
||||
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))),
|
||||
normalizedNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
|
||||
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_cascaded_shadow_normal_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the normal map texture
|
||||
uniform sampler2D normalMap;
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
// the distances to the cascade sections
|
||||
uniform vec3 shadowDistances;
|
||||
|
||||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
// the interpolated tangent
|
||||
varying vec4 interpolatedTangent;
|
||||
|
||||
void main(void) {
|
||||
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
|
||||
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
|
||||
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
|
||||
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
|
||||
|
||||
// compute the index of the cascade to use and the corresponding texture coordinates
|
||||
int shadowIndex = int(dot(step(vec3(interpolatedPosition.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
|
||||
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], interpolatedPosition),
|
||||
dot(gl_EyePlaneT[shadowIndex], interpolatedPosition),
|
||||
dot(gl_EyePlaneR[shadowIndex], interpolatedPosition));
|
||||
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
|
||||
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
|
||||
float diffuse = dot(viewNormal, gl_LightSource[0].position);
|
||||
float facingLight = step(0.0, diffuse) * 0.25 *
|
||||
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
|
||||
normalize(vec4(vec3(interpolatedPosition), 0.0))), viewNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) +
|
||||
vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_cascaded_shadow_normal_specular_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the normal map texture
|
||||
uniform sampler2D normalMap;
|
||||
|
||||
// the specular map texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
// the distances to the cascade sections
|
||||
uniform vec3 shadowDistances;
|
||||
|
||||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
// the interpolated tangent
|
||||
varying vec4 interpolatedTangent;
|
||||
|
||||
void main(void) {
|
||||
vec3 normalizedNormal = normalize(vec3(interpolatedNormal));
|
||||
vec3 normalizedTangent = normalize(vec3(interpolatedTangent));
|
||||
vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent));
|
||||
vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0);
|
||||
|
||||
// compute the index of the cascade to use and the corresponding texture coordinates
|
||||
int shadowIndex = int(dot(step(vec3(interpolatedPosition.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
|
||||
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], interpolatedPosition),
|
||||
dot(gl_EyePlaneT[shadowIndex], interpolatedPosition),
|
||||
dot(gl_EyePlaneR[shadowIndex], interpolatedPosition));
|
||||
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 viewNormal = vec4(normalizedTangent * localNormal.x +
|
||||
normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0);
|
||||
float diffuse = dot(viewNormal, gl_LightSource[0].position);
|
||||
float facingLight = step(0.0, diffuse) * 0.25 *
|
||||
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position -
|
||||
normalize(vec4(interpolatedPosition.xyz, 0.0))), viewNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
|
||||
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// model_cascaded_shadow_specular_map.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 5/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
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the specular texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the shadow texture
|
||||
uniform sampler2DShadow shadowMap;
|
||||
|
||||
// the distances to the cascade sections
|
||||
uniform vec3 shadowDistances;
|
||||
|
||||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the interpolated position in view space
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
// compute the index of the cascade to use and the corresponding texture coordinates
|
||||
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
|
||||
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
|
||||
dot(gl_EyePlaneR[shadowIndex], position));
|
||||
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 normalizedNormal = normalize(normal);
|
||||
float diffuse = dot(normalizedNormal, gl_LightSource[0].position);
|
||||
float facingLight = step(0.0, diffuse) * 0.25 *
|
||||
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
|
||||
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))),
|
||||
normalizedNormal));
|
||||
|
||||
// modulate texture by base color and add specular contribution
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) *
|
||||
gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0);
|
||||
}
|
|
@ -16,6 +16,7 @@ uniform sampler2DShadow shadowMap;
|
|||
// the inverse of the size of the shadow map
|
||||
const float shadowScale = 1.0 / 2048.0;
|
||||
|
||||
// the color in shadow
|
||||
varying vec4 shadowColor;
|
||||
|
||||
void main(void) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the color in shadow
|
||||
varying vec4 shadowColor;
|
||||
|
||||
void main(void) {
|
||||
|
@ -21,10 +22,10 @@ void main(void) {
|
|||
vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
|
||||
gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position)));
|
||||
|
||||
// generate the shadow texture coordinate using the eye position
|
||||
// generate the shadow texture coordinates using the eye position
|
||||
vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition),
|
||||
dot(gl_EyePlaneR[0], eyePosition), 1.0);
|
||||
dot(gl_EyePlaneR[0], eyePosition), 1.0);
|
||||
|
||||
// use the fixed function transform
|
||||
gl_Position = ftransform();
|
||||
|
|
20
interface/resources/styles/console.qss
Normal file
20
interface/resources/styles/console.qss
Normal file
|
@ -0,0 +1,20 @@
|
|||
* {
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#promptTextEdit {
|
||||
color: #425d72;
|
||||
}
|
||||
|
||||
#promptTextEdit:!enabled {
|
||||
color: #7f7f7f;
|
||||
}
|
||||
|
||||
#promptGutterLabel {
|
||||
color: #a9bbc3;
|
||||
}
|
||||
|
||||
#promptGutterLabel:!enabled {
|
||||
color: #7f7f7f;
|
||||
}
|
|
@ -105,6 +105,8 @@ const int STARTUP_JITTER_SAMPLES = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / 2
|
|||
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js";
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
if (message.size() > 0) {
|
||||
QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate);
|
||||
|
@ -162,10 +164,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_bytesPerSecond(0),
|
||||
_nodeBoundsDisplay(this),
|
||||
_previousScriptLocation(),
|
||||
_applicationOverlay(),
|
||||
_runningScriptsWidget(new RunningScriptsWidget(_window)),
|
||||
_runningScriptsWidgetWasVisible(false),
|
||||
_trayIcon(new QSystemTrayIcon(_window)),
|
||||
_applicationOverlay()
|
||||
_trayIcon(new QSystemTrayIcon(_window))
|
||||
{
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
@ -362,7 +364,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
qDebug() << "This is a first run...";
|
||||
// clear the scripts, and set out script to our default scripts
|
||||
clearScriptsBeforeRunning();
|
||||
loadScript("http://public.highfidelity.io/scripts/defaultScripts.js");
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
|
||||
QMutexLocker locker(&_settingsMutex);
|
||||
_settings->setValue("firstRun",QVariant(false));
|
||||
|
@ -552,6 +554,8 @@ void Application::initializeGL() {
|
|||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
PerformanceTimer perfTimer("paintGL");
|
||||
|
||||
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
|
@ -616,7 +620,7 @@ void Application::paintGL() {
|
|||
whichCamera = _viewFrustumOffsetCamera;
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
|
||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||
updateShadowMap();
|
||||
}
|
||||
|
||||
|
@ -646,7 +650,10 @@ void Application::paintGL() {
|
|||
_rearMirrorTools->render(true);
|
||||
}
|
||||
|
||||
_applicationOverlay.renderOverlay();
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/renderOverlay");
|
||||
_applicationOverlay.renderOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
_frameCount++;
|
||||
|
@ -1286,11 +1293,13 @@ void Application::timer() {
|
|||
}
|
||||
|
||||
void Application::idle() {
|
||||
PerformanceTimer perfTimer("idle");
|
||||
|
||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
|
||||
// details normally.
|
||||
bool showWarnings = getLogger()->extraDebugging();
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()");
|
||||
PerformanceWarning warn(showWarnings, "idle()");
|
||||
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
|
||||
|
||||
|
@ -1298,15 +1307,18 @@ void Application::idle() {
|
|||
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
|
||||
_lastTimeUpdated.start();
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update");
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS));
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/updateGL");
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()");
|
||||
_glWidget->updateGL();
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/rest");
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... rest of it");
|
||||
_idleLoopStdev.addValue(timeSinceLastUpdate);
|
||||
|
||||
|
@ -1318,14 +1330,16 @@ void Application::idle() {
|
|||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
|
||||
PerformanceTimer perfTimer("idle/rest/_buckyBalls");
|
||||
_buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData());
|
||||
}
|
||||
|
||||
// After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
|
||||
idleTimer->start(2);
|
||||
}
|
||||
if (_numChangedSettings > 0) {
|
||||
saveSettings();
|
||||
|
||||
if (_numChangedSettings > 0) {
|
||||
saveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1727,6 +1741,7 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
|
|||
}
|
||||
|
||||
void Application::updateLOD() {
|
||||
PerformanceTimer perfTimer("idle/update/updateLOD");
|
||||
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) {
|
||||
Menu::getInstance()->autoAdjustLOD(_fps);
|
||||
|
@ -1736,6 +1751,7 @@ void Application::updateLOD() {
|
|||
}
|
||||
|
||||
void Application::updateMouseRay() {
|
||||
PerformanceTimer perfTimer("idle/update/updateMouseRay");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMouseRay()");
|
||||
|
@ -1768,6 +1784,7 @@ void Application::updateMouseRay() {
|
|||
}
|
||||
|
||||
void Application::updateFaceshift() {
|
||||
PerformanceTimer perfTimer("idle/update/updateFaceshift");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateFaceshift()");
|
||||
|
@ -1782,6 +1799,7 @@ void Application::updateFaceshift() {
|
|||
}
|
||||
|
||||
void Application::updateVisage() {
|
||||
PerformanceTimer perfTimer("idle/update/updateVisage");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateVisage()");
|
||||
|
@ -1791,6 +1809,7 @@ void Application::updateVisage() {
|
|||
}
|
||||
|
||||
void Application::updateMyAvatarLookAtPosition() {
|
||||
PerformanceTimer perfTimer("idle/update/updateMyAvatarLookAtPosition");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
|
||||
|
@ -1856,6 +1875,7 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
}
|
||||
|
||||
void Application::updateThreads(float deltaTime) {
|
||||
PerformanceTimer perfTimer("idle/update/updateThreads");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateThreads()");
|
||||
|
||||
|
@ -1870,6 +1890,7 @@ void Application::updateThreads(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::updateMetavoxels(float deltaTime) {
|
||||
PerformanceTimer perfTimer("idle/update/updateMetavoxels");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
|
||||
|
||||
|
@ -1899,6 +1920,7 @@ void Application::cameraMenuChanged() {
|
|||
}
|
||||
|
||||
void Application::updateCamera(float deltaTime) {
|
||||
PerformanceTimer perfTimer("idle/update/updateCamera");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateCamera()");
|
||||
|
||||
|
@ -1916,6 +1938,7 @@ void Application::updateCamera(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::updateDialogs(float deltaTime) {
|
||||
PerformanceTimer perfTimer("idle/update/updateDialogs");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateDialogs()");
|
||||
|
||||
|
@ -1932,6 +1955,7 @@ void Application::updateDialogs(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::updateCursor(float deltaTime) {
|
||||
PerformanceTimer perfTimer("idle/update/updateCursor");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateCursor()");
|
||||
|
||||
|
@ -1956,51 +1980,87 @@ void Application::updateCursor(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::update(float deltaTime) {
|
||||
//PerformanceTimer perfTimer("idle/update"); // NOTE: we track this above in Application::idle()
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
updateLOD();
|
||||
|
||||
// check what's under the mouse and update the mouse voxel
|
||||
updateMouseRay();
|
||||
|
||||
updateMouseRay(); // check what's under the mouse and update the mouse voxel
|
||||
updateFaceshift();
|
||||
updateVisage();
|
||||
_myAvatar->updateLookAtTargetAvatar();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/updateLookAtTargetAvatar");
|
||||
_myAvatar->updateLookAtTargetAvatar();
|
||||
}
|
||||
updateMyAvatarLookAtPosition();
|
||||
_sixenseManager.update(deltaTime);
|
||||
_joystickManager.update();
|
||||
_prioVR.update(deltaTime);
|
||||
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/sixense,joystick,prioVR");
|
||||
_sixenseManager.update(deltaTime);
|
||||
_joystickManager.update();
|
||||
_prioVR.update(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/updateMyAvatar");
|
||||
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
}
|
||||
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/_avatarManager");
|
||||
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
}
|
||||
updateMetavoxels(deltaTime); // update metavoxels
|
||||
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
updateCursor(deltaTime); // Handle cursor updates
|
||||
|
||||
_particles.update(); // update the particles...
|
||||
_particleCollisionSystem.update(); // collide the particles...
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/_particles");
|
||||
_particles.update(); // update the particles...
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/_particleCollisionSystem");
|
||||
_particleCollisionSystem.update(); // collide the particles...
|
||||
}
|
||||
|
||||
_models.update(); // update the models...
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/_models");
|
||||
_models.update(); // update the models...
|
||||
}
|
||||
|
||||
_overlays.update(deltaTime);
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/_overlays");
|
||||
_overlays.update(deltaTime);
|
||||
}
|
||||
|
||||
// let external parties know we're updating
|
||||
emit simulating(deltaTime);
|
||||
{
|
||||
PerformanceTimer perfTimer("idle/update/emit simulating");
|
||||
// let external parties know we're updating
|
||||
emit simulating(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateMyAvatar(float deltaTime) {
|
||||
PerformanceTimer perfTimer("updateMyAvatar");
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMyAvatar()");
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
{
|
||||
PerformanceTimer perfTimer("updateMyAvatar/_myAvatar->update()");
|
||||
_myAvatar->update(deltaTime);
|
||||
}
|
||||
|
||||
// send head/hand data to the avatar mixer and voxel server
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
packet.append(_myAvatar->toByteArray());
|
||||
|
||||
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
|
||||
{
|
||||
// send head/hand data to the avatar mixer and voxel server
|
||||
PerformanceTimer perfTimer("updateMyAvatar/sendToAvatarMixer");
|
||||
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
|
||||
packet.append(_myAvatar->toByteArray());
|
||||
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
|
||||
// Update _viewFrustum with latest camera and view frustum data...
|
||||
// NOTE: we get this from the view frustum, to make it simpler, since the
|
||||
|
@ -2008,22 +2068,28 @@ void Application::updateMyAvatar(float deltaTime) {
|
|||
// We could optimize this to not actually load the viewFrustum, since we don't
|
||||
// actually need to calculate the view frustum planes to send these details
|
||||
// to the server.
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
{
|
||||
PerformanceTimer perfTimer("updateMyAvatar/loadViewFrustum");
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
}
|
||||
|
||||
// Update my voxel servers with my current voxel query...
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
|
||||
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
|
||||
{
|
||||
PerformanceTimer perfTimer("updateMyAvatar/queryOctree");
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
|
||||
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
|
||||
|
||||
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
|
||||
if (queryIsDue || viewIsDifferentEnough) {
|
||||
_lastQueriedTime = now;
|
||||
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
|
||||
queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
|
||||
queryOctree(NodeType::ModelServer, PacketTypeModelQuery, _modelServerJurisdictions);
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
|
||||
if (queryIsDue || viewIsDifferentEnough) {
|
||||
_lastQueriedTime = now;
|
||||
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
|
||||
queryOctree(NodeType::ParticleServer, PacketTypeParticleQuery, _particleServerJurisdictions);
|
||||
queryOctree(NodeType::ModelServer, PacketTypeModelQuery, _modelServerJurisdictions);
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2251,99 +2317,122 @@ glm::vec3 Application::getSunDirection() {
|
|||
}
|
||||
|
||||
void Application::updateShadowMap() {
|
||||
PerformanceTimer perfTimer("paintGL/updateShadowMap");
|
||||
QOpenGLFramebufferObject* fbo = _textureCache.getShadowFramebufferObject();
|
||||
fbo->bind();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glViewport(0, 0, fbo->width(), fbo->height());
|
||||
|
||||
glm::vec3 lightDirection = -getSunDirection();
|
||||
glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection);
|
||||
glm::quat inverseRotation = glm::inverse(rotation);
|
||||
float nearScale = 0.0f;
|
||||
const float MAX_SHADOW_DISTANCE = 2.0f;
|
||||
float farScale = (MAX_SHADOW_DISTANCE - _viewFrustum.getNearClip()) /
|
||||
(_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
|
||||
|
||||
const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f };
|
||||
const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f),
|
||||
glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) };
|
||||
|
||||
float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
glm::vec3 points[] = {
|
||||
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale),
|
||||
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale),
|
||||
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale),
|
||||
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) };
|
||||
glm::vec3 center;
|
||||
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
|
||||
center += points[i];
|
||||
}
|
||||
center /= (float)(sizeof(points) / sizeof(points[0]));
|
||||
float radius = 0.0f;
|
||||
for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) {
|
||||
radius = qMax(radius, glm::distance(points[i], center));
|
||||
}
|
||||
center = inverseRotation * center;
|
||||
|
||||
// to reduce texture "shimmer," move in texel increments
|
||||
float texelSize = (2.0f * radius) / fbo->width();
|
||||
center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize,
|
||||
roundf(center.z / texelSize) * texelSize);
|
||||
int matrixCount = 1;
|
||||
int targetSize = fbo->width();
|
||||
float targetScale = 1.0f;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
matrixCount = CASCADED_SHADOW_MATRIX_COUNT;
|
||||
targetSize = fbo->width() / 2;
|
||||
targetScale = 0.5f;
|
||||
}
|
||||
for (int i = 0; i < matrixCount; i++) {
|
||||
const glm::vec2& coord = MAP_COORDS[i];
|
||||
glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize);
|
||||
|
||||
float nearScale = SHADOW_MATRIX_DISTANCES[i] * frustumScale;
|
||||
float farScale = SHADOW_MATRIX_DISTANCES[i + 1] * frustumScale;
|
||||
glm::vec3 points[] = {
|
||||
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale),
|
||||
glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale),
|
||||
glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale),
|
||||
glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale),
|
||||
glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) };
|
||||
glm::vec3 center;
|
||||
for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) {
|
||||
center += points[j];
|
||||
}
|
||||
center /= (float)(sizeof(points) / sizeof(points[0]));
|
||||
float radius = 0.0f;
|
||||
for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) {
|
||||
radius = qMax(radius, glm::distance(points[j], center));
|
||||
}
|
||||
if (i < 3) {
|
||||
const float RADIUS_SCALE = 0.5f;
|
||||
_shadowDistances[i] = -glm::distance(_viewFrustum.getPosition(), center) - radius * RADIUS_SCALE;
|
||||
}
|
||||
center = inverseRotation * center;
|
||||
|
||||
// to reduce texture "shimmer," move in texel increments
|
||||
float texelSize = (2.0f * radius) / targetSize;
|
||||
center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize,
|
||||
roundf(center.z / texelSize) * texelSize);
|
||||
|
||||
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
|
||||
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
|
||||
|
||||
// stretch out our extents in z so that we get all of the avatars
|
||||
minima.z -= _viewFrustum.getFarClip() * 0.5f;
|
||||
maxima.z += _viewFrustum.getFarClip() * 0.5f;
|
||||
|
||||
// save the combined matrix for rendering
|
||||
_shadowMatrices[i] = glm::transpose(glm::translate(glm::vec3(coord, 0.0f)) *
|
||||
glm::scale(glm::vec3(targetScale, targetScale, 1.0f)) *
|
||||
glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) *
|
||||
glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation));
|
||||
|
||||
// update the shadow view frustum
|
||||
_shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f));
|
||||
_shadowViewFrustum.setOrientation(rotation);
|
||||
_shadowViewFrustum.setOrthographic(true);
|
||||
_shadowViewFrustum.setWidth(maxima.x - minima.x);
|
||||
_shadowViewFrustum.setHeight(maxima.y - minima.y);
|
||||
_shadowViewFrustum.setNearClip(minima.z);
|
||||
_shadowViewFrustum.setFarClip(maxima.z);
|
||||
_shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
|
||||
_shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
|
||||
_shadowViewFrustum.calculate();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glm::vec3 axis = glm::axis(inverseRotation);
|
||||
glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// store view matrix without translation, which we'll use for precision-sensitive objects
|
||||
updateUntranslatedViewMatrix();
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
|
||||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
|
||||
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
|
||||
|
||||
// stretch out our extents in z so that we get all of the avatars
|
||||
minima.z -= _viewFrustum.getFarClip() * 0.5f;
|
||||
maxima.z += _viewFrustum.getFarClip() * 0.5f;
|
||||
|
||||
// save the combined matrix for rendering
|
||||
_shadowMatrix = glm::transpose(glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) *
|
||||
glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation));
|
||||
|
||||
// update the shadow view frustum
|
||||
_shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f));
|
||||
_shadowViewFrustum.setOrientation(rotation);
|
||||
_shadowViewFrustum.setOrthographic(true);
|
||||
_shadowViewFrustum.setWidth(maxima.x - minima.x);
|
||||
_shadowViewFrustum.setHeight(maxima.y - minima.y);
|
||||
_shadowViewFrustum.setNearClip(minima.z);
|
||||
_shadowViewFrustum.setFarClip(maxima.z);
|
||||
_shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
|
||||
_shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
|
||||
_shadowViewFrustum.calculate();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glm::vec3 axis = glm::axis(inverseRotation);
|
||||
glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// store view matrix without translation, which we'll use for precision-sensitive objects
|
||||
updateUntranslatedViewMatrix();
|
||||
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt
|
||||
|
||||
_avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE);
|
||||
_particles.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
_models.render(OctreeRenderer::SHADOW_RENDER_MODE);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
fbo->release();
|
||||
|
||||
glViewport(0, 0, _glWidget->width(), _glWidget->height());
|
||||
|
@ -2390,6 +2479,7 @@ QImage Application::renderAvatarBillboard() {
|
|||
}
|
||||
|
||||
void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
||||
// transform by eye offset
|
||||
|
||||
|
@ -2422,9 +2512,27 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z);
|
||||
|
||||
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
|
||||
setupWorldLight();
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/setupWorldLight");
|
||||
setupWorldLight();
|
||||
}
|
||||
|
||||
// setup shadow matrices (again, after the camera transform)
|
||||
int shadowMatrixCount = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
|
||||
shadowMatrixCount = 1;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
shadowMatrixCount = CASCADED_SHADOW_MATRIX_COUNT;
|
||||
}
|
||||
for (int i = shadowMatrixCount - 1; i >= 0; i--) {
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][0]);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][1]);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][2]);
|
||||
}
|
||||
|
||||
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/stars");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... stars...");
|
||||
if (!_stars.isStarsLoaded()) {
|
||||
|
@ -2453,6 +2561,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// draw the sky dome
|
||||
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/atmosphere");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... atmosphere...");
|
||||
_environment.renderAtmospheres(whichCamera);
|
||||
|
@ -2472,10 +2581,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
|
||||
|
||||
// draw the audio reflector overlay
|
||||
_audioReflector.render();
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/audioReflector");
|
||||
_audioReflector.render();
|
||||
}
|
||||
|
||||
// Draw voxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/voxels");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... voxels...");
|
||||
_voxels.render();
|
||||
|
@ -2483,12 +2596,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// also, metavoxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/metavoxels");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... metavoxels...");
|
||||
_metavoxels.render();
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/buckyBalls");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... bucky balls...");
|
||||
_buckyBalls.render();
|
||||
|
@ -2496,6 +2611,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// render particles...
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Particles)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/particles");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... particles...");
|
||||
_particles.render();
|
||||
|
@ -2503,6 +2619,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// render models...
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/models");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... models...");
|
||||
_models.render();
|
||||
|
@ -2510,6 +2627,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// render the ambient occlusion effect if enabled
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/AmbientOcclusion");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... AmbientOcclusion...");
|
||||
_ambientOcclusionEffect.render();
|
||||
|
@ -2523,16 +2641,21 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
|
||||
bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR);
|
||||
_avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly);
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/renderAvatars");
|
||||
_avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly);
|
||||
}
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
// Render the world box
|
||||
if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/renderWorldBox");
|
||||
renderWorldBox();
|
||||
}
|
||||
|
||||
// brad's frustum for debugging
|
||||
// view frustum for debugging
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/ViewFrustum");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... renderViewFrustum...");
|
||||
renderViewFrustum(_viewFrustum);
|
||||
|
@ -2540,6 +2663,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
|
||||
// render voxel fades if they exist
|
||||
if (_voxelFades.size() > 0) {
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/voxel fades");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... voxel fades...");
|
||||
_voxelFadesLock.lockForWrite();
|
||||
|
@ -2555,10 +2679,16 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
|
||||
// give external parties a change to hook in
|
||||
emit renderingInWorldInterface();
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/inWorldInterface");
|
||||
emit renderingInWorldInterface();
|
||||
}
|
||||
|
||||
// render JS/scriptable overlays
|
||||
_overlays.render3D();
|
||||
{
|
||||
PerformanceTimer perfTimer("paintGL/displaySide/3dOverlays");
|
||||
_overlays.render3D();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3254,16 +3384,21 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
|
|||
return _scriptEnginesHash[scriptName];
|
||||
}
|
||||
|
||||
// start the script on a new thread...
|
||||
QUrl scriptUrl(scriptName);
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
||||
_scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine);
|
||||
ScriptEngine* scriptEngine;
|
||||
if (scriptName.isNull()) {
|
||||
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
||||
} else {
|
||||
// start the script on a new thread...
|
||||
QUrl scriptUrl(scriptName);
|
||||
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
||||
_scriptEnginesHash.insert(scriptName, scriptEngine);
|
||||
|
||||
if (!scriptEngine->hasScript()) {
|
||||
qDebug() << "Application::loadScript(), script failed to load...";
|
||||
return NULL;
|
||||
if (!scriptEngine->hasScript()) {
|
||||
qDebug() << "Application::loadScript(), script failed to load...";
|
||||
return NULL;
|
||||
}
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
}
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
|
@ -3362,6 +3497,12 @@ void Application::reloadAllScripts() {
|
|||
stopAllScripts(true);
|
||||
}
|
||||
|
||||
void Application::loadDefaultScripts() {
|
||||
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::manageRunningScriptsWidgetVisibility(bool shown) {
|
||||
if (_runningScriptsWidgetWasVisible && shown) {
|
||||
_runningScriptsWidget->show();
|
||||
|
|
|
@ -261,11 +261,11 @@ public:
|
|||
/// result from matrix multiplication at high translation magnitudes.
|
||||
void loadTranslatedViewMatrix(const glm::vec3& translation);
|
||||
|
||||
const glm::mat4& getShadowMatrix() const { return _shadowMatrix; }
|
||||
|
||||
void getModelViewMatrix(glm::dmat4* modelViewMatrix);
|
||||
void getProjectionMatrix(glm::dmat4* projectionMatrix);
|
||||
|
||||
const glm::vec3& getShadowDistances() const { return _shadowDistances; }
|
||||
|
||||
/// Computes the off-axis frustum parameters for the view frustum, taking mirroring into account.
|
||||
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
|
||||
|
@ -323,11 +323,12 @@ public slots:
|
|||
void loadScriptURLDialog();
|
||||
void toggleLogDialog();
|
||||
void initAvatarAndViewFrustum();
|
||||
ScriptEngine* loadScript(const QString& fileNameString, bool loadScriptFromEditor = false);
|
||||
ScriptEngine* loadScript(const QString& fileNameString = QString(), bool loadScriptFromEditor = false);
|
||||
void scriptFinished(const QString& scriptName);
|
||||
void stopAllScripts(bool restart = false);
|
||||
void stopScript(const QString& scriptName);
|
||||
void reloadAllScripts();
|
||||
void loadDefaultScripts();
|
||||
void toggleRunningScriptsWidget();
|
||||
|
||||
void uploadHead();
|
||||
|
@ -491,7 +492,9 @@ private:
|
|||
float _rotateMirror;
|
||||
float _raiseMirror;
|
||||
|
||||
glm::mat4 _shadowMatrix;
|
||||
static const int CASCADED_SHADOW_MATRIX_COUNT = 4;
|
||||
glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT];
|
||||
glm::vec3 _shadowDistances;
|
||||
|
||||
Environment _environment;
|
||||
|
||||
|
|
|
@ -160,6 +160,11 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
|||
int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& packet) {
|
||||
// push past the packet header
|
||||
int bytesRead = numBytesForPacketHeader(packet);
|
||||
|
||||
// push past flags, sequence, timestamp
|
||||
bytesRead += sizeof(OCTREE_PACKET_FLAGS);
|
||||
bytesRead += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
bytesRead += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
// get the lock for the duration of the call
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
|
|
@ -73,6 +73,11 @@ const int ONE_SECOND_OF_FRAMES = 60;
|
|||
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
|
||||
const float MUTE_RADIUS = 50;
|
||||
|
||||
const QString CONSOLE_TITLE = "Scripting Console";
|
||||
const float CONSOLE_WINDOW_OPACITY = 0.95f;
|
||||
const int CONSOLE_WIDTH = 800;
|
||||
const int CONSOLE_HEIGHT = 200;
|
||||
|
||||
Menu::Menu() :
|
||||
_actionHash(),
|
||||
_audioJitterBufferSamples(0),
|
||||
|
@ -81,6 +86,7 @@ Menu::Menu() :
|
|||
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
|
||||
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
|
||||
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
|
||||
_jsConsole(NULL),
|
||||
_octreeStatsDialog(NULL),
|
||||
_lodToolsDialog(NULL),
|
||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||
|
@ -227,6 +233,12 @@ Menu::Menu() :
|
|||
_chatWindow = new ChatWindow(Application::getInstance()->getWindow());
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
this,
|
||||
SLOT(toggleConsole()));
|
||||
|
||||
QMenu* viewMenu = addMenu("View");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
|
@ -303,7 +315,12 @@ Menu::Menu() :
|
|||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
|
||||
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true);
|
||||
|
@ -374,8 +391,18 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
|
||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||
|
||||
|
||||
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandDisplaySideTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarSimulateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMiscAvatarTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandIdleTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
|
||||
|
@ -666,6 +693,10 @@ void Menu::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) {
|
|||
set->endGroup();
|
||||
}
|
||||
|
||||
bool Menu::getShadowsEnabled() const {
|
||||
return isOptionChecked(MenuOption::SimpleShadows) || isOptionChecked(MenuOption::CascadedShadows);
|
||||
}
|
||||
|
||||
void Menu::handleViewFrustumOffsetKeyModifier(int key) {
|
||||
const float VIEW_FRUSTUM_OFFSET_DELTA = 0.5f;
|
||||
const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f;
|
||||
|
@ -827,6 +858,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
|
||||
void Menu::removeAction(QMenu* menu, const QString& actionName) {
|
||||
menu->removeAction(_actionHash.value(actionName));
|
||||
_actionHash.remove(actionName);
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
|
@ -836,8 +868,8 @@ void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Menu::isOptionChecked(const QString& menuOption) {
|
||||
QAction* menu = _actionHash.value(menuOption);
|
||||
bool Menu::isOptionChecked(const QString& menuOption) const {
|
||||
const QAction* menu = _actionHash.value(menuOption);
|
||||
if (menu) {
|
||||
return menu->isChecked();
|
||||
}
|
||||
|
@ -1258,6 +1290,25 @@ void Menu::toggleChat() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void Menu::toggleConsole() {
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
if (!_jsConsole) {
|
||||
QDialog* dialog = new QDialog(mainWindow, Qt::WindowStaysOnTopHint);
|
||||
QVBoxLayout* layout = new QVBoxLayout(dialog);
|
||||
dialog->setLayout(new QVBoxLayout(dialog));
|
||||
|
||||
dialog->resize(QSize(CONSOLE_WIDTH, CONSOLE_HEIGHT));
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(new JSConsole(dialog));
|
||||
dialog->setWindowOpacity(CONSOLE_WINDOW_OPACITY);
|
||||
dialog->setWindowTitle(CONSOLE_TITLE);
|
||||
|
||||
_jsConsole = dialog;
|
||||
}
|
||||
_jsConsole->setVisible(!_jsConsole->isVisible());
|
||||
}
|
||||
|
||||
void Menu::audioMuteToggled() {
|
||||
QAction *muteAction = _actionHash.value(MenuOption::MuteAudio);
|
||||
muteAction->setChecked(Application::getInstance()->getAudio()->getMuted());
|
||||
|
@ -1600,6 +1651,16 @@ void Menu::removeMenu(const QString& menuName) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMenu* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
|
@ -1675,8 +1736,17 @@ void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
|||
QMenu* menuObj = getMenu(menu);
|
||||
if (menuObj) {
|
||||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
QMenuBar::repaint();
|
||||
};
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QMenu* menuObj = getMenu(menu);
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
if (menuObj && menuItemAction) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
QString Menu::getSnapshotsLocation() const {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "location/LocationManager.h"
|
||||
#include "ui/PreferencesDialog.h"
|
||||
#include "ui/ChatWindow.h"
|
||||
#include "ui/JSConsole.h"
|
||||
#include "ui/ScriptEditorWindow.h"
|
||||
|
||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||
|
@ -102,6 +103,8 @@ public:
|
|||
int getMaxVoxels() const { return _maxVoxels; }
|
||||
QAction* getUseVoxelShader() const { return _useVoxelShader; }
|
||||
|
||||
bool getShadowsEnabled() const;
|
||||
|
||||
void handleViewFrustumOffsetKeyModifier(int key);
|
||||
|
||||
// User Tweakable LOD Items
|
||||
|
@ -166,11 +169,13 @@ public slots:
|
|||
|
||||
QMenu* addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
private slots:
|
||||
|
@ -189,6 +194,7 @@ private slots:
|
|||
void showMetavoxelEditor();
|
||||
void showScriptEditor();
|
||||
void showChat();
|
||||
void toggleConsole();
|
||||
void toggleChat();
|
||||
void audioMuteToggled();
|
||||
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
|
||||
|
@ -243,6 +249,7 @@ private:
|
|||
QPointer<MetavoxelEditor> _MetavoxelEditor;
|
||||
QPointer<ScriptEditorWindow> _ScriptEditor;
|
||||
QPointer<ChatWindow> _chatWindow;
|
||||
QDialog* _jsConsole;
|
||||
OctreeStatsDialog* _octreeStatsDialog;
|
||||
LodToolsDialog* _lodToolsDialog;
|
||||
int _maxVoxels;
|
||||
|
@ -301,6 +308,7 @@ namespace MenuOption {
|
|||
const QString Bandwidth = "Bandwidth Display";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BuckyBalls = "Bucky Balls";
|
||||
const QString CascadedShadows = "Cascaded";
|
||||
const QString Chat = "Chat...";
|
||||
const QString ChatCircling = "Chat Circling";
|
||||
const QString CollideWithAvatars = "Collide With Avatars";
|
||||
|
@ -308,6 +316,7 @@ namespace MenuOption {
|
|||
const QString CollideWithParticles = "Collide With Particles";
|
||||
const QString CollideWithVoxels = "Collide With Voxels";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString Console = "Console...";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
|
@ -317,10 +326,18 @@ namespace MenuOption {
|
|||
const QString DisplayModelBounds = "Display Model Bounds";
|
||||
const QString DisplayModelElementProxy = "Display Model Element Bounds";
|
||||
const QString DisplayModelElementChildProxies = "Display Model Element Children";
|
||||
const QString DisplayTimingDetails = "Display Timing Details";
|
||||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing";
|
||||
const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing";
|
||||
const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing";
|
||||
const QString ExpandDisplaySideTiming = "Expand Display Side Timing";
|
||||
const QString ExpandIdleTiming = "Expand Idle Timing";
|
||||
const QString ExpandPaintGLTiming = "Expand PaintGL Timing";
|
||||
const QString ExpandUpdateTiming = "Expand Update Timing";
|
||||
const QString Faceplus = "Faceplus";
|
||||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
|
@ -376,7 +393,7 @@ namespace MenuOption {
|
|||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString SettingsExport = "Export Settings";
|
||||
const QString SettingsImport = "Import Settings";
|
||||
const QString Shadows = "Shadows";
|
||||
const QString SimpleShadows = "Simple";
|
||||
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
||||
const QString ShowBordersModelNodes = "Show Model Nodes";
|
||||
const QString ShowBordersParticleNodes = "Show Particle Nodes";
|
||||
|
|
|
@ -378,10 +378,10 @@ void Avatar::simulateAttachments(float deltaTime) {
|
|||
model->setLODDistance(getLODDistance());
|
||||
}
|
||||
if (_skeletonModel.getJointPosition(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
_skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScale(_skeletonModel.getScale() * attachment.scale);
|
||||
model->setScaleToFit(true, _scale * attachment.scale);
|
||||
model->simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -705,6 +705,54 @@ QStringList Avatar::getJointNames() const {
|
|||
return _skeletonModel.isActive() ? _skeletonModel.getGeometry()->getFBXGeometry().getJointNames() : QStringList();
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(int index) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 position;
|
||||
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index));
|
||||
return position;
|
||||
}
|
||||
glm::vec3 position;
|
||||
_skeletonModel.getJointPosition(index, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::vec3 position;
|
||||
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name));
|
||||
return position;
|
||||
}
|
||||
glm::vec3 position;
|
||||
_skeletonModel.getJointPosition(getJointIndex(name), position);
|
||||
return position;
|
||||
}
|
||||
|
||||
glm::quat Avatar::getJointCombinedRotation(int index) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::quat rotation;
|
||||
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const int, index));
|
||||
return rotation;
|
||||
}
|
||||
glm::quat rotation;
|
||||
_skeletonModel.getJointCombinedRotation(index, rotation);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
glm::quat Avatar::getJointCombinedRotation(const QString& name) const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
glm::quat rotation;
|
||||
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const QString&, name));
|
||||
return rotation;
|
||||
}
|
||||
glm::quat rotation;
|
||||
_skeletonModel.getJointCombinedRotation(getJointIndex(name), rotation);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
AvatarData::setFaceModelURL(faceModelURL);
|
||||
const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile(Application::resourcesPath() + "meshes/defaultAvatar_head.fst");
|
||||
|
@ -734,6 +782,8 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
|
||||
// update the urls
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||
_attachmentModels[i]->setScaleToFit(true, _scale * _attachmentData.at(i).scale);
|
||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,12 @@ public:
|
|||
|
||||
quint32 getCollisionGroups() const { return _collisionGroups; }
|
||||
virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }
|
||||
|
||||
|
||||
Q_INVOKABLE glm::vec3 getJointPosition(int index) const;
|
||||
Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const;
|
||||
Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const;
|
||||
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
|
||||
|
||||
public slots:
|
||||
void updateCollisionGroups();
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
#include <GeometryUtil.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <ShapeCollider.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Audio.h"
|
||||
|
@ -76,8 +76,7 @@ MyAvatar::MyAvatar() :
|
|||
_lastFloorContactPoint(0.0f),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
_billboardValid(false),
|
||||
_oculusYawOffset(0.0f)
|
||||
_billboardValid(false)
|
||||
{
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
|
@ -90,8 +89,7 @@ MyAvatar::~MyAvatar() {
|
|||
|
||||
void MyAvatar::reset() {
|
||||
_skeletonModel.reset();
|
||||
getHead()->reset();
|
||||
_oculusYawOffset = 0.0f;
|
||||
getHead()->reset();
|
||||
|
||||
setVelocity(glm::vec3(0.0f));
|
||||
setThrust(glm::vec3(0.0f));
|
||||
|
@ -103,10 +101,15 @@ void MyAvatar::reset() {
|
|||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
PerformanceTimer perfTimer("MyAvatar::update/");
|
||||
Head* head = getHead();
|
||||
head->relaxLean(deltaTime);
|
||||
updateFromTrackers(deltaTime);
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::update/updateFromTrackers");
|
||||
updateFromTrackers(deltaTime);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) {
|
||||
PerformanceTimer perfTimer("MyAvatar::update/moveWithLean");
|
||||
// Faceshift drive is enabled, set the avatar drive based on the head position
|
||||
moveWithLean();
|
||||
}
|
||||
|
@ -117,13 +120,18 @@ void MyAvatar::update(float deltaTime) {
|
|||
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
|
||||
PerformanceTimer perfTimer("MyAvatar::update/gravityWork");
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
}
|
||||
|
||||
simulate(deltaTime);
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::update/simulate");
|
||||
simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate");
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
|
@ -134,34 +142,56 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// no extra movement of the hand here any more ...
|
||||
_handState = HAND_STATE_NULL;
|
||||
|
||||
updateOrientation(deltaTime);
|
||||
updatePosition(deltaTime);
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
getHand()->collideAgainstOurself();
|
||||
getHand()->simulate(deltaTime, true);
|
||||
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
simulateAttachments(deltaTime);
|
||||
|
||||
// copy out the skeleton joints from the model
|
||||
_jointData.resize(_skeletonModel.getJointStateCount());
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = _skeletonModel.getJointState(i, data.rotation);
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/updateOrientation");
|
||||
updateOrientation(deltaTime);
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/updatePosition");
|
||||
updatePosition(deltaTime);
|
||||
}
|
||||
|
||||
Head* head = getHead();
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/hand Collision,simulate");
|
||||
// update avatar skeleton and simulate hand and head
|
||||
getHand()->collideAgainstOurself();
|
||||
getHand()->simulate(deltaTime, true);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/_skeletonModel.simulate()");
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/simulateAttachments");
|
||||
simulateAttachments(deltaTime);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/copy joints");
|
||||
// copy out the skeleton joints from the model
|
||||
_jointData.resize(_skeletonModel.getJointStateCount());
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = _skeletonModel.getJointState(i, data.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate");
|
||||
Head* head = getHead();
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
}
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
head->simulate(deltaTime, true);
|
||||
}
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
head->simulate(deltaTime, true);
|
||||
|
||||
// now that we're done stepping the avatar forward in time, compute new collisions
|
||||
if (_collisionGroups != 0) {
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/_collisionGroups");
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
|
@ -171,14 +201,17 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
}
|
||||
updateShapePositions();
|
||||
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithEnvironment");
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithVoxels");
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
} else {
|
||||
_trapDuration = 0.0f;
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithAvatars");
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -791,6 +824,7 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend
|
|||
}
|
||||
|
||||
float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) {
|
||||
PerformanceTimer perfTimer("MyAvatar::computeDistanceToFloor()");
|
||||
glm::vec3 direction = -_worldUpDirection;
|
||||
OctreeElement* elementHit; // output from findRayIntersection
|
||||
float distance = FLT_MAX; // output from findRayIntersection
|
||||
|
@ -828,43 +862,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
OculusManager::getEulerAngles(yaw, pitch, roll);
|
||||
// ... so they need to be converted to degrees before we do math...
|
||||
|
||||
// The neck is limited in how much it can yaw, so we check its relative
|
||||
// yaw from the body and yaw the body if necessary.
|
||||
yaw *= DEGREES_PER_RADIAN;
|
||||
float bodyToHeadYaw = yaw - _oculusYawOffset;
|
||||
const float MAX_NECK_YAW = 85.0f; // degrees
|
||||
if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) {
|
||||
// We've wrapped around the range for yaw so adjust
|
||||
// the measured yaw to be relative to _oculusYawOffset.
|
||||
if (yaw > 0.0f) {
|
||||
yaw -= 360.0f;
|
||||
} else {
|
||||
yaw += 360.0f;
|
||||
}
|
||||
bodyToHeadYaw = yaw - _oculusYawOffset;
|
||||
}
|
||||
|
||||
float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW;
|
||||
if (delta > 0.0f) {
|
||||
yaw = MAX_NECK_YAW;
|
||||
if (bodyToHeadYaw < 0.0f) {
|
||||
delta *= -1.0f;
|
||||
bodyToHeadYaw = -MAX_NECK_YAW;
|
||||
} else {
|
||||
bodyToHeadYaw = MAX_NECK_YAW;
|
||||
}
|
||||
// constrain _oculusYawOffset to be within range [-180,180]
|
||||
_oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f;
|
||||
|
||||
// We must adjust the body orientation using a delta rotation (rather than
|
||||
// doing yaw math) because the body's yaw ranges are not the same
|
||||
// as what the Oculus API provides.
|
||||
glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), _worldUpDirection);
|
||||
orientation = orientation * bodyCorrection;
|
||||
}
|
||||
Head* head = getHead();
|
||||
head->setBaseYaw(bodyToHeadYaw);
|
||||
|
||||
head->setBaseYaw(yaw * DEGREES_PER_RADIAN);
|
||||
head->setBasePitch(pitch * DEGREES_PER_RADIAN);
|
||||
head->setBaseRoll(roll * DEGREES_PER_RADIAN);
|
||||
}
|
||||
|
@ -876,6 +875,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
|
||||
|
||||
void MyAvatar::updatePosition(float deltaTime) {
|
||||
PerformanceTimer perfTimer("MyAvatar::updatePosition");
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
|
|
|
@ -422,7 +422,25 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco
|
|||
return false;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
return getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) &&
|
||||
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition);
|
||||
if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) &&
|
||||
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) {
|
||||
return true;
|
||||
}
|
||||
// no eye joints; try to estimate based on head/neck joints
|
||||
glm::vec3 neckPosition, headPosition;
|
||||
if (getJointPosition(geometry.neckJointIndex, neckPosition) &&
|
||||
getJointPosition(geometry.headJointIndex, headPosition)) {
|
||||
const float EYE_PROPORTION = 0.6f;
|
||||
glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION);
|
||||
glm::quat headRotation;
|
||||
getJointRotationInWorldFrame(geometry.headJointIndex, headRotation);
|
||||
const float EYES_FORWARD = 0.25f;
|
||||
const float EYE_SEPARATION = 0.1f;
|
||||
float headHeight = glm::distance(neckPosition, headPosition);
|
||||
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
|
||||
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ void OculusManager::connect() {
|
|||
_sensorDevice = *_hmdDevice->GetSensor();
|
||||
_sensorFusion = new SensorFusion;
|
||||
_sensorFusion->AttachToSensor(_sensorDevice);
|
||||
|
||||
_sensorFusion->SetPredictionEnabled(true);
|
||||
|
||||
HMDInfo info;
|
||||
_hmdDevice->GetDeviceInfo(&info);
|
||||
_stereoConfig.SetHMDInfo(info);
|
||||
|
@ -83,7 +84,9 @@ void OculusManager::display(Camera& whichCamera) {
|
|||
#ifdef HAVE_LIBOVR
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
// We only need to render the overlays to a texture once, then we just render the texture as a quad
|
||||
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
|
||||
applicationOverlay.renderOverlay(true);
|
||||
const bool displayOverlays = false;
|
||||
|
||||
Application::getInstance()->getGlowEffect()->prepare();
|
||||
|
||||
|
@ -104,7 +107,9 @@ void OculusManager::display(Camera& whichCamera) {
|
|||
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTextureOculus(whichCamera);
|
||||
if (displayOverlays) {
|
||||
applicationOverlay.displayOverlayTextureOculus(whichCamera);
|
||||
}
|
||||
|
||||
// and the right eye to the right side
|
||||
const StereoEyeParams& rightEyeParams = _stereoConfig.GetEyeRenderParams(StereoEye_Right);
|
||||
|
@ -121,7 +126,9 @@ void OculusManager::display(Camera& whichCamera) {
|
|||
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
|
||||
applicationOverlay.displayOverlayTextureOculus(whichCamera);
|
||||
if (displayOverlays) {
|
||||
applicationOverlay.displayOverlayTextureOculus(whichCamera);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
|
@ -195,7 +202,7 @@ void OculusManager::reset() {
|
|||
|
||||
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
|
||||
#ifdef HAVE_LIBOVR
|
||||
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
|
||||
_sensorFusion->GetPredictedOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "GlowEffect.h"
|
||||
#include "ProgramObject.h"
|
||||
|
@ -119,6 +121,8 @@ static void maybeRelease(QOpenGLFramebufferObject* fbo) {
|
|||
}
|
||||
|
||||
QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
||||
PerformanceTimer perfTimer("paintGL/glowEffect");
|
||||
|
||||
QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject();
|
||||
primaryFBO->release();
|
||||
glBindTexture(GL_TEXTURE_2D, primaryFBO->texture());
|
||||
|
|
|
@ -65,6 +65,11 @@ ProgramObject Model::_shadowNormalMapProgram;
|
|||
ProgramObject Model::_shadowSpecularMapProgram;
|
||||
ProgramObject Model::_shadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_cascadedShadowMapProgram;
|
||||
ProgramObject Model::_cascadedShadowNormalMapProgram;
|
||||
ProgramObject Model::_cascadedShadowSpecularMapProgram;
|
||||
ProgramObject Model::_cascadedShadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_shadowProgram;
|
||||
|
||||
ProgramObject Model::_skinProgram;
|
||||
|
@ -77,13 +82,25 @@ ProgramObject Model::_skinShadowNormalMapProgram;
|
|||
ProgramObject Model::_skinShadowSpecularMapProgram;
|
||||
ProgramObject Model::_skinShadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_skinCascadedShadowMapProgram;
|
||||
ProgramObject Model::_skinCascadedShadowNormalMapProgram;
|
||||
ProgramObject Model::_skinCascadedShadowSpecularMapProgram;
|
||||
ProgramObject Model::_skinCascadedShadowNormalSpecularMapProgram;
|
||||
|
||||
ProgramObject Model::_skinShadowProgram;
|
||||
|
||||
int Model::_normalMapTangentLocation;
|
||||
int Model::_normalSpecularMapTangentLocation;
|
||||
int Model::_shadowNormalMapTangentLocation;
|
||||
int Model::_shadowNormalSpecularMapTangentLocation;
|
||||
int Model::_cascadedShadowNormalMapTangentLocation;
|
||||
int Model::_cascadedShadowNormalSpecularMapTangentLocation;
|
||||
|
||||
int Model::_cascadedShadowMapDistancesLocation;
|
||||
int Model::_cascadedShadowNormalMapDistancesLocation;
|
||||
int Model::_cascadedShadowSpecularMapDistancesLocation;
|
||||
int Model::_cascadedShadowNormalSpecularMapDistancesLocation;
|
||||
|
||||
Model::SkinLocations Model::_skinLocations;
|
||||
Model::SkinLocations Model::_skinNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinSpecularMapLocations;
|
||||
|
@ -92,6 +109,10 @@ Model::SkinLocations Model::_skinShadowMapLocations;
|
|||
Model::SkinLocations Model::_skinShadowNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowNormalSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinCascadedShadowMapLocations;
|
||||
Model::SkinLocations Model::_skinCascadedShadowNormalMapLocations;
|
||||
Model::SkinLocations Model::_skinCascadedShadowSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinCascadedShadowNormalSpecularMapLocations;
|
||||
Model::SkinLocations Model::_skinShadowLocations;
|
||||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
|
@ -128,6 +149,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati
|
|||
locations.clusterIndices = program.attributeLocation("clusterIndices");
|
||||
locations.clusterWeights = program.attributeLocation("clusterWeights");
|
||||
locations.tangent = program.attributeLocation("tangent");
|
||||
locations.shadowDistances = program.uniformLocation("shadowDistances");
|
||||
program.setUniformValue("diffuseMap", 0);
|
||||
program.setUniformValue("normalMap", 1);
|
||||
program.setUniformValue("specularMap", specularTextureUnit);
|
||||
|
@ -238,7 +260,7 @@ void Model::init() {
|
|||
_normalSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_normalSpecularMapProgram.setUniformValue("normalMap", 1);
|
||||
_normalSpecularMapProgram.setUniformValue("specularMap", 2);
|
||||
_normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_normalSpecularMapTangentLocation = _normalSpecularMapProgram.attributeLocation("tangent");
|
||||
_normalSpecularMapProgram.release();
|
||||
|
||||
|
||||
|
@ -288,10 +310,66 @@ void Model::init() {
|
|||
_shadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
|
||||
_shadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
|
||||
_shadowNormalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent");
|
||||
_shadowNormalSpecularMapTangentLocation = _shadowNormalSpecularMapProgram.attributeLocation("tangent");
|
||||
_shadowNormalSpecularMapProgram.release();
|
||||
|
||||
|
||||
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/model.vert");
|
||||
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/model_cascaded_shadow_map.frag");
|
||||
_cascadedShadowMapProgram.link();
|
||||
|
||||
_cascadedShadowMapProgram.bind();
|
||||
_cascadedShadowMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_cascadedShadowMapProgram.setUniformValue("shadowMap", 1);
|
||||
_cascadedShadowMapDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowMapProgram.release();
|
||||
|
||||
_cascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_cascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_map.frag");
|
||||
_cascadedShadowNormalMapProgram.link();
|
||||
|
||||
_cascadedShadowNormalMapProgram.bind();
|
||||
_cascadedShadowNormalMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_cascadedShadowNormalMapProgram.setUniformValue("normalMap", 1);
|
||||
_cascadedShadowNormalMapProgram.setUniformValue("shadowMap", 2);
|
||||
_cascadedShadowNormalMapDistancesLocation = _cascadedShadowNormalMapProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowNormalMapTangentLocation = _cascadedShadowNormalMapProgram.attributeLocation("tangent");
|
||||
_cascadedShadowNormalMapProgram.release();
|
||||
|
||||
_cascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model.vert");
|
||||
_cascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_specular_map.frag");
|
||||
_cascadedShadowSpecularMapProgram.link();
|
||||
|
||||
_cascadedShadowSpecularMapProgram.bind();
|
||||
_cascadedShadowSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_cascadedShadowSpecularMapProgram.setUniformValue("specularMap", 1);
|
||||
_cascadedShadowSpecularMapProgram.setUniformValue("shadowMap", 2);
|
||||
_cascadedShadowSpecularMapDistancesLocation = _cascadedShadowSpecularMapProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowSpecularMapProgram.release();
|
||||
|
||||
_cascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/model_normal_map.vert");
|
||||
_cascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_specular_map.frag");
|
||||
_cascadedShadowNormalSpecularMapProgram.link();
|
||||
|
||||
_cascadedShadowNormalSpecularMapProgram.bind();
|
||||
_cascadedShadowNormalSpecularMapProgram.setUniformValue("diffuseMap", 0);
|
||||
_cascadedShadowNormalSpecularMapProgram.setUniformValue("normalMap", 1);
|
||||
_cascadedShadowNormalSpecularMapProgram.setUniformValue("specularMap", 2);
|
||||
_cascadedShadowNormalSpecularMapProgram.setUniformValue("shadowMap", 3);
|
||||
_cascadedShadowNormalSpecularMapDistancesLocation =
|
||||
_cascadedShadowNormalSpecularMapProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowNormalSpecularMapTangentLocation = _cascadedShadowNormalSpecularMapProgram.attributeLocation("tangent");
|
||||
_cascadedShadowNormalSpecularMapProgram.release();
|
||||
|
||||
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert");
|
||||
_shadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_shadow.frag");
|
||||
|
@ -362,6 +440,39 @@ void Model::init() {
|
|||
initSkinProgram(_skinShadowNormalSpecularMapProgram, _skinShadowNormalSpecularMapLocations, 2, 3);
|
||||
|
||||
|
||||
_skinCascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/skin_model.vert");
|
||||
_skinCascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/model_cascaded_shadow_map.frag");
|
||||
_skinCascadedShadowMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinCascadedShadowMapProgram, _skinCascadedShadowMapLocations);
|
||||
|
||||
_skinCascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinCascadedShadowNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_map.frag");
|
||||
_skinCascadedShadowNormalMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinCascadedShadowNormalMapProgram, _skinCascadedShadowNormalMapLocations, 1, 2);
|
||||
|
||||
_skinCascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model.vert");
|
||||
_skinCascadedShadowSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_specular_map.frag");
|
||||
_skinCascadedShadowSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinCascadedShadowSpecularMapProgram, _skinCascadedShadowSpecularMapLocations, 1, 2);
|
||||
|
||||
_skinCascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_normal_map.vert");
|
||||
_skinCascadedShadowNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/model_cascaded_shadow_normal_specular_map.frag");
|
||||
_skinCascadedShadowNormalSpecularMapProgram.link();
|
||||
|
||||
initSkinProgram(_skinCascadedShadowNormalSpecularMapProgram, _skinCascadedShadowNormalSpecularMapLocations, 2, 3);
|
||||
|
||||
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/skin_model_shadow.vert");
|
||||
_skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
|
@ -517,7 +628,7 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) {
|
|||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, 0.5f * alpha);
|
||||
|
||||
receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows);
|
||||
receiveShadows &= Menu::getInstance()->getShadowsEnabled();
|
||||
renderMeshes(alpha, mode, false, receiveShadows);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
@ -637,7 +748,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
|
|||
}
|
||||
|
||||
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
position = _translation + extractTranslation(_jointStates[jointIndex].getHybridTransform());
|
||||
|
@ -653,6 +764,14 @@ bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation, bo
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex].getRotationInWorldFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList Model::getJointNames() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
|
@ -1470,11 +1589,7 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<NetworkMesh>& networkMeshes = _geometry->getMeshes();
|
||||
|
||||
if (receiveShadows) {
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
|
||||
}
|
||||
bool cascadedShadows = Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows);
|
||||
for (int i = 0; i < networkMeshes.size(); i++) {
|
||||
// exit early if the translucency doesn't match what we're drawing
|
||||
const NetworkMesh& networkMesh = networkMeshes.at(i);
|
||||
|
@ -1496,6 +1611,8 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
ProgramObject* program = &_program;
|
||||
ProgramObject* skinProgram = &_skinProgram;
|
||||
SkinLocations* skinLocations = &_skinLocations;
|
||||
int tangentLocation = _normalMapTangentLocation;
|
||||
int shadowDistancesLocation = _cascadedShadowMapDistancesLocation;
|
||||
GLenum specularTextureUnit = 0;
|
||||
GLenum shadowTextureUnit = 0;
|
||||
if (mode == SHADOW_RENDER_MODE) {
|
||||
|
@ -1506,21 +1623,40 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
} else if (!mesh.tangents.isEmpty()) {
|
||||
if (mesh.hasSpecularTexture()) {
|
||||
if (receiveShadows) {
|
||||
program = &_shadowNormalSpecularMapProgram;
|
||||
skinProgram = &_skinShadowNormalSpecularMapProgram;
|
||||
skinLocations = &_skinShadowNormalSpecularMapLocations;
|
||||
if (cascadedShadows) {
|
||||
program = &_cascadedShadowNormalSpecularMapProgram;
|
||||
skinProgram = &_skinCascadedShadowNormalSpecularMapProgram;
|
||||
skinLocations = &_skinCascadedShadowNormalSpecularMapLocations;
|
||||
tangentLocation = _cascadedShadowNormalSpecularMapTangentLocation;
|
||||
shadowDistancesLocation = _cascadedShadowNormalSpecularMapDistancesLocation;
|
||||
} else {
|
||||
program = &_shadowNormalSpecularMapProgram;
|
||||
skinProgram = &_skinShadowNormalSpecularMapProgram;
|
||||
skinLocations = &_skinShadowNormalSpecularMapLocations;
|
||||
tangentLocation = _shadowNormalSpecularMapTangentLocation;
|
||||
}
|
||||
shadowTextureUnit = GL_TEXTURE3;
|
||||
} else {
|
||||
program = &_normalSpecularMapProgram;
|
||||
skinProgram = &_skinNormalSpecularMapProgram;
|
||||
skinLocations = &_skinNormalSpecularMapLocations;
|
||||
tangentLocation = _normalSpecularMapTangentLocation;
|
||||
}
|
||||
specularTextureUnit = GL_TEXTURE2;
|
||||
|
||||
} else if (receiveShadows) {
|
||||
program = &_shadowNormalMapProgram;
|
||||
skinProgram = &_skinShadowNormalMapProgram;
|
||||
skinLocations = &_skinShadowNormalMapLocations;
|
||||
if (cascadedShadows) {
|
||||
program = &_cascadedShadowNormalMapProgram;
|
||||
skinProgram = &_skinCascadedShadowNormalMapProgram;
|
||||
skinLocations = &_skinCascadedShadowNormalMapLocations;
|
||||
tangentLocation = _cascadedShadowNormalMapTangentLocation;
|
||||
shadowDistancesLocation = _cascadedShadowNormalMapDistancesLocation;
|
||||
} else {
|
||||
program = &_shadowNormalMapProgram;
|
||||
skinProgram = &_skinShadowNormalMapProgram;
|
||||
skinLocations = &_skinShadowNormalMapLocations;
|
||||
tangentLocation = _shadowNormalMapTangentLocation;
|
||||
}
|
||||
shadowTextureUnit = GL_TEXTURE2;
|
||||
} else {
|
||||
program = &_normalMapProgram;
|
||||
|
@ -1529,9 +1665,16 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
}
|
||||
} else if (mesh.hasSpecularTexture()) {
|
||||
if (receiveShadows) {
|
||||
program = &_shadowSpecularMapProgram;
|
||||
skinProgram = &_skinShadowSpecularMapProgram;
|
||||
skinLocations = &_skinShadowSpecularMapLocations;
|
||||
if (cascadedShadows) {
|
||||
program = &_cascadedShadowSpecularMapProgram;
|
||||
skinProgram = &_skinCascadedShadowSpecularMapProgram;
|
||||
skinLocations = &_skinCascadedShadowSpecularMapLocations;
|
||||
shadowDistancesLocation = _cascadedShadowSpecularMapDistancesLocation;
|
||||
} else {
|
||||
program = &_shadowSpecularMapProgram;
|
||||
skinProgram = &_skinShadowSpecularMapProgram;
|
||||
skinLocations = &_skinShadowSpecularMapLocations;
|
||||
}
|
||||
shadowTextureUnit = GL_TEXTURE2;
|
||||
} else {
|
||||
program = &_specularMapProgram;
|
||||
|
@ -1541,15 +1684,20 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
specularTextureUnit = GL_TEXTURE1;
|
||||
|
||||
} else if (receiveShadows) {
|
||||
program = &_shadowMapProgram;
|
||||
skinProgram = &_skinShadowMapProgram;
|
||||
skinLocations = &_skinShadowMapLocations;
|
||||
if (cascadedShadows) {
|
||||
program = &_cascadedShadowMapProgram;
|
||||
skinProgram = &_skinCascadedShadowMapProgram;
|
||||
skinLocations = &_skinCascadedShadowMapLocations;
|
||||
} else {
|
||||
program = &_shadowMapProgram;
|
||||
skinProgram = &_skinShadowMapProgram;
|
||||
skinLocations = &_skinShadowMapLocations;
|
||||
}
|
||||
shadowTextureUnit = GL_TEXTURE1;
|
||||
}
|
||||
|
||||
const MeshState& state = _meshStates.at(i);
|
||||
ProgramObject* activeProgram = program;
|
||||
int tangentLocation = _normalMapTangentLocation;
|
||||
glPushMatrix();
|
||||
Application::getInstance()->loadTranslatedViewMatrix(_translation);
|
||||
|
||||
|
@ -1567,10 +1715,15 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re
|
|||
skinProgram->enableAttributeArray(skinLocations->clusterWeights);
|
||||
activeProgram = skinProgram;
|
||||
tangentLocation = skinLocations->tangent;
|
||||
|
||||
if (cascadedShadows) {
|
||||
program->setUniform(skinLocations->shadowDistances, Application::getInstance()->getShadowDistances());
|
||||
}
|
||||
} else {
|
||||
glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]);
|
||||
program->bind();
|
||||
if (cascadedShadows) {
|
||||
program->setUniform(shadowDistancesLocation, Application::getInstance()->getShadowDistances());
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
|
|
|
@ -174,6 +174,7 @@ public:
|
|||
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
|
||||
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
QStringList getJointNames() const;
|
||||
|
||||
|
@ -319,6 +320,11 @@ private:
|
|||
static ProgramObject _shadowSpecularMapProgram;
|
||||
static ProgramObject _shadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _cascadedShadowMapProgram;
|
||||
static ProgramObject _cascadedShadowNormalMapProgram;
|
||||
static ProgramObject _cascadedShadowSpecularMapProgram;
|
||||
static ProgramObject _cascadedShadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _shadowProgram;
|
||||
|
||||
static ProgramObject _skinProgram;
|
||||
|
@ -331,12 +337,24 @@ private:
|
|||
static ProgramObject _skinShadowSpecularMapProgram;
|
||||
static ProgramObject _skinShadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _skinCascadedShadowMapProgram;
|
||||
static ProgramObject _skinCascadedShadowNormalMapProgram;
|
||||
static ProgramObject _skinCascadedShadowSpecularMapProgram;
|
||||
static ProgramObject _skinCascadedShadowNormalSpecularMapProgram;
|
||||
|
||||
static ProgramObject _skinShadowProgram;
|
||||
|
||||
static int _normalMapTangentLocation;
|
||||
static int _normalSpecularMapTangentLocation;
|
||||
static int _shadowNormalMapTangentLocation;
|
||||
static int _shadowNormalSpecularMapTangentLocation;
|
||||
static int _cascadedShadowNormalMapTangentLocation;
|
||||
static int _cascadedShadowNormalSpecularMapTangentLocation;
|
||||
|
||||
static int _cascadedShadowMapDistancesLocation;
|
||||
static int _cascadedShadowNormalMapDistancesLocation;
|
||||
static int _cascadedShadowSpecularMapDistancesLocation;
|
||||
static int _cascadedShadowNormalSpecularMapDistancesLocation;
|
||||
|
||||
class SkinLocations {
|
||||
public:
|
||||
|
@ -344,6 +362,7 @@ private:
|
|||
int clusterIndices;
|
||||
int clusterWeights;
|
||||
int tangent;
|
||||
int shadowDistances;
|
||||
};
|
||||
|
||||
static SkinLocations _skinLocations;
|
||||
|
@ -354,6 +373,10 @@ private:
|
|||
static SkinLocations _skinShadowNormalMapLocations;
|
||||
static SkinLocations _skinShadowSpecularMapLocations;
|
||||
static SkinLocations _skinShadowNormalSpecularMapLocations;
|
||||
static SkinLocations _skinCascadedShadowMapLocations;
|
||||
static SkinLocations _skinCascadedShadowNormalMapLocations;
|
||||
static SkinLocations _skinCascadedShadowSpecularMapLocations;
|
||||
static SkinLocations _skinCascadedShadowNormalSpecularMapLocations;
|
||||
static SkinLocations _skinShadowLocations;
|
||||
|
||||
static void initSkinProgram(ProgramObject& program, SkinLocations& locations,
|
||||
|
|
|
@ -35,6 +35,14 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
|
|||
QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu));
|
||||
}
|
||||
|
||||
bool MenuScriptingInterface::menuExists(const QString& menu) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, menu));
|
||||
return result;
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator",
|
||||
Q_ARG(const QString&, menuName),
|
||||
|
@ -67,6 +75,15 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString&
|
|||
Q_ARG(const QString&, menuitem));
|
||||
};
|
||||
|
||||
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, menu),
|
||||
Q_ARG(const QString&, menuitem));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
||||
|
|
|
@ -33,6 +33,7 @@ private slots:
|
|||
public slots:
|
||||
void addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
@ -42,6 +43,7 @@ public slots:
|
|||
void addMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
|
229
interface/src/ui/JSConsole.cpp
Normal file
229
interface/src/ui/JSConsole.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
//
|
||||
// JSConsole.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 05/12/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 <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ScriptHighlighting.h"
|
||||
|
||||
#include "JSConsole.h"
|
||||
|
||||
const int NO_CURRENT_HISTORY_COMMAND = -1;
|
||||
const int MAX_HISTORY_SIZE = 64;
|
||||
|
||||
const QString COMMAND_STYLE = "color: #266a9b;";
|
||||
|
||||
const QString RESULT_SUCCESS_STYLE = "color: #677373;";
|
||||
const QString RESULT_ERROR_STYLE = "color: #d13b22;";
|
||||
|
||||
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\"><</span>";
|
||||
const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
|
||||
|
||||
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
|
||||
QWidget(parent),
|
||||
_ui(new Ui::Console),
|
||||
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
|
||||
_commandHistory(),
|
||||
_scriptEngine(scriptEngine) {
|
||||
|
||||
_ui->setupUi(this);
|
||||
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
|
||||
_ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap);
|
||||
_ui->promptTextEdit->installEventFilter(this);
|
||||
|
||||
QFile styleSheet(Application::resourcesPath() + "styles/console.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
QDir::setCurrent(Application::resourcesPath());
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom()));
|
||||
connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput()));
|
||||
|
||||
|
||||
if (_scriptEngine == NULL) {
|
||||
_scriptEngine = Application::getInstance()->loadScript();
|
||||
}
|
||||
|
||||
connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)),
|
||||
this, SLOT(handleEvalutationFinished(QScriptValue, bool)));
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
|
||||
|
||||
resizeTextInput();
|
||||
}
|
||||
|
||||
JSConsole::~JSConsole() {
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void JSConsole::executeCommand(const QString& command) {
|
||||
_commandHistory.prepend(command);
|
||||
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
|
||||
_commandHistory.removeLast();
|
||||
}
|
||||
|
||||
_ui->promptTextEdit->setDisabled(true);
|
||||
|
||||
appendMessage(">", "<span style='" + COMMAND_STYLE + "'>" + command.toHtmlEscaped() + "</span>");
|
||||
|
||||
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command));
|
||||
|
||||
resetCurrentCommandHistory();
|
||||
}
|
||||
|
||||
void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) {
|
||||
_ui->promptTextEdit->setDisabled(false);
|
||||
|
||||
// Make sure focus is still on this window - some commands are blocking and can take awhile to execute.
|
||||
if (window()->isActiveWindow()) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
||||
QString gutter = (isException || result.isError()) ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND;
|
||||
QString resultColor = (isException || result.isError()) ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE;
|
||||
QString resultStr = "<span style='" + resultColor + "'>" + result.toString().toHtmlEscaped() + "</span>";
|
||||
appendMessage(gutter, resultStr);
|
||||
}
|
||||
|
||||
void JSConsole::handlePrint(const QString& message) {
|
||||
appendMessage("", message);
|
||||
}
|
||||
|
||||
void JSConsole::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
||||
void JSConsole::showEvent(QShowEvent* event) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
||||
bool JSConsole::eventFilter(QObject* sender, QEvent* event) {
|
||||
if (sender == _ui->promptTextEdit) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
int key = keyEvent->key();
|
||||
|
||||
if ((key == Qt::Key_Return || key == Qt::Key_Enter)) {
|
||||
if (keyEvent->modifiers() & Qt::ShiftModifier) {
|
||||
// If the shift key is being used then treat it as a regular return/enter. If this isn't done,
|
||||
// a new QTextBlock isn't created.
|
||||
keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier);
|
||||
} else {
|
||||
QString command = _ui->promptTextEdit->toPlainText().trimmed();
|
||||
|
||||
if (!command.isEmpty()) {
|
||||
QTextCursor cursor = _ui->promptTextEdit->textCursor();
|
||||
_ui->promptTextEdit->clear();
|
||||
|
||||
executeCommand(command);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if (key == Qt::Key_Down) {
|
||||
// Go to the next command in history if the cursor is at the last line of the current command.
|
||||
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
|
||||
int blockCount = _ui->promptTextEdit->document()->blockCount();
|
||||
if (blockNumber == blockCount - 1) {
|
||||
setToNextCommandInHistory();
|
||||
return true;
|
||||
}
|
||||
} else if (key == Qt::Key_Up) {
|
||||
// Go to the previous command in history if the cursor is at the first line of the current command.
|
||||
int blockNumber = _ui->promptTextEdit->textCursor().blockNumber();
|
||||
if (blockNumber == 0) {
|
||||
setToPreviousCommandInHistory();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JSConsole::setToNextCommandInHistory() {
|
||||
if (_currentCommandInHistory >= 0) {
|
||||
_currentCommandInHistory--;
|
||||
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
|
||||
setAndSelectCommand(_rootCommand);
|
||||
} else {
|
||||
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JSConsole::setToPreviousCommandInHistory() {
|
||||
if (_currentCommandInHistory < (_commandHistory.length() - 1)) {
|
||||
if (_currentCommandInHistory == NO_CURRENT_HISTORY_COMMAND) {
|
||||
_rootCommand = _ui->promptTextEdit->toPlainText();
|
||||
}
|
||||
_currentCommandInHistory++;
|
||||
setAndSelectCommand(_commandHistory[_currentCommandInHistory]);
|
||||
}
|
||||
}
|
||||
|
||||
void JSConsole::resetCurrentCommandHistory() {
|
||||
_currentCommandInHistory = NO_CURRENT_HISTORY_COMMAND;
|
||||
}
|
||||
|
||||
void JSConsole::resizeTextInput() {
|
||||
_ui->promptTextEdit->setFixedHeight(_ui->promptTextEdit->document()->size().height());
|
||||
_ui->promptTextEdit->updateGeometry();
|
||||
}
|
||||
|
||||
void JSConsole::setAndSelectCommand(const QString& text) {
|
||||
QTextCursor cursor = _ui->promptTextEdit->textCursor();
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.deleteChar();
|
||||
cursor.insertText(text);
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
}
|
||||
|
||||
void JSConsole::scrollToBottom() {
|
||||
QScrollBar* scrollBar = _ui->scrollArea->verticalScrollBar();
|
||||
scrollBar->setValue(scrollBar->maximum());
|
||||
}
|
||||
|
||||
void JSConsole::appendMessage(const QString& gutter, const QString& message) {
|
||||
QWidget* logLine = new QWidget(_ui->logArea);
|
||||
QHBoxLayout* layout = new QHBoxLayout(logLine);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(4);
|
||||
|
||||
QLabel* gutterLabel = new QLabel(logLine);
|
||||
QLabel* messageLabel = new QLabel(logLine);
|
||||
|
||||
gutterLabel->setFixedWidth(16);
|
||||
gutterLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
messageLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
gutterLabel->setStyleSheet("font-size: 14px; font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;");
|
||||
messageLabel->setStyleSheet("font-size: 14px; font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;");
|
||||
|
||||
gutterLabel->setText(gutter);
|
||||
messageLabel->setText(message);
|
||||
|
||||
layout->addWidget(gutterLabel);
|
||||
layout->addWidget(messageLabel);
|
||||
logLine->setLayout(layout);
|
||||
|
||||
layout->setAlignment(gutterLabel, Qt::AlignTop);
|
||||
|
||||
layout->setStretch(0, 0);
|
||||
layout->setStretch(1, 1);
|
||||
|
||||
_ui->logArea->layout()->addWidget(logLine);
|
||||
|
||||
_ui->logArea->updateGeometry();
|
||||
scrollToBottom();
|
||||
}
|
62
interface/src/ui/JSConsole.h
Normal file
62
interface/src/ui/JSConsole.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// JSConsole.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 05/12/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_JSConsole_h
|
||||
#define hifi_JSConsole_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QEvent>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui_console.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
class JSConsole : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL);
|
||||
~JSConsole();
|
||||
|
||||
public slots:
|
||||
void executeCommand(const QString& command);
|
||||
|
||||
signals:
|
||||
void commandExecuting(const QString& command);
|
||||
void commandFinished(const QString& result);
|
||||
|
||||
protected:
|
||||
void setAndSelectCommand(const QString& command);
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
protected slots:
|
||||
void scrollToBottom();
|
||||
void resizeTextInput();
|
||||
void handleEvalutationFinished(QScriptValue result, bool isException);
|
||||
void handlePrint(const QString& message);
|
||||
|
||||
private:
|
||||
void appendMessage(const QString& gutter, const QString& message);
|
||||
void setToNextCommandInHistory();
|
||||
void setToPreviousCommandInHistory();
|
||||
void resetCurrentCommandHistory();
|
||||
|
||||
Ui::Console* _ui;
|
||||
int _currentCommandInHistory;
|
||||
QList<QString> _commandHistory;
|
||||
QString _rootCommand;
|
||||
ScriptEngine* _scriptEngine;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_JSConsole_h
|
|
@ -82,7 +82,7 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz
|
|||
_activeWebView = new QWebView;
|
||||
|
||||
// keep the window on top and delete it when it closes
|
||||
_activeWebView->setWindowFlags(Qt::Sheet | Qt::WindowStaysOnTopHint);
|
||||
_activeWebView->setWindowFlags(Qt::Sheet);
|
||||
_activeWebView->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
qDebug() << "Displaying QWebView for OAuth authorization at" << authorizationURL.toString();
|
||||
|
|
|
@ -29,6 +29,8 @@ PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : F
|
|||
connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &PreferencesDialog::openHeadModelBrowser);
|
||||
connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &PreferencesDialog::openBodyModelBrowser);
|
||||
connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser);
|
||||
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked,
|
||||
Application::getInstance(), &Application::loadDefaultScripts);
|
||||
}
|
||||
|
||||
void PreferencesDialog::accept() {
|
||||
|
|
|
@ -106,36 +106,12 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) {
|
|||
createRecentlyLoadedScriptsTable();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
int loadScriptNumber = -1;
|
||||
switch(event->key()) {
|
||||
case Qt::Key_Escape:
|
||||
Application::getInstance()->toggleRunningScriptsWidget();
|
||||
break;
|
||||
|
||||
case Qt::Key_1:
|
||||
case Qt::Key_2:
|
||||
case Qt::Key_3:
|
||||
case Qt::Key_4:
|
||||
case Qt::Key_5:
|
||||
case Qt::Key_6:
|
||||
case Qt::Key_7:
|
||||
case Qt::Key_8:
|
||||
case Qt::Key_9:
|
||||
loadScriptNumber = event->key() - Qt::Key_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) {
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
return;
|
||||
} else {
|
||||
FramelessDialog::keyPressEvent(keyEvent);
|
||||
}
|
||||
if (loadScriptNumber >= 0) {
|
||||
if (_recentlyLoadedScripts.size() > loadScriptNumber) {
|
||||
Application::getInstance()->loadScript(_recentlyLoadedScripts.at(loadScriptNumber));
|
||||
}
|
||||
}
|
||||
|
||||
FramelessDialog::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::paintEvent(QPaintEvent* event) {
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "FlowLayout.h"
|
||||
#include "JSConsole.h"
|
||||
|
||||
const int CONSOLE_HEIGHT = 150;
|
||||
|
||||
ScriptEditorWindow::ScriptEditorWindow() :
|
||||
_ScriptEditorWindowUI(new Ui::ScriptEditorWindow),
|
||||
|
@ -48,6 +51,10 @@ ScriptEditorWindow::ScriptEditorWindow() :
|
|||
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);
|
||||
|
||||
QWidget* console = new JSConsole(this);
|
||||
console->setFixedHeight(CONSOLE_HEIGHT);
|
||||
this->layout()->addWidget(console);
|
||||
}
|
||||
|
||||
ScriptEditorWindow::~ScriptEditorWindow() {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "Stats.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Menu.h"
|
||||
|
@ -158,6 +160,39 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
|
|||
glColor4f(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
bool Stats::includeTimingRecord(const QString& name) {
|
||||
bool included = false;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
|
||||
if (name == "idle/update") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming) ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name == "idle/updateGL") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name.startsWith("idle/update")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming);
|
||||
} else if (name.startsWith("idle/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name.startsWith("MyAvatar::simulate")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarSimulateTiming);
|
||||
} else if (name.startsWith("MyAvatar::update/") || name.startsWith("updateMyAvatar")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarUpdateTiming);
|
||||
} else if (name.startsWith("MyAvatar::")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandMiscAvatarTiming);
|
||||
} else if (name == "paintGL/displaySide") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming) ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else if (name.startsWith("paintGL/displaySide/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming);
|
||||
} else if (name.startsWith("paintGL/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else {
|
||||
included = true; // include everything else
|
||||
}
|
||||
}
|
||||
return included;
|
||||
}
|
||||
|
||||
// display expanded or contracted stats
|
||||
void Stats::display(
|
||||
const float* color,
|
||||
|
@ -345,11 +380,25 @@ void Stats::display(
|
|||
|
||||
VoxelSystem* voxels = Application::getInstance()->getVoxels();
|
||||
|
||||
lines = _expanded ? 12 : 3;
|
||||
lines = _expanded ? 11 : 3;
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
|
||||
lines += 9; // spatial audio processing adds 1 spacing line and 8 extra lines of info
|
||||
}
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
// we will also include room for 1 line per timing record and a header
|
||||
lines += 1;
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
lines++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
||||
|
@ -454,8 +503,6 @@ void Stats::display(
|
|||
}
|
||||
}
|
||||
|
||||
verticalOffset += (_expanded ? STATS_PELS_PER_LINE : 0);
|
||||
|
||||
QString serversTotalString = locale.toString((uint)totalNodes); // consider adding: .rightJustified(10, ' ');
|
||||
|
||||
// Server Voxels
|
||||
|
@ -508,6 +555,29 @@ void Stats::display(
|
|||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
|
||||
}
|
||||
|
||||
// TODO: the display of these timing details should all be moved to JavaScript
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
// Timing details...
|
||||
const int TIMER_OUTPUT_LINE_LENGTH = 300;
|
||||
char perfLine[TIMER_OUTPUT_LINE_LENGTH];
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font,
|
||||
"--------------------- Function -------------------- --msecs- -calls--", color);
|
||||
|
||||
const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (includeTimingRecord(i.key())) {
|
||||
sprintf(perfLine, "%50s: %8.4f [%6llu]", qPrintable(i.key()),
|
||||
(float)i.value().getMovingAverage() / (float)USECS_PER_MSEC,
|
||||
i.value().getCount());
|
||||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, perfLine, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessing)) {
|
||||
verticalOffset += STATS_PELS_PER_LINE; // space one line...
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
|
||||
void resetWidth(int width, int horizontalOffset);
|
||||
void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess);
|
||||
bool includeTimingRecord(const QString& name);
|
||||
private:
|
||||
static Stats* _sharedInstance;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, c
|
|||
}
|
||||
|
||||
PacketType voxelPacketType = packetTypeForPacket(mutablePacket);
|
||||
|
||||
|
||||
// note: PacketType_OCTREE_STATS can have PacketType_VOXEL_DATA
|
||||
// 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
|
||||
|
@ -81,6 +81,7 @@ void VoxelPacketProcessor::processPacket(const SharedNodePointer& sendingNode, c
|
|||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
|
||||
app->trackIncomingVoxelPacket(mutablePacket, sendingNode, wasStatsPacket);
|
||||
|
||||
if (sendingNode) {
|
||||
|
|
|
@ -519,6 +519,17 @@ void VoxelSystem::initVoxelMemory() {
|
|||
_shadowMapProgram.bind();
|
||||
_shadowMapProgram.setUniformValue("shadowMap", 0);
|
||||
_shadowMapProgram.release();
|
||||
|
||||
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex,
|
||||
Application::resourcesPath() + "shaders/cascaded_shadow_map.vert");
|
||||
_cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment,
|
||||
Application::resourcesPath() + "shaders/cascaded_shadow_map.frag");
|
||||
_cascadedShadowMapProgram.link();
|
||||
|
||||
_cascadedShadowMapProgram.bind();
|
||||
_cascadedShadowMapProgram.setUniformValue("shadowMap", 0);
|
||||
_shadowDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances");
|
||||
_cascadedShadowMapProgram.release();
|
||||
}
|
||||
}
|
||||
_renderer = new PrimitiveRenderer(_maxVoxels);
|
||||
|
@ -1166,6 +1177,8 @@ glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float vo
|
|||
|
||||
ProgramObject VoxelSystem::_perlinModulateProgram;
|
||||
ProgramObject VoxelSystem::_shadowMapProgram;
|
||||
ProgramObject VoxelSystem::_cascadedShadowMapProgram;
|
||||
int VoxelSystem::_shadowDistancesLocation;
|
||||
|
||||
void VoxelSystem::init() {
|
||||
if (_initialized) {
|
||||
|
@ -1486,14 +1499,15 @@ void VoxelSystem::render() {
|
|||
|
||||
void VoxelSystem::applyScaleAndBindProgram(bool texture) {
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
|
||||
_shadowMapProgram.bind();
|
||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
_cascadedShadowMapProgram.bind();
|
||||
_cascadedShadowMapProgram.setUniform(_shadowDistancesLocation, Application::getInstance()->getShadowDistances());
|
||||
} else {
|
||||
_shadowMapProgram.bind();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID());
|
||||
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]);
|
||||
|
||||
} else if (texture) {
|
||||
_perlinModulateProgram.bind();
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID());
|
||||
|
@ -1507,11 +1521,14 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) {
|
|||
// scale back down to 1 so heads aren't massive
|
||||
glPopMatrix();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) {
|
||||
_shadowMapProgram.release();
|
||||
if (Menu::getInstance()->getShadowsEnabled()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
|
||||
_cascadedShadowMapProgram.release();
|
||||
} else {
|
||||
_shadowMapProgram.release();
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
|
||||
} else if (texture) {
|
||||
_perlinModulateProgram.release();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
|
|
@ -233,6 +233,8 @@ private:
|
|||
|
||||
static ProgramObject _perlinModulateProgram;
|
||||
static ProgramObject _shadowMapProgram;
|
||||
static ProgramObject _cascadedShadowMapProgram;
|
||||
static int _shadowDistancesLocation;
|
||||
|
||||
int _hookID;
|
||||
std::vector<glBufferIndex> _freeIndexes;
|
||||
|
|
258
interface/ui/console.ui
Normal file
258
interface/ui/console.ui
Normal file
|
@ -0,0 +1,258 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Console</class>
|
||||
<widget class="QWidget" name="Console">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1055</width>
|
||||
<height>205</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QDialog { background: white }</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="widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_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>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</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>1040</width>
|
||||
<height>205</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: white;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<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="logArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: white;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>4</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="inputArea" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0">
|
||||
<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 alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="promptGutterLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>23</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>23</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">padding: 0px 0 0 0;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="promptTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Inconsolata,Lucida Console,Andale Mono,Monaco</family>
|
||||
<pointsize>-1</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -134,8 +134,8 @@ color: #0e7077</string>
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>30</y>
|
||||
<width>615</width>
|
||||
<height>491</height>
|
||||
<width>494</width>
|
||||
<height>384</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
|
@ -154,9 +154,9 @@ color: #0e7077</string>
|
|||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-271</y>
|
||||
<width>598</width>
|
||||
<height>1018</height>
|
||||
<y>-204</y>
|
||||
<width>494</width>
|
||||
<height>1091</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
|
@ -612,6 +612,78 @@ color: #0e7077</string>
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="avatarTitleLabel_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>20</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: #0e7077</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Scripts</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonReloadDefaultScripts">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background: #0e7077;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
font: bold 14pt;
|
||||
padding: 10px;margin-top:10px</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load Default Scripts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_13">
|
||||
<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>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -143,7 +143,7 @@ font: bold 14pt;</string>
|
|||
font-size: 14pt;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>(click a script or use the 1-9 keys to load and run it)</string>
|
||||
<string>(click a script to load and run it)</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
|
|
@ -88,6 +88,7 @@ void AudioInjector::injectAudio() {
|
|||
int currentSendPosition = 0;
|
||||
|
||||
int numPreAudioDataBytes = injectAudioPacket.size();
|
||||
bool shouldLoop = _options.getLoop();
|
||||
|
||||
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
|
||||
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
||||
|
@ -120,6 +121,10 @@ void AudioInjector::injectAudio() {
|
|||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldLoop && currentSendPosition == soundByteArray.size()) {
|
||||
currentSendPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) :
|
|||
QObject(parent),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_volume(1.0f),
|
||||
_loop(false),
|
||||
_orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_loopbackAudioInterface(NULL)
|
||||
{
|
||||
|
@ -24,6 +25,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) :
|
|||
AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) {
|
||||
_position = other._position;
|
||||
_volume = other._volume;
|
||||
_loop = other._loop;
|
||||
_orientation = other._orientation;
|
||||
_loopbackAudioInterface = other._loopbackAudioInterface;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class AudioInjectorOptions : public QObject {
|
|||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(float volume READ getVolume WRITE setVolume)
|
||||
Q_PROPERTY(bool loop READ getLoop WRITE setLoop)
|
||||
public:
|
||||
AudioInjectorOptions(QObject* parent = 0);
|
||||
AudioInjectorOptions(const AudioInjectorOptions& other);
|
||||
|
@ -36,6 +37,9 @@ public:
|
|||
float getVolume() const { return _volume; }
|
||||
void setVolume(float volume) { _volume = volume; }
|
||||
|
||||
float getLoop() const { return _loop; }
|
||||
void setLoop(float loop) { _loop = loop; }
|
||||
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
|
||||
|
@ -45,6 +49,7 @@ public:
|
|||
private:
|
||||
glm::vec3 _position;
|
||||
float _volume;
|
||||
bool _loop;
|
||||
glm::quat _orientation;
|
||||
AbstractAudioInterface* _loopbackAudioInterface;
|
||||
};
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
// degrees
|
||||
const float MIN_HEAD_YAW = -110.f;
|
||||
const float MAX_HEAD_YAW = 110.f;
|
||||
const float MIN_HEAD_YAW = -180.f;
|
||||
const float MAX_HEAD_YAW = 180.f;
|
||||
const float MIN_HEAD_PITCH = -60.f;
|
||||
const float MAX_HEAD_PITCH = 60.f;
|
||||
const float MIN_HEAD_ROLL = -50.f;
|
||||
|
|
|
@ -545,8 +545,8 @@ bool ModelTree::hasModelsDeletedSince(quint64 sinceTime) {
|
|||
}
|
||||
|
||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||
bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outputBuffer, size_t maxLength,
|
||||
size_t& outputLength) {
|
||||
bool ModelTree::encodeModelsDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* outputBuffer,
|
||||
size_t maxLength, size_t& outputLength) {
|
||||
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
|
@ -555,6 +555,26 @@ bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outp
|
|||
copyAt += numBytesPacketHeader;
|
||||
outputLength = numBytesPacketHeader;
|
||||
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)copyAt;
|
||||
*flagsAt = flags;
|
||||
copyAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
outputLength += sizeof(OCTREE_PACKET_FLAGS);
|
||||
|
||||
// pack in sequence number
|
||||
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)copyAt;
|
||||
*sequenceAt = sequenceNumber;
|
||||
copyAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
outputLength += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)copyAt;
|
||||
*timeAt = now;
|
||||
copyAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
outputLength += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
unsigned char* numberOfIDsAt = copyAt;
|
||||
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
|
||||
|
@ -642,6 +662,10 @@ void ModelTree::processEraseMessage(const QByteArray& dataByteArray, const Share
|
|||
size_t processedBytes = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
|
||||
dataAt += sizeof(numberOfIds);
|
||||
|
|
|
@ -74,7 +74,7 @@ public:
|
|||
|
||||
bool hasAnyDeletedModels() const { return _recentlyDeletedModelItemIDs.size() > 0; }
|
||||
bool hasModelsDeletedSince(quint64 sinceTime);
|
||||
bool encodeModelsDeletedSince(quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
bool encodeModelsDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
void forgetModelsDeletedBefore(quint64 sinceTime);
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
|
|
@ -192,7 +192,7 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons
|
|||
|
||||
// if it's in our AABOX for our rotated extents, then check to see if it's in our non-AABox
|
||||
if (rotatedExtentsBox.findRayIntersection(origin, direction, localDistance, localFace)) {
|
||||
|
||||
|
||||
// extents is the model relative, scaled, centered extents of the model
|
||||
glm::mat4 rotation = glm::mat4_cast(model.getModelRotation());
|
||||
glm::mat4 translation = glm::translate(model.getPosition());
|
||||
|
@ -202,7 +202,7 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons
|
|||
AABox modelFrameBox(extents.minimum, (extents.maximum - extents.minimum));
|
||||
|
||||
glm::vec3 modelFrameOrigin = glm::vec3(worldToModelMatrix * glm::vec4(origin, 1.0f));
|
||||
glm::vec3 modelFrameDirection = glm::vec3(worldToModelMatrix * glm::vec4(direction, 1.0f));
|
||||
glm::vec3 modelFrameDirection = glm::vec3(worldToModelMatrix * glm::vec4(direction, 0.0f));
|
||||
|
||||
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
|
||||
// and testing intersection there.
|
||||
|
|
|
@ -52,7 +52,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAvatarIdentity:
|
||||
return 1;
|
||||
case PacketTypeEnvironmentData:
|
||||
return 1;
|
||||
return 2;
|
||||
case PacketTypeDomainList:
|
||||
case PacketTypeDomainListRequest:
|
||||
return 3;
|
||||
|
@ -66,8 +66,12 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 1;
|
||||
case PacketTypeParticleData:
|
||||
return 1;
|
||||
case PacketTypeParticleErase:
|
||||
return 1;
|
||||
case PacketTypeModelData:
|
||||
return 2;
|
||||
return 2;
|
||||
case PacketTypeModelErase:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1308,11 +1308,6 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
|
||||
keepSearching = true; // assume that we will continue searching after this.
|
||||
|
||||
// by default, we only allow intersections with leaves with content
|
||||
if (!canRayIntersect()) {
|
||||
return false; // we don't intersect with non-leaves, and we keep searching
|
||||
}
|
||||
|
||||
AACube cube = getAACube();
|
||||
float localDistance;
|
||||
BoxFace localFace;
|
||||
|
@ -1323,6 +1318,11 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
return false; // we did not intersect
|
||||
}
|
||||
|
||||
// by default, we only allow intersections with leaves with content
|
||||
if (!canRayIntersect()) {
|
||||
return false; // we don't intersect with non-leaves, and we keep searching
|
||||
}
|
||||
|
||||
// we did hit this element, so calculate appropriate distances
|
||||
localDistance *= TREE_SCALE;
|
||||
if (localDistance < distance) {
|
||||
|
@ -1346,6 +1346,7 @@ bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const g
|
|||
if (intersectedObject) {
|
||||
*intersectedObject = this;
|
||||
}
|
||||
keepSearching = false;
|
||||
return true; // we did intersect
|
||||
}
|
||||
return false; // we did not intersect
|
||||
|
|
|
@ -858,7 +858,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
|||
//bool packetIsCompressed = oneAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||
|
||||
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
|
||||
int flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
|
||||
qint64 flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qDebug() << "sentAt:" << sentAt << " usecs";
|
||||
|
@ -866,7 +866,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
|||
qDebug() << "nodeClockSkewUsec:" << nodeClockSkewUsec << " usecs";
|
||||
qDebug() << "flightTime:" << flightTime << " usecs";
|
||||
}
|
||||
|
||||
|
||||
// Guard against possible corrupted packets... with bad timestamps
|
||||
const int MAX_RESONABLE_FLIGHT_TIME = 200 * USECS_PER_SECOND; // 200 seconds is more than enough time for a packet to arrive
|
||||
const int MIN_RESONABLE_FLIGHT_TIME = 0;
|
||||
|
@ -985,6 +985,6 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -511,8 +511,8 @@ bool ParticleTree::hasParticlesDeletedSince(quint64 sinceTime) {
|
|||
}
|
||||
|
||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||
bool ParticleTree::encodeParticlesDeletedSince(quint64& sinceTime, unsigned char* outputBuffer, size_t maxLength,
|
||||
size_t& outputLength) {
|
||||
bool ParticleTree::encodeParticlesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* outputBuffer,
|
||||
size_t maxLength, size_t& outputLength) {
|
||||
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
|
@ -521,6 +521,24 @@ bool ParticleTree::encodeParticlesDeletedSince(quint64& sinceTime, unsigned char
|
|||
copyAt += numBytesPacketHeader;
|
||||
outputLength = numBytesPacketHeader;
|
||||
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
memcpy(copyAt, &flags, sizeof(OCTREE_PACKET_FLAGS));
|
||||
copyAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
outputLength += sizeof(OCTREE_PACKET_FLAGS);
|
||||
|
||||
// pack in sequence number
|
||||
memcpy(copyAt, &sequenceNumber, sizeof(OCTREE_PACKET_SEQUENCE));
|
||||
copyAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
outputLength += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
memcpy(copyAt, &now, sizeof(OCTREE_PACKET_SENT_TIME));
|
||||
copyAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
outputLength += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
unsigned char* numberOfIDsAt = copyAt;
|
||||
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
|
||||
|
@ -609,6 +627,10 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Sh
|
|||
size_t processedBytes = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
|
||||
dataAt += sizeof(numberOfIds);
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
|
||||
bool hasAnyDeletedParticles() const { return _recentlyDeletedParticleIDs.size() > 0; }
|
||||
bool hasParticlesDeletedSince(quint64 sinceTime);
|
||||
bool encodeParticlesDeletedSince(quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
bool encodeParticlesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
void forgetParticlesDeletedBefore(quint64 sinceTime);
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
|
|
|
@ -314,6 +314,18 @@ void ScriptEngine::evaluate() {
|
|||
}
|
||||
}
|
||||
|
||||
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
|
||||
QScriptValue result = _engine.evaluate(program, fileName, lineNumber);
|
||||
bool hasUncaughtException = _engine.hasUncaughtException();
|
||||
if (hasUncaughtException) {
|
||||
int line = _engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at line" << line << ": " << result.toString();
|
||||
}
|
||||
emit evaluationFinished(result, hasUncaughtException);
|
||||
_engine.clearExceptions();
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScriptEngine::sendAvatarIdentityPacket() {
|
||||
if (_isAvatar && _avatarData) {
|
||||
_avatarData->sendIdentityPacket();
|
||||
|
|
|
@ -92,6 +92,7 @@ public:
|
|||
public slots:
|
||||
void stop();
|
||||
|
||||
QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1);
|
||||
QObject* setInterval(const QScriptValue& function, int intervalMS);
|
||||
QObject* setTimeout(const QScriptValue& function, int timeoutMS);
|
||||
void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
|
||||
|
@ -107,6 +108,7 @@ signals:
|
|||
void printedMessage(const QString& message);
|
||||
void errorMessage(const QString& message);
|
||||
void runningStateChanged();
|
||||
void evaluationFinished(QScriptValue result, bool isException);
|
||||
|
||||
protected:
|
||||
QString _scriptContents;
|
||||
|
|
|
@ -52,4 +52,22 @@ PerformanceWarning::~PerformanceWarning() {
|
|||
}
|
||||
};
|
||||
|
||||
QMap<QString, PerformanceTimerRecord> PerformanceTimer::_records;
|
||||
|
||||
|
||||
PerformanceTimer::~PerformanceTimer() {
|
||||
quint64 end = usecTimestampNow();
|
||||
quint64 elapsedusec = (end - _start);
|
||||
PerformanceTimerRecord& namedRecord = _records[_name];
|
||||
namedRecord.recordResult(elapsedusec);
|
||||
}
|
||||
|
||||
void PerformanceTimer::dumpAllTimerRecords() {
|
||||
QMapIterator<QString, PerformanceTimerRecord> i(_records);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
qDebug() << i.key() << ": average " << i.value().getAverage()
|
||||
<< " [" << i.value().getMovingAverage() << "]"
|
||||
<< "usecs over" << i.value().getCount() << "calls";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "SharedUtil.h"
|
||||
#include "SimpleMovingAverage.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
@ -49,5 +50,41 @@ public:
|
|||
static void setSuppressShortTimings(bool suppressShortTimings) { _suppressShortTimings = suppressShortTimings; }
|
||||
};
|
||||
|
||||
class PerformanceTimerRecord {
|
||||
public:
|
||||
PerformanceTimerRecord() : _runningTotal(0), _totalCalls(0) {}
|
||||
|
||||
void recordResult(quint64 elapsed) { _runningTotal += elapsed; _totalCalls++; _movingAverage.updateAverage(elapsed); }
|
||||
quint64 getAverage() const { return (_totalCalls == 0) ? 0 : _runningTotal / _totalCalls; }
|
||||
quint64 getMovingAverage() const { return (_totalCalls == 0) ? 0 : _movingAverage.getAverage(); }
|
||||
quint64 getCount() const { return _totalCalls; }
|
||||
|
||||
private:
|
||||
quint64 _runningTotal;
|
||||
quint64 _totalCalls;
|
||||
SimpleMovingAverage _movingAverage;
|
||||
};
|
||||
|
||||
class PerformanceTimer {
|
||||
public:
|
||||
|
||||
PerformanceTimer(const QString& name) :
|
||||
_start(usecTimestampNow()),
|
||||
_name(name) { }
|
||||
|
||||
quint64 elapsed() const { return (usecTimestampNow() - _start); };
|
||||
|
||||
~PerformanceTimer();
|
||||
|
||||
static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; };
|
||||
static const QMap<QString, PerformanceTimerRecord>& getAllTimerRecords() { return _records; };
|
||||
static void dumpAllTimerRecords();
|
||||
|
||||
private:
|
||||
quint64 _start;
|
||||
QString _name;
|
||||
static QMap<QString, PerformanceTimerRecord> _records;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_PerfStat_h
|
||||
|
|
Loading…
Reference in a new issue