Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Jeffrey Ventrella 2013-05-03 10:54:07 -07:00
commit 026ee4345c
14 changed files with 802 additions and 293 deletions

View file

@ -20,6 +20,27 @@
using namespace std; using namespace std;
const bool BALLS_ON = false;
const bool AVATAR_GRAVITY = true;
const float DECAY = 0.1;
const float THRUST_MAG = 1200.0;
const float YAW_MAG = 500.0;
const float BODY_SPIN_FRICTION = 5.0;
const float BODY_UPRIGHT_FORCE = 10.0;
const float BODY_PITCH_WHILE_WALKING = 30.0;
const float BODY_ROLL_WHILE_TURNING = 0.1;
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 BODY_SPRING_DEFAULT_TIGHTNESS = 10.0f;
const float COLLISION_RADIUS_SCALAR = 1.8;
const float COLLISION_BALL_FORCE = 1.0;
const float COLLISION_BODY_FORCE = 6.0;
const float COLLISION_BALL_FRICTION = 60.0;
const float COLLISION_BODY_FRICTION = 0.5;
float skinColor[] = {1.0, 0.84, 0.66}; float skinColor[] = {1.0, 0.84, 0.66};
float lightBlue[] = { 0.7, 0.8, 1.0 }; float lightBlue[] = { 0.7, 0.8, 1.0 };
float browColor[] = {210.0/255.0, 105.0/255.0, 30.0/255.0}; float browColor[] = {210.0/255.0, 105.0/255.0, 30.0/255.0};
@ -118,6 +139,7 @@ Avatar::Avatar(bool isMine) {
_sphere = NULL; _sphere = NULL;
_interactingOther = NULL; _interactingOther = NULL;
_handHoldingPosition = glm::vec3( 0.0, 0.0, 0.0 ); _handHoldingPosition = glm::vec3( 0.0, 0.0, 0.0 );
_distanceToNearestAvatar = std::numeric_limits<float>::max();
initializeSkeleton(); initializeSkeleton();
@ -205,6 +227,7 @@ Avatar::Avatar(const Avatar &otherAvatar) {
_head.lastLoudness = otherAvatar._head.lastLoudness; _head.lastLoudness = otherAvatar._head.lastLoudness;
_head.browAudioLift = otherAvatar._head.browAudioLift; _head.browAudioLift = otherAvatar._head.browAudioLift;
_head.noise = otherAvatar._head.noise; _head.noise = otherAvatar._head.noise;
_distanceToNearestAvatar = otherAvatar._distanceToNearestAvatar;
initializeSkeleton(); initializeSkeleton();
@ -302,6 +325,7 @@ bool Avatar::getIsNearInteractingOther() {
void Avatar::simulate(float deltaTime) { void Avatar::simulate(float deltaTime) {
// update balls // update balls
if (_balls) { _balls->simulate(deltaTime); } if (_balls) { _balls->simulate(deltaTime); }
@ -382,6 +406,15 @@ void Avatar::simulate(float deltaTime) {
// decay velocity // decay velocity
_velocity *= ( 1.0 - LIN_VEL_DECAY * deltaTime ); _velocity *= ( 1.0 - LIN_VEL_DECAY * deltaTime );
// If someone is near, damp velocity as a function of closeness
const float AVATAR_BRAKING_RANGE = 1.2f;
const float AVATAR_BRAKING_STRENGTH = 25.f;
if (_isMine && (_distanceToNearestAvatar < AVATAR_BRAKING_RANGE)) {
_velocity *=
(1.f - deltaTime * AVATAR_BRAKING_STRENGTH *
(AVATAR_BRAKING_RANGE - _distanceToNearestAvatar));
}
// update head information // update head information
updateHead(deltaTime); updateHead(deltaTime);
@ -417,7 +450,10 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
// if the avatar being simulated is mine, then loop through // if the avatar being simulated is mine, then loop through
// all the other avatars for potential interactions... // all the other avatars for potential interactions...
if ( _isMine ) if ( _isMine )
{ {
// Reset detector for nearest avatar
_distanceToNearestAvatar = std::numeric_limits<float>::max();
AgentList* agentList = AgentList::getInstance(); AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) { if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
@ -431,7 +467,9 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
v -= otherAvatar->getJointPosition( AVATAR_JOINT_RIGHT_SHOULDER ); v -= otherAvatar->getJointPosition( AVATAR_JOINT_RIGHT_SHOULDER );
float distance = glm::length( v ); float distance = glm::length( v );
if ( distance < _maxArmLength + _maxArmLength ) { if (distance < _distanceToNearestAvatar) { _distanceToNearestAvatar = distance; }
if (distance < _maxArmLength + _maxArmLength) {
_interactingOther = otherAvatar; _interactingOther = otherAvatar;
@ -445,7 +483,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
vectorBetweenHands -= otherAvatar->getJointPosition( AVATAR_JOINT_RIGHT_FINGERTIPS ); vectorBetweenHands -= otherAvatar->getJointPosition( AVATAR_JOINT_RIGHT_FINGERTIPS );
float distanceBetweenHands = glm::length(vectorBetweenHands); float distanceBetweenHands = glm::length(vectorBetweenHands);
if (distanceBetweenHands < _avatarTouch.HANDS_CLOSE_ENOUGH_TO_GRASP) { if (distanceBetweenHands < HANDS_CLOSE_ENOUGH_TO_GRASP) {
_avatarTouch.setHandsCloseEnoughToGrasp(true); _avatarTouch.setHandsCloseEnoughToGrasp(true);
} }
@ -453,7 +491,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
if (( _handState == 1 ) || ( _interactingOther->_handState == 1 )) { if (( _handState == 1 ) || ( _interactingOther->_handState == 1 )) {
// if the hands are close enough to grasp... // if the hands are close enough to grasp...
if (distanceBetweenHands < _avatarTouch.HANDS_CLOSE_ENOUGH_TO_GRASP) if (distanceBetweenHands < HANDS_CLOSE_ENOUGH_TO_GRASP)
{ {
// apply the forces... // apply the forces...
glm::vec3 vectorToOtherHand = _interactingOther->_handPosition - _handHoldingPosition; glm::vec3 vectorToOtherHand = _interactingOther->_handPosition - _handHoldingPosition;
@ -497,8 +535,7 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
} }
} }
void Avatar::updateHead(float deltaTime) { void Avatar::updateHead(float deltaTime) {
//apply the head lean values to the springy position... //apply the head lean values to the springy position...
@ -1351,7 +1388,9 @@ void Avatar::SetNewHeadTarget(float pitch, float yaw) {
_head.yawTarget = yaw; _head.yawTarget = yaw;
} }
//
// Process UDP interface data from Android transmitter or Google Glass // Process UDP interface data from Android transmitter or Google Glass
//
void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) {
// Read a packet from a transmitter app, process the data // Read a packet from a transmitter app, process the data
float float
@ -1362,30 +1401,43 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) {
rot1, rot2, rot3, rot4; // Rotation of device: rot1, rot2, rot3, rot4; // Rotation of device:
// rot1 = roll, ranges from -1 to 1, 0 = flat on table // rot1 = roll, ranges from -1 to 1, 0 = flat on table
// rot2 = pitch, ranges from -1 to 1, 0 = flat on table // rot2 = pitch, ranges from -1 to 1, 0 = flat on table
// rot3 = yaw, ranges from -1 to 1 // rot3 = yaw, ranges from -1 to 1
char device[100]; // Device ID
const bool IS_GLASS = false; // Whether to assume this is a Google glass transmitting enum deviceTypes { DEVICE_GLASS, DEVICE_ANDROID, DEVICE_IPHONE, DEVICE_UNKNOWN };
sscanf((char *)packetData, "tacc %f %f %f gra %f %f %f gyr %f %f %f lin %f %f %f rot %f %f %f %f", sscanf((char *)packetData,
"tacc %f %f %f gra %f %f %f gyr %f %f %f lin %f %f %f rot %f %f %f %f dna \"%s",
&accX, &accY, &accZ, &accX, &accY, &accZ,
&graX, &graY, &graZ, &graX, &graY, &graZ,
&gyrX, &gyrY, &gyrZ, &gyrX, &gyrY, &gyrZ,
&linX, &linY, &linZ, &linX, &linY, &linZ,
&rot1, &rot2, &rot3, &rot4); &rot1, &rot2, &rot3, &rot4, (char *)&device);
// decode transmitter device type
deviceTypes deviceType = DEVICE_UNKNOWN;
if (strcmp(device, "ADR")) {
deviceType = DEVICE_ANDROID;
} else {
deviceType = DEVICE_GLASS;
}
if (_transmitterPackets++ == 0) { if (_transmitterPackets++ == 0) {
// If first packet received, note time, turn head spring return OFF, get start rotation // If first packet received, note time, turn head spring return OFF, get start rotation
gettimeofday(&_transmitterTimer, NULL); gettimeofday(&_transmitterTimer, NULL);
if (IS_GLASS) { if (deviceType == DEVICE_GLASS) {
setHeadReturnToCenter(true); setHeadReturnToCenter(true);
setHeadSpringScale(10.f); setHeadSpringScale(10.f);
printLog("Using Google Glass to drive head, springs ON.\n"); printLog("Using Google Glass to drive head, springs ON.\n");
} else { } else {
setHeadReturnToCenter(false); setHeadReturnToCenter(false);
printLog("Using Transmitter to drive head, springs OFF.\n"); printLog("Using Transmitter %s to drive head, springs OFF.\n", device);
} }
//printLog("Packet: [%s]\n", packetData);
//printLog("Version: %s\n", device);
_transmitterInitialReading = glm::vec3( rot3, _transmitterInitialReading = glm::vec3( rot3,
rot2, rot2,
rot1 ); rot1 );
@ -1411,7 +1463,7 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) {
if (eulerAngles.x < -180.f) { eulerAngles.x += 360.f; } if (eulerAngles.x < -180.f) { eulerAngles.x += 360.f; }
glm::vec3 angularVelocity; glm::vec3 angularVelocity;
if (!IS_GLASS) { if (deviceType != DEVICE_GLASS) {
angularVelocity = glm::vec3(glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY)); angularVelocity = glm::vec3(glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY));
setHeadFromGyros( &eulerAngles, &angularVelocity, setHeadFromGyros( &eulerAngles, &angularVelocity,
(_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1.0); (_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1.0);
@ -1422,7 +1474,6 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) {
(_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1000.0); (_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1000.0);
} }
} }
void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity, float deltaTime, float smoothingTime) { void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity, float deltaTime, float smoothingTime) {
@ -1469,8 +1520,13 @@ glm::vec3 Avatar::getGravity(glm::vec3 pos) {
// For now, we'll test this with a simple global lookup, but soon we will add getting this // For now, we'll test this with a simple global lookup, but soon we will add getting this
// from the domain/voxelserver (or something similar) // from the domain/voxelserver (or something similar)
// //
if (glm::length(pos) < 5.f) { if ((pos.x > 0.f) &&
// If near the origin sphere, turn gravity ON (pos.x < 10.f) &&
(pos.z > 0.f) &&
(pos.z < 10.f) &&
(pos.y > 0.f) &&
(pos.y < 3.f)) {
// If above ground plane, turn gravity on
return glm::vec3(0.f, -1.f, 0.f); return glm::vec3(0.f, -1.f, 0.f);
} else { } else {
// If flying in space, turn gravity OFF // If flying in space, turn gravity OFF

View file

@ -148,27 +148,6 @@ public:
private: private:
const bool BALLS_ON = false;
const bool AVATAR_GRAVITY = true;
const float DECAY = 0.1;
const float THRUST_MAG = 1200.0;
const float YAW_MAG = 500.0;
const float BODY_SPIN_FRICTION = 5.0;
const float BODY_UPRIGHT_FORCE = 10.0;
const float BODY_PITCH_WHILE_WALKING = 30.0;
const float BODY_ROLL_WHILE_TURNING = 0.1;
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 BODY_SPRING_DEFAULT_TIGHTNESS = 10.0f;
const float COLLISION_RADIUS_SCALAR = 1.8;
const float COLLISION_BALL_FORCE = 1.0;
const float COLLISION_BODY_FORCE = 6.0;
const float COLLISION_BALL_FRICTION = 60.0;
const float COLLISION_BODY_FRICTION = 0.5;
// Do you want head to try to return to center (depends on interface detected) // Do you want head to try to return to center (depends on interface detected)
void setHeadReturnToCenter(bool r) { _returnHeadToCenter = r; }; void setHeadReturnToCenter(bool r) { _returnHeadToCenter = r; };
const bool getHeadReturnToCenter() const { return _returnHeadToCenter; }; const bool getHeadReturnToCenter() const { return _returnHeadToCenter; };
@ -268,6 +247,7 @@ private:
AvatarTouch _avatarTouch; AvatarTouch _avatarTouch;
bool _displayingHead; // should be false if in first-person view bool _displayingHead; // should be false if in first-person view
bool _returnHeadToCenter; bool _returnHeadToCenter;
float _distanceToNearestAvatar; // How close is the nearest avatar?
// private methods... // private methods...
void initializeSkeleton(); void initializeSkeleton();

View file

@ -11,11 +11,11 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
const float HANDS_CLOSE_ENOUGH_TO_GRASP = 0.1;
class AvatarTouch { class AvatarTouch {
public: public:
const float HANDS_CLOSE_ENOUGH_TO_GRASP = 0.1;
AvatarTouch(); AvatarTouch();
void simulate(float deltaTime); void simulate(float deltaTime);

View file

@ -213,42 +213,28 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
} }
void drawGroundPlaneGrid( float size, int resolution ) void drawGroundPlaneGrid(float size)
{ {
glColor3f( 0.4f, 0.5f, 0.3f ); glColor3f( 0.4f, 0.5f, 0.3f );
glLineWidth(2.0); glLineWidth(2.0);
float gridSize = 10.0; for (float x = 0; x <= size; x++) {
int gridResolution = 20; glBegin(GL_LINES);
glVertex3f(x, 0.0f, 0);
for (int g=0; g<gridResolution; g++) glVertex3f(x, 0.0f, size);
{ glVertex3f(0, 0.0f, x);
float fraction = (float)g / (float)( gridResolution - 1 ); glVertex3f(size, 0.0f, x);
float inc = -gridSize * ONE_HALF + fraction * gridSize; glEnd();
glBegin( GL_LINE_STRIP ); }
glVertex3f( inc, 0.0f, -gridSize * ONE_HALF );
glVertex3f( inc, 0.0f, gridSize * ONE_HALF );
glEnd();
}
for (int g=0; g<gridResolution; g++)
{
float fraction = (float)g / (float)( gridResolution - 1 );
float inc = -gridSize * ONE_HALF + fraction * gridSize;
glBegin( GL_LINE_STRIP );
glVertex3f( -gridSize * ONE_HALF, 0.0f, inc );
glVertex3f( gridSize * ONE_HALF, 0.0f, inc );
glEnd();
}
// Draw a translucent quad just underneath the grid. // Draw a translucent quad just underneath the grid.
glColor4f(0.5, 0.5, 0.5, 0.4); glColor4f(0.5, 0.5, 0.5, 0.4);
glBegin(GL_QUADS); glBegin(GL_QUADS);
glVertex3f(-gridSize * ONE_HALF, 0, -gridSize * ONE_HALF); glVertex3f(0, 0, 0);
glVertex3f(gridSize * ONE_HALF, 0, -gridSize * ONE_HALF); glVertex3f(size, 0, 0);
glVertex3f(gridSize * ONE_HALF, 0, gridSize * ONE_HALF); glVertex3f(size, 0, size);
glVertex3f(-gridSize * ONE_HALF, 0, gridSize * ONE_HALF); glVertex3f(0, 0, size);
glEnd(); glEnd();
} }

View file

@ -42,7 +42,7 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
float r=1.0, float g=1.0, float b=1.0); float r=1.0, float g=1.0, float b=1.0);
double diffclock(timeval *clock1,timeval *clock2); double diffclock(timeval *clock1,timeval *clock2);
void drawGroundPlaneGrid( float size, int resolution ); void drawGroundPlaneGrid(float size);
void renderOrientationDirections( glm::vec3 position, Orientation orientation, float size ); void renderOrientationDirections( glm::vec3 position, Orientation orientation, float size );

View file

@ -97,8 +97,26 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
switch(command) { switch(command) {
case PACKET_HEADER_VOXEL_DATA: case PACKET_HEADER_VOXEL_DATA:
{
double start = usecTimestampNow();
// ask the VoxelTree to read the bitstream into the tree // ask the VoxelTree to read the bitstream into the tree
_tree->readBitstreamToTree(voxelData, numBytes - 1); _tree->readBitstreamToTree(voxelData, numBytes - 1);
if (_renderWarningsOn && _tree->getNodesChangedFromBitstream()) {
printLog("readBitstreamToTree()... getNodesChangedFromBitstream=%ld _tree->isDirty()=%s \n",
_tree->getNodesChangedFromBitstream(), (_tree->isDirty() ? "yes" : "no") );
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! readBitstreamToTree() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! readBitstreamToTree() took %lf milliseconds\n",elapsedmsec);
}
}
}
break; break;
case PACKET_HEADER_ERASE_VOXEL: case PACKET_HEADER_ERASE_VOXEL:
// ask the tree to read the "remove" bitstream // ask the tree to read the "remove" bitstream
@ -135,17 +153,48 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
} }
void VoxelSystem::setupNewVoxelsForDrawing() { void VoxelSystem::setupNewVoxelsForDrawing() {
_voxelsUpdated = newTreeToArrays(_tree->rootNode); double start = usecTimestampNow();
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished);
if (sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed,SIXTY_FPS_IN_MILLISECONDS)) {
return; // bail early, it hasn't been long enough since the last time we ran
}
if (_tree->isDirty()) {
_callsToTreesToArrays++;
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
} else {
_voxelsUpdated = 0;
}
if (_voxelsUpdated) { if (_voxelsUpdated) {
_voxelsDirty=true; _voxelsDirty=true;
} }
// copy the newly written data to the arrays designated for reading if (_voxelsDirty) {
copyWrittenDataToReadArrays(); // copy the newly written data to the arrays designated for reading
copyWrittenDataToReadArrays();
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! newTreeToArrays() took %lf seconds %ld voxels updated\n", elapsedsec, _voxelsUpdated);
} else {
printLog("WARNING! newTreeToArrays() took %lf milliseconds %ld voxels updated\n", elapsedmsec, _voxelsUpdated);
}
}
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
} }
void VoxelSystem::copyWrittenDataToReadArrays() { void VoxelSystem::copyWrittenDataToReadArrays() {
if (_voxelsDirty) { double start = usecTimestampNow();
if (_voxelsDirty && _voxelsUpdated) {
// lock on the buffer write lock so we can't modify the data when the GPU is reading it // lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock); pthread_mutex_lock(&_bufferWriteLock);
int bytesOfVertices = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat); int bytesOfVertices = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
@ -154,6 +203,18 @@ void VoxelSystem::copyWrittenDataToReadArrays() {
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors ); memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
pthread_mutex_unlock(&_bufferWriteLock); pthread_mutex_unlock(&_bufferWriteLock);
} }
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! copyWrittenDataToReadArrays() took %lf seconds for %ld voxels %ld updated\n",
elapsedsec, _voxelsInArrays, _voxelsUpdated);
} else {
printLog("WARNING! copyWrittenDataToReadArrays() took %lf milliseconds for %ld voxels %ld updated\n",
elapsedmsec, _voxelsInArrays, _voxelsUpdated);
}
}
} }
int VoxelSystem::newTreeToArrays(VoxelNode* node) { int VoxelSystem::newTreeToArrays(VoxelNode* node) {
@ -164,7 +225,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
float childBoundary = boundaryDistanceForRenderLevel(*node->octalCode + 2); float childBoundary = boundaryDistanceForRenderLevel(*node->octalCode + 2);
bool inBoundary = (distanceToNode <= boundary); bool inBoundary = (distanceToNode <= boundary);
bool inChildBoundary = (distanceToNode <= childBoundary); bool inChildBoundary = (distanceToNode <= childBoundary);
bool shouldRender = node->isColored() && ((node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary)); bool shouldRender = node->isColored() && ((node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary));
node->setShouldRender(shouldRender); node->setShouldRender(shouldRender);
// let children figure out their renderness // let children figure out their renderness
@ -213,8 +274,8 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
_voxelsInArrays++; // our know vertices in the arrays _voxelsInArrays++; // our know vertices in the arrays
} }
voxelsUpdated++; voxelsUpdated++;
node->clearDirtyBit();
} }
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated; return voxelsUpdated;
} }
@ -225,6 +286,11 @@ VoxelSystem* VoxelSystem::clone() const {
void VoxelSystem::init() { void VoxelSystem::init() {
_renderWarningsOn = false;
_callsToTreesToArrays = 0;
_setupNewVoxelsForDrawingLastFinished = 0;
_setupNewVoxelsForDrawingLastElapsed = 0;
// When we change voxels representations in the arrays, we'll update this // When we change voxels representations in the arrays, we'll update this
_voxelsDirty = false; _voxelsDirty = false;
_voxelsInArrays = 0; _voxelsInArrays = 0;
@ -296,6 +362,7 @@ void VoxelSystem::init() {
} }
void VoxelSystem::updateVBOs() { void VoxelSystem::updateVBOs() {
double start = usecTimestampNow();
if (_voxelsDirty) { if (_voxelsDirty) {
glBufferIndex segmentStart = 0; glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0; glBufferIndex segmentEnd = 0;
@ -327,9 +394,27 @@ void VoxelSystem::updateVBOs() {
} }
_voxelsDirty = false; _voxelsDirty = false;
} }
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! updateVBOs() took %lf seconds after %d calls to newTreeToArrays()\n",
elapsedsec, _callsToTreesToArrays);
} else {
printLog("WARNING! updateVBOs() took %lf milliseconds after %d calls to newTreeToArrays()\n",
elapsedmsec, _callsToTreesToArrays);
}
} else {
printLog("WARNING! updateVBOs() called after %d calls to newTreeToArrays()\n",_callsToTreesToArrays);
}
}
_callsToTreesToArrays = 0; // clear it
} }
void VoxelSystem::render() { void VoxelSystem::render() {
double start = usecTimestampNow();
glPushMatrix(); glPushMatrix();
updateVBOs(); updateVBOs();
// tell OpenGL where to find vertex and color information // tell OpenGL where to find vertex and color information
@ -348,7 +433,7 @@ void VoxelSystem::render() {
// draw the number of voxels we have // draw the number of voxels we have
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
glScalef(10, 10, 10); glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
glDrawElements(GL_TRIANGLES, 36 * _voxelsInArrays, GL_UNSIGNED_INT, 0); glDrawElements(GL_TRIANGLES, 36 * _voxelsInArrays, GL_UNSIGNED_INT, 0);
// deactivate vertex and color arrays after drawing // deactivate vertex and color arrays after drawing
@ -362,14 +447,31 @@ void VoxelSystem::render() {
// scale back down to 1 so heads aren't massive // scale back down to 1 so heads aren't massive
glPopMatrix(); glPopMatrix();
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! render() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! render() took %lf milliseconds\n",elapsedmsec);
}
}
} }
int VoxelSystem::_nodeCount = 0; int VoxelSystem::_nodeCount = 0;
void VoxelSystem::killLocalVoxels() {
_tree->eraseAllVoxels();
_voxelsInArrays = 0; // better way to do this??
//setupNewVoxelsForDrawing();
}
bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) {
_nodeCount++; _nodeCount++;
if (node->isColored()) { if (node->isColored()) {
nodeColor newColor = { randomColorValue(150), randomColorValue(150), randomColorValue(150), 1 }; nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 };
node->setColor(newColor); node->setColor(newColor);
} }
return true; return true;
@ -385,7 +487,7 @@ void VoxelSystem::randomizeVoxelColors() {
bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) {
_nodeCount++; _nodeCount++;
// always false colorize // always false colorize
node->setFalseColor(randomColorValue(150), randomColorValue(150), randomColorValue(150)); node->setFalseColor(255, randomColorValue(150), randomColorValue(150));
return true; // keep going! return true; // keep going!
} }

