From 44860a5f5a5acb1951c3ced95224dbf36a053981 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Mar 2014 12:01:37 -0700 Subject: [PATCH 1/9] put servers alphabetically first in node list --- domain-server/resources/web/js/tables.js | 25 +++++++++++++++++++++--- domain-server/src/DomainServer.cpp | 11 +++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index 18e67ac6e7..3f7b2b7b28 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -2,16 +2,35 @@ $(document).ready(function(){ // setup a function to grab the assignments function getNodesAndAssignments() { $.getJSON("nodes.json", function(json){ + + json.nodes.sort(function(a, b){ + if (a.type === b.type) { + return 0; + } + + if (a.type === "agent" && b.type !== "agent") { + return 1; + } + + if (a.type > b.type) { + return 1; + } + + if (a.type < b.type) { + return -1; + } + }); + nodesTableBody = ""; - $.each(json.nodes, function (uuid, data) { + $.each(json.nodes, function(index, data) { nodesTableBody += ""; nodesTableBody += "" + data.type + ""; - nodesTableBody += "" + uuid + ""; + nodesTableBody += "" + data.uuid + ""; nodesTableBody += "" + (data.pool ? data.pool : "") + ""; nodesTableBody += "" + data.public.ip + ":" + data.public.port + ""; nodesTableBody += "" + data.local.ip + ":" + data.local.port + ""; - nodesTableBody += ""; + nodesTableBody += ""; nodesTableBody += ""; }); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 035e6c9a20..d18f12ec7f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -622,6 +622,7 @@ QJsonObject DomainServer::jsonForSocket(const HifiSockAddr& socket) { return socketJSON; } +const char JSON_KEY_UUID[] = "uuid"; const char JSON_KEY_TYPE[] = "type"; const char JSON_KEY_PUBLIC_SOCKET[] = "public"; const char JSON_KEY_LOCAL_SOCKET[] = "local"; @@ -635,6 +636,9 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeTypeName = nodeTypeName.toLower(); nodeTypeName.replace(' ', '-'); + // add the node UUID + nodeJson[JSON_KEY_UUID] = uuidStringWithoutCurlyBraces(node->getUUID()); + // add the node type nodeJson[JSON_KEY_TYPE] = nodeTypeName; @@ -707,18 +711,17 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url } else if (url.path() == QString("%1.json").arg(URI_NODES)) { // setup the JSON QJsonObject rootJSON; - QJsonObject nodesJSON; + QJsonArray nodesJSONArray; // enumerate the NodeList to find the assigned nodes NodeList* nodeList = NodeList::getInstance(); foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { // add the node using the UUID as the key - QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID()); - nodesJSON[uuidString] = jsonObjectForNode(node); + nodesJSONArray.append(jsonObjectForNode(node)); } - rootJSON["nodes"] = nodesJSON; + rootJSON["nodes"] = nodesJSONArray; // print out the created JSON QJsonDocument nodesDocument(rootJSON); From f6c98a1cc23107f2fa0c416510c1b0afde4d6e78 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 4 Apr 2014 10:40:55 -0700 Subject: [PATCH 2/9] fix bug in gun.js: unplayed target delete sound --- examples/gun.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gun.js b/examples/gun.js index e358e6b391..3edb823bd1 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -176,7 +176,6 @@ function particleCollisionWithParticle(particle1, particle2) { print("hit, msecs = " + msecs); Particles.deleteParticle(particle1); Particles.deleteParticle(particle2); - audioOptions.position = newPosition; audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); Audio.playSound(targetHitSound, audioOptions); } From 5e2b8bb2c3296095278bd59c74443368ff350ae6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 4 Apr 2014 10:43:06 -0700 Subject: [PATCH 3/9] connect SIGNAL to SIGNAL to avoid forwarding slot --- interface/src/Application.cpp | 4 ++-- libraries/particles/src/ParticlesScriptingInterface.h | 11 ----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 21fefac6f1..7ba3e5c631 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1567,12 +1567,12 @@ void Application::init() { connect(&_particleCollisionSystem, SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&)), ScriptEngine::getParticlesScriptingInterface(), - SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&))); + SIGNAL(particleCollisionWithVoxels(const ParticleID&, const VoxelDetail&, const glm::vec3&))); connect(&_particleCollisionSystem, SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&)), ScriptEngine::getParticlesScriptingInterface(), - SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&))); + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&))); _audio.init(_glWidget); diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index af5f76a6af..24bbad9e3e 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -27,17 +27,6 @@ public: void setParticleTree(ParticleTree* particleTree) { _particleTree = particleTree; } ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; } -private slots: - /// inbound slots for external collision systems - void forwardParticleCollisionWithVoxel(const ParticleID& particleID, - const VoxelDetail& voxel, const glm::vec3& penetration) { - emit particleCollisionWithVoxel(particleID, voxel, penetration); - } - - void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB, const glm::vec3& penetration) { - emit particleCollisionWithParticle(idA, idB, penetration); - } - public slots: /// adds a particle with the specific properties ParticleID addParticle(const ParticleProperties& properties); From 5adcf6875241fd7c58ec309f026e8b866fd87a8a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 7 Apr 2014 09:08:51 -0700 Subject: [PATCH 4/9] sort nodes on DS page by their uptime --- domain-server/resources/web/index.shtml | 1 + domain-server/resources/web/js/tables.js | 16 ++++++++++++++-- domain-server/src/DomainServer.cpp | 4 ++++ libraries/shared/src/Node.cpp | 3 ++- libraries/shared/src/Node.h | 6 +++--- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index bba4dc4d22..afd0af1679 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -12,6 +12,7 @@ Pool Public Local + Uptime (s) Kill? diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index 3f7b2b7b28..a4884486c3 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -5,13 +5,21 @@ $(document).ready(function(){ json.nodes.sort(function(a, b){ if (a.type === b.type) { - return 0; + if (a.wake_timestamp < b.wake_timestamp) { + return 1; + } else if (a.wake_timestamp > b.wake_timestamp) { + return -1; + } else { + return 0; + } } if (a.type === "agent" && b.type !== "agent") { return 1; + } else if (b.type === "agent" && a.type !== "agent") { + return -1; } - + if (a.type > b.type) { return 1; } @@ -30,6 +38,10 @@ $(document).ready(function(){ nodesTableBody += "" + (data.pool ? data.pool : "") + ""; nodesTableBody += "" + data.public.ip + ":" + data.public.port + ""; nodesTableBody += "" + data.local.ip + ":" + data.local.port + ""; + + var uptimeSeconds = (Date.now() - data.wake_timestamp) / 1000; + nodesTableBody += "" + uptimeSeconds.toLocaleString() + ""; + nodesTableBody += ""; nodesTableBody += ""; }); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d18f12ec7f..8d1ee9d3dd 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -627,6 +627,7 @@ const char JSON_KEY_TYPE[] = "type"; const char JSON_KEY_PUBLIC_SOCKET[] = "public"; const char JSON_KEY_LOCAL_SOCKET[] = "local"; const char JSON_KEY_POOL[] = "pool"; +const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { QJsonObject nodeJson; @@ -646,6 +647,9 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { nodeJson[JSON_KEY_PUBLIC_SOCKET] = jsonForSocket(node->getPublicSocket()); nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); + // add the node uptime in our list + nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); + // if the node has pool information, add it SharedAssignmentPointer matchingAssignment = _staticAssignmentHash.value(node->getUUID()); if (matchingAssignment) { diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index a4491fb707..1e78bc3feb 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -19,6 +19,7 @@ #include "SharedUtil.h" #include +#include #include const QString UNKNOWN_NodeType_t_NAME = "Unknown"; @@ -47,7 +48,7 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { Node::Node(const QUuid& uuid, char type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) : _type(type), _uuid(uuid), - _wakeMicrostamp(usecTimestampNow()), + _wakeTimestamp(QDateTime::currentMSecsSinceEpoch()), _lastHeardMicrostamp(usecTimestampNow()), _publicSocket(publicSocket), _localSocket(localSocket), diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 79d75629a6..5d30d6f7b0 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -60,8 +60,8 @@ public: const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid) { _uuid = uuid; } - quint64 getWakeMicrostamp() const { return _wakeMicrostamp; } - void setWakeMicrostamp(quint64 wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; } + quint64 getWakeTimestamp() const { return _wakeTimestamp; } + void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; } quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } @@ -109,7 +109,7 @@ private: NodeType_t _type; QUuid _uuid; - quint64 _wakeMicrostamp; + quint64 _wakeTimestamp; quint64 _lastHeardMicrostamp; HifiSockAddr _publicSocket; HifiSockAddr _localSocket; From d902580d6faff2d6ca460a93ff850df4e4f2e5d5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 7 Apr 2014 10:03:10 -0700 Subject: [PATCH 5/9] Add Collision metatype to JS script system --- examples/collidingParticles.js | 10 +++-- examples/globalCollisionsExample.js | 10 +++-- examples/gun.js | 16 +++---- examples/paintGun.js | 5 ++- examples/spaceInvadersExample.js | 5 ++- interface/src/Application.cpp | 8 ++-- .../particles/src/ParticleCollisionSystem.cpp | 42 ++++++++++++------- .../particles/src/ParticleCollisionSystem.h | 8 ++-- .../src/ParticlesScriptingInterface.h | 6 ++- libraries/shared/src/RegisteredMetaTypes.cpp | 13 ++++++ libraries/shared/src/RegisteredMetaTypes.h | 5 +++ 11 files changed, 82 insertions(+), 46 deletions(-) diff --git a/examples/collidingParticles.js b/examples/collidingParticles.js index 2d2cf4fecc..95520df757 100644 --- a/examples/collidingParticles.js +++ b/examples/collidingParticles.js @@ -30,9 +30,10 @@ var gravity = { var damping = 0.1; var scriptA = " " + - " function collisionWithParticle(other, penetration) { " + + " function collisionWithParticle(other, collision) { " + " print('collisionWithParticle(other.getID()=' + other.getID() + ')...'); " + - " Vec3.print('penetration=', penetration); " + + " Vec3.print('penetration=', collision.penetration); " + + " Vec3.print('contactPoint=', collision.contactPoint); " + " print('myID=' + Particle.getID() + '\\n'); " + " var colorBlack = { red: 0, green: 0, blue: 0 };" + " var otherColor = other.getColor();" + @@ -46,9 +47,10 @@ var scriptA = " " + " "; var scriptB = " " + - " function collisionWithParticle(other, penetration) { " + + " function collisionWithParticle(other, collision) { " + " print('collisionWithParticle(other.getID()=' + other.getID() + ')...'); " + - " Vec3.print('penetration=', penetration); " + + " Vec3.print('penetration=', collision.penetration); " + + " Vec3.print('contactPoint=', collision.contactPoint); " + " print('myID=' + Particle.getID() + '\\n'); " + " Particle.setScript('Particle.setShouldDie(true);'); " + " } " + diff --git a/examples/globalCollisionsExample.js b/examples/globalCollisionsExample.js index 266823f564..7abf707cbf 100644 --- a/examples/globalCollisionsExample.js +++ b/examples/globalCollisionsExample.js @@ -12,18 +12,20 @@ print("hello..."); -function particleCollisionWithVoxel(particle, voxel, penetration) { +function particleCollisionWithVoxel(particle, voxel, collision) { print("particleCollisionWithVoxel().."); print(" particle.getID()=" + particle.id); print(" voxel color...=" + voxel.red + ", " + voxel.green + ", " + voxel.blue); - Vec3.print('penetration=', penetration); + Vec3.print('penetration=', collision.penetration); + Vec3.print('contactPoint=', collision.contactPoint); } -function particleCollisionWithParticle(particleA, particleB, penetration) { +function particleCollisionWithParticle(particleA, particleB, collision) { print("particleCollisionWithParticle().."); print(" particleA.getID()=" + particleA.id); print(" particleB.getID()=" + particleB.id); - Vec3.print('penetration=', penetration); + Vec3.print('penetration=', collision.penetration); + Vec3.print('contactPoint=', collision.contactPoint); } Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); diff --git a/examples/gun.js b/examples/gun.js index 3edb823bd1..7bdde19d94 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -145,24 +145,21 @@ function shootTarget() { -function particleCollisionWithVoxel(particle, voxel, penetration) { +function particleCollisionWithVoxel(particle, voxel, collision) { var HOLE_SIZE = 0.125; var particleProperties = Particles.getParticleProperties(particle); var position = particleProperties.position; Particles.deleteParticle(particle); // Make a hole in this voxel - Vec3.print("penetration", penetration); - Vec3.print("position", position); - var pointOfEntry = Vec3.subtract(position, penetration); - Vec3.print("pointOfEntry", pointOfEntry); - Voxels.eraseVoxel(pointOfEntry.x, pointOfEntry.y, pointOfEntry.z, HOLE_SIZE); + Vec3.print("penetration", collision.penetration); + Vec3.print("contactPoint", collision.contactPoint); + Voxels.eraseVoxel(contactPoint.x, contactPoint.y, contactPoint.z, HOLE_SIZE); Voxels.eraseVoxel(position.x, position.y, position.z, HOLE_SIZE); - //audioOptions.position = position; audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); Audio.playSound(impactSound, audioOptions); } -function particleCollisionWithParticle(particle1, particle2) { +function particleCollisionWithParticle(particle1, particle2, collision) { score++; if (showScore) { Overlays.editOverlay(text, { text: "Score: " + score } ); @@ -174,8 +171,11 @@ function particleCollisionWithParticle(particle1, particle2) { var endTime = new Date(); var msecs = endTime.valueOf() - shotTime.valueOf(); print("hit, msecs = " + msecs); + Vec3.print("penetration = ", collision.penetration); + Vec3.print("contactPoint = ", collision.contactPoint); Particles.deleteParticle(particle1); Particles.deleteParticle(particle2); + // play the sound near the camera so the shooter can hear it audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); Audio.playSound(targetHitSound, audioOptions); } diff --git a/examples/paintGun.js b/examples/paintGun.js index 6b6e78b43e..0b30d99fb5 100644 --- a/examples/paintGun.js +++ b/examples/paintGun.js @@ -62,9 +62,10 @@ function checkController(deltaTime) { // This is the script for the particles that this gun shoots. var script = - " function collisionWithVoxel(voxel, penetration) { " + + " function collisionWithVoxel(voxel, collision) { " + " print('collisionWithVoxel(voxel)... '); " + - " Vec3.print('penetration=', penetration); " + + " Vec3.print('penetration=', collision.penetration); " + + " Vec3.print('contactPoint=', collision.contactPoint); " + " print('myID=' + Particle.getID() + '\\n'); " + " var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" + " var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" + diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js index 61ff93fc8f..df985e2e18 100644 --- a/examples/spaceInvadersExample.js +++ b/examples/spaceInvadersExample.js @@ -392,9 +392,10 @@ function deleteIfInvader(possibleInvaderParticle) { } } -function particleCollisionWithParticle(particleA, particleB, penetration) { +function particleCollisionWithParticle(particleA, particleB, collision) { print("particleCollisionWithParticle() a.id="+particleA.id + " b.id=" + particleB.id); - Vec3.print('particleCollisionWithParticle() penetration=', penetration); + Vec3.print('particleCollisionWithParticle() penetration=', collision.penetration); + Vec3.print('particleCollisionWithParticle() contactPoint=', collision.contactPoint); if (missileFired) { myMissile = Particles.identifyParticle(myMissile); if (myMissile.id == particleA.id) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7ba3e5c631..48aab620a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1565,14 +1565,14 @@ void Application::init() { // connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface connect(&_particleCollisionSystem, - SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const glm::vec3&)), + SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&, const CollisionInfo&)), ScriptEngine::getParticlesScriptingInterface(), - SIGNAL(particleCollisionWithVoxels(const ParticleID&, const VoxelDetail&, const glm::vec3&))); + SIGNAL(particleCollisionWithVoxels(const ParticleID&, const VoxelDetail&, const CollisionInfo&))); connect(&_particleCollisionSystem, - SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&)), + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const CollisionInfo&)), ScriptEngine::getParticlesScriptingInterface(), - SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const glm::vec3&))); + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&, const CollisionInfo&))); _audio.init(_glWidget); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index c827e28c78..028cf80df9 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -72,17 +72,17 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) { } void ParticleCollisionSystem::emitGlobalParticleCollisionWithVoxel(Particle* particle, - VoxelDetail* voxelDetails, const glm::vec3& penetration) { + VoxelDetail* voxelDetails, const CollisionInfo& collision) { ParticleID particleID = particle->getParticleID(); - emit particleCollisionWithVoxel(particleID, *voxelDetails, penetration); + emit particleCollisionWithVoxel(particleID, *voxelDetails, collision); } void ParticleCollisionSystem::emitGlobalParticleCollisionWithParticle(Particle* particleA, - Particle* particleB, const glm::vec3& penetration) { + Particle* particleB, const CollisionInfo& collision) { ParticleID idA = particleA->getParticleID(); ParticleID idB = particleB->getParticleID(); - emit particleCollisionWithParticle(idA, idB, penetration); + emit particleCollisionWithParticle(idA, idB, collision); } void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { @@ -100,11 +100,17 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { // let the particles run their collision scripts if they have them particle->collisionWithVoxel(voxelDetails, collisionInfo._penetration); - // let the global script run their collision scripts for particles if they have them - emitGlobalParticleCollisionWithVoxel(particle, voxelDetails, collisionInfo._penetration); - + // findSpherePenetration() only computes the penetration but we also want some other collision info + // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding + // the results to systems outside of this octree reference frame. updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._contactPoint = (float)TREE_SCALE * (particle->getPosition() + particle->getRadius() * glm::normalize(collisionInfo._penetration)); + // let the global script run their collision scripts for particles if they have them + emitGlobalParticleCollisionWithVoxel(particle, voxelDetails, collisionInfo); + + // we must scale back down to the octree reference frame before updating the particle properties collisionInfo._penetration /= (float)(TREE_SCALE); + collisionInfo._contactPoint /= (float)(TREE_SCALE); particle->applyHardCollision(collisionInfo); queueParticlePropertiesUpdate(particle); @@ -121,8 +127,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) glm::vec3 penetration; Particle* particleB; if (_particles->findSpherePenetration(center, radius, penetration, (void**)&particleB, Octree::NoLock)) { - // NOTE: 'penetration' is the depth that 'particleA' overlaps 'particleB'. - // That is, it points from A into B. + // NOTE: 'penetration' is the depth that 'particleA' overlaps 'particleB'. It points from A into B. // Even if the particles overlap... when the particles are already moving appart // we don't want to count this as a collision. @@ -130,7 +135,12 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) if (glm::dot(relativeVelocity, penetration) > 0.0f) { particleA->collisionWithParticle(particleB, penetration); particleB->collisionWithParticle(particleA, penetration * -1.0f); // the penetration is reversed - emitGlobalParticleCollisionWithParticle(particleA, particleB, penetration); + + CollisionInfo collision; + collision._penetration = penetration; + // for now the contactPoint is the average between the the two paricle centers + collision._contactPoint = (0.5f * (float)TREE_SCALE) * (particleA->getPosition() + particleB->getPosition()); + emitGlobalParticleCollisionWithParticle(particleA, particleB, collision); glm::vec3 axis = glm::normalize(penetration); glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; @@ -142,25 +152,25 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) float massB = (particleB->getInHand()) ? MAX_MASS : particleB->getMass(); float totalMass = massA + massB; - // handle A particle + // handle particle A particleA->setVelocity(particleA->getVelocity() - axialVelocity * (2.0f * massB / totalMass)); particleA->setPosition(particleA->getPosition() - 0.5f * penetration); ParticleProperties propertiesA; - ParticleID particleAid(particleA->getID()); + ParticleID idA(particleA->getID()); propertiesA.copyFromParticle(*particleA); propertiesA.setVelocity(particleA->getVelocity() * (float)TREE_SCALE); propertiesA.setPosition(particleA->getPosition() * (float)TREE_SCALE); - _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleAid, propertiesA); + _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, idA, propertiesA); - // handle B particle + // handle particle B particleB->setVelocity(particleB->getVelocity() + axialVelocity * (2.0f * massA / totalMass)); particleA->setPosition(particleB->getPosition() + 0.5f * penetration); ParticleProperties propertiesB; - ParticleID particleBid(particleB->getID()); + ParticleID idB(particleB->getID()); propertiesB.copyFromParticle(*particleB); propertiesB.setVelocity(particleB->getVelocity() * (float)TREE_SCALE); propertiesB.setPosition(particleB->getPosition() * (float)TREE_SCALE); - _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, particleBid, propertiesB); + _packetSender->queueParticleEditMessage(PacketTypeParticleAddOrEdit, idB, propertiesB); _packetSender->releaseQueuedMessages(); diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 1b30fd31ac..c6ab97c02b 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -53,13 +53,13 @@ public: void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency); signals: - void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel, const glm::vec3& penetration); - void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB, const glm::vec3& penetration); + void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel, const CollisionInfo& penetration); + void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB, const CollisionInfo& penetration); private: static bool updateOperation(OctreeElement* element, void* extraData); - void emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails, const glm::vec3& penetration); - void emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB, const glm::vec3& penetration); + void emitGlobalParticleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails, const CollisionInfo& penetration); + void emitGlobalParticleCollisionWithParticle(Particle* particleA, Particle* particleB, const CollisionInfo& penetration); ParticleEditPacketSender* _packetSender; ParticleTree* _particles; diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index 24bbad9e3e..8de44e20bb 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -11,6 +11,8 @@ #include +#include + #include #include "ParticleEditPacketSender.h" @@ -55,8 +57,8 @@ public slots: QVector findParticles(const glm::vec3& center, float radius) const; signals: - void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel, const glm::vec3& penetration); - void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB, const glm::vec3& penetration); + void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel, const CollisionInfo& collision); + void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB, const CollisionInfo& collision); private: void queueParticleMessage(PacketType packetType, ParticleID particleID, const ParticleProperties& properties); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index d106977ae0..af074c59bc 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -16,6 +16,7 @@ static int vec2MetaTypeId = qRegisterMetaType(); static int quatMetaTypeId = qRegisterMetaType(); static int xColorMetaTypeId = qRegisterMetaType(); static int pickRayMetaTypeId = qRegisterMetaType(); +static int collisionMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); @@ -24,6 +25,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); + qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); } QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { @@ -122,3 +124,14 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { } } +QScriptValue collisionToScriptValue(QScriptEngine* engine, const CollisionInfo& collision) { + QScriptValue obj = engine->newObject(); + obj.setProperty("penetration", vec3toScriptValue(engine, collision._penetration)); + obj.setProperty("contactPoint", vec3toScriptValue(engine, collision._contactPoint)); + return obj; +} + +void collisionFromScriptValue(const QScriptValue &object, CollisionInfo& collision) { + // TODO: implement this when we know what it means to accept collision events from JS +} + diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ea65e45c95..0e7732c057 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -15,6 +15,7 @@ #include +#include "CollisionInfo.h" #include "SharedUtil.h" Q_DECLARE_METATYPE(glm::vec4) @@ -50,4 +51,8 @@ Q_DECLARE_METATYPE(PickRay) QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay); void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); +Q_DECLARE_METATYPE(CollisionInfo) +QScriptValue collisionToScriptValue(QScriptEngine* engine, const CollisionInfo& collision); +void collisionFromScriptValue(const QScriptValue &object, CollisionInfo& collision); + #endif From 0011276b0bc1e6aab7b8854d39acae874a19261d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 7 Apr 2014 14:19:17 -0700 Subject: [PATCH 6/9] Added back the download stats, which got lost in a merge. --- interface/src/ui/Stats.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1722732e76..dbcbb3d8bb 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -20,6 +20,8 @@ #include "Menu.h" #include "Util.h" +using namespace std; + const int STATS_PELS_PER_LINE = 20; const int STATS_GENERAL_MIN_WIDTH = 165; @@ -287,7 +289,7 @@ void Stats::display( MyAvatar* myAvatar = Application::getInstance()->getAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); - lines = _expanded ? 4 : 3; + lines = _expanded ? 5 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -318,6 +320,16 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, color); + + stringstream downloads; + downloads << "Downloads: "; + foreach (Resource* resource, ResourceCache::getLoadingRequests()) { + downloads << (int)(resource->getProgress() * 100.0f) << "% "; + } + downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)"; + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloads.str().c_str(), color); } verticalOffset = 0; From 56b0583600bb21ff12c4180cc58c72d780283b22 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Apr 2014 14:42:35 -0700 Subject: [PATCH 7/9] Fix for editVoxels/inspect conflict --- examples/editVoxels.js | 46 ++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 4922caf0d8..1dc8c189f3 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -32,6 +32,7 @@ var MIN_PASTE_VOXEL_SCALE = .256; var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting var previewLineWidth = 1.5; +var inspectJsIsRunning = false; var isAdding = false; var isExtruding = false; var extrudeDirection = { x: 0, y: 0, z: 0 }; @@ -62,9 +63,9 @@ var whichColor = -1; // Starting color is 'Copy' mode // Create sounds for adding, deleting, recoloring voxels var addSound1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+2.raw"); -var addSound2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+3.raw"); -var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+4.raw"); +var addSound2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+4.raw"); +var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+3.raw"); var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete+2.raw"); var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit+2.raw"); var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw"); @@ -727,6 +728,9 @@ function trackKeyPressEvent(event) { trackAsEyedropper = true; moveTools(); } + if (event.text == "ALT") { + inspectJsIsRunning = true; + } showPreviewGuides(); } @@ -739,6 +743,10 @@ function trackKeyReleaseEvent(event) { showPreviewGuides(); Audio.playSound(clickSound, audioOptions); } + + if (event.text == "ALT") { + inspectJsIsRunning = false; + } if (editToolsOn) { if (event.text == "ESC") { @@ -777,11 +785,13 @@ function trackKeyReleaseEvent(event) { } function mousePressEvent(event) { - // if our tools are off, then don't do anything if (!editToolsOn) { return; } + if (inspectJsIsRunning) { + return; + } var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -1061,7 +1071,9 @@ function mouseMoveEvent(event) { if (!editToolsOn) { return; } - + if (inspectJsIsRunning) { + return; + } if (isMovingSlider) { thumbX = (event.x - thumbClickOffsetX) - sliderX; @@ -1121,7 +1133,10 @@ function mouseReleaseEvent(event) { if (!editToolsOn) { return; } - + if (inspectJsIsRunning) { + return; + } + if (isMovingSlider) { isMovingSlider = false; } @@ -1214,24 +1229,6 @@ function moveTools() { } -function touchBeginEvent(event) { - if (!editToolsOn) { - return; - } -} - -function touchUpdateEvent(event) { - if (!editToolsOn) { - return; - } -} - -function touchEndEvent(event) { - if (!editToolsOn) { - return; - } -} - var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area var lastFingerDeleteVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area @@ -1332,9 +1329,6 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Controller.touchBeginEvent.connect(touchBeginEvent); -Controller.touchUpdateEvent.connect(touchUpdateEvent); -Controller.touchEndEvent.connect(touchEndEvent); Controller.captureKeyEvents({ text: "+" }); Controller.captureKeyEvents({ text: "-" }); From bf0eb69e2c9733b500ecff3d8d03a706e58b98e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 7 Apr 2014 16:28:55 -0700 Subject: [PATCH 8/9] fix for sometimes slow avatar --- interface/src/avatar/Avatar.cpp | 4 ++-- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/MyAvatar.cpp | 18 ++++++++---------- interface/src/avatar/MyAvatar.h | 1 - 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 899514d1c1..75b9db1581 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,7 +46,6 @@ Avatar::Avatar() : _mode(AVATAR_MODE_STANDING), _velocity(0.0f, 0.0f, 0.0f), _thrust(0.0f, 0.0f, 0.0f), - _speed(0.0f), _leanScale(0.5f), _scale(1.0f), _worldUpDirection(DEFAULT_UP_DIRECTION), @@ -137,7 +136,8 @@ void Avatar::simulate(float deltaTime) { } // use speed and angular velocity to determine walking vs. standing - if (_speed + fabs(_bodyYawDelta) > 0.2) { + float speed = glm::length(_velocity); + if (speed + fabs(_bodyYawDelta) > 0.2) { _mode = AVATAR_MODE_WALKING; } else { _mode = AVATAR_MODE_INTERACTING; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index f6d5669859..a6cefedef8 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -160,7 +160,6 @@ protected: AvatarMode _mode; glm::vec3 _velocity; glm::vec3 _thrust; - float _speed; float _leanScale; float _scale; glm::vec3 _worldUpDirection; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1ff93794c5..05636166e2 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -169,9 +169,6 @@ void MyAvatar::simulate(float deltaTime) { // Collect thrust forces from keyboard and devices updateThrust(deltaTime); - // calculate speed - _speed = glm::length(_velocity); - // update the movement of the hand and process handshaking with other avatars... updateHandMovementAndTouching(deltaTime); @@ -203,9 +200,9 @@ void MyAvatar::simulate(float deltaTime) { if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.f; } if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.f; } - const float MAX_STATIC_FRICTION_VELOCITY = 0.5f; + const float MAX_STATIC_FRICTION_SPEED = 0.5f; const float STATIC_FRICTION_STRENGTH = _scale * 20.f; - applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_VELOCITY, STATIC_FRICTION_STRENGTH); + applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH); // Damp avatar velocity const float LINEAR_DAMPING_STRENGTH = 0.5f; @@ -230,7 +227,8 @@ void MyAvatar::simulate(float deltaTime) { const float WALKING_SPEED_THRESHOLD = 0.2f; // use speed and angular velocity to determine walking vs. standing - if (_speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) { + float speed = glm::length(_velocity); + if (speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) { _mode = AVATAR_MODE_WALKING; } else { _mode = AVATAR_MODE_INTERACTING; @@ -238,7 +236,7 @@ void MyAvatar::simulate(float deltaTime) { // update moving flag based on speed const float MOVING_SPEED_THRESHOLD = 0.01f; - _moving = _speed > MOVING_SPEED_THRESHOLD; + _moving = speed > MOVING_SPEED_THRESHOLD; // If a move target is set, update position explicitly const float MOVE_FINISHED_TOLERANCE = 0.1f; @@ -681,7 +679,6 @@ void MyAvatar::updateThrust(float deltaTime) { if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { const float THRUST_INCREASE_RATE = 1.05f; const float MAX_THRUST_MULTIPLIER = 75.0f; - //printf("m = %.3f\n", _thrustMultiplier); _thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE; if (_thrustMultiplier > MAX_THRUST_MULTIPLIER) { _thrustMultiplier = MAX_THRUST_MULTIPLIER; @@ -703,11 +700,12 @@ void MyAvatar::updateThrust(float deltaTime) { if ((glm::length(_thrust) == 0.0f) && _isThrustOn && (glm::length(_velocity) > MIN_SPEED_BRAKE_VELOCITY)) { _speedBrakes = true; } + _isThrustOn = (glm::length(_thrust) > EPSILON); - if (_speedBrakes && (glm::length(_velocity) < MIN_SPEED_BRAKE_VELOCITY)) { + if (_isThrustOn || (_speedBrakes && (glm::length(_velocity) < MIN_SPEED_BRAKE_VELOCITY))) { _speedBrakes = false; } - _isThrustOn = (glm::length(_thrust) > EPSILON); + } void MyAvatar::updateHandMovementAndTouching(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5c940f0f50..2125b126b3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -50,7 +50,6 @@ public: void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } // getters - float getSpeed() const { return _speed; } AvatarMode getMode() const { return _mode; } float getLeanScale() const { return _leanScale; } float getElapsedTimeStopped() const { return _elapsedTimeStopped; } From fa05a482707a049c53ac554aede2b7190579b87c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 7 Apr 2014 17:55:36 -0700 Subject: [PATCH 9/9] Provide a means of supplying the joint mappings in the FST file so that agent scripts can address joints by name. Closes #2526. --- examples/crazylegs.js | 7 +++-- interface/src/avatar/Avatar.cpp | 4 +++ interface/src/avatar/Avatar.h | 2 ++ libraries/avatars/src/AvatarData.cpp | 46 ++++++++++++++++++++++++++++ libraries/avatars/src/AvatarData.h | 12 ++++++-- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/examples/crazylegs.js b/examples/crazylegs.js index 099387e000..19a171dbdf 100644 --- a/examples/crazylegs.js +++ b/examples/crazylegs.js @@ -12,9 +12,12 @@ var AMPLITUDE = 45.0; var cumulativeTime = 0.0; -print("Joint List:"); +print("# Joint list start"); var jointList = MyAvatar.getJointNames(); -print(jointList); +for (var i = 0; i < jointList.length; i++) { + print("jointIndex = " + jointList[i] + " = " + i); +} +print("# Joint list end"); Script.update.connect(function(deltaTime) { cumulativeTime += deltaTime; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 899514d1c1..7a55d4d534 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -331,6 +331,10 @@ void Avatar::renderBody(RenderMode renderMode) { getHand()->render(false); } +void Avatar::updateJointMappings() { + // no-op; joint mappings come from skeleton model +} + void Avatar::renderBillboard() { if (_billboard.isEmpty()) { return; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index f6d5669859..e0d46c8624 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -187,6 +187,8 @@ protected: void renderDisplayName(); virtual void renderBody(RenderMode renderMode); + virtual void updateJointMappings(); + private: bool _initialized; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 930e3f7350..e84636b5a4 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -639,6 +639,8 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModelURL = skeletonModelURL.isEmpty() ? DEFAULT_BODY_MODEL_URL : skeletonModelURL; qDebug() << "Changing skeleton model for avatar to" << _skeletonModelURL.toString(); + + updateJointMappings(); } void AvatarData::setDisplayName(const QString& displayName) { @@ -673,6 +675,40 @@ void AvatarData::setBillboardFromURL(const QString &billboardURL) { void AvatarData::setBillboardFromNetworkReply() { QNetworkReply* networkReply = reinterpret_cast(sender()); setBillboard(networkReply->readAll()); + networkReply->deleteLater(); +} + +void AvatarData::setJointMappingsFromNetworkReply() { + QNetworkReply* networkReply = static_cast(sender()); + + QByteArray line; + while (!(line = networkReply->readLine()).isEmpty()) { + if (!(line = line.trimmed()).startsWith("jointIndex")) { + continue; + } + int jointNameIndex = line.indexOf('=') + 1; + if (jointNameIndex == 0) { + continue; + } + int secondSeparatorIndex = line.indexOf('=', jointNameIndex); + if (secondSeparatorIndex == -1) { + continue; + } + QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed(); + bool ok; + int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok); + if (ok) { + while (_jointNames.size() < jointIndex + 1) { + _jointNames.append(QString()); + } + _jointNames[jointIndex] = jointName; + } + } + for (int i = 0; i < _jointNames.size(); i++) { + _jointIndices.insert(_jointNames.at(i), i + 1); + } + + networkReply->deleteLater(); } void AvatarData::setClampedTargetScale(float targetScale) { @@ -705,3 +741,13 @@ void AvatarData::sendBillboardPacket() { NodeList::getInstance()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); } } + +void AvatarData::updateJointMappings() { + _jointIndices.clear(); + _jointNames.clear(); + + if (networkAccessManager && _skeletonModelURL.fileName().toLower().endsWith(".fst")) { + QNetworkReply* networkReply = networkAccessManager->get(QNetworkRequest(_skeletonModelURL)); + connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply())); + } +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 221bbd0428..cf645855e8 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -30,6 +30,7 @@ typedef unsigned long long quint64; #include #include +#include #include #include #include @@ -160,9 +161,9 @@ public: Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; /// Returns the index of the joint with the specified name, or -1 if not found/unknown. - Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return -1; } + Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; } - Q_INVOKABLE virtual QStringList getJointNames() const { return QStringList(); } + Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; } // key state void setKeyState(KeyState s) { _keyState = s; } @@ -217,6 +218,7 @@ public slots: void sendIdentityPacket(); void sendBillboardPacket(); void setBillboardFromNetworkReply(); + void setJointMappingsFromNetworkReply(); protected: glm::vec3 _position; glm::vec3 _handPosition; @@ -258,10 +260,16 @@ protected: QByteArray _billboard; QString _billboardURL; + QHash _jointIndices; ///< 1-based, since zero is returned for missing keys + QStringList _jointNames; ///< in order of depth-first traversal + static QNetworkAccessManager* networkAccessManager; quint64 _errorLogExpiry; ///< time in future when to log an error + /// Loads the joint indices, names from the FST file (if any) + virtual void updateJointMappings(); + private: // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&);