From 40c66a94beb60a3490c54cfca3b8e30091270a78 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 28 Jan 2016 11:44:50 -0800 Subject: [PATCH 01/15] Moves text props to top of Entity Props panel --- examples/html/entityProperties.html | 72 +++++++++++++++-------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 898f2bea6d..8ea43810fd 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -976,6 +976,42 @@ +
+ +
+ +
+
Text Content
+
+ +
+
+
+
Line Height
+
+ +
+
+
+
Text Color
+
+
+
R
+
G
+
B
+
+
+
+
Background Color
+
+
+
R
+
G
+
B
+
+
+ +
@@ -1365,41 +1401,7 @@
-
- -
- -
-
Text Content
-
- -
-
-
-
Line Height
-
- -
-
-
-
Text Color
-
-
-
R
-
G
-
B
-
-
-
-
Background Color
-
-
-
R
-
G
-
B
-
-
- +
From 9f95a9de89a34854ee542f8decafbe2012f48d5c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 29 Jan 2016 00:52:59 -0800 Subject: [PATCH 02/15] Limits on distance and mass for far grabbing --- examples/FlockOfbirds.js | 4 +- examples/controllers/handControllerGrab.js | 80 ++++++++++++++-------- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/examples/FlockOfbirds.js b/examples/FlockOfbirds.js index 514e8a5fab..f466fa2909 100644 --- a/examples/FlockOfbirds.js +++ b/examples/FlockOfbirds.js @@ -12,7 +12,7 @@ // The rectangular area in the domain where the flock will fly var lowerCorner = { x: 0, y: 0, z: 0 }; -var upperCorner = { x: 10, y: 10, z: 10 }; +var upperCorner = { x: 30, y: 10, z: 30 }; var STARTING_FRACTION = 0.25; var NUM_BIRDS = 50; @@ -36,7 +36,7 @@ var ALIGNMENT_FORCE = 1.5; var COHESION_FORCE = 1.0; var MAX_COHESION_VELOCITY = 0.5; -var followBirds = true; +var followBirds = false; var AVATAR_FOLLOW_RATE = 0.001; var AVATAR_FOLLOW_VELOCITY_TIMESCALE = 2.0; var AVATAR_FOLLOW_ORIENTATION_RATE = 0.005; diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9dade9191f..d6fd4bce27 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -41,6 +41,8 @@ var PICK_WITH_HAND_RAY = true; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position +var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified +var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did var MOVE_WITH_HEAD = true; // experimental head-control of distantly held objects var FAR_TO_NEAR_GRAB_PADDING_FACTOR = 1.2; @@ -114,7 +116,9 @@ var GRABBABLE_PROPERTIES = [ "name", "shapeType", "parentID", - "parentJointIndex" + "parentJointIndex", + "density", + "dimensions" ]; var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js @@ -295,7 +299,7 @@ function MyController(hand) { this.rawBumperValue = 0; //for visualizations this.overlayLine = null; - this.particleBeam = null; + this.particleBeamObject = null; //for lights this.spotlight = null; @@ -479,34 +483,32 @@ function MyController(hand) { this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotation = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - + var finalRotationObject = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); var distance = Vec3.distance(handPosition, objectPosition); - var speed = 5; + var speed = distance * 3; var spread = 0; - var lifespan = distance / speed; - if (this.particleBeam === null) { - this.createParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + if (this.particleBeamObject === null) { + this.createParticleBeam(objectPosition, finalRotationObject, color, speed, spread, lifespan); } else { - this.updateParticleBeam(objectPosition, finalRotation, color, speed, spread, lifespan); + this.updateParticleBeam(objectPosition, finalRotationObject, color, speed, spread, lifespan); } }; - this.createParticleBeam = function(position, orientation, color, speed, spread, lifespan) { + this.createParticleBeam = function(positionObject, orientationObject, color, speed, spread, lifespan) { - var particleBeamProperties = { + var particleBeamPropertiesObject = { type: "ParticleEffect", isEmitting: true, - position: position, + position: positionObject, visible: false, lifetime: 60, "name": "Particle Beam", "color": color, "maxParticles": 2000, "lifespan": lifespan, - "emitRate": 50, + "emitRate": 1000, "emitSpeed": speed, "speedSpread": spread, "emitOrientation": { @@ -544,26 +546,25 @@ function MyController(hand) { "additiveBlending": 0, "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" } - - this.particleBeam = Entities.addEntity(particleBeamProperties); + + this.particleBeamObject = Entities.addEntity(particleBeamPropertiesObject); }; - this.updateParticleBeam = function(position, orientation, color, speed, spread, lifespan) { - Entities.editEntity(this.particleBeam, { - rotation: orientation, - position: position, + this.updateParticleBeam = function(positionObject, orientationObject, color, speed, spread, lifespan) { + Entities.editEntity(this.particleBeamObject, { + rotation: orientationObject, + position: positionObject, visible: true, color: color, emitSpeed: speed, speedSpread: spread, lifespan: lifespan }) - }; this.renewParticleBeamLifetime = function() { - var props = Entities.getEntityProperties(this.particleBeam, "age"); - Entities.editEntity(this.particleBeam, { + var props = Entities.getEntityProperties(this.particleBeamObject, "age"); + Entities.editEntity(this.particleBeamObject, { lifetime: TEMPORARY_PARTICLE_BEAM_LIFETIME + props.age // renew lifetime }) } @@ -684,9 +685,9 @@ function MyController(hand) { }; this.particleBeamOff = function() { - if (this.particleBeam !== null) { - Entities.deleteEntity(this.particleBeam); - this.particleBeam = null; + if (this.particleBeamObject !== null) { + Entities.deleteEntity(this.particleBeamObject); + this.particleBeamObject = null; } } @@ -864,6 +865,7 @@ function MyController(hand) { // too far away, don't grab continue; } + if (distance < minDistance) { this.grabbedEntity = candidateEntities[i]; minDistance = distance; @@ -932,6 +934,18 @@ function MyController(hand) { } }; + this.distanceGrabTimescale = function(mass, distance) { + var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / DISTANCE_HOLDING_UNITY_MASS * distance / DISTANCE_HOLDING_UNITY_DISTANCE; + if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) { + timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } + return timeScale; + } + + this.getMass = function(dimensions, density) { + return (dimensions.x * dimensions.y * dimensions.z) * density; + } + this.distanceHolding = function() { var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; @@ -953,12 +967,17 @@ function MyController(hand) { this.radiusScalar = 1.0; } + // compute the mass for the purpose of energy and how quickly to move object + this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); + var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); + var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); + this.actionID = NULL_UUID; this.actionID = Entities.addAction("spring", this.grabbedEntity, { targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + linearTimeScale: timeScale, targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + angularTimeScale: timeScale, tag: getTag(), ttl: ACTION_TTL }); @@ -1135,11 +1154,12 @@ function MyController(hand) { this.handleSpotlight(this.grabbedEntity); } + var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: targetPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), ttl: ACTION_TTL }); if (success) { @@ -1607,7 +1627,7 @@ function MyController(hand) { this.cleanup = function() { this.release(); - Entities.deleteEntity(this.particleBeam); + Entities.deleteEntity(this.particleBeamObject); Entities.deleteEntity(this.spotLight); Entities.deleteEntity(this.pointLight); }; From ee95f7906b50c4fb6c66eccd7379d57508d5bc6c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 29 Jan 2016 00:59:06 -0800 Subject: [PATCH 03/15] Fish flocking example --- examples/FlockOfFish.js | 161 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 examples/FlockOfFish.js diff --git a/examples/FlockOfFish.js b/examples/FlockOfFish.js new file mode 100644 index 0000000000..a6b724ab9d --- /dev/null +++ b/examples/FlockOfFish.js @@ -0,0 +1,161 @@ +// +// flockOfFish.js +// examples +// +// Philip Rosedale +// Copyright 2016 High Fidelity, Inc. +// Fish smimming around in a space in front of you +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +var LIFETIME = 60; +var NUM_FISH = 20; +var TANK_WIDTH = 3.0; +var TANK_HEIGHT = 1.0; +var FISH_WIDTH = 0.03; +var FISH_LENGTH = 0.15; +var MAX_SIGHT_DISTANCE = 0.4; +var MIN_SEPARATION = 0.15; +var AVOIDANCE_FORCE = 0.2; +var COHESION_FORCE = 0.05; +var ALIGNMENT_FORCE = 0.05; +var SWIMMING_FORCE = 0.05; +var SWIMMING_SPEED = 2.0; + +var fishLoaded = false; +var fish = []; + +var lowerCorner = { x: 0, y: 0, z: 0 }; +var upperCorner = { x: 0, y: 0, z: 0 }; + +function randomVector(scale) { + return { x: Math.random() * scale - scale / 2.0, y: Math.random() * scale - scale / 2.0, z: Math.random() * scale - scale / 2.0 }; +} + +function updateFish(deltaTime) { + if (!Entities.serversExist() || !Entities.canRez()) { + return; + } + if (!fishLoaded) { + loadFish(NUM_FISH); + fishLoaded = true; + return; + } + + var averageVelocity = { x: 0, y: 0, z: 0 }; + var averagePosition = { x: 0, y: 0, z: 0 }; + var birdPositionsCounted = 0; + var birdVelocitiesCounted = 0; + for (var i = 0; i < fish.length; i++) { + if (fish[i].entityId) { + var properties = Entities.getEntityProperties(fish[i].entityId); + // If Bird has been deleted, bail + if (properties.id != fish[i].entityId) { + fish[i].entityId = false; + return; + } + + var velocity = properties.velocity; + var position = properties.position; + averageVelocity = { x: 0, y: 0, z: 0 }; + averagePosition = { x: 0, y: 0, z: 0 }; + + var othersCounted = 0; + for (var j = 0; j < fish.length; j++) { + if (i != j) { + var otherProps = Entities.getEntityProperties(fish[j].entityId); + var separation = Vec3.distance(properties.position, otherProps.position); + if (separation < MAX_SIGHT_DISTANCE) { + averageVelocity = Vec3.sum(averageVelocity, otherProps.velocity); + averagePosition = Vec3.sum(averagePosition, otherProps.position); + othersCounted++; + } + if (separation < MIN_SEPARATION) { + var pushAway = Vec3.multiply(Vec3.normalize(Vec3.subtract(properties.position, otherProps.position)), AVOIDANCE_FORCE); + velocity = Vec3.sum(velocity, pushAway); + } + } + } + + if (othersCounted > 0) { + averageVelocity = Vec3.multiply(averageVelocity, 1.0 / othersCounted); + averagePosition = Vec3.multiply(averagePosition, 1.0 / othersCounted); + // Alignment: Follow group's direction and speed + velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(averageVelocity), Vec3.length(velocity)), ALIGNMENT_FORCE); + // Cohesion: Steer towards center of flock + var towardCenter = Vec3.subtract(averagePosition, position); + velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(towardCenter), Vec3.length(velocity)), COHESION_FORCE); + // Try to swim at a constant speed + velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE); + } + + // Keep fish in their 'tank' + if (position.x < lowerCorner.x) { + position.x = lowerCorner.x; + velocity.x *= -1.0; + } else if (position.x > upperCorner.x) { + position.x = upperCorner.x; + velocity.x *= -1.0; + } + if (position.y < lowerCorner.y) { + position.y = lowerCorner.y; + velocity.y *= -1.0; + } else if (position.y > upperCorner.y) { + position.y = upperCorner.y; + velocity.y *= -1.0; + } + if (position.z < lowerCorner.z) { + position.z = lowerCorner.z; + velocity.z *= -1.0; + } else if (position.z > upperCorner.z) { + position.z = upperCorner.z; + velocity.z *= -1.0; + } + + // Orient in direction of velocity + var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, velocity); + Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, 0.33) }); + } + } +} + +// Connect a call back that happens every frame +Script.update.connect(updateFish); + +// Delete our little friends if script is stopped +Script.scriptEnding.connect(function() { + for (var i = 0; i < fish.length; i++) { + Entities.deleteEntity(fish[i].entityId); + } +}); + +var STARTING_FRACTION = 0.25; +function loadFish(howMany) { + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 3 * TANK_WIDTH)); + lowerCorner = { x: center.x - TANK_WIDTH / 2, y: center.y, z: center.z - TANK_WIDTH / 2 }; + upperCorner = { x: center.x + TANK_WIDTH / 2, y: center.y + TANK_HEIGHT, z: center.z + TANK_WIDTH / 2 }; + + for (var i = 0; i < howMany; i++) { + var position = { + x: lowerCorner.x + (upperCorner.x - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.x - lowerCorner.x) * STARTING_FRACTION, + y: lowerCorner.y + (upperCorner.y - lowerCorner.y) / 2.0 + (Math.random() - 0.5) * (upperCorner.y - lowerCorner.y) * STARTING_FRACTION, + z: lowerCorner.z + (upperCorner.z - lowerCorner.x) / 2.0 + (Math.random() - 0.5) * (upperCorner.z - lowerCorner.z) * STARTING_FRACTION + }; + + fish.push({ + entityId: Entities.addEntity({ + type: "Box", + position: position, + rotation: { x: 0, y: 0, z: 0, w: 1 }, + dimensions: { x: FISH_WIDTH, y: FISH_WIDTH, z: FISH_LENGTH }, + velocity: { x: SWIMMING_SPEED, y: SWIMMING_SPEED, z: SWIMMING_SPEED }, + damping: 0.0, + dynamic: false, + lifetime: LIFETIME, + color: { red: 0, green: 255, blue: 255 } + }) + }); + } +} From a72c49793e494574e6a65e2922d8818025f65e66 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 29 Jan 2016 10:57:17 -0800 Subject: [PATCH 04/15] speedups per code review --- examples/FlockOfFish.js | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/examples/FlockOfFish.js b/examples/FlockOfFish.js index a6b724ab9d..2e0fc53b65 100644 --- a/examples/FlockOfFish.js +++ b/examples/FlockOfFish.js @@ -11,7 +11,7 @@ var LIFETIME = 60; -var NUM_FISH = 20; +var NUM_FISH = 15; var TANK_WIDTH = 3.0; var TANK_HEIGHT = 1.0; var FISH_WIDTH = 0.03; @@ -48,24 +48,37 @@ function updateFish(deltaTime) { var averagePosition = { x: 0, y: 0, z: 0 }; var birdPositionsCounted = 0; var birdVelocitiesCounted = 0; + + // First pre-load an array with properties on all the other fish so our per-fish loop + // isn't doing it. + var flockProperties = []; + for (var i = 0; i < fish.length; i++) { + var otherProps = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); + flockProperties.push(otherProps); + } + for (var i = 0; i < fish.length; i++) { if (fish[i].entityId) { - var properties = Entities.getEntityProperties(fish[i].entityId); + // Get only the properties we need, because that is faster + // var properties = flockProperties[i]; + var properties = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); // If Bird has been deleted, bail if (properties.id != fish[i].entityId) { fish[i].entityId = false; return; } - var velocity = properties.velocity; - var position = properties.position; + // Store old values so we can check if they have changed enough to update + var velocity = { x: properties.velocity.x, y: properties.velocity.y, z: properties.velocity.z }; + var position = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; averageVelocity = { x: 0, y: 0, z: 0 }; averagePosition = { x: 0, y: 0, z: 0 }; var othersCounted = 0; for (var j = 0; j < fish.length; j++) { if (i != j) { - var otherProps = Entities.getEntityProperties(fish[j].entityId); + // Get only the properties we need, because that is faster + var otherProps = flockProperties[j]; var separation = Vec3.distance(properties.position, otherProps.position); if (separation < MAX_SIGHT_DISTANCE) { averageVelocity = Vec3.sum(averageVelocity, otherProps.velocity); @@ -91,6 +104,7 @@ function updateFish(deltaTime) { velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE); } + // Keep fish in their 'tank' if (position.x < lowerCorner.x) { position.x = lowerCorner.x; @@ -112,11 +126,19 @@ function updateFish(deltaTime) { } else if (position.z > upperCorner.z) { position.z = upperCorner.z; velocity.z *= -1.0; - } + } // Orient in direction of velocity var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, velocity); - Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, 0.33) }); + var VELOCITY_FOLLOW_RATE = 0.33; + + // Only update properties if they have changed, to save bandwidth + var MIN_POSITION_CHANGE_FOR_UPDATE = 0.001; + if (Vec3.distance(properties.position, position) < MIN_POSITION_CHANGE_FOR_UPDATE) { + Entities.editEntity(fish[i].entityId, { velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) }); + } else { + Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) }); + } } } } From c4794dd0d551a82f4bdd09ed22f7e577be21b5b2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 29 Jan 2016 13:41:43 -0800 Subject: [PATCH 05/15] Fix entity properties HTML --- examples/html/entityProperties.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index b9038ccacc..fc585a374e 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1103,10 +1103,13 @@
B
+
Skybox URL
+
+
From 0e016838b4029e7ceabf00405fe7f7006bd242c8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 10:17:08 -0800 Subject: [PATCH 06/15] tweak size of values in AudioMixer --- assignment-client/src/audio/AudioMixer.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index bcd9478700..d85e1d137b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -680,11 +680,11 @@ void AudioMixer::domainSettingsRequestComplete() { void AudioMixer::broadcastMixes() { auto nodeList = DependencyManager::get(); - int nextFrame = 0; + int64_t nextFrame = 0; QElapsedTimer timer; timer.start(); - int usecToSleep = AudioConstants::NETWORK_FRAME_USECS; + int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS; const int TRAILING_AVERAGE_FRAMES = 100; int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; @@ -826,12 +826,7 @@ void AudioMixer::broadcastMixes() { break; } - usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; // ns to us - - if (usecToSleep > int(USECS_PER_SECOND)) { - qDebug() << "DANGER: amount to sleep is" << usecToSleep; - qDebug() << "NextFrame is" << nextFrame << "and timer nsecs elapsed is" << timer.nsecsElapsed(); - } + usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - (timer.nsecsElapsed() / 1000); if (usecToSleep > 0) { usleep(usecToSleep); From e50e75cba74ce4bdbb2dd77c13efda8f4d1d3f6e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 11:41:23 -0800 Subject: [PATCH 07/15] drop assignment even if DDOS locks NL thread --- libraries/networking/src/NodeList.cpp | 3 +++ libraries/networking/src/NodeList.h | 1 + libraries/networking/src/ThreadedAssignment.cpp | 13 ++++++++++++- libraries/networking/src/ThreadedAssignment.h | 2 ++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b0cd8cce39..16277caace 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -490,6 +490,9 @@ void NodeList::processDomainServerList(QSharedPointer message) // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; + // emit our signal so listeners know we just heard from the DS + emit receivedDomainServerList(); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList); QDataStream packetStream(message->getMessage()); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 171ea0c6a7..4b196d5f7b 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -87,6 +87,7 @@ public slots: signals: void limitOfSilentDomainCheckInsReached(); + void receivedDomainServerList(); private slots: void stopKeepalivePingTimer(); void sendPendingDSPathQuery(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 303755c8f3..10f9c03cab 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -30,6 +30,10 @@ ThreadedAssignment::ThreadedAssignment(ReceivedMessage& message) : connect(&_domainServerTimer, &QTimer::timeout, this, &ThreadedAssignment::checkInWithDomainServerOrExit); _domainServerTimer.setInterval(DOMAIN_SERVER_CHECK_IN_MSECS); + + // if the NL tells us we got a DS response, clear our member variable of queued check-ins + auto nodeList = DependencyManager::get(); + connect(nodeList.data(), &NodeList::receivedDomainServerList, this, &ThreadedAssignment::clearQueuedCheckIns); } void ThreadedAssignment::setFinished(bool isFinished) { @@ -103,11 +107,18 @@ void ThreadedAssignment::sendStatsPacket() { } void ThreadedAssignment::checkInWithDomainServerOrExit() { - if (DependencyManager::get()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "WE ARE AT" << _numQueuedCheckIns << "queued check-ins"; + + // verify that the number of queued check-ins is not >= our max + // the number of queued check-ins is cleared anytime we get a response from the domain-server + if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { setFinished(true); } else { auto nodeList = DependencyManager::get(); QMetaObject::invokeMethod(nodeList.data(), "sendDomainServerCheckIn"); + + // increase the number of queued check ins + _numQueuedCheckIns++; } } diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 42d4903c2f..13b9b5bf79 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -33,6 +33,7 @@ public slots: virtual void run() = 0; Q_INVOKABLE virtual void stop() { setFinished(true); } virtual void sendStatsPacket(); + void clearQueuedCheckIns() { _numQueuedCheckIns = 0; } signals: void finished(); @@ -42,6 +43,7 @@ protected: bool _isFinished; QTimer _domainServerTimer; QTimer _statsTimer; + int _numQueuedCheckIns { 0 }; protected slots: void domainSettingsRequestFailed(); From 5551a052617898eb948a2ff9700f1698f3bce5ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 11:49:21 -0800 Subject: [PATCH 08/15] removed debug for queued check-ins --- libraries/networking/src/ThreadedAssignment.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 10f9c03cab..4f7ea76b56 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -107,8 +107,6 @@ void ThreadedAssignment::sendStatsPacket() { } void ThreadedAssignment::checkInWithDomainServerOrExit() { - qDebug() << "WE ARE AT" << _numQueuedCheckIns << "queued check-ins"; - // verify that the number of queued check-ins is not >= our max // the number of queued check-ins is cleared anytime we get a response from the domain-server if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { From c91f8c7685acd28c516e10afd108a627f1f11a6b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 11:50:13 -0800 Subject: [PATCH 09/15] add debug for DDOS AC drop --- libraries/networking/src/ThreadedAssignment.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 4f7ea76b56..9277aa70cc 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -110,6 +110,8 @@ void ThreadedAssignment::checkInWithDomainServerOrExit() { // verify that the number of queued check-ins is not >= our max // the number of queued check-ins is cleared anytime we get a response from the domain-server if (_numQueuedCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "At least" << MAX_SILENT_DOMAIN_SERVER_CHECK_INS << "have been queued without a response from domain-server" + << "Stopping the current assignment"; setFinished(true); } else { auto nodeList = DependencyManager::get(); From 8d17df338bcb90934cf92b39500c7c6739f35ed5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 12:09:39 -0800 Subject: [PATCH 10/15] allow AIM event processing during repeated injection --- libraries/audio/src/AudioInjectorManager.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 2327bda0e7..f6f4dc58bf 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -79,6 +79,11 @@ void AudioInjectorManager::run() { if (_injectors.size() > 0) { // loop through the injectors in the map and send whatever frames need to go out auto front = _injectors.top(); + + // create an InjectorQueue to hold injectors to be queued + // this allows us to call processEvents even if a single injector wants to be re-queued immediately + InjectorQueue holdingQueue; + while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { // either way we're popping this injector off - get a copy first auto injector = front.second; @@ -89,8 +94,8 @@ void AudioInjectorManager::run() { auto nextCallDelta = injector->injectNextFrame(); if (nextCallDelta >= 0 && !injector->isFinished()) { - // re-enqueue the injector with the correct timing - _injectors.emplace(usecTimestampNow() + nextCallDelta, injector); + // enqueue the injector with the correct timing in our holding queue + holdingQueue.emplace(usecTimestampNow() + nextCallDelta, injector); } } @@ -101,6 +106,12 @@ void AudioInjectorManager::run() { break; } } + + // if there are injectors in the holding queue, push them to our persistent queue now + while (!holdingQueue.empty()) { + _injectors.push(holdingQueue.top()); + holdingQueue.pop(); + } } } else { From 20cfe800363984b032e6e6b641f8494dcbd9348e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 Jan 2016 15:25:23 -0800 Subject: [PATCH 11/15] use a vector instead of priority queue to avoid double sort --- libraries/audio/src/AudioInjectorManager.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index f6f4dc58bf..032ad71145 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -82,7 +82,8 @@ void AudioInjectorManager::run() { // create an InjectorQueue to hold injectors to be queued // this allows us to call processEvents even if a single injector wants to be re-queued immediately - InjectorQueue holdingQueue; + std::vector heldInjectors; + heldInjectors.reserve(_injectors.size()); while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { // either way we're popping this injector off - get a copy first @@ -95,7 +96,7 @@ void AudioInjectorManager::run() { if (nextCallDelta >= 0 && !injector->isFinished()) { // enqueue the injector with the correct timing in our holding queue - holdingQueue.emplace(usecTimestampNow() + nextCallDelta, injector); + heldInjectors.emplace(heldInjectors.end(), usecTimestampNow() + nextCallDelta, injector); } } @@ -108,9 +109,9 @@ void AudioInjectorManager::run() { } // if there are injectors in the holding queue, push them to our persistent queue now - while (!holdingQueue.empty()) { - _injectors.push(holdingQueue.top()); - holdingQueue.pop(); + while (!heldInjectors.empty()) { + _injectors.push(heldInjectors.back()); + heldInjectors.pop_back(); } } From d3028606c5ae844ac8388ecc1f92ec716a476abe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 Jan 2016 14:47:27 -0800 Subject: [PATCH 12/15] retart the nextFrame and frameTimer on injector restart --- libraries/audio/src/AudioInjector.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index ea63df7f17..3bd46833d4 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -102,6 +102,10 @@ void AudioInjector::restart() { // reset the current send offset to zero _currentSendOffset = 0; + // reset the nextFrame and elapsed timer + _nextFrame = 0; + _frameTimer->invalidate(); + // check our state to decide if we need extra handling for the restart request if (_state == State::Finished) { // we finished playing, need to reset state so we can get going again @@ -246,6 +250,11 @@ int64_t AudioInjector::injectNextFrame() { } } + if (!_frameTimer->isValid()) { + // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here + _frameTimer->restart(); + } + int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; if (!_options.loop) { // If we aren't looping, let's make sure we don't read past the end From d7990bf88af976a3c9fbe7afe3080988ee5cf5bd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 Jan 2016 14:55:08 -0800 Subject: [PATCH 13/15] reset hasSentFirstFrame to false for double frame on first send --- libraries/audio/src/AudioInjector.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 3bd46833d4..157e973f5b 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -102,9 +102,10 @@ void AudioInjector::restart() { // reset the current send offset to zero _currentSendOffset = 0; - // reset the nextFrame and elapsed timer + // reset state to start sending from beginning again _nextFrame = 0; _frameTimer->invalidate(); + _hasSentFirstFrame = false; // check our state to decide if we need extra handling for the restart request if (_state == State::Finished) { From 11890badd8721459a2c9ce756ff61d0b6cdb2670 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 29 Jan 2016 17:16:48 -0800 Subject: [PATCH 14/15] Changes per PR suggestions --- examples/FlockOfFish.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/FlockOfFish.js b/examples/FlockOfFish.js index 2e0fc53b65..28086d304f 100644 --- a/examples/FlockOfFish.js +++ b/examples/FlockOfFish.js @@ -10,19 +10,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -var LIFETIME = 60; -var NUM_FISH = 15; +var LIFETIME = 300; // Fish live for 5 minutes +var NUM_FISH = 20; var TANK_WIDTH = 3.0; var TANK_HEIGHT = 1.0; var FISH_WIDTH = 0.03; var FISH_LENGTH = 0.15; -var MAX_SIGHT_DISTANCE = 0.4; +var MAX_SIGHT_DISTANCE = 0.8; var MIN_SEPARATION = 0.15; var AVOIDANCE_FORCE = 0.2; var COHESION_FORCE = 0.05; var ALIGNMENT_FORCE = 0.05; var SWIMMING_FORCE = 0.05; -var SWIMMING_SPEED = 2.0; +var SWIMMING_SPEED = 1.5; var fishLoaded = false; var fish = []; @@ -53,15 +53,15 @@ function updateFish(deltaTime) { // isn't doing it. var flockProperties = []; for (var i = 0; i < fish.length; i++) { - var otherProps = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); + var otherProps = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity", "rotation"]); flockProperties.push(otherProps); } for (var i = 0; i < fish.length; i++) { if (fish[i].entityId) { // Get only the properties we need, because that is faster - // var properties = flockProperties[i]; - var properties = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); + var properties = flockProperties[i]; + //var properties = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); // If Bird has been deleted, bail if (properties.id != fish[i].entityId) { fish[i].entityId = false; @@ -100,11 +100,11 @@ function updateFish(deltaTime) { // Cohesion: Steer towards center of flock var towardCenter = Vec3.subtract(averagePosition, position); velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(towardCenter), Vec3.length(velocity)), COHESION_FORCE); - // Try to swim at a constant speed - velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE); } - + // Try to swim at a constant speed + velocity = Vec3.mix(velocity, Vec3.multiply(Vec3.normalize(velocity), SWIMMING_SPEED), SWIMMING_FORCE); + // Keep fish in their 'tank' if (position.x < lowerCorner.x) { position.x = lowerCorner.x; @@ -130,14 +130,14 @@ function updateFish(deltaTime) { // Orient in direction of velocity var rotation = Quat.rotationBetween(Vec3.UNIT_NEG_Z, velocity); - var VELOCITY_FOLLOW_RATE = 0.33; + var VELOCITY_FOLLOW_RATE = 0.30; // Only update properties if they have changed, to save bandwidth var MIN_POSITION_CHANGE_FOR_UPDATE = 0.001; if (Vec3.distance(properties.position, position) < MIN_POSITION_CHANGE_FOR_UPDATE) { Entities.editEntity(fish[i].entityId, { velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) }); } else { - Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.mix(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) }); + Entities.editEntity(fish[i].entityId, { position: position, velocity: velocity, rotation: Quat.slerp(properties.rotation, rotation, VELOCITY_FOLLOW_RATE) }); } } } @@ -155,7 +155,7 @@ Script.scriptEnding.connect(function() { var STARTING_FRACTION = 0.25; function loadFish(howMany) { - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 3 * TANK_WIDTH)); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 2 * TANK_WIDTH)); lowerCorner = { x: center.x - TANK_WIDTH / 2, y: center.y, z: center.z - TANK_WIDTH / 2 }; upperCorner = { x: center.x + TANK_WIDTH / 2, y: center.y + TANK_HEIGHT, z: center.z + TANK_WIDTH / 2 }; From 1766f3fc2322e68cc614de76a40cf0b08b0caa37 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 29 Jan 2016 17:20:22 -0800 Subject: [PATCH 15/15] remove debug --- examples/FlockOfFish.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/FlockOfFish.js b/examples/FlockOfFish.js index 28086d304f..b6f5789aff 100644 --- a/examples/FlockOfFish.js +++ b/examples/FlockOfFish.js @@ -61,8 +61,7 @@ function updateFish(deltaTime) { if (fish[i].entityId) { // Get only the properties we need, because that is faster var properties = flockProperties[i]; - //var properties = Entities.getEntityProperties(fish[i].entityId, ["position", "velocity"]); - // If Bird has been deleted, bail + // If fish has been deleted, bail if (properties.id != fish[i].entityId) { fish[i].entityId = false; return;