mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 08:04:01 +02:00
merge upstream/master into andrew/inertia
This commit is contained in:
commit
8d660c76cc
47 changed files with 752 additions and 186 deletions
|
@ -213,6 +213,8 @@ void Agent::run() {
|
|||
|
||||
loop.exec();
|
||||
|
||||
|
||||
|
||||
// let the AvatarData and ResourceCache classes use our QNetworkAccessManager
|
||||
AvatarData::setNetworkAccessManager(networkManager);
|
||||
ResourceCache::setNetworkAccessManager(networkManager);
|
||||
|
|
|
@ -158,10 +158,11 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
|
|||
|
||||
void OctreeQueryNode::init() {
|
||||
_myPacketType = getMyPacketType();
|
||||
resetOctreePacket(true); // don't bump sequence
|
||||
resetOctreePacket(); // don't bump sequence
|
||||
}
|
||||
|
||||
void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
|
||||
|
||||
void OctreeQueryNode::resetOctreePacket() {
|
||||
// if shutting down, return immediately
|
||||
if (_isShuttingDown) {
|
||||
return;
|
||||
|
@ -196,15 +197,12 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
|
|||
*flagsAt = flags;
|
||||
_octreePacketAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS);
|
||||
|
||||
|
||||
// pack in sequence number
|
||||
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt;
|
||||
*sequenceAt = _sequenceNumber;
|
||||
_octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
if (!(lastWasSurpressed || _lastOctreePacketLength == (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE))) {
|
||||
_sequenceNumber++;
|
||||
}
|
||||
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
|
@ -365,3 +363,6 @@ void OctreeQueryNode::dumpOutOfView() {
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeQueryNode::incrementSequenceNumber() {
|
||||
_sequenceNumber++;
|
||||
}
|
|
@ -35,7 +35,7 @@ public:
|
|||
void init(); // called after creation to set up some virtual items
|
||||
virtual PacketType getMyPacketType() const = 0;
|
||||
|
||||
void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header
|
||||
void resetOctreePacket(); // resets octree packet to after "V" header
|
||||
|
||||
void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet
|
||||
|
||||
|
@ -99,6 +99,8 @@ public:
|
|||
void nodeKilled();
|
||||
void forceNodeShutdown();
|
||||
bool isShuttingDown() const { return _isShuttingDown; }
|
||||
|
||||
void incrementSequenceNumber();
|
||||
|
||||
private slots:
|
||||
void sendThreadFinished();
|
||||
|
@ -135,8 +137,9 @@ private:
|
|||
float _lastClientOctreeSizeScale;
|
||||
bool _lodChanged;
|
||||
bool _lodInitialized;
|
||||
|
||||
|
||||
OCTREE_PACKET_SEQUENCE _sequenceNumber;
|
||||
|
||||
quint64 _lastRootTimestamp;
|
||||
|
||||
PacketType _myPacketType;
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
@ -139,7 +137,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
|
||||
// this rate control savings.
|
||||
if (nodeData->shouldSuppressDuplicatePacket()) {
|
||||
nodeData->resetOctreePacket(true); // we still need to reset it though!
|
||||
nodeData->resetOctreePacket(); // we still need to reset it though!
|
||||
return packetsSent; // without sending...
|
||||
}
|
||||
|
||||
|
@ -244,6 +242,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
packetsSent++;
|
||||
nodeData->incrementSequenceNumber();
|
||||
nodeData->resetOctreePacket();
|
||||
}
|
||||
|
||||
|
|
|
@ -218,6 +218,8 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
}
|
||||
}
|
||||
|
||||
qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
|
||||
|
||||
// assume that the fact we are authing against HF data server means we will pay for assignments
|
||||
// setup a timer to send transactions to pay assigned nodes every 30 seconds
|
||||
QTimer* creditSetupTimer = new QTimer(this);
|
||||
|
@ -731,10 +733,11 @@ void DomainServer::setupPendingAssignmentCredits() {
|
|||
qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed();
|
||||
nodeData->getPaymentIntervalTimer().restart();
|
||||
|
||||
const float CREDITS_PER_HOUR = 3;
|
||||
const float CREDITS_PER_HOUR = 0.10f;
|
||||
const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000);
|
||||
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * powf(10.0f, 8.0f);
|
||||
|
||||
float pendingCredits = elapsedMsecsSinceLastPayment * CREDITS_PER_MSEC;
|
||||
float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC;
|
||||
|
||||
if (existingTransaction) {
|
||||
existingTransaction->incrementAmount(pendingCredits);
|
||||
|
@ -1001,6 +1004,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
return true;
|
||||
} else {
|
||||
// check if this is for json stats for a node
|
||||
const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING);
|
||||
QRegExp nodeShowRegex(NODE_JSON_REGEX_STRING);
|
||||
|
||||
|
@ -1025,6 +1029,40 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
// tell the caller we processed the request
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if this is a request for a scripted assignment (with a temp unique UUID)
|
||||
const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING);
|
||||
QRegExp assignmentRegex(ASSIGNMENT_REGEX_STRING);
|
||||
|
||||
if (assignmentRegex.indexIn(url.path()) != -1) {
|
||||
QUuid matchingUUID = QUuid(assignmentRegex.cap(1));
|
||||
|
||||
SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID);
|
||||
if (!matchingAssignment) {
|
||||
// check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment
|
||||
PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID);
|
||||
if (pendingData) {
|
||||
matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID());
|
||||
|
||||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||
// via correct URL for the script so the client can download
|
||||
|
||||
QUrl scriptURL = url;
|
||||
scriptURL.setPath(URI_ASSIGNMENT + "/"
|
||||
+ uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID()));
|
||||
|
||||
// have the HTTPManager serve the appropriate script file
|
||||
return _httpManager.handleHTTPRequest(connection, scriptURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// request not handled
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {
|
||||
|
|
|
@ -40,6 +40,16 @@ var modelURLs = [
|
|||
|
||||
var toolBar;
|
||||
|
||||
function isLocked(properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (location.hostname == "hq.highfidelity.io" &&
|
||||
properties.modelURL == "https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/Terrain_Reduce_forAlpha.fbx") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function controller(wichSide) {
|
||||
this.side = wichSide;
|
||||
this.palm = 2 * wichSide;
|
||||
|
@ -117,7 +127,7 @@ function controller(wichSide) {
|
|||
|
||||
|
||||
this.grab = function (modelID, properties) {
|
||||
if (this.isLocked(properties)) {
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + modelID.id);
|
||||
} else {
|
||||
print("Grabbing " + modelID.id);
|
||||
|
@ -150,18 +160,9 @@ function controller(wichSide) {
|
|||
}
|
||||
}
|
||||
|
||||
this.isLocked = function (properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (location.hostname == "hq.highfidelity.io" &&
|
||||
properties.modelURL == "https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/Terrain_Reduce_forAlpha.fbx") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
this.checkModel = function (properties) {
|
||||
// special case to lock the ground plane model in hq.
|
||||
if (this.isLocked(properties)) {
|
||||
if (isLocked(properties)) {
|
||||
return { valid: false };
|
||||
}
|
||||
|
||||
|
@ -293,6 +294,7 @@ function controller(wichSide) {
|
|||
if (this.pressing) {
|
||||
Vec3.print("Looking at: ", this.palmPosition);
|
||||
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
|
||||
|
||||
for (var i = 0; i < foundModels.length; i++) {
|
||||
|
||||
if (!foundModels[i].isKnownID) {
|
||||
|
@ -305,7 +307,9 @@ function controller(wichSide) {
|
|||
}
|
||||
|
||||
var properties = Models.getModelProperties(foundModels[i]);
|
||||
if (this.isLocked(properties)) {
|
||||
print("foundModels["+i+"].modelURL=" + properties.modelURL);
|
||||
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID);
|
||||
|
@ -475,6 +479,7 @@ function mousePressEvent(event) {
|
|||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Vec3.print("[Mouse] Looking at: ", pickRay.origin);
|
||||
var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR);
|
||||
var closest = -1.0;
|
||||
for (var i = 0; i < foundModels.length; i++) {
|
||||
if (!foundModels[i].isKnownID) {
|
||||
var identify = Models.identifyModel(foundModels[i]);
|
||||
|
@ -486,7 +491,7 @@ function mousePressEvent(event) {
|
|||
}
|
||||
|
||||
var properties = Models.getModelProperties(foundModels[i]);
|
||||
if (this.isLocked(properties)) {
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID);
|
||||
|
@ -510,35 +515,51 @@ function mousePressEvent(event) {
|
|||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
|
||||
if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) {
|
||||
modelSelected = true;
|
||||
selectedModelID = foundModels[i];
|
||||
selectedModelProperties = properties;
|
||||
|
||||
selectedModelProperties.oldRadius = selectedModelProperties.radius;
|
||||
selectedModelProperties.oldPosition = {
|
||||
x: selectedModelProperties.position.x,
|
||||
y: selectedModelProperties.position.y,
|
||||
z: selectedModelProperties.position.z,
|
||||
};
|
||||
selectedModelProperties.oldRotation = {
|
||||
x: selectedModelProperties.modelRotation.x,
|
||||
y: selectedModelProperties.modelRotation.y,
|
||||
z: selectedModelProperties.modelRotation.z,
|
||||
w: selectedModelProperties.modelRotation.w,
|
||||
};
|
||||
|
||||
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
|
||||
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
||||
return;
|
||||
if (closest < 0.0) {
|
||||
closest = x;
|
||||
}
|
||||
|
||||
if (x <= closest) {
|
||||
modelSelected = true;
|
||||
selectedModelID = foundModels[i];
|
||||
selectedModelProperties = properties;
|
||||
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modelSelected) {
|
||||
selectedModelProperties.oldRadius = selectedModelProperties.radius;
|
||||
selectedModelProperties.oldPosition = {
|
||||
x: selectedModelProperties.position.x,
|
||||
y: selectedModelProperties.position.y,
|
||||
z: selectedModelProperties.position.z,
|
||||
};
|
||||
selectedModelProperties.oldRotation = {
|
||||
x: selectedModelProperties.modelRotation.x,
|
||||
y: selectedModelProperties.modelRotation.y,
|
||||
z: selectedModelProperties.modelRotation.z,
|
||||
w: selectedModelProperties.modelRotation.w,
|
||||
};
|
||||
|
||||
selectedModelProperties.glowLevel = 0.1;
|
||||
Models.editModel(selectedModelID, { glowLevel: selectedModelProperties.glowLevel});
|
||||
|
||||
print("Clicked on " + selectedModelID.id + " " + modelSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseReleaseEvent.connect(function() {
|
||||
if (modelSelected) {
|
||||
Models.editModel(selectedModelID, { glowLevel: 0.0 });
|
||||
modelSelected = false;
|
||||
}
|
||||
});
|
||||
|
||||
var oldModifier = 0;
|
||||
var modifier = 0;
|
||||
var wasShifted = false;
|
||||
|
@ -632,10 +653,23 @@ function mouseMoveEvent(event) {
|
|||
Models.editModel(selectedModelID, selectedModelProperties);
|
||||
}
|
||||
|
||||
function 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" });
|
||||
}
|
||||
|
||||
function cleanupModelMenus() {
|
||||
// delete our menuitems
|
||||
Menu.removeSeparator("Edit", "Models");
|
||||
Menu.removeMenuItem("Edit", "Delete Model");
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
leftController.cleanup();
|
||||
rightController.cleanup();
|
||||
toolBar.cleanup();
|
||||
cleanupModelMenus();
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
@ -644,5 +678,26 @@ Script.update.connect(checkController);
|
|||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
|
||||
setupModelMenus();
|
||||
Menu.menuItemEvent.connect(function(menuItem){
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Delete Model") {
|
||||
if (leftController.grabbing) {
|
||||
print(" Delete Model.... leftController.modelID="+ leftController.modelID);
|
||||
Models.deleteModel(leftController.modelID);
|
||||
leftController.grabbing = false;
|
||||
} else if (rightController.grabbing) {
|
||||
print(" Delete Model.... rightController.modelID="+ rightController.modelID);
|
||||
Models.deleteModel(rightController.modelID);
|
||||
rightController.grabbing = false;
|
||||
} else if (modelSelected) {
|
||||
print(" Delete Model.... selectedModelID="+ selectedModelID);
|
||||
Models.deleteModel(selectedModelID);
|
||||
modelSelected = false;
|
||||
} else {
|
||||
print(" Delete Model.... not holding...");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ var THRUST_INCREASE_RATE = 1.05;
|
|||
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||
var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
var grabDelta = { x: 0, y: 0, z: 0};
|
||||
var grabStartPosition = { x: 0, y: 0, z: 0};
|
||||
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
|
@ -50,7 +51,7 @@ var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
|
|||
|
||||
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_BUTTON_4 = 5;
|
||||
var LEFT_BUTTON_4 = 4;
|
||||
var LEFT_BUTTON_FWD = 5;
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_BUTTON_4 = 10;
|
||||
|
@ -63,6 +64,63 @@ function printVector(text, v, decimals) {
|
|||
}
|
||||
|
||||
var debug = false;
|
||||
var RED_COLOR = { red: 255, green: 0, blue: 0 };
|
||||
var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
|
||||
var defaultPosition = { x: 0, y: 0, z: 0};
|
||||
var RADIUS = 0.05;
|
||||
var greenSphere = -1;
|
||||
var redSphere = -1;
|
||||
|
||||
function createDebugOverlay() {
|
||||
|
||||
if (greenSphere == -1) {
|
||||
greenSphere = Overlays.addOverlay("sphere", {
|
||||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: GRAY_COLOR,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
});
|
||||
redSphere = Overlays.addOverlay("sphere", {
|
||||
position: defaultPosition,
|
||||
size: RADIUS,
|
||||
color: RED_COLOR,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
solid: true,
|
||||
anchor: "MyAvatar"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function destroyDebugOverlay() {
|
||||
if (greenSphere != -1) {
|
||||
Overlays.deleteOverlay(greenSphere);
|
||||
Overlays.deleteOverlay(redSphere);
|
||||
greenSphere = -1;
|
||||
redSphere = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function displayDebug() {
|
||||
if (!(grabbingWithRightHand || grabbingWithLeftHand)) {
|
||||
if (greenSphere != -1) {
|
||||
destroyDebugOverlay();
|
||||
}
|
||||
} else {
|
||||
// update debug indicator
|
||||
if (greenSphere == -1) {
|
||||
createDebugOverlay();
|
||||
}
|
||||
|
||||
var displayOffset = { x:0, y:0.5, z:-0.5 };
|
||||
|
||||
Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } );
|
||||
Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } );
|
||||
}
|
||||
}
|
||||
|
||||
function getJoystickPosition(palm) {
|
||||
// returns CONTROLLER_ID position in avatar local frame
|
||||
|
@ -220,6 +278,8 @@ function flyWithHydra(deltaTime) {
|
|||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior(deltaTime);
|
||||
displayDebug();
|
||||
|
||||
}
|
||||
|
||||
Script.update.connect(flyWithHydra);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var alwaysLook = true; // if you want the mouse look to happen only when you click, change this to false
|
||||
var alwaysLook = false; // if you want the mouse look to happen only when you click, change this to false
|
||||
var isMouseDown = false;
|
||||
var lastX = 0;
|
||||
var lastY = 0;
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
|
@ -26,7 +29,7 @@ void main(void) {
|
|||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)),
|
||||
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
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
|
@ -19,6 +22,9 @@ void main(void) {
|
|||
// transform and store the normal for interpolation
|
||||
normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0));
|
||||
|
||||
// likewise with the position
|
||||
position = gl_ModelViewMatrix * gl_Vertex;
|
||||
|
||||
// pass along the vertex color
|
||||
gl_FrontColor = gl_Color;
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ uniform sampler2D diffuseMap;
|
|||
// the normal map texture
|
||||
uniform sampler2D normalMap;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
|
@ -38,8 +41,8 @@ void main(void) {
|
|||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)),
|
||||
viewNormal));
|
||||
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) +
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
// the tangent vector
|
||||
attribute vec3 tangent;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
|
@ -22,7 +25,8 @@ varying vec4 interpolatedTangent;
|
|||
|
||||
void main(void) {
|
||||
|
||||
// transform and store the normal and tangent for interpolation
|
||||
// transform and store the position, normal and tangent for interpolation
|
||||
interpolatedPosition = gl_ModelViewMatrix * gl_Vertex;
|
||||
interpolatedNormal = gl_ModelViewMatrix * vec4(gl_Normal, 0.0);
|
||||
interpolatedTangent = gl_ModelViewMatrix * vec4(tangent, 0.0);
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ uniform sampler2D normalMap;
|
|||
// the specular map texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
|
@ -41,8 +44,8 @@ void main(void) {
|
|||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)),
|
||||
viewNormal));
|
||||
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) *
|
||||
|
|
|
@ -17,6 +17,9 @@ uniform sampler2D diffuseMap;
|
|||
// the specular texture
|
||||
uniform sampler2D specularMap;
|
||||
|
||||
// the interpolated position in view space
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
|
@ -29,7 +32,7 @@ void main(void) {
|
|||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||
|
||||
// compute the specular component (sans exponent)
|
||||
float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)),
|
||||
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
|
||||
|
|
|
@ -19,11 +19,14 @@ uniform mat4 clusterMatrices[MAX_CLUSTERS];
|
|||
attribute vec4 clusterIndices;
|
||||
attribute vec4 clusterWeights;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 position;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
normal = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])];
|
||||
|
@ -31,7 +34,7 @@ void main(void) {
|
|||
position += clusterMatrix * gl_Vertex * clusterWeight;
|
||||
normal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight;
|
||||
}
|
||||
position = gl_ModelViewProjectionMatrix * position;
|
||||
position = gl_ModelViewMatrix * position;
|
||||
normal = normalize(gl_ModelViewMatrix * normal);
|
||||
|
||||
// pass along the vertex color
|
||||
|
@ -40,5 +43,5 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
gl_Position = position;
|
||||
gl_Position = gl_ProjectionMatrix * position;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ attribute vec3 tangent;
|
|||
attribute vec4 clusterIndices;
|
||||
attribute vec4 clusterWeights;
|
||||
|
||||
// the interpolated position
|
||||
varying vec4 interpolatedPosition;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 interpolatedNormal;
|
||||
|
||||
|
@ -29,17 +32,17 @@ varying vec4 interpolatedNormal;
|
|||
varying vec4 interpolatedTangent;
|
||||
|
||||
void main(void) {
|
||||
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
interpolatedPosition = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int i = 0; i < INDICES_PER_VERTEX; i++) {
|
||||
mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])];
|
||||
float clusterWeight = clusterWeights[i];
|
||||
position += clusterMatrix * gl_Vertex * clusterWeight;
|
||||
interpolatedPosition += clusterMatrix * gl_Vertex * clusterWeight;
|
||||
interpolatedNormal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight;
|
||||
interpolatedTangent += clusterMatrix * vec4(tangent, 0.0) * clusterWeight;
|
||||
}
|
||||
position = gl_ModelViewProjectionMatrix * position;
|
||||
interpolatedPosition = gl_ModelViewMatrix * interpolatedPosition;
|
||||
interpolatedNormal = gl_ModelViewMatrix * interpolatedNormal;
|
||||
interpolatedTangent = gl_ModelViewMatrix * interpolatedTangent;
|
||||
|
||||
|
@ -49,5 +52,5 @@ void main(void) {
|
|||
// and the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
gl_Position = position;
|
||||
gl_Position = gl_ProjectionMatrix * interpolatedPosition;
|
||||
}
|
||||
|
|
|
@ -237,12 +237,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
|
||||
|
||||
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one
|
||||
const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f;
|
||||
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
|
||||
|
||||
QTimer* locationUpdateTimer = new QTimer(this);
|
||||
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
|
||||
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
|
||||
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||
|
@ -251,9 +251,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
|
||||
connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&)));
|
||||
connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset);
|
||||
|
||||
|
||||
// connect to appropriate slots on AccountManager
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
|
||||
|
||||
QTimer* balanceUpdateTimer = new QTimer(this);
|
||||
connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
|
||||
balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);
|
||||
|
||||
connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
|
||||
connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
|
@ -2525,6 +2534,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
if (_voxelFades.size() > 0) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... voxel fades...");
|
||||
_voxelFadesLock.lockForWrite();
|
||||
for(std::vector<VoxelFade>::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) {
|
||||
fade->render();
|
||||
if(fade->isDone()) {
|
||||
|
@ -2533,6 +2543,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
++fade;
|
||||
}
|
||||
}
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// give external parties a change to hook in
|
||||
|
@ -3094,7 +3105,18 @@ void Application::updateWindowTitle(){
|
|||
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ nodeList->getDomainHandler().getHostname() + buildVersion;
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (accountManager.getAccountInfo().hasBalance()) {
|
||||
float creditBalance = accountManager.getAccountInfo().getBalance() * pow(10.0f, -8.0f);
|
||||
|
||||
QString creditBalanceString;
|
||||
creditBalanceString.sprintf("%.8f", creditBalance);
|
||||
|
||||
title += " - ₵" + creditBalanceString;
|
||||
}
|
||||
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
||||
|
@ -3188,7 +3210,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3219,7 +3243,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the particle server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3251,7 +3277,9 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
|
||||
// If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server
|
||||
|
@ -3336,7 +3364,9 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
|
|||
fade.voxelDetails = rootDetails;
|
||||
const float slightly_smaller = 0.99f;
|
||||
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
|
||||
_voxelFadesLock.lockForWrite();
|
||||
_voxelFades.push_back(fade);
|
||||
_voxelFadesLock.unlock();
|
||||
}
|
||||
}
|
||||
// store jurisdiction details for later use
|
||||
|
|
|
@ -533,6 +533,7 @@ private:
|
|||
NodeBounds _nodeBoundsDisplay;
|
||||
|
||||
std::vector<VoxelFade> _voxelFades;
|
||||
QReadWriteLock _voxelFadesLock;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
QPointer<SnapshotShareDialog> _snapshotShareDialog;
|
||||
|
|
|
@ -17,7 +17,7 @@ const float MINIMUM_ATTENUATION_TO_REFLECT = 1.0f / 256.0f;
|
|||
const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f;
|
||||
const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long
|
||||
const int DEFAULT_DIFFUSION_FANOUT = 5;
|
||||
const int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10;
|
||||
const unsigned int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10;
|
||||
const float DEFAULT_LOCAL_ATTENUATION_FACTOR = 0.125;
|
||||
const float DEFAULT_COMB_FILTER_WINDOW = 0.05f; //ms delay differential to avoid
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ static const QString JOINT_FIELD = "joint";
|
|||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
||||
|
||||
static const QString S3_URL = "http://public.highfidelity.io";
|
||||
static const QString DATA_SERVER_URL = "https://data-web.highfidelity.io";
|
||||
static const QString MODEL_URL = "/api/v1/models";
|
||||
|
||||
static const QString SETTING_NAME = "LastModelUploadLocation";
|
||||
|
|
|
@ -390,8 +390,6 @@ void MyAvatar::setGravity(const glm::vec3& gravity) {
|
|||
|
||||
AnimationHandlePointer MyAvatar::addAnimationHandle() {
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
handle->setLoop(true);
|
||||
handle->start();
|
||||
_animationHandles.append(handle);
|
||||
return handle;
|
||||
}
|
||||
|
@ -401,10 +399,12 @@ void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) {
|
|||
_animationHandles.removeOne(handle);
|
||||
}
|
||||
|
||||
void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, const QStringList& maskedJoints) {
|
||||
void MyAvatar::startAnimation(const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url),
|
||||
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(const QStringList&, maskedJoints));
|
||||
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
|
||||
Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame),
|
||||
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints));
|
||||
return;
|
||||
}
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
|
@ -412,10 +412,54 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, boo
|
|||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
handle->start();
|
||||
}
|
||||
|
||||
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url),
|
||||
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame),
|
||||
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints));
|
||||
return;
|
||||
}
|
||||
// check for a configured animation for the role
|
||||
foreach (const AnimationHandlePointer& handle, _animationHandles) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->start();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// no joy; use the parameters provided
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
handle->setRole(role);
|
||||
handle->setURL(url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
handle->start();
|
||||
}
|
||||
|
||||
void MyAvatar::stopAnimationByRole(const QString& role) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role));
|
||||
return;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::stopAnimation(const QString& url) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url));
|
||||
|
@ -424,7 +468,6 @@ void MyAvatar::stopAnimation(const QString& url) {
|
|||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
handle->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,9 +514,15 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
for (int i = 0; i < _animationHandles.size(); i++) {
|
||||
settings->setArrayIndex(i);
|
||||
const AnimationHandlePointer& pointer = _animationHandles.at(i);
|
||||
settings->setValue("role", pointer->getRole());
|
||||
settings->setValue("url", pointer->getURL());
|
||||
settings->setValue("fps", pointer->getFPS());
|
||||
settings->setValue("priority", pointer->getPriority());
|
||||
settings->setValue("loop", pointer->getLoop());
|
||||
settings->setValue("hold", pointer->getHold());
|
||||
settings->setValue("startAutomatically", pointer->getStartAutomatically());
|
||||
settings->setValue("firstFrame", pointer->getFirstFrame());
|
||||
settings->setValue("lastFrame", pointer->getLastFrame());
|
||||
settings->setValue("maskedJoints", pointer->getMaskedJoints());
|
||||
}
|
||||
settings->endArray();
|
||||
|
@ -538,9 +587,15 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
for (int i = 0; i < animationCount; i++) {
|
||||
settings->setArrayIndex(i);
|
||||
const AnimationHandlePointer& handle = _animationHandles.at(i);
|
||||
handle->setRole(settings->value("role", "idle").toString());
|
||||
handle->setURL(settings->value("url").toUrl());
|
||||
handle->setFPS(loadSetting(settings, "fps", 30.0f));
|
||||
handle->setPriority(loadSetting(settings, "priority", 1.0f));
|
||||
handle->setLoop(settings->value("loop", true).toBool());
|
||||
handle->setHold(settings->value("hold", false).toBool());
|
||||
handle->setStartAutomatically(settings->value("startAutomatically", true).toBool());
|
||||
handle->setFirstFrame(settings->value("firstFrame", 0).toInt());
|
||||
handle->setLastFrame(settings->value("lastFrame", INT_MAX).toInt());
|
||||
handle->setMaskedJoints(settings->value("maskedJoints").toStringList());
|
||||
}
|
||||
settings->endArray();
|
||||
|
@ -658,17 +713,19 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
|||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
|
||||
}
|
||||
|
||||
const float JOINT_PRIORITY = 2.0f;
|
||||
|
||||
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
|
||||
Avatar::setJointData(index, rotation);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, true, rotation);
|
||||
_skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
Avatar::clearJointData(index);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, false);
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,12 +67,21 @@ public:
|
|||
void removeAnimationHandle(const AnimationHandlePointer& handle);
|
||||
|
||||
/// Allows scripts to run animations.
|
||||
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, const QStringList& maskedJoints = QStringList());
|
||||
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
|
||||
bool hold = false, int firstFrame = 0, int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
|
||||
/// Stops an animation as identified by a URL.
|
||||
Q_INVOKABLE void stopAnimation(const QString& url);
|
||||
|
||||
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
|
||||
/// animation for the role.
|
||||
Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, int firstFrame = 0,
|
||||
int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
|
||||
/// Stops an animation identified by its role.
|
||||
Q_INVOKABLE void stopAnimationByRole(const QString& role);
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* settings);
|
||||
void loadData(QSettings* settings);
|
||||
|
|
|
@ -21,6 +21,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
|||
_owningAvatar(owningAvatar) {
|
||||
}
|
||||
|
||||
const float PALM_PRIORITY = 3.0f;
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)));
|
||||
|
@ -43,7 +45,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex);
|
||||
if (jointIndex != -1) {
|
||||
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true);
|
||||
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -58,16 +60,16 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
if (leftPalmIndex == -1) {
|
||||
// palms are not yet set, use mouse
|
||||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE);
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
} else {
|
||||
applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition());
|
||||
}
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else if (leftPalmIndex == rightPalmIndex) {
|
||||
// right hand only
|
||||
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE);
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
|
||||
} else {
|
||||
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
|
||||
|
@ -132,7 +134,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
if (jointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
setJointPosition(jointIndex, position);
|
||||
setJointPosition(jointIndex, position, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::vec3 handPosition, elbowPosition;
|
||||
|
@ -148,7 +150,8 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
|
||||
// align hand with forearm
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector));
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f),
|
||||
forearmVector), true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
|
@ -183,12 +186,14 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
|
||||
setJointRotation(parentJointIndex, palmRotation, true);
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
|
||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
setJointRotation(parentJointIndex, palmRotation, true, PALM_PRIORITY);
|
||||
_jointStates[jointIndex].rotation = glm::quat();
|
||||
|
||||
} else {
|
||||
setJointPosition(jointIndex, palm.getPosition(), palmRotation, true);
|
||||
setJointPosition(jointIndex, palm.getPosition(), palmRotation,
|
||||
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,11 +340,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
|
|||
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
||||
|
||||
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
||||
setJointRotation(shoulderJointIndex, shoulderRotation, true);
|
||||
setJointRotation(shoulderJointIndex, shoulderRotation, true, PALM_PRIORITY);
|
||||
|
||||
setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector,
|
||||
wristPosition - elbowPosition) * shoulderRotation, true);
|
||||
wristPosition - elbowPosition) * shoulderRotation, true, PALM_PRIORITY);
|
||||
|
||||
setJointRotation(jointIndex, rotation, true);
|
||||
setJointRotation(jointIndex, rotation, true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,6 @@ void SixenseManager::update(float deltaTime) {
|
|||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
|
||||
palm->setRawRotation(rotation);
|
||||
|
||||
// Compute current velocity from position change
|
||||
glm::vec3 rawVelocity;
|
||||
|
@ -130,7 +129,12 @@ void SixenseManager::update(float deltaTime) {
|
|||
rawVelocity = glm::vec3(0.0f);
|
||||
}
|
||||
palm->setRawVelocity(rawVelocity); // meters/sec
|
||||
palm->setRawPosition(position);
|
||||
|
||||
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
||||
// no latency.
|
||||
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
|
||||
palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter));
|
||||
palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter));
|
||||
|
||||
// use the velocity to determine whether there's any movement (if the hand isn't new)
|
||||
const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f;
|
||||
|
|
|
@ -203,7 +203,13 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
// TODO: should we allow modelItems to have alpha on their models?
|
||||
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
|
||||
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||
model->render(alpha, modelRenderMode);
|
||||
|
||||
if (modelItem.getGlowLevel() > 0.0f) {
|
||||
Glower glower(modelItem.getGlowLevel());
|
||||
model->render(alpha, modelRenderMode);
|
||||
} else {
|
||||
model->render(alpha, modelRenderMode);
|
||||
}
|
||||
|
||||
if (!isShadowMode && displayModelBounds) {
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
|
|
|
@ -127,5 +127,9 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
}
|
||||
|
||||
void ParticleTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
|
||||
static_cast<ParticleTree*>(_tree)->processEraseMessage(dataByteArray, sourceNode);
|
||||
if (_tree){
|
||||
_tree->lockForWrite();
|
||||
static_cast<ParticleTree*>(_tree)->processEraseMessage(dataByteArray, sourceNode);
|
||||
_tree->unlock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
|
|||
JointState state;
|
||||
state.translation = joint.translation;
|
||||
state.rotation = joint.rotation;
|
||||
state.animationDisabled = false;
|
||||
state.animationPriority = 0.0f;
|
||||
jointStates.append(state);
|
||||
}
|
||||
|
||||
|
@ -473,9 +473,18 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
|
|||
glm::abs(rotation.w - defaultRotation.w) >= EPSILON;
|
||||
}
|
||||
|
||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation) {
|
||||
void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index].rotation = valid ? rotation : _geometry->getFBXGeometry().joints.at(index).rotation;
|
||||
JointState& state = _jointStates[index];
|
||||
if (priority >= state.animationPriority) {
|
||||
if (valid) {
|
||||
state.rotation = rotation;
|
||||
state.animationPriority = priority;
|
||||
} else if (priority == state.animationPriority) {
|
||||
state.rotation = _geometry->getFBXGeometry().joints.at(index).rotation;
|
||||
state.animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,8 +544,8 @@ bool Model::getRightHandRotation(glm::quat& rotation) const {
|
|||
return getJointRotation(getRightHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::restoreLeftHandPosition(float percent) {
|
||||
return restoreJointPosition(getLeftHandJointIndex(), percent);
|
||||
bool Model::restoreLeftHandPosition(float percent, float priority) {
|
||||
return restoreJointPosition(getLeftHandJointIndex(), percent, priority);
|
||||
}
|
||||
|
||||
bool Model::getLeftShoulderPosition(glm::vec3& position) const {
|
||||
|
@ -547,8 +556,8 @@ float Model::getLeftArmLength() const {
|
|||
return getLimbLength(getLeftHandJointIndex());
|
||||
}
|
||||
|
||||
bool Model::restoreRightHandPosition(float percent) {
|
||||
return restoreJointPosition(getRightHandJointIndex(), percent);
|
||||
bool Model::restoreRightHandPosition(float percent, float priority) {
|
||||
return restoreJointPosition(getRightHandJointIndex(), percent, priority);
|
||||
}
|
||||
|
||||
bool Model::getRightShoulderPosition(glm::vec3& position) const {
|
||||
|
@ -1114,7 +1123,7 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint
|
|||
}
|
||||
|
||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) {
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1137,7 +1146,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
glm::quat endRotation;
|
||||
if (useRotation) {
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation));
|
||||
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), priority);
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
}
|
||||
|
||||
|
@ -1181,7 +1190,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
1.0f / (combinedWeight + 1.0f));
|
||||
}
|
||||
}
|
||||
applyRotationDelta(index, combinedDelta);
|
||||
applyRotationDelta(index, combinedDelta, priority);
|
||||
glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation);
|
||||
endPosition = actualDelta * jointVector + jointPosition;
|
||||
if (useRotation) {
|
||||
|
@ -1199,15 +1208,17 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind) {
|
||||
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation *
|
||||
glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation :
|
||||
_geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation);
|
||||
state.animationDisabled = true;
|
||||
if (priority >= state.animationPriority) {
|
||||
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation *
|
||||
glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation :
|
||||
_geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation);
|
||||
state.animationPriority = priority;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1228,7 +1239,7 @@ void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) {
|
|||
state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation;
|
||||
}
|
||||
|
||||
bool Model::restoreJointPosition(int jointIndex, float percent) {
|
||||
bool Model::restoreJointPosition(int jointIndex, float percent, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1237,10 +1248,12 @@ bool Model::restoreJointPosition(int jointIndex, float percent) {
|
|||
|
||||
foreach (int index, freeLineage) {
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXJoint& joint = geometry.joints.at(index);
|
||||
state.rotation = safeMix(state.rotation, joint.rotation, percent);
|
||||
state.translation = glm::mix(state.translation, joint.translation, percent);
|
||||
state.animationDisabled = false;
|
||||
if (priority == state.animationPriority) {
|
||||
const FBXJoint& joint = geometry.joints.at(index);
|
||||
state.rotation = safeMix(state.rotation, joint.rotation, percent);
|
||||
state.translation = glm::mix(state.translation, joint.translation, percent);
|
||||
state.animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1259,8 +1272,12 @@ float Model::getLimbLength(int jointIndex) const {
|
|||
return length;
|
||||
}
|
||||
|
||||
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) {
|
||||
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
if (priority < state.animationPriority) {
|
||||
return;
|
||||
}
|
||||
state.animationPriority = priority;
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) &&
|
||||
joint.rotationMax == glm::vec3(PI, PI, PI))) {
|
||||
|
@ -1274,7 +1291,6 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons
|
|||
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
|
||||
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
|
||||
state.rotation = newRotation;
|
||||
state.animationDisabled = true;
|
||||
}
|
||||
|
||||
const int BALL_SUBDIVISIONS = 10;
|
||||
|
@ -1665,7 +1681,7 @@ void AnimationHandle::setURL(const QUrl& url) {
|
|||
|
||||
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||
if (handle->getPriority() < (*it)->getPriority()) {
|
||||
if (handle->getPriority() > (*it)->getPriority()) {
|
||||
handles.insert(it, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -1674,12 +1690,25 @@ static void insertSorted(QList<AnimationHandlePointer>& handles, const Animation
|
|||
}
|
||||
|
||||
void AnimationHandle::setPriority(float priority) {
|
||||
if (_priority != priority) {
|
||||
_priority = priority;
|
||||
if (_running) {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
if (_priority == priority) {
|
||||
return;
|
||||
}
|
||||
if (_running) {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
if (priority < _priority) {
|
||||
replaceMatchingPriorities(priority);
|
||||
}
|
||||
_priority = priority;
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
|
||||
} else {
|
||||
_priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
|
||||
if ((_startAutomatically = startAutomatically) && !_running) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1689,15 +1718,24 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
|
|||
}
|
||||
|
||||
void AnimationHandle::setRunning(bool running) {
|
||||
if (_running == running) {
|
||||
if (running) {
|
||||
// move back to the beginning
|
||||
_frameIndex = _firstFrame;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((_running = running)) {
|
||||
if (!_model->_runningAnimations.contains(_self)) {
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
}
|
||||
_frameIndex = 0.0f;
|
||||
_frameIndex = _firstFrame;
|
||||
|
||||
} else {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
replaceMatchingPriorities(0.0f);
|
||||
}
|
||||
emit runningChanged(_running);
|
||||
}
|
||||
|
||||
AnimationHandle::AnimationHandle(Model* model) :
|
||||
|
@ -1706,6 +1744,10 @@ AnimationHandle::AnimationHandle(Model* model) :
|
|||
_fps(30.0f),
|
||||
_priority(1.0f),
|
||||
_loop(false),
|
||||
_hold(false),
|
||||
_startAutomatically(false),
|
||||
_firstFrame(0),
|
||||
_lastFrame(INT_MAX),
|
||||
_running(false) {
|
||||
}
|
||||
|
||||
|
@ -1736,36 +1778,55 @@ void AnimationHandle::simulate(float deltaTime) {
|
|||
stop();
|
||||
return;
|
||||
}
|
||||
int ceilFrameIndex = (int)glm::ceil(_frameIndex);
|
||||
if (!_loop && ceilFrameIndex >= animationGeometry.animationFrames.size()) {
|
||||
int lastFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - 1);
|
||||
int firstFrameIndex = qMin(_firstFrame, lastFrameIndex);
|
||||
if ((!_loop && _frameIndex >= lastFrameIndex) || firstFrameIndex == lastFrameIndex) {
|
||||
// passed the end; apply the last frame
|
||||
const FBXAnimationFrame& frame = animationGeometry.animationFrames.last();
|
||||
const FBXAnimationFrame& frame = animationGeometry.animationFrames.at(lastFrameIndex);
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
Model::JointState& state = _model->_jointStates[mapping];
|
||||
if (!state.animationDisabled) {
|
||||
if (_priority >= state.animationPriority) {
|
||||
state.rotation = frame.rotations.at(i);
|
||||
state.animationPriority = _priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop();
|
||||
if (!_hold) {
|
||||
stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int frameCount = lastFrameIndex - firstFrameIndex + 1;
|
||||
_frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount);
|
||||
|
||||
// blend between the closest two frames
|
||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at(
|
||||
ceilFrameIndex % animationGeometry.animationFrames.size());
|
||||
firstFrameIndex + ((int)glm::ceil(_frameIndex) - firstFrameIndex) % frameCount);
|
||||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at(
|
||||
(int)glm::floor(_frameIndex) % animationGeometry.animationFrames.size());
|
||||
firstFrameIndex + ((int)glm::floor(_frameIndex) - firstFrameIndex) % frameCount);
|
||||
float frameFraction = glm::fract(_frameIndex);
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
Model::JointState& state = _model->_jointStates[mapping];
|
||||
if (!state.animationDisabled) {
|
||||
if (_priority >= state.animationPriority) {
|
||||
state.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
state.animationPriority = _priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationHandle::replaceMatchingPriorities(float newPriority) {
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
Model::JointState& state = _model->_jointStates[mapping];
|
||||
if (_priority == state.animationPriority) {
|
||||
state.animationPriority = newPriority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
bool getJointState(int index, glm::quat& rotation) const;
|
||||
|
||||
/// Sets the joint state at the specified index.
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat());
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
|
||||
|
||||
/// Returns the index of the left hand joint, or -1 if not found.
|
||||
int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }
|
||||
|
@ -166,7 +166,7 @@ public:
|
|||
/// Restores some percentage of the default position of the left hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool restoreLeftHandPosition(float percent = 1.0f);
|
||||
bool restoreLeftHandPosition(float percent = 1.0f, float priority = 1.0f);
|
||||
|
||||
/// Gets the position of the left shoulder.
|
||||
/// \return whether or not the left shoulder joint was found
|
||||
|
@ -178,7 +178,7 @@ public:
|
|||
/// Restores some percentage of the default position of the right hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool restoreRightHandPosition(float percent = 1.0f);
|
||||
bool restoreRightHandPosition(float percent = 1.0f, float priority = 1.0f);
|
||||
|
||||
/// Gets the position of the right shoulder.
|
||||
/// \return whether or not the right shoulder joint was found
|
||||
|
@ -254,7 +254,7 @@ protected:
|
|||
glm::quat rotation; // rotation relative to parent
|
||||
glm::mat4 transform; // rotation to world frame + translation in model frame
|
||||
glm::quat combinedRotation; // rotation from joint local to world frame
|
||||
bool animationDisabled; // if true, animations do not affect this joint
|
||||
float animationPriority; // the priority of the animation affecting this joint
|
||||
};
|
||||
|
||||
bool _shapesAreDirty;
|
||||
|
@ -290,8 +290,8 @@ protected:
|
|||
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
|
||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false);
|
||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
|
||||
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false, float priority = 1.0f);
|
||||
|
||||
void setJointTranslation(int jointIndex, const glm::vec3& translation);
|
||||
|
||||
|
@ -299,13 +299,13 @@ protected:
|
|||
/// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
||||
/// the original position
|
||||
/// \return true if the joint was found
|
||||
bool restoreJointPosition(int jointIndex, float percent = 1.0f);
|
||||
bool restoreJointPosition(int jointIndex, float percent = 1.0f, float priority = 0.0f);
|
||||
|
||||
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
|
||||
/// first free ancestor.
|
||||
float getLimbLength(int jointIndex) const;
|
||||
|
||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
|
||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
|
||||
|
@ -381,6 +381,9 @@ class AnimationHandle : public QObject {
|
|||
|
||||
public:
|
||||
|
||||
void setRole(const QString& role) { _role = role; }
|
||||
const QString& getRole() const { return _role; }
|
||||
|
||||
void setURL(const QUrl& url);
|
||||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
|
@ -393,12 +396,30 @@ public:
|
|||
void setLoop(bool loop) { _loop = loop; }
|
||||
bool getLoop() const { return _loop; }
|
||||
|
||||
void setHold(bool hold) { _hold = hold; }
|
||||
bool getHold() const { return _hold; }
|
||||
|
||||
void setStartAutomatically(bool startAutomatically);
|
||||
bool getStartAutomatically() const { return _startAutomatically; }
|
||||
|
||||
void setFirstFrame(int firstFrame) { _firstFrame = firstFrame; }
|
||||
int getFirstFrame() const { return _firstFrame; }
|
||||
|
||||
void setLastFrame(int lastFrame) { _lastFrame = lastFrame; }
|
||||
int getLastFrame() const { return _lastFrame; }
|
||||
|
||||
void setMaskedJoints(const QStringList& maskedJoints);
|
||||
const QStringList& getMaskedJoints() const { return _maskedJoints; }
|
||||
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const { return _running; }
|
||||
|
||||
signals:
|
||||
|
||||
void runningChanged(bool running);
|
||||
|
||||
public slots:
|
||||
|
||||
void start() { setRunning(true); }
|
||||
void stop() { setRunning(false); }
|
||||
|
||||
|
@ -409,14 +430,20 @@ private:
|
|||
AnimationHandle(Model* model);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
|
||||
void replaceMatchingPriorities(float newPriority);
|
||||
|
||||
Model* _model;
|
||||
WeakAnimationHandlePointer _self;
|
||||
AnimationPointer _animation;
|
||||
QString _role;
|
||||
QUrl _url;
|
||||
float _fps;
|
||||
float _priority;
|
||||
bool _loop;
|
||||
bool _hold;
|
||||
bool _startAutomatically;
|
||||
int _firstFrame;
|
||||
int _lastFrame;
|
||||
QStringList _maskedJoints;
|
||||
bool _running;
|
||||
QVector<int> _jointMappings;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFileDialog>
|
||||
|
@ -79,6 +81,13 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
|
|||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
setLayout(layout);
|
||||
|
||||
layout->addRow("Role:", _role = new QComboBox());
|
||||
_role->addItem("idle");
|
||||
_role->addItem("sit");
|
||||
_role->setEditable(true);
|
||||
_role->setCurrentText(handle->getRole());
|
||||
connect(_role, SIGNAL(currentTextChanged(const QString&)), SLOT(updateHandle()));
|
||||
|
||||
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||
layout->addRow("URL:", urlBox);
|
||||
urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1);
|
||||
|
@ -107,9 +116,40 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
|
|||
maskedJointBox->addWidget(_chooseMaskedJoints = new QPushButton("Choose"));
|
||||
connect(_chooseMaskedJoints, SIGNAL(clicked(bool)), SLOT(chooseMaskedJoints()));
|
||||
|
||||
layout->addRow("Loop:", _loop = new QCheckBox());
|
||||
_loop->setChecked(handle->getLoop());
|
||||
connect(_loop, SIGNAL(toggled(bool)), SLOT(updateHandle()));
|
||||
|
||||
layout->addRow("Hold:", _hold = new QCheckBox());
|
||||
_hold->setChecked(handle->getHold());
|
||||
connect(_hold, SIGNAL(toggled(bool)), SLOT(updateHandle()));
|
||||
|
||||
layout->addRow("Start Automatically:", _startAutomatically = new QCheckBox());
|
||||
_startAutomatically->setChecked(handle->getStartAutomatically());
|
||||
connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle()));
|
||||
|
||||
layout->addRow("First Frame:", _firstFrame = new QSpinBox());
|
||||
_firstFrame->setMaximum(INT_MAX);
|
||||
_firstFrame->setValue(handle->getFirstFrame());
|
||||
connect(_firstFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle()));
|
||||
|
||||
layout->addRow("Last Frame:", _lastFrame = new QSpinBox());
|
||||
_lastFrame->setMaximum(INT_MAX);
|
||||
_lastFrame->setValue(handle->getLastFrame());
|
||||
connect(_lastFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle()));
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout();
|
||||
layout->addRow(buttons);
|
||||
buttons->addWidget(_start = new QPushButton("Start"));
|
||||
_handle->connect(_start, SIGNAL(clicked(bool)), SLOT(start()));
|
||||
buttons->addWidget(_stop = new QPushButton("Stop"));
|
||||
_handle->connect(_stop, SIGNAL(clicked(bool)), SLOT(stop()));
|
||||
QPushButton* remove = new QPushButton("Delete");
|
||||
layout->addRow(remove);
|
||||
buttons->addWidget(remove);
|
||||
connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle()));
|
||||
|
||||
_stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
|
||||
_stop->setEnabled(_handle->isRunning());
|
||||
}
|
||||
|
||||
void AnimationPanel::chooseURL() {
|
||||
|
@ -146,9 +186,15 @@ void AnimationPanel::chooseMaskedJoints() {
|
|||
}
|
||||
|
||||
void AnimationPanel::updateHandle() {
|
||||
_handle->setRole(_role->currentText());
|
||||
_handle->setURL(_url->text());
|
||||
_handle->setFPS(_fps->value());
|
||||
_handle->setPriority(_priority->value());
|
||||
_handle->setLoop(_loop->isChecked());
|
||||
_handle->setHold(_hold->isChecked());
|
||||
_handle->setStartAutomatically(_startAutomatically->isChecked());
|
||||
_handle->setFirstFrame(_firstFrame->value());
|
||||
_handle->setLastFrame(_lastFrame->value());
|
||||
_handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*")));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#include "avatar/MyAvatar.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDoubleSpinner;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QVBoxLayout;
|
||||
|
||||
/// Allows users to edit the avatar animations.
|
||||
|
@ -61,11 +64,19 @@ private:
|
|||
|
||||
AnimationsDialog* _dialog;
|
||||
AnimationHandlePointer _handle;
|
||||
QComboBox* _role;
|
||||
QLineEdit* _url;
|
||||
QDoubleSpinBox* _fps;
|
||||
QDoubleSpinBox* _priority;
|
||||
QCheckBox* _loop;
|
||||
QCheckBox* _hold;
|
||||
QCheckBox* _startAutomatically;
|
||||
QSpinBox* _firstFrame;
|
||||
QSpinBox* _lastFrame;
|
||||
QLineEdit* _maskedJoints;
|
||||
QPushButton* _chooseMaskedJoints;
|
||||
QPushButton* _start;
|
||||
QPushButton* _stop;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "LoginDialog.h"
|
||||
|
||||
const QString FORGOT_PASSWORD_URL = "https://data-web.highfidelity.io/password/new";
|
||||
const QString FORGOT_PASSWORD_URL = "https://data.highfidelity.io/password/new";
|
||||
|
||||
LoginDialog::LoginDialog(QWidget* parent) :
|
||||
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
|
||||
|
|
|
@ -57,6 +57,11 @@ void ScriptEditorWidget::onScriptModified() {
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptEditorWidget::onScriptEnding() {
|
||||
// signals will automatically be disonnected when the _scriptEngine is deleted later
|
||||
_scriptEngine = NULL;
|
||||
}
|
||||
|
||||
bool ScriptEditorWidget::isModified() {
|
||||
return _scriptEditorWidgetUI->scriptEdit->document()->isModified();
|
||||
}
|
||||
|
@ -69,11 +74,13 @@ bool ScriptEditorWidget::setRunning(bool run) {
|
|||
if (run && !save()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean-up old connections.
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
disconnect(_scriptEngine, &ScriptEngine::scriptEnding, this, &ScriptEditorWidget::onScriptEnding);
|
||||
}
|
||||
|
||||
if (run) {
|
||||
|
@ -83,6 +90,7 @@ bool ScriptEditorWidget::setRunning(bool run) {
|
|||
// Make new connections.
|
||||
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
connect(_scriptEngine, &ScriptEngine::scriptEnding, this, &ScriptEditorWidget::onScriptEnding);
|
||||
} else {
|
||||
Application::getInstance()->stopScript(_currentScript);
|
||||
_scriptEngine = NULL;
|
||||
|
@ -125,6 +133,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
disconnect(_scriptEngine, &ScriptEngine::scriptEnding, this, &ScriptEditorWidget::onScriptEnding);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
|
@ -144,6 +153,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError);
|
||||
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint);
|
||||
connect(_scriptEngine, &ScriptEngine::scriptEnding, this, &ScriptEditorWidget::onScriptEnding);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ private slots:
|
|||
void onScriptError(const QString& message);
|
||||
void onScriptPrint(const QString& message);
|
||||
void onScriptModified();
|
||||
void onScriptEnding();
|
||||
|
||||
private:
|
||||
Ui::ScriptEditorWidget* _scriptEditorWidgetUI;
|
||||
|
|
|
@ -39,25 +39,35 @@ void Overlays::init(QGLWidget* parent) {
|
|||
}
|
||||
|
||||
void Overlays::update(float deltatime) {
|
||||
foreach (Overlay* thisOverlay, _overlays2D) {
|
||||
thisOverlay->update(deltatime);
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
||||
thisOverlay->update(deltatime);
|
||||
}
|
||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
||||
thisOverlay->update(deltatime);
|
||||
}
|
||||
}
|
||||
foreach (Overlay* thisOverlay, _overlays3D) {
|
||||
thisOverlay->update(deltatime);
|
||||
}
|
||||
while (!_overlaysToDelete.isEmpty()) {
|
||||
delete _overlaysToDelete.takeLast();
|
||||
|
||||
if (!_overlaysToDelete.isEmpty()) {
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
do {
|
||||
delete _overlaysToDelete.takeLast();
|
||||
} while (!_overlaysToDelete.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Overlays::render2D() {
|
||||
QReadLocker lock(&_lock);
|
||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
||||
thisOverlay->render();
|
||||
}
|
||||
}
|
||||
|
||||
void Overlays::render3D() {
|
||||
QReadLocker lock(&_lock);
|
||||
if (_overlays3D.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -96,7 +106,6 @@ void Overlays::render3D() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: make multi-threaded safe
|
||||
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
|
||||
unsigned int thisID = 0;
|
||||
bool created = false;
|
||||
|
@ -140,6 +149,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
|
|||
}
|
||||
|
||||
if (created) {
|
||||
QWriteLocker lock(&_lock);
|
||||
thisID = _nextOverlayID;
|
||||
_nextOverlayID++;
|
||||
if (is3D) {
|
||||
|
@ -152,9 +162,9 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope
|
|||
return thisID;
|
||||
}
|
||||
|
||||
// TODO: make multi-threaded safe
|
||||
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
||||
Overlay* thisOverlay = NULL;
|
||||
QWriteLocker lock(&_lock);
|
||||
if (_overlays2D.contains(id)) {
|
||||
thisOverlay = _overlays2D[id];
|
||||
} else if (_overlays3D.contains(id)) {
|
||||
|
@ -167,21 +177,26 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO: make multi-threaded safe
|
||||
void Overlays::deleteOverlay(unsigned int id) {
|
||||
Overlay* overlayToDelete;
|
||||
if (_overlays2D.contains(id)) {
|
||||
overlayToDelete = _overlays2D.take(id);
|
||||
} else if (_overlays3D.contains(id)) {
|
||||
overlayToDelete = _overlays3D.take(id);
|
||||
} else {
|
||||
return;
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
if (_overlays2D.contains(id)) {
|
||||
overlayToDelete = _overlays2D.take(id);
|
||||
} else if (_overlays3D.contains(id)) {
|
||||
overlayToDelete = _overlays3D.take(id);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
_overlaysToDelete.push_back(overlayToDelete);
|
||||
}
|
||||
|
||||
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
QReadLocker lock(&_lock);
|
||||
QMapIterator<unsigned int, Overlay*> i(_overlays2D);
|
||||
i.toBack();
|
||||
while (i.hasPrevious()) {
|
||||
|
|
|
@ -45,6 +45,8 @@ private:
|
|||
QList<Overlay*> _overlaysToDelete;
|
||||
unsigned int _nextOverlayID;
|
||||
QGLWidget* _parent;
|
||||
QReadWriteLock _lock;
|
||||
QReadWriteLock _deleteLock;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
<string><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
Invalid username or password. <a href="https://data-web.highfidelity.io/password/new">Recover?</a></string>
|
||||
Invalid username or password. <a href="https://data.highfidelity.io/password/new">Recover?</a></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
|
@ -458,7 +458,7 @@ border-radius: 4px; padding-top: 1px;</string>
|
|||
<string><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
<a href="https://data-web.highfidelity.io/password/new">Recover password?</a></string>
|
||||
<a href="https://data.highfidelity.io/password/new">Recover password?</a></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
const char* HTTPConnection::StatusCode200 = "200 OK";
|
||||
const char* HTTPConnection::StatusCode301 = "301 Moved Permanently";
|
||||
const char* HTTPConnection::StatusCode302 = "302 Found";
|
||||
const char* HTTPConnection::StatusCode400 = "400 Bad Request";
|
||||
const char* HTTPConnection::StatusCode404 = "404 Not Found";
|
||||
const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1";
|
||||
|
|
|
@ -44,6 +44,7 @@ class HTTPConnection : public QObject {
|
|||
public:
|
||||
static const char* StatusCode200;
|
||||
static const char* StatusCode301;
|
||||
static const char* StatusCode302;
|
||||
static const char* StatusCode400;
|
||||
static const char* StatusCode404;
|
||||
static const char* DefaultContentType;
|
||||
|
|
|
@ -85,12 +85,13 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties&
|
|||
_shouldDie = false;
|
||||
_modelURL = MODEL_DEFAULT_MODEL_URL;
|
||||
_modelRotation = MODEL_DEFAULT_MODEL_ROTATION;
|
||||
|
||||
|
||||
// animation related
|
||||
_animationURL = MODEL_DEFAULT_ANIMATION_URL;
|
||||
_animationIsPlaying = false;
|
||||
_animationFrameIndex = 0.0f;
|
||||
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||
_glowLevel = 0.0f;
|
||||
|
||||
_jointMappingCompleted = false;
|
||||
_lastAnimated = now;
|
||||
|
@ -125,6 +126,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t
|
|||
_animationIsPlaying = false;
|
||||
_animationFrameIndex = 0.0f;
|
||||
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||
_glowLevel = 0.0f;
|
||||
_jointMappingCompleted = false;
|
||||
_lastAnimated = now;
|
||||
}
|
||||
|
@ -802,6 +804,7 @@ ModelItemProperties::ModelItemProperties() :
|
|||
_animationIsPlaying(false),
|
||||
_animationFrameIndex(0.0),
|
||||
_animationFPS(MODEL_DEFAULT_ANIMATION_FPS),
|
||||
_glowLevel(0.0f),
|
||||
|
||||
_id(UNKNOWN_MODEL_ID),
|
||||
_idSet(false),
|
||||
|
@ -817,6 +820,7 @@ ModelItemProperties::ModelItemProperties() :
|
|||
_animationIsPlayingChanged(false),
|
||||
_animationFrameIndexChanged(false),
|
||||
_animationFPSChanged(false),
|
||||
_glowLevelChanged(false),
|
||||
_defaultSettings(true)
|
||||
{
|
||||
}
|
||||
|
@ -890,6 +894,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const
|
|||
properties.setProperty("animationIsPlaying", _animationIsPlaying);
|
||||
properties.setProperty("animationFrameIndex", _animationFrameIndex);
|
||||
properties.setProperty("animationFPS", _animationFPS);
|
||||
properties.setProperty("glowLevel", _glowLevel);
|
||||
|
||||
if (_idSet) {
|
||||
properties.setProperty("id", _id);
|
||||
|
@ -1015,7 +1020,7 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
|
|||
_animationFrameIndexChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QScriptValue animationFPS = object.property("animationFPS");
|
||||
if (animationFPS.isValid()) {
|
||||
float newFPS;
|
||||
|
@ -1025,6 +1030,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) {
|
|||
_animationFPSChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue glowLevel = object.property("glowLevel");
|
||||
if (glowLevel.isValid()) {
|
||||
float newGlowLevel;
|
||||
newGlowLevel = glowLevel.toVariant().toFloat();
|
||||
if (_defaultSettings || newGlowLevel != _glowLevel) {
|
||||
_glowLevel = newGlowLevel;
|
||||
_glowLevelChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -1075,11 +1090,16 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
|
|||
modelItem.setAnimationFrameIndex(_animationFrameIndex);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (_animationFPSChanged) {
|
||||
modelItem.setAnimationFPS(_animationFPS);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (_glowLevelChanged) {
|
||||
modelItem.setGlowLevel(_glowLevel);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -1104,6 +1124,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
|
|||
_animationIsPlaying = modelItem.getAnimationIsPlaying();
|
||||
_animationFrameIndex = modelItem.getAnimationFrameIndex();
|
||||
_animationFPS = modelItem.getAnimationFPS();
|
||||
_glowLevel = modelItem.getGlowLevel();
|
||||
|
||||
_id = modelItem.getID();
|
||||
_idSet = true;
|
||||
|
@ -1119,6 +1140,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
|
|||
_animationIsPlayingChanged = false;
|
||||
_animationFrameIndexChanged = false;
|
||||
_animationFPSChanged = false;
|
||||
_glowLevelChanged = false;
|
||||
_defaultSettings = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
float getAnimationFrameIndex() const { return _animationFrameIndex; }
|
||||
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
|
||||
float getAnimationFPS() const { return _animationFPS; }
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
|
||||
quint64 getLastEdited() const { return _lastEdited; }
|
||||
uint16_t getChangedBits() const;
|
||||
|
@ -100,6 +101,7 @@ public:
|
|||
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; }
|
||||
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
|
||||
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
|
||||
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
|
||||
|
||||
/// used by ModelScriptingInterface to return ModelItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
|
||||
|
@ -119,6 +121,7 @@ private:
|
|||
bool _animationIsPlaying;
|
||||
float _animationFrameIndex;
|
||||
float _animationFPS;
|
||||
float _glowLevel;
|
||||
|
||||
uint32_t _id;
|
||||
bool _idSet;
|
||||
|
@ -135,6 +138,7 @@ private:
|
|||
bool _animationIsPlayingChanged;
|
||||
bool _animationFrameIndexChanged;
|
||||
bool _animationFPSChanged;
|
||||
bool _glowLevelChanged;
|
||||
bool _defaultSettings;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ModelItemProperties);
|
||||
|
@ -206,6 +210,7 @@ public:
|
|||
const glm::quat& getModelRotation() const { return _modelRotation; }
|
||||
bool hasAnimation() const { return !_animationURL.isEmpty(); }
|
||||
const QString& getAnimationURL() const { return _animationURL; }
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
|
||||
ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); }
|
||||
ModelItemProperties getProperties() const;
|
||||
|
@ -248,6 +253,7 @@ public:
|
|||
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
|
||||
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
|
||||
void setAnimationFPS(float value) { _animationFPS = value; }
|
||||
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
|
||||
|
||||
void setProperties(const ModelItemProperties& properties);
|
||||
|
||||
|
@ -293,6 +299,8 @@ protected:
|
|||
// model related items
|
||||
QString _modelURL;
|
||||
glm::quat _modelRotation;
|
||||
|
||||
float _glowLevel;
|
||||
|
||||
uint32_t _creatorTokenID;
|
||||
bool _newlyCreated;
|
||||
|
|
|
@ -61,6 +61,8 @@ AccountManager::AccountManager() :
|
|||
|
||||
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
|
||||
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
|
||||
|
||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||
}
|
||||
|
||||
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
|
||||
|
@ -69,6 +71,9 @@ void AccountManager::logout() {
|
|||
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
|
||||
_accountInfo = DataServerAccountInfo();
|
||||
|
||||
emit balanceChanged(0);
|
||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
|
@ -82,6 +87,21 @@ void AccountManager::logout() {
|
|||
emit usernameChanged(QString());
|
||||
}
|
||||
|
||||
void AccountManager::updateBalance() {
|
||||
if (hasValidAccessToken()) {
|
||||
// ask our auth endpoint for our balance
|
||||
JSONCallbackParameters callbackParameters;
|
||||
callbackParameters.jsonCallbackReceiver = &_accountInfo;
|
||||
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
|
||||
|
||||
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::accountInfoBalanceChanged(quint64 newBalance) {
|
||||
emit balanceChanged(newBalance);
|
||||
}
|
||||
|
||||
void AccountManager::setAuthURL(const QUrl& authURL) {
|
||||
if (_authURL != authURL) {
|
||||
_authURL = authURL;
|
||||
|
|
|
@ -63,6 +63,8 @@ public slots:
|
|||
void requestFinished();
|
||||
void requestError(QNetworkReply::NetworkError error);
|
||||
void logout();
|
||||
void updateBalance();
|
||||
void accountInfoBalanceChanged(quint64 newBalance);
|
||||
signals:
|
||||
void authRequired();
|
||||
void authEndpointChanged();
|
||||
|
@ -71,6 +73,7 @@ signals:
|
|||
void loginComplete(const QUrl& authURL);
|
||||
void loginFailed();
|
||||
void logoutComplete();
|
||||
void balanceChanged(quint64 newBalance);
|
||||
private slots:
|
||||
void processReply();
|
||||
private:
|
||||
|
|
|
@ -17,7 +17,9 @@ DataServerAccountInfo::DataServerAccountInfo() :
|
|||
_accessToken(),
|
||||
_username(),
|
||||
_xmppPassword(),
|
||||
_discourseApiKey()
|
||||
_discourseApiKey(),
|
||||
_balance(0),
|
||||
_hasBalance(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -25,7 +27,9 @@ DataServerAccountInfo::DataServerAccountInfo() :
|
|||
DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
|
||||
_accessToken(jsonObject),
|
||||
_username(),
|
||||
_xmppPassword()
|
||||
_xmppPassword(),
|
||||
_balance(0),
|
||||
_hasBalance(false)
|
||||
{
|
||||
QJsonObject userJSONObject = jsonObject["user"].toObject();
|
||||
setUsername(userJSONObject["username"].toString());
|
||||
|
@ -38,6 +42,8 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI
|
|||
_username = otherInfo._username;
|
||||
_xmppPassword = otherInfo._xmppPassword;
|
||||
_discourseApiKey = otherInfo._discourseApiKey;
|
||||
_balance = otherInfo._balance;
|
||||
_hasBalance = otherInfo._hasBalance;
|
||||
}
|
||||
|
||||
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
|
||||
|
@ -53,6 +59,8 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
|||
swap(_username, otherInfo._username);
|
||||
swap(_xmppPassword, otherInfo._xmppPassword);
|
||||
swap(_discourseApiKey, otherInfo._discourseApiKey);
|
||||
swap(_balance, otherInfo._balance);
|
||||
swap(_hasBalance, otherInfo._hasBalance);
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setUsername(const QString& username) {
|
||||
|
@ -75,6 +83,22 @@ void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
|
|||
}
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setBalance(quint64 balance) {
|
||||
if (!_hasBalance || _balance != balance) {
|
||||
_balance = balance;
|
||||
_hasBalance = true;
|
||||
|
||||
emit balanceChanged(_balance);
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toInt();
|
||||
setBalance(balanceInSatoshis);
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
||||
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey;
|
||||
return out;
|
||||
|
|
|
@ -34,9 +34,17 @@ public:
|
|||
|
||||
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
|
||||
void setDiscourseApiKey(const QString& discourseApiKey);
|
||||
|
||||
quint64 getBalance() const { return _balance; }
|
||||
void setBalance(quint64 balance);
|
||||
bool hasBalance() const { return _hasBalance; }
|
||||
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
|
||||
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
|
||||
|
||||
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
|
||||
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
|
||||
signals:
|
||||
quint64 balanceChanged(quint64 newBalance);
|
||||
private:
|
||||
void swap(DataServerAccountInfo& otherInfo);
|
||||
|
||||
|
@ -44,6 +52,8 @@ private:
|
|||
QString _username;
|
||||
QString _xmppPassword;
|
||||
QString _discourseApiKey;
|
||||
quint64 _balance;
|
||||
bool _hasBalance;
|
||||
};
|
||||
|
||||
#endif // hifi_DataServerAccountInfo_h
|
||||
|
|
|
@ -33,7 +33,7 @@ const char SOLO_NODE_TYPES[2] = {
|
|||
NodeType::AudioMixer
|
||||
};
|
||||
|
||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data-web.highfidelity.io");
|
||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io");
|
||||
|
||||
LimitedNodeList* LimitedNodeList::_sharedInstance = NULL;
|
||||
|
||||
|
|
|
@ -20,8 +20,9 @@ ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeEleme
|
|||
|
||||
ParticleTreeElement::~ParticleTreeElement() {
|
||||
_voxelMemoryUsage -= sizeof(ParticleTreeElement);
|
||||
delete _particles;
|
||||
QList<Particle>* tmpParticles = _particles;
|
||||
_particles = NULL;
|
||||
delete tmpParticles;
|
||||
}
|
||||
|
||||
// This will be called primarily on addChildAt(), which means we're adding a child of our
|
||||
|
@ -277,12 +278,14 @@ const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
|||
|
||||
bool ParticleTreeElement::removeParticleWithID(uint32_t id) {
|
||||
bool foundParticle = false;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if ((*_particles)[i].getID() == id) {
|
||||
foundParticle = true;
|
||||
_particles->removeAt(i);
|
||||
break;
|
||||
if (_particles) {
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if ((*_particles)[i].getID() == id) {
|
||||
foundParticle = true;
|
||||
_particles->removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundParticle;
|
||||
|
|
|
@ -276,7 +276,7 @@ unsigned char* pointToOctalCode(float x, float y, float z, float s) {
|
|||
unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b ) {
|
||||
|
||||
// special case for size 1, the root node
|
||||
if (s >= 1.0) {
|
||||
if (s >= 1.0f) {
|
||||
unsigned char* voxelOut = new unsigned char;
|
||||
*voxelOut = 0;
|
||||
return voxelOut;
|
||||
|
@ -289,7 +289,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r,
|
|||
// voxel of size S.
|
||||
unsigned int voxelSizeInOctets = 1;
|
||||
while (sTest > s) {
|
||||
sTest /= 2.0;
|
||||
sTest /= 2.0f;
|
||||
voxelSizeInOctets++;
|
||||
}
|
||||
|
||||
|
@ -314,11 +314,11 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r,
|
|||
if (x >= xTest) {
|
||||
//<write 1 bit>
|
||||
byte = (byte << 1) | true;
|
||||
xTest += sTest/2.0;
|
||||
xTest += sTest/2.0f;
|
||||
} else {
|
||||
//<write 0 bit;>
|
||||
byte = (byte << 1) | false;
|
||||
xTest -= sTest/2.0;
|
||||
xTest -= sTest/2.0f;
|
||||
}
|
||||
bitInByteNDX++;
|
||||
// If we've reached the last bit of the byte, then we want to copy this byte
|
||||
|
@ -333,11 +333,11 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r,
|
|||
if (y >= yTest) {
|
||||
//<write 1 bit>
|
||||
byte = (byte << 1) | true;
|
||||
yTest += sTest/2.0;
|
||||
yTest += sTest/2.0f;
|
||||
} else {
|
||||
//<write 0 bit;>
|
||||
byte = (byte << 1) | false;
|
||||
yTest -= sTest/2.0;
|
||||
yTest -= sTest/2.0f;
|
||||
}
|
||||
bitInByteNDX++;
|
||||
// If we've reached the last bit of the byte, then we want to copy this byte
|
||||
|
@ -352,11 +352,11 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r,
|
|||
if (z >= zTest) {
|
||||
//<write 1 bit>
|
||||
byte = (byte << 1) | true;
|
||||
zTest += sTest/2.0;
|
||||
zTest += sTest/2.0f;
|
||||
} else {
|
||||
//<write 0 bit;>
|
||||
byte = (byte << 1) | false;
|
||||
zTest -= sTest/2.0;
|
||||
zTest -= sTest/2.0f;
|
||||
}
|
||||
bitInByteNDX++;
|
||||
// If we've reached the last bit of the byte, then we want to copy this byte
|
||||
|
@ -369,7 +369,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r,
|
|||
}
|
||||
|
||||
octetsDone++;
|
||||
sTest /= 2.0;
|
||||
sTest /= 2.0f;
|
||||
}
|
||||
|
||||
// If we've got here, and we didn't fill the last byte, we need to zero pad this
|
||||
|
|
Loading…
Reference in a new issue