Merge pull request #973 from birarda/assignment

changes to assignment to allow for a voxelized game of life
This commit is contained in:
ZappoMan 2013-09-23 11:02:04 -07:00
commit ab098978b6
8 changed files with 215 additions and 28 deletions

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;