Conflicts:
	libraries/shared/src/PacketHeaders.h
This commit is contained in:
Andrzej Kapolka 2013-05-07 14:44:17 -07:00
commit c7e31ec414
46 changed files with 1237 additions and 1016 deletions

View file

@ -16,10 +16,14 @@
#include <errno.h>
#include <fstream>
#include <limits>
#include <signal.h>
#include <AgentList.h>
#include <AgentTypes.h>
#include <SharedUtil.h>
#include <StdDev.h>
#include <Stacktrace.h>
#include "AudioRingBuffer.h"
#include "PacketHeaders.h"
@ -56,11 +60,8 @@ const float DISTANCE_RATIO = 3.0f / 0.3f;
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
const int PHASE_DELAY_AT_90 = 20;
const int AGENT_LOOPBACK_MODIFIER = 307;
const int LOOPBACK_SANITY_CHECK = 0;
StDev stdev;
const float MAX_OFF_AXIS_ATTENUATION = 0.5f;
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
long sumSample = sampleToAdd + mixSample;
@ -71,8 +72,7 @@ void plateauAdditionOfSamples(int16_t &mixSample, int16_t sampleToAdd) {
mixSample = normalizedSample;
}
void *sendBuffer(void *args)
{
void *sendBuffer(void *args) {
int sentBytes;
int nextFrame = 0;
timeval startTime;
@ -106,115 +106,117 @@ void *sendBuffer(void *args)
}
int numAgents = agentList->size();
float distanceCoeffs[numAgents][numAgents];
memset(distanceCoeffs, 0, sizeof(distanceCoeffs));
float distanceCoefficients[numAgents][numAgents];
memset(distanceCoefficients, 0, sizeof(distanceCoefficients));
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
AudioRingBuffer* agentRingBuffer = (AudioRingBuffer*) agent->getLinkedData();
float agentBearing = agentRingBuffer->getBearing();
bool agentWantsLoopback = false;
if (agentBearing > 180 || agentBearing < -180) {
// we were passed an invalid bearing because this agent wants loopback (pressed the H key)
agentWantsLoopback = true;
// correct the bearing
agentBearing = agentBearing > 0
? agentBearing - AGENT_LOOPBACK_MODIFIER
: agentBearing + AGENT_LOOPBACK_MODIFIER;
}
int16_t clientMix[BUFFER_LENGTH_SAMPLES_PER_CHANNEL * 2] = {};
for (AgentList::iterator otherAgent = agentList->begin(); otherAgent != agentList->end(); otherAgent++) {
if (otherAgent != agent || (otherAgent == agent && agentWantsLoopback)) {
if (otherAgent != agent || (otherAgent == agent && agentRingBuffer->shouldLoopbackForAgent())) {
AudioRingBuffer* otherAgentBuffer = (AudioRingBuffer*) otherAgent->getLinkedData();
if (otherAgentBuffer->shouldBeAddedToMix()) {
float *agentPosition = agentRingBuffer->getPosition();
float *otherAgentPosition = otherAgentBuffer->getPosition();
float bearingRelativeAngleToSource = 0.f;
float attenuationCoefficient = 1.f;
int numSamplesDelay = 0;
float weakChannelAmplitudeRatio = 1.f;
// 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 (otherAgent != agent) {
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 (distanceCoefficients[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));
distanceCoefficients[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 absoluteAngleToSource = 0;
float bearingRelativeAngleToSource = 0;
// find the angle we need for calculation based on the orientation of the triangle
if (otherAgentPosition[0] > agentPosition[0]) {
if (otherAgentPosition[2] > agentPosition[2]) {
absoluteAngleToSource = -90 + triangleAngle;
} else {
absoluteAngleToSource = -90 - triangleAngle;
}
} else {
angleToSource = 90 + triangleAngle - agentBearing;
if (otherAgentPosition[2] > agentPosition[2]) {
absoluteAngleToSource = 90 - triangleAngle;
} else {
absoluteAngleToSource = 90 + triangleAngle;
}
}
if (absoluteAngleToSource > 180) {
absoluteAngleToSource -= 360;
} else if (absoluteAngleToSource < -180) {
absoluteAngleToSource += 360;
}
bearingRelativeAngleToSource = absoluteAngleToSource - agentBearing;
bearingRelativeAngleToSource *= (M_PI / 180);
float angleOfDelivery = absoluteAngleToSource - otherAgentBuffer->getBearing();
if (angleOfDelivery < -180) {
angleOfDelivery += 360;
}
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (fabsf(angleOfDelivery) / 90.0f));
attenuationCoefficient = distanceCoefficients[lowAgentIndex][highAgentIndex]
* otherAgentBuffer->getAttenuationRatio()
* offAxisCoefficient;
float sinRatio = fabsf(sinf(bearingRelativeAngleToSource));
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
}
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;
int16_t* goodChannel = bearingRelativeAngleToSource > 0 ? clientMix + BUFFER_LENGTH_SAMPLES_PER_CHANNEL : clientMix;
int16_t* delayedChannel = bearingRelativeAngleToSource > 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
int earlierSample = delaySamplePointer[s] *
distanceCoeffs[lowAgentIndex][highAgentIndex] *
otherAgentBuffer->getAttenuationRatio();
int earlierSample = delaySamplePointer[s] * attenuationCoefficient;
plateauAdditionOfSamples(delayedChannel[s], earlierSample * weakChannelAmplitudeRatio);
}
int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] *
distanceCoeffs[lowAgentIndex][highAgentIndex] *
otherAgentBuffer->getAttenuationRatio());
int16_t currentSample = (otherAgentBuffer->getNextOutput()[s] * attenuationCoefficient);
plateauAdditionOfSamples(goodChannel[s], currentSample);
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
plateauAdditionOfSamples(delayedChannel[s + numSamplesDelay],
currentSample *
weakChannelAmplitudeRatio *
otherAgentBuffer->getAttenuationRatio());
weakChannelAmplitudeRatio);
}
}
}
@ -255,11 +257,12 @@ void attachNewBufferToAgent(Agent *newAgent) {
}
}
int main(int argc, const char * argv[])
{
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT);
int main(int argc, const char* argv[]) {
signal(SIGSEGV, printStacktrace);
setvbuf(stdout, NULL, _IOLBF, 0);
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT);
ssize_t receivedBytes = 0;
agentList->linkedDataCreateCallback = attachNewBufferToAgent;
@ -272,49 +275,17 @@ int main(int argc, const char * argv[])
pthread_t sendBufferThread;
pthread_create(&sendBufferThread, NULL, sendBuffer, NULL);
int16_t *loopbackAudioPacket;
if (LOOPBACK_SANITY_CHECK) {
loopbackAudioPacket = new int16_t[1024];
}
sockaddr *agentAddress = new sockaddr;
timeval lastReceive;
gettimeofday(&lastReceive, NULL);
bool firstSample = true;
while (true) {
if(agentList->getAgentSocket().receive(agentAddress, packetData, &receivedBytes)) {
if (packetData[0] == PACKET_HEADER_INJECT_AUDIO) {
// Compute and report standard deviation for jitter calculation
if (firstSample) {
stdev.reset();
firstSample = false;
} else {
double tDiff = (usecTimestampNow() - usecTimestamp(&lastReceive)) / 1000;
stdev.addValue(tDiff);
if (stdev.getSamples() > 500) {
//printf("Avg: %4.2f, Stdev: %4.2f\n", stdev.getAverage(), stdev.getStDev());
stdev.reset();
}
if (agentList->addOrUpdateAgent(agentAddress, agentAddress, packetData[0], agentList->getLastAgentId())) {
agentList->increaseAgentId();
}
gettimeofday(&lastReceive, NULL);
// add or update the existing interface agent
if (!LOOPBACK_SANITY_CHECK) {
if (agentList->addOrUpdateAgent(agentAddress, agentAddress, packetData[0], agentList->getLastAgentId())) {
agentList->increaseAgentId();
}
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
} else {
memcpy(loopbackAudioPacket, packetData + 1 + (sizeof(float) * 4), 1024);
agentList->getAgentSocket().send(agentAddress, loopbackAudioPacket, 1024);
}
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
}
}
}

View file

@ -51,8 +51,7 @@ void attachAvatarDataToAgent(Agent *newAgent) {
}
}
int main(int argc, const char* argv[])
{
int main(int argc, const char* argv[]) {
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);
@ -69,17 +68,20 @@ int main(int argc, const char* argv[])
*broadcastPacket = PACKET_HEADER_BULK_AVATAR_DATA;
unsigned char* currentBufferPosition = NULL;
uint16_t agentID = 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();
}
// grab the agent ID from the packet
unpackAgentId(packetData + 1, &agentID);
// this is positional data from an agent
// add or update the agent in our list
agentList->addOrUpdateAgent(agentAddress, agentAddress, AGENT_TYPE_AVATAR, agentID);
// parse positional data from an agent
agentList->updateAgentWithData(agentAddress, packetData, receivedBytes);
currentBufferPosition = broadcastPacket + 1;

View file

@ -86,7 +86,6 @@ int main(int argc, const char * argv[])
unsigned char* currentBufferPos;
unsigned char* startPointer;
int packetBytesWithoutLeadingChar;
sockaddr_in agentPublicAddress, agentLocalAddress;
agentLocalAddress.sin_family = AF_INET;
@ -95,12 +94,15 @@ int main(int argc, const char * argv[])
agentList->startSilentAgentRemovalThread();
uint16_t packetAgentID = 0;
while (true) {
if (agentList->getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes)) {
if (agentList->getAgentSocket().receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes) &&
(packetData[0] == PACKET_HEADER_DOMAIN_RFD || packetData[0] == PACKET_HEADER_DOMAIN_LIST_REQUEST)) {
std::map<char, Agent *> newestSoloAgents;
agentType = packetData[1];
unpackSocket(&packetData[2], (sockaddr*) &agentLocalAddress);
unpackSocket(packetData + 2, (sockaddr*) &agentLocalAddress);
// check the agent public address
// if it matches our local address we're on the same box
@ -118,14 +120,9 @@ int main(int argc, const char * argv[])
agentType,
agentList->getLastAgentId())) {
agentList->increaseAgentId();
} else if (packetData[0] == PACKET_HEADER_DOMAIN_RFD) {
// if this is a previous agent, and they are re-reporting for duty
// then we need to update the first receive time
Agent* refreshedAgent = agentList->agentWithAddress((sockaddr*) &agentLocalAddress);
refreshedAgent->setWakeMicrostamp(usecTimestampNow());
}
currentBufferPos = broadcastPacket + 2;
currentBufferPos = broadcastPacket + 1;
startPointer = currentBufferPos;
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
@ -146,8 +143,18 @@ int main(int argc, const char * argv[])
}
}
} else {
double timeNow = usecTimestampNow();
// this is the agent, just update last receive to now
agent->setLastHeardMicrostamp(usecTimestampNow());
agent->setLastHeardMicrostamp(timeNow);
// grab the ID for this agent so we can send it back with the packet
packetAgentID = agent->getAgentId();
if (packetData[0] == PACKET_HEADER_DOMAIN_RFD
&& memchr(SOLO_AGENT_TYPES, agentType, sizeof(SOLO_AGENT_TYPES))) {
agent->setWakeMicrostamp(timeNow);
}
}
}
@ -158,11 +165,13 @@ int main(int argc, const char * argv[])
currentBufferPos = addAgentToBroadcastPacket(currentBufferPos, soloAgent->second);
}
if ((packetBytesWithoutLeadingChar = (currentBufferPos - startPointer))) {
agentList->getAgentSocket().send((sockaddr*) &agentPublicAddress,
broadcastPacket,
packetBytesWithoutLeadingChar + 1);
}
// add the agent ID to the end of the pointer
currentBufferPos += packAgentId(currentBufferPos, packetAgentID);
// send the constructed list back to this agent
agentList->getAgentSocket().send((sockaddr*) &agentPublicAddress,
broadcastPacket,
(currentBufferPos - startPointer) + 1);
}
}

View file

