fixed merge

This commit is contained in:
Jeffrey Ventrella 2013-05-01 13:51:59 -07:00
commit c88a7f8f55
45 changed files with 1676 additions and 996 deletions

View file

@ -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);
}
}

View file

@ -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;
}

BIN
eve/resources/audio/eve.raw Normal file

Binary file not shown.

View file

@ -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) {

View file

@ -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)

View file

@ -13,14 +13,36 @@
#include <SharedUtil.h>
#include "Avatar.h"
#include "Log.h"
#include "ui/TextRenderer.h"
#include <AgentList.h>
#include <AgentTypes.h>
#include <PacketHeaders.h>
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<unsigned char> 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<float>( (double)TRANSMITTER_COUNT/(msecsElapsed/1000.0) );
_transmitterHz = static_cast<float>( (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

View file

@ -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

View file

@ -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 );

View file

@ -21,8 +21,6 @@ public:
void setYourHandState ( int state );
void simulate (float deltaTime);
void render();
const float THREAD_RADIUS = 0.007;
private:

View file

@ -14,6 +14,7 @@
#include <stdarg.h>
#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;

View file

@ -15,6 +15,7 @@
#include <SharedUtil.h>
#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<float>(x), static_cast<float>(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();
}

View file

@ -19,6 +19,13 @@
#include <Orientation.h>
// 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,

View file

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

View file

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

View file

@ -39,6 +39,8 @@
#include <ifaddrs.h>
#endif
#include <QApplication>
#include <pthread.h>
#include <glm/glm.hpp>
@ -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 <AgentList.h>
@ -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<char**>(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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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 <QFont>
#include <QPaintEngine>
#include <QtDebug>
#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;
}

View file

@ -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 <QFont>
#include <QFontMetrics>
#include <QHash>
#include <QImage>
#include <QVector>
// 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<char, Glyph> _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<GLuint> _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__) */

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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__) */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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