mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +02:00
Merge pull request #973 from birarda/assignment
changes to assignment to allow for a voxelized game of life
This commit is contained in:
commit
ab098978b6
8 changed files with 215 additions and 28 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
|
||||
void run();
|
||||
signals:
|
||||
void preSendCallback();
|
||||
void willSendVisualDataCallback();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Operative__) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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);
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
PacketSenderNotify* getPacketSenderNotify() const { return _notify; }
|
||||
|
||||
virtual bool process();
|
||||
virtual void processWithoutSleep();
|
||||
|
||||
protected:
|
||||
int _packetsPerSecond;
|
||||
|
|
Loading…
Reference in a new issue