@ -25,6 +25,10 @@ const int MIN_ITERATIONS_BETWEEN_AUDIO_SENDS = (MIN_AUDIO_SEND_INTERVAL_SECS * 1
const int MAX_AUDIO_SEND_INTERVAL_SECS = 15;
const float MAX_ITERATIONS_BETWEEN_AUDIO_SENDS = (MAX_AUDIO_SEND_INTERVAL_SECS * 1000) / DATA_SEND_INTERVAL_MSECS;
const int ITERATIONS_BEFORE_HAND_GRAB = 100;
const int HAND_GRAB_DURATION_ITERATIONS = 50;
const int HAND_TIMER_SLEEP_ITERATIONS = 50;
bool stopReceiveAgentDataThread;
bool injectAudioThreadRunning = false;
@ -130,8 +134,6 @@ int main(int argc, const char* argv[]) {
unsigned char broadcastPacket[MAX_PACKET_SIZE];
broadcastPacket[0] = PACKET_HEADER_HEAD_DATA;
int numBytesToSend = 0;
timeval thisSend;
double numMicrosecondsSleep = 0;
@ -145,16 +147,19 @@ int main(int argc, const char* argv[]) {
gettimeofday(&thisSend, NULL);
// find the current avatar mixer
Agent *avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
Agent* avatarMixer = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
// make sure we actually have an avatar mixer with an active socket
if (avatarMixer != NULL && avatarMixer->getActiveSocket() != NULL) {
if (agentList->getOwnerID() != UNKNOWN_AGENT_ID && avatarMixer && avatarMixer->getActiveSocket() != NULL) {
unsigned char* packetPosition = broadcastPacket + sizeof(PACKET_HEADER);
packetPosition += packAgentId(packetPosition, agentList->getOwnerID());
// use the getBroadcastData method in the AvatarData class to populate the broadcastPacket buffer
numBytesToSend = eve.getBroadcastData((broadcastPacket + 1));
packetPosition += eve.getBroadcastData(packetPosition);
// use the UDPSocket instance attached to our agent list to send avatar data to mixer
agentList->getAgentSocket().send(avatarMixer->getActiveSocket(), broadcastPacket, numBytesToSend);
}
agentList->getAgentSocket().send(avatarMixer->getActiveSocket(), broadcastPacket, packetPosition - broadcastPacket);
}
// temporarily disable Eve's audio sending until the file is actually available on EC2 box
if (numIterationsLeftBeforeAudioSend == 0) {
@ -175,13 +180,12 @@ int main(int argc, const char* argv[]) {
// simulate the effect of pressing and un-pressing the mouse button/pad
handStateTimer++;
if ( handStateTimer == 100 ) {
if (handStateTimer == ITERATIONS_BEFORE_HAND_GRAB) {
eve.setHandState(1);
}
if ( handStateTimer == 150 ) {
} else if (handStateTimer == ITERATIONS_BEFORE_HAND_GRAB + HAND_GRAB_DURATION_ITERATIONS) {
eve.setHandState(0);
}
if ( handStateTimer >= 200 ) {
} else if (handStateTimer >= ITERATIONS_BEFORE_HAND_GRAB + HAND_GRAB_DURATION_ITERATIONS + HAND_TIMER_SLEEP_ITERATIONS) {
handStateTimer = 0;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -34,10 +34,6 @@ const float BODY_ROLL_WHILE_TURNING = 0.1;
const float LIN_VEL_DECAY = 5.0;
const float MY_HAND_HOLDING_PULL = 0.2;
const float YOUR_HAND_HOLDING_PULL = 1.0;
//const float BODY_SPRING_DEFAULT_TIGHTNESS = 20.0f;
//const float BODY_SPRING_FORCE = 6.0f;
const float BODY_SPRING_DEFAULT_TIGHTNESS = 1500.0f;
const float BODY_SPRING_FORCE = 300.0f;
@ -66,8 +62,8 @@ bool usingBigSphereCollisionTest = true;
char iris_texture_file[] = "resources/images/green_eye.png";
float chatMessageScale = 0.001;
float chatMessageHeight = 0.4;
float chatMessageScale = 0.0015;
float chatMessageHeight = 0.45;
vector<unsigned char> iris_texture;
unsigned int iris_texture_width = 512;
@ -144,13 +140,14 @@ Avatar::Avatar(bool isMine) {
_renderYaw = 0.0;
_renderPitch = 0.0;
_sphere = NULL;
_interactingOther = NULL;
_handHoldingPosition = glm::vec3(0.0f, 0.0f, 0.0f);
_distanceToNearestAvatar = std::numeric_limits<float>::max();
_gravity = glm::vec3(0.0f, -1.0f, 0.0f); // default
initializeSkeleton();
_avatarTouch.setReachableRadius(0.6);
if (iris_texture.size() == 0) {
switchToResourcesParentIfRequired();
unsigned error = lodepng::decode(iris_texture, iris_texture_width, iris_texture_height, iris_texture_file);
@ -295,7 +292,6 @@ void Avatar::UpdateGyros(float frametime, SerialInterface* serialInterface, glm:
if ((_headYaw < MAX_YAW) && (_headYaw > MIN_YAW)) {
addHeadYaw(_head.yawRate * HEAD_ROTATION_SCALE * frametime);
}
}
float Avatar::getAbsoluteHeadYaw() const {
@ -316,16 +312,14 @@ void Avatar::setLeanSideways(float dist){
_head.leanSideways = dist;
}
void Avatar::setMousePressed(bool d) {
_mousePressed = d;
void Avatar::setMousePressed(bool mousePressed) {
_mousePressed = mousePressed;
}
bool Avatar::getIsNearInteractingOther() {
return _avatarTouch.getAbleToReachOtherAvatar();
}
void Avatar::simulate(float deltaTime) {
@ -335,9 +329,15 @@ void Avatar::simulate(float deltaTime) {
// update avatar skeleton
updateSkeleton();
//detect and respond to collisions with other avatars...
if (_isMine) {
updateAvatarCollisions(deltaTime);
}
//update the movement of the hand and process handshaking with other avatars...
updateHandMovementAndTouching(deltaTime);
_avatarTouch.simulate(deltaTime);
// apply gravity and collision with the ground/floor
if (USING_AVATAR_GRAVITY) {
@ -381,7 +381,7 @@ void Avatar::simulate(float deltaTime) {
// decay body rotation momentum
float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime;
if ( bodySpinMomentum < 0.0f ) { bodySpinMomentum = 0.0f; }
if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
_bodyPitchDelta *= bodySpinMomentum;
_bodyYawDelta *= bodySpinMomentum;
_bodyRollDelta *= bodySpinMomentum;
@ -430,8 +430,6 @@ void Avatar::simulate(float deltaTime) {
}
//update the movement of the hand and process handshaking with other avatars...
void Avatar::updateHandMovementAndTouching(float deltaTime) {
// reset hand and arm positions according to hand movement
@ -441,104 +439,68 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) {
+ _orientation.getFront() * -_movedHandOffset.y * 1.0f;
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement;
if (_isMine) {
_handState = _mousePressed;
}
//reset these for the next go-round
_avatarTouch.setAbleToReachOtherAvatar (false);
_avatarTouch.setHandsCloseEnoughToGrasp(false);
// if the avatar being simulated is mine, then loop through
// all the other avatars for potential interactions...
if (_isMine)
{
// Reset detector for nearest avatar
_distanceToNearestAvatar = std::numeric_limits<float>::max();
_avatarTouch.setMyBodyPosition(_position);
Avatar * _interactingOther = NULL;
float closestDistance = std::numeric_limits<float>::max();
//loop through all the other avatars for potential interactions...
AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *otherAvatar = (Avatar *)agent->getLinkedData();
// check for collisions with other avatars and respond
updateCollisionWithOtherAvatar(otherAvatar, deltaTime );
// test other avatar hand position for proximity
glm::vec3 v(_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position);
v -= otherAvatar->getJointPosition(AVATAR_JOINT_RIGHT_SHOULDER);
/*
// Test: Show angle between your fwd vector and nearest avatar
glm::vec3 vectorBetweenUs = otherAvatar->getJointPosition(AVATAR_JOINT_PELVIS) -
getJointPosition(AVATAR_JOINT_PELVIS);
glm::vec3 myForwardVector = _orientation.getFront();
printLog("Angle between: %f\n", angleBetween(&vectorBetweenUs, &myForwardVector));
*/
// test whether shoulders are close enough to allow for reaching to touch hands
glm::vec3 v(_position - otherAvatar->_position);
float distance = glm::length(v);
if (distance < _distanceToNearestAvatar) {_distanceToNearestAvatar = distance;}
if (distance < _maxArmLength + _maxArmLength) {
if (distance < closestDistance) {
closestDistance = distance;
_interactingOther = otherAvatar;
if (! _avatarTouch.getAbleToReachOtherAvatar()) {
//initialize _handHolding
_handHoldingPosition = _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position;
_avatarTouch.setAbleToReachOtherAvatar(true);
}
glm::vec3 vectorBetweenHands(_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
vectorBetweenHands -= otherAvatar->getJointPosition(AVATAR_JOINT_RIGHT_FINGERTIPS);
float distanceBetweenHands = glm::length(vectorBetweenHands);
if (distanceBetweenHands < HANDS_CLOSE_ENOUGH_TO_GRASP) {
_avatarTouch.setHandsCloseEnoughToGrasp(true);
}
// if I am holding hands with another avatar, a force is applied
if ((_handState == 1) || (_interactingOther->_handState == 1)) {
// if the hands are close enough to grasp...
if (distanceBetweenHands < HANDS_CLOSE_ENOUGH_TO_GRASP)
{
// apply the forces...
glm::vec3 vectorToOtherHand = _interactingOther->_handPosition - _handHoldingPosition;
glm::vec3 vectorToMyHand = _joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position - _handHoldingPosition;
_handHoldingPosition += vectorToOtherHand * YOUR_HAND_HOLDING_PULL;
_handHoldingPosition += vectorToMyHand * MY_HAND_HOLDING_PULL;
_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handHoldingPosition;
// apply a force to the avatar body
if (glm::length(vectorToOtherHand) > _maxArmLength * 0.9) {
_velocity += vectorToOtherHand;
}
}
}
}
}
}
// Set the vector we send for hand position to other people to be our right hand
setHandPosition(_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
if (_interactingOther) {
_avatarTouch.setYourBodyPosition(_interactingOther->_position);
_avatarTouch.setYourHandPosition(_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition);
_avatarTouch.setYourHandState (_interactingOther->_handState);
}
}//if (_isMine)
//constrain right arm length and re-adjust elbow position as it bends
// NOTE - the following must be called on all avatars - not just _isMine
updateArmIKAndConstraints(deltaTime);
// set hand positions for _avatarTouch.setMyHandPosition AFTER calling updateArmIKAndConstraints
if (_interactingOther) {
if (_isMine) {
_avatarTouch.setMyHandPosition (_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
_avatarTouch.setYourHandPosition(_interactingOther->_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
_avatarTouch.setMyHandState (_handState);
_avatarTouch.setYourHandState (_interactingOther->_handState);
_avatarTouch.simulate(deltaTime);
}
}
if (!_avatarTouch.getAbleToReachOtherAvatar() ) {
_interactingOther = NULL;
if (_isMine) {
//Set the vector we send for hand position to other people to be our right hand
setHandPosition(_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position);
if (_mousePressed) {
_handState = 1;
} else {
_handState = 0;
}
_avatarTouch.setMyHandState(_handState);
if (_handState == 1) {
_avatarTouch.setMyHandPosition(_joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].springyPosition);
}
}
}
void Avatar::updateHead(float deltaTime) {
//apply the head lean values to the springy position...
@ -690,66 +652,96 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
}
//detect collisions with other avatars and respond
void Avatar::updateCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
// check if the bounding spheres of the two avatars are colliding
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) {
void Avatar::updateAvatarCollisions(float deltaTime) {
// Reset detector for nearest avatar
_distanceToNearestAvatar = std::numeric_limits<float>::max();
//loop through all the other avatars for potential interactions...
AgentList* agentList = AgentList::getInstance();
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *otherAvatar = (Avatar *)agent->getLinkedData();
// check if the bounding spheres of the two avatars are colliding
glm::vec3 vectorBetweenBoundingSpheres(_position - otherAvatar->_position);
if (glm::length(vectorBetweenBoundingSpheres) < _height * ONE_HALF + otherAvatar->_height * ONE_HALF) {
//apply forces from collision
applyCollisionWithOtherAvatar(otherAvatar, deltaTime );
}
// test other avatar hand position for proximity
glm::vec3 v(_joint[ AVATAR_JOINT_RIGHT_SHOULDER ].position);
v -= otherAvatar->getPosition();
float distance = glm::length(v);
if (distance < _distanceToNearestAvatar) {
_distanceToNearestAvatar = distance;
}
}
}
}
//detect collisions with other avatars and respond
void Avatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
float bodyMomentum = 1.0f;
glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f);
// loop through the joints of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_JOINTS; b++) {
if (_joint[b].isCollidable) {
// loop through the joints of each avatar to check for every possible collision
for (int b=1; b<NUM_AVATAR_JOINTS; b++) {
if (_joint[b].isCollidable) {
for (int o=b+1; o<NUM_AVATAR_JOINTS; o++) {
if (otherAvatar->_joint[o].isCollidable) {
for (int o=b+1; o<NUM_AVATAR_JOINTS; o++) {
if (otherAvatar->_joint[o].isCollidable) {
glm::vec3 vectorBetweenJoints(_joint[b].springyPosition - otherAvatar->_joint[o].springyPosition);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
glm::vec3 vectorBetweenJoints(_joint[b].springyPosition - otherAvatar->_joint[o].springyPosition);
float distanceBetweenJoints = glm::length(vectorBetweenJoints);
if (distanceBetweenJoints > 0.0 ) { // to avoid divide by zero
float combinedRadius = _joint[b].radius + otherAvatar->_joint[o].radius;
if (distanceBetweenJoints > 0.0 ) { // to avoid divide by zero
float combinedRadius = _joint[b].radius + otherAvatar->_joint[o].radius;
// check for collision
if (distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
// check for collision
if (distanceBetweenJoints < combinedRadius * COLLISION_RADIUS_SCALAR) {
glm::vec3 directionVector = vectorBetweenJoints / distanceBetweenJoints;
// push balls away from each other and apply friction
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * deltaTime;
float ballMomentum = 1.0 - COLLISION_BALL_FRICTION * deltaTime;
if (ballMomentum < 0.0 ) { ballMomentum = 0.0;}
_joint[b].springyVelocity += ballPushForce;
otherAvatar->_joint[o].springyVelocity -= ballPushForce;
_joint[b].springyVelocity *= ballMomentum;
otherAvatar->_joint[o].springyVelocity *= ballMomentum;
// accumulate forces and frictions to apply to the velocities of avatar bodies
bodyPushForce += directionVector * COLLISION_BODY_FORCE * deltaTime;
bodyMomentum -= COLLISION_BODY_FRICTION * deltaTime;
if (bodyMomentum < 0.0 ) { bodyMomentum = 0.0;}
}// check for collision
} // to avoid divide by zero
} // o loop
} // collidable
} // b loop
} // collidable
//apply forces and frictions on the bodies of both avatars
_velocity += bodyPushForce;
otherAvatar->_velocity -= bodyPushForce;
_velocity *= bodyMomentum;
otherAvatar->_velocity *= bodyMomentum;
}
// push balls away from each other and apply friction
glm::vec3 ballPushForce = directionVector * COLLISION_BALL_FORCE * deltaTime;
float ballMomentum = 1.0 - COLLISION_BALL_FRICTION * deltaTime;
if (ballMomentum < 0.0 ) { ballMomentum = 0.0;}
_joint[b].springyVelocity += ballPushForce;
otherAvatar->_joint[o].springyVelocity -= ballPushForce;
_joint[b].springyVelocity *= ballMomentum;
otherAvatar->_joint[o].springyVelocity *= ballMomentum;
// accumulate forces and frictions to apply to the velocities of avatar bodies
bodyPushForce += directionVector * COLLISION_BODY_FORCE * deltaTime;
bodyMomentum -= COLLISION_BODY_FRICTION * deltaTime;
if (bodyMomentum < 0.0 ) { bodyMomentum = 0.0;}
}// check for collision
} // to avoid divide by zero
} // o loop
} // collidable
} // b loop
} // collidable
//apply forces and frictions on the bodies of both avatars
_velocity += bodyPushForce;
otherAvatar->_velocity -= bodyPushForce;
_velocity *= bodyMomentum;
otherAvatar->_velocity *= bodyMomentum;
} // bounding sphere collision
} //method
void Avatar::setDisplayingHead(bool displayingHead ) {
@ -768,7 +760,7 @@ void Avatar::setGravity(glm::vec3 gravity) {
}
void Avatar::render(bool lookingInMirror) {
void Avatar::render(bool lookingInMirror, glm::vec3 cameraPosition) {
// render a simple round on the ground projected down from the avatar's position
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f ), 0.1f, 0.2f );
@ -803,7 +795,7 @@ void Avatar::render(bool lookingInMirror) {
// if this is my avatar, then render my interactions with the other avatar
if (_isMine ) {
_avatarTouch.render();
_avatarTouch.render(cameraPosition);
}
// Render the balls
@ -1193,6 +1185,9 @@ void Avatar::initializeSkeleton() {
// generate world positions
updateSkeleton();
//set spring positions to be in the skeleton bone positions
initializeBodySprings();
}
void Avatar::calculateBoneLengths() {

View file

@ -101,13 +101,6 @@ public:
void setLeanForward(float dist);
void setLeanSideways(float dist);
void addLean(float x, float z);
/*
const glm::vec3& getHeadRightDirection() const { return _orientation.getRight(); };
const glm::vec3& getHeadUpDirection () const { return _orientation.getUp (); };
const glm::vec3& getHeadFrontDirection() const { return _orientation.getFront(); };
*/
const glm::vec3& getHeadPosition() const ;
const glm::vec3& getJointPosition(AvatarJointID j) const { return _joint[j].position; };
const glm::vec3& getBodyUpDirection() const { return _orientation.getUp(); };
@ -117,8 +110,8 @@ public:
AvatarMode getMode();
void setMousePressed( bool pressed );
void render(bool lookingInMirror);
void setMousePressed(bool pressed);
void render(bool lookingInMirrorm, glm::vec3 cameraPosition);
void renderBody();
void renderHead(bool lookingInMirror);
void simulate(float);
@ -241,7 +234,6 @@ private:
float _transmitterHz;
int _transmitterPackets;
glm::vec3 _transmitterInitialReading;
Avatar* _interactingOther;
float _pelvisStandingHeight;
float _height;
Balls* _balls;
@ -260,8 +252,9 @@ private:
void readSensors();
void updateHead( float deltaTime );
void updateHandMovementAndTouching(float deltaTime);
void updateAvatarCollisions(float deltaTime);
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
void updateCollisionWithOtherAvatar( Avatar * other, float deltaTime );
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
void setHeadFromGyros(glm::vec3 * eulerAngles, glm::vec3 * angularVelocity, float deltaTime, float smoothingTime);
void setHeadSpringScale(float s) { _head.returnSpringScale = s; }
};

View file

@ -16,13 +16,4 @@ AvatarRenderer::AvatarRenderer() {
// this method renders the avatar
void AvatarRenderer::render(Avatar *avatar, bool lookingInMirror) {
/*
// show avatar position
glColor4f( 0.5f, 0.5f, 0.5f, 0.6 );
glPushMatrix();
glTranslatef(avatar->_position.x, avatar->_position.y, avatar->_position.z);
glScalef( 0.03, 0.03, 0.03 );
glutSolidSphere( 1, 10, 10 );
glPopMatrix();
*/
}

View file

@ -10,6 +10,7 @@
#include <SharedUtil.h>
#include "AvatarTouch.h"
#include "InterfaceConfig.h"
#include "Util.h"
const float THREAD_RADIUS = 0.012;
@ -17,8 +18,11 @@ AvatarTouch::AvatarTouch() {
_myHandPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
_yourHandPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
_myBodyPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
_yourBodyPosition = glm::vec3( 0.0f, 0.0f, 0.0f );
_myHandState = 0;
_yourHandState = 0;
_yourHandState = 0;
_reachableRadius = 0.0f;
_canReachToOtherAvatar = false;
_handsCloseEnoughToGrasp = false;
@ -28,40 +32,46 @@ AvatarTouch::AvatarTouch() {
}
}
void AvatarTouch::setMyHandPosition( glm::vec3 position ) {
void AvatarTouch::setMyHandPosition(glm::vec3 position) {
_myHandPosition = position;
}
void AvatarTouch::setYourHandPosition( glm::vec3 position ) {
void AvatarTouch::setYourHandPosition(glm::vec3 position) {
_yourHandPosition = position;
}
void AvatarTouch::setMyHandState( int state ) {
void AvatarTouch::setMyBodyPosition(glm::vec3 position) {
_myBodyPosition = position;
}
void AvatarTouch::setYourBodyPosition(glm::vec3 position) {
_yourBodyPosition = position;
}
void AvatarTouch::setMyHandState(int state) {
_myHandState = state;
}
void AvatarTouch::setYourHandState( int state ) {
void AvatarTouch::setYourHandState(int state) {
_yourHandState = state;
}
void AvatarTouch::render() {
void AvatarTouch::setReachableRadius(float r) {
_reachableRadius = r;
}
glm::vec3 v1( _myHandPosition );
glm::vec3 v2( _yourHandPosition );
void AvatarTouch::render(glm::vec3 cameraPosition) {
if (_canReachToOtherAvatar) {
// if my hand is grasping, show it...
if ( _myHandState == 1 ) {
glPushMatrix();
glTranslatef(_myHandPosition.x, _myHandPosition.y, _myHandPosition.z);
glColor4f( 1.0, 1.0, 0.8, 0.3 ); glutSolidSphere( 0.020f, 10.0f, 10.0f );
glColor4f( 1.0, 1.0, 0.4, 0.2 ); glutSolidSphere( 0.025f, 10.0f, 10.0f );
glColor4f( 1.0, 1.0, 0.2, 0.1 ); glutSolidSphere( 0.030f, 10.0f, 10.0f );
glPopMatrix();
}
glColor4f( 0.3, 0.4, 0.5, 0.5 );
glm::vec3 p(_yourBodyPosition);
p.y = 0.0005f;
renderCircle(p, _reachableRadius, glm::vec3(0.0f, 1.0f, 0.0f), 30);
// if your hand is grasping, show it...
if ( _yourHandState == 1 ) {
if (_yourHandState == 1) {
glPushMatrix();
glTranslatef(_yourHandPosition.x, _yourHandPosition.y, _yourHandPosition.z);
glColor4f( 1.0, 1.0, 0.8, 0.3 ); glutSolidSphere( 0.020f, 10.0f, 10.0f );
@ -69,35 +79,61 @@ void AvatarTouch::render() {
glColor4f( 1.0, 1.0, 0.2, 0.1 ); glutSolidSphere( 0.030f, 10.0f, 10.0f );
glPopMatrix();
}
//show beam
glm::vec3 v1(_myHandPosition);
glm::vec3 v2(_yourHandPosition);
if (_handsCloseEnoughToGrasp) {
glLineWidth(2.0);
glColor4f( 0.7f, 0.4f, 0.1f, 0.3 );
glBegin(GL_LINE_STRIP);
glVertex3f( v1.x, v1.y, v1.z );
glVertex3f( v2.x, v2.y, v2.z );
glEnd();
glColor4f( 1.0f, 1.0f, 0.0f, 0.8 );
for (int p=0; p<NUM_POINTS; p++) {
glBegin(GL_POINTS);
glVertex3f(_point[p].x, _point[p].y, _point[p].z);
glEnd();
}
}
}
//show beam
if (_handsCloseEnoughToGrasp) {
glLineWidth( 2.0 );
glColor4f( 0.7f, 0.4f, 0.1f, 0.3 );
glBegin( GL_LINE_STRIP );
glVertex3f( v1.x, v1.y, v1.z );
glVertex3f( v2.x, v2.y, v2.z );
glEnd();
glColor4f( 1.0f, 1.0f, 0.0f, 0.8 );
for (int p=0; p<NUM_POINTS; p++) {
glBegin(GL_POINTS);
glVertex3f(_point[p].x, _point[p].y, _point[p].z);
glEnd();
}
// if my hand is grasping, show it...
if (_myHandState == 1) {
glPushMatrix();
glTranslatef(_myHandPosition.x, _myHandPosition.y, _myHandPosition.z);
glColor4f( 1.0, 1.0, 0.8, 0.3 ); glutSolidSphere( 0.020f, 10.0f, 10.0f );
glColor4f( 1.0, 1.0, 0.4, 0.2 ); glutSolidSphere( 0.025f, 10.0f, 10.0f );
glColor4f( 1.0, 1.0, 0.2, 0.1 ); glutSolidSphere( 0.030f, 10.0f, 10.0f );
glPopMatrix();
}
}
void AvatarTouch::simulate (float deltaTime) {
glm::vec3 v = _yourHandPosition - _myHandPosition;
glm::vec3 v = _yourBodyPosition - _myBodyPosition;
float distance = glm::length(v);
if (distance < _reachableRadius ) {
_canReachToOtherAvatar = true;
} else {
_canReachToOtherAvatar = false;
}
/*
for (int p=0; p<NUM_POINTS; p++) {
_point[p] = _myHandPosition + v * ( (float)p / (float)NUM_POINTS );
_point[p].x += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
_point[p].y += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
_point[p].z += randFloatInRange( -THREAD_RADIUS, THREAD_RADIUS );
}
*/
}

View file

@ -19,30 +19,35 @@ public:
AvatarTouch();
void simulate(float deltaTime);
void render();
void render(glm::vec3 cameraPosition);
void setMyHandPosition ( glm::vec3 position );
void setYourHandPosition( glm::vec3 position );
void setMyHandState ( int state );
void setYourHandState ( int state );
void setMyHandPosition (glm::vec3 position);
void setYourHandPosition(glm::vec3 position);
void setMyBodyPosition (glm::vec3 position);
void setYourBodyPosition(glm::vec3 position);
void setMyHandState (int state);
void setYourHandState (int state);
void setReachableRadius (float r);
void setAbleToReachOtherAvatar (bool a) {_canReachToOtherAvatar = a;}
void setHandsCloseEnoughToGrasp(bool h) {_handsCloseEnoughToGrasp = h;}
void setAbleToReachOtherAvatar ( bool a ) { _canReachToOtherAvatar = a; }
void setHandsCloseEnoughToGrasp( bool h ) { _handsCloseEnoughToGrasp = h; }
bool getAbleToReachOtherAvatar () { return _canReachToOtherAvatar; }
bool getHandsCloseEnoughToGrasp() { return _handsCloseEnoughToGrasp; }
bool getAbleToReachOtherAvatar () const {return _canReachToOtherAvatar;}
bool getHandsCloseEnoughToGrasp() const {return _handsCloseEnoughToGrasp;}
private:
static const int NUM_POINTS = 100;
glm::vec3 _point [NUM_POINTS];
glm::vec3 _myBodyPosition;
glm::vec3 _yourBodyPosition;
glm::vec3 _myHandPosition;
glm::vec3 _yourHandPosition;
int _myHandState;
int _yourHandState;
bool _canReachToOtherAvatar;
bool _handsCloseEnoughToGrasp;
float _reachableRadius;
};
#endif

View file

@ -150,30 +150,63 @@ void SerialInterface::renderLevels(int width, int height) {
int i;
int disp_x = 10;
const int GAP = 16;
char val[10];
for(i = 0; i < NUM_CHANNELS; i++) {
// Actual value
char val[40];
if (!USING_INVENSENSE_MPU9150) {
for(i = 0; i < NUM_CHANNELS; i++) {
// Actual value
glLineWidth(2.0);
glColor4f(1, 1, 1, 1);
glBegin(GL_LINES);
glVertex2f(disp_x, height * 0.95);
glVertex2f(disp_x, height * (0.25 + 0.75f * getValue(i) / 4096));
glColor4f(1, 0, 0, 1);
glVertex2f(disp_x - 3, height * (0.25 + 0.75f * getValue(i) / 4096));
glVertex2f(disp_x, height * (0.25 + 0.75f * getValue(i) / 4096));
glEnd();
// Trailing Average value
glBegin(GL_LINES);
glColor4f(1, 1, 1, 1);
glVertex2f(disp_x, height * (0.25 + 0.75f * getTrailingValue(i) / 4096));
glVertex2f(disp_x + 4, height * (0.25 + 0.75f * getTrailingValue(i) / 4096));
glEnd();
sprintf(val, "%d", getValue(i));
drawtext(disp_x - GAP / 2, (height * 0.95) + 2, 0.08, 90, 1.0, 0, val, 0, 1, 0);
disp_x += GAP;
}
} else {
const int LEVEL_CORNER_X = 10;
const int LEVEL_CORNER_Y = 200;
// Draw the text values
sprintf(val, "Yaw %d", _lastYaw);
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y, 0.10, 0, 1.0, 1, val, 0, 1, 0);
sprintf(val, "Pitch %d", _lastPitch);
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 15, 0.10, 0, 1.0, 1, val, 0, 1, 0);
sprintf(val, "Roll %d", _lastRoll);
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 30, 0.10, 0, 1.0, 1, val, 0, 1, 0);
// Draw the levels as horizontal lines
const int LEVEL_CENTER = 150;
glLineWidth(2.0);
glColor4f(1, 1, 1, 1);
glBegin(GL_LINES);
glVertex2f(disp_x, height*0.95);
glVertex2f(disp_x, height*(0.25 + 0.75f*getValue(i)/4096));
glColor4f(1, 0, 0, 1);
glVertex2f(disp_x - 3, height*(0.25 + 0.75f*getValue(i)/4096));
glVertex2f(disp_x, height*(0.25 + 0.75f*getValue(i)/4096));
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 3);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastYaw, LEVEL_CORNER_Y - 3);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 12);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastPitch, LEVEL_CORNER_Y + 12);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 27);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastRoll, LEVEL_CORNER_Y + 27);
glEnd();
// Trailing Average value
// Draw green vertical centerline
glColor4f(0, 1, 0, 0.5);
glBegin(GL_LINES);
glColor4f(1, 1, 1, 1);
glVertex2f(disp_x, height*(0.25 + 0.75f*getTrailingValue(i)/4096));
glVertex2f(disp_x + 4, height*(0.25 + 0.75f*getTrailingValue(i)/4096));
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 6);
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 30);
glEnd();
sprintf(val, "%d", getValue(i));
drawtext(disp_x-GAP/2, (height*0.95)+2, 0.08, 90, 1.0, 0, val, 0, 1, 0);
disp_x += GAP;
}
// Display Serial latency block
if (LED) {
glColor3f(1,0,0);
@ -204,7 +237,7 @@ void SerialInterface::readData() {
int initialSamples = totalSamples;
if (USING_INVENSENSE_MPU9150) {
if (USING_INVENSENSE_MPU9150) {
unsigned char gyroBuffer[20];
// ask the invensense for raw gyro data

View file

@ -1,76 +0,0 @@
//
// Texture.cpp
// interface
//
// Added by Yoz on 11/5/12.
//
// Code lifted from http://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures
#include "Texture.h"
#include "InterfaceConfig.h"
#include "Log.h"
#include <lodepng.h>
#include <vector>
#include <cstdio>
#define TEXTURE_LOAD_ERROR 0
/**
* Read a given filename as a PNG texture, and set
it as the current default OpenGL texture.
* @param[in] filename Relative path to PNG file
* @return Zero for success.
*/
int load_png_as_texture(char* filename)
{
std::vector<unsigned char> image;
// width and height will be read from the file at the start
// and loaded into these vars
unsigned int width = 1, height = 1;
unsigned error = lodepng::decode(image, width, height, filename);
if (error) {
printLog("Error loading texture\n");
return (int) error;
}
// Make some OpenGL properties better for 2D and enable alpha channel.
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
if(glGetError() != GL_NO_ERROR)
{
printLog("Error initing GL\n");
return 1;
}
// Texture size must be power of two for the primitive OpenGL version this is written for. Find next power of two.
size_t u2 = 1; while(u2 < width) u2 *= 2;
size_t v2 = 1; while(v2 < height) v2 *= 2;
// Ratio for power of two version compared to actual version, to render the non power of two image with proper size.
// double u3 = (double)width / u2;
// double v3 = (double)height / v2;
// Make power of two version of the image.
std::vector<unsigned char> image2(u2 * v2 * 4);
for(size_t y = 0; y < height; y++)
for(size_t x = 0; x < width; x++)
for(size_t c = 0; c < 4; c++)
{
image2[4 * u2 * y + 4 * x + c] = image[4 * width * y + 4 * x + c];
}
// Enable the texture for OpenGL.
glEnable(GL_TEXTURE_2D);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_NEAREST = no smoothing
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 4, u2, v2, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image2[0]);
return 0;
}

View file

@ -1,16 +0,0 @@
//
// Texture.h
// interface
//
// Created by Yoz Work on 11/5/12.
//
//
#ifndef __interface__Texture__
#define __interface__Texture__
#include "InterfaceConfig.h"
int load_png_as_texture(char* filename);
#endif /* defined(__interface__texture__) */

View file

@ -11,6 +11,7 @@
#include <cstring>
#include <glm/glm.hpp>
#include <glm/gtc/noise.hpp>
#include <glm/gtc/quaternion.hpp>
#include <SharedUtil.h>
@ -65,81 +66,107 @@ float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf + render_yaw + head_yaw;
}
void render_vector(glm::vec3 * vec)
{
// Show edge of world
glDisable(GL_LIGHTING);
glColor4f(1.0, 1.0, 1.0, 1.0);
glLineWidth(1.0);
glBegin(GL_LINES);
// Draw axes
glColor3f(1,0,0);
glVertex3f(-1,0,0);
glVertex3f(1,0,0);
glColor3f(0,1,0);
glVertex3f(0,-1,0);
glVertex3f(0, 1, 0);
glColor3f(0,0,1);
glVertex3f(0,0,-1);
glVertex3f(0, 0, 1);
// Draw vector
glColor3f(1,1,1);
glVertex3f(0,0,0);
glVertex3f(vec->x, vec->y, vec->z);
// Draw marker dots for magnitude
glEnd();
float particleAttenuationQuadratic[] = { 0.0f, 0.0f, 2.0f }; // larger Z = smaller particles
float particleAttenuationConstant[] = { 1.0f, 0.0f, 0.0f };
glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, particleAttenuationQuadratic );
glEnable(GL_POINT_SMOOTH);
glPointSize(10.0);
glBegin(GL_POINTS);
glColor3f(1,0,0);
glVertex3f(vec->x,0,0);
glColor3f(0,1,0);
glVertex3f(0,vec->y,0);
glColor3f(0,0,1);
glVertex3f(0,0,vec->z);
glEnd();
glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, particleAttenuationConstant );
// Helper function returns the positive angle in degrees between two 3D vectors
float angleBetween(glm::vec3 * v1, glm::vec3 * v2) {
return acos((glm::dot(*v1, *v2)) / (glm::length(*v1) * glm::length(*v2))) * 180.f / PI;
}
void render_world_box()
{
// Draw a 3D vector floating in space
void drawVector(glm::vec3 * vector) {
glDisable(GL_LIGHTING);
glEnable(GL_POINT_SMOOTH);
glPointSize(3.0);
glLineWidth(2.0);
// Draw axes
glBegin(GL_LINES);
glColor3f(1,0,0);
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glColor3f(0,1,0);
glVertex3f(0,0,0);
glVertex3f(0, 1, 0);
glColor3f(0,0,1);
glVertex3f(0,0,0);
glVertex3f(0, 0, 1);
glEnd();
// Draw the vector itself
glBegin(GL_LINES);
glColor3f(1,1,1);
glVertex3f(0,0,0);
glVertex3f(vector->x, vector->y, vector->z);
glEnd();
// Draw spheres for magnitude
glPushMatrix();
glColor3f(1,0,0);
glTranslatef(vector->x, 0, 0);
glutSolidSphere(0.02, 10, 10);
glColor3f(0,1,0);
glTranslatef(-vector->x, vector->y, 0);
glutSolidSphere(0.02, 10, 10);
glColor3f(0,0,1);
glTranslatef(0, -vector->y, vector->z);
glutSolidSphere(0.02, 10, 10);
glPopMatrix();
}
// Render a 2D set of squares using perlin/fractal noise
void noiseTest(int w, int h) {
const float CELLS = 100;
float xStep = (float) w / CELLS;
float yStep = (float) h / CELLS;
glBegin(GL_QUADS);
for (float x = 0; x < (float)w; x += xStep) {
for (float y = 0; y < (float)h; y += yStep) {
// Generate a vector varying between 0-1 corresponding to the screen location
glm::vec2 position(x / (float) w, y / (float) h);
// Set the cell color using the noise value at that location
float color = glm::simplex(position);
glColor4f(color, color, color, 1.0);
glVertex2f(x, y);
glVertex2f(x + xStep, y);
glVertex2f(x + xStep, y + yStep);
glVertex2f(x, y + yStep);
}
}
glEnd();
}
void render_world_box() {
// Show edge of world
glDisable(GL_LIGHTING);
glColor4f(1.0, 1.0, 1.0, 1.0);
glLineWidth(1.0);
glBegin(GL_LINES);
glColor3f(1,0,0);
glVertex3f(0,0,0);
glVertex3f(WORLD_SIZE,0,0);
glColor3f(0,1,0);
glVertex3f(0,0,0);
glColor3f(1, 0, 0);
glVertex3f(0, 0, 0);
glVertex3f(WORLD_SIZE, 0, 0);
glColor3f(0, 1, 0);
glVertex3f(0, 0, 0);
glVertex3f(0, WORLD_SIZE, 0);
glColor3f(0,0,1);
glVertex3f(0,0,0);
glColor3f(0, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, WORLD_SIZE);
glEnd();
// Draw little marker dots along the axis
glEnable(GL_LIGHTING);
glPushMatrix();
glTranslatef(WORLD_SIZE,0,0);
glColor3f(1,0,0);
glutSolidSphere(0.125,10,10);
glTranslatef(WORLD_SIZE, 0, 0);
glColor3f(1, 0, 0);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0,WORLD_SIZE,0);
glColor3f(0,1,0);
glutSolidSphere(0.125,10,10);
glTranslatef(0, WORLD_SIZE, 0);
glColor3f(0, 1, 0);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,WORLD_SIZE);
glColor3f(0,0,1);
glutSolidSphere(0.125,10,10);
glTranslatef(0, 0, WORLD_SIZE);
glColor3f(0, 0, 1);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
}
@ -166,8 +193,7 @@ 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, float g, float b)
{
char const* string, float r, float g, float b) {
//
// Draws text on screen as stroked so it can be resized
//
@ -184,10 +210,7 @@ void drawtext(int x, int y, float scale, float rotate, float thick, int mono,
}
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec,
float r, float g, float b)
{
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, float r, float g, float b) {
//
// Draws text on screen as stroked so it can be resized
//
@ -202,18 +225,15 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
glLineWidth(thick);
glScalef(scale, scale, 1.0);
len = (int) strlen(vectext);
for (i = 0; i < len; i++)
{
for (i = 0; i < len; i++) {
if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i]));
else glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int(vectext[i]));
}
glPopMatrix();
}
void drawGroundPlaneGrid(float size)
{
void drawGroundPlaneGrid(float size) {
glColor3f( 0.4f, 0.5f, 0.3f );
glLineWidth(2.0);
@ -267,6 +287,51 @@ void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, f
}
void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition) {
glm::vec3 vectorToPosition(glm::normalize(position - cameraPosition));
glm::vec3 right = glm::cross(vectorToPosition, glm::vec3( 0.0f, 1.0f, 0.0f));
glm::vec3 up = glm::cross(right, vectorToPosition);
glBegin(GL_LINE_STRIP);
for (int i=0; i<numSides+1; i++) {
float r = ((float)i / (float)numSides) * PI * 2.0;
float s = radius * sin(r);
float c = radius * cos(r);
glVertex3f
(
position.x + right.x * s + up.x * c,
position.y + right.y * s + up.y * c,
position.z + right.z * s + up.z * c
);
}
glEnd();
}
void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides ) {
glm::vec3 perp1 = glm::vec3(surfaceNormal.y, surfaceNormal.z, surfaceNormal.x);
glm::vec3 perp2 = glm::vec3(surfaceNormal.z, surfaceNormal.x, surfaceNormal.y);
glBegin(GL_LINE_STRIP);
for (int i=0; i<numSides+1; i++) {
float r = ((float)i / (float)numSides) * PI * 2.0;
float s = radius * sin(r);
float c = radius * cos(r);
glVertex3f
(
position.x + perp1.x * s + perp2.x * c,
position.y + perp1.y * s + perp2.y * c,
position.z + perp1.z * s + perp2.z * c
);
}
glEnd();
}
void renderOrientationDirections( glm::vec3 position, Orientation orientation, float size ) {
glm::vec3 pRight = position + orientation.getRight() * size;
glm::vec3 pUp = position + orientation.getUp() * size;

View file

@ -33,13 +33,19 @@ float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float
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,
float r=1.0, float g=1.0, float b=1.0);
void noiseTest(int w, int h);
void drawVector(glm::vec3* vector);
float angleBetween(glm::vec3 * v1, glm::vec3 * v2);
double diffclock(timeval *clock1,timeval *clock2);
void drawGroundPlaneGrid(float size);
@ -48,6 +54,9 @@ void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, f
void renderOrientationDirections( glm::vec3 position, Orientation orientation, float size );
void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition);
void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int numSides );
class oTestCase {
public:

View file

@ -15,6 +15,7 @@
#include <fstream> // to load voxels from file
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <OctalCode.h>
#include <pthread.h>
#include "Log.h"
@ -41,7 +42,8 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
4,5,6, 4,6,7 }; // Z+ .
VoxelSystem::VoxelSystem() {
_voxelsInArrays = _voxelsUpdated = 0;
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
_alwaysRenderFullVBO = true;
_tree = new VoxelTree();
pthread_mutex_init(&_bufferWriteLock, NULL);
}
@ -58,7 +60,7 @@ VoxelSystem::~VoxelSystem() {
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
_tree->loadVoxelsFile(fileName, wantColorRandomizer);
copyWrittenDataToReadArrays();
setupNewVoxelsForDrawing();
}
void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) {
@ -98,24 +100,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
switch(command) {
case PACKET_HEADER_VOXEL_DATA:
{
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
// ask the VoxelTree to read the bitstream into the tree
_tree->readBitstreamToTree(voxelData, numBytes - 1);
if (_renderWarningsOn && _tree->getNodesChangedFromBitstream()) {
printLog("readBitstreamToTree()... getNodesChangedFromBitstream=%ld _tree->isDirty()=%s \n",
_tree->getNodesChangedFromBitstream(), (_tree->isDirty() ? "yes" : "no") );
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! readBitstreamToTree() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! readBitstreamToTree() took %lf milliseconds\n",elapsedmsec);
}
}
}
break;
case PACKET_HEADER_ERASE_VOXEL:
@ -138,7 +125,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
if (0==strcmp(command,(char*)"erase all")) {
printLog("got Z message == erase all\n");
_tree->eraseAllVoxels();
_voxelsInArrays = 0; // better way to do this??
_voxelsInReadArrays = _voxelsInWriteArrays = 0; // better way to do this??
}
if (0==strcmp(command,(char*)"add scene")) {
printLog("got Z message == add scene - NOT SUPPORTED ON INTERFACE\n");
@ -153,16 +140,29 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
}
void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
double start = usecTimestampNow();
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished);
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000.0;
if (sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed,SIXTY_FPS_IN_MILLISECONDS)) {
return; // bail early, it hasn't been long enough since the last time we ran
}
double sinceLastViewCulling = (start - _lastViewCulling) / 1000.0;
// If the view frustum has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= VIEW_CULLING_RATE_IN_MILLISECONDS) && hasViewChanged()) {
_lastViewCulling = start;
removeOutOfView();
}
if (_tree->isDirty()) {
PerformanceWarning warn(_renderWarningsOn, "calling... newTreeToArrays()");
_callsToTreesToArrays++;
if (_alwaysRenderFullVBO) {
_voxelsInWriteArrays = 0; // reset our VBO
}
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
} else {
@ -172,78 +172,88 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
_voxelsDirty=true;
}
if (_voxelsDirty) {
// copy the newly written data to the arrays designated for reading
copyWrittenDataToReadArrays();
}
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
copyWrittenDataToReadArrays();
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! newTreeToArrays() took %lf seconds %ld voxels updated\n", elapsedsec, _voxelsUpdated);
} else {
printLog("WARNING! newTreeToArrays() took %lf milliseconds %ld voxels updated\n", elapsedmsec, _voxelsUpdated);
}
}
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
}
void VoxelSystem::copyWrittenDataToReadArrays() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()"); // would like to include _voxelsInArrays, _voxelsUpdated
if (_voxelsDirty && _voxelsUpdated) {
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock);
int bytesOfVertices = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
int bytesOfColors = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices);
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
_voxelsInReadArrays = _voxelsInWriteArrays;
pthread_mutex_unlock(&_bufferWriteLock);
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! copyWrittenDataToReadArrays() took %lf seconds for %ld voxels %ld updated\n",
elapsedsec, _voxelsInArrays, _voxelsUpdated);
} else {
printLog("WARNING! copyWrittenDataToReadArrays() took %lf milliseconds for %ld voxels %ld updated\n",
elapsedmsec, _voxelsInArrays, _voxelsUpdated);
}
}
}
int VoxelSystem::newTreeToArrays(VoxelNode* node) {
assert(_viewFrustum); // you must set up _viewFrustum before calling this
int voxelsUpdated = 0;
float distanceToNode = node->distanceToCamera(*_viewFrustum);
float boundary = boundaryDistanceForRenderLevel(*node->octalCode + 1);
float childBoundary = boundaryDistanceForRenderLevel(*node->octalCode + 2);
bool inBoundary = (distanceToNode <= boundary);
bool inChildBoundary = (distanceToNode <= childBoundary);
bool shouldRender = node->isColored() && ((node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary));
bool shouldRender = false; // assume we don't need to render it
// if it's colored, we might need to render it!
if (node->isColored()) {
float distanceToNode = node->distanceToCamera(*_viewFrustum);
float boundary = boundaryDistanceForRenderLevel(node->getLevel());
float childBoundary = boundaryDistanceForRenderLevel(node->getLevel() + 1);
bool inBoundary = (distanceToNode <= boundary);
bool inChildBoundary = (distanceToNode <= childBoundary);
shouldRender = (node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary);
}
node->setShouldRender(shouldRender);
// let children figure out their renderness
for (int i = 0; i < 8; i++) {
if (node->children[i]) {
voxelsUpdated += newTreeToArrays(node->children[i]);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (node->getChildAtIndex(i)) {
voxelsUpdated += newTreeToArrays(node->getChildAtIndex(i));
}
}
if (_alwaysRenderFullVBO) {
voxelsUpdated += newway__updateNodeInArray(node);
} else {
voxelsUpdated += oldway__updateNodeInArray(node);
}
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated;
}
int VoxelSystem::newway__updateNodeInArray(VoxelNode* node) {
if (node->getShouldRender()) {
glm::vec3 startVertex = node->getCorner();
float voxelScale = node->getScale();
glBufferIndex nodeIndex = _voxelsInWriteArrays;
// populate the array with points for the 8 vertices
// and RGB color for each added vertex
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
*(writeColorsAt +j) = node->getColor()[j % 3];
}
_voxelsInWriteArrays++; // our know vertices in the arrays
return 1; // rendered
}
return 0; // not-rendered
}
int VoxelSystem::oldway__updateNodeInArray(VoxelNode* node) {
// Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays... for us
if (node->isDirty() && (shouldRender || node->isKnownBufferIndex())) {
if (node->isDirty() && (node->getShouldRender() || node->isKnownBufferIndex())) {
glm::vec3 startVertex;
float voxelScale = 0;
// If we're should render, use our legit location and scale,
if (node->getShouldRender()) {
copyFirstVertexForCode(node->octalCode, (float*)&startVertex);
voxelScale = (1 / powf(2, *node->octalCode));
startVertex = node->getCorner();
voxelScale = node->getScale();
} else {
// if we shouldn't render then set out location to some infinitely distant location,
// and our scale as infinitely small
@ -256,7 +266,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
if (node->isKnownBufferIndex()) {
nodeIndex = node->getBufferIndex();
} else {
nodeIndex = _voxelsInArrays;
nodeIndex = _voxelsInWriteArrays;
}
_voxelDirtyArray[nodeIndex] = true;
@ -271,12 +281,11 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
}
if (!node->isKnownBufferIndex()) {
node->setBufferIndex(nodeIndex);
_voxelsInArrays++; // our know vertices in the arrays
_voxelsInWriteArrays++; // our know vertices in the arrays
}
voxelsUpdated++;
return 1; // updated!
}
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated;
return 0; // not-updated
}
VoxelSystem* VoxelSystem::clone() const {
@ -290,10 +299,13 @@ void VoxelSystem::init() {
_callsToTreesToArrays = 0;
_setupNewVoxelsForDrawingLastFinished = 0;
_setupNewVoxelsForDrawingLastElapsed = 0;
_lastViewCulling = 0;
// When we change voxels representations in the arrays, we'll update this
_voxelsDirty = false;
_voxelsInArrays = 0;
_voxelsInWriteArrays = 0;
_voxelsInReadArrays = 0;
_unusedArraySpace = 0;
// we will track individual dirty sections with this array of bools
_voxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
@ -362,59 +374,60 @@ void VoxelSystem::init() {
}
void VoxelSystem::updateVBOs() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "updateVBOs()"); // would like to include _callsToTreesToArrays
if (_voxelsDirty) {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInArrays; i++) {
if (!inSegment) {
if (_voxelDirtyArray[i]) {
segmentStart = i;
inSegment = true;
_voxelDirtyArray[i] = false; // consider us clean!
}
} else {
if (!_voxelDirtyArray[i] || (i == (_voxelsInArrays - 1)) ) {
segmentEnd = i;
inSegment = false;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
if (_alwaysRenderFullVBO) {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = _voxelsInWriteArrays;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
} else {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
if (!inSegment) {
if (_voxelDirtyArray[i]) {
segmentStart = i;
inSegment = true;
_voxelDirtyArray[i] = false; // consider us clean!
}
} else {
if (!_voxelDirtyArray[i] || (i == (_voxelsInWriteArrays - 1)) ) {
segmentEnd = i;
inSegment = false;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
}
}
}
}
_voxelsDirty = false;
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! updateVBOs() took %lf seconds after %d calls to newTreeToArrays()\n",
elapsedsec, _callsToTreesToArrays);
} else {
printLog("WARNING! updateVBOs() took %lf milliseconds after %d calls to newTreeToArrays()\n",
elapsedmsec, _callsToTreesToArrays);
}
} else {
printLog("WARNING! updateVBOs() called after %d calls to newTreeToArrays()\n",_callsToTreesToArrays);
}
}
_callsToTreesToArrays = 0; // clear it
}
void VoxelSystem::render() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "render()");
glPushMatrix();
updateVBOs();
// tell OpenGL where to find vertex and color information
@ -434,7 +447,7 @@ void VoxelSystem::render() {
// draw the number of voxels we have
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
glDrawElements(GL_TRIANGLES, 36 * _voxelsInArrays, GL_UNSIGNED_INT, 0);
glDrawElements(GL_TRIANGLES, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
// deactivate vertex and color arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);
@ -447,23 +460,13 @@ void VoxelSystem::render() {
// scale back down to 1 so heads aren't massive
glPopMatrix();
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! render() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! render() took %lf milliseconds\n",elapsedmsec);
}
}
}
int VoxelSystem::_nodeCount = 0;
void VoxelSystem::killLocalVoxels() {
_tree->eraseAllVoxels();
_voxelsInArrays = 0; // better way to do this??
_voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this??
//setupNewVoxelsForDrawing();
}
@ -584,4 +587,33 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) {
setupNewVoxelsForDrawing();
}
// "Remove" voxels from the tree that are not in view. We don't actually delete them,
// we remove them from the tree and place them into a holding area for later deletion
bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
VoxelSystem* thisVoxelSystem = (VoxelSystem*) extraData;
_nodeCount++;
// Need to operate on our child nodes, so we can remove them
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
if (childNode && !childNode->isInView(*thisVoxelSystem->_viewFrustum)) {
node->removeChildAtIndex(i);
thisVoxelSystem->_removedVoxels.insert(childNode);
}
}
return true; // keep going!
}
bool VoxelSystem::hasViewChanged() {
bool result = false; // assume the best
if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) {
result = true;
_lastKnowViewFrustum = *_viewFrustum; // save last known
}
return result;
}
void VoxelSystem::removeOutOfView() {
PerformanceWarning warn(_renderWarningsOn, "removeOutOfView()"); // would like to include removedCount, _nodeCount, _removedVoxels.count()
_nodeCount = 0;
_tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)this);
}

View file

@ -37,7 +37,7 @@ public:
void render();
unsigned long getVoxelsUpdated() const {return _voxelsUpdated;};
unsigned long getVoxelsRendered() const {return _voxelsInArrays;};
unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;};
void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; };
void setCamera(Camera* newCamera) { _camera = newCamera; };
@ -61,9 +61,13 @@ public:
void killLocalVoxels();
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
bool getRenderPipelineWarnings() const { return _renderWarningsOn; };
void removeOutOfView();
bool hasViewChanged();
private:
int _callsToTreesToArrays;
VoxelNodeBag _removedVoxels;
bool _renderWarningsOn;
// Operation functions for tree recursion methods
@ -74,6 +78,10 @@ private:
static bool falseColorizeInViewOperation(VoxelNode* node, void* extraData);
static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData);
static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData);
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
int newway__updateNodeInArray(VoxelNode* node);
int oldway__updateNodeInArray(VoxelNode* node);
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
static float _maxDistance;
@ -88,11 +96,15 @@ private:
GLubyte* _writeColorsArray;
bool* _voxelDirtyArray;
unsigned long _voxelsUpdated;
unsigned long _voxelsInArrays;
unsigned long _voxelsInWriteArrays;
unsigned long _voxelsInReadArrays;
unsigned long _unusedArraySpace;
bool _alwaysRenderFullVBO;
double _setupNewVoxelsForDrawingLastElapsed;
double _setupNewVoxelsForDrawingLastFinished;
double _lastViewCulling;
GLuint _vboVerticesID;
GLuint _vboNormalsID;
@ -101,6 +113,7 @@ private:
pthread_mutex_t _bufferWriteLock;
ViewFrustum* _viewFrustum;
ViewFrustum _lastKnowViewFrustum;
int newTreeToArrays(VoxelNode *currentNode);
void setupNewVoxelsForDrawing();

