mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 13:33:38 +02:00
fixed merge
This commit is contained in:
commit
c88a7f8f55
45 changed files with 1676 additions and 996 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
BIN
eve/resources/audio/eve.raw
Normal file
Binary file not shown.
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -21,8 +21,6 @@ public:
|
|||
void setYourHandState ( int state );
|
||||
void simulate (float deltaTime);
|
||||
void render();
|
||||
|
||||
const float THREAD_RADIUS = 0.007;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
142
interface/src/ui/TextRenderer.cpp
Normal file
142
interface/src/ui/TextRenderer.cpp
Normal 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;
|
||||
}
|
89
interface/src/ui/TextRenderer.h
Normal file
89
interface/src/ui/TextRenderer.h
Normal 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__) */
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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__) */
|
|
@ -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;
|
||||
|
|
24
libraries/voxels/src/VoxelConstants.h
Normal file
24
libraries/voxels/src/VoxelConstants.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
90
libraries/voxels/src/VoxelNodeBag.cpp
Normal file
90
libraries/voxels/src/VoxelNodeBag.cpp
Normal 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;
|
||||
}
|
44
libraries/voxels/src/VoxelNodeBag.h
Normal file
44
libraries/voxels/src/VoxelNodeBag.h
Normal 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__) */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
Loading…
Reference in a new issue