View file

@ -58,7 +58,14 @@ public:
void falseColorizeInView(ViewFrustum* viewFrustum); void falseColorizeInView(ViewFrustum* viewFrustum);
void falseColorizeDistanceFromView(ViewFrustum* viewFrustum); void falseColorizeDistanceFromView(ViewFrustum* viewFrustum);
void killLocalVoxels();
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
bool getRenderPipelineWarnings() const { return _renderWarningsOn; };
private: private:
int _callsToTreesToArrays;
bool _renderWarningsOn;
// Operation functions for tree recursion methods // Operation functions for tree recursion methods
static int _nodeCount; static int _nodeCount;
static bool randomColorOperation(VoxelNode* node, void* extraData); static bool randomColorOperation(VoxelNode* node, void* extraData);
@ -83,6 +90,10 @@ private:
unsigned long _voxelsUpdated; unsigned long _voxelsUpdated;
unsigned long _voxelsInArrays; unsigned long _voxelsInArrays;
double _setupNewVoxelsForDrawingLastElapsed;
double _setupNewVoxelsForDrawingLastFinished;
GLuint _vboVerticesID; GLuint _vboVerticesID;
GLuint _vboNormalsID; GLuint _vboNormalsID;
GLuint _vboColorsID; GLuint _vboColorsID;

