diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index a7c34b8a36..5ab7f621df 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -92,13 +92,15 @@ void *sendBuffer(void *args) if (!agentBuffer->isStarted() && agentBuffer->diffLastWriteNextOutput() <= BUFFER_LENGTH_SAMPLES_PER_CHANNEL + JITTER_BUFFER_SAMPLES) { printf("Held back buffer for agent with ID %d.\n", agent->getAgentId()); + agentBuffer->setShouldBeAddedToMix(false); } else if (agentBuffer->diffLastWriteNextOutput() < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { printf("Buffer from agent with ID %d starved.\n", agent->getAgentId()); agentBuffer->setStarted(false); + agentBuffer->setShouldBeAddedToMix(false); } else { // good buffer, add this to the mix agentBuffer->setStarted(true); - agentBuffer->setAddedToMix(true); + agentBuffer->setShouldBeAddedToMix(true); } } } @@ -129,89 +131,91 @@ void *sendBuffer(void *args) if (otherAgent != agent || (otherAgent == agent && agentWantsLoopback)) { AudioRingBuffer* otherAgentBuffer = (AudioRingBuffer*) otherAgent->getLinkedData(); - float *agentPosition = agentRingBuffer->getPosition(); - float *otherAgentPosition = otherAgentBuffer->getPosition(); - - // calculate the distance to the other agent - - // use the distance to the other agent to calculate the change in volume for this frame - int lowAgentIndex = std::min(agent.getAgentIndex(), otherAgent.getAgentIndex()); - int highAgentIndex = std::max(agent.getAgentIndex(), otherAgent.getAgentIndex()); - - if (distanceCoeffs[lowAgentIndex][highAgentIndex] == 0) { - float distanceToAgent = sqrtf(powf(agentPosition[0] - otherAgentPosition[0], 2) + - powf(agentPosition[1] - otherAgentPosition[1], 2) + - powf(agentPosition[2] - otherAgentPosition[2], 2)); + if (otherAgentBuffer->shouldBeAddedToMix()) { + float *agentPosition = agentRingBuffer->getPosition(); + float *otherAgentPosition = otherAgentBuffer->getPosition(); - float minCoefficient = std::min(1.0f, - powf(0.5, (logf(DISTANCE_RATIO * distanceToAgent) / logf(3)) - 1)); - distanceCoeffs[lowAgentIndex][highAgentIndex] = minCoefficient; - } - - - // get the angle from the right-angle triangle - float triangleAngle = atan2f(fabsf(agentPosition[2] - otherAgentPosition[2]), - fabsf(agentPosition[0] - otherAgentPosition[0])) * (180 / M_PI); - float angleToSource; - - - // find the angle we need for calculation based on the orientation of the triangle - if (otherAgentPosition[0] > agentPosition[0]) { - if (otherAgentPosition[2] > agentPosition[2]) { - angleToSource = -90 + triangleAngle - agentBearing; - } else { - angleToSource = -90 - triangleAngle - agentBearing; + // calculate the distance to the other agent + + // use the distance to the other agent to calculate the change in volume for this frame + int lowAgentIndex = std::min(agent.getAgentIndex(), otherAgent.getAgentIndex()); + int highAgentIndex = std::max(agent.getAgentIndex(), otherAgent.getAgentIndex()); + + if (distanceCoeffs[lowAgentIndex][highAgentIndex] == 0) { + float distanceToAgent = sqrtf(powf(agentPosition[0] - otherAgentPosition[0], 2) + + powf(agentPosition[1] - otherAgentPosition[1], 2) + + powf(agentPosition[2] - otherAgentPosition[2], 2)); + + float minCoefficient = std::min(1.0f, + powf(0.5, (logf(DISTANCE_RATIO * distanceToAgent) / logf(3)) - 1)); + distanceCoeffs[lowAgentIndex][highAgentIndex] = minCoefficient; } - } else { - if (otherAgentPosition[2] > agentPosition[2]) { - angleToSource = 90 - triangleAngle - agentBearing; + + + // get the angle from the right-angle triangle + float triangleAngle = atan2f(fabsf(agentPosition[2] - otherAgentPosition[2]), + fabsf(agentPosition[0] - otherAgentPosition[0])) * (180 / M_PI); + float angleToSource; + + + // find the angle we need for calculation based on the orientation of the triangle + if (otherAgentPosition[0] > agentPosition[0]) { + if (otherAgentPosition[2] > agentPosition[2]) { + angleToSource = -90 + triangleAngle - agentBearing; + } else { + angleToSource = -90 - triangleAngle - agentBearing; + } } else { - angleToSource = 90 + triangleAngle - agentBearing; + if (otherAgentPosition[2] > agentPosition[2]) { + angleToSource = 90 - triangleAngle - agentBearing; + } else { + angleToSource = 90 + triangleAngle - agentBearing; + } } - } - - if (angleToSource > 180) { - angleToSource -= 360; - } else if (angleToSource < -180) { - angleToSource += 360; - } - - angleToSource *= (M_PI / 180); - - float sinRatio = fabsf(sinf(angleToSource)); - int numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; - float weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); - - int16_t *goodChannel = angleToSource > 0 ? clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL : clientMix; - int16_t *delayedChannel = angleToSource > 0 ? clientMix : clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - - int16_t *delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer() + + if (angleToSource > 180) { + angleToSource -= 360; + } else if (angleToSource < -180) { + angleToSource += 360; + } + + angleToSource *= (M_PI / 180); + + float sinRatio = fabsf(sinf(angleToSource)); + int numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; + float weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); + + int16_t *goodChannel = angleToSource > 0 ? clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL : clientMix; + int16_t *delayedChannel = angleToSource > 0 ? clientMix : clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + + int16_t *delaySamplePointer = otherAgentBuffer->getNextOutput() == otherAgentBuffer->getBuffer() ? otherAgentBuffer->getBuffer() + RING_BUFFER_SAMPLES - numSamplesDelay : otherAgentBuffer->getNextOutput() - numSamplesDelay; - - - for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { - if (s < numSamplesDelay) { - // pull the earlier sample for the delayed channel + + for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { - int earlierSample = delaySamplePointer[s] * - distanceCoeffs[lowAgentIndex][highAgentIndex] * - otherAgentBuffer->getAttenuationRatio(); + if (s < numSamplesDelay) { + // pull the earlier sample for the delayed channel + + int earlierSample = delaySamplePointer[s] * + distanceCoeffs[lowAgentIndex][highAgentIndex] * + otherAgentBuffer->getAttenuationRatio(); + + plateauAdditionOfSamples(delayedChannel[s], earlierSample * weakChannelAmplitudeRatio); + } - plateauAdditionOfSamples(delayedChannel[s], earlierSample * weakChannelAmplitudeRatio); - } - - int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * - distanceCoeffs[lowAgentIndex][highAgentIndex] * - otherAgentBuffer->getAttenuationRatio()); - plateauAdditionOfSamples(goodChannel[s], currentSample); - - if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay], - currentSample * - weakChannelAmplitudeRatio * + int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * + distanceCoeffs[lowAgentIndex][highAgentIndex] * otherAgentBuffer->getAttenuationRatio()); + plateauAdditionOfSamples(goodChannel[s], currentSample); + + if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay], + currentSample * + weakChannelAmplitudeRatio * + otherAgentBuffer->getAttenuationRatio()); + } } } } @@ -222,14 +226,14 @@ void *sendBuffer(void *args) for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { AudioRingBuffer* agentBuffer = (AudioRingBuffer*) agent->getLinkedData(); - if (agentBuffer->wasAddedToMix()) { + if (agentBuffer->shouldBeAddedToMix()) { agentBuffer->setNextOutput(agentBuffer->getNextOutput() + BUFFER_LENGTH_SAMPLES_PER_CHANNEL); if (agentBuffer->getNextOutput() >= agentBuffer->getBuffer() + RING_BUFFER_SAMPLES) { agentBuffer->setNextOutput(agentBuffer->getBuffer()); } - agentBuffer->setAddedToMix(false); + agentBuffer->setShouldBeAddedToMix(false); } } diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index 657b897ee6..e3fb26ade3 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -60,7 +60,6 @@ int main(int argc, const char* argv[]) agentList->startDomainServerCheckInThread(); agentList->startSilentAgentRemovalThread(); - agentList->startPingUnknownAgentsThread(); sockaddr *agentAddress = new sockaddr; unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; @@ -70,34 +69,36 @@ int main(int argc, const char* argv[]) *broadcastPacket = PACKET_HEADER_BULK_AVATAR_DATA; unsigned char* currentBufferPosition = NULL; - int agentIndex = 0; while (true) { if (agentList->getAgentSocket().receive(agentAddress, packetData, &receivedBytes)) { switch (packetData[0]) { case PACKET_HEADER_HEAD_DATA: + // add this agent if we don't have them yet + if (agentList->addOrUpdateAgent(agentAddress, agentAddress, AGENT_TYPE_AVATAR, agentList->getLastAgentId())) { + agentList->increaseAgentId(); + } + // this is positional data from an agent agentList->updateAgentWithData(agentAddress, packetData, receivedBytes); - + currentBufferPosition = broadcastPacket + 1; - agentIndex = 0; // send back a packet with other active agent data to this agent - for (AgentList::iterator avatarAgent = agentList->begin(); - avatarAgent != agentList->end(); - avatarAgent++) { - if (avatarAgent->getLinkedData() != NULL - && !socketMatch(agentAddress, avatarAgent->getActiveSocket())) { - currentBufferPosition = addAgentToBroadcastPacket(currentBufferPosition, &*avatarAgent); + for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { + if (agent->getLinkedData() != NULL + && !socketMatch(agentAddress, agent->getActiveSocket())) { + currentBufferPosition = addAgentToBroadcastPacket(currentBufferPosition, &*agent); } - - agentIndex++; } agentList->getAgentSocket().send(agentAddress, broadcastPacket, currentBufferPosition - broadcastPacket); + break; + case PACKET_HEADER_DOMAIN: + // ignore the DS packet, for now agents are added only when they communicate directly with us break; default: // hand this off to the AgentList @@ -107,9 +108,8 @@ int main(int argc, const char* argv[]) } } - agentList->stopDomainServerCheckInThread(); agentList->stopSilentAgentRemovalThread(); - agentList->stopPingUnknownAgentsThread(); + agentList->stopDomainServerCheckInThread(); return 0; } diff --git a/eve/resources/audio/eve.raw b/eve/resources/audio/eve.raw new file mode 100644 index 0000000000..012fbe0e23 Binary files /dev/null and b/eve/resources/audio/eve.raw differ diff --git a/eve/src/main.cpp b/eve/src/main.cpp index d0ac737e2d..a06ec4981e 100644 --- a/eve/src/main.cpp +++ b/eve/src/main.cpp @@ -29,7 +29,7 @@ bool stopReceiveAgentDataThread; bool injectAudioThreadRunning = false; int TEMP_AUDIO_LISTEN_PORT = 55439; -// UDPSocket audioSocket(TEMP_AUDIO_LISTEN_PORT); +UDPSocket audioSocket(TEMP_AUDIO_LISTEN_PORT); void *receiveAgentData(void *args) { sockaddr senderAddress; @@ -80,7 +80,7 @@ void *injectAudio(void *args) { } // we have an active audio mixer we can send data to -// eveAudioInjector->injectAudio(&::audioSocket, audioMixer->getActiveSocket()); + eveAudioInjector->injectAudio(&::audioSocket, audioMixer->getActiveSocket()); } ::injectAudioThreadRunning = false; @@ -124,7 +124,7 @@ int main(int argc, const char* argv[]) { 0.32, // this is the same as the pelvis standing height (as of 4/26/13) eve.getPosition()[2] + 0.1)); // read eve's audio data - AudioInjector eveAudioInjector("eve.raw"); + AudioInjector eveAudioInjector("/etc/highfidelity/eve/resources/eve.raw"); unsigned char broadcastPacket[MAX_PACKET_SIZE]; broadcastPacket[0] = PACKET_HEADER_HEAD_DATA; @@ -134,8 +134,8 @@ int main(int argc, const char* argv[]) { timeval thisSend; double numMicrosecondsSleep = 0; -// int numIterationsLeftBeforeAudioSend = 0; -// pthread_t injectAudioThread; + int numIterationsLeftBeforeAudioSend = 0; + pthread_t injectAudioThread; int handStateTimer = 0; @@ -156,16 +156,16 @@ int main(int argc, const char* argv[]) { } // temporarily disable Eve's audio sending until the file is actually available on EC2 box -// if (numIterationsLeftBeforeAudioSend == 0) { -// if (!::injectAudioThreadRunning) { -// pthread_create(&injectAudioThread, NULL, injectAudio, (void*) &eveAudioInjector); -// -// numIterationsLeftBeforeAudioSend = randIntInRange(MIN_ITERATIONS_BETWEEN_AUDIO_SENDS, -// MAX_ITERATIONS_BETWEEN_AUDIO_SENDS); -// } -// } else { -// numIterationsLeftBeforeAudioSend--; -// } + if (numIterationsLeftBeforeAudioSend == 0) { + if (!::injectAudioThreadRunning) { + pthread_create(&injectAudioThread, NULL, injectAudio, (void*) &eveAudioInjector); + + numIterationsLeftBeforeAudioSend = randIntInRange(MIN_ITERATIONS_BETWEEN_AUDIO_SENDS, + MAX_ITERATIONS_BETWEEN_AUDIO_SENDS); + } + } else { + numIterationsLeftBeforeAudioSend--; + } // sleep for the correct amount of time to have data send be consistently timed if ((numMicrosecondsSleep = (DATA_SEND_INTERVAL_MSECS * 1000) - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index e4e8dc1614..dbcb79edc7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -30,8 +30,12 @@ include_glm(${TARGET_NAME} ${ROOT_DIR}) # create the InterfaceConfig.h file based on GL_HEADERS above configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h) -# grab the implementation and header files from src dir +# grab the implementation and header files from src dirs file(GLOB INTERFACE_SRCS src/*.cpp src/*.h) +foreach(SUBDIR ui) + file(GLOB SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h) + set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS}) +endforeach(SUBDIR) # project subdirectories add_subdirectory(src/starfield) @@ -73,6 +77,10 @@ include_directories( ${LODEPNG_INCLUDE_DIRS} ) +find_package(Qt4 REQUIRED QtCore QtGui) +include(${QT_USE_FILE}) +target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES}) + if (NOT APPLE) find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 127efe1b2e..b945ebe05e 100644 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -13,14 +13,36 @@ #include #include "Avatar.h" #include "Log.h" +#include "ui/TextRenderer.h" #include #include #include using namespace std; -const bool BALLS_ON = false; +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; //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; + float skinColor[] = {1.0, 0.84, 0.66}; float lightBlue[] = { 0.7, 0.8, 1.0 }; float browColor[] = {210.0/255.0, 105.0/255.0, 30.0/255.0}; @@ -39,7 +61,7 @@ bool usingBigSphereCollisionTest = true; char iris_texture_file[] = "resources/images/green_eye.png"; -float chatMessageScale = 0.00025; +float chatMessageScale = 0.001; float chatMessageHeight = 0.4; vector iris_texture; @@ -50,38 +72,30 @@ Avatar::Avatar(bool isMine) { _orientation.setToIdentity(); - _velocity = glm::vec3( 0.0, 0.0, 0.0 ); - _thrust = glm::vec3( 0.0, 0.0, 0.0 ); - _rotation = glm::quat( 0.0f, 0.0f, 0.0f, 0.0f ); - _bodyYaw = -90.0; - _bodyPitch = 0.0; - _bodyRoll = 0.0; - _bodyYawDelta = 0.0; - _mousePressed = false; - _mode = AVATAR_MODE_STANDING; - _isMine = isMine; - _maxArmLength = 0.0; - _transmitterHz = 0.0; - _transmitterPackets = 0; - _speed = 0.0; - _pelvisStandingHeight = 0.0f; - _displayingHead = true; - _TEST_bigSphereRadius = 0.3f; - _TEST_bigSpherePosition = glm::vec3( 0.0f, _TEST_bigSphereRadius, 2.0f ); + _velocity = glm::vec3( 0.0, 0.0, 0.0 ); + _thrust = glm::vec3( 0.0, 0.0, 0.0 ); + _rotation = glm::quat( 0.0f, 0.0f, 0.0f, 0.0f ); + _bodyYaw = -90.0; + _bodyPitch = 0.0; + _bodyRoll = 0.0; + _bodyYawDelta = 0.0; + _mousePressed = false; + _mode = AVATAR_MODE_STANDING; + _isMine = isMine; + _maxArmLength = 0.0; + _transmitterHz = 0.0; + _transmitterPackets = 0; + _transmitterIsFirstData = true; + _transmitterInitialReading = glm::vec3( 0.f, 0.f, 0.f ); + _speed = 0.0; + _pelvisStandingHeight = 0.0f; + _displayingHead = true; + _TEST_bigSphereRadius = 0.3f; + _TEST_bigSpherePosition = glm::vec3( 0.0f, _TEST_bigSphereRadius, 2.0f ); for (int i = 0; i < MAX_DRIVE_KEYS; i++) _driveKeys[i] = false; - _head.pupilSize = 0.10; - _head.interPupilDistance = 0.6; - _head.interBrowDistance = 0.75; - _head.nominalPupilSize = 0.10; - _head.pitchRate = 0.0; - _head.yawRate = 0.0; - _head.rollRate = 0.0; - _head.eyebrowPitch[0] = -30; - _head.eyebrowPitch[1] = -30; - _head.eyebrowRoll [0] = 20; - _head.eyebrowRoll [1] = -20; +/* _head.mouthPitch = 0; _head.mouthYaw = 0; _head.mouthWidth = 1.0; @@ -107,15 +121,55 @@ Avatar::Avatar(bool isMine) { _head.lastLoudness = 0.0; _head.browAudioLift = 0.0; _head.noise = 0; - - _movedHandOffset = glm::vec3( 0.0, 0.0, 0.0 ); - _usingBodySprings = true; - _renderYaw = 0.0; - _renderPitch = 0.0; - _sphere = NULL; - _interactingOther = NULL; +*/ + + _head.pupilSize = 0.10; + _head.interPupilDistance = 0.6; + _head.interBrowDistance = 0.75; + _head.nominalPupilSize = 0.10; + _head.pitchRate = 0.0; + _head.yawRate = 0.0; + _head.rollRate = 0.0; + _head.eyebrowPitch[0] = -30; + _head.eyebrowPitch[1] = -30; + _head.eyebrowRoll [0] = 20; + _head.eyebrowRoll [1] = -20; + _head.mouthPitch = 0; + _head.mouthYaw = 0; + _head.mouthWidth = 1.0; + _head.mouthHeight = 0.2; + _head.eyeballPitch[0] = 0; + _head.eyeballPitch[1] = 0; + _head.eyeballScaleX = 1.2; + _head.eyeballScaleY = 1.5; + _head.eyeballScaleZ = 1.0; + _head.eyeballYaw[0] = 0; + _head.eyeballYaw[1] = 0; + _head.pitchTarget = 0; + _head.yawTarget = 0; + _head.noiseEnvelope = 1.0; + _head.pupilConverge = 10.0; + _head.leanForward = 0.0; + _head.leanSideways = 0.0; + _head.eyeContact = 1; + _head.eyeContactTarget = LEFT_EYE; + _head.scale = 1.0; + _head.audioAttack = 0.0; + _head.averageLoudness = 0.0; + _head.lastLoudness = 0.0; + _head.browAudioLift = 0.0; + _head.noise = 0; + _head.returnSpringScale = 1.0; + _movedHandOffset = glm::vec3( 0.0, 0.0, 0.0 ); + _usingBodySprings = true; + _renderYaw = 0.0; + _renderPitch = 0.0; + _sphere = NULL; + _interactingOther = NULL; _closeEnoughToHoldHands = false; - _handHoldingPosition = glm::vec3( 0.0, 0.0, 0.0 ); + //_interactingOtherIsNearby = false; + _handHoldingPosition = glm::vec3( 0.0, 0.0, 0.0 ); + initializeSkeleton(); @@ -134,27 +188,32 @@ Avatar::Avatar(bool isMine) { Avatar::Avatar(const Avatar &otherAvatar) { - _velocity = otherAvatar._velocity; - _thrust = otherAvatar._thrust; - _rotation = otherAvatar._rotation; - _closeEnoughToHoldHands = otherAvatar._closeEnoughToHoldHands; - _bodyYaw = otherAvatar._bodyYaw; - _bodyPitch = otherAvatar._bodyPitch; - _bodyRoll = otherAvatar._bodyRoll; - _bodyYawDelta = otherAvatar._bodyYawDelta; - _mousePressed = otherAvatar._mousePressed; - _mode = otherAvatar._mode; - _isMine = otherAvatar._isMine; - _renderYaw = otherAvatar._renderYaw; - _renderPitch = otherAvatar._renderPitch; - _maxArmLength = otherAvatar._maxArmLength; - _transmitterTimer = otherAvatar._transmitterTimer; - _transmitterHz = otherAvatar._transmitterHz; - _transmitterPackets = otherAvatar._transmitterPackets; - _TEST_bigSphereRadius = otherAvatar._TEST_bigSphereRadius; - _TEST_bigSpherePosition = otherAvatar._TEST_bigSpherePosition; - _movedHandOffset = otherAvatar._movedHandOffset; - _usingBodySprings = otherAvatar._usingBodySprings; + _velocity = otherAvatar._velocity; + _thrust = otherAvatar._thrust; + _rotation = otherAvatar._rotation; + _closeEnoughToHoldHands = otherAvatar._closeEnoughToHoldHands; + //_interactingOtherIsNearby = otherAvatar._interactingOtherIsNearby; + _bodyYaw = otherAvatar._bodyYaw; + _bodyPitch = otherAvatar._bodyPitch; + _bodyRoll = otherAvatar._bodyRoll; + _bodyYawDelta = otherAvatar._bodyYawDelta; + _mousePressed = otherAvatar._mousePressed; + _mode = otherAvatar._mode; + _isMine = otherAvatar._isMine; + _renderYaw = otherAvatar._renderYaw; + _renderPitch = otherAvatar._renderPitch; + _maxArmLength = otherAvatar._maxArmLength; + _transmitterTimer = otherAvatar._transmitterTimer; + _transmitterIsFirstData = otherAvatar._transmitterIsFirstData; + _transmitterTimeLastReceived = otherAvatar._transmitterTimeLastReceived; + _transmitterHz = otherAvatar._transmitterHz; + _transmitterInitialReading = otherAvatar._transmitterInitialReading; + _transmitterPackets = otherAvatar._transmitterPackets; + _TEST_bigSphereRadius = otherAvatar._TEST_bigSphereRadius; + _TEST_bigSpherePosition = otherAvatar._TEST_bigSpherePosition; + _movedHandOffset = otherAvatar._movedHandOffset; + _usingBodySprings = otherAvatar._usingBodySprings; + _orientation.set( otherAvatar._orientation ); _sphere = NULL; @@ -237,9 +296,9 @@ void Avatar::UpdateGyros(float frametime, SerialInterface * serialInterface, glm float measured_pitch_rate = serialInterface->getRelativeValue(HEAD_PITCH_RATE); _head.yawRate = serialInterface->getRelativeValue(HEAD_YAW_RATE); float measured_lateral_accel = serialInterface->getRelativeValue(ACCEL_X) - - ROLL_ACCEL_COUPLING*serialInterface->getRelativeValue(HEAD_ROLL_RATE); + ROLL_ACCEL_COUPLING * serialInterface->getRelativeValue(HEAD_ROLL_RATE); float measured_fwd_accel = serialInterface->getRelativeValue(ACCEL_Z) - - PITCH_ACCEL_COUPLING*serialInterface->getRelativeValue(HEAD_PITCH_RATE); + PITCH_ACCEL_COUPLING * serialInterface->getRelativeValue(HEAD_PITCH_RATE); float measured_roll_rate = serialInterface->getRelativeValue(HEAD_ROLL_RATE); //printLog("Pitch Rate: %d ACCEL_Z: %d\n", serialInterface->getRelativeValue(PITCH_RATE), @@ -445,19 +504,23 @@ void Avatar::simulate(float deltaTime) { } void Avatar::updateHead(float deltaTime) { - if (!_head.noise) { + + // Decay head back to center if turned on + if (_returnHeadToCenter) { // Decay back toward center - _headPitch *= (1.0f - DECAY * 2 * deltaTime); - _headYaw *= (1.0f - DECAY * 2 * deltaTime); - _headRoll *= (1.0f - DECAY * 2 * deltaTime); + _headPitch *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); + _headYaw *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); + _headRoll *= (1.0f - DECAY * _head.returnSpringScale * 2 * deltaTime); } - else { + + if (_head.noise) { // Move toward new target _headPitch += (_head.pitchTarget - _headPitch) * 10 * deltaTime; // (1.f - DECAY*deltaTime)*Pitch + ; _headYaw += (_head.yawTarget - _headYaw ) * 10 * deltaTime; // (1.f - DECAY*deltaTime); _headRoll *= 1.f - (DECAY * deltaTime); } + _head.leanForward *= (1.f - DECAY * 30 * deltaTime); _head.leanSideways *= (1.f - DECAY * 30 * deltaTime); @@ -651,6 +714,11 @@ void Avatar::setDisplayingHead( bool displayingHead ) { } +static TextRenderer* textRenderer() { + static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24); + return renderer; +} + void Avatar::render(bool lookingInMirror) { /* @@ -697,10 +765,10 @@ void Avatar::render(bool lookingInMirror) { } if (!_chatMessage.empty()) { - float width = 0; - float lastWidth; + int width = 0; + int lastWidth; for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) { - width += (lastWidth = glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*chatMessageScale); + width += (lastWidth = textRenderer()->computeWidth(*it)); } glPushMatrix(); @@ -712,11 +780,14 @@ void Avatar::render(bool lookingInMirror) { glTranslatef(_position.x, _position.y + chatMessageHeight, _position.z); glRotatef(atan2(-modelview[2], -modelview[10]) * 180 / PI, 0, 1, 0); - glTranslatef(width * 0.5, 0, 0); + glColor3f(0, 0.8, 0); + glRotatef(180, 0, 0, 1); + glScalef(chatMessageScale, chatMessageScale, 1.0f); + glDisable(GL_LIGHTING); if (_keyState == NO_KEY_DOWN) { - drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0); + textRenderer()->draw(-width/2, 0, _chatMessage.c_str()); } else { // rather than using substr and allocating a new string, just replace the last @@ -724,11 +795,10 @@ void Avatar::render(bool lookingInMirror) { int lastIndex = _chatMessage.size() - 1; char lastChar = _chatMessage[lastIndex]; _chatMessage[lastIndex] = '\0'; - drawtext(0, 0, chatMessageScale, 180, 1.0, 0, _chatMessage.c_str(), 0, 1, 0); + textRenderer()->draw(-width/2, 0, _chatMessage.c_str()); _chatMessage[lastIndex] = lastChar; - glTranslatef(lastWidth - width, 0, 0); - drawtext(0, 0, chatMessageScale, 180, 3.0, - 0, _chatMessage.c_str() + lastIndex, 0, 1, 0); + glColor3f(0, 1, 0); + textRenderer()->draw(width/2 - lastWidth, 0, _chatMessage.c_str() + lastIndex); } glEnable(GL_LIGHTING); @@ -1264,14 +1334,21 @@ void Avatar::SetNewHeadTarget(float pitch, float yaw) { _head.yawTarget = yaw; } -// getting data from Android transmitte app +// Process UDP interface data from Android transmitter or Google Glass void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { // Read a packet from a transmitter app, process the data - float accX, accY, accZ, - graX, graY, graZ, - gyrX, gyrY, gyrZ, - linX, linY, linZ, - rot1, rot2, rot3, rot4; + float + accX, accY, accZ, // Measured acceleration + graX, graY, graZ, // Gravity + gyrX, gyrY, gyrZ, // Gyro velocity in radians/sec as (pitch, roll, yaw) + linX, linY, linZ, // Linear Acceleration (less gravity) + rot1, rot2, rot3, rot4; // Rotation of device: + // rot1 = roll, 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 + + const bool IS_GLASS = false; // Whether to assume this is a Google glass transmitting + sscanf((char *)packetData, "tacc %f %f %f gra %f %f %f gyr %f %f %f lin %f %f %f rot %f %f %f %f", &accX, &accY, &accZ, &graX, &graY, &graZ, @@ -1280,7 +1357,21 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { &rot1, &rot2, &rot3, &rot4); if (_transmitterPackets++ == 0) { + // If first packet received, note time, turn head spring return OFF, get start rotation gettimeofday(&_transmitterTimer, NULL); + if (IS_GLASS) { + setHeadReturnToCenter(true); + setHeadSpringScale(10.f); + printLog("Using Google Glass to drive head, springs ON.\n"); + + } else { + setHeadReturnToCenter(false); + printLog("Using Transmitter to drive head, springs OFF.\n"); + + } + _transmitterInitialReading = glm::vec3( rot3, + rot2, + rot1 ); } const int TRANSMITTER_COUNT = 100; if (_transmitterPackets % TRANSMITTER_COUNT == 0) { @@ -1288,27 +1379,71 @@ void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) { timeval now; gettimeofday(&now, NULL); double msecsElapsed = diffclock(&_transmitterTimer, &now); - _transmitterHz = static_cast( (double)TRANSMITTER_COUNT/(msecsElapsed/1000.0) ); + _transmitterHz = static_cast( (double)TRANSMITTER_COUNT / (msecsElapsed / 1000.0) ); _transmitterTimer = now; + printLog("Transmitter Hz: %3.1f\n", _transmitterHz); + } + //printLog("Gyr: %3.1f, %3.1f, %3.1f\n", glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY)); + //printLog("Rot: %3.1f, %3.1f, %3.1f, %3.1f\n", rot1, rot2, rot3, rot4); + + // Update the head with the transmitter data + glm::vec3 eulerAngles((rot3 - _transmitterInitialReading.x) * 180.f, + -(rot2 - _transmitterInitialReading.y) * 180.f, + (rot1 - _transmitterInitialReading.z) * 180.f); + if (eulerAngles.x > 180.f) { eulerAngles.x -= 360.f; } + if (eulerAngles.x < -180.f) { eulerAngles.x += 360.f; } + + glm::vec3 angularVelocity; + if (!IS_GLASS) { + angularVelocity = glm::vec3(glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY)); + setHeadFromGyros( &eulerAngles, &angularVelocity, + (_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1.0); + + } else { + angularVelocity = glm::vec3(glm::degrees(gyrY), glm::degrees(-gyrX), glm::degrees(-gyrZ)); + setHeadFromGyros( &eulerAngles, &angularVelocity, + (_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1000.0); + + } + +} + +void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity, float deltaTime, float smoothingTime) { + // + // Given absolute position and angular velocity information, update the avatar's head angles + // with the goal of fast instantaneous updates that gradually follow the absolute data. + // + // Euler Angle format is (Yaw, Pitch, Roll) in degrees + // + // Angular Velocity is (Yaw, Pitch, Roll) in degrees per second + // + // SMOOTHING_TIME is the time is seconds over which the head should average to the + // absolute eulerAngles passed. + // + // + float const MAX_YAW = 90.f; + float const MIN_YAW = -90.f; + float const MAX_PITCH = 85.f; + float const MIN_PITCH = -85.f; + float const MAX_ROLL = 90.f; + float const MIN_ROLL = -90.f; + + if (deltaTime == 0.f) { + // On first sample, set head to absolute position + setHeadYaw(eulerAngles->x); + setHeadPitch(eulerAngles->y); + setHeadRoll(eulerAngles->z); + } else { + glm::vec3 angles(getHeadYaw(), getHeadPitch(), getHeadRoll()); + // Increment by detected velocity + angles += (*angularVelocity) * deltaTime; + // Smooth to slowly follow absolute values + angles = ((1.f - deltaTime / smoothingTime) * angles) + (deltaTime / smoothingTime) * (*eulerAngles); + setHeadYaw(fmin(fmax(angles.x, MIN_YAW), MAX_YAW)); + setHeadPitch(fmin(fmax(angles.y, MIN_PITCH), MAX_PITCH)); + setHeadRoll(fmin(fmax(angles.z, MIN_ROLL), MAX_ROLL)); + //printLog("Y/P/R: %3.1f, %3.1f, %3.1f\n", angles.x, angles.y, angles.z); } - /* NOTE: PR: Will add back in when ready to animate avatar hand - - // Add rotational forces to the hand - const float ANG_VEL_SENSITIVITY = 4.0; - const float ANG_VEL_THRESHOLD = 0.0; - float angVelScale = ANG_VEL_SENSITIVITY*(1.0f/getTransmitterHz()); - - addAngularVelocity(fabs(gyrX*angVelScale)>ANG_VEL_THRESHOLD?gyrX*angVelScale:0, - fabs(gyrZ*angVelScale)>ANG_VEL_THRESHOLD?gyrZ*angVelScale:0, - fabs(-gyrY*angVelScale)>ANG_VEL_THRESHOLD?-gyrY*angVelScale:0); - - // Add linear forces to the hand - //const float LINEAR_VEL_SENSITIVITY = 50.0; - const float LINEAR_VEL_SENSITIVITY = 5.0; - float linVelScale = LINEAR_VEL_SENSITIVITY*(1.0f/getTransmitterHz()); - glm::vec3 linVel(linX*linVelScale, linZ*linVelScale, -linY*linVelScale); - addVelocity(linVel); - */ } // Find and return the gravity vector at my location diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index 4b0ca1042d..91951795f8 100644 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -160,6 +160,10 @@ private: 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) + void setHeadReturnToCenter(bool r) { _returnHeadToCenter = r; }; + const bool getHeadReturnToCenter() const { return _returnHeadToCenter; }; + struct AvatarBone { AvatarBoneID parent; // which bone is this bone connected to? @@ -214,39 +218,48 @@ private: float lastLoudness; float averageLoudness; float audioAttack; + + // Strength of return springs + float returnSpringScale; }; - AvatarHead _head; - bool _isMine; - glm::vec3 _TEST_bigSpherePosition; - float _TEST_bigSphereRadius; - bool _mousePressed; - float _bodyYawDelta; - bool _usingBodySprings; - glm::vec3 _movedHandOffset; - glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion - AvatarBone _bone[ NUM_AVATAR_BONES ]; - AvatarMode _mode; - glm::vec3 _handHoldingPosition; - glm::vec3 _velocity; - glm::vec3 _thrust; - float _speed; - float _maxArmLength; - Orientation _orientation; - int _driveKeys[MAX_DRIVE_KEYS]; - GLUquadric* _sphere; - float _renderYaw; - float _renderPitch; // Pitch from view frustum when this is own head - timeval _transmitterTimer; - float _transmitterHz; - int _transmitterPackets; - Avatar* _interactingOther; - bool _closeEnoughToHoldHands; - float _pelvisStandingHeight; - float _height; - Balls* _balls; - AvatarTouch _avatarTouch; - bool _displayingHead; // should be false if in first-person view + AvatarHead _head; + bool _isMine; + glm::vec3 _TEST_bigSpherePosition; + float _TEST_bigSphereRadius; + bool _mousePressed; + float _bodyYawDelta; + bool _usingBodySprings; + glm::vec3 _movedHandOffset; + glm::quat _rotation; // the rotation of the avatar body as a whole expressed as a quaternion + AvatarBone _bone[ NUM_AVATAR_BONES ]; + AvatarMode _mode; + glm::vec3 _handHoldingPosition; + glm::vec3 _velocity; + glm::vec3 _thrust; + float _speed; + float _maxArmLength; + Orientation _orientation; + int _driveKeys[MAX_DRIVE_KEYS]; + GLUquadric* _sphere; + float _renderYaw; + float _renderPitch; // Pitch from view frustum when this is own head + bool _transmitterIsFirstData; + timeval _transmitterTimeLastReceived; + timeval _transmitterTimer; + float _transmitterHz; + int _transmitterPackets; + glm::vec3 _transmitterInitialReading; + Avatar* _interactingOther; + bool _closeEnoughToHoldHands; + //bool _interactingOtherIsNearby; + float _pelvisStandingHeight; + float _height; + Balls* _balls; + AvatarTouch _avatarTouch; + bool _displayingHead; // should be false if in first-person view + bool _returnHeadToCenter; + // private methods... void initializeSkeleton(); @@ -258,6 +271,8 @@ private: 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, float smoothingTime); + void setHeadSpringScale(float s) { _head.returnSpringScale = s; } }; #endif diff --git a/interface/src/AvatarTouch.cpp b/interface/src/AvatarTouch.cpp index d90077a2d8..bf694afdac 100644 --- a/interface/src/AvatarTouch.cpp +++ b/interface/src/AvatarTouch.cpp @@ -11,6 +11,8 @@ #include "AvatarTouch.h" #include "InterfaceConfig.h" +const float THREAD_RADIUS = 0.007; + AvatarTouch::AvatarTouch() { _myHandPosition = glm::vec3( 0.0f, 0.0f, 0.0f ); diff --git a/interface/src/AvatarTouch.h b/interface/src/AvatarTouch.h index 80c95c3bc1..64874df3b9 100644 --- a/interface/src/AvatarTouch.h +++ b/interface/src/AvatarTouch.h @@ -21,8 +21,6 @@ public: void setYourHandState ( int state ); void simulate (float deltaTime); void render(); - - const float THREAD_RADIUS = 0.007; private: diff --git a/interface/src/Log.cpp b/interface/src/Log.cpp index 454a64a0fb..5fcfb51e25 100644 --- a/interface/src/Log.cpp +++ b/interface/src/Log.cpp @@ -14,6 +14,7 @@ #include #include "Util.h" +#include "ui/TextRenderer.h" namespace { // anonymous namespace - everything in here only exists within this very .cpp file @@ -23,6 +24,8 @@ namespace { unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message + const char* FONT_FAMILY = SANS_FONT_FAMILY; + bool const TEXT_MONOSPACED = true; float const TEXT_RED = 0.7f; @@ -194,6 +197,11 @@ void Log::setCharacterSize(unsigned width, unsigned height) { pthread_mutex_unlock(& _mtx); } +static TextRenderer* textRenderer() { + static TextRenderer* renderer = new TextRenderer(FONT_FAMILY); + return renderer; +} + void Log::render(unsigned screenWidth, unsigned screenHeight) { // rendering might take some time, so create a local copy of the portion we need @@ -261,10 +269,8 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) { } // get values for rendering - float scaleFactor = _valCharScale; - int yStart = int((screenHeight - _valCharYoffset) / _valCharAspect); - int yStep = int(_valCharHeight / _valCharAspect); - float yScale = _valCharAspect; + int yStep = textRenderer()->metrics().lineSpacing(); + int yStart = screenHeight - textRenderer()->metrics().descent(); // render text char** line = _ptrLinesEnd + showLines; @@ -273,11 +279,6 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) { pthread_mutex_unlock(& _mtx); // ok, we got all we need - GLint matrixMode; - glGetIntegerv(GL_MATRIX_MODE, & matrixMode); - glPushMatrix(); - glScalef(1.0f, yScale, 1.0f); - for (int y = yStart; y > 0; y -= yStep) { // debug mode: check line pointer is valid @@ -299,14 +300,11 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) { assert(! (chars < _ptrCharsEnd || chars >= _ptrCharsEnd + (_ptrCharsEnd - _arrChars))); // render the string - drawtext(x, y, scaleFactor, 0.0f, 1.0f, int(TEXT_MONOSPACED), - chars, TEXT_RED, TEXT_GREEN, TEXT_BLUE); + glColor3f(TEXT_RED, TEXT_GREEN, TEXT_BLUE); + textRenderer()->draw(x, y, chars); //fprintf(stderr, "Log::render, message = \"%s\"\n", chars); } - - glPopMatrix(); - glMatrixMode(matrixMode); } Log logger; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 55b55b12eb..ef51f9b72a 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -15,6 +15,7 @@ #include #include "Log.h" +#include "ui/TextRenderer.h" #include "world.h" #include "Util.h" @@ -25,7 +26,6 @@ using namespace std; // see http://www.opengl.org/resources/libraries/glut/spec3/node78.html static float MONO_STROKE_WIDTH_GLUT = 104.76; - void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * front, glm::vec3 * right, glm::vec3 * up) { // // Converts from three euler angles to the associated orthonormal vectors @@ -152,19 +152,18 @@ double diffclock(timeval *clock1,timeval *clock2) return diffms; } +static TextRenderer* textRenderer(int mono) { + static TextRenderer* monoRenderer = new TextRenderer(MONO_FONT_FAMILY); + static TextRenderer* proportionalRenderer = new TextRenderer(SANS_FONT_FAMILY); + return mono ? monoRenderer : proportionalRenderer; +} + int widthText(float scale, int mono, char const* string) { - int width = 0; - if (!mono) { - width = scale * glutStrokeLength(GLUT_STROKE_ROMAN, (const unsigned char *) string); - } else { -#ifndef WORKAROUND_BROKEN_GLUT_STROKES - width = scale * glutStrokeLength(GLUT_STROKE_MONO_ROMAN, (const unsigned char *) string); -#else - // return value is unreliable, so just calculate it - width = scale * float(strlen(string)) * MONO_STROKE_WIDTH_GLUT; -#endif - } - return width; + return textRenderer(mono)->computeWidth(string) * (scale / 0.10); +} + +float widthChar(float scale, int mono, char ch) { + return textRenderer(mono)->computeWidth(ch) * (scale / 0.10); } void drawtext(int x, int y, float scale, float rotate, float thick, int mono, @@ -177,35 +176,12 @@ void drawtext(int x, int y, float scale, float rotate, float thick, int mono, glPushMatrix(); glTranslatef( static_cast(x), static_cast(y), 0.0f); glColor3f(r,g,b); - glRotated(180+rotate,0,0,1); - glRotated(180,0,1,0); - glLineWidth(thick); - glScalef(scale, scale, 1.0); - len = (int) strlen(string); - for (i = 0; i < len; i++) - { - if (!mono) { - glutStrokeCharacter(GLUT_STROKE_ROMAN, int(string[i])); - } else { -#ifdef WORKAROUND_BROKEN_GLUT_STROKES - if (string[i] != 'm') { -#endif - glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(string[i])); -#ifdef WORKAROUND_BROKEN_GLUT_STROKES - } else { - // some glut implementations have a broken 'm'... - unsigned char tmpStr[2]; - tmpStr[0] = string[i]; - tmpStr[1] = '\0'; - float scale = MONO_STROKE_WIDTH_GLUT / glutStrokeLength(GLUT_STROKE_ROMAN, tmpStr); - glScalef(scale, 1.0f, 1.0f); - glutStrokeCharacter(GLUT_STROKE_ROMAN, int(string[i])); - // staying humble on the stack - might be in projection mode - glScalef(1.0f / scale, 1.0f, 1.0f); - } -#endif - } - } + glRotated(rotate,0,0,1); + // glLineWidth(thick); + glScalef(scale / 0.10, scale / 0.10, 1.0); + + textRenderer(mono)->draw(0, 0, string); + glPopMatrix(); } diff --git a/interface/src/Util.h b/interface/src/Util.h index b23885e6bc..02b3ae8152 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -19,6 +19,13 @@ #include +// the standard sans serif font family +#define SANS_FONT_FAMILY "Helvetica" + +// the standard mono font family +#define MONO_FONT_FAMILY "Courier" + + void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos); @@ -28,6 +35,7 @@ float randFloat(); void render_world_box(); void render_vector(glm::vec3 * vec); int widthText(float scale, int mono, char const* string); +float widthChar(float scale, int mono, char ch); void drawtext(int x, int y, float scale, float rotate, float thick, int mono, char const* string, float r=1.0, float g=1.0, float b=1.0); void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index e96dc5ef8b..24f7665e50 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -18,15 +18,10 @@ #include #include #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(); } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index ca4825121b..f12cc7521a 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -16,6 +16,7 @@ #include #include #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; diff --git a/interface/src/main.cpp b/interface/src/main.cpp index ff16d98251..322446829a 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -39,6 +39,8 @@ #include #endif +#include + #include #include @@ -59,11 +61,13 @@ #include "AngleUtil.h" #include "Stars.h" -#include "MenuRow.h" -#include "MenuColumn.h" -#include "Menu.h" +#include "ui/ChatEntry.h" +#include "ui/MenuRow.h" +#include "ui/MenuColumn.h" +#include "ui/Menu.h" +#include "ui/TextRenderer.h" + #include "Camera.h" -#include "ChatEntry.h" #include "Avatar.h" #include "Texture.h" #include @@ -86,6 +90,8 @@ using namespace std; void reshape(int width, int height); // will be defined below void loadViewFrustum(ViewFrustum& viewFrustum); // will be defined below +QApplication* app; + bool enableNetworkThread = true; pthread_t networkReceiveThread; bool stopNetworkReceiveThread = false; @@ -299,6 +305,7 @@ void init(void) { voxels.init(); voxels.setViewerAvatar(&myAvatar); + voxels.setCamera(&myCamera); handControl.setScreenDimensions(WIDTH, HEIGHT); @@ -1318,7 +1325,7 @@ void key(unsigned char k, int x, int y) if (chatEntry.key(k)) { myAvatar.setKeyState(k == '\b' || k == 127 ? // backspace or delete DELETE_KEY_DOWN : INSERT_KEY_DOWN); - myAvatar.setChatMessage(string(chatEntry.getContents().size(), 'X')); + myAvatar.setChatMessage(string(chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); } else { myAvatar.setChatMessage(chatEntry.getContents()); @@ -1411,6 +1418,7 @@ void* networkReceive(void* args) switch (incomingPacket[0]) { case PACKET_HEADER_TRANSMITTER_DATA: + // Process UDP packets that are sent to the client from local sensor devices myAvatar.processTransmitterData(incomingPacket, bytesReceived); break; case PACKET_HEADER_VOXEL_DATA: @@ -1605,12 +1613,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) { @@ -1663,7 +1665,10 @@ int main(int argc, const char * argv[]) #ifdef _WIN32 glewInit(); #endif - + + // we need to create a QApplication instance in order to use Qt's font rendering + app = new QApplication(argc, const_cast(argv)); + // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); diff --git a/interface/src/ChatEntry.cpp b/interface/src/ui/ChatEntry.cpp similarity index 95% rename from interface/src/ChatEntry.cpp rename to interface/src/ui/ChatEntry.cpp index aca13a79ac..2b6144e76d 100644 --- a/interface/src/ChatEntry.cpp +++ b/interface/src/ui/ChatEntry.cpp @@ -14,7 +14,7 @@ using namespace std; const int MAX_CONTENT_LENGTH = 140; -void ChatEntry::clear () { +void ChatEntry::clear() { _contents.clear(); _cursorPos = 0; } @@ -67,7 +67,7 @@ void ChatEntry::render(int screenWidth, int screenHeight) { float width = 0; for (string::iterator it = _contents.begin(), end = it + _cursorPos; it != end; it++) { - width += glutStrokeWidth(GLUT_STROKE_ROMAN, *it)*0.10; + width += widthChar(0.10, 0, *it); } glDisable(GL_LINE_SMOOTH); glBegin(GL_LINE_STRIP); diff --git a/interface/src/ChatEntry.h b/interface/src/ui/ChatEntry.h similarity index 85% rename from interface/src/ChatEntry.h rename to interface/src/ui/ChatEntry.h index db92822158..c2f1254c41 100644 --- a/interface/src/ChatEntry.h +++ b/interface/src/ui/ChatEntry.h @@ -14,9 +14,9 @@ class ChatEntry { public: - const std::string& getContents () const { return _contents; } + const std::string& getContents() const { return _contents; } - void clear (); + void clear(); bool key(unsigned char k); void specialKey(unsigned char k); diff --git a/interface/src/Menu.cpp b/interface/src/ui/Menu.cpp similarity index 100% rename from interface/src/Menu.cpp rename to interface/src/ui/Menu.cpp diff --git a/interface/src/Menu.h b/interface/src/ui/Menu.h similarity index 100% rename from interface/src/Menu.h rename to interface/src/ui/Menu.h diff --git a/interface/src/MenuColumn.cpp b/interface/src/ui/MenuColumn.cpp similarity index 94% rename from interface/src/MenuColumn.cpp rename to interface/src/ui/MenuColumn.cpp index 94e8595c8b..a70e261c52 100644 --- a/interface/src/MenuColumn.cpp +++ b/interface/src/ui/MenuColumn.cpp @@ -16,6 +16,7 @@ #include "MenuColumn.h" #include "Menu.h" +#include "ui/TextRenderer.h" MenuColumn::MenuColumn() { } @@ -137,9 +138,12 @@ int MenuColumn::getMaxRowWidth() { return maxColumnWidth; } +static TextRenderer* textRenderer() { + static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 11); + return renderer; +} + void MenuColumn::render(int yOffset, int menuHeight, int lineHeight) { - float scale = 0.09; - int mono = 0; int numberOfRows = rows.size(); if (numberOfRows > 0) { @@ -158,7 +162,8 @@ void MenuColumn::render(int yOffset, int menuHeight, int lineHeight) { char* rowName; for (unsigned int i = 0; i < rows.size(); ++i) { rowName = rows[i].getName(); - drawtext(leftPosition + SPACE_BEFORE_ROW_NAME, y+5 + yOffset, scale, 0, 1.0, mono, rowName, 0, 0, 0); + glColor3f(0, 0, 0); + textRenderer()->draw(leftPosition + SPACE_BEFORE_ROW_NAME, y + 5 + yOffset, rowName); y += lineHeight; } renderMouseOver(yOffset); diff --git a/interface/src/MenuColumn.h b/interface/src/ui/MenuColumn.h similarity index 100% rename from interface/src/MenuColumn.h rename to interface/src/ui/MenuColumn.h diff --git a/interface/src/MenuRow.cpp b/interface/src/ui/MenuRow.cpp similarity index 100% rename from interface/src/MenuRow.cpp rename to interface/src/ui/MenuRow.cpp diff --git a/interface/src/MenuRow.h b/interface/src/ui/MenuRow.h similarity index 100% rename from interface/src/MenuRow.h rename to interface/src/ui/MenuRow.h diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp new file mode 100644 index 0000000000..3130463398 --- /dev/null +++ b/interface/src/ui/TextRenderer.cpp @@ -0,0 +1,142 @@ +// +// TextRenderer.cpp +// interface +// +// Created by Andrzej Kapolka on 4/24/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. + +#include +#include +#include + +#include "InterfaceConfig.h" +#include "TextRenderer.h" + +// the width/height of the cached glyph textures +const int IMAGE_SIZE = 256; + +Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : + _textureID(textureID), _location(location), _bounds(bounds), _width(width) { +} + +TextRenderer::TextRenderer(const char* family, int pointSize, int weight, bool italic) + : _font(family, pointSize, weight, italic), + _metrics(_font), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) { + _font.setKerning(false); +} + +TextRenderer::~TextRenderer() { + glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); +} + +void TextRenderer::draw(int x, int y, const char* str) { + + glEnable(GL_TEXTURE_2D); + + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + x += glyph.width(); + continue; + } + + glBindTexture(GL_TEXTURE_2D, glyph.textureID()); + + int left = x + glyph.bounds().x(); + int right = x + glyph.bounds().x() + glyph.bounds().width(); + int bottom = y + glyph.bounds().y(); + int top = y + glyph.bounds().y() + glyph.bounds().height(); + + float scale = 1.0 / IMAGE_SIZE; + float ls = glyph.location().x() * scale; + float rs = (glyph.location().x() + glyph.bounds().width()) * scale; + float bt = glyph.location().y() * scale; + float tt = (glyph.location().y() + glyph.bounds().height()) * scale; + + glBegin(GL_QUADS); + glTexCoord2f(ls, bt); + glVertex2f(left, bottom); + glTexCoord2f(rs, bt); + glVertex2f(right, bottom); + glTexCoord2f(rs, tt); + glVertex2f(right, top); + glTexCoord2f(ls, tt); + glVertex2f(left, top); + glEnd(); + + x += glyph.width(); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); +} + +int TextRenderer::computeWidth(char ch) +{ + return getGlyph(ch).width(); +} + +int TextRenderer::computeWidth(const char* str) +{ + int width = 0; + for (const char* ch = str; *ch != 0; ch++) { + width += computeWidth(*ch); + } + return width; +} + +const Glyph& TextRenderer::getGlyph(char c) { + Glyph& glyph = _glyphs[c]; + if (glyph.isValid()) { + return glyph; + } + // we use 'J' as a representative size for the solid block character + QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); + QRect bounds = _metrics.boundingRect(ch); + if (bounds.isEmpty()) { + glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); + return glyph; + } + // grow the bounds to account for antialiasing + bounds.adjust(-1, -1, 1, 1); + + if (_x + bounds.width() > IMAGE_SIZE) { + // we can't fit it on the current row; move to next + _y += _rowHeight; + _x = _rowHeight = 0; + } + if (_y + bounds.height() > IMAGE_SIZE) { + // can't fit it on current texture; make a new one + glGenTextures(1, &_currentTextureID); + _x = _y = _rowHeight = 0; + + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _allTextureIDs.append(_currentTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + } + // render the glyph into an image and copy it into the texture + QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); + if (c == SOLID_BLOCK_CHAR) { + image.fill(QColor(255, 255, 255)); + + } else { + image.fill(0); + QPainter painter(&image); + painter.setFont(_font); + painter.setPen(QColor(255, 255, 255)); + painter.drawText(-bounds.x(), -bounds.y(), ch); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + + glyph = Glyph(_currentTextureID, QPoint(_x, _y), bounds, _metrics.width(ch)); + _x += bounds.width(); + _rowHeight = qMax(_rowHeight, bounds.height()); + + glBindTexture(GL_TEXTURE_2D, 0); + return glyph; +} diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h new file mode 100644 index 0000000000..6de0c77bad --- /dev/null +++ b/interface/src/ui/TextRenderer.h @@ -0,0 +1,89 @@ +// +// TextRenderer.h +// interface +// +// Created by Andrzej Kapolka on 4/26/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__TextRenderer__ +#define __interface__TextRenderer__ + +#include +#include +#include +#include +#include + +// a special "character" that renders as a solid block +const char SOLID_BLOCK_CHAR = 127; + +class Glyph; + +class TextRenderer { +public: + + TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false); + ~TextRenderer(); + + const QFontMetrics& metrics() const { return _metrics; } + + void draw(int x, int y, const char* str); + + int computeWidth(char ch); + int computeWidth(const char* str); + +private: + + const Glyph& getGlyph (char c); + + // the font to render + QFont _font; + + // the font metrics + QFontMetrics _metrics; + + // maps characters to cached glyph info + QHash _glyphs; + + // the id of the glyph texture to which we're currently writing + GLuint _currentTextureID; + + // the position within the current glyph texture + int _x, _y; + + // the height of the current row of characters + int _rowHeight; + + // the list of all texture ids for which we're responsible + QVector _allTextureIDs; +}; + +class Glyph { +public: + + Glyph(int textureID = 0, const QPoint& location = QPoint(), const QRect& bounds = QRect(), int width = 0); + + GLuint textureID() const { return _textureID; } + const QPoint& location () const { return _location; } + const QRect& bounds() const { return _bounds; } + int width () const { return _width; } + + bool isValid() { return _width != 0; } + +private: + + // the id of the OpenGL texture containing the glyph + GLuint _textureID; + + // the location of the character within the texture + QPoint _location; + + // the bounds of the character + QRect _bounds; + + // the width of the character (distance to next, as opposed to bounds width) + int _width; +}; + +#endif /* defined(__interface__TextRenderer__) */ diff --git a/libraries/shared/src/AgentList.cpp b/libraries/shared/src/AgentList.cpp index b05c4558d3..ce312d5eb2 100644 --- a/libraries/shared/src/AgentList.cpp +++ b/libraries/shared/src/AgentList.cpp @@ -259,7 +259,9 @@ bool AgentList::addOrUpdateAgent(sockaddr *publicSocket, sockaddr *localSocket, // to use the local socket information the domain server gave us sockaddr_in *publicSocketIn = (sockaddr_in *)publicSocket; audioMixerSocketUpdate(publicSocketIn->sin_addr.s_addr, publicSocketIn->sin_port); - } else if (newAgent->getType() == AGENT_TYPE_VOXEL) { + } else if (newAgent->getType() == AGENT_TYPE_VOXEL || newAgent->getType() == AGENT_TYPE_AVATAR_MIXER) { + // this is currently the cheat we use to talk directly to our test servers on EC2 + // to be removed when we have a proper identification strategy newAgent->activatePublicSocket(); } diff --git a/libraries/shared/src/AudioRingBuffer.cpp b/libraries/shared/src/AudioRingBuffer.cpp index 5180e238cb..bee34c88eb 100644 --- a/libraries/shared/src/AudioRingBuffer.cpp +++ b/libraries/shared/src/AudioRingBuffer.cpp @@ -14,7 +14,7 @@ AudioRingBuffer::AudioRingBuffer(int ringSamples, int bufferSamples) { bufferLengthSamples = bufferSamples; started = false; - addedToMix = false; + _shouldBeAddedToMix = false; endOfLastWrite = NULL; @@ -26,7 +26,7 @@ AudioRingBuffer::AudioRingBuffer(const AudioRingBuffer &otherRingBuffer) { ringBufferLengthSamples = otherRingBuffer.ringBufferLengthSamples; bufferLengthSamples = otherRingBuffer.bufferLengthSamples; started = otherRingBuffer.started; - addedToMix = otherRingBuffer.addedToMix; + _shouldBeAddedToMix = otherRingBuffer._shouldBeAddedToMix; buffer = new int16_t[ringBufferLengthSamples]; memcpy(buffer, otherRingBuffer.buffer, sizeof(int16_t) * ringBufferLengthSamples); @@ -71,14 +71,6 @@ void AudioRingBuffer::setStarted(bool status) { started = status; } -bool AudioRingBuffer::wasAddedToMix() { - return addedToMix; -} - -void AudioRingBuffer::setAddedToMix(bool added) { - addedToMix = added; -} - float* AudioRingBuffer::getPosition() { return position; } @@ -136,8 +128,6 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { endOfLastWrite += bufferLengthSamples; - addedToMix = false; - if (endOfLastWrite >= buffer + ringBufferLengthSamples) { endOfLastWrite = buffer; } diff --git a/libraries/shared/src/AudioRingBuffer.h b/libraries/shared/src/AudioRingBuffer.h index 48620aa133..09defccb42 100644 --- a/libraries/shared/src/AudioRingBuffer.h +++ b/libraries/shared/src/AudioRingBuffer.h @@ -13,42 +13,42 @@ #include "AgentData.h" class AudioRingBuffer : public AgentData { - public: - AudioRingBuffer(int ringSamples, int bufferSamples); - ~AudioRingBuffer(); - AudioRingBuffer(const AudioRingBuffer &otherRingBuffer); - - int parseData(unsigned char* sourceBuffer, int numBytes); - AudioRingBuffer* clone() const; +public: + AudioRingBuffer(int ringSamples, int bufferSamples); + ~AudioRingBuffer(); + AudioRingBuffer(const AudioRingBuffer &otherRingBuffer); - int16_t* getNextOutput(); - void setNextOutput(int16_t *newPointer); - int16_t* getEndOfLastWrite(); - void setEndOfLastWrite(int16_t *newPointer); - int16_t* getBuffer(); - bool isStarted(); - void setStarted(bool status); - bool wasAddedToMix(); - void setAddedToMix(bool added); - float* getPosition(); - void setPosition(float newPosition[]); - float getAttenuationRatio(); - void setAttenuationRatio(float newAttenuation); - float getBearing(); - void setBearing(float newBearing); - - short diffLastWriteNextOutput(); - private: - int ringBufferLengthSamples; - int bufferLengthSamples; - float position[3]; - float attenuationRatio; - float bearing; - int16_t *nextOutput; - int16_t *endOfLastWrite; - int16_t *buffer; - bool started; - bool addedToMix; + int parseData(unsigned char* sourceBuffer, int numBytes); + AudioRingBuffer* clone() const; + + int16_t* getNextOutput(); + void setNextOutput(int16_t *newPointer); + int16_t* getEndOfLastWrite(); + void setEndOfLastWrite(int16_t *newPointer); + int16_t* getBuffer(); + bool isStarted(); + void setStarted(bool status); + bool shouldBeAddedToMix() const { return _shouldBeAddedToMix; } + void setShouldBeAddedToMix(bool shouldBeAddedToMix) { _shouldBeAddedToMix = shouldBeAddedToMix; } + float* getPosition(); + void setPosition(float newPosition[]); + float getAttenuationRatio(); + void setAttenuationRatio(float newAttenuation); + float getBearing(); + void setBearing(float newBearing); + + short diffLastWriteNextOutput(); +private: + int ringBufferLengthSamples; + int bufferLengthSamples; + float position[3]; + float attenuationRatio; + float bearing; + int16_t *nextOutput; + int16_t *endOfLastWrite; + int16_t *buffer; + bool started; + bool _shouldBeAddedToMix; }; #endif /* defined(__interface__AudioRingBuffer__) */ diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 898eaed377..da2b017875 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -7,9 +7,13 @@ // #include +#include // std:min #include #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; +} + diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 7569e99868..bf4a6ef699 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -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__) */ diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 533135d3ac..7c8f7f5e9b 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #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 +} + diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index e70229637a..1b40de5448 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -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__) */ diff --git a/libraries/voxels/src/MarkerNode.cpp b/libraries/voxels/src/MarkerNode.cpp deleted file mode 100644 index f3199f4a32..0000000000 --- a/libraries/voxels/src/MarkerNode.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// MarkerNode.cpp -// hifi -// -// Created by Stephen Birarda on 3/26/13. -// -// - -#include "MarkerNode.h" -#include - -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]); - } - } -} diff --git a/libraries/voxels/src/MarkerNode.h b/libraries/voxels/src/MarkerNode.h deleted file mode 100644 index b411830325..0000000000 --- a/libraries/voxels/src/MarkerNode.h +++ /dev/null @@ -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__) */ diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 9ebfba6e9a..d05bb9e1cf 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -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; diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h new file mode 100644 index 0000000000..66ddbcb915 --- /dev/null +++ b/libraries/voxels/src/VoxelConstants.h @@ -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 diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 4278d8d75e..c1527d3334 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -7,12 +7,15 @@ // #include +#include #include #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; +} diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index cf644a1fd1..4cda55f8a7 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -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__) */ diff --git a/libraries/voxels/src/VoxelNodeBag.cpp b/libraries/voxels/src/VoxelNodeBag.cpp new file mode 100644 index 0000000000..e6b20cbf4b --- /dev/null +++ b/libraries/voxels/src/VoxelNodeBag.cpp @@ -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 + +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; +} diff --git a/libraries/voxels/src/VoxelNodeBag.h b/libraries/voxels/src/VoxelNodeBag.h new file mode 100644 index 0000000000..9eb6a91e6a --- /dev/null +++ b/libraries/voxels/src/VoxelNodeBag.h @@ -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__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index db69535e14..cbbccedb59 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -17,33 +17,16 @@ #include "PacketHeaders.h" #include "OctalCode.h" #include "VoxelTree.h" +#include "VoxelNodeBag.h" #include "ViewFrustum.h" #include // 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; +} diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 60c66925ec..ac3c762fcf 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -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); diff --git a/voxel-server/src/VoxelAgentData.cpp b/voxel-server/src/VoxelAgentData.cpp index e01ae90a69..7eba95364b 100644 --- a/voxel-server/src/VoxelAgentData.cpp +++ b/voxel-server/src/VoxelAgentData.cpp @@ -6,21 +6,45 @@ // // +#include "PacketHeaders.h" #include "VoxelAgentData.h" #include #include 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 { diff --git a/voxel-server/src/VoxelAgentData.h b/voxel-server/src/VoxelAgentData.h index 4ca7949b92..74a402bb78 100644 --- a/voxel-server/src/VoxelAgentData.h +++ b/voxel-server/src/VoxelAgentData.h @@ -12,17 +12,41 @@ #include #include #include -#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__) */ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index a3c018765a..716a4e9d7b 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -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"));