View file

@ -14,14 +14,6 @@
//
// Welcome Aboard!
//
//
// Keyboard Commands:
//
// / = toggle stats display
// spacebar = reset gyros/head position
// h = render Head facing yourself (mirror)
// l = show incoming gyro levels
//
#include "InterfaceConfig.h"
#include <math.h>
@ -69,7 +61,6 @@
#include "Camera.h"
#include "Avatar.h"
#include "Texture.h"
#include <AgentList.h>
#include <AgentTypes.h>
#include "VoxelSystem.h"
@ -443,17 +434,22 @@ void updateAvatar(float frametime) {
myAvatar.setCameraAspectRatio(::viewFrustum.getAspectRatio());
myAvatar.setCameraNearClip(::viewFrustum.getNearClip());
myAvatar.setCameraFarClip(::viewFrustum.getFarClip());
// Send my stream of head/hand data to the avatar mixer and voxel server
unsigned char broadcastString[200];
*broadcastString = PACKET_HEADER_HEAD_DATA;
int broadcastBytes = myAvatar.getBroadcastData(broadcastString + 1);
broadcastBytes++;
AgentList* agentList = AgentList::getInstance();
const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL, AGENT_TYPE_AVATAR_MIXER};
AgentList::getInstance()->broadcastToAgents(broadcastString, broadcastBytes, broadcastReceivers, 2);
if (agentList->getOwnerID() != UNKNOWN_AGENT_ID) {
// if I know my ID, send head/hand data to the avatar mixer and voxel server
unsigned char broadcastString[200];
unsigned char* endOfBroadcastStringWrite = broadcastString;
*(endOfBroadcastStringWrite++) = PACKET_HEADER_HEAD_DATA;
endOfBroadcastStringWrite += packAgentId(endOfBroadcastStringWrite, agentList->getOwnerID());
endOfBroadcastStringWrite += myAvatar.getBroadcastData(endOfBroadcastStringWrite);
const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL, AGENT_TYPE_AVATAR_MIXER};
AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers));
}
// If I'm in paint mode, send a voxel out to VOXEL server agents.
if (::paintOn) {
@ -461,9 +457,9 @@ void updateAvatar(float frametime) {
glm::vec3 avatarPos = myAvatar.getPosition();
// For some reason, we don't want to flip X and Z here.
::paintingVoxel.x = avatarPos.x/10.0;
::paintingVoxel.y = avatarPos.y/10.0;
::paintingVoxel.z = avatarPos.z/10.0;
::paintingVoxel.x = avatarPos.x / 10.0;
::paintingVoxel.y = avatarPos.y / 10.0;
::paintingVoxel.z = avatarPos.z / 10.0;
unsigned char* bufferOut;
int sizeOut;
@ -704,15 +700,14 @@ void displaySide(Camera& whichCamera) {
float sphereRadius = 0.25f;
glColor3f(1,0,0);
glPushMatrix();
glutSolidSphere( sphereRadius, 15, 15 );
glutSolidSphere(sphereRadius, 15, 15);
glPopMatrix();
//draw a grid ground plane....
drawGroundPlaneGrid(10.f);
// Draw voxels
if ( showingVoxels )
{
if (showingVoxels) {
voxels.render();
}
@ -722,7 +717,7 @@ void displaySide(Camera& whichCamera) {
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
Avatar *avatar = (Avatar *)agent->getLinkedData();
avatar->render(0);
avatar->render(0, ::myCamera.getPosition());
}
}
agentList->unlock();
@ -734,7 +729,7 @@ void displaySide(Camera& whichCamera) {
if (::frustumOn) renderViewFrustum(::viewFrustum);
//Render my own avatar
myAvatar.render(::lookingInMirror);
myAvatar.render(::lookingInMirror, ::myCamera.getPosition());
glPopMatrix();
}
@ -905,6 +900,8 @@ void displayOverlay() {
audioScope.render();
#endif
// noiseTest(WIDTH, HEIGHT);
if (displayHeadMouse && !::lookingInMirror && statsOn) {
// Display small target box at center or head mouse target that can also be used to measure LOD
glColor3f(1.0, 1.0, 1.0);
@ -996,15 +993,15 @@ void display(void)
glMateriali(GL_FRONT, GL_SHININESS, 96);
// camera settings
if ( ::lookingInMirror ) {
if (::lookingInMirror) {
// set the camera to looking at my own face
myCamera.setTargetPosition ( myAvatar.getHeadPosition() );
myCamera.setTargetYaw ( myAvatar.getBodyYaw() - 180.0f ); // 180 degrees from body yaw
myCamera.setPitch ( 0.0 );
myCamera.setRoll ( 0.0 );
myCamera.setUpShift ( 0.0 );
myCamera.setDistance ( 0.2 );
myCamera.setTightness ( 100.0f );
myCamera.setTargetPosition (myAvatar.getHeadPosition());
myCamera.setTargetYaw (myAvatar.getBodyYaw() - 180.0f); // 180 degrees from body yaw
myCamera.setPitch (0.0);
myCamera.setRoll (0.0);
myCamera.setUpShift (0.0);
myCamera.setDistance (0.2);
myCamera.setTightness (100.0f);
} else {
//float firstPersonPitch = 20.0f;
@ -1022,36 +1019,24 @@ void display(void)
float thirdPersonDistance = 1.2f;
float thirdPersonTightness = 8.0f;
if ( USING_FIRST_PERSON_EFFECT ) {
if (USING_FIRST_PERSON_EFFECT) {
float ff = 0.0;
float min = 0.1;
float max = 0.5;
if ( myAvatar.getIsNearInteractingOther()){
if ( myAvatar.getSpeed() < max ) {
if (myAvatar.getIsNearInteractingOther()){
if (myAvatar.getSpeed() < max) {
float s = (myAvatar.getSpeed()- min)/max ;
ff = 1.0 - s;
}
}
/*
if ( ff < 0.8 ) {
myAvatar.setDisplayingHead( true );
} else {
myAvatar.setDisplayingHead( false );
}
*/
//printf( "ff = %f\n", ff );
myCamera.setPitch ( thirdPersonPitch + ff * ( firstPersonPitch - thirdPersonPitch ));
myCamera.setUpShift ( thirdPersonUpShift + ff * ( firstPersonUpShift - thirdPersonUpShift ));
myCamera.setDistance ( thirdPersonDistance + ff * ( firstPersonDistance - thirdPersonDistance ));
myCamera.setTightness ( thirdPersonTightness + ff * ( firstPersonTightness - thirdPersonTightness ));
myCamera.setPitch (thirdPersonPitch + ff * (firstPersonPitch - thirdPersonPitch ));
myCamera.setUpShift (thirdPersonUpShift + ff * (firstPersonUpShift - thirdPersonUpShift ));
myCamera.setDistance (thirdPersonDistance + ff * (firstPersonDistance - thirdPersonDistance ));
myCamera.setTightness (thirdPersonTightness + ff * (firstPersonTightness - thirdPersonTightness));
// this version uses a ramp-up/ramp-down timer in the camera to determine shift between first and thirs-person view
/*
if ( myAvatar.getSpeed() < 0.02 ) {
@ -1085,13 +1070,23 @@ void display(void)
myCamera.setTightness(thirdPersonTightness);
}
myCamera.setTargetPosition( myAvatar.getHeadPosition() );
myCamera.setTargetYaw ( myAvatar.getBodyYaw() );
myCamera.setRoll ( 0.0 );
myCamera.setTargetPosition(myAvatar.getHeadPosition());
myCamera.setTargetYaw (myAvatar.getBodyYaw());
myCamera.setRoll (0.0);
}
// important...
myCamera.update( 1.f/FPS );
// Render anything (like HUD items) that we want to be in 3D but not in worldspace
const float HUD_Z_OFFSET = -5.f;
glPushMatrix();
glm::vec3 test(0.5, 0.5, 0.5);
glTranslatef(1, 1, HUD_Z_OFFSET);
drawVector(&test);
glPopMatrix();
// Note: whichCamera is used to pick between the normal camera myCamera for our
// main camera, vs, an alternate camera. The alternate camera we support right now
@ -1107,11 +1102,11 @@ void display(void)
if (::viewFrustumFromOffset && ::frustumOn) {
// set the camera to third-person view but offset so we can see the frustum
viewFrustumOffsetCamera.setTargetYaw( ::viewFrustumOffsetYaw + myAvatar.getBodyYaw() );
viewFrustumOffsetCamera.setPitch ( ::viewFrustumOffsetPitch );
viewFrustumOffsetCamera.setRoll ( ::viewFrustumOffsetRoll );
viewFrustumOffsetCamera.setUpShift ( ::viewFrustumOffsetUp );
viewFrustumOffsetCamera.setDistance ( ::viewFrustumOffsetDistance );
viewFrustumOffsetCamera.setTargetYaw(::viewFrustumOffsetYaw + myAvatar.getBodyYaw() );
viewFrustumOffsetCamera.setPitch (::viewFrustumOffsetPitch );
viewFrustumOffsetCamera.setRoll (::viewFrustumOffsetRoll );
viewFrustumOffsetCamera.setUpShift (::viewFrustumOffsetUp );
viewFrustumOffsetCamera.setDistance (::viewFrustumOffsetDistance);
viewFrustumOffsetCamera.update(1.f/FPS);
whichCamera = viewFrustumOffsetCamera;
}
@ -1121,11 +1116,11 @@ void display(void)
// or could be viewFrustumOffsetCamera if in offset mode
// I changed the ordering here - roll is FIRST (JJV)
glRotatef ( whichCamera.getRoll(), IDENTITY_FRONT.x, IDENTITY_FRONT.y, IDENTITY_FRONT.z );
glRotatef ( whichCamera.getPitch(), IDENTITY_RIGHT.x, IDENTITY_RIGHT.y, IDENTITY_RIGHT.z );
glRotatef ( 180.0 - whichCamera.getYaw(), IDENTITY_UP.x, IDENTITY_UP.y, IDENTITY_UP.z );
glRotatef ( whichCamera.getRoll(), IDENTITY_FRONT.x, IDENTITY_FRONT.y, IDENTITY_FRONT.z);
glRotatef ( whichCamera.getPitch(), IDENTITY_RIGHT.x, IDENTITY_RIGHT.y, IDENTITY_RIGHT.z);
glRotatef (180.0 - whichCamera.getYaw(), IDENTITY_UP.x, IDENTITY_UP.y, IDENTITY_UP.z );
glTranslatef( -whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z );
glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z);
if (::oculusOn) {
displayOculus(whichCamera);
@ -1397,8 +1392,7 @@ void initMenu() {
menuColumnDebug->addRow("Show TRUE Colors", doTrueVoxelColors);
}
void testPointToVoxel()
{
void testPointToVoxel() {
float y=0;
float z=0;
float s=0.1;
@ -1450,8 +1444,7 @@ void setupPaintingVoxel() {
shiftPaintingColor();
}
void addRandomSphere(bool wantColorRandomizer)
{
void addRandomSphere(bool wantColorRandomizer) {
float r = randFloatInRange(0.05,0.1);
float xc = randFloatInRange(r,(1-r));
float yc = randFloatInRange(r,(1-r));
@ -1468,7 +1461,6 @@ void addRandomSphere(bool wantColorRandomizer)
voxels.createSphere(r,xc,yc,zc,s,solid,wantColorRandomizer);
}
const float KEYBOARD_YAW_RATE = 0.8;
const float KEYBOARD_PITCH_RATE = 0.6;
const float KEYBOARD_STRAFE_RATE = 0.03;
@ -1522,7 +1514,6 @@ void specialkey(int k, int x, int y) {
}
}
void keyUp(unsigned char k, int x, int y) {
if (::chatEntryOn) {
myAvatar.setKeyState(NO_KEY_DOWN);
@ -1537,8 +1528,7 @@ void keyUp(unsigned char k, int x, int y) {
if (k == 'd') myAvatar.setDriveKeys(ROT_RIGHT, 0);
}
void key(unsigned char k, int x, int y)
{
void key(unsigned char k, int x, int y) {
if (::chatEntryOn) {
if (chatEntry.key(k)) {
myAvatar.setKeyState(k == '\b' || k == 127 ? // backspace or delete
@ -1687,18 +1677,16 @@ void idle(void) {
// update behaviors for avatar hand movement: handControl takes mouse values as input,
// and gives back 3D values modulated for smooth transitioning between interaction modes.
handControl.update( mouseX, mouseY );
myAvatar.setHandMovementValues( handControl.getValues() );
handControl.update(mouseX, mouseY);
myAvatar.setHandMovementValues(handControl.getValues());
// tell my avatar if the mouse is being pressed...
if ( mousePressed == 1 ) {
myAvatar.setMousePressed( true );
} else {
myAvatar.setMousePressed( false );
}
if (mousePressed) {
myAvatar.setMousePressed(mousePressed);
}
// walking triggers the handControl to stop
if ( myAvatar.getMode() == AVATAR_MODE_WALKING ) {
if (myAvatar.getMode() == AVATAR_MODE_WALKING) {
handControl.stop();
}
@ -1789,10 +1777,6 @@ void reshape(int width, int height) {
glLoadIdentity();
}
//Find and return the gravity vector at this location
glm::vec3 getGravity(glm::vec3 pos) {
//
@ -1851,8 +1835,7 @@ void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) {
}
#endif
int main(int argc, const char * argv[])
{
int main(int argc, const char * argv[]) {
voxels.setViewFrustum(&::viewFrustum);
shared_lib::printLog = & ::printLog;
@ -1892,7 +1875,7 @@ int main(int argc, const char * argv[])
#ifdef _WIN32
WSADATA WsaData;
int wsaresult = WSAStartup( MAKEWORD(2,2), &WsaData );
int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
#endif
// start the agentList threads

View file

@ -128,8 +128,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
// called on the other agents - assigns it to my views of the others
int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
// increment to push past the packet header
sourceBuffer++;
// increment to push past the packet header and agent ID
sourceBuffer += sizeof(PACKET_HEADER_HEAD_DATA) + sizeof(uint16_t);
unsigned char* startPosition = sourceBuffer;

View file

@ -62,8 +62,9 @@ AgentList::AgentList(char newOwnerType, unsigned int newSocketListenPort) :
_agentBuckets(),
_numAgents(0),
agentSocket(newSocketListenPort),
ownerType(newOwnerType),
_ownerType(newOwnerType),
socketListenPort(newSocketListenPort),
_ownerID(UNKNOWN_AGENT_ID),
lastAgentId(0) {
pthread_mutex_init(&mutex, 0);
}
@ -81,10 +82,6 @@ UDPSocket& AgentList::getAgentSocket() {
return agentSocket;
}
char AgentList::getOwnerType() {
return ownerType;
}
unsigned int AgentList::getSocketListenPort() {
return socketListenPort;
}
@ -92,7 +89,7 @@ unsigned int AgentList::getSocketListenPort() {
void AgentList::processAgentData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
switch (((char *)packetData)[0]) {
case PACKET_HEADER_DOMAIN: {
updateList(packetData, dataBytes);
processDomainServerList(packetData, dataBytes);
break;
}
case PACKET_HEADER_PING: {
@ -203,7 +200,7 @@ void AgentList::increaseAgentId() {
++lastAgentId;
}
int AgentList::updateList(unsigned char *packetData, size_t dataBytes) {
int AgentList::processDomainServerList(unsigned char *packetData, size_t dataBytes) {
int readAgents = 0;
char agentType;
@ -218,14 +215,17 @@ int AgentList::updateList(unsigned char *packetData, size_t dataBytes) {
unsigned char *readPtr = packetData + 1;
unsigned char *startPtr = packetData;
while((readPtr - startPtr) < dataBytes) {
while((readPtr - startPtr) < dataBytes - sizeof(uint16_t)) {
agentType = *readPtr++;
readPtr += unpackAgentId(readPtr, (uint16_t *)&agentId);
readPtr += unpackSocket(readPtr, (sockaddr *)&agentPublicSocket);
readPtr += unpackSocket(readPtr, (sockaddr *)&agentLocalSocket);
addOrUpdateAgent((sockaddr *)&agentPublicSocket, (sockaddr *)&agentLocalSocket, agentType, agentId);
}
}
// read out our ID from the packet
unpackAgentId(readPtr, &_ownerID);
return readAgents;
}

View file

@ -31,6 +31,8 @@ extern char DOMAIN_HOSTNAME[];
extern char DOMAIN_IP[100]; // IP Address will be re-set by lookup on startup
extern const int DOMAINSERVER_PORT;
const int UNKNOWN_AGENT_ID = -1;
class AgentListIterator;
class AgentList {
@ -56,7 +58,7 @@ public:
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
int updateList(unsigned char *packetData, size_t dataBytes);
int processDomainServerList(unsigned char *packetData, size_t dataBytes);
Agent* agentWithAddress(sockaddr *senderAddress);
Agent* agentWithID(uint16_t agentID);
@ -70,9 +72,13 @@ public:
int updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes);
void broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes);
char getOwnerType();
unsigned int getSocketListenPort();
char getOwnerType() const { return _ownerType; }
uint16_t getOwnerID() const { return _ownerID; }
void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; }
Agent* soloAgentOfType(char agentType);
void startSilentAgentRemovalThread();
@ -96,8 +102,9 @@ private:
Agent** _agentBuckets[MAX_NUM_AGENTS / AGENTS_PER_BUCKET];
int _numAgents;
UDPSocket agentSocket;
char ownerType;
char _ownerType;
unsigned int socketListenPort;
uint16_t _ownerID;
uint16_t lastAgentId;
pthread_t removeSilentAgentsThread;
pthread_t checkInWithDomainServerThread;

View file

@ -97,6 +97,8 @@ void AudioRingBuffer::setBearing(float newBearing) {
bearing = newBearing;
}
const int AGENT_LOOPBACK_MODIFIER = 307;
int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
if (numBytes > (bufferLengthSamples * sizeof(int16_t))) {
@ -111,7 +113,19 @@ int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
attenuationRatio = attenuationByte / 255.0f;
memcpy(&bearing, dataPtr, sizeof(float));
dataPtr += sizeof(float);
dataPtr += sizeof(bearing);
if (bearing > 180 || bearing < -180) {
// we were passed an invalid bearing because this agent wants loopback (pressed the H key)
_shouldLoopbackForAgent = true;
// correct the bearing
bearing = bearing > 0
? bearing - AGENT_LOOPBACK_MODIFIER
: bearing + AGENT_LOOPBACK_MODIFIER;
} else {
_shouldLoopbackForAgent = false;
}
sourceBuffer = dataPtr;
}

View file

@ -36,6 +36,8 @@ public:
void setAttenuationRatio(float newAttenuation);
float getBearing();
void setBearing(float newBearing);
bool shouldLoopbackForAgent() const { return _shouldLoopbackForAgent; }
short diffLastWriteNextOutput();
private:
@ -49,6 +51,7 @@ private:
int16_t *buffer;
bool started;
bool _shouldBeAddedToMix;
bool _shouldLoopbackForAgent;
};
#endif /* defined(__interface__AudioRingBuffer__) */

View file

@ -13,19 +13,20 @@
#ifndef hifi_PacketHeaders_h
#define hifi_PacketHeaders_h
const char PACKET_HEADER_DOMAIN = 'D';
const char PACKET_HEADER_PING = 'P';
const char PACKET_HEADER_PING_REPLY = 'R';
const char PACKET_HEADER_HEAD_DATA = 'H';
const char PACKET_HEADER_Z_COMMAND = 'Z';
const char PACKET_HEADER_INJECT_AUDIO = 'I';
const char PACKET_HEADER_SET_VOXEL = 'S';
const char PACKET_HEADER_ERASE_VOXEL = 'E';
const char PACKET_HEADER_VOXEL_DATA = 'V';
const char PACKET_HEADER_BULK_AVATAR_DATA = 'X';
const char PACKET_HEADER_TRANSMITTER_DATA = 't';
const char PACKET_HEADER_ENVIRONMENT_DATA = 'e';
const char PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
const char PACKET_HEADER_DOMAIN_RFD = 'C';
typedef char PACKET_HEADER;
const PACKET_HEADER PACKET_HEADER_DOMAIN = 'D';
const PACKET_HEADER PACKET_HEADER_PING = 'P';
const PACKET_HEADER PACKET_HEADER_PING_REPLY = 'R';
const PACKET_HEADER PACKET_HEADER_HEAD_DATA = 'H';
const PACKET_HEADER PACKET_HEADER_Z_COMMAND = 'Z';
const PACKET_HEADER PACKET_HEADER_INJECT_AUDIO = 'I';
const PACKET_HEADER PACKET_HEADER_SET_VOXEL = 'S';
const PACKET_HEADER PACKET_HEADER_ERASE_VOXEL = 'E';
const PACKET_HEADER PACKET_HEADER_VOXEL_DATA = 'V';
const PACKET_HEADER PACKET_HEADER_BULK_AVATAR_DATA = 'X';
const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA = 't';
const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e';
const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
const PACKET_HEADER PACKET_HEADER_DOMAIN_RFD = 'C';
#endif

View file

@ -103,3 +103,20 @@ int PerfStat::DumpStats(char** array) {
return lineCount;
}
// Destructor handles recording all of our stats
PerformanceWarning::~PerformanceWarning() {
double end = usecTimestampNow();
double elapsedmsec = (end - _start) / 1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - _start) / 1000000.0;
printLog("WARNING! %s took %lf seconds\n", _message, elapsedsec);
} else {
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
}
}
};

View file

@ -13,6 +13,7 @@
#define __hifi__PerfStat__
#include <stdint.h>
#include "SharedUtil.h"
#ifdef _WIN32
#define snprintf _snprintf
@ -81,5 +82,19 @@ public:
typedef std::map<std::string,PerfStatHistory,std::less<std::string> >::iterator PerfStatMapItr;
class PerformanceWarning {
private:
double _start;
const char* _message;
bool _renderWarningsOn;
public:
PerformanceWarning(bool renderWarnings, const char* message) :
_start(usecTimestampNow()),
_message(message),
_renderWarningsOn(renderWarnings) { };
~PerformanceWarning();
};
#endif /* defined(__hifi__PerfStat__) */

View file

@ -0,0 +1,28 @@
//
// Stacktrace.cpp
// hifi
//
// Created by Stephen Birarda on 5/6/13.
//
//
#include <signal.h>
#include <stdio.h>
#include <execinfo.h>
#include <cstdlib>
#include "Stacktrace.h"
const int NUMBER_OF_STACK_ENTRIES = 20;
void printStacktrace(int signal) {
void* array[NUMBER_OF_STACK_ENTRIES];
// get void*'s for all entries on the stack
size_t size = backtrace(array, NUMBER_OF_STACK_ENTRIES);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", signal);
backtrace_symbols_fd(array, size, 2);
exit(1);
}

View file

@ -0,0 +1,16 @@
//
// Stacktrace.h
// hifi
//
// Created by Stephen Birarda on 5/6/13.
//
//
#ifndef __hifi__Stacktrace__
#define __hifi__Stacktrace__
#include <iostream>
void printStacktrace(int signal);
#endif /* defined(__hifi__Stacktrace__) */

18
libraries/voxels/src/AABox.cpp Executable file → Normal file
View file

@ -14,8 +14,9 @@
void AABox::scale(float scale) {
_corner = _corner*scale;
_size = _size*scale;
_corner = _corner * scale;
_size = _size * scale;
_center = _center * scale;
}
@ -36,6 +37,7 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
_size.z = -size.z;
_corner.z -= _size.z;
}
_center = _corner + (_size * 0.5f);
}
glm::vec3 AABox::getVertexP(const glm::vec3 &normal) const {
@ -101,15 +103,15 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
}
// check each axis
float axisDistance;
if (findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z) ||
findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z)) ||
(findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z) ||
findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z)) ||
(findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x)) {
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
distance = axisDistance;
return true;
}

View file

@ -34,12 +34,14 @@ public:
const glm::vec3& getCorner() const { return _corner; };
const glm::vec3& getSize() const { return _size; };
const glm::vec3& getCenter() const { return _center; };
bool contains(const glm::vec3& point) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
private:
glm::vec3 _corner;
glm::vec3 _center;
glm::vec3 _size;
};

