Merge branch 'master' of https://github.com/worklist/hifi into blendface

This commit is contained in:
Andrzej Kapolka 2013-09-23 12:43:39 -07:00
commit 7bedcd3d5a
9 changed files with 227 additions and 38 deletions

View file

@ -64,8 +64,8 @@ development.
Running Interface
-----
Using finder locate the interface.app Application in build/interface/Debug,
double-click the icon, and wait for interface to launch. At this point you will
Using Finder, locate the interface.app Application in build/interface/Debug,
double-click the icon, and wait for interface to launch. At this point you will automatically
connect to our default domain: "root.highfidelity.io".
I'm in-world, what can I do?
@ -94,13 +94,13 @@ I want to run my own virtual world!
In order to set up your own virtual world, you need to set up and run your own
local "domain". At a minimum, you must run a domain-server, voxel-server,
audio-mixer, and avatar-mixer to have a working virtual world. The audio-mixer, avatar-mixer, and voxel-server are assignments given from the domain-server to any assignment-client that reports directly to it.
audio-mixer, and avatar-mixer to have a working virtual world. The domain server gives three different types of assignments to the assignment-client: audio-mixer, avatar-mixer and voxel server.
Complete the steps above to build the system components, using the default Cmake Unix Makefiles generator. Start with an empty build directory.
cmake ..
Then from the terminal
Then from the Terminal
window, change directory into the build directory, make the needed components, and then launch them.
First we make the targets we'll need.
@ -114,15 +114,17 @@ If after this step you're seeing something like the following
you likely had Cmake generate Xcode project files and have not run `cmake ..` in a clean build directory.
Then, launch the static domain-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a seperate terminal window per target.
Then, launch the static domain-server. All of the targets will run in the foreground, so you'll either want to background it yourself or open a separate terminal window per target.
cd domain-server && ./domain-server
Then, run an assignment-client with 3 forks to fulfill the avatar-mixer, audio-mixer, and voxel-server assignments. It uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
Then, run an assignment-client with all three necessary components: avatar-mixer, audio-mixer, and voxel-server assignments. The assignment-client uses localhost as its assignment-server and talks to it on port 40102 (the default domain-server port).
In a new Terminal window, run:
./assignment-client/assignment-client -n 3
Any target can be terminated with CTRL-C (SIGINT) in the associated terminal window.
Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal window.
To test things out you'll want to run the Interface client. You can make that target with the following command:
@ -130,9 +132,9 @@ To test things out you'll want to run the Interface client. You can make that ta
Then run the executable it builds, or open interface.app if you're on OS X.
To access your local domain in Interface, open the Preferences dialog box, from
the Interface menu on OS X or the File menu on Linux, and enter "localhost" for the
server hostname in the "Domain" edit control.
To access your local domain in Interface, open your Preferences -- on OS X this is available in the Interface menu, on Linux you'll find it in the File menu. Enter "localhost" in the "Domain server" field.
If everything worked you should see "Servers: 3" in the upper right. Nice work!
In the voxel-server/src directory you will find a README that explains in
further detail how to setup and administer a voxel-server.

View file

@ -11,6 +11,7 @@
#include <AvatarData.h>
#include <NodeList.h>
#include <VoxelConstants.h>
#include "Agent.h"
#include "voxels/VoxelScriptingInterface.h"
@ -20,8 +21,11 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuf
}
void Agent::run() {
NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT);
NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
nodeList->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1);
nodeList->getNodeSocket()->setBlocking(false);
QNetworkAccessManager manager;
@ -50,19 +54,33 @@ void Agent::run() {
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
const long long VISUAL_DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000;
QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000)));
engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue);
qDebug() << "Downloaded script:" << scriptString << "\n";
qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n";
QScriptValue result = engine.evaluate(scriptString);
qDebug() << "Evaluated script.\n";
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
}
timeval thisSend;
timeval lastDomainServerCheckIn = {};
int numMicrosecondsSleep = 0;
const long long DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000;
sockaddr_in senderAddress;
unsigned char receivedData[MAX_PACKET_SIZE];
ssize_t receivedBytes;
bool hasVoxelServer = false;
while (!_shouldStop) {
// update the thisSend timeval to the current time
gettimeofday(&thisSend, NULL);
@ -78,17 +96,30 @@ void Agent::run() {
NodeList::getInstance()->sendDomainServerCheckIn();
}
// allow the scripter's call back to setup visual data
emit preSendCallback();
// flush the voxel packet queue
voxelScripter.getVoxelPacketSender()->process();
if (!hasVoxelServer) {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
hasVoxelServer = true;
}
}
} else {
// allow the scripter's call back to setup visual data
emit willSendVisualDataCallback();
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
}
voxelScripter.getVoxelPacketSender()->processWithoutSleep();
}
if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) {
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);
}
// sleep for the correct amount of time to have data send be consistently timed
if ((numMicrosecondsSleep = DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
if ((numMicrosecondsSleep = VISUAL_DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) {
usleep(numMicrosecondsSleep);
}
}

View file

@ -23,7 +23,7 @@ public:
void run();
signals:
void preSendCallback();
void willSendVisualDataCallback();
};
#endif /* defined(__hifi__Operative__) */

View file

@ -103,6 +103,9 @@ void childClient() {
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->reset();
// set the NodeList socket back to blocking
nodeList->getNodeSocket()->setBlocking(true);
// reset the logging target to the the CHILD_TARGET_NAME
Logging::setTargetName(CHILD_TARGET_NAME);
}

View file

@ -8,8 +8,23 @@
#include "VoxelScriptingInterface.h"
void VoxelScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) {
_voxelPacketSender.sendVoxelEditMessage(addPacketType, addVoxelDetails);
}
void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) {
// setup a VoxelDetail struct with the data
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
_voxelPacketSender.sendVoxelEditMessage(PACKET_TYPE_SET_VOXEL, addVoxelDetail);
// queue the packet
queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail);
}
void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale,
uchar red, uchar green, uchar blue) {
// setup a VoxelDetail struct with the data
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
// queue the destructive add
queueVoxelAdd(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, addVoxelDetail);
}

