Merge pull request #386 from ZappoMan/voxel_animation

Fix some crashing issues on delete
This commit is contained in:
birarda 2013-05-22 14:27:29 -07:00
commit 9ec04855f8
6 changed files with 215 additions and 84 deletions

View file

@ -65,6 +65,110 @@ static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
}
}
const float BUG_VOXEL_SIZE = 0.0625f / TREE_SCALE;
glm::vec3 bugPosition = glm::vec3(BUG_VOXEL_SIZE * 10.0, 0, BUG_VOXEL_SIZE * 10.0);
glm::vec3 bugDirection = glm::vec3(0, 0, 1);
const unsigned char bugColor[3] = {0, 255, 255};
const int VOXELS_PER_BUG = 14;
const glm::vec3 bugParts[VOXELS_PER_BUG] = {
// body
glm::vec3(0, 0, -3),
glm::vec3(0, 0, -2),
glm::vec3(0, 0, -1),
glm::vec3(0, 0, 0),
glm::vec3(0, 0, 1),
glm::vec3(0, 0, 2),
// eyes
glm::vec3(1, 0, 3),
glm::vec3(-1, 0, 3),
// wings
glm::vec3(1, 0, 1),
glm::vec3(2, 0, 1),
glm::vec3(3, 0, 1),
glm::vec3(-1, 0, 1),
glm::vec3(-2, 0, 1),
glm::vec3(-3, 0, 1),
};
static void renderMovingBug() {
VoxelDetail details[VOXELS_PER_BUG];
unsigned char* bufferOut;
int sizeOut;
// Generate voxels for where bug used to be
for (int i = 0; i < VOXELS_PER_BUG; i++) {
details[i].s = BUG_VOXEL_SIZE;
details[i].x = bugPosition.x + (bugParts[i].x * BUG_VOXEL_SIZE);
details[i].y = bugPosition.y + (bugParts[i].y * BUG_VOXEL_SIZE);
details[i].z = bugPosition.z + (bugParts[i].z * BUG_VOXEL_SIZE);
details[i].red = bugColor[0];
details[i].green = bugColor[1];
details[i].blue = bugColor[2];
}
// send the "erase message" first...
PACKET_HEADER message = PACKET_HEADER_ERASE_VOXEL;
if (createVoxelEditMessage(message, 0, VOXELS_PER_BUG, (VoxelDetail*)&details, bufferOut, sizeOut)){
::packetsSent++;
::bytesSent += sizeOut;
if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut);
}
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1);
delete[] bufferOut;
}
// Move the bug...
bugPosition.x += (bugDirection.x * BUG_VOXEL_SIZE);
bugPosition.y += (bugDirection.y * BUG_VOXEL_SIZE);
bugPosition.z += (bugDirection.z * BUG_VOXEL_SIZE);
// Check boundaries
if (bugPosition.z > 0.25) {
bugDirection.z = -1;
}
if (bugPosition.z < 0.01) {
bugDirection.z = 1;
}
printf("bugPosition=(%f,%f,%f)\n", bugPosition.x, bugPosition.y, bugPosition.z);
printf("bugDirection=(%f,%f,%f)\n", bugDirection.x, bugDirection.y, bugDirection.z);
// would be nice to add some randomness here...
// Generate voxels for where bug is going to
for (int i = 0; i < VOXELS_PER_BUG; i++) {
details[i].s = BUG_VOXEL_SIZE;
details[i].x = bugPosition.x + (bugParts[i].x * BUG_VOXEL_SIZE);
details[i].y = bugPosition.y + (bugParts[i].y * BUG_VOXEL_SIZE);
details[i].z = bugPosition.z + (bugParts[i].z * BUG_VOXEL_SIZE);
details[i].red = bugColor[0];
details[i].green = bugColor[1];
details[i].blue = bugColor[2];
}
// send the "create message" ...
message = PACKET_HEADER_SET_VOXEL_DESTRUCTIVE;
if (createVoxelEditMessage(message, 0, VOXELS_PER_BUG, (VoxelDetail*)&details, bufferOut, sizeOut)){
::packetsSent++;
::bytesSent += sizeOut;
if (::shouldShowPacketsPerSecond) {
printf("sending packet of size=%d\n", sizeOut);
}
AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1);
delete[] bufferOut;
}
}
float intensity = 0.5f;
float intensityIncrement = 0.1f;
const float MAX_INTENSITY = 1.0f;
@ -325,6 +429,7 @@ void* animateVoxels(void* args) {
//sendVoxelBlinkMessage();
sendBlinkingStringOfLights();
sendBillboard();
//renderMovingBug();
double end = usecTimestampNow();
double elapsedSeconds = (end - ::start) / 1000000.0;

View file

@ -337,16 +337,15 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
} else {
voxelsUpdated += updateNodeInArraysAsPartialVBO(node);
}
node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
// If the node has been asked to be deleted, but we've gotten to here, after updateNodeInArraysXXX()
// then it means our VBOs are "clean" and our vertices have been removed or not added. So we can now
// safely remove the node from the tree and actually delete it.
// otherwise honor our calculated shouldRender
if (node->isStagedForDeletion()) {
_tree->deleteVoxelCodeFromTree(node->getOctalCode());
}
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated;
}

View file

@ -33,4 +33,10 @@ const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e';
const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
const PACKET_HEADER PACKET_HEADER_DOMAIN_RFD = 'C';
// These are supported Z-Command
#define ERASE_ALL_COMMAND "erase all"
#define ADD_SCENE_COMMAND "add scene"
#define TEST_COMMAND "a message"
#endif

View file

@ -30,14 +30,15 @@ int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
return voxelSizeScale / powf(2, renderLevel);
}
VoxelTree::VoxelTree() :
VoxelTree::VoxelTree(bool shouldReaverage) :
voxelsCreated(0),
voxelsColored(0),
voxelsBytesRead(0),
voxelsCreatedStats(100),
voxelsColoredStats(100),
voxelsBytesReadStats(100),
_isDirty(true) {
_isDirty(true),
_shouldReaverage(shouldReaverage) {
rootNode = new VoxelNode();
}
@ -255,12 +256,13 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) {
unsigned char* octalCode = pointToVoxel(x,y,z,s,0,0,0);
deleteVoxelCodeFromTree(octalCode, stage);
delete octalCode; // cleanup memory
reaverageVoxelColors(rootNode);
}
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the node from the tree.
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage) {
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, bool collapseEmptyTrees) {
VoxelNode* parentNode = NULL;
VoxelNode* nodeToDelete = nodeForOctalCode(rootNode, codeBuffer, &parentNode);
// If the node exists...
@ -277,14 +279,13 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage) {
}
// If we're not a colored leaf, and we have no children, then delete ourselves
// This will collapse the empty tree above us.
if (parentNode->getChildCount() == 0 && !parentNode->isColored()) {
// This will collapse the empty tree above us.
if (collapseEmptyTrees && parentNode->getChildCount() == 0 && !parentNode->isColored()) {
// Can't delete the root this way.
if (parentNode != rootNode) {
deleteVoxelCodeFromTree(parentNode->getOctalCode(),stage);
deleteVoxelCodeFromTree(parentNode->getOctalCode(), stage, collapseEmptyTrees);
}
}
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
_isDirty = true;
}
} else if (nodeToDelete->isLeaf()) {
@ -358,11 +359,12 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe
unsigned char octets = (unsigned char)*pVoxelData;
int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color!
deleteVoxelCodeFromTree(pVoxelData);
deleteVoxelCodeFromTree(pVoxelData, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE);
pVoxelData+=voxelDataSize;
atByte+=voxelDataSize;
}
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
}
void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
@ -412,19 +414,20 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
}
void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) {
bool hasChildren = false;
// if our tree is a reaveraging tree, then we do this, otherwise we don't do anything
if (_shouldReaverage) {
bool hasChildren = false;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i)) {
reaverageVoxelColors(startNode->getChildAtIndex(i));
hasChildren = true;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i)) {
reaverageVoxelColors(startNode->getChildAtIndex(i));
hasChildren = true;
}
}
}
if (hasChildren) {
bool childrenCollapsed = startNode->collapseIdenticalLeaves();
if (!childrenCollapsed) {
// collapseIdenticalLeaves() returns true if it collapses the leaves
// in which case we don't need to set the average color
if (hasChildren && !startNode->collapseIdenticalLeaves()) {
startNode->setColorFromAverageOfChildren();
}
}

View file

@ -24,6 +24,10 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
#define NO_COLOR false
#define WANT_COLOR true
#define IGNORE_VIEW_FRUSTUM NULL
#define JUST_STAGE_DELETION true
#define ACTUALLY_DELETE false
#define COLLAPSE_EMPTY_TREE true
#define DONT_COLLAPSE false
class VoxelTree {
public:
@ -37,7 +41,7 @@ public:
SimpleMovingAverage voxelsColoredStats;
SimpleMovingAverage voxelsBytesReadStats;
VoxelTree();
VoxelTree(bool shouldReaverage = false);
~VoxelTree();
VoxelNode *rootNode;
@ -49,7 +53,8 @@ public:
void readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS);
void readCodeColorBufferToTree(unsigned char *codeColorBuffer, bool destructive = false);
void deleteVoxelCodeFromTree(unsigned char *codeBuffer, bool stage = false);
void deleteVoxelCodeFromTree(unsigned char *codeBuffer, bool stage = ACTUALLY_DELETE,
bool collapseEmptyTrees = DONT_COLLAPSE);
void printTreeForDebugging(VoxelNode *startNode);
void reaverageVoxelColors(VoxelNode *startNode);
@ -111,6 +116,7 @@ private:
bool _isDirty;
unsigned long int _nodesChangedFromBitstream;
bool _shouldReaverage;
};
int boundaryDistanceForRenderLevel(unsigned int renderLevel);

View file

@ -49,7 +49,7 @@ int PACKETS_PER_CLIENT_PER_INTERVAL = 50;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
VoxelTree randomTree;
VoxelTree randomTree(true); // this is a reaveraging tree
bool wantVoxelPersist = true;
bool wantLocalDomain = false;
@ -228,12 +228,17 @@ void resInVoxelDistributor(AgentList* agentList,
}
}
pthread_mutex_t treeLock;
// Version of voxel distributor that sends the deepest LOD level at once
void deepestLevelVoxelDistributor(AgentList* agentList,
AgentList::iterator& agent,
VoxelAgentData* agentData,
bool viewFrustumChanged) {
pthread_mutex_lock(&::treeLock);
int maxLevelReached = 0;
double start = usecTimestampNow();
@ -362,6 +367,8 @@ void deepestLevelVoxelDistributor(AgentList* agentList,
} // end if bag wasn't empty, and so we sent stuff...
pthread_mutex_unlock(&::treeLock);
}
double lastPersistVoxels = 0;
@ -407,11 +414,11 @@ void *distributeVoxelsToListeners(void *args) {
VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData();
// Sometimes the agent data has not yet been linked, in which case we can't really do anything
if (agentData) {
bool viewFrustumChanged = agentData->updateCurrentViewFrustum();
if (agentData) {
bool viewFrustumChanged = agentData->updateCurrentViewFrustum();
if (::debugVoxelSending) {
printf("agentData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
printf("agentData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
if (agentData->getWantResIn()) {
resInVoxelDistributor(agentList, agent, agentData);
@ -440,8 +447,10 @@ void attachVoxelAgentDataToAgent(Agent *newAgent) {
}
}
int main(int argc, const char * argv[])
{
int main(int argc, const char * argv[]) {
pthread_mutex_init(&::treeLock, NULL);
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_VOXEL, VOXEL_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);
@ -569,89 +578,91 @@ int main(int argc, const char * argv[])
PerformanceWarning warn(::shouldShowAnimationDebug,
destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL",
::shouldShowAnimationDebug);
unsigned short int itemNumber = (*((unsigned short int*)&packetData[1]));
if (::shouldShowAnimationDebug) {
unsigned short int itemNumber = (*((unsigned short int*)&packetData[1]));
if (::shouldShowAnimationDebug) {
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
destructive ? "PACKET_HEADER_SET_VOXEL_DESTRUCTIVE" : "PACKET_HEADER_SET_VOXEL",
receivedBytes,itemNumber);
}
int atByte = 3;
unsigned char* pVoxelData = (unsigned char*)&packetData[3];
while (atByte < receivedBytes) {
unsigned char octets = (unsigned char)*pVoxelData;
int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color!
int voxelCodeSize = bytesRequiredForCodeLength(octets);
int atByte = sizeof(PACKET_HEADER) + sizeof(itemNumber);
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
while (atByte < receivedBytes) {
unsigned char octets = (unsigned char)*voxelData;
const int COLOR_SIZE_IN_BYTES = 3;
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int voxelCodeSize = bytesRequiredForCodeLength(octets);
// color randomization on insert
int colorRandomizer = ::wantColorRandomizer ? randIntInRange (-50, 50) : 0;
int red = pVoxelData[voxelCodeSize+0];
int green = pVoxelData[voxelCodeSize+1];
int blue = pVoxelData[voxelCodeSize+2];
// color randomization on insert
int colorRandomizer = ::wantColorRandomizer ? randIntInRange (-50, 50) : 0;
int red = voxelData[voxelCodeSize + 0];
int green = voxelData[voxelCodeSize + 1];
int blue = voxelData[voxelCodeSize + 2];
if (::shouldShowAnimationDebug) {
printf("insert voxels - wantColorRandomizer=%s old r=%d,g=%d,b=%d \n",
(::wantColorRandomizer?"yes":"no"),red,green,blue);
}
red = std::max(0,std::min(255,red + colorRandomizer));
green = std::max(0,std::min(255,green + colorRandomizer));
blue = std::max(0,std::min(255,blue + colorRandomizer));
red = std::max(0, std::min(255, red + colorRandomizer));
green = std::max(0, std::min(255, green + colorRandomizer));
blue = std::max(0, std::min(255, blue + colorRandomizer));
if (::shouldShowAnimationDebug) {
printf("insert voxels - wantColorRandomizer=%s NEW r=%d,g=%d,b=%d \n",
(::wantColorRandomizer?"yes":"no"),red,green,blue);
}
pVoxelData[voxelCodeSize+0]=red;
pVoxelData[voxelCodeSize+1]=green;
pVoxelData[voxelCodeSize+2]=blue;
voxelData[voxelCodeSize + 0] = red;
voxelData[voxelCodeSize + 1] = green;
voxelData[voxelCodeSize + 2] = blue;
if (::shouldShowAnimationDebug) {
float* vertices = firstVertexForCode(pVoxelData);
printf("inserting voxel at: %f,%f,%f\n",vertices[0],vertices[1],vertices[2]);
float* vertices = firstVertexForCode(voxelData);
printf("inserting voxel at: %f,%f,%f\n", vertices[0], vertices[1], vertices[2]);
delete []vertices;
}
randomTree.readCodeColorBufferToTree(pVoxelData, destructive);
// skip to next
pVoxelData+=voxelDataSize;
atByte+=voxelDataSize;
}
randomTree.readCodeColorBufferToTree(voxelData, destructive);
// skip to next
voxelData += voxelDataSize;
atByte += voxelDataSize;
}
}
if (packetData[0] == PACKET_HEADER_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them
//printf("got Erase Voxels message, have voxel tree do the work... randomTree.processRemoveVoxelBitstream()\n");
randomTree.processRemoveVoxelBitstream((unsigned char*)packetData,receivedBytes);
// Send these bits off to the VoxelTree class to process them
pthread_mutex_lock(&::treeLock);
randomTree.processRemoveVoxelBitstream((unsigned char*)packetData, receivedBytes);
pthread_mutex_unlock(&::treeLock);
}
if (packetData[0] == PACKET_HEADER_Z_COMMAND) {
// the Z command is a special command that allows the sender to send the voxel server high level semantic
// requests, like erase all, or add sphere scene
char* command = (char*) &packetData[1]; // start of the command
int commandLength = strlen(command); // commands are null terminated strings
int totalLength = 1+commandLength+1;
// the Z command is a special command that allows the sender to send the voxel server high level semantic
// requests, like erase all, or add sphere scene
char* command = (char*) &packetData[1]; // start of the command
int commandLength = strlen(command); // commands are null terminated strings
int totalLength = sizeof(PACKET_HEADER_Z_COMMAND) + commandLength + 1; // 1 for null termination
printf("got Z message len(%ld)= %s\n",receivedBytes,command);
printf("got Z message len(%ld)= %s\n", receivedBytes, command);
while (totalLength <= receivedBytes) {
if (0==strcmp(command,(char*)"erase all")) {
printf("got Z message == erase all\n");
eraseVoxelTreeAndCleanupAgentVisitData();
}
if (0==strcmp(command,(char*)"add scene")) {
printf("got Z message == add scene\n");
addSphereScene(&randomTree);
}
if (0==strcmp(command,(char*)"a message")) {
printf("got Z message == a message, nothing to do, just report\n");
}
totalLength += commandLength+1;
}
while (totalLength <= receivedBytes) {
if (strcmp(command, ERASE_ALL_COMMAND) == 0) {
printf("got Z message == erase all\n");
eraseVoxelTreeAndCleanupAgentVisitData();
}
if (strcmp(command, ADD_SCENE_COMMAND) == 0) {
printf("got Z message == add scene\n");
addSphereScene(&randomTree);
}
if (strcmp(command, TEST_COMMAND) == 0) {
printf("got Z message == a message, nothing to do, just report\n");
}
totalLength += commandLength + 1; // 1 for null termination
}
// Now send this to the connected agents so they can also process these messages
printf("rebroadcasting Z message to connected agents... agentList.broadcastToAgents()\n");
agentList->broadcastToAgents(packetData,receivedBytes, &AGENT_TYPE_AVATAR, 1);
// Now send this to the connected agents so they can also process these messages
printf("rebroadcasting Z message to connected agents... agentList.broadcastToAgents()\n");
agentList->broadcastToAgents(packetData, receivedBytes, &AGENT_TYPE_AVATAR, 1);
}
// If we got a PACKET_HEADER_HEAD_DATA, then we're talking to an AGENT_TYPE_AVATAR, and we
// need to make sure we have it in our agentList.
@ -669,6 +680,7 @@ int main(int argc, const char * argv[])
}
pthread_join(sendVoxelThread, NULL);
pthread_mutex_destroy(&::treeLock);
return 0;
}