diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f4b920fad1..645c39868e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -689,42 +689,47 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& // this is a script upload - ask the HTTPConnection to parse the form data QList formData = connection->parseFormData(); - // create an assignment for this saved script - Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType); + // check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); + + int numInstances = 1; + if (!assignmentInstancesValue.isEmpty()) { // the user has requested a specific number of instances // so set that on the created assignment - int numInstances = assignmentInstancesValue.toInt(); - if (numInstances > 0) { - qDebug() << numInstances; - scriptAssignment->setNumberOfInstances(numInstances); - } + + numInstances = assignmentInstancesValue.toInt(); } - + const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment"; - QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION); - newPath += "/"; - // append the UUID for this script as the new filename, remove the curly braces - newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID()); - - // create a file with the GUID of the assignment in the script host locaiton - QFile scriptFile(newPath); - scriptFile.open(QIODevice::WriteOnly); - scriptFile.write(formData[0].second); - - qDebug("Saved a script for assignment at %s", qPrintable(newPath)); + for (int i = 0; i < numInstances; i++) { + + // create an assignment for this saved script + Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType); + + QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION); + newPath += "/"; + // append the UUID for this script as the new filename, remove the curly braces + newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID()); + + // create a file with the GUID of the assignment in the script host locaiton + QFile scriptFile(newPath); + scriptFile.open(QIODevice::WriteOnly); + scriptFile.write(formData[0].second); + + qDebug("Saved a script for assignment at %s", qPrintable(newPath)); + + // add the script assigment to the assignment queue + _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment)); + } // respond with a 200 code for successful upload connection->respond(HTTPConnection::StatusCode200); - - // add the script assigment to the assignment queue - _assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment)); } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { if (path.startsWith(URI_NODE)) { @@ -834,14 +839,7 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig if (assignment->getType() == Assignment::AgentType) { // if there is more than one instance to send out, simply decrease the number of instances - - if (assignment->getNumberOfInstances() == 1) { - return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin()); - } else { - assignment->decrementNumberOfInstances(); - return *sharedAssignment; - } - + return _assignmentQueue.takeAt(sharedAssignment - _assignmentQueue.begin()); } else { // remove the assignment from the queue SharedAssignmentPointer deployableAssignment = _assignmentQueue.takeAt(sharedAssignment diff --git a/examples/bot.js b/examples/bot.js index 8ac718e34d..53f723381c 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -18,8 +18,8 @@ function getRandomInt (min, max) { } // choose a random x and y in the range of 0 to 50 -positionX = getRandomFloat(0, 50); -positionZ = getRandomFloat(0, 50); +positionX = getRandomFloat(0, 18); +positionZ = getRandomFloat(0, 18); // change the avatar's position to the random one Avatar.position = {x: positionX, y: 0, z: positionZ}; @@ -49,6 +49,6 @@ if (botNumber <= 20) { // set the face model fst using the bot number // there is no need to change the body model - we're using the default Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; -Avatar.bodyModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; +Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; Agent.isAvatar = true; \ No newline at end of file diff --git a/examples/ribbon.js b/examples/ribbon.js new file mode 100644 index 0000000000..7858c744fe --- /dev/null +++ b/examples/ribbon.js @@ -0,0 +1,173 @@ +// +// ribbon.js +// hifi +// +// Created by Andrzej Kapolka on 2/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +function vectorMultiply(vector, scalar) { + return [ vector[0] * scalar, vector[1] * scalar, vector[2] * scalar ]; +} + +function vectorAdd(firstVector, secondVector) { + return [ firstVector[0] + secondVector[0], firstVector[1] + secondVector[1], firstVector[2] + secondVector[2] ]; +} + +function vectorSubtract(firstVector, secondVector) { + return [ firstVector[0] - secondVector[0], firstVector[1] - secondVector[1], firstVector[2] - secondVector[2] ]; +} + +function vectorCross(firstVector, secondVector) { + return [ firstVector[1] * secondVector[2] - firstVector[2] * secondVector[1], + firstVector[2] * secondVector[0] - firstVector[0] * secondVector[2], + firstVector[0] * secondVector[1] - firstVector[1] * secondVector[0] ]; +} + +function vectorLength(vector) { + return Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]); +} + +function vectorNormalize(vector) { + return vectorMultiply(vector, 1.0 / vectorLength(vector)); +} + +function vectorClone(vector) { + return [ vector[0], vector[1], vector[2] ]; +} + +function mix(first, second, amount) { + return first + (second - first) * amount; +} + +function vectorMix(firstVector, secondVector, amount) { + return vectorAdd(firstVector, vectorMultiply(vectorSubtract(secondVector, firstVector), amount)); +} + +function randomVector(minVector, maxVector) { + return [ mix(minVector[0], maxVector[0], Math.random()), mix(minVector[1], maxVector[1], Math.random()), + mix(minVector[2], maxVector[2], Math.random()) ]; +} + +function applyToLine(start, end, granularity, fn) { + // determine the number of steps from the maximum length + var steps = Math.max( + Math.abs(Math.floor(start[0] / granularity) - Math.floor(end[0] / granularity)), + Math.abs(Math.floor(start[1] / granularity) - Math.floor(end[1] / granularity)), + Math.abs(Math.floor(start[2] / granularity) - Math.floor(end[2] / granularity))); + var position = vectorClone(start); + var increment = vectorMultiply(vectorSubtract(end, start), 1.0 / steps); + for (var i = 0; i <= steps; i++) { + fn(granularity * Math.floor(position[0] / granularity), + granularity * Math.floor(position[1] / granularity), + granularity * Math.floor(position[2] / granularity), + granularity); + position = vectorAdd(position, increment); + } +} + +function drawLine(start, end, color, granularity) { + applyToLine(start, end, granularity, function(x, y, z, scale) { + Voxels.setVoxel(x, y, z, scale, color[0], color[1], color[2]); + }); +} + +function eraseLine(start, end, granularity) { + applyToLine(start, end, granularity, function(x, y, z, scale) { + Voxels.eraseVoxel(x, y, z, scale); + }); +} + +function getHueColor(hue) { + // see http://en.wikipedia.org/wiki/HSL_and_HSV + var hPrime = hue / 60.0; + var x = Math.floor(255.0 * (1.0 - Math.abs(hPrime % 2.0 - 1.0))); + if (hPrime < 1) { + return [255, x, 0]; + + } else if (hPrime < 2) { + return [x, 255, 0]; + + } else if (hPrime < 3) { + return [0, 255, x]; + + } else if (hPrime < 4) { + return [0, x, 255]; + + } else if (hPrime < 5) { + return [x, 0, 255]; + + } else { // hPrime < 6 + return [255, 0, x]; + } +} + +var UNIT_MIN = [-1.0, -1.0, -1.0]; +var UNIT_MAX = [1.0, 1.0, 1.0]; + +var EPSILON = 0.00001; + +var BOUNDS_MIN = [5.0, 0.0, 5.0]; +var BOUNDS_MAX = [15.0, 10.0, 15.0]; + +var GRANULARITY = 1.0 / 16.0; + +var WIDTH = 0.5; + +var HISTORY_LENGTH = 300; + +var stateHistory = []; +var position; +var velocity; +var hueAngle = 0; +var smoothedOffset; + +function step() { + if (stateHistory.length === 0) { + // start at a random position within the bounds, with a random velocity + position = randomVector(BOUNDS_MIN, BOUNDS_MAX); + do { + velocity = randomVector(UNIT_MIN, UNIT_MAX); + } while (vectorLength(velocity) < EPSILON); + velocity = vectorMultiply(velocity, GRANULARITY * 0.5 / vectorLength(velocity)); + smoothedOffset = [0.0, 0.0, 0.0]; + } + + var right = vectorCross(velocity, [0.0, 1.0, 0.0]); + if (vectorLength(right) < EPSILON) { + right = [1.0, 0.0, 0.0]; + } else { + right = vectorNormalize(right); + } + var up = vectorNormalize(vectorCross(right, velocity)); + var ANGULAR_SPEED = 2.0; + var radians = hueAngle * Math.PI * ANGULAR_SPEED / 180.0; + var offset = vectorAdd(vectorMultiply(right, WIDTH * Math.cos(radians)), vectorMultiply(up, WIDTH * Math.sin(radians))); + var OFFSET_SMOOTHING = 0.9; + smoothedOffset = vectorMix(offset, smoothedOffset, OFFSET_SMOOTHING); + + var state = { start: vectorAdd(position, smoothedOffset), end: vectorSubtract(position, smoothedOffset) }; + drawLine(state.start, state.end, getHueColor(hueAngle), GRANULARITY); + stateHistory.push(state); + if (stateHistory.length > HISTORY_LENGTH) { + var last = stateHistory.shift(); + eraseLine(last.start, last.end, GRANULARITY); + } + + // update position, check against bounds + position = vectorAdd(position, velocity); + for (var i = 0; i < 3; i++) { + if (position[i] < BOUNDS_MIN[i]) { + velocity[i] = -velocity[i]; + position[i] += 2.0 * (BOUNDS_MIN[i] - position[i]); + + } else if (position[i] > BOUNDS_MAX[i]) { + velocity[i] = -velocity[i]; + position[i] += 2.0 * (BOUNDS_MAX[i] - position[i]); + } + } + var MAX_HUE_ANGLE = 360; + hueAngle = (hueAngle + 1) % MAX_HUE_ANGLE; +} + +Script.willSendVisualDataCallback.connect(step); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index be98254696..9a693f4a86 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -171,6 +171,8 @@ protected: float getPelvisFloatingHeight() const; float getPelvisToHeadLength() const; + void renderDisplayName(); + private: bool _initialized; @@ -178,8 +180,6 @@ private: void renderBody(); void renderBillboard(); - - void renderDisplayName(); }; #endif diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e9a510e0da..80f355f41d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -463,8 +463,8 @@ void MyAvatar::render(bool forceRenderHead) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { renderBody(forceRenderHead); } - - //renderDebugBodyPoints(); + setShowDisplayName(true); + renderDisplayName(); if (!_chatMessage.empty()) { int width = 0; diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 83296991f3..992ec96a67 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -45,7 +45,6 @@ Assignment::Assignment() : _type(Assignment::AllTypes), _pool(), _location(Assignment::LocalLocation), - _numberOfInstances(1), _payload() { @@ -57,7 +56,6 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const _type(type), _pool(pool), _location(location), - _numberOfInstances(1), _payload() { if (_command == Assignment::CreateCommand) { @@ -69,7 +67,6 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const Assignment::Assignment(const QByteArray& packet) : _pool(), _location(GlobalLocation), - _numberOfInstances(1), _payload() { PacketType packetType = packetTypeForPacket(packet); @@ -99,7 +96,6 @@ Assignment::Assignment(const Assignment& otherAssignment) { _type = otherAssignment._type; _location = otherAssignment._location; _pool = otherAssignment._pool; - _numberOfInstances = otherAssignment._numberOfInstances; _payload = otherAssignment._payload; } @@ -117,8 +113,6 @@ void Assignment::swap(Assignment& otherAssignment) { swap(_type, otherAssignment._type); swap(_location, otherAssignment._location); swap(_pool, otherAssignment._pool); - - swap(_numberOfInstances, otherAssignment._numberOfInstances); swap(_payload, otherAssignment._payload); } diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index dd34751b57..a8ab3be4a8 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -79,11 +79,7 @@ public: void setPool(const QString& pool) { _pool = pool; }; const QString& getPool() const { return _pool; } - - int getNumberOfInstances() const { return _numberOfInstances; } - void setNumberOfInstances(int numberOfInstances) { _numberOfInstances = numberOfInstances; } - void decrementNumberOfInstances() { --_numberOfInstances; } - + const char* getTypeName() const; // implement parseData to return 0 so we can be a subclass of NodeData @@ -99,7 +95,6 @@ protected: Assignment::Type _type; /// the type of the assignment, defines what the assignee will do QString _pool; /// the destination pool for this assignment Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs - int _numberOfInstances; /// the number of instances of this assignment QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed };