Merge remote-tracking branch 'origin/master'

This commit is contained in:
Philip Rosedale 2013-04-30 13:44:49 -07:00
commit 6142372219
24 changed files with 1098 additions and 797 deletions

View file

@ -298,6 +298,7 @@ void Avatar::setMousePressed( bool d ) {
_mousePressed = d;
}
void Avatar::simulate(float deltaTime) {
// update balls
@ -323,14 +324,13 @@ void Avatar::simulate(float deltaTime) {
float closestDistance = 10000.0f;
AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *otherAvatar = (Avatar *)agent->getLinkedData();
// check for collisions with other avatars and respond
updateCollisionWithOtherAvatar(otherAvatar, deltaTime );
// test other avatar hand position for proximity
glm::vec3 v( _bone[ AVATAR_BONE_RIGHT_SHOULDER ].position );
v -= otherAvatar->getBonePosition( AVATAR_BONE_RIGHT_HAND );
@ -424,9 +424,8 @@ void Avatar::simulate(float deltaTime) {
// decay velocity
_velocity *= ( 1.0 - LIN_VEL_DECAY * deltaTime );
//
// Update Head information
//
// update head information
updateHead(deltaTime);
// Decay head back to center if turned on
@ -524,17 +523,12 @@ void Avatar::simulate(float deltaTime) {
const float AUDIO_AVERAGING_SECS = 0.05;
_head.averageLoudness = (1.f - deltaTime / AUDIO_AVERAGING_SECS) * _head.averageLoudness +
(deltaTime / AUDIO_AVERAGING_SECS) * _audioLoudness;
_speed = glm::length( _velocity );
float rotationalSpeed = fabs( _bodyYawDelta );
if ( _speed + rotationalSpeed > 0.2 ) {
_mode = AVATAR_MODE_WALKING;
} else {
_mode = AVATAR_MODE_INTERACTING;
}
}
float Avatar::getHeight() {
return _height;
}
@ -548,23 +542,21 @@ void Avatar::updateCollisionWithSphere( glm::vec3 position, float radius, float
float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere);
if ( distanceToBigSphere < myBodyApproximateBoundingRadius + radius ) {
for (int b = 0; b < NUM_AVATAR_BONES; b++) {
if ( _bone[b].isCollidable ) {
glm::vec3 vectorFromJointToBigSphereCenter(_bone[b].springyPosition - position);
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter);
float combinedRadius = _bone[b].radius + radius;
if ( distanceToBigSphereCenter < combinedRadius ) {
jointCollision = true;
if (distanceToBigSphereCenter > 0.0) {
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter;
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration;
_bone[b].springyVelocity += collisionForce * 30.0f * deltaTime;
_velocity += collisionForce * 100.0f * deltaTime;
_bone[b].springyPosition = position + directionVector * combinedRadius;
}
glm::vec3 vectorFromJointToBigSphereCenter(_bone[b].springyPosition - position);
float distanceToBigSphereCenter = glm::length(vectorFromJointToBigSphereCenter);
float combinedRadius = _bone[b].radius + radius;
if ( distanceToBigSphereCenter < combinedRadius ) {
jointCollision = true;
if (distanceToBigSphereCenter > 0.0) {
glm::vec3 directionVector = vectorFromJointToBigSphereCenter / distanceToBigSphereCenter;
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
glm::vec3 collisionForce = vectorFromJointToBigSphereCenter * penetration;
_bone[b].springyVelocity += collisionForce * 30.0f * deltaTime;
_velocity += collisionForce * 100.0f * deltaTime;
_bone[b].springyPosition = position + directionVector * combinedRadius;
}
}
}
@ -586,40 +578,63 @@ void Avatar::updateCollisionWithOtherAvatar( Avatar * otherAvatar, float deltaTi
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
if ( glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF ) {
float bodyMomentum = 1.0f;
glm::vec3 bodyPushForce = glm::vec3( 0.0, 0.0, 0.0 );
// loop through the bones of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_BONES; b++) {
if ( _bone[b].isCollidable ) {
for (int o=b+1; o<NUM_AVATAR_BONES; o++) {
if ( _bone[o].isCollidable ) {
if ( otherAvatar->_bone[o].isCollidable ) {
glm::vec3 vectorBetweenJoints(_bone[b].springyPosition - otherAvatar->_bone[o].springyPosition);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
// to avoid divide by zero
if ( distanceBetweenJoints > 0.0 ) {
if ( distanceBetweenJoints > 0.0 ) { // to avoid divide by zero
float combinedRadius = _bone[b].radius + otherAvatar->_bone[o].radius;
// check for collision
if ( distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
// push ball away from colliding other ball and puch avatar body (_velocity) as well
_bone[b].springyVelocity += directionVector * COLLISION_BALL_FORCE * deltaTime;
_velocity += directionVector * COLLISION_BODY_FORCE * deltaTime;
// push balls away from each other and apply friction
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * deltaTime;
float ballMomentum = COLLISION_BALL_FRICTION * deltaTime;
if ( ballMomentum < 0.0 ) { ballMomentum = 0.0;}
_bone[b].springyVelocity += ballPushForce;
otherAvatar->_bone[o].springyVelocity -= ballPushForce;
// apply fruction to _velocity
float momentum = 1.0 - COLLISION_FRICTION * deltaTime;
if ( momentum < 0.0 ) { momentum = 0.0;}
_velocity *= momentum;
}
}
}
}
}
}
}
}
_bone[b].springyVelocity *= 0.9;
otherAvatar->_bone[o].springyVelocity *= 0.9;
// accumulate forces and frictions to the velocities of avatar bodies
bodyPushForce += directionVector * COLLISION_BODY_FORCE * deltaTime;
bodyMomentum -= COLLISION_BODY_FRICTION * deltaTime;
if ( bodyMomentum < 0.0 ) { bodyMomentum = 0.0;}
}// check for collision
} // to avoid divide by zero
} // o loop
} // collidable
} // b loop
} // collidable
//apply forces and frictions on the bodies of both avatars
_velocity += bodyPushForce;
otherAvatar->_velocity -= bodyPushForce;
_velocity *= bodyMomentum;
otherAvatar->_velocity *= bodyMomentum;
} // bounding sphere collision
} //method
void Avatar::setDisplayingHead( bool displayingHead ) {
@ -1078,20 +1093,20 @@ void Avatar::updateBodySprings( float deltaTime ) {
if ( length > 0.0f ) {
glm::vec3 springDirection = springVector / length;
float force = ( length - _bone[b].length ) * _springForce * deltaTime;
float force = (length - _bone[b].length) * BODY_SPRING_FORCE * deltaTime;
_bone[b].springyVelocity -= springDirection * force;
if ( _bone[b].parent != AVATAR_BONE_NULL ) {
_bone[ _bone[b].parent ].springyVelocity += springDirection * force;
_bone[_bone[b].parent].springyVelocity += springDirection * force;
}
}
_bone[b].springyVelocity += ( _bone[b].position - _bone[b].springyPosition ) * _bone[b].springBodyTightness * deltaTime;
_bone[b].springyVelocity += (_bone[b].position - _bone[b].springyPosition) * _bone[b].springBodyTightness * deltaTime;
float decay = 1.0 - _springVelocityDecay * deltaTime;
float decay = 1.0 - BODY_SPRING_DECAY * deltaTime;
if ( decay > 0.0 ) {
if (decay > 0.0) {
_bone[b].springyVelocity *= decay;
}
else {

View file

@ -9,36 +9,16 @@
#ifndef __interface__avatar__
#define __interface__avatar__
#include <AvatarData.h>
#include <Orientation.h>
#include "world.h"
#include "AvatarTouch.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp> //looks like we might not need this
#include <AvatarData.h>
#include <Orientation.h>
#include "world.h"
#include "AvatarTouch.h"
#include "InterfaceConfig.h"
#include "SerialInterface.h"
#include "Balls.h"
const bool AVATAR_GRAVITY = true;
const float DECAY = 0.1;
const float THRUST_MAG = 1200.0;
const float YAW_MAG = 500.0; //JJV - changed from 300.0;
const float TEST_YAW_DECAY = 5.0;
const float LIN_VEL_DECAY = 5.0;
const float COLLISION_FRICTION = 0.5;
const float COLLISION_RADIUS_SCALAR = 1.8;
const float COLLISION_BALL_FORCE = 0.1;
const float COLLISION_BODY_FORCE = 3.0;
const float MY_HAND_HOLDING_PULL = 0.2;
const float YOUR_HAND_HOLDING_PULL = 1.0;
enum eyeContactTargets {LEFT_EYE, RIGHT_EYE, MOUTH};
enum DriveKeys
@ -54,22 +34,6 @@ enum DriveKeys
MAX_DRIVE_KEYS
};
/*
#define FWD 0
#define BACK 1
#define LEFT 2
#define RIGHT 3
#define UP 4
#define DOWN 5
#define ROT_LEFT 6
#define ROT_RIGHT 7
#define MAX_DRIVE_KEYS 8
*/
//#define MAX_OTHER_AVATARS 10 // temporary - for testing purposes!
enum AvatarMode
{
AVATAR_MODE_STANDING = 0,
@ -108,62 +72,6 @@ enum AvatarBoneID
NUM_AVATAR_BONES
};
struct AvatarBone
{
AvatarBoneID parent; // which bone is this bone connected to?
glm::vec3 position; // the position at the "end" of the bone - in global space
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the "T-pose"
glm::vec3 springyPosition; // used for special effects (a 'flexible' variant of position)
glm::vec3 springyVelocity; // used for special effects ( the velocity of the springy position)
float springBodyTightness; // how tightly the springy position tries to stay on the position
glm::quat rotation; // this will eventually replace yaw, pitch and roll (and maybe orientation)
float yaw; // the yaw Euler angle of the bone rotation off the parent
float pitch; // the pitch Euler angle of the bone rotation off the parent
float roll; // the roll Euler angle of the bone rotation off the parent
Orientation orientation; // three orthogonal normals determined by yaw, pitch, roll
float length; // the length of the bone
float radius; // used for detecting collisions for certain physical effects
bool isCollidable; // when false, the bone position will not register a collision
};
struct AvatarHead
{
float pitchRate;
float yawRate;
float rollRate;
float noise;
float eyeballPitch[2];
float eyeballYaw [2];
float eyebrowPitch[2];
float eyebrowRoll [2];
float eyeballScaleX;
float eyeballScaleY;
float eyeballScaleZ;
float interPupilDistance;
float interBrowDistance;
float nominalPupilSize;
float pupilSize;
float mouthPitch;
float mouthYaw;
float mouthWidth;
float mouthHeight;
float leanForward;
float leanSideways;
float pitchTarget;
float yawTarget;
float noiseEnvelope;
float pupilConverge;
float scale;
int eyeContact;
float browAudioLift;
eyeContactTargets eyeContactTarget;
// Sound loudness information
float lastLoudness;
float averageLoudness;
float audioAttack;
};
class Avatar : public AvatarData {
public:
@ -240,6 +148,85 @@ public:
const bool getHeadReturnToCenter() const { return _returnHeadToCenter; };
private:
const bool AVATAR_GRAVITY = true;
const float DECAY = 0.1;
const float THRUST_MAG = 1200.0;
const float YAW_MAG = 500.0; //JJV - changed from 300.0;
const float TEST_YAW_DECAY = 5.0;
const float LIN_VEL_DECAY = 5.0;
const float MY_HAND_HOLDING_PULL = 0.2;
const float YOUR_HAND_HOLDING_PULL = 1.0;
const float BODY_SPRING_FORCE = 6.0f;
const float BODY_SPRING_DECAY = 16.0f;
//const float COLLISION_FRICTION = 0.5;
//const float COLLISION_RADIUS_SCALAR = 1.8;
//const float COLLISION_BALL_FORCE = 0.1;
//const float COLLISION_BODY_FORCE = 3.0;
const float COLLISION_RADIUS_SCALAR = 1.8;
const float COLLISION_BALL_FORCE = 0.6;
const float COLLISION_BODY_FORCE = 6.0;
const float COLLISION_BALL_FRICTION = 200.0;
const float COLLISION_BODY_FRICTION = 0.5;
struct AvatarBone
{
AvatarBoneID parent; // which bone is this bone connected to?
glm::vec3 position; // the position at the "end" of the bone - in global space
glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the "T-pose"
glm::vec3 springyPosition; // used for special effects (a 'flexible' variant of position)
glm::vec3 springyVelocity; // used for special effects ( the velocity of the springy position)
float springBodyTightness; // how tightly the springy position tries to stay on the position
glm::quat rotation; // this will eventually replace yaw, pitch and roll (and maybe orientation)
float yaw; // the yaw Euler angle of the bone rotation off the parent
float pitch; // the pitch Euler angle of the bone rotation off the parent
float roll; // the roll Euler angle of the bone rotation off the parent
Orientation orientation; // three orthogonal normals determined by yaw, pitch, roll
float length; // the length of the bone
float radius; // used for detecting collisions for certain physical effects
bool isCollidable; // when false, the bone position will not register a collision
};
struct AvatarHead
{
float pitchRate;
float yawRate;
float rollRate;
float noise;
float eyeballPitch[2];
float eyeballYaw [2];
float eyebrowPitch[2];
float eyebrowRoll [2];
float eyeballScaleX;
float eyeballScaleY;
float eyeballScaleZ;
float interPupilDistance;
float interBrowDistance;
float nominalPupilSize;
float pupilSize;
float mouthPitch;
float mouthYaw;
float mouthWidth;
float mouthHeight;
float leanForward;
float leanSideways;
float pitchTarget;
float yawTarget;
float noiseEnvelope;
float pupilConverge;
float scale;
int eyeContact;
float browAudioLift;
eyeContactTargets eyeContactTarget;
// Sound loudness information
float lastLoudness;
float averageLoudness;
float audioAttack;
};
AvatarHead _head;
bool _isMine;
glm::vec3 _TEST_bigSpherePosition;
@ -248,8 +235,6 @@ private:
float _bodyYawDelta;
bool _usingBodySprings;
glm::vec3 _movedHandOffset;
float _springVelocityDecay;
float _springForce;
glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion
AvatarBone _bone[ NUM_AVATAR_BONES ];
AvatarMode _mode;
@ -285,6 +270,7 @@ private:
void updateBodySprings( float deltaTime );
void calculateBoneLengths();
void readSensors();
void updateHead( float deltaTime );
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
void updateCollisionWithOtherAvatar( Avatar * other, float deltaTime );
void setHeadFromGyros(glm::vec3 * eulerAngles, glm::vec3 * angularVelocity, float deltaTime);

View file

@ -56,8 +56,8 @@ void AvatarTouch::simulate (float deltaTime) {
glm::vec3 v = _yourHandPosition - _myHandPosition;
for (int p=0; p<NUM_POINTS; p++) {
_point[p] = _myHandPosition + v * ( (float)p / (float)NUM_POINTS );
_point[p].x += randFloatInRange( -0.007, 0.007 );
_point[p].y += randFloatInRange( -0.007, 0.007 );
_point[p].z += randFloatInRange( -0.007, 0.007 );
_point[p].x += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
_point[p].y += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
_point[p].z += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
}
}

View file

@ -19,6 +19,8 @@ public:
void setYourHandPosition( glm::vec3 position );
void simulate(float deltaTime);
void render();
const float THREAD_RADIUS = 0.007;
private:

View file

@ -18,15 +18,10 @@
#include <OctalCode.h>
#include <pthread.h>
#include "Log.h"
#include "VoxelConstants.h"
#include "VoxelSystem.h"
const int MAX_VOXELS_PER_SYSTEM = 250000;
const int VERTICES_PER_VOXEL = 24;
const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
const int INDICES_PER_VOXEL = 3 * 12;
float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1,
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1,
0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 };
@ -60,10 +55,6 @@ VoxelSystem::~VoxelSystem() {
pthread_mutex_destroy(&bufferWriteLock);
}
void VoxelSystem::setViewerAvatar(Avatar *newViewerAvatar) {
viewerAvatar = newViewerAvatar;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: VoxelSystem::loadVoxelsFile()
// Description: Loads HiFidelity encoded Voxels from a binary file. The current file
@ -115,7 +106,6 @@ float VoxelSystem::getVoxelsBytesReadPerSecondAverage() {
return tree->voxelsBytesReadStats.getAverageSampleValuePerSecond();
}
int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
unsigned char command = *sourceBuffer;
@ -183,31 +173,21 @@ void VoxelSystem::copyWrittenDataToReadArrays() {
pthread_mutex_unlock(&bufferWriteLock);
}
int VoxelSystem::treeToArrays(VoxelNode *currentNode, const glm::vec3& nodePosition) {
int VoxelSystem::treeToArrays(VoxelNode* currentNode, const glm::vec3& nodePosition) {
int voxelsAdded = 0;
float halfUnitForVoxel = powf(0.5, *currentNode->octalCode) * (0.5 * TREE_SCALE);
glm::vec3 viewerPosition = viewerAvatar->getPosition();
glm::vec3 viewerPosition = _camera->getPosition(); //_viewerAvatar->getPosition();
// debug LOD code
glm::vec3 debugNodePosition;
copyFirstVertexForCode(currentNode->octalCode,(float*)&debugNodePosition);
//printf("-----------------\n");
//printf("halfUnitForVoxel=%f\n",halfUnitForVoxel);
//printf("viewer.x=%f y=%f z=%f \n", viewerPosition.x, viewerPosition.y, viewerPosition.z);
//printf("node.x=%f y=%f z=%f \n", nodePosition[0], nodePosition[1], nodePosition[2]);
//printf("debugNodePosition.x=%f y=%f z=%f \n", debugNodePosition[0], debugNodePosition[1], debugNodePosition[2]);
float distanceToVoxelCenter = sqrtf(powf(viewerPosition.x - nodePosition[0] - halfUnitForVoxel, 2) +
powf(viewerPosition.y - nodePosition[1] - halfUnitForVoxel, 2) +
powf(viewerPosition.z - nodePosition[2] - halfUnitForVoxel, 2));
int renderLevel = *currentNode->octalCode + 1;
int boundaryPosition = boundaryDistanceForRenderLevel(renderLevel);
//printLog("treeToArrays() renderLevel=%d distanceToVoxelCenter=%f boundaryPosition=%d\n",
// renderLevel,distanceToVoxelCenter,boundaryPosition);
bool alwaysDraw = false; // XXXBHG - temporary debug code. Flip this to true to disable LOD blurring
if (alwaysDraw || distanceToVoxelCenter < boundaryPosition) {
@ -218,22 +198,6 @@ int VoxelSystem::treeToArrays(VoxelNode *currentNode, const glm::vec3& nodePosi
glm::vec3 childNodePosition;
copyFirstVertexForCode(currentNode->children[i]->octalCode,(float*)&childNodePosition);
childNodePosition *= (float)TREE_SCALE; // scale it up
/**** disabled ************************************************************************************************
// Note: Stephen, I intentionally left this in so you would talk to me about it. Here's the deal, this code
// doesn't seem to work correctly. It returns X and Z flipped and the values are negative. Since we use the
// firstVertexForCode() function below to calculate the child vertex and that DOES work, I've decided to use
// that function to calculate our position for LOD handling.
//
// calculate the child's position based on the parent position
for (int j = 0; j < 3; j++) {
childNodePosition[j] = nodePosition[j];
if (oneAtBit(branchIndexWithDescendant(currentNode->octalCode,currentNode->children[i]->octalCode),(7 - j))) {
childNodePosition[j] -= (powf(0.5, *currentNode->children[i]->octalCode) * TREE_SCALE);
}
}
**** disabled ************************************************************************************************/
voxelsAdded += treeToArrays(currentNode->children[i], childNodePosition);
}
}
@ -399,13 +363,7 @@ bool VoxelSystem::randomColorOperation(VoxelNode* node, bool down, void* extraDa
newColor[0] = randomColorValue(150);
newColor[1] = randomColorValue(150);
newColor[1] = randomColorValue(150);
//printf("randomize color node %d was %x,%x,%x NOW %x,%x,%x\n",
// _nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2],
// newColor[0],newColor[1],newColor[2]);
node->setColor(newColor);
} else {
//printf("not randomizing color node of %d since it has no color\n",_nodeCount);
}
return true;
}
@ -413,7 +371,7 @@ bool VoxelSystem::randomColorOperation(VoxelNode* node, bool down, void* extraDa
void VoxelSystem::randomizeVoxelColors() {
_nodeCount = 0;
tree->recurseTreeWithOperation(randomColorOperation);
printf("setting randomized true color for %d nodes\n",_nodeCount);
printLog("setting randomized true color for %d nodes\n",_nodeCount);
setupNewVoxelsForDrawing();
}
@ -430,10 +388,6 @@ bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, bool down, void*
unsigned char newR = randomColorValue(150);
unsigned char newG = randomColorValue(150);
unsigned char newB = randomColorValue(150);
printf("randomize FALSE color node %d was %x,%x,%x NOW %x,%x,%x\n",
_nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2],
newR,newG,newB);
node->setFalseColor(newR,newG,newB);
return true; // keep going!
@ -442,7 +396,7 @@ bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, bool down, void*
void VoxelSystem::falseColorizeRandom() {
_nodeCount = 0;
tree->recurseTreeWithOperation(falseColorizeRandomOperation);
printf("setting randomized false color for %d nodes\n",_nodeCount);
printLog("setting randomized false color for %d nodes\n",_nodeCount);
setupNewVoxelsForDrawing();
}
@ -455,14 +409,13 @@ bool VoxelSystem::trueColorizeOperation(VoxelNode* node, bool down, void* extraD
_nodeCount++;
node->setFalseColored(false);
//printf("setting true color for node %d\n",_nodeCount);
return true;
}
void VoxelSystem::trueColorize() {
_nodeCount = 0;
tree->recurseTreeWithOperation(trueColorizeOperation);
printf("setting true color for %d nodes\n",_nodeCount);
printLog("setting true color for %d nodes\n",_nodeCount);
setupNewVoxelsForDrawing();
}
@ -474,37 +427,20 @@ bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, bool down, void*
return true;
}
ViewFrustum* viewFrustum = (ViewFrustum*) extraData;
const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData;
_nodeCount++;
// only do this for truely colored voxels...
if (node->isColored()) {
// first calculate the AAbox for the voxel
AABox voxelBox;
node->getAABox(voxelBox);
voxelBox.scale(TREE_SCALE);
printf("voxelBox corner=(%f,%f,%f) x=%f\n",
voxelBox.getCorner().x, voxelBox.getCorner().y, voxelBox.getCorner().z,
voxelBox.getSize().x);
// If the voxel is outside of the view frustum, then false color it red
if (ViewFrustum::OUTSIDE == viewFrustum->boxInFrustum(voxelBox)) {
if (!node->isInView(*viewFrustum)) {
// Out of view voxels are colored RED
unsigned char newR = 255;
unsigned char newG = 0;
unsigned char newB = 0;
//printf("voxel OUTSIDE view - FALSE colorizing node %d TRUE color is %x,%x,%x \n",
// _nodeCount,node->getTrueColor()[0],node->getTrueColor()[1],node->getTrueColor()[2]);
node->setFalseColor(newR,newG,newB);
} else {
printf("voxel NOT OUTSIDE view\n");
}
} else {
printf("voxel not colored, don't consider it\n");
}
return true; // keep going!
@ -513,15 +449,13 @@ bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, bool down, void*
void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) {
_nodeCount = 0;
tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum);
printf("setting in view false color for %d nodes\n",_nodeCount);
printLog("setting in view false color for %d nodes\n",_nodeCount);
setupNewVoxelsForDrawing();
}
// Will false colorize voxels based on distance from view
bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool down, void* extraData) {
//printf("falseColorizeDistanceFromViewOperation() down=%s\n",(down ? "TRUE" : "FALSE"));
// we do our operations on the way up!
if (down) {
return true;
@ -546,10 +480,6 @@ bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool d
float halfUnitForVoxel = powf(0.5, *node->octalCode) * (0.5 * TREE_SCALE);
glm::vec3 viewerPosition = viewFrustum->getPosition();
//printf("halfUnitForVoxel=%f\n",halfUnitForVoxel);
//printf("viewer.x=%f y=%f z=%f \n", viewerPosition.x, viewerPosition.y, viewerPosition.z);
//printf("node.x=%f y=%f z=%f \n", nodePosition.x, nodePosition.y, nodePosition.z);
float distance = sqrtf(powf(viewerPosition.x - nodePosition.x - halfUnitForVoxel, 2) +
powf(viewerPosition.y - nodePosition.y - halfUnitForVoxel, 2) +
powf(viewerPosition.z - nodePosition.z - halfUnitForVoxel, 2));
@ -567,12 +497,7 @@ bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, bool d
unsigned char newR = (colorBand*(gradientOver/colorBands))+(maxColor-gradientOver);
unsigned char newG = 0;
unsigned char newB = 0;
//printf("Setting color down=%s distance=%f min=%f max=%f distanceRatio=%f color=%d \n",
// (down ? "TRUE" : "FALSE"), distance, _minDistance, _maxDistance, distanceRatio, (int)newR);
node->setFalseColor(newR,newG,newB);
} else {
//printf("voxel not colored, don't consider it - down=%s\n",(down ? "TRUE" : "FALSE"));
}
return true; // keep going!
}
@ -590,8 +515,6 @@ bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, bool down,
return true;
}
//printf("getDistanceFromViewRangeOperation() down=%s\n",(down ? "TRUE" : "FALSE"));
ViewFrustum* viewFrustum = (ViewFrustum*) extraData;
// only do this for truly colored voxels...
@ -618,11 +541,9 @@ bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, bool down,
// on way down, calculate the range of distances
if (distance > _maxDistance) {
_maxDistance = distance;
//printf("new maxDistance=%f down=%s\n",_maxDistance, (down ? "TRUE" : "FALSE"));
}
if (distance < _minDistance) {
_minDistance = distance;
//printf("new minDistance=%f down=%s\n",_minDistance, (down ? "TRUE" : "FALSE"));
}
_nodeCount++;
@ -636,11 +557,11 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) {
_maxDistance = 0.0;
_minDistance = FLT_MAX;
tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum);
printf("determining distance range for %d nodes\n",_nodeCount);
printLog("determining distance range for %d nodes\n",_nodeCount);
_nodeCount = 0;
tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum);
printf("setting in distance false color for %d nodes\n",_nodeCount);
printLog("setting in distance false color for %d nodes\n",_nodeCount);
setupNewVoxelsForDrawing();
}

View file

@ -16,6 +16,7 @@
#include <VoxelTree.h>
#include <ViewFrustum.h>
#include "Avatar.h"
#include "Camera.h"
#include "Util.h"
#include "world.h"
@ -34,7 +35,8 @@ public:
void render();
void setVoxelsRendered(int v) {voxelsRendered = v;};
int getVoxelsRendered() {return voxelsRendered;};
void setViewerAvatar(Avatar *newViewerAvatar);
void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; };
void setCamera(Camera* newCamera) { _camera = newCamera; };
void loadVoxelsFile(const char* fileName,bool wantColorRandomizer);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer);
@ -67,7 +69,8 @@ private:
static float _minDistance;
int voxelsRendered;
Avatar *viewerAvatar;
Avatar* _viewerAvatar;
Camera* _camera;
VoxelTree *tree;
GLfloat *readVerticesArray;
GLubyte *readColorsArray;

View file

@ -299,6 +299,7 @@ void init(void)
{
voxels.init();
voxels.setViewerAvatar(&myAvatar);
voxels.setCamera(&myCamera);
handControl.setScreenDimensions(WIDTH, HEIGHT);
@ -1616,12 +1617,6 @@ int main(int argc, const char * argv[])
voxels_lib::printLog = & ::printLog;
avatars_lib::printLog = & ::printLog;
// Quick test of the Orientation class on startup!
if (cmdOptionExists(argc, argv, "--testOrientation")) {
testOrientationClass();
return EXIT_SUCCESS;
}
unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
const char* portStr = getCmdOption(argc, argv, "--listenPort");
if (portStr) {

View file

@ -7,9 +7,13 @@
//
#include <cmath>
#include <algorithm> // std:min
#include <cstring>
#include "SharedUtil.h"
#include "OctalCode.h"
#include "shared_Log.h"
using shared_lib::printLog;
int numberOfThreeBitSectionsInCode(unsigned char * octalCode) {
if (*octalCode == 255) {
@ -20,8 +24,13 @@ int numberOfThreeBitSectionsInCode(unsigned char * octalCode) {
}
void printOctalCode(unsigned char * octalCode) {
for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) {
outputBits(octalCode[i]);
if (!octalCode) {
printLog("NULL\n");
} else {
for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) {
outputBits(octalCode[i],false);
}
printLog("\n");
}
}
@ -126,3 +135,38 @@ float * firstVertexForCode(unsigned char * octalCode) {
return firstVertex;
}
OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB) {
if (!codeA || !codeB) {
return ILLEGAL_CODE;
}
OctalCodeComparison result = LESS_THAN; // assume it's shallower
int numberOfBytes = std::min(bytesRequiredForCodeLength(*codeA), bytesRequiredForCodeLength(*codeB));
int compare = memcmp(codeA, codeB, numberOfBytes);
if (compare < 0) {
result = LESS_THAN;
} else if (compare > 0) {
result = GREATER_THAN;
} else {
int codeLengthA = numberOfThreeBitSectionsInCode(codeA);
int codeLengthB = numberOfThreeBitSectionsInCode(codeB);
if (codeLengthA == codeLengthB) {
// if the memcmp matched exactly, and they were the same length,
// then these must be the same code!
result = EXACT_MATCH;
} else {
// if the memcmp matched exactly, but they aren't the same length,
// then they have a matching common parent, but they aren't the same
if (codeLengthA < codeLengthB) {
result = LESS_THAN;
} else {
result = GREATER_THAN;
}
}
}
return result;
}

View file

@ -23,4 +23,12 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber
float * firstVertexForCode(unsigned char * octalCode);
void copyFirstVertexForCode(unsigned char * octalCode, float* output);
typedef enum {
ILLEGAL_CODE = -2,
LESS_THAN = -1,
EXACT_MATCH = 0,
GREATER_THAN = 1
} OctalCodeComparison;
OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2);
#endif /* defined(__hifi__OctalCode__) */

View file

@ -9,6 +9,7 @@
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cctype>
#ifdef _WIN32
#include "Syssocket.h"
#endif
@ -52,14 +53,30 @@ bool randomBoolean() {
return rand() % 2;
}
void outputBits(unsigned char byte) {
printLog("%d: ", byte);
void outputBufferBits(unsigned char* buffer, int length, bool withNewLine) {
for (int i = 0; i < length; i++) {
outputBits(buffer[i], false);
}
if (withNewLine) {
printLog("\n");
}
}
void outputBits(unsigned char byte, bool withNewLine) {
if (isalnum(byte)) {
printLog("[ %d (%c): ", byte, byte);
} else {
printLog("[ %d (0x%x): ", byte, byte);
}
for (int i = 0; i < 8; i++) {
printLog("%d", byte >> (7 - i) & 1);
}
printLog(" ] ");
printLog("\n");
if (withNewLine) {
printLog("\n");
}
}
int numberOfOnes(unsigned char byte) {
@ -356,3 +373,35 @@ void printVoxelCode(unsigned char* voxelCode) {
}
#endif
// Inserts the value and key into three arrays sorted by the key array, the first array is the value,
// the second array is a sorted key for the value, the third array is the index for the value in it original
// non-sorted array
// returns -1 if size exceeded
int insertIntoSortedArrays(void* value, float key, int originalIndex,
void** valueArray, float* keyArray, int* originalIndexArray,
int currentCount, int maxCount) {
if (currentCount < maxCount) {
int i = 0;
if (currentCount > 0) {
while (i < currentCount && key > keyArray[i]) {
i++;
}
// i is our desired location
// shift array elements to the right
if (i < currentCount && i+1 < maxCount) {
memcpy(&valueArray[i + 1], &valueArray[i], sizeof(void*) * (currentCount - i));
memcpy(&keyArray[i + 1], &keyArray[i], sizeof(float) * (currentCount - i));
memcpy(&originalIndexArray[i + 1], &originalIndexArray[i], sizeof(int) * (currentCount - i));
}
}
// place new element at i
valueArray[i] = value;
keyArray[i] = key;
originalIndexArray[i] = originalIndex;
return currentCount + 1;
}
return -1; // error case
}

View file

@ -42,7 +42,8 @@ float randFloatInRange (float min,float max);
unsigned char randomColorValue(int minimum);
bool randomBoolean();
void outputBits(unsigned char byte);
void outputBufferBits(unsigned char* buffer, int length, bool withNewLine = true);
void outputBits(unsigned char byte, bool withNewLine = true);
void printVoxelCode(unsigned char* voxelCode);
int numberOfOnes(unsigned char byte);
bool oneAtBit(unsigned char byte, int bitIndex);
@ -70,5 +71,8 @@ bool createVoxelEditMessage(unsigned char command, short int sequence,
void usleep(int waitTime);
#endif
int insertIntoSortedArrays(void* value, float key, int originalIndex,
void** valueArray, float* keyArray, int* originalIndexArray,
int currentCount, int maxCount);
#endif /* defined(__hifi__SharedUtil__) */

View file

@ -1,35 +0,0 @@
//
// MarkerNode.cpp
// hifi
//
// Created by Stephen Birarda on 3/26/13.
//
//
#include "MarkerNode.h"
#include <stddef.h>
MarkerNode::MarkerNode() {
for (int i = 0; i < 8; i++) {
children[i] = NULL;
}
childrenVisitedMask = 0;
}
MarkerNode::~MarkerNode() {
for (int i = 0; i < 8; i++) {
delete children[i];
}
}
MarkerNode::MarkerNode(const MarkerNode &otherMarkerNode) {
childrenVisitedMask = otherMarkerNode.childrenVisitedMask;
// recursively copy the children marker nodes
for (int i = 0; i < 8; i++) {
if (children[i] != NULL) {
children[i] = new MarkerNode(*otherMarkerNode.children[i]);
}
}
}

View file

@ -1,22 +0,0 @@
//
// MarkerNode.h
// hifi
//
// Created by Stephen Birarda on 3/26/13.
//
//
#ifndef __hifi__MarkerNode__
#define __hifi__MarkerNode__
class MarkerNode {
public:
MarkerNode();
~MarkerNode();
MarkerNode(const MarkerNode &otherMarkerNode);
unsigned char childrenVisitedMask;
MarkerNode *children[8];
};
#endif /* defined(__hifi__MarkerNode__) */

View file

@ -92,7 +92,7 @@ public:
void dump() const;
enum {OUTSIDE, INTERSECT, INSIDE};
typedef enum {OUTSIDE, INTERSECT, INSIDE} location;
int pointInFrustum(const glm::vec3& point) const;
int sphereInFrustum(const glm::vec3& center, float radius) const;

View file

@ -0,0 +1,24 @@
//
// VoxelConstants.h
// hifi
//
// Created by Brad Hefta-Gaub on 4/29/13.
//
//
// Various important constants used throughout the system related to voxels
//
//
#ifndef __hifi_VoxelConstants_h__
#define __hifi_VoxelConstants_h__
const int MAX_VOXEL_PACKET_SIZE = 1492;
const int MAX_TREE_SLICE_BYTES = 26;
const int TREE_SCALE = 10;
const int MAX_VOXELS_PER_SYSTEM = 250000;
const int VERTICES_PER_VOXEL = 24;
const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
const int INDICES_PER_VOXEL = 3 * 12;
const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
#endif

View file

@ -7,12 +7,15 @@
//
#include <stdio.h>
#include <cmath>
#include <cstring>
#include "SharedUtil.h"
//#include "voxels_Log.h"
#include "voxels_Log.h"
#include "VoxelNode.h"
#include "VoxelConstants.h"
#include "OctalCode.h"
#include "AABox.h"
using voxels_lib::printLog;
// using voxels_lib::printLog;
@ -118,7 +121,6 @@ void VoxelNode::setFalseColored(bool isFalseColored) {
void VoxelNode::setColor(const nodeColor& color) {
//printf("VoxelNode::setColor() isFalseColored=%s\n",_falseColored ? "Yes" : "No");
memcpy(&_trueColor,&color,sizeof(nodeColor));
if (!_falseColored) {
memcpy(&_currentColor,&color,sizeof(nodeColor));
@ -179,3 +181,38 @@ void VoxelNode::setRandomColor(int minimumBrightness) {
newColor[3] = 1;
setColor(newColor);
}
bool VoxelNode::isLeaf() const {
for (int i = 0; i < 8; i++) {
if (children[i]) {
return false;
}
}
return true;
}
void VoxelNode::printDebugDetails(const char* label) const {
AABox box;
getAABox(box);
printLog("%s - Voxel at corner=(%f,%f,%f) size=%f octcode=", label,
box.getCorner().x, box.getCorner().y, box.getCorner().z, box.getSize().x);
printOctalCode(octalCode);
}
bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const {
AABox box;
getAABox(box);
box.scale(TREE_SCALE);
bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box));
return inView;
}
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box;
getAABox(box);
float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - (box.getCorner().x + box.getSize().x), 2) +
powf(viewFrustum.getPosition().y - (box.getCorner().y + box.getSize().y), 2) +
powf(viewFrustum.getPosition().z - (box.getCorner().z + box.getSize().z), 2));
return distanceToVoxelCenter;
}

View file

@ -10,6 +10,7 @@
#define __hifi__VoxelNode__
#include "AABox.h"
#include "ViewFrustum.h"
typedef unsigned char colorPart;
typedef unsigned char nodeColor[4];
@ -34,6 +35,11 @@ public:
VoxelNode *children[8];
bool isColored() const { return (_trueColor[3]==1); };
bool isInView(const ViewFrustum& viewFrustum) const;
float distanceToCamera(const ViewFrustum& viewFrustum) const;
bool isLeaf() const;
void getAABox(AABox& box) const;
void printDebugDetails(const char* label) const;
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
void setFalseColor(colorPart red, colorPart green, colorPart blue);
@ -50,8 +56,6 @@ public:
const nodeColor& getTrueColor() const { return _trueColor; };
const nodeColor& getColor() const { return _trueColor; };
#endif
void getAABox(AABox& box) const;
};
#endif /* defined(__hifi__VoxelNode__) */

View file

@ -0,0 +1,90 @@
//
// VoxelNodeBag.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 4/25/2013
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "VoxelNodeBag.h"
#include <OctalCode.h>
VoxelNodeBag::~VoxelNodeBag() {
deleteAll();
}
void VoxelNodeBag::deleteAll() {
if (_bagElements) {
delete[] _bagElements;
}
_bagElements = NULL;
_elementsInUse = 0;
_sizeOfElementsArray = 0;
}
const int GROW_BAG_BY = 100;
// put a node into the bag
void VoxelNodeBag::insert(VoxelNode* node) {
// Search for where we should live in the bag (sorted)
// Note: change this to binary search... instead of linear!
int insertAt = _elementsInUse;
for (int i = 0; i < _elementsInUse; i++) {
// compare the newNode to the elements already in the bag
OctalCodeComparison comparison = compareOctalCodes(_bagElements[i]->octalCode, node->octalCode);
// If we found a code in the bag that matches, then just return, since the element is already in the bag.
if (comparison == EXACT_MATCH) {
return; // exit early!!
}
// if we found a node "greater than" the inserted node, then
// we want to insert our node here.
if (comparison == GREATER_THAN) {
insertAt = i;
break;
}
}
// at this point, inserAt will be the location we want to insert at.
// If we don't have room in our bag, then grow the bag
if (_sizeOfElementsArray < _elementsInUse + 1) {
VoxelNode** oldBag = _bagElements;
_bagElements = new VoxelNode * [_sizeOfElementsArray + GROW_BAG_BY];
_sizeOfElementsArray += GROW_BAG_BY;
// If we had an old bag...
if (oldBag) {
// copy old elements into the new bag, but leave a space where we need to
// insert the new node
memcpy(_bagElements, oldBag, insertAt * sizeof(VoxelNode*));
memcpy(&_bagElements[insertAt + 1], &oldBag[insertAt], (_elementsInUse - insertAt) * sizeof(VoxelNode*));
delete[] oldBag;
}
} else {
// move existing elements further back in the bag array, leave a space where we need to
// insert the new node
memmove(&_bagElements[insertAt + 1], &_bagElements[insertAt], (_elementsInUse - insertAt) * sizeof(VoxelNode*));
}
_bagElements[insertAt] = node;
_elementsInUse++;
}
// pull a node out of the bag (could come in any order)
VoxelNode* VoxelNodeBag::extract() {
// pull the last node out, and shrink our list...
if (_elementsInUse) {
// get the last element
VoxelNode* node = _bagElements[_elementsInUse - 1];
// reduce the count
_elementsInUse--;
return node;
}
return NULL;
}

View file

@ -0,0 +1,44 @@
//
// VoxelNodeBag.h
// hifi
//
// Created by Brad Hefta-Gaub on 4/25/2013
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// This class is used by the VoxelTree:encodeTreeBitstream() functions to store extra nodes that need to be sent
// it's a generic bag style storage mechanism. But It has the property that you can't put the same node into the bag
// more than once (in other words, it de-dupes automatically), also, it supports collapsing it's several peer nodes
// into a parent node in cases where you add enough peers that it makes more sense to just add the parent.
//
#ifndef __hifi__VoxelNodeBag__
#define __hifi__VoxelNodeBag__
#include "VoxelNode.h"
class VoxelNodeBag {
public:
VoxelNodeBag() :
_bagElements(NULL),
_elementsInUse(0),
_sizeOfElementsArray(0) {};
~VoxelNodeBag();
void insert(VoxelNode* node); // put a node into the bag
VoxelNode* extract(); // pull a node out of the bag (could come in any order)
bool isEmpty() const { return (_elementsInUse == 0); };
int count() const { return _elementsInUse; };
void deleteAll();
private:
VoxelNode** _bagElements;
int _elementsInUse;
int _sizeOfElementsArray;
};
#endif /* defined(__hifi__VoxelNodeBag__) */

View file

@ -17,33 +17,16 @@
#include "PacketHeaders.h"
#include "OctalCode.h"
#include "VoxelTree.h"
#include "VoxelNodeBag.h"
#include "ViewFrustum.h"
#include <fstream> // to load voxels from file
#include "VoxelConstants.h"
using voxels_lib::printLog;
int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
switch (renderLevel) {
case 1:
case 2:
case 3:
return 100;
case 4:
return 75;
break;
case 5:
return 50;
break;
case 6:
return 25;
break;
case 7:
return 12;
break;
default:
return 6;
break;
}
float voxelSizeScale = 5000.0;
return voxelSizeScale / powf(2, renderLevel);
}
VoxelTree::VoxelTree() :
@ -117,12 +100,19 @@ VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char *
return ancestorNode;
}
VoxelNode * VoxelTree::createMissingNode(VoxelNode *lastParentNode, unsigned char *codeToReach) {
// returns the node created!
VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char* codeToReach) {
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->octalCode, codeToReach);
lastParentNode->addChildAtIndex(indexOfNewChild);
// we could be coming down a branch that was already created, so don't stomp on it.
if (lastParentNode->children[indexOfNewChild] == NULL) {
lastParentNode->addChildAtIndex(indexOfNewChild);
}
// This works because we know we traversed down the same tree so if the length is the same, then the whole code is the same
if (*lastParentNode->children[indexOfNewChild]->octalCode == *codeToReach) {
return lastParentNode;
return lastParentNode->children[indexOfNewChild];
} else {
return createMissingNode(lastParentNode->children[indexOfNewChild], codeToReach);
}
@ -131,23 +121,21 @@ VoxelNode * VoxelTree::createMissingNode(VoxelNode *lastParentNode, unsigned cha
// BHG Notes: We appear to call this function for every Voxel Node getting created.
// This is recursive in nature. So, for example, if we are given an octal code for
// a 1/256th size voxel, we appear to call this function 8 times. Maybe??
int VoxelTree::readNodeData(VoxelNode *destinationNode,
unsigned char * nodeData,
int VoxelTree::readNodeData(VoxelNode* destinationNode,
unsigned char* nodeData,
int bytesLeftToRead) {
// instantiate variable for bytes already read
int bytesRead = 1;
for (int i = 0; i < 8; i++) {
// check the colors mask to see if we have a child to color in
if (oneAtBit(*nodeData, i)) {
// create the child if it doesn't exist
if (destinationNode->children[i] == NULL) {
destinationNode->addChildAtIndex(i);
this->voxelsCreated++;
this->voxelsCreatedStats.updateAverage(1);
}
// pull the color for this child
nodeColor newColor;
memcpy(newColor, nodeData + bytesRead, 3);
@ -159,10 +147,9 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode,
bytesRead += 3;
}
}
// average node's color based on color of children
destinationNode->setColorFromAverageOfChildren();
// give this destination node the child mask from the packet
unsigned char childMask = *(nodeData + bytesRead);
@ -193,68 +180,83 @@ int VoxelTree::readNodeData(VoxelNode *destinationNode,
}
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes) {
VoxelNode *bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstream, NULL);
if (*bitstream != *bitstreamRootNode->octalCode) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
bitstreamRootNode = createMissingNode(bitstreamRootNode, (unsigned char *)bitstream);
int bytesRead = 0;
unsigned char* bitstreamAt = bitstream;
// Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes
// into a single network packet. readNodeData() basically goes down a tree from the root, and fills things in from there
// if there are more bytes after that, it's assumed to be another root relative tree
while (bitstreamAt < bitstream + bufferSizeBytes) {
VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL);
if (*bitstreamAt != *bitstreamRootNode->octalCode) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
// Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial
// octal code is always relative to root!
bitstreamRootNode = createMissingNode(rootNode, (unsigned char*) bitstreamAt);
}
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
int theseBytesRead = 0;
theseBytesRead += octalCodeBytes;
theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes,
bufferSizeBytes - (bytesRead + octalCodeBytes));
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
bytesRead += theseBytesRead;
}
int octalCodeBytes = bytesRequiredForCodeLength(*bitstream);
readNodeData(bitstreamRootNode, bitstream + octalCodeBytes, bufferSizeBytes - octalCodeBytes);
this->voxelsBytesRead += bufferSizeBytes;
this->voxelsBytesReadStats.updateAverage(bufferSizeBytes);
this->voxelsBytesReadStats.updateAverage(bufferSizeBytes);
}
// 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) {
VoxelNode* parentNode = NULL;
VoxelNode* parentNode = NULL;
VoxelNode* nodeToDelete = nodeForOctalCode(rootNode, codeBuffer, &parentNode);
// If the node exists...
int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color!
int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color!
if (0 == memcmp(nodeToDelete->octalCode,codeBuffer,lengthInBytes)) {
float* vertices = firstVertexForCode(nodeToDelete->octalCode);
delete []vertices;
float* vertices = firstVertexForCode(nodeToDelete->octalCode);
delete[] vertices;
if (parentNode) {
float* vertices = firstVertexForCode(parentNode->octalCode);
delete []vertices;
int childNDX = branchIndexWithDescendant(parentNode->octalCode, codeBuffer);
if (parentNode) {
float* vertices = firstVertexForCode(parentNode->octalCode);
delete[] vertices;
int childIndex = branchIndexWithDescendant(parentNode->octalCode, codeBuffer);
delete parentNode->children[childNDX]; // delete the child nodes
parentNode->children[childNDX]=NULL; // set it to NULL
delete parentNode->children[childIndex]; // delete the child nodes
parentNode->children[childIndex] = NULL; // set it to NULL
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
}
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
}
}
}
void VoxelTree::eraseAllVoxels() {
// XXXBHG Hack attack - is there a better way to erase the voxel tree?
delete rootNode; // this will recurse and delete all children
rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1];
*rootNode->octalCode = 0;
// XXXBHG Hack attack - is there a better way to erase the voxel tree?
delete rootNode; // this will recurse and delete all children
rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1];
*rootNode->octalCode = 0;
}
void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) {
VoxelNode *lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL);
VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL);
// create the node if it does not exist
if (*lastCreatedNode->octalCode != *codeColorBuffer) {
VoxelNode *parentNode = createMissingNode(lastCreatedNode, codeColorBuffer);
lastCreatedNode = parentNode->children[branchIndexWithDescendant(parentNode->octalCode, codeColorBuffer)];
lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer);
}
// give this node its color
int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer);
@ -264,273 +266,6 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) {
lastCreatedNode->setColor(newColor);
}
unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer,
VoxelNode *currentVoxelNode,
MarkerNode *currentMarkerNode,
const glm::vec3& agentPosition,
float thisNodePosition[3],
const ViewFrustum& viewFrustum,
bool viewFrustumCulling,
unsigned char * stopOctalCode)
{
static unsigned char *initialBitstreamPos = bitstreamBuffer;
unsigned char * childStopOctalCode = NULL;
if (stopOctalCode == NULL) {
stopOctalCode = rootNode->octalCode;
}
// check if we have any children
bool hasAtLeastOneChild;
for (int i = 0; i < 8; i++) {
if (currentVoxelNode->children[i] != NULL) {
hasAtLeastOneChild = true;
}
}
// if we have at least one child, check if it will be worth recursing into our children
if (hasAtLeastOneChild) {
int firstIndexToCheck = 0;
unsigned char * childMaskPointer = NULL;
float halfUnitForVoxel = powf(0.5, *currentVoxelNode->octalCode) * (0.5 * TREE_SCALE);
float distanceToVoxelCenter = sqrtf(powf(agentPosition[0] - thisNodePosition[0] - halfUnitForVoxel, 2) +
powf(agentPosition[1] - thisNodePosition[1] - halfUnitForVoxel, 2) +
powf(agentPosition[2] - thisNodePosition[2] - halfUnitForVoxel, 2));
// If the voxel is outside of the view frustum, then don't bother sending or recursing
bool voxelInView = true;
/**** not yet working properly at this level! **************************************************************************
if (viewFrustumCulling) {
float fullUnitForVoxel = halfUnitForVoxel * 2.0f;
AABox voxelBox;
voxelBox.setBox(glm::vec3(thisNodePosition[0],thisNodePosition[1],thisNodePosition[2]),
fullUnitForVoxel,fullUnitForVoxel,fullUnitForVoxel);
//printf("VoxelTree::loadBitstreamBuffer() voxelBox.corner=(%f,%f,%f) x=%f \n",
// voxelBox.getCorner().x,voxelBox.getCorner().y,voxelBox.getCorner().z, voxelBox.getSize().x);
voxelInView = (ViewFrustum::OUTSIDE != viewFrustum.pointInFrustum(voxelBox.getCorner()));
} else {
voxelInView = true;
}
**********************************************************************************************************************/
// if the distance to this voxel's center is less than the threshold
// distance for its children, we should send the children
bool voxelIsClose = (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentVoxelNode->octalCode + 1));
bool sendVoxel = voxelIsClose && voxelInView;
//printf("VoxelTree::loadBitstreamBuffer() sendVoxel=%d, voxelIsClose=%d, voxelInView=%d, viewFrustumCulling=%d\n",
// sendVoxel, voxelIsClose, voxelInView, viewFrustumCulling);
if (sendVoxel) {
// write this voxel's data if we're below or at
// or at the same level as the stopOctalCode
if (*currentVoxelNode->octalCode >= *stopOctalCode) {
if ((bitstreamBuffer - initialBitstreamPos) + MAX_TREE_SLICE_BYTES > MAX_VOXEL_PACKET_SIZE) {
// we can't send this packet, not enough room
// return our octal code as the stop
return currentVoxelNode->octalCode;
}
if (strcmp((char *)stopOctalCode, (char *)currentVoxelNode->octalCode) == 0) {
// this is is the root node for this packet
// add the leading V
*(bitstreamBuffer++) = PACKET_HEADER_VOXEL_DATA;
// add its octal code to the packet
int octalCodeBytes = bytesRequiredForCodeLength(*currentVoxelNode->octalCode);
memcpy(bitstreamBuffer, currentVoxelNode->octalCode, octalCodeBytes);
bitstreamBuffer += octalCodeBytes;
}
// default color mask is 0, increment pointer for colors
*bitstreamBuffer = 0;
// keep a colorPointer so we can check how many colors were added
unsigned char *colorPointer = bitstreamBuffer + 1;
for (int i = 0; i < 8; i++) {
// Rules for including a child:
// 1) child must exists
if ((currentVoxelNode->children[i] != NULL)) {
// 2) child must have a color...
if (currentVoxelNode->children[i]->isColored()) {
unsigned char* childOctalCode = currentVoxelNode->children[i]->octalCode;
float childPosition[3];
copyFirstVertexForCode(childOctalCode,(float*)&childPosition);
childPosition[0] *= TREE_SCALE; // scale it up
childPosition[1] *= TREE_SCALE; // scale it up
childPosition[2] *= TREE_SCALE; // scale it up
float halfChildVoxel = powf(0.5, *childOctalCode) * (0.5 * TREE_SCALE);
float distanceToChildCenter = sqrtf(powf(agentPosition[0] - childPosition[0] - halfChildVoxel, 2) +
powf(agentPosition[1] - childPosition[1] - halfChildVoxel, 2) +
powf(agentPosition[2] - childPosition[2] - halfChildVoxel, 2));
float fullChildVoxel = halfChildVoxel * 2.0f;
AABox childBox;
childBox.setBox(glm::vec3(childPosition[0], childPosition[1], childPosition[2]),
fullChildVoxel, fullChildVoxel, fullChildVoxel);
//printf("VoxelTree::loadBitstreamBuffer() childBox.corner=(%f,%f,%f) x=%f \n",
// childBox.getCorner().x,childBox.getCorner().y,childBox.getCorner().z, childBox.getSize().x);
// XXXBHG - not sure we want to do this "distance/LOD culling" at this level.
//bool childIsClose = (distanceToChildCenter < boundaryDistanceForRenderLevel(*childOctalCode + 1));
bool childIsClose = true; // for now, assume we're close enough
bool childInView = !viewFrustumCulling ||
(ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(childBox));
/// XXXBHG - debug code, switch this to true, and we'll send everything but include false coloring
// on voxels based on whether or not they match these rules.
bool falseColorInsteadOfCulling = false;
// removed childIsClose - until we determine if we want to include that
bool sendChild = (childInView) || falseColorInsteadOfCulling;
//printf("VoxelTree::loadBitstreamBuffer() childIsClose=%d, childInView=%d\n",
// childIsClose, childInView);
// if we sendAnyway, we'll do false coloring of the voxels based on childIsClose && childInView
if (sendChild) {
// copy in the childs color to bitstreamBuffer
if (childIsClose && childInView) {
// true color
memcpy(colorPointer, currentVoxelNode->children[i]->getTrueColor(), 3);
} else {
unsigned char red[3] = {255,0,0};
unsigned char green[3] = {0,255,0};
unsigned char blue[3] = {0,0,255};
if (!childIsClose && !childInView) {
// If both too far, and not in view, color them red
memcpy(colorPointer, red, 3);
} else if (!childIsClose) {
// If too far, but in view, color them blue
memcpy(colorPointer, blue, 3);
} else {
// If close, but out of view, color them green
memcpy(colorPointer, green, 3);
}
}
colorPointer += 3;
// set the colorMask by bitshifting the value of childExists
*bitstreamBuffer += (1 << (7 - i));
}
}
}
}
// push the bitstreamBuffer forwards for the number of added colors
bitstreamBuffer += (colorPointer - bitstreamBuffer);
// maintain a pointer to this spot in the buffer so we can set our child mask
// depending on the results of the recursion below
childMaskPointer = bitstreamBuffer++;
// reset the childMaskPointer for this node to 0
*childMaskPointer = 0;
} else {
firstIndexToCheck = *stopOctalCode > 0
? branchIndexWithDescendant(currentVoxelNode->octalCode, stopOctalCode)
: 0;
}
unsigned char * arrBufferBeforeChild = bitstreamBuffer;
for (int i = firstIndexToCheck; i < 8; i ++) {
// ask the child to load this bitstream buffer
// if they or their descendants fill the MTU we will receive the childStopOctalCode back
if (currentVoxelNode->children[i] != NULL) {
if (!oneAtBit(currentMarkerNode->childrenVisitedMask, i)) {
// create the marker node for this child if it does not yet exist
if (currentMarkerNode->children[i] == NULL) {
currentMarkerNode->children[i] = new MarkerNode();
}
float childNodePosition[3];
copyFirstVertexForCode(currentVoxelNode->children[i]->octalCode,(float*)&childNodePosition);
childNodePosition[0] *= TREE_SCALE; // scale it up
childNodePosition[1] *= TREE_SCALE; // scale it up
childNodePosition[2] *= TREE_SCALE; // scale it up
/**** disabled *****************************************************************************************
// Note: Stephen, I intentionally left this in so you would talk to me about it. Here's the deal, this
// code doesn't seem to work correctly. It returns X and Z flipped and the values are negative. Since
// we use the firstVertexForCode() function in VoxelSystem to calculate the child vertex and that DOES
// work, I've decided to use that function to calculate our position for LOD handling.
//
// calculate the child's position based on the parent position
for (int j = 0; j < 3; j++) {
childNodePosition[j] = thisNodePosition[j];
if (oneAtBit(branchIndexWithDescendant(currentVoxelNode->octalCode,
currentVoxelNode->children[i]->octalCode),
(7 - j))) {
childNodePosition[j] -= (powf(0.5, *currentVoxelNode->children[i]->octalCode) * TREE_SCALE);
}
}
**** disabled *****************************************************************************************/
// ask the child to load the bitstream buffer with their data
childStopOctalCode = loadBitstreamBuffer(bitstreamBuffer,
currentVoxelNode->children[i],
currentMarkerNode->children[i],
agentPosition,
childNodePosition,
viewFrustum,
viewFrustumCulling,
stopOctalCode);
if (bitstreamBuffer - arrBufferBeforeChild > 0) {
// this child added data to the packet - add it to our child mask
if (childMaskPointer != NULL) {
*childMaskPointer += (1 << (7 - i));
}
arrBufferBeforeChild = bitstreamBuffer;
}
}
}
if (childStopOctalCode != NULL) {
break;
} else {
// this child node has been covered
// add the appropriate bit to the childrenVisitedMask for the current marker node
currentMarkerNode->childrenVisitedMask += 1 << (7 - i);
// if we are above the stopOctal and we got a NULL code
// we cannot go to the next child
// so break and return the NULL stop code
if (*currentVoxelNode->octalCode < *stopOctalCode) {
break;
}
}
}
}
}
return childStopOctalCode;
}
void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) {
// XXXBHG: validate buffer is at least 4 bytes long? other guards??
unsigned short int itemNumber = (*((unsigned short int*)&bitstream[1]));
@ -554,35 +289,39 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe
void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
int colorMask = 0;
// create the color mask
for (int i = 0; i < 8; i++) {
if (startNode->children[i] != NULL && startNode->children[i]->isColored()) {
colorMask += (1 << (7 - i));
}
}
printLog("color mask: ");
outputBits(colorMask);
// output the colors we have
for (int j = 0; j < 8; j++) {
if (startNode->children[j] != NULL && startNode->children[j]->isColored()) {
printLog("color %d : ",j);
for (int c = 0; c < 3; c++) {
outputBits(startNode->children[j]->getTrueColor()[c]);
outputBits(startNode->children[j]->getTrueColor()[c],false);
}
startNode->children[j]->printDebugDetails("");
}
}
unsigned char childMask = 0;
for (int k = 0; k < 8; k++) {
if (startNode->children[k] != NULL) {
childMask += (1 << (7 - k));
}
}
printLog("child mask: ");
outputBits(childMask);
if (childMask > 0) {
// ask children to recursively output their trees
// if they aren't a leaf
@ -596,28 +335,23 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) {
bool hasChildren = false;
for (int i = 0; i < 8; i++) {
if (startNode->children[i] != NULL) {
reaverageVoxelColors(startNode->children[i]);
hasChildren = true;
}
}
if (hasChildren) {
bool childrenCollapsed = startNode->collapseIdenticalLeaves();
if (!childrenCollapsed) {
startNode->setColorFromAverageOfChildren();
}
}
bool childrenCollapsed = startNode->collapseIdenticalLeaves();
if (!childrenCollapsed) {
startNode->setColorFromAverageOfChildren();
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: VoxelTree::loadVoxelsFile()
// Description: Loads HiFidelity encoded Voxels from a binary file. The current file
// format is a stream of single voxels with color data.
// Complaints: Brad :)
void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
int vCount = 0;
@ -672,11 +406,6 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: VoxelTree::createSphere()
// Description: Creates a sphere of voxels in the local system at a given location/radius
// To Do: Move this function someplace better?
// Complaints: Brad :)
void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) {
// About the color of the sphere... we're going to make this sphere be a gradient
// between two RGB colors. We will do the gradient along the phi spectrum
@ -748,11 +477,321 @@ void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool
unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue);
this->readCodeColorBufferToTree(voxelData);
//printLog("voxel data for x:%f y:%f z:%f s:%f\n",x,y,z,s);
//printVoxelCode(voxelData);
delete voxelData;
}
}
}
this->reaverageVoxelColors(this->rootNode);
}
int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) {
// call the recursive version, this will add all found colored node roots to the bag
int currentSearchLevel = 0;
int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, viewFrustum, bag);
return levelReached;
}
int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) {
// Keep track of how deep we've searched.
currentSearchLevel++;
// If we've reached our max Search Level, then stop searching.
if (currentSearchLevel >= maxSearchLevel) {
return currentSearchLevel;
}
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
if (!node->isInView(viewFrustum)) {
return currentSearchLevel;
}
// Ok, this is a little tricky, each child may have been deeper than the others, so we need to track
// how deep each child went. And we actually return the maximum of each child. We use these variables below
// when we recurse the children.
int thisLevel = currentSearchLevel;
int maxChildLevel = thisLevel;
const int MAX_CHILDREN = 8;
VoxelNode* inViewChildren[MAX_CHILDREN];
float distancesToChildren[MAX_CHILDREN];
int positionOfChildren[MAX_CHILDREN];
int inViewCount = 0;
int inViewNotLeafCount = 0;
int inViewWithColorCount = 0;
// for each child node, check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int i = 0; i < MAX_CHILDREN; i++) {
VoxelNode* childNode = node->children[i];
bool childExists = (childNode != NULL);
bool childIsColored = (childExists && childNode->isColored());
bool childIsInView = (childExists && childNode->isInView(viewFrustum));
bool childIsLeaf = (childExists && childNode->isLeaf());
if (childIsInView) {
// track children in view as existing and not a leaf
if (!childIsLeaf) {
inViewNotLeafCount++;
}
// track children with actual color
if (childIsColored) {
inViewWithColorCount++;
}
float distance = childNode->distanceToCamera(viewFrustum);
if (distance < boundaryDistanceForRenderLevel(*childNode->octalCode + 1)) {
inViewCount = insertIntoSortedArrays((void*)childNode, distance, i,
(void**)&inViewChildren, (float*)&distancesToChildren,
(int*)&positionOfChildren, inViewCount, MAX_CHILDREN);
}
}
}
// If we have children with color, then we do want to add this node (and it's descendants) to the bag to be written
// we don't need to dig deeper.
//
// XXXBHG - this might be a good time to look at colors and add them to a dictionary? But we're not planning
// on scanning the whole tree, so we won't actually see all the colors, so maybe no point in that.
if (inViewWithColorCount) {
bag.insert(node);
} else {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about. We will iterate
// these based on which tree is closer.
for (int i = 0; i < inViewCount; i++) {
VoxelNode* childNode = inViewChildren[i];
thisLevel = currentSearchLevel; // reset this, since the children will munge it up
int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag);
maxChildLevel = std::max(maxChildLevel, childLevelReached);
}
}
return maxChildLevel;
}
int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, const ViewFrustum& viewFrustum,
unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag) {
// How many bytes have we written so far at this level;
int bytesWritten = 0;
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
if (!node->isInView(viewFrustum)) {
return bytesWritten;
}
// write the octal code
int codeLength = bytesRequiredForCodeLength(*node->octalCode);
memcpy(outputBuffer,node->octalCode,codeLength);
outputBuffer += codeLength; // move the pointer
bytesWritten += codeLength; // keep track of byte count
availableBytes -= codeLength; // keep track or remaining space
int currentEncodeLevel = 0;
int childBytesWritten = encodeTreeBitstreamRecursion(maxEncodeLevel, currentEncodeLevel,
node, viewFrustum,
outputBuffer, availableBytes, bag);
// if childBytesWritten == 1 then something went wrong... that's not possible
assert(childBytesWritten != 1);
// if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason
// couldn't be written... so reset them here...
if (childBytesWritten == 2) {
childBytesWritten = 0;
}
// if we wrote child bytes, then return our result of all bytes written
if (childBytesWritten) {
bytesWritten += childBytesWritten;
} else {
// otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code
bytesWritten = 0;
}
return bytesWritten;
}
int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
VoxelNode* node, const ViewFrustum& viewFrustum,
unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag) const {
// How many bytes have we written so far at this level;
int bytesAtThisLevel = 0;
// Keep track of how deep we've encoded.
currentEncodeLevel++;
// If we've reached our max Search Level, then stop searching.
if (currentEncodeLevel >= maxEncodeLevel) {
return bytesAtThisLevel;
}
float distance = node->distanceToCamera(viewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->octalCode + 1);
// If we're too far away for our render level, then just return
if (distance >= boundaryDistance) {
return bytesAtThisLevel;
}
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
// we're out of view
if (!node->isInView(viewFrustum)) {
return bytesAtThisLevel;
}
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
// At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level
// is 1 byte for child colors + 3*8 bytes for the actual colors + 1 byte for child trees. There could be sub trees
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
const int CHILD_COLOR_MASK_BYTES = 1;
const int MAX_CHILDREN = 8;
const int BYTES_PER_COLOR = 3;
const int CHILD_TREE_EXISTS_BYTES = 1;
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + MAX_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES;
// Make our local buffer large enough to handle writing at this level in case we need to.
unsigned char thisLevelBuffer[MAX_LEVEL_BYTES];
unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0];
unsigned char childrenExistBits = 0;
unsigned char childrenColoredBits = 0;
int inViewCount = 0;
int inViewNotLeafCount = 0;
int inViewWithColorCount = 0;
// for each child node, check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int i = 0; i < MAX_CHILDREN; i++) {
VoxelNode* childNode = node->children[i];
bool childExists = (childNode != NULL);
bool childIsInView = (childExists && childNode->isInView(viewFrustum));
if (childIsInView) {
// Before we determine consider this further, let's see if it's in our LOD scope...
float distance = childNode->distanceToCamera(viewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(*childNode->octalCode + 1);
if (distance < boundaryDistance) {
inViewCount++;
// track children in view as existing and not a leaf, if they're a leaf,
// we don't care about recursing deeper on them, and we don't consider their
// subtree to exist
if (!(childExists && childNode->isLeaf())) {
childrenExistBits += (1 << (7 - i));
inViewNotLeafCount++;
}
// track children with actual color
if (childExists && childNode->isColored()) {
childrenColoredBits += (1 << (7 - i));
inViewWithColorCount++;
}
}
}
}
*writeToThisLevelBuffer = childrenColoredBits;
writeToThisLevelBuffer += sizeof(childrenColoredBits); // move the pointer
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
// write the color data...
for (int i = 0; i < MAX_CHILDREN; i++) {
if (oneAtBit(childrenColoredBits, i)) {
memcpy(writeToThisLevelBuffer, &node->children[i]->getColor(), BYTES_PER_COLOR);
writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color
bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color
}
}
// write the child exist bits
*writeToThisLevelBuffer = childrenExistBits;
writeToThisLevelBuffer += sizeof(childrenExistBits); // move the pointer
bytesAtThisLevel += sizeof(childrenExistBits); // keep track of byte count
// We only need to keep digging, if there is at least one child that is inView, and not a leaf.
keepDiggingDeeper = (inViewNotLeafCount > 0);
// If we have enough room to copy our local results into the buffer, then do so...
if (availableBytes >= bytesAtThisLevel) {
memcpy(outputBuffer, &thisLevelBuffer[0], bytesAtThisLevel);
outputBuffer += bytesAtThisLevel;
availableBytes -= bytesAtThisLevel;
} else {
bag.insert(node);
return 0;
}
if (keepDiggingDeeper) {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about.
//
// Since this recursive function assumes we're already writing, we know we've already written our
// childrenExistBits. But... we don't really know how big the child tree will be. And we don't know if
// we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is
// write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they
// write something, we keep them in the bits, if they don't, we take them out.
//
// we know the last thing we wrote to the outputBuffer was our childrenExistBits. Let's remember where that was!
unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistBits);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (oneAtBit(childrenExistBits, i)) {
VoxelNode* childNode = node->children[i];
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,
viewFrustum, outputBuffer, availableBytes, bag);
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
// basically, the children below don't contain any info.
// if the child tree wrote 1 byte??? something must have gone wrong... because it must have at least the color
// byte and the child exist byte.
//
assert(childTreeBytesOut != 1);
// if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because...
// if it had colors it would write 1 byte for the color mask,
// and at least a color's worth of bytes for the node of colors.
// if it had child trees (with something in them) then it would have the 1 byte for child mask
// and some number of bytes of lower children...
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
//
// we can make this act like no bytes out, by just resetting the bytes out in this case
if (childTreeBytesOut == 2) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
bytesAtThisLevel += childTreeBytesOut;
availableBytes -= childTreeBytesOut;
outputBuffer += childTreeBytesOut;
// If we had previously started writing, and if the child DIDN'T write any bytes,
// then we want to remove their bit from the childExistsPlaceHolder bitmask
if (childTreeBytesOut == 0) {
// remove this child's bit...
childrenExistBits -= (1 << (7 - i));
// repair the child exists mask
*childExistsPlaceHolder = childrenExistBits;
// Note: no need to move the pointer, cause we already stored this
} // end if (childTreeBytesOut == 0)
} // end if (oneAtBit(childrenExistBits, i))
} // end for
} // end keepDiggingDeeper
return bytesAtThisLevel;
}

View file

@ -13,11 +13,7 @@
#include "ViewFrustum.h"
#include "VoxelNode.h"
#include "MarkerNode.h"
const int MAX_VOXEL_PACKET_SIZE = 1492;
const int MAX_TREE_SLICE_BYTES = 26;
const int TREE_SCALE = 10;
#include "VoxelNodeBag.h"
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, bool down, void* extraData);
@ -48,25 +44,30 @@ public:
void deleteVoxelCodeFromTree(unsigned char *codeBuffer);
void printTreeForDebugging(VoxelNode *startNode);
void reaverageVoxelColors(VoxelNode *startNode);
unsigned char * loadBitstreamBuffer(unsigned char *& bitstreamBuffer,
VoxelNode *currentVoxelNode,
MarkerNode *currentMarkerNode,
const glm::vec3& agentPosition,
float thisNodePosition[3],
const ViewFrustum& viewFrustum,
bool viewFrustumCulling,
unsigned char * octalCode = NULL);
void loadVoxelsFile(const char* fileName, bool wantColorRandomizer);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer);
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
int encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, const ViewFrustum& viewFrustum,
unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag);
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag);
private:
void recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData);
VoxelNode * nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode, VoxelNode** parentOfFoundNode);
VoxelNode * createMissingNode(VoxelNode *lastParentNode, unsigned char *deepestCodeToCreate);
int readNodeData(VoxelNode *destinationNode, unsigned char * nodeData, int bufferSizeBytes);
int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
VoxelNode* node, const ViewFrustum& viewFrustum,
unsigned char* outputBuffer, int availableBytes,
VoxelNodeBag& bag) const;
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag);
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode);
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes);
};
int boundaryDistanceForRenderLevel(unsigned int renderLevel);