View file

@ -15,7 +15,7 @@
// Welcome Aboard! // Welcome Aboard!
// //
// //
// Keyboard Commands: // Keyboard Commands:
// //
// / = toggle stats display // / = toggle stats display
// spacebar = reset gyros/head position // spacebar = reset gyros/head position
@ -134,16 +134,21 @@ glm::vec3 box(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE);
VoxelSystem voxels; VoxelSystem voxels;
bool wantToKillLocalVoxels = false;
#ifndef _WIN32 #ifndef _WIN32
Audio audio(&audioScope, &myAvatar); Audio audio(&audioScope, &myAvatar);
#endif #endif
#define IDLE_SIMULATE_MSECS 8 // How often should call simulate and other stuff #define IDLE_SIMULATE_MSECS 16 // How often should call simulate and other stuff
// in the idle loop? // in the idle loop?
// Where one's own agent begins in the world (needs to become a dynamic thing passed to the program) // Where one's own agent begins in the world (needs to become a dynamic thing passed to the program)
glm::vec3 start_location(6.1f, 0, 1.4f); glm::vec3 start_location(6.1f, 0, 1.4f);
bool renderWarningsOn = false; // Whether to show render pipeline warnings
bool statsOn = false; // Whether to show onscreen text overlay with stats bool statsOn = false; // Whether to show onscreen text overlay with stats
bool starsOn = false; // Whether to display the stars bool starsOn = false; // Whether to display the stars
bool paintOn = false; // Whether to paint voxels as you fly around bool paintOn = false; // Whether to paint voxels as you fly around
@ -178,7 +183,10 @@ int menuOn = 1; // Whether to show onscreen menu
ChatEntry chatEntry; // chat entry field ChatEntry chatEntry; // chat entry field
bool chatEntryOn = false; // Whether to show the chat entry bool chatEntryOn = false; // Whether to show the chat entry
bool oculusOn = false; // Whether to configure the display for the Oculus Rift
GLuint oculusTextureID = 0; // The texture to which we render for Oculus distortion
GLhandleARB oculusProgramID = 0; // The GLSL program containing the distortion shader
float oculusDistortionScale = 1.25; // Controls the Oculus field of view
// //
// Serial USB Variables // Serial USB Variables
@ -225,37 +233,30 @@ void displayStats(void)
if (::menuOn == 0) { if (::menuOn == 0) {
statsVerticalOffset = 8; statsVerticalOffset = 8;
} }
// bitmap chars are about 10 pels high
char legend[] = "/ - toggle this display, Q - exit, H - show head, M - show hand, T - test audio";
drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, legend);
char legend2[] = "* - toggle stars, & - toggle paint mode, '-' - send erase all, '%' - send add scene";
drawtext(10, statsVerticalOffset + 32, 0.10f, 0, 1.0, 0, legend2);
glm::vec3 avatarPos = myAvatar.getPosition();
char stats[200]; char stats[200];
sprintf(stats, "FPS = %3.0f Pkts/s = %d Bytes/s = %d Head(x,y,z)= %4.2f, %4.2f, %4.2f ", sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps",
FPS, packetsPerSecond, bytesPerSecond, avatarPos.x,avatarPos.y,avatarPos.z); FPS, packetsPerSecond, (float)bytesPerSecond * 8.f / 1000000.f);
drawtext(10, statsVerticalOffset + 49, 0.10f, 0, 1.0, 0, stats); drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats);
std::stringstream voxelStats; std::stringstream voxelStats;
voxelStats << "Voxels Rendered: " << voxels.getVoxelsRendered() << " Updated: " << voxels.getVoxelsUpdated(); voxelStats.precision(4);
drawtext(10, statsVerticalOffset + 70, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats << "Voxels Rendered: " << voxels.getVoxelsRendered() / 1000.f << "K Updated: " << voxels.getVoxelsUpdated()/1000.f << "K";
drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelStats << "Voxels Created: " << voxels.getVoxelsCreated() << " (" << voxels.getVoxelsCreatedPerSecondAverage() voxelStats << "Voxels Created: " << voxels.getVoxelsCreated() / 1000.f << "K (" << voxels.getVoxelsCreatedPerSecondAverage() / 1000.f
<< "/sec) "; << "Kps) ";
drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelStats << "Voxels Colored: " << voxels.getVoxelsColored() << " (" << voxels.getVoxelsColoredPerSecondAverage() voxelStats << "Voxels Colored: " << voxels.getVoxelsColored() / 1000.f << "K (" << voxels.getVoxelsColoredPerSecondAverage() / 1000.f
<< "/sec) "; << "Kps) ";
drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
voxelStats << "Voxels Bytes Read: " << voxels.getVoxelsBytesRead() voxelStats << "Voxel Bits Read: " << voxels.getVoxelsBytesRead() * 8.f / 1000000.f
<< " (" << voxels.getVoxelsBytesReadPerSecondAverage() << " Bps)"; << "M (" << voxels.getVoxelsBytesReadPerSecondAverage() * 8.f / 1000000.f << " Mbps)";
drawtext(10, statsVerticalOffset + 290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
voxelStats.str(""); voxelStats.str("");
@ -263,13 +264,13 @@ void displayStats(void)
? ((float) voxels.getVoxelsBytesRead() / voxels.getVoxelsColored()) ? ((float) voxels.getVoxelsBytesRead() / voxels.getVoxelsColored())
: 0; : 0;
voxelStats << "Voxels Bytes per Colored: " << voxelsBytesPerColored; voxelStats << "Voxels Bits per Colored: " << voxelsBytesPerColored * 8;
drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
Agent *avatarMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER); Agent *avatarMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
char avatarMixerStats[200]; char avatarMixerStats[200];
if (avatarMixer) { if (avatarMixer) {
sprintf(avatarMixerStats, "Avatar Mixer - %.f kbps, %.f pps", sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
roundf(avatarMixer->getAverageKilobitsPerSecond()), roundf(avatarMixer->getAverageKilobitsPerSecond()),
roundf(avatarMixer->getAveragePacketsPerSecond())); roundf(avatarMixer->getAveragePacketsPerSecond()));
} else { } else {
@ -680,6 +681,289 @@ void renderViewFrustum(ViewFrustum& viewFrustum) {
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
} }
// displays a single side (left, right, or combined for non-Oculus)
void displaySide(Camera& whichCamera) {
glPushMatrix();
if (::starsOn) {
// should be the first rendering pass - w/o depth buffer / lighting
// finally render the starfield
stars.render(whichCamera.getFieldOfView(), aspectRatio, whichCamera.getNearClip());
}
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// draw a red sphere
float sphereRadius = 0.25f;
glColor3f(1,0,0);
glPushMatrix();
glutSolidSphere( sphereRadius, 15, 15 );
glPopMatrix();
//draw a grid ground plane....
drawGroundPlaneGrid(10.f);
// Draw voxels
if ( showingVoxels )
{
voxels.render();
}
// Render avatars of other agents
AgentList* agentList = AgentList::getInstance();
agentList->lock();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *avatar = (Avatar *)agent->getLinkedData();
avatar->render(0);
//avatarRenderer.render(avatar, 0); // this will replace the above call
}
}
agentList->unlock();
// Render the world box
if (!::lookingInMirror && ::statsOn) { render_world_box(); }
// brad's frustum for debugging
if (::frustumOn) renderViewFrustum(::viewFrustum);
//Render my own avatar
myAvatar.render(::lookingInMirror);
//avatarRenderer.render(&myAvatar, lookingInMirror); // this will replace the above call
glPopMatrix();
}
// this shader is an adaptation (HLSL -> GLSL, removed conditional) of the one in the Oculus sample
// code (Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp), which is under the Apache license
// (http://www.apache.org/licenses/LICENSE-2.0)
const char* DISTORTION_FRAGMENT_SHADER =
"#version 120\n"
"uniform sampler2D texture;"
"uniform vec2 lensCenter;"
"uniform vec2 screenCenter;"
"uniform vec2 scale;"
"uniform vec2 scaleIn;"
"uniform vec4 hmdWarpParam;"
"vec2 hmdWarp(vec2 in01) {"
" vec2 theta = (in01 - lensCenter) * scaleIn;"
" float rSq = theta.x * theta.x + theta.y * theta.y;"
" vec2 theta1 = theta * (hmdWarpParam.x + hmdWarpParam.y * rSq + "
" hmdWarpParam.z * rSq * rSq + hmdWarpParam.w * rSq * rSq * rSq);"
" return lensCenter + scale * theta1;"
"}"
"void main(void) {"
" vec2 tc = hmdWarp(gl_TexCoord[0].st);"
" vec2 below = step(screenCenter.st + vec2(-0.25, -0.5), tc.st);"
" vec2 above = vec2(1.0, 1.0) - step(screenCenter.st + vec2(0.25, 0.5), tc.st);"
" gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), texture2D(texture, tc), "
" above.s * above.t * below.s * below.t);"
"}";
// the locations of the uniform variables
int textureLocation;
int lensCenterLocation;
int screenCenterLocation;
int scaleLocation;
int scaleInLocation;
int hmdWarpParamLocation;
// renders both sides into a texture, then renders the texture to the display with distortion
void displayOculus(Camera& whichCamera) {
// magic numbers ahoy! in order to avoid pulling in the Oculus utility library that calculates
// the rendering parameters from the hardware stats, i just folded their calculations into
// constants using the stats for the current-model hardware as contained in the SDK file
// LibOVR/Src/Util/Util_Render_Stereo.cpp
// eye
// render the left eye view to the left side of the screen
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.151976, 0, 0); // +h, see Oculus SDK docs p. 26
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glTranslatef(0.032, 0, 0); // dip/2, see p. 27
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, WIDTH/2, HEIGHT);
displaySide(whichCamera);
// and the right eye to the right side
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef(-0.151976, 0, 0); // -h
gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
whichCamera.getNearClip(), whichCamera.getFarClip());
glTranslatef(-0.032, 0, 0);
glMatrixMode(GL_MODELVIEW);
glViewport(WIDTH/2, 0, WIDTH/2, HEIGHT);
displaySide(whichCamera);
glPopMatrix();
// restore our normal viewport
glViewport(0, 0, WIDTH, HEIGHT);
if (::oculusTextureID == 0) {
glGenTextures(1, &::oculusTextureID);
glBindTexture(GL_TEXTURE_2D, ::oculusTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLhandleARB shaderID = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
glShaderSourceARB(shaderID, 1, &DISTORTION_FRAGMENT_SHADER, 0);
glCompileShaderARB(shaderID);
::oculusProgramID = glCreateProgramObjectARB();
glAttachObjectARB(::oculusProgramID, shaderID);
glLinkProgramARB(::oculusProgramID);
textureLocation = glGetUniformLocationARB(::oculusProgramID, "texture");
lensCenterLocation = glGetUniformLocationARB(::oculusProgramID, "lensCenter");
screenCenterLocation = glGetUniformLocationARB(::oculusProgramID, "screenCenter");
scaleLocation = glGetUniformLocationARB(::oculusProgramID, "scale");
scaleInLocation = glGetUniformLocationARB(::oculusProgramID, "scaleIn");
hmdWarpParamLocation = glGetUniformLocationARB(::oculusProgramID, "hmdWarpParam");
} else {
glBindTexture(GL_TEXTURE_2D, ::oculusTextureID);
}
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WIDTH, 0, HEIGHT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// for reference on setting these values, see SDK file Samples/OculusRoomTiny/RenderTiny_Device.cpp
float scaleFactor = 1.0 / ::oculusDistortionScale;
float aspectRatio = (WIDTH * 0.5) / HEIGHT;
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glUseProgramObjectARB(::oculusProgramID);
glUniform1fARB(textureLocation, 0);
glUniform2fARB(lensCenterLocation, 0.287994, 0.5); // see SDK docs, p. 29
glUniform2fARB(screenCenterLocation, 0.25, 0.5);
glUniform2fARB(scaleLocation, 0.25 * scaleFactor, 0.5 * scaleFactor * aspectRatio);
glUniform2fARB(scaleInLocation, 4, 2 / aspectRatio);
glUniform4fARB(hmdWarpParamLocation, 1.0, 0.22, 0.24, 0);
glColor3f(1, 0, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(0.5, 0);
glVertex2f(WIDTH/2, 0);
glTexCoord2f(0.5, 1);
glVertex2f(WIDTH/2, HEIGHT);
glTexCoord2f(0, 1);
glVertex2f(0, HEIGHT);
glEnd();
glUniform2fARB(lensCenterLocation, 0.787994, 0.5);
glUniform2fARB(screenCenterLocation, 0.75, 0.5);
glBegin(GL_QUADS);
glTexCoord2f(0.5, 0);
glVertex2f(WIDTH/2, 0);
glTexCoord2f(1, 0);
glVertex2f(WIDTH, 0);
glTexCoord2f(1, 1);
glVertex2f(WIDTH, HEIGHT);
glTexCoord2f(0.5, 1);
glVertex2f(WIDTH/2, HEIGHT);
glEnd();
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgramObjectARB(0);
glPopMatrix();
}
void displayOverlay() {
// Render 2D overlay: I/O level bar graphs and text
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, WIDTH, HEIGHT, 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
#ifndef _WIN32
audio.render(WIDTH, HEIGHT);
audioScope.render();
#endif
if (displayHeadMouse && !::lookingInMirror && statsOn) {
// Display small target box at center or head mouse target that can also be used to measure LOD
glColor3f(1.0, 1.0, 1.0);
glDisable(GL_LINE_SMOOTH);
const int PIXEL_BOX = 20;
glBegin(GL_LINE_STRIP);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glEnd();
glEnable(GL_LINE_SMOOTH);
}
// Show detected levels from the serial I/O ADC channel sensors
if (displayLevels) serialPort.renderLevels(WIDTH,HEIGHT);
// Display stats and log text onscreen
glLineWidth(1.0f);
glPointSize(1.0f);
if (::statsOn) { displayStats(); }
if (::logOn) { logger.render(WIDTH, HEIGHT); }
// Show menu
if (::menuOn) {
glLineWidth(1.0f);
glPointSize(1.0f);
menu.render(WIDTH,HEIGHT);
}
// Show chat entry field
if (::chatEntryOn) {
chatEntry.render(WIDTH, HEIGHT);
}
// Stats at upper right of screen about who domain server is telling us about
glPointSize(1.0f);
char agents[100];
AgentList* agentList = AgentList::getInstance();
int totalAvatars = 0, totalServers = 0;
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
agent->getType() == AGENT_TYPE_AVATAR ? totalAvatars++ : totalServers++;
}
sprintf(agents, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
drawtext(WIDTH-150,20, 0.10, 0, 1.0, 0, agents, 1, 0, 0);
if (::paintOn) {
char paintMessage[100];
sprintf(paintMessage,"Painting (%.3f,%.3f,%.3f/%.3f/%d,%d,%d)",
::paintingVoxel.x,::paintingVoxel.y,::paintingVoxel.z,::paintingVoxel.s,
(unsigned int)::paintingVoxel.red,(unsigned int)::paintingVoxel.green,(unsigned int)::paintingVoxel.blue);
drawtext(WIDTH-350,50, 0.10, 0, 1.0, 0, paintMessage, 1, 1, 0);
}
glPopMatrix();
}
void display(void) void display(void)
{ {
@ -840,133 +1124,17 @@ void display(void)
glTranslatef( -whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z ); glTranslatef( -whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z );
if (::starsOn) { if (::oculusOn) {
// should be the first rendering pass - w/o depth buffer / lighting displayOculus(whichCamera);
// finally render the starfield } else {
stars.render(whichCamera.getFieldOfView(), aspectRatio, whichCamera.getNearClip()); displaySide(whichCamera);
glPopMatrix();
displayOverlay();
} }
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// draw a red sphere
float sphereRadius = 0.25f;
glColor3f(1,0,0);
glPushMatrix();
glutSolidSphere( sphereRadius, 15, 15 );
glPopMatrix();
//draw a grid ground plane....
drawGroundPlaneGrid( 5.0f, 9 );
// Draw voxels
if ( showingVoxels )
{
voxels.render();
}
// Render avatars of other agents
AgentList* agentList = AgentList::getInstance();
agentList->lock();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *avatar = (Avatar *)agent->getLinkedData();
avatar->render(0);
//avatarRenderer.render(avatar, 0); // this will replace the above call
}
}
agentList->unlock();
// Render the world box
if (!::lookingInMirror && ::statsOn) { render_world_box(); }
// brad's frustum for debugging
if (::frustumOn) renderViewFrustum(::viewFrustum);
//Render my own avatar
myAvatar.render(::lookingInMirror);
//avatarRenderer.render(&myAvatar, lookingInMirror); // this will replace the above call
} }
glPopMatrix();
// Render 2D overlay: I/O level bar graphs and text
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, WIDTH, HEIGHT, 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
#ifndef _WIN32
audio.render(WIDTH, HEIGHT);
audioScope.render();
#endif
if (displayHeadMouse && !::lookingInMirror && statsOn) {
// Display small target box at center or head mouse target that can also be used to measure LOD
glColor3f(1.0, 1.0, 1.0);
glDisable(GL_LINE_SMOOTH);
const int PIXEL_BOX = 20;
glBegin(GL_LINE_STRIP);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
glEnd();
glEnable(GL_LINE_SMOOTH);
}
// Show detected levels from the serial I/O ADC channel sensors
if (displayLevels) serialPort.renderLevels(WIDTH,HEIGHT);
// Display stats and log text onscreen
glLineWidth(1.0f);
glPointSize(1.0f);
if (::statsOn) { displayStats(); }
if (::logOn) { logger.render(WIDTH, HEIGHT); }
// Show menu
if (::menuOn) {
glLineWidth(1.0f);
glPointSize(1.0f);
menu.render(WIDTH,HEIGHT);
}
// Show chat entry field
if (::chatEntryOn) {
chatEntry.render(WIDTH, HEIGHT);
}
// Stats at upper right of screen about who domain server is telling us about
glPointSize(1.0f);
char agents[100];
AgentList* agentList = AgentList::getInstance();
int totalAvatars = 0, totalServers = 0;
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
agent->getType() == AGENT_TYPE_AVATAR ? totalAvatars++ : totalServers++;
}
sprintf(agents, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
drawtext(WIDTH-150,20, 0.10, 0, 1.0, 0, agents, 1, 0, 0);
if (::paintOn) {
char paintMessage[100];
sprintf(paintMessage,"Painting (%.3f,%.3f,%.3f/%.3f/%d,%d,%d)",
::paintingVoxel.x,::paintingVoxel.y,::paintingVoxel.z,::paintingVoxel.s,
(unsigned int)::paintingVoxel.red,(unsigned int)::paintingVoxel.green,(unsigned int)::paintingVoxel.blue);
drawtext(WIDTH-350,50, 0.10, 0, 1.0, 0, paintMessage, 1, 1, 0);
}
glPopMatrix();
glutSwapBuffers(); glutSwapBuffers();
frameCount++; frameCount++;
@ -1028,6 +1196,20 @@ int setGyroLook(int state) {
return iRet; return iRet;
} }
int setFullscreen(int state) {
bool wasFullscreen = ::fullscreen;
int value = setValue(state, &::fullscreen);
if (::fullscreen != wasFullscreen) {
if (::fullscreen) {
glutFullScreen();
} else {
glutReshapeWindow(WIDTH, HEIGHT);
}
}
return value;
}
int setVoxels(int state) { int setVoxels(int state) {
return setValue(state, &::showingVoxels); return setValue(state, &::showingVoxels);
} }
@ -1036,6 +1218,15 @@ int setStars(int state) {
return setValue(state, &::starsOn); return setValue(state, &::starsOn);
} }
int setOculus(int state) {
bool wasOn = ::oculusOn;
int value = setValue(state, &::oculusOn);
if (::oculusOn != wasOn) {
reshape(WIDTH, HEIGHT);
}
return value;
}
int setStats(int state) { int setStats(int state) {
return setValue(state, &::statsOn); return setValue(state, &::statsOn);
} }
@ -1044,6 +1235,14 @@ int setMenu(int state) {
return setValue(state, &::menuOn); return setValue(state, &::menuOn);
} }
int setRenderWarnings(int state) {
int value = setValue(state, &::renderWarningsOn);
if (state == MENU_ROW_PICKED) {
::voxels.setRenderPipelineWarnings(::renderWarningsOn);
}
return value;
}
int setDisplayFrustum(int state) { int setDisplayFrustum(int state) {
return setValue(state, &::frustumOn); return setValue(state, &::frustumOn);
} }
@ -1077,6 +1276,13 @@ int setFrustumRenderMode(int state) {
return ::frustumDrawingMode; return ::frustumDrawingMode;
} }
int doKillLocalVoxels(int state) {
if (state == MENU_ROW_PICKED) {
::wantToKillLocalVoxels = true;
}
return state;
}
int doRandomizeVoxelColors(int state) { int doRandomizeVoxelColors(int state) {
if (state == MENU_ROW_PICKED) { if (state == MENU_ROW_PICKED) {
::voxels.randomizeVoxelColors(); ::voxels.randomizeVoxelColors();
@ -1152,12 +1358,14 @@ void initMenu() {
menuColumnOptions->addRow("Mirror (h)", setHead); menuColumnOptions->addRow("Mirror (h)", setHead);
menuColumnOptions->addRow("Noise (n)", setNoise); menuColumnOptions->addRow("Noise (n)", setNoise);
menuColumnOptions->addRow("Gyro Look", setGyroLook); menuColumnOptions->addRow("Gyro Look", setGyroLook);
menuColumnOptions->addRow("Fullscreen (f)", setFullscreen);
menuColumnOptions->addRow("Quit (q)", quitApp); menuColumnOptions->addRow("Quit (q)", quitApp);
// Render // Render
menuColumnRender = menu.addColumn("Render"); menuColumnRender = menu.addColumn("Render");
menuColumnRender->addRow("Voxels (V)", setVoxels); menuColumnRender->addRow("Voxels (V)", setVoxels);
menuColumnRender->addRow("Stars (*)", setStars); menuColumnRender->addRow("Stars (*)", setStars);
menuColumnRender->addRow("Oculus (o)", setOculus);
// Tools // Tools
menuColumnTools = menu.addColumn("Tools"); menuColumnTools = menu.addColumn("Tools");
@ -1174,6 +1382,8 @@ void initMenu() {
// Debug // Debug
menuColumnDebug = menu.addColumn("Debug"); menuColumnDebug = menu.addColumn("Debug");
menuColumnDebug->addRow("Show Render Pipeline Warnings", setRenderWarnings);
menuColumnDebug->addRow("Kill Local Voxels", doKillLocalVoxels);
menuColumnDebug->addRow("Randomize Voxel TRUE Colors", doRandomizeVoxelColors); menuColumnDebug->addRow("Randomize Voxel TRUE Colors", doRandomizeVoxelColors);
menuColumnDebug->addRow("FALSE Color Voxels Randomly", doFalseRandomizeVoxelColors); menuColumnDebug->addRow("FALSE Color Voxels Randomly", doFalseRandomizeVoxelColors);
menuColumnDebug->addRow("FALSE Color Voxels by Distance", doFalseColorizeByDistance); menuColumnDebug->addRow("FALSE Color Voxels by Distance", doFalseColorizeByDistance);
@ -1349,6 +1559,8 @@ void key(unsigned char k, int x, int y)
if (k == 'F') ::frustumOn = !::frustumOn; // toggle view frustum debugging if (k == 'F') ::frustumOn = !::frustumOn; // toggle view frustum debugging
if (k == 'C') ::cameraFrustum = !::cameraFrustum; // toggle which frustum to look at if (k == 'C') ::cameraFrustum = !::cameraFrustum; // toggle which frustum to look at
if (k == 'O' || k == 'G') setFrustumOffset(MENU_ROW_PICKED); // toggle view frustum offset debugging if (k == 'O' || k == 'G') setFrustumOffset(MENU_ROW_PICKED); // toggle view frustum offset debugging
if (k == 'f') setFullscreen(!::fullscreen);
if (k == 'o') setOculus(!::oculusOn);
if (k == '[') ::viewFrustumOffsetYaw -= 0.5; if (k == '[') ::viewFrustumOffsetYaw -= 0.5;
if (k == ']') ::viewFrustumOffsetYaw += 0.5; if (k == ']') ::viewFrustumOffsetYaw += 0.5;
@ -1418,6 +1630,12 @@ void* networkReceive(void* args)
ssize_t bytesReceived; ssize_t bytesReceived;
while (!stopNetworkReceiveThread) { while (!stopNetworkReceiveThread) {
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
if (::wantToKillLocalVoxels) {
::voxels.killLocalVoxels();
::wantToKillLocalVoxels = false;
}
if (AgentList::getInstance()->getAgentSocket().receive(&senderAddress, incomingPacket, &bytesReceived)) { if (AgentList::getInstance()->getAgentSocket().receive(&senderAddress, incomingPacket, &bytesReceived)) {
packetCount++; packetCount++;
bytesCount += bytesReceived; bytesCount += bytesReceived;
@ -1519,19 +1737,19 @@ void reshape(int width, int height)
HEIGHT = height; HEIGHT = height;
aspectRatio = ((float)width/(float)height); // based on screen resize aspectRatio = ((float)width/(float)height); // based on screen resize
float fov;
float nearClip;
float farClip;
// get the lens details from the current camera // get the lens details from the current camera
if (::viewFrustumFromOffset) { Camera& camera = ::viewFrustumFromOffset ? (::viewFrustumOffsetCamera) : (::myCamera);
fov = ::viewFrustumOffsetCamera.getFieldOfView(); float nearClip = camera.getNearClip();
nearClip = ::viewFrustumOffsetCamera.getNearClip(); float farClip = camera.getFarClip();
farClip = ::viewFrustumOffsetCamera.getFarClip(); float fov;
if (::oculusOn) {
// more magic numbers; see Oculus SDK docs, p. 32
camera.setAspectRatio(aspectRatio *= 0.5);
camera.setFieldOfView(fov = 2 * atan((0.0468 * ::oculusDistortionScale) / 0.041) * (180 / PI));
} else { } else {
fov = ::myCamera.getFieldOfView(); camera.setFieldOfView(fov = 60);
nearClip = ::myCamera.getNearClip();
farClip = ::myCamera.getFarClip();
} }
//printLog("reshape() width=%d, height=%d, aspectRatio=%f fov=%f near=%f far=%f \n", //printLog("reshape() width=%d, height=%d, aspectRatio=%f fov=%f near=%f far=%f \n",
@ -1644,7 +1862,7 @@ int main(int argc, const char * argv[])
int ip = getLocalAddress(); int ip = getLocalAddress();
sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF));
} }
// the callback for our instance of AgentList is attachNewHeadToAgent // the callback for our instance of AgentList is attachNewHeadToAgent
AgentList::getInstance()->linkedDataCreateCallback = &attachNewHeadToAgent; AgentList::getInstance()->linkedDataCreateCallback = &attachNewHeadToAgent;
@ -1681,7 +1899,7 @@ int main(int argc, const char * argv[])
// field of view and near and far clip to make it interesting. // field of view and near and far clip to make it interesting.
//viewFrustumOffsetCamera.setFieldOfView(90.0); //viewFrustumOffsetCamera.setFieldOfView(90.0);
viewFrustumOffsetCamera.setNearClip(0.1); viewFrustumOffsetCamera.setNearClip(0.1);
viewFrustumOffsetCamera.setFarClip(500.0); viewFrustumOffsetCamera.setFarClip(500.0*TREE_SCALE);
printLog( "Created Display Window.\n" ); printLog( "Created Display Window.\n" );

View file

@ -24,4 +24,5 @@ const int COLOR_VALUES_PER_VOXEL = 3 * VERTICES_PER_VOXEL;
typedef unsigned long int glBufferIndex; typedef unsigned long int glBufferIndex;
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX; const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
#endif #endif

View file

@ -125,7 +125,9 @@ void VoxelNode::setFalseColor(colorPart red, colorPart green, colorPart blue) {
_currentColor[1] = green; _currentColor[1] = green;
_currentColor[2] = blue; _currentColor[2] = blue;
_currentColor[3] = 1; // XXXBHG - False colors are always considered set _currentColor[3] = 1; // XXXBHG - False colors are always considered set
_isDirty = true; //if (_shouldRender) {
_isDirty = true;
//}
} }
} }
@ -136,18 +138,24 @@ void VoxelNode::setFalseColored(bool isFalseColored) {
memcpy(&_currentColor,&_trueColor,sizeof(nodeColor)); memcpy(&_currentColor,&_trueColor,sizeof(nodeColor));
} }
_falseColored = isFalseColored; _falseColored = isFalseColored;
_isDirty = true; //if (_shouldRender) {
_isDirty = true;
//}
} }
}; };
void VoxelNode::setColor(const nodeColor& color) { void VoxelNode::setColor(const nodeColor& color) {
if (_trueColor[0] != color[0] || _trueColor[1] != color[1] || _trueColor[2] != color[2]) { if (_trueColor[0] != color[0] || _trueColor[1] != color[1] || _trueColor[2] != color[2]) {
//printLog("VoxelNode::setColor() was: (%d,%d,%d) is: (%d,%d,%d)\n",
// _trueColor[0],_trueColor[1],_trueColor[2],color[0],color[1],color[2]);
memcpy(&_trueColor,&color,sizeof(nodeColor)); memcpy(&_trueColor,&color,sizeof(nodeColor));
if (!_falseColored) { if (!_falseColored) {
memcpy(&_currentColor,&color,sizeof(nodeColor)); memcpy(&_currentColor,&color,sizeof(nodeColor));
} }
_isDirty = true; //if (_shouldRender) {
_isDirty = true;
//}
} }
} }
#endif #endif
@ -234,6 +242,7 @@ bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const {
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const { float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box; AABox box;
getAABox(box); getAABox(box);
box.scale(TREE_SCALE);
float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - (box.getCorner().x + box.getSize().x), 2) + 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().y - (box.getCorner().y + box.getSize().y), 2) +
powf(viewFrustum.getPosition().z - (box.getCorner().z + box.getSize().z), 2)); powf(viewFrustum.getPosition().z - (box.getCorner().z + box.getSize().z), 2));