View file

@ -28,9 +28,21 @@ public slots:
/// \param green the G value for RGB color of voxel
/// \param blue the B value for RGB color of voxel
void queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
/// queues the destructive creation of a voxel which will be sent by calling process on the PacketSender
/// \param x the x-coordinate of the voxel (in VS space)
/// \param y the y-coordinate of the voxel (in VS space)
/// \param z the z-coordinate of the voxel (in VS space)
/// \param scale the scale of the voxel (in VS space)
/// \param red the R value for RGB color of voxel
/// \param green the G value for RGB color of voxel
/// \param blue the B value for RGB color of voxel
void queueDestructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue);
private:
/// attached VoxelEditPacketSender that handles queuing and sending of packets to VS
VoxelEditPacketSender _voxelPacketSender;
void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails);
};
#endif /* defined(__hifi__VoxelScriptingInterface__) */

View file

@ -1,20 +1,126 @@
/* Add your JavaScript for assignment below this line */
// Add your JavaScript for assignment below this line
// here are some examples of things you can call
Avatar.position = {x: 0, y: 0.565925, z: 10};
Avatar.chatMessage = "I am not a robot!";
Avatar.handPosition = {x: 0, y: 4.5, z: 0};
// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life)
// here I'm creating a function to fire before each data send
function dance() {
// switch the body yaw from 1 to 90
var randomAngle = Math.floor(Math.random() * 90);
if (Math.random() < 0.5) {
randomAngle * -1;
}
var NUMBER_OF_CELLS_EACH_DIMENSION = 32;
var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION;
var currentCells = [];
var nextCells = [];
var METER_LENGTH = 1 / TREE_SCALE;
var cellScale = (8 * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION;
print("TREE_SCALE = " + TREE_SCALE + "\n");
// randomly populate the cell start values
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
// create the array to hold this row
currentCells[i] = [];
Avatar.bodyYaw = randomAngle;
// create the array to hold this row in the nextCells array
nextCells[i] = [];
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
currentCells[i][j] = Math.floor(Math.random() * 2);
// put the same value in the nextCells array for first board draw
nextCells[i][j] = currentCells[i][j];
}
}
// register the call back so it fires before each data send
Agent.preSendCallback.connect(dance);
function isNeighbourAlive(i, j) {
if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION
|| i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) {
return 0;
} else {
return currentCells[i][j];
}
}
function updateCells() {
var i = 0;
var j = 0;
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
// figure out the number of live neighbours for the i-j cell
var liveNeighbours =
isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) +
isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) +
isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1);
if (currentCells[i][j]) {
// live cell
if (liveNeighbours < 2) {
// rule #1 - under-population - this cell will die
// mark it zero to mark the change
nextCells[i][j] = 0;
} else if (liveNeighbours < 4) {
// rule #2 - this cell lives
// mark it -1 to mark no change
nextCells[i][j] = -1;
} else {
// rule #3 - overcrowding - this cell dies
// mark it zero to mark the change
nextCells[i][j] = 0;
}
} else {
// dead cell
if (liveNeighbours == 3) {
// rule #4 - reproduction - this cell revives
// mark it one to mark the change
nextCells[i][j] = 1;
} else {
// this cell stays dead
// mark it -1 for no change
nextCells[i][j] = -1;
}
}
}
}
for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
if (nextCells[i][j] != -1) {
// there has been a change to this cell, change the value in the currentCells array
currentCells[i][j] = nextCells[i][j];
}
}
}
}
function sendNextCells() {
for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) {
for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) {
if (nextCells[i][j] != -1) {
// there has been a change to the state of this cell, send it
// find the x and y position for this voxel, z = 0
var x = j * cellScale;
var y = i * cellScale;
// queue a packet to add a voxel for the new cell
var color = (nextCells[i][j] == 1) ? 255 : 1;
Voxels.queueDestructiveVoxelAdd(x, y, 0, cellScale, color, color, color);
}
}
}
}
var sentFirstBoard = false;
function step() {
if (sentFirstBoard) {
// we've already sent the first full board, perform a step in time
updateCells();
} else {
// this will be our first board send
sentFirstBoard = true;
}
sendNextCells();
}
Agent.willSendVisualDataCallback.connect(step);

View file

@ -68,3 +68,22 @@ bool PacketSender::process() {
}
return isStillRunning(); // keep running till they terminate us
}
void PacketSender::processWithoutSleep() {
while (_packets.size() > 0) {
NetworkPacket& packet = _packets.front();
// send the packet through the NodeList...
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength());
if (_notify) {
_notify->packetSentNotification(packet.getLength());
}
lock();
_packets.erase(_packets.begin());
unlock();
}
}

View file

@ -43,6 +43,7 @@ public:
PacketSenderNotify* getPacketSenderNotify() const { return _notify; }
virtual bool process();
virtual void processWithoutSleep();
protected:
int _packetsPerSecond;