View file

@ -6,21 +6,45 @@
//
//
#include "PacketHeaders.h"
#include "VoxelAgentData.h"
#include <cstring>
#include <cstdio>
VoxelAgentData::VoxelAgentData() {
rootMarkerNode = new MarkerNode();
init();
}
void VoxelAgentData::init() {
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE;
_voxelPacketAt = _voxelPacket;
_maxSearchLevel = 1;
_maxLevelReachedInLastSearch = 1;
resetVoxelPacket();
}
void VoxelAgentData::resetVoxelPacket() {
_voxelPacket[0] = PACKET_HEADER_VOXEL_DATA;
_voxelPacketAt = &_voxelPacket[1];
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - 1;
_voxelPacketWaiting = false;
}
void VoxelAgentData::writeToPacket(unsigned char* buffer, int bytes) {
memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes;
_voxelPacketWaiting = true;
}
VoxelAgentData::~VoxelAgentData() {
delete rootMarkerNode;
delete[] _voxelPacket;
}
VoxelAgentData::VoxelAgentData(const VoxelAgentData &otherAgentData) {
memcpy(&_position, &otherAgentData._position, sizeof(_position));
rootMarkerNode = new MarkerNode();
init();
}
VoxelAgentData* VoxelAgentData::clone() const {

View file

@ -12,17 +12,41 @@
#include <iostream>
#include <AgentData.h>
#include <AvatarData.h>
#include "MarkerNode.h"
#include "VoxelNodeBag.h"
#include "VoxelConstants.h"
class VoxelAgentData : public AvatarData {
public:
MarkerNode *rootMarkerNode;
VoxelAgentData();
~VoxelAgentData();
VoxelAgentData(const VoxelAgentData &otherAgentData);
VoxelAgentData* clone() const;
void init(); // sets up data internals
void resetVoxelPacket(); // resets voxel packet to after "V" header
void writeToPacket(unsigned char* buffer, int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _voxelPacket; }
int getPacketLength() const { return (MAX_VOXEL_PACKET_SIZE - _voxelPacketAvailableBytes); }
bool isPacketWaiting() const { return _voxelPacketWaiting; }
int getAvailable() const { return _voxelPacketAvailableBytes; }
int getMaxSearchLevel() const { return _maxSearchLevel; };
void resetMaxSearchLevel() { _maxSearchLevel = 1; };
void incrementMaxSearchLevel() { _maxSearchLevel++; };
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; };
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
VoxelNodeBag nodeBag;
private:
unsigned char* _voxelPacket;
unsigned char* _voxelPacketAt;
int _voxelPacketAvailableBytes;
bool _voxelPacketWaiting;
int _maxSearchLevel;
int _maxLevelReachedInLastSearch;
};
#endif /* defined(__hifi__VoxelAgentData__) */

View file

@ -29,9 +29,6 @@
const int VOXEL_LISTEN_PORT = 40106;
const int VERTICES_PER_VOXEL = 8;
const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float));
const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES;
@ -70,14 +67,54 @@ void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) {
tree->createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer);
}
int _nodeCount=0;
bool countVoxelsOperation(VoxelNode* node, bool down, void* extraData) {
if (down) {
if (node->isColored()){
_nodeCount++;
}
}
return true; // keep going
}
void addSphereScene(VoxelTree * tree, bool wantColorRandomizer) {
printf("adding scene of spheres...\n");
tree->createSphere(0.25,0.5,0.5,0.5,(1.0/256),true,wantColorRandomizer);
tree->createSphere(0.030625,0.5,0.5,(0.25-0.06125),(1.0/512),true,true);
tree->createSphere(0.030625,(1.0-0.030625),(1.0-0.030625),(1.0-0.06125),(1.0/512),true,true);
tree->createSphere(0.030625,(1.0-0.030625),(1.0-0.030625),0.06125,(1.0/512),true,true);
tree->createSphere(0.030625,(1.0-0.030625),0.06125,(1.0-0.06125),(1.0/512),true,true);
tree->createSphere(0.06125,0.125,0.125,(1.0-0.125),(1.0/512),true,true);
int sphereBaseSize = 256;
tree->createSphere(0.25, 0.5, 0.5, 0.5, (1.0 / sphereBaseSize), true, wantColorRandomizer);
printf("one sphere added...\n");
tree->createSphere(0.030625, 0.5, 0.5, (0.25-0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("two spheres added...\n");
tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), (1.0 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("three spheres added...\n");
tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), 0.06125, (1.0 / (sphereBaseSize * 2)), true, true);
printf("four spheres added...\n");
tree->createSphere(0.030625, (1.0 - 0.030625), 0.06125, (1.0 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("five spheres added...\n");
tree->createSphere(0.06125, 0.125, 0.125, (1.0 - 0.125), (1.0 / (sphereBaseSize * 2)), true, true);
float radius = 0.0125f;
printf("6 spheres added...\n");
tree->createSphere(radius, 0.25, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("7 spheres added...\n");
tree->createSphere(radius, 0.125, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("8 spheres added...\n");
tree->createSphere(radius, 0.075, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("9 spheres added...\n");
tree->createSphere(radius, 0.05, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("10 spheres added...\n");
tree->createSphere(radius, 0.025, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("11 spheres added...\n");
_nodeCount=0;
tree->recurseTreeWithOperation(countVoxelsOperation);
printf("Nodes after adding scene %d nodes\n", _nodeCount);
printf("DONE adding scene of spheres...\n");
}
@ -118,21 +155,76 @@ void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
void eraseVoxelTreeAndCleanupAgentVisitData() {
// As our tree to erase all it's voxels
::randomTree.eraseAllVoxels();
// As our tree to erase all it's voxels
::randomTree.eraseAllVoxels();
// enumerate the agents clean up their marker nodes
for (AgentList::iterator agent = AgentList::getInstance()->begin(); agent != AgentList::getInstance()->end(); agent++) {
VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData();
if (agentData) {
// clean up the agent visit data
agentData->nodeBag.deleteAll();
}
}
}
// enumerate the agents clean up their marker nodes
for (AgentList::iterator agent = AgentList::getInstance()->begin(); agent != AgentList::getInstance()->end(); agent++) {
//printf("eraseVoxelTreeAndCleanupAgentVisitData() agent[%d]\n",i);
void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAgentData* agentData, ViewFrustum& viewFrustum) {
// If the bag is empty, fill it...
if (agentData->nodeBag.isEmpty()) {
int maxLevelReached = randomTree.searchForColoredNodes(agentData->getMaxSearchLevel(), randomTree.rootNode,
viewFrustum, agentData->nodeBag);
agentData->setMaxLevelReached(maxLevelReached);
VoxelAgentData *agentData = (VoxelAgentData *)agent->getLinkedData();
// If nothing got added, then we bump our levels.
if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
agentData->resetMaxSearchLevel();
} else {
agentData->incrementMaxSearchLevel();
}
}
}
// clean up the agent visit data
delete agentData->rootMarkerNode;
agentData->rootMarkerNode = new MarkerNode();
}
// If we have something in our nodeBag, then turn them into packets and send them out...
if (!agentData->nodeBag.isEmpty()) {
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
int bytesWritten = 0;
int packetsSentThisInterval = 0;
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL) {
if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract();
bytesWritten = randomTree.encodeTreeBitstream(agentData->getMaxSearchLevel(), subTree, viewFrustum,
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag);
if (agentData->getAvailable() >= bytesWritten) {
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
packetsSentThisInterval++;
agentData->resetVoxelPacket();
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
}
} else {
if (agentData->isPacketWaiting()) {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
agentData->resetVoxelPacket();
}
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
}
}
// if during this last pass, we emptied our bag, then we want to move to the next level.
if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
agentData->resetMaxSearchLevel();
} else {
agentData->incrementMaxSearchLevel();
}
}
}
}
void *distributeVoxelsToListeners(void *args) {
@ -140,77 +232,29 @@ void *distributeVoxelsToListeners(void *args) {
AgentList* agentList = AgentList::getInstance();
timeval lastSendTime;
unsigned char *stopOctal;
int packetCount;
int totalBytesSent;
unsigned char *voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
unsigned char *voxelPacketEnd;
float treeRoot[3] = {0, 0, 0};
while (true) {
gettimeofday(&lastSendTime, NULL);
// enumerate the agents to send 3 packets to each
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
VoxelAgentData *agentData = (VoxelAgentData *)agent->getLinkedData();
ViewFrustum viewFrustum;
// get position and orientation details from the camera
viewFrustum.setPosition(agentData->getCameraPosition());
viewFrustum.setOrientation(agentData->getCameraDirection(), agentData->getCameraUp(), agentData->getCameraRight());
// Also make sure it's got the correct lens details from the camera
viewFrustum.setFieldOfView(agentData->getCameraFov());
viewFrustum.setAspectRatio(agentData->getCameraAspectRatio());
viewFrustum.setNearClip(agentData->getCameraNearClip());
viewFrustum.setFarClip(agentData->getCameraFarClip());
viewFrustum.calculate();
VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData();
// debug for fun!!
if (::debugViewFrustum) {
viewFrustum.dump();
}
// Sometimes the agent data has not yet been linked, in which case we can't really do anything
if (agentData) {
ViewFrustum viewFrustum;
// get position and orientation details from the camera
viewFrustum.setPosition(agentData->getCameraPosition());
viewFrustum.setOrientation(agentData->getCameraDirection(), agentData->getCameraUp(), agentData->getCameraRight());
// Also make sure it's got the correct lens details from the camera
viewFrustum.setFieldOfView(agentData->getCameraFov());
viewFrustum.setAspectRatio(agentData->getCameraAspectRatio());
viewFrustum.setNearClip(agentData->getCameraNearClip());
viewFrustum.setFarClip(agentData->getCameraFarClip());
stopOctal = NULL;
packetCount = 0;
totalBytesSent = 0;
randomTree.leavesWrittenToBitstream = 0;
for (int j = 0; j < PACKETS_PER_CLIENT_PER_INTERVAL; j++) {
voxelPacketEnd = voxelPacket;
stopOctal = randomTree.loadBitstreamBuffer(voxelPacketEnd,
randomTree.rootNode,
agentData->rootMarkerNode,
agentData->getPosition(),
treeRoot,
viewFrustum,
::viewFrustumCulling,
stopOctal);
agentList->getAgentSocket().send(agent->getActiveSocket(), voxelPacket, voxelPacketEnd - voxelPacket);
packetCount++;
totalBytesSent += voxelPacketEnd - voxelPacket;
// XXXBHG Hack Attack: This is temporary code to help debug an issue.
// Normally we use this break to prevent resending voxels that an agent has
// already visited. But since we might be modifying the voxel tree we might
// want to always send. This is a hack to test the behavior
bool alwaysSend = true;
if (!alwaysSend && agentData->rootMarkerNode->childrenVisitedMask == 255) {
break;
}
}
// for any agent that has a root marker node with 8 visited children
// recursively delete its marker nodes so we can revisit
if (agentData->rootMarkerNode->childrenVisitedMask == 255) {
delete agentData->rootMarkerNode;
agentData->rootMarkerNode = new MarkerNode();
viewFrustum.calculate();
voxelDistributor(agentList, agent, agentData, viewFrustum);
}
}
@ -262,7 +306,7 @@ int main(int argc, const char * argv[])
::viewFrustumCulling = !cmdOptionExists(argc, argv, NO_VIEW_FRUSTUM_CULLING);
printf("viewFrustumCulling=%s\n", (::viewFrustumCulling ? "yes" : "no"));
const char* WANT_COLOR_RANDOMIZER = "--WantColorRandomizer";
const char* WANT_COLOR_RANDOMIZER = "--wantColorRandomizer";
::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER);
printf("wantColorRandomizer=%s\n", (::wantColorRandomizer ? "yes" : "no"));