View file

@ -15,6 +15,7 @@
typedef unsigned char colorPart; typedef unsigned char colorPart;
typedef unsigned char nodeColor[4]; typedef unsigned char nodeColor[4];
typedef unsigned char rgbColor[3];
class VoxelNode { class VoxelNode {
private: private:

View file

@ -25,7 +25,7 @@
using voxels_lib::printLog; using voxels_lib::printLog;
int boundaryDistanceForRenderLevel(unsigned int renderLevel) { int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
float voxelSizeScale = 5000.0; float voxelSizeScale = 500.0*TREE_SCALE;
return voxelSizeScale / powf(2, renderLevel); return voxelSizeScale / powf(2, renderLevel);
} }
@ -35,7 +35,8 @@ VoxelTree::VoxelTree() :
voxelsBytesRead(0), voxelsBytesRead(0),
voxelsCreatedStats(100), voxelsCreatedStats(100),
voxelsColoredStats(100), voxelsColoredStats(100),
voxelsBytesReadStats(100) { voxelsBytesReadStats(100),
_isDirty(true) {
rootNode = new VoxelNode(); rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1]; rootNode->octalCode = new unsigned char[1];
@ -127,17 +128,29 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
// check the colors mask to see if we have a child to color in // check the colors mask to see if we have a child to color in
if (oneAtBit(*nodeData, i)) { if (oneAtBit(*nodeData, i)) {
// create the child if it doesn't exist // create the child if it doesn't exist
if (destinationNode->children[i] == NULL) { if (!destinationNode->children[i]) {
destinationNode->addChildAtIndex(i); destinationNode->addChildAtIndex(i);
this->voxelsCreated++; if (destinationNode->isDirty()) {
this->voxelsCreatedStats.updateAverage(1); _isDirty = true;
_nodesChangedFromBitstream++;
}
voxelsCreated++;
voxelsCreatedStats.updateAverage(1);
} }
// pull the color for this child // pull the color for this child
nodeColor newColor; nodeColor newColor;
memcpy(newColor, nodeData + bytesRead, 3); memcpy(newColor, nodeData + bytesRead, 3);
newColor[3] = 1; newColor[3] = 1;
bool nodeWasDirty = destinationNode->children[i]->isDirty();
destinationNode->children[i]->setColor(newColor); destinationNode->children[i]->setColor(newColor);
bool nodeIsDirty = destinationNode->children[i]->isDirty();
if (nodeIsDirty) {
_isDirty = true;
}
if (!nodeWasDirty && nodeIsDirty) {
_nodesChangedFromBitstream++;
}
this->voxelsColored++; this->voxelsColored++;
this->voxelsColoredStats.updateAverage(1); this->voxelsColoredStats.updateAverage(1);
@ -145,7 +158,15 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
} }
} }
// average node's color based on color of children // average node's color based on color of children
bool nodeWasDirty = destinationNode->isDirty();
destinationNode->setColorFromAverageOfChildren(); destinationNode->setColorFromAverageOfChildren();
bool nodeIsDirty = destinationNode->isDirty();
if (nodeIsDirty) {
_isDirty = true;
}
if (!nodeWasDirty && nodeIsDirty) {
_nodesChangedFromBitstream++;
}
// give this destination node the child mask from the packet // give this destination node the child mask from the packet
unsigned char childMask = *(nodeData + bytesRead); unsigned char childMask = *(nodeData + bytesRead);
@ -157,9 +178,17 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
// check the exists mask to see if we have a child to traverse into // check the exists mask to see if we have a child to traverse into
if (oneAtBit(childMask, childIndex)) { if (oneAtBit(childMask, childIndex)) {
if (destinationNode->children[childIndex] == NULL) { if (!destinationNode->children[childIndex]) {
// add a child at that index, if it doesn't exist // add a child at that index, if it doesn't exist
bool nodeWasDirty = destinationNode->isDirty();
destinationNode->addChildAtIndex(childIndex); destinationNode->addChildAtIndex(childIndex);
bool nodeIsDirty = destinationNode->isDirty();
if (nodeIsDirty) {
_isDirty = true;
}
if (!nodeWasDirty && nodeIsDirty) {
_nodesChangedFromBitstream++;
}
this->voxelsCreated++; this->voxelsCreated++;
this->voxelsCreatedStats.updateAverage(this->voxelsCreated); this->voxelsCreatedStats.updateAverage(this->voxelsCreated);
} }
@ -179,6 +208,8 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes) { void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes) {
int bytesRead = 0; int bytesRead = 0;
unsigned char* bitstreamAt = bitstream; unsigned char* bitstreamAt = bitstream;
_nodesChangedFromBitstream = 0;
// Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes // 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 // into a single network packet. readNodeData() basically goes down a tree from the root, and fills things in from there
@ -193,6 +224,10 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeByt
// Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial // 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! // octal code is always relative to root!
bitstreamRootNode = createMissingNode(rootNode, (unsigned char*) bitstreamAt); bitstreamRootNode = createMissingNode(rootNode, (unsigned char*) bitstreamAt);
if (bitstreamRootNode->isDirty()) {
_isDirty = true;
_nodesChangedFromBitstream++;
}
} }
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
@ -403,6 +438,27 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
} }
} }
void VoxelTree::createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue) {
unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue);
this->readCodeColorBufferToTree(voxelData);
delete voxelData;
}
void VoxelTree::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color) {
glm::vec3 distance = point2 - point1;
glm::vec3 items = distance / unitSize;
int maxItems = std::max(items.x, std::max(items.y, items.z));
glm::vec3 increment = distance * (1.0f/ maxItems);
glm::vec3 pointAt = point1;
for (int i = 0; i <= maxItems; i++ ) {
pointAt += increment;
unsigned char* voxelData = pointToVoxel(pointAt.x,pointAt.y,pointAt.z,unitSize,color[0],color[1],color[2]);
readCodeColorBufferToTree(voxelData);
delete voxelData;
}
}
void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) { 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 // 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 // between two RGB colors. We will do the gradient along the phi spectrum
@ -498,10 +554,10 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
// Keep track of how deep we've searched. // Keep track of how deep we've searched.
currentSearchLevel++; currentSearchLevel++;
// If we've reached our max Search Level, then stop searching. // If we've passed our max Search Level, then stop searching. return last level searched
if (currentSearchLevel >= maxSearchLevel) { if (currentSearchLevel > maxSearchLevel) {
return currentSearchLevel; return currentSearchLevel-1;
} }
// 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 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)) { if (!node->isInView(viewFrustum)) {

View file

@ -46,6 +46,9 @@ public:
void reaverageVoxelColors(VoxelNode *startNode); void reaverageVoxelColors(VoxelNode *startNode);
void loadVoxelsFile(const char* fileName, bool wantColorRandomizer); void loadVoxelsFile(const char* fileName, bool wantColorRandomizer);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer); void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer);
void createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue);
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color);
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL); void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
@ -54,6 +57,10 @@ public:
VoxelNodeBag& bag); VoxelNodeBag& bag);
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag); int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag);
bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; };
unsigned long int getNodesChangedFromBitstream() const { return _nodesChangedFromBitstream; };
private: private:
int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel, int encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEncodeLevel,
@ -68,6 +75,9 @@ private:
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode); VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode);
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes); int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes);
bool _isDirty;
unsigned long int _nodesChangedFromBitstream;
}; };
int boundaryDistanceForRenderLevel(unsigned int renderLevel); int boundaryDistanceForRenderLevel(unsigned int renderLevel);

View file

@ -38,15 +38,14 @@ const float DEATH_STAR_RADIUS = 4.0;
const float MAX_CUBE = 0.05f; const float MAX_CUBE = 0.05f;
const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000; const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000;
const int PACKETS_PER_CLIENT_PER_INTERVAL = 20; int PACKETS_PER_CLIENT_PER_INTERVAL = 20;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4; const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
VoxelTree randomTree; VoxelTree randomTree;
bool wantColorRandomizer = false; bool wantColorRandomizer = false;
bool debugViewFrustum = false; bool debugVoxelSending = false;
bool viewFrustumCulling = true; // for now
void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) { void addSphere(VoxelTree * tree,bool random, bool wantColorRandomizer) {
float r = random ? randFloatInRange(0.05,0.1) : 0.25; float r = random ? randFloatInRange(0.05,0.1) : 0.25;
@ -76,43 +75,71 @@ bool countVoxelsOperation(VoxelNode* node, void* extraData) {
} }
void addSphereScene(VoxelTree * tree, bool wantColorRandomizer) { void addSphereScene(VoxelTree * tree, bool wantColorRandomizer) {
printf("adding scene of spheres...\n"); printf("adding scene...\n");
int sphereBaseSize = 512; float voxelSize = 1.f/32;
printf("creating corner points...\n");
tree->createSphere(0.25, 0.5, 0.5, 0.5, (1.0 / sphereBaseSize), true, wantColorRandomizer); tree->createVoxel(0 , 0 , 0 , voxelSize, 255, 255 ,255);
printf("one sphere added...\n"); tree->createVoxel(1.0 - voxelSize, 0 , 0 , voxelSize, 255, 0 ,0 );
tree->createSphere(0.030625, 0.5, 0.5, (0.25-0.06125), (1.0 / (sphereBaseSize * 2)), true, true); tree->createVoxel(0 , 1.0 - voxelSize, 0 , voxelSize, 0 , 255 ,0 );
tree->createVoxel(0 , 0 , 1.0 - voxelSize, voxelSize, 0 , 0 ,255);
printf("two spheres added...\n"); tree->createVoxel(1.0 - voxelSize, 0 , 1.0 - voxelSize, voxelSize, 255, 0 ,255);
tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), (1.0 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true); tree->createVoxel(0 , 1.0 - voxelSize, 1.0 - voxelSize, voxelSize, 0 , 255 ,255);
printf("three spheres added...\n"); tree->createVoxel(1.0 - voxelSize, 1.0 - voxelSize, 0 , voxelSize, 255, 255 ,0 );
tree->createSphere(0.030625, (1.0 - 0.030625), (1.0 - 0.030625), 0.06125, (1.0 / (sphereBaseSize * 2)), true, true); tree->createVoxel(1.0 - voxelSize, 1.0 - voxelSize, 1.0 - voxelSize, voxelSize, 255, 255 ,255);
printf("four spheres added...\n"); printf("DONE creating corner points...\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"); printf("creating voxel lines...\n");
tree->createSphere(0.06125, 0.125, 0.125, (1.0 - 0.125), (1.0 / (sphereBaseSize * 2)), true, true); float lineVoxelSize = 0.99f/256;
rgbColor red = {255,0,0};
rgbColor green = {0,255,0};
rgbColor blue = {0,0,255};
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(0, 0, 1), lineVoxelSize, blue);
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(1, 0, 0), lineVoxelSize, red);
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(0, 1, 0), lineVoxelSize, green);
printf("DONE creating lines...\n");
int sphereBaseSize = 512;
printf("creating spheres...\n");
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, (0.75 - 0.030625), (0.75 - 0.030625), (0.75 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("three spheres added...\n");
tree->createSphere(0.030625, (0.75 - 0.030625), (0.75 - 0.030625), 0.06125, (1.0 / (sphereBaseSize * 2)), true, true);
printf("four spheres added...\n");
tree->createSphere(0.030625, (0.75 - 0.030625), 0.06125, (0.75 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("five spheres added...\n");
tree->createSphere(0.06125, 0.125, 0.125, (0.75 - 0.125), (1.0 / (sphereBaseSize * 2)), true, true);
float radius = 0.0125f; float radius = 0.0125f;
printf("6 spheres added...\n"); printf("6 spheres added...\n");
tree->createSphere(radius, 0.25, radius * 5.0f, 0.25, (1.0 / 4096), true, true); tree->createSphere(radius, 0.25, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("7 spheres added...\n"); printf("7 spheres added...\n");
tree->createSphere(radius, 0.125, radius * 5.0f, 0.25, (1.0 / 4096), true, true); tree->createSphere(radius, 0.125, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("8 spheres added...\n"); printf("8 spheres added...\n");
tree->createSphere(radius, 0.075, radius * 5.0f, 0.25, (1.0 / 4096), true, true); tree->createSphere(radius, 0.075, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("9 spheres added...\n"); printf("9 spheres added...\n");
tree->createSphere(radius, 0.05, radius * 5.0f, 0.25, (1.0 / 4096), true, true); tree->createSphere(radius, 0.05, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("10 spheres added...\n"); printf("10 spheres added...\n");
tree->createSphere(radius, 0.025, radius * 5.0f, 0.25, (1.0 / 4096), true, true); tree->createSphere(radius, 0.025, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("11 spheres added...\n"); printf("11 spheres added...\n");
printf("DONE creating spheres...\n");
_nodeCount=0; _nodeCount=0;
tree->recurseTreeWithOperation(countVoxelsOperation); tree->recurseTreeWithOperation(countVoxelsOperation);
printf("Nodes after adding scene %d nodes\n", _nodeCount); printf("Nodes after adding scene %d nodes\n", _nodeCount);
printf("DONE adding scene of spheres...\n"); printf("DONE adding scene of spheres...\n");
} }
@ -167,8 +194,14 @@ void eraseVoxelTreeAndCleanupAgentVisitData() {
void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAgentData* agentData, ViewFrustum& viewFrustum) { void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAgentData* agentData, ViewFrustum& viewFrustum) {
// If the bag is empty, fill it... bool searchReset = false;
if (agentData->nodeBag.isEmpty()) { int searchLoops = 0;
int searchLevelWas = agentData->getMaxSearchLevel();
double start = usecTimestampNow();
while (!searchReset && agentData->nodeBag.isEmpty()) {
searchLoops++;
searchLevelWas = agentData->getMaxSearchLevel();
int maxLevelReached = randomTree.searchForColoredNodes(agentData->getMaxSearchLevel(), randomTree.rootNode, int maxLevelReached = randomTree.searchForColoredNodes(agentData->getMaxSearchLevel(), randomTree.rootNode,
viewFrustum, agentData->nodeBag); viewFrustum, agentData->nodeBag);
agentData->setMaxLevelReached(maxLevelReached); agentData->setMaxLevelReached(maxLevelReached);
@ -177,17 +210,38 @@ void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAge
if (agentData->nodeBag.isEmpty()) { if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) { if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
agentData->resetMaxSearchLevel(); agentData->resetMaxSearchLevel();
searchReset = true;
} else { } else {
agentData->incrementMaxSearchLevel(); agentData->incrementMaxSearchLevel();
} }
} }
} }
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! searchForColoredNodes() took %lf seconds to identify %d nodes at level %d in %d loops\n",
elapsedsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
} else {
printf("WARNING! searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
}
} else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
}
// If we have something in our nodeBag, then turn them into packets and send them out... // If we have something in our nodeBag, then turn them into packets and send them out...
if (!agentData->nodeBag.isEmpty()) { if (!agentData->nodeBag.isEmpty()) {
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
int bytesWritten = 0; int bytesWritten = 0;
int packetsSentThisInterval = 0; int packetsSentThisInterval = 0;
int truePacketsSent = 0;
int trueBytesSent = 0;
double start = usecTimestampNow();
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL) { while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL) {
if (!agentData->nodeBag.isEmpty()) { if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract(); VoxelNode* subTree = agentData->nodeBag.extract();
@ -200,6 +254,8 @@ void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAge
} else { } else {
agentList->getAgentSocket().send(agent->getActiveSocket(), agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength()); agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
packetsSentThisInterval++; packetsSentThisInterval++;
agentData->resetVoxelPacket(); agentData->resetVoxelPacket();
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten); agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
@ -208,12 +264,30 @@ void voxelDistributor(AgentList* agentList, AgentList::iterator& agent, VoxelAge
if (agentData->isPacketWaiting()) { if (agentData->isPacketWaiting()) {
agentList->getAgentSocket().send(agent->getActiveSocket(), agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength()); agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
agentData->resetVoxelPacket(); agentData->resetVoxelPacket();
} }
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
} }
} }
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! packetLoop() took %lf seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
}
} else if (::debugVoxelSending) {
printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
}
// if during this last pass, we emptied our bag, then we want to move to the next level. // if during this last pass, we emptied our bag, then we want to move to the next level.
if (agentData->nodeBag.isEmpty()) { if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) { if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
@ -296,13 +370,9 @@ int main(int argc, const char * argv[])
srand((unsigned)time(0)); srand((unsigned)time(0));
const char* DEBUG_VIEW_FRUSTUM = "--DebugViewFrustum"; const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
::debugViewFrustum = cmdOptionExists(argc, argv, DEBUG_VIEW_FRUSTUM); ::debugVoxelSending = cmdOptionExists(argc, argv, DEBUG_VOXEL_SENDING);
printf("debugViewFrustum=%s\n", (::debugViewFrustum ? "yes" : "no")); printf("debugVoxelSending=%s\n", (::debugVoxelSending ? "yes" : "no"));
const char* NO_VIEW_FRUSTUM_CULLING = "--NoViewFrustumCulling";
::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); ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER);
@ -315,6 +385,17 @@ int main(int argc, const char * argv[])
if (voxelsFilename) { if (voxelsFilename) {
randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer); randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer);
} }
// Check to see if the user passed in a command line option for setting packet send rate
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND);
if (packetsPerSecond) {
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/10;
if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) {
PACKETS_PER_CLIENT_PER_INTERVAL = 1;
}
printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL);
}
const char* ADD_RANDOM_VOXELS = "--AddRandomVoxels"; const char* ADD_RANDOM_VOXELS = "--AddRandomVoxels";
if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) { if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) {
@ -380,8 +461,6 @@ int main(int argc, const char * argv[])
delete []vertices; delete []vertices;
randomTree.readCodeColorBufferToTree(pVoxelData); randomTree.readCodeColorBufferToTree(pVoxelData);
//printf("readCodeColorBufferToTree() of size=%d atByte=%d receivedBytes=%ld\n",
// voxelDataSize,atByte,receivedBytes);
// skip to next // skip to next
pVoxelData+=voxelDataSize; pVoxelData+=voxelDataSize;
atByte+=voxelDataSize; atByte+=voxelDataSize;