View file

@ -224,6 +224,53 @@ int ViewFrustum::boxInFrustum(const AABox& box) const {
}
return(result);
}
bool ViewFrustum::matches(const ViewFrustum& compareTo) const {
bool debug = false;
bool result = compareTo._position == _position &&
compareTo._direction == _direction &&
compareTo._up == _up &&
compareTo._right == _right &&
compareTo._fieldOfView == _fieldOfView &&
compareTo._aspectRatio == _aspectRatio &&
compareTo._nearClip == _nearClip &&
compareTo._farClip == _farClip;
if (!result && debug) {
printLog("ViewFrustum::matches()... result=%s\n", (result ? "yes" : "no"));
printLog("%s -- compareTo._position=%f,%f,%f _position=%f,%f,%f\n",
(compareTo._position == _position ? "MATCHES " : "NO MATCH"),
compareTo._position.x, compareTo._position.y, compareTo._position.z,
_position.x, _position.y, _position.z );
printLog("%s -- compareTo._direction=%f,%f,%f _direction=%f,%f,%f\n",
(compareTo._direction == _direction ? "MATCHES " : "NO MATCH"),
compareTo._direction.x, compareTo._direction.y, compareTo._direction.z,
_direction.x, _direction.y, _direction.z );
printLog("%s -- compareTo._up=%f,%f,%f _up=%f,%f,%f\n",
(compareTo._up == _up ? "MATCHES " : "NO MATCH"),
compareTo._up.x, compareTo._up.y, compareTo._up.z,
_up.x, _up.y, _up.z );
printLog("%s -- compareTo._right=%f,%f,%f _right=%f,%f,%f\n",
(compareTo._right == _right ? "MATCHES " : "NO MATCH"),
compareTo._right.x, compareTo._right.y, compareTo._right.z,
_right.x, _right.y, _right.z );
printLog("%s -- compareTo._fieldOfView=%f _fieldOfView=%f\n",
(compareTo._fieldOfView == _fieldOfView ? "MATCHES " : "NO MATCH"),
compareTo._fieldOfView, _fieldOfView);
printLog("%s -- compareTo._aspectRatio=%f _aspectRatio=%f\n",
(compareTo._aspectRatio == _aspectRatio ? "MATCHES " : "NO MATCH"),
compareTo._aspectRatio, _aspectRatio);
printLog("%s -- compareTo._nearClip=%f _nearClip=%f\n",
(compareTo._nearClip == _nearClip ? "MATCHES " : "NO MATCH"),
compareTo._nearClip, _nearClip);
printLog("%s -- compareTo._farClip=%f _farClip=%f\n",
(compareTo._farClip == _farClip ? "MATCHES " : "NO MATCH"),
compareTo._farClip, _farClip);
}
return result;
}
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);

View file

@ -98,6 +98,9 @@ public:
int sphereInFrustum(const glm::vec3& center, float radius) const;
int boxInFrustum(const AABox& box) const;
// some frustum comparisons
bool matches(const ViewFrustum& compareTo) const;
bool matches(const ViewFrustum* compareTo) const { return matches(*compareTo); };
void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const;
};

View file

@ -14,9 +14,11 @@
#include <limits.h>
const int TREE_SCALE = 100;
const int NUMBER_OF_CHILDREN = 8;
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;
@ -27,4 +29,5 @@ typedef unsigned long int glBufferIndex;
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
const double VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0; // once a second is fine
#endif

View file

@ -20,29 +20,41 @@ using voxels_lib::printLog;
// using voxels_lib::printLog;
VoxelNode::VoxelNode() {
octalCode = NULL;
unsigned char* rootCode = new unsigned char[1];
*rootCode = 0;
init(rootCode);
}
VoxelNode::VoxelNode(unsigned char * octalCode) {
init(octalCode);
}
void VoxelNode::init(unsigned char * octalCode) {
_octalCode = octalCode;
#ifdef HAS_FALSE_COLOR
_falseColored = false; // assume true color
#endif
// default pointers to child nodes to NULL
for (int i = 0; i < 8; i++) {
children[i] = NULL;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
_children[i] = NULL;
}
_glBufferIndex = GLBUFFER_INDEX_UNKNOWN;
_isDirty = true;
_shouldRender = false;
calculateAABox();
}
VoxelNode::~VoxelNode() {
delete[] octalCode;
delete[] _octalCode;
// delete all of this node's children
for (int i = 0; i < 8; i++) {
if (children[i]) {
delete children[i];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i]) {
delete _children[i];
}
}
}
@ -55,33 +67,47 @@ void VoxelNode::setShouldRender(bool shouldRender) {
}
}
void VoxelNode::getAABox(AABox& box) const {
void VoxelNode::calculateAABox() {
glm::vec3 corner;
glm::vec3 size;
// copy corner into box
copyFirstVertexForCode(octalCode,(float*)&corner);
copyFirstVertexForCode(_octalCode,(float*)&corner);
// this tells you the "size" of the voxel
float voxelScale = 1 / powf(2, *octalCode);
float voxelScale = 1 / powf(2, *_octalCode);
size = glm::vec3(voxelScale,voxelScale,voxelScale);
box.setBox(corner,size);
_box.setBox(corner,size);
}
void VoxelNode::deleteChildAtIndex(int childIndex) {
if (_children[childIndex]) {
delete _children[childIndex];
_children[childIndex] = NULL;
}
}
// does not delete the node!
VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
VoxelNode* returnedChild = _children[childIndex];
if (_children[childIndex]) {
_children[childIndex] = NULL;
}
return returnedChild;
}
void VoxelNode::addChildAtIndex(int childIndex) {
if (!children[childIndex]) {
children[childIndex] = new VoxelNode();
if (!_children[childIndex]) {
_children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex));
// XXXBHG - When the node is constructed, it should be cleanly set up as
// true colored, but for some reason, not so much. I've added a a basecamp
// to-do to research this. But for now we'll use belt and suspenders and set
// it to not-false-colored here!
children[childIndex]->setFalseColored(false);
_children[childIndex]->setFalseColored(false);
// give this child its octal code
children[childIndex]->octalCode = childOctalCode(octalCode, childIndex);
_isDirty = true;
}
}
@ -89,10 +115,10 @@ void VoxelNode::addChildAtIndex(int childIndex) {
// will average the child colors...
void VoxelNode::setColorFromAverageOfChildren() {
int colorArray[4] = {0,0,0,0};
for (int i = 0; i < 8; i++) {
if (children[i] != NULL && children[i]->isColored()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i] && _children[i]->isColored()) {
for (int j = 0; j < 3; j++) {
colorArray[j] += children[i]->getTrueColor()[j]; // color averaging should always be based on true colors
colorArray[j] += _children[i]->getTrueColor()[j]; // color averaging should always be based on true colors
}
colorArray[3]++;
}
@ -168,19 +194,19 @@ bool VoxelNode::collapseIdenticalLeaves() {
// scan children, verify that they are ALL present and accounted for
bool allChildrenMatch = true; // assume the best (ottimista)
int red,green,blue;
for (int i = 0; i < 8; i++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
// if no child, or child doesn't have a color
if (children[i] == NULL || !children[i]->isColored()) {
if (!_children[i] || !_children[i]->isColored()) {
allChildrenMatch=false;
//printLog("SADNESS child missing or not colored! i=%d\n",i);
break;
} else {
if (i==0) {
red = children[i]->getColor()[0];
green = children[i]->getColor()[1];
blue = children[i]->getColor()[2];
} else if (red != children[i]->getColor()[0] ||
green != children[i]->getColor()[1] || blue != children[i]->getColor()[2]) {
red = _children[i]->getColor()[0];
green = _children[i]->getColor()[1];
blue = _children[i]->getColor()[2];
} else if (red != _children[i]->getColor()[0] ||
green != _children[i]->getColor()[1] || blue != _children[i]->getColor()[2]) {
allChildrenMatch=false;
break;
}
@ -190,9 +216,9 @@ bool VoxelNode::collapseIdenticalLeaves() {
if (allChildrenMatch) {
//printLog("allChildrenMatch: pruning tree\n");
for (int i = 0; i < 8; i++) {
delete children[i]; // delete all the child nodes
children[i]=NULL; // set it to NULL
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
delete _children[i]; // delete all the child nodes
_children[i]=NULL; // set it to NULL
}
nodeColor collapsedColor;
collapsedColor[0]=red;
@ -215,8 +241,8 @@ void VoxelNode::setRandomColor(int minimumBrightness) {
}
bool VoxelNode::isLeaf() const {
for (int i = 0; i < 8; i++) {
if (children[i]) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i]) {
return false;
}
}
@ -224,27 +250,22 @@ bool VoxelNode::isLeaf() const {
}
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);
_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);
AABox box = _box; // use temporary box so we can scale it
box.scale(TREE_SCALE);
bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box));
return inView;
}
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box;
getAABox(box);
box.scale(TREE_SCALE);
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));
glm::vec3 center = _box.getCenter() * (float)TREE_SCALE;
float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - center.x, 2) +
powf(viewFrustum.getPosition().y - center.y, 2) +
powf(viewFrustum.getPosition().z - center.z, 2));
return distanceToVoxelCenter;
}

View file

@ -27,23 +27,38 @@ private:
glBufferIndex _glBufferIndex;
bool _isDirty;
bool _shouldRender;
AABox _box;
unsigned char* _octalCode;
VoxelNode* _children[8];
void calculateAABox();
void init(unsigned char * octalCode);
public:
VoxelNode();
VoxelNode(); // root node constructor
VoxelNode(unsigned char * octalCode); // regular constructor
~VoxelNode();
unsigned char* getOctalCode() const { return _octalCode; };
VoxelNode* getChildAtIndex(int i) const { return _children[i]; };
void deleteChildAtIndex(int childIndex);
VoxelNode* removeChildAtIndex(int childIndex);
void addChildAtIndex(int childIndex);
void setColorFromAverageOfChildren();
void setRandomColor(int minimumBrightness);
bool collapseIdenticalLeaves();
unsigned char *octalCode;
VoxelNode *children[8];
const AABox& getAABox() const { return _box; };
const glm::vec3& getCenter() const { return _box.getCenter(); };
const glm::vec3& getCorner() const { return _box.getCorner(); };
float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ };
int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ };
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;
bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; };

View file

@ -32,18 +32,12 @@ void VoxelNodeBag::insert(VoxelNode* node) {
// 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) {
// just compare the pointers... that's good enough
if (_bagElements[i] == node) {
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) {
if (_bagElements[i] > node) {
insertAt = i;
break;
}

View file

@ -37,17 +37,14 @@ VoxelTree::VoxelTree() :
voxelsColoredStats(100),
voxelsBytesReadStats(100),
_isDirty(true) {
rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1];
*rootNode->octalCode = 0;
}
VoxelTree::~VoxelTree() {
// delete the children of the root node
// this recursively deletes the tree
for (int i = 0; i < 8; i++) {
delete rootNode->children[i];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
delete rootNode->getChildAtIndex(i);
}
}
@ -60,8 +57,8 @@ void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, vo
// Recurses voxel node with an operation function
void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData) {
if (operation(node, extraData)) {
for (int i = 0; i < sizeof(node->children) / sizeof(node->children[0]); i++) {
VoxelNode* child = node->children[i];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* child = node->getChildAtIndex(i);
if (child) {
recurseNodeWithOperation(child, operation, extraData);
}
@ -72,11 +69,11 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati
VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode, VoxelNode** parentOfFoundNode) const {
// find the appropriate branch index based on this ancestorNode
if (*needleCode > 0) {
int branchForNeedle = branchIndexWithDescendant(ancestorNode->octalCode, needleCode);
VoxelNode *childNode = ancestorNode->children[branchForNeedle];
int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode);
VoxelNode *childNode = ancestorNode->getChildAtIndex(branchForNeedle);
if (childNode != NULL) {
if (*childNode->octalCode == *needleCode) {
if (childNode) {
if (*childNode->getOctalCode() == *needleCode) {
// If the caller asked for the parent, then give them that too...
if (parentOfFoundNode) {
@ -101,34 +98,31 @@ VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char *
// returns the node created!
VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char* codeToReach) {
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->octalCode, codeToReach);
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->getOctalCode(), codeToReach);
// we could be coming down a branch that was already created, so don't stomp on it.
if (lastParentNode->children[indexOfNewChild] == NULL) {
if (!lastParentNode->getChildAtIndex(indexOfNewChild)) {
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->children[indexOfNewChild];
if (*lastParentNode->getChildAtIndex(indexOfNewChild)->getOctalCode() == *codeToReach) {
return lastParentNode->getChildAtIndex(indexOfNewChild);
} else {
return createMissingNode(lastParentNode->children[indexOfNewChild], codeToReach);
return createMissingNode(lastParentNode->getChildAtIndex(indexOfNewChild), codeToReach);
}
}
// 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 bytesLeftToRead) {
// instantiate variable for bytes already read
int bytesRead = 1;
for (int i = 0; i < 8; i++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; 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]) {
if (!destinationNode->getChildAtIndex(i)) {
destinationNode->addChildAtIndex(i);
if (destinationNode->isDirty()) {
_isDirty = true;
@ -142,9 +136,9 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
nodeColor newColor;
memcpy(newColor, nodeData + bytesRead, 3);
newColor[3] = 1;
bool nodeWasDirty = destinationNode->children[i]->isDirty();
destinationNode->children[i]->setColor(newColor);
bool nodeIsDirty = destinationNode->children[i]->isDirty();
bool nodeWasDirty = destinationNode->getChildAtIndex(i)->isDirty();
destinationNode->getChildAtIndex(i)->setColor(newColor);
bool nodeIsDirty = destinationNode->getChildAtIndex(i)->isDirty();
if (nodeIsDirty) {
_isDirty = true;
}
@ -164,11 +158,11 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
int childIndex = 0;
bytesRead++;
while (bytesLeftToRead - bytesRead > 0 && childIndex < 8) {
while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
// check the exists mask to see if we have a child to traverse into
if (oneAtBit(childMask, childIndex)) {
if (!destinationNode->children[childIndex]) {
if (!destinationNode->getChildAtIndex(childIndex)) {
// add a child at that index, if it doesn't exist
bool nodeWasDirty = destinationNode->isDirty();
destinationNode->addChildAtIndex(childIndex);
@ -184,7 +178,7 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
}
// tell the child to read the subsequent data
bytesRead += readNodeData(destinationNode->children[childIndex],
bytesRead += readNodeData(destinationNode->getChildAtIndex(childIndex),
nodeData + bytesRead,
bytesLeftToRead - bytesRead);
}
@ -207,7 +201,7 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
while (bitstreamAt < bitstream + bufferSizeBytes) {
VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL);
if (*bitstreamAt != *bitstreamRootNode->octalCode) {
if (*bitstreamAt != *bitstreamRootNode->getOctalCode()) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
@ -251,12 +245,11 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char *codeBuffer) {
// If the node exists...
int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color!
if (0 == memcmp(nodeToDelete->octalCode,codeBuffer,lengthInBytes)) {
if (0 == memcmp(nodeToDelete->getOctalCode(),codeBuffer,lengthInBytes)) {
if (parentNode) {
int childIndex = branchIndexWithDescendant(parentNode->octalCode, codeBuffer);
int childIndex = branchIndexWithDescendant(parentNode->getOctalCode(), codeBuffer);
delete parentNode->children[childIndex]; // delete the child nodes
parentNode->children[childIndex] = NULL; // set it to NULL
parentNode->deleteChildAtIndex(childIndex);
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
_isDirty = true;
@ -268,8 +261,6 @@ 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;
_isDirty = true;
}
@ -277,7 +268,7 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) {
VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL);
// create the node if it does not exist
if (*lastCreatedNode->octalCode != *codeColorBuffer) {
if (*lastCreatedNode->getOctalCode() != *codeColorBuffer) {
lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer);
_isDirty = true;
}
@ -319,8 +310,8 @@ 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()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i) && startNode->getChildAtIndex(i)->isColored()) {
colorMask += (1 << (7 - i));
}
}
@ -329,20 +320,20 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
outputBits(colorMask);
// output the colors we have
for (int j = 0; j < 8; j++) {
if (startNode->children[j] != NULL && startNode->children[j]->isColored()) {
for (int j = 0; j < NUMBER_OF_CHILDREN; j++) {
if (startNode->getChildAtIndex(j) && startNode->getChildAtIndex(j)->isColored()) {
printLog("color %d : ",j);
for (int c = 0; c < 3; c++) {
outputBits(startNode->children[j]->getTrueColor()[c],false);
outputBits(startNode->getChildAtIndex(j)->getTrueColor()[c],false);
}
startNode->children[j]->printDebugDetails("");
startNode->getChildAtIndex(j)->printDebugDetails("");
}
}
unsigned char childMask = 0;
for (int k = 0; k < 8; k++) {
if (startNode->children[k] != NULL) {
for (int k = 0; k < NUMBER_OF_CHILDREN; k++) {
if (startNode->getChildAtIndex(k)) {
childMask += (1 << (7 - k));
}
}
@ -353,9 +344,9 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
if (childMask > 0) {
// ask children to recursively output their trees
// if they aren't a leaf
for (int l = 0; l < 8; l++) {
if (startNode->children[l] != NULL) {
printTreeForDebugging(startNode->children[l]);
for (int l = 0; l < NUMBER_OF_CHILDREN; l++) {
if (startNode->getChildAtIndex(l)) {
printTreeForDebugging(startNode->getChildAtIndex(l));
}
}
}
@ -364,9 +355,9 @@ 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]);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i)) {
reaverageVoxelColors(startNode->getChildAtIndex(i));
hasChildren = true;
}
}
@ -396,8 +387,8 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
file.get(octets);
//printLog("octets=%d...\n",octets);
totalBytesRead++;
lengthInBytes = bytesRequiredForCodeLength(octets)-1; //(octets*3/8)+1;
unsigned char * voxelData = new unsigned char[lengthInBytes+1+3];
lengthInBytes = bytesRequiredForCodeLength(octets) - 1;
unsigned char * voxelData = new unsigned char[lengthInBytes + 1 + 3];
voxelData[0]=octets;
char byte;
@ -437,7 +428,7 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
VoxelNode* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
unsigned char* octalCode = pointToVoxel(x,y,z,s,0,0,0);
VoxelNode* node = nodeForOctalCode(rootNode, octalCode, NULL);
if (*node->octalCode != *octalCode) {
if (*node->getOctalCode() != *octalCode) {
node = NULL;
}
delete octalCode; // cleanup memory
@ -465,28 +456,30 @@ void VoxelTree::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, r
}
}
void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) {
void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float voxelSize,
bool solid, bool wantColorRandomizer, bool debug) {
// 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
unsigned char dominantColor1 = randIntInRange(1,3); //1=r, 2=g, 3=b dominant
unsigned char dominantColor2 = randIntInRange(1,3);
unsigned char dominantColor1 = randIntInRange(1, 3); //1=r, 2=g, 3=b dominant
unsigned char dominantColor2 = randIntInRange(1, 3);
if (dominantColor1==dominantColor2) {
dominantColor2 = dominantColor1+1%3;
if (dominantColor1 == dominantColor2) {
dominantColor2 = dominantColor1 + 1 % 3;
}
unsigned char r1 = (dominantColor1==1)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char g1 = (dominantColor1==2)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char b1 = (dominantColor1==3)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char r2 = (dominantColor2==1)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char g2 = (dominantColor2==2)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char b2 = (dominantColor2==3)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char r1 = (dominantColor1 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100);
unsigned char g1 = (dominantColor1 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100);
unsigned char b1 = (dominantColor1 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100);
unsigned char r2 = (dominantColor2 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100);
unsigned char g2 = (dominantColor2 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100);
unsigned char b2 = (dominantColor2 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100);
// We initialize our rgb to be either "grey" in case of randomized surface, or
// the average of the gradient, in the case of the gradient sphere.
unsigned char red = wantColorRandomizer ? 128 : (r1+r2)/2; // average of the colors
unsigned char green = wantColorRandomizer ? 128 : (g1+g2)/2;
unsigned char blue = wantColorRandomizer ? 128 : (b1+b2)/2;
unsigned char red = wantColorRandomizer ? 128 : (r1 + r2) / 2; // average of the colors
unsigned char green = wantColorRandomizer ? 128 : (g1 + g2) / 2;
unsigned char blue = wantColorRandomizer ? 128 : (b1 + b2) / 2;
// Psuedocode for creating a sphere:
//
@ -495,50 +488,54 @@ void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool
// x = xc+r*cos(theta)*sin(phi)
// y = yc+r*sin(theta)*sin(phi)
// z = zc+r*cos(phi)
int t=0; // total points
// We want to make sure that as we "sweep" through our angles we use a delta angle that's small enough to not skip any
// voxels we can calculate theta from our desired arc length
// lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R
// lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r
float angleDelta = (s/r);
// assume solid for now
float ri = 0.0;
float thisRadius = 0.0;
float thisVoxelSize = radius / 4.0f;
if (!solid) {
ri=r; // just the outer surface
thisRadius = radius; // just the outer surface
thisVoxelSize = voxelSize;
}
// If you also iterate form the interior of the sphere to the radius, makeing
// larger and larger sphere's you'd end up with a solid sphere. And lots of voxels!
for (; ri <= (r+(s/2.0)); ri+=s) {
//printLog("radius: ri=%f ri+s=%f (r+(s/2.0))=%f\n",ri,ri+s,(r+(s/2.0)));
for (float theta=0.0; theta <= 2*M_PI; theta += angleDelta) {
// larger and larger sphere'voxelSize you'd end up with a solid sphere. And lots of voxels!
while (thisRadius <= (radius + (voxelSize / 2.0))) {
if (debug) {
printLog("radius: thisRadius=%f thisVoxelSize=%f thisRadius+thisVoxelSize=%f (radius+(voxelSize/2.0))=%f\n",
thisRadius, thisVoxelSize, thisRadius+thisVoxelSize, (radius + (voxelSize / 2.0)));
}
// We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize
// small enough to not skip any voxels we can calculate theta from our desired arc length
// lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R
// lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r
float angleDelta = (thisVoxelSize / thisRadius);
for (float theta=0.0; theta <= 2 * M_PI; theta += angleDelta) {
for (float phi=0.0; phi <= M_PI; phi += angleDelta) {
t++; // total voxels
float x = xc+ri*cos(theta)*sin(phi);
float y = yc+ri*sin(theta)*sin(phi);
float z = zc+ri*cos(phi);
float x = xc + thisRadius * cos(theta) * sin(phi);
float y = yc + thisRadius * sin(theta) * sin(phi);
float z = zc + thisRadius * cos(phi);
// gradient color data
float gradient = (phi/M_PI);
float gradient = (phi / M_PI);
// only use our actual desired color on the outer edge, otherwise
// use our "average" color
if (ri+(s*2.0)>=r) {
//printLog("painting candy shell radius: ri=%f r=%f\n",ri,r);
red = wantColorRandomizer ? randomColorValue(165) : r1+((r2-r1)*gradient);
green = wantColorRandomizer ? randomColorValue(165) : g1+((g2-g1)*gradient);
blue = wantColorRandomizer ? randomColorValue(165) : b1+((b2-b1)*gradient);
if (thisRadius + (voxelSize * 2.0) >= radius) {
//printLog("painting candy shell radius: thisRadius=%f radius=%f\n",thisRadius,radius);
red = wantColorRandomizer ? randomColorValue(165) : r1 + ((r2 - r1) * gradient);
green = wantColorRandomizer ? randomColorValue(165) : g1 + ((g2 - g1) * gradient);
blue = wantColorRandomizer ? randomColorValue(165) : b1 + ((b2 - b1) * gradient);
}
unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue);
unsigned char* voxelData = pointToVoxel(x, y, z, thisVoxelSize, red, green, blue);
this->readCodeColorBufferToTree(voxelData);
delete voxelData;
}
}
thisRadius += thisVoxelSize;
thisVoxelSize = std::max(voxelSize, thisVoxelSize / 2.0f);
}
this->reaverageVoxelColors(this->rootNode);
}
@ -564,8 +561,7 @@ public:
bool findRayOperation(VoxelNode* node, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
AABox box;
node->getAABox(box);
AABox box = node->getAABox();
float distance;
if (!box.findRayIntersection(args->origin, args->direction, distance)) {
return false;
@ -610,22 +606,20 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
int thisLevel = currentSearchLevel;
int maxChildLevel = thisLevel;
const int MAX_CHILDREN = 8;
VoxelNode* inViewChildren[MAX_CHILDREN];
float distancesToChildren[MAX_CHILDREN];
int positionOfChildren[MAX_CHILDREN];
VoxelNode* inViewChildren[NUMBER_OF_CHILDREN];
float distancesToChildren[NUMBER_OF_CHILDREN];
int positionOfChildren[NUMBER_OF_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());
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
bool childIsColored = (childNode && childNode->isColored());
bool childIsInView = (childNode && childNode->isInView(viewFrustum));
bool childIsLeaf = (childNode && childNode->isLeaf());
if (childIsInView) {
@ -641,10 +635,10 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
float distance = childNode->distanceToCamera(viewFrustum);
if (distance < boundaryDistanceForRenderLevel(*childNode->octalCode + 1)) {
if (distance < boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1)) {
inViewCount = insertIntoSortedArrays((void*)childNode, distance, i,
(void**)&inViewChildren, (float*)&distancesToChildren,
(int*)&positionOfChildren, inViewCount, MAX_CHILDREN);
(int*)&positionOfChildren, inViewCount, NUMBER_OF_CHILDREN);
}
}
}
@ -682,8 +676,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
}
// write the octal code
int codeLength = bytesRequiredForCodeLength(*node->octalCode);
memcpy(outputBuffer,node->octalCode,codeLength);
int codeLength = bytesRequiredForCodeLength(*node->getOctalCode());
memcpy(outputBuffer,node->getOctalCode(),codeLength);
outputBuffer += codeLength; // move the pointer
bytesWritten += codeLength; // keep track of byte count
@ -729,7 +723,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// caller can pass NULL as viewFrustum if they want everything
if (viewFrustum) {
float distance = node->distanceToCamera(*viewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->octalCode + 1);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
// If we're too far away for our render level, then just return
if (distance >= boundaryDistance) {
@ -747,14 +741,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
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
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN 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;
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_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];
@ -768,14 +761,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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 && (!viewFrustum || childNode->isInView(*viewFrustum)));
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
bool childIsInView = (childNode && (!viewFrustum || childNode->isInView(*viewFrustum)));
if (childIsInView) {
// Before we determine consider this further, let's see if it's in our LOD scope...
float distance = viewFrustum ? childNode->distanceToCamera(*viewFrustum) : 0;
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->octalCode + 1) : 1;
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
if (distance < boundaryDistance) {
inViewCount++;
@ -783,13 +775,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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())) {
if (!(childNode && childNode->isLeaf())) {
childrenExistBits += (1 << (7 - i));
inViewNotLeafCount++;
}
// track children with actual color
if (childExists && childNode->isColored()) {
if (childNode && childNode->isColored()) {
childrenColoredBits += (1 << (7 - i));
inViewWithColorCount++;
}
@ -801,9 +793,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
// write the color data...
for (int i = 0; i < MAX_CHILDREN; i++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenColoredBits, i)) {
memcpy(writeToThisLevelBuffer, &node->children[i]->getColor(), BYTES_PER_COLOR);
memcpy(writeToThisLevelBuffer, &node->getChildAtIndex(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
}
@ -841,10 +833,10 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenExistBits, i)) {
VoxelNode* childNode = node->children[i];
VoxelNode* childNode = node->getChildAtIndex(i);
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,

View file

@ -49,7 +49,7 @@ public:
VoxelNode* getVoxelAt(float x, float y, float z, float s) const;
void createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue);
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer, bool debug = false);
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);

View file

@ -22,7 +22,8 @@ bool countVoxelsOperation(VoxelNode* node, void* extraData) {
void addScene(VoxelTree * tree) {
printf("adding scene...\n");
float voxelSize = 1.f/32;
// We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so...
float voxelSize = 0.5f / TREE_SCALE;
// Here's an example of how to create a voxel.
printf("creating corner points...\n");
@ -50,8 +51,6 @@ void addScene(VoxelTree * tree) {
// Now some more examples... a little more complex
printf("creating corner points...\n");
tree->createVoxel(0 , 0 , 0 , voxelSize, 255, 255 ,255);
tree->createVoxel(1.0 - voxelSize, 0 , 0 , voxelSize, 255, 0 ,0 );
tree->createVoxel(0 , 1.0 - voxelSize, 0 , voxelSize, 0 , 255 ,0 );
tree->createVoxel(0 , 0 , 1.0 - voxelSize, voxelSize, 0 , 0 ,255);
@ -63,31 +62,32 @@ void addScene(VoxelTree * tree) {
// Now some more examples... creating some lines using the line primitive
printf("creating voxel lines...\n");
float lineVoxelSize = 0.99f/256;
rgbColor red = {255,0,0};
rgbColor green = {0,255,0};
rgbColor blue = {0,0,255};
// We want our line voxels to be about 1/32 meter high, and our TREE_SCALE is in meters, so...
float lineVoxelSize = 1.f / (32 * TREE_SCALE);
rgbColor red = {255, 0, 0};
rgbColor green = {0, 255, 0};
rgbColor blue = {0, 0, 255};
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(0, 0, 1), lineVoxelSize, blue);
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(1, 0, 0), lineVoxelSize, red);
tree->createLine(glm::vec3(0, 0, 0), glm::vec3(0, 1, 0), lineVoxelSize, green);
printf("DONE creating lines...\n");
// Now some more examples... creating some spheres using the sphere primitive
int sphereBaseSize = 512;
printf("creating spheres...\n");
tree->createSphere(0.25, 0.5, 0.5, 0.5, (1.0 / sphereBaseSize), true, false);
printf("one sphere added...\n");
tree->createSphere(0.030625, 0.5, 0.5, (0.25-0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
// We want the smallest unit of our spheres to be about 1/16th of a meter tall
float sphereVoxelSize = 1.f / (16 * TREE_SCALE);
printf("creating spheres... sphereVoxelSize=%f\n",sphereVoxelSize);
tree->createSphere(0.25, 0.5, 0.5, 0.5, sphereVoxelSize, true, false, true);
printf("one sphere added... sphereVoxelSize=%f\n",sphereVoxelSize);
printf("two spheres added...\n");
tree->createSphere(0.030625, (0.75 - 0.030625), (0.75 - 0.030625), (0.75 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("three spheres added...\n");
tree->createSphere(0.030625, (0.75 - 0.030625), (0.75 - 0.030625), 0.06125, (1.0 / (sphereBaseSize * 2)), true, true);
printf("four spheres added...\n");
tree->createSphere(0.030625, (0.75 - 0.030625), 0.06125, (0.75 - 0.06125), (1.0 / (sphereBaseSize * 2)), true, true);
printf("five spheres added...\n");
tree->createSphere(0.06125, 0.125, 0.125, (0.75 - 0.125), (1.0 / (sphereBaseSize * 2)), true, true);
tree->createSphere(0.030625, 0.5, 0.5, (0.25 - 0.06125), sphereVoxelSize, true, true);
printf("two spheres added... sphereVoxelSize=%f\n",sphereVoxelSize);
tree->createSphere(0.030625, (0.75 - 0.030625), (0.75 - 0.030625), (0.75 - 0.06125), sphereVoxelSize, true, true);
printf("three spheres added... sphereVoxelSize=%f\n",sphereVoxelSize);
tree->createSphere(0.030625, (0.75 - 0.030625), (0.75 - 0.030625), 0.06125, sphereVoxelSize, true, true);
printf("four spheres added... sphereVoxelSize=%f\n",sphereVoxelSize);
tree->createSphere(0.030625, (0.75 - 0.030625), 0.06125, (0.75 - 0.06125), sphereVoxelSize, true, true);
printf("five spheres added... sphereVoxelSize=%f\n",sphereVoxelSize);
tree->createSphere(0.06125, 0.125, 0.125, (0.75 - 0.125), sphereVoxelSize, true, true);
float radius = 0.0125f;
printf("6 spheres added...\n");
@ -102,7 +102,6 @@ void addScene(VoxelTree * tree) {
tree->createSphere(radius, 0.025, radius * 5.0f, 0.25, (1.0 / 4096), true, true);
printf("11 spheres added...\n");
printf("DONE creating spheres...\n");
// Here's an example of how to recurse the tree and do some operation on the nodes as you recurse them.
// This one is really simple, it just couts them...
// Look at the function countVoxelsOperation() for an example of how you could use this function

View file

@ -38,7 +38,7 @@ const float DEATH_STAR_RADIUS = 4.0;
const float MAX_CUBE = 0.05f;
const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000;
int PACKETS_PER_CLIENT_PER_INTERVAL = 20;
int PACKETS_PER_CLIENT_PER_INTERVAL = 50;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
@ -153,12 +153,8 @@ void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
for (int i = 0; i < 8; i++) {
if (true) {
// create a new VoxelNode to put here
currentRootNode->children[i] = new VoxelNode();
// give this child it's octal code
currentRootNode->children[i]->octalCode = childOctalCode(currentRootNode->octalCode, i);
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->children[i]);
currentRootNode->addChildAtIndex(i);
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->getChildAtIndex(i));
createdChildren = true;
}
}
@ -551,12 +547,9 @@ int main(int argc, const char * argv[])
// If we got a PACKET_HEADER_HEAD_DATA, then we're talking to an AGENT_TYPE_AVATAR, and we
// need to make sure we have it in our agentList.
if (packetData[0] == PACKET_HEADER_HEAD_DATA) {
if (agentList->addOrUpdateAgent(&agentPublicAddress,
&agentPublicAddress,
AGENT_TYPE_AVATAR,
agentList->getLastAgentId())) {
agentList->increaseAgentId();
}
uint16_t agentID = 0;
unpackAgentId(packetData + sizeof(PACKET_HEADER_HEAD_DATA), &agentID);
agentList->addOrUpdateAgent(&agentPublicAddress, &agentPublicAddress, AGENT_TYPE_AVATAR, agentID);
agentList->updateAgentWithData(&agentPublicAddress, packetData, receivedBytes);
}