resolve conflicts on merge with upstream master

This commit is contained in:
Stephen Birarda 2013-08-01 14:33:23 -07:00
commit 2b0cfe3c67
42 changed files with 1812 additions and 821 deletions

View file

@ -8,6 +8,7 @@
#include <cstring>
#include <Node.h>
#include <PacketHeaders.h>
#include "PositionalAudioRingBuffer.h"
@ -16,25 +17,81 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer() :
AudioRingBuffer(false),
_position(0.0f, 0.0f, 0.0f),
_orientation(0.0f, 0.0f, 0.0f, 0.0f),
_willBeAddedToMix(false)
_willBeAddedToMix(false),
_listenMode(AudioRingBuffer::NORMAL),
_listenRadius(0.0f)
{
}
PositionalAudioRingBuffer::~PositionalAudioRingBuffer() {
}
bool PositionalAudioRingBuffer::isListeningToNode(Node& other) const {
switch (_listenMode) {
default:
case AudioRingBuffer::NORMAL:
return true;
break;
case AudioRingBuffer::OMNI_DIRECTIONAL_POINT: {
PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) other.getLinkedData();
float distance = glm::distance(_position, otherNodeBuffer->_position);
return distance <= _listenRadius;
break;
}
case AudioRingBuffer::SELECTED_SOURCES:
for (int i = 0; i < _listenSources.size(); i++) {
if (other.getNodeID() == _listenSources[i]) {
return true;
}
}
return false;
break;
}
}
int PositionalAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer);
currentBuffer += sizeof(uint16_t); // the source ID
currentBuffer += parseListenModeData(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
currentBuffer += parseAudioSamples(currentBuffer, numBytes - (currentBuffer - sourceBuffer));
return currentBuffer - sourceBuffer;
}
int PositionalAudioRingBuffer::parseListenModeData(unsigned char* sourceBuffer, int numBytes) {
unsigned char* currentBuffer = sourceBuffer;
memcpy(&_listenMode, currentBuffer, sizeof(_listenMode));
currentBuffer += sizeof(_listenMode);
if (_listenMode == AudioRingBuffer::OMNI_DIRECTIONAL_POINT) {
memcpy(&_listenRadius, currentBuffer, sizeof(_listenRadius));
currentBuffer += sizeof(_listenRadius);
} else if (_listenMode == AudioRingBuffer::SELECTED_SOURCES) {
int listenSourcesCount;
memcpy(&listenSourcesCount, currentBuffer, sizeof(listenSourcesCount));
currentBuffer += sizeof(listenSourcesCount);
for (int i = 0; i < listenSourcesCount; i++) {
int sourceID;
memcpy(&sourceID, currentBuffer, sizeof(sourceID));
currentBuffer += sizeof(sourceID);
_listenSources.push_back(sourceID);
}
}
return currentBuffer - sourceBuffer;
}
int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, int numBytes) {
unsigned char* currentBuffer = sourceBuffer;
memcpy(&_position, currentBuffer, sizeof(_position));
currentBuffer += sizeof(_position);
memcpy(&_orientation, currentBuffer, sizeof(_orientation));
currentBuffer += sizeof(_orientation);
@ -63,6 +120,6 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) {
return true;
}
}
printf("packet mismatch...\n");
return false;
}

View file

@ -9,6 +9,7 @@
#ifndef __hifi__PositionalAudioRingBuffer__
#define __hifi__PositionalAudioRingBuffer__
#include <vector>
#include <glm/gtx/quaternion.hpp>
#include <AudioRingBuffer.h>
@ -16,9 +17,11 @@
class PositionalAudioRingBuffer : public AudioRingBuffer {
public:
PositionalAudioRingBuffer();
~PositionalAudioRingBuffer();
int parseData(unsigned char* sourceBuffer, int numBytes);
int parsePositionalData(unsigned char* sourceBuffer, int numBytes);
int parseListenModeData(unsigned char* sourceBuffer, int numBytes);
bool shouldBeAddedToMix(int numJitterBufferSamples);
@ -27,6 +30,9 @@ public:
const glm::vec3& getPosition() const { return _position; }
const glm::quat& getOrientation() const { return _orientation; }
bool isListeningToNode(Node& other) const;
ListenMode getListeningMode() const { return _listenMode; }
protected:
// disallow copying of PositionalAudioRingBuffer objects
@ -36,6 +42,10 @@ protected:
glm::vec3 _position;
glm::quat _orientation;
bool _willBeAddedToMix;
ListenMode _listenMode;
float _listenRadius;
std::vector<int> _listenSources;
};
#endif /* defined(__hifi__PositionalAudioRingBuffer__) */

View file

@ -141,7 +141,6 @@ int main(int argc, const char* argv[]) {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) node->getLinkedData();
if (positionalRingBuffer && positionalRingBuffer->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES)) {
// this is a ring buffer that is ready to go
// set its flag so we know to push its buffer when all is said and done
@ -159,168 +158,171 @@ int main(int argc, const char* argv[]) {
// zero out the client mix for this node
memset(clientSamples, 0, sizeof(clientSamples));
// loop through all other nodes that have sufficient audio to mix
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
if (((PositionalAudioRingBuffer*) otherNode->getLinkedData())->willBeAddedToMix()
&& (otherNode != node || (otherNode == node && nodeRingBuffer->shouldLoopbackForNode()))) {
PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) otherNode->getLinkedData();
// based on our listen mode we will do this mixing...
if (nodeRingBuffer->isListeningToNode(*otherNode)) {
float bearingRelativeAngleToSource = 0.0f;
float attenuationCoefficient = 1.0f;
int numSamplesDelay = 0;
float weakChannelAmplitudeRatio = 1.0f;
float bearingRelativeAngleToSource = 0.0f;
float attenuationCoefficient = 1.0f;
int numSamplesDelay = 0;
float weakChannelAmplitudeRatio = 1.0f;
stk::TwoPole* otherNodeTwoPole = NULL;
stk::TwoPole* otherNodeTwoPole = NULL;
if (otherNode != node) {
// only do axis/distance attenuation when in normal mode
if (otherNode != node && nodeRingBuffer->getListeningMode() == AudioRingBuffer::NORMAL) {
glm::vec3 listenerPosition = nodeRingBuffer->getPosition();
glm::vec3 relativePosition = otherNodeBuffer->getPosition() - nodeRingBuffer->getPosition();
glm::quat inverseOrientation = glm::inverse(nodeRingBuffer->getOrientation());
glm::vec3 listenerPosition = nodeRingBuffer->getPosition();
glm::vec3 relativePosition = otherNodeBuffer->getPosition() - nodeRingBuffer->getPosition();
glm::quat inverseOrientation = glm::inverse(nodeRingBuffer->getOrientation());
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
float radius = 0.0f;
float distanceSquareToSource = glm::dot(relativePosition, relativePosition);
float radius = 0.0f;
if (otherNode->getType() == NODE_TYPE_AUDIO_INJECTOR) {
InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) otherNodeBuffer;
radius = injectedBuffer->getRadius();
attenuationCoefficient *= injectedBuffer->getAttenuationRatio();
}
if (otherNode->getType() == NODE_TYPE_AUDIO_INJECTOR) {
InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) otherNodeBuffer;
radius = injectedBuffer->getRadius();
attenuationCoefficient *= injectedBuffer->getAttenuationRatio();
}
if (radius == 0 || (distanceSquareToSource > radius * radius)) {
// this is either not a spherical source, or the listener is outside the sphere
if (radius == 0 || (distanceSquareToSource > radius * radius)) {
// this is either not a spherical source, or the listener is outside the sphere
if (radius > 0) {
// this is a spherical source - the distance used for the coefficient
// needs to be the closest point on the boundary to the source
if (radius > 0) {
// this is a spherical source - the distance used for the coefficient
// needs to be the closest point on the boundary to the source
// ovveride the distance to the node with the distance to the point on the
// boundary of the sphere
distanceSquareToSource -= (radius * radius);
// ovveride the distance to the node with the distance to the point on the
// boundary of the sphere
distanceSquareToSource -= (radius * radius);
} else {
// calculate the angle delivery for off-axis attenuation
glm::vec3 rotatedListenerPosition = glm::inverse(otherNodeBuffer->getOrientation())
* relativePosition;
} else {
// calculate the angle delivery for off-axis attenuation
glm::vec3 rotatedListenerPosition = glm::inverse(otherNodeBuffer->getOrientation())
* relativePosition;
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedListenerPosition));
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedListenerPosition));
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
// multiply the current attenuation coefficient by the calculated off axis coefficient
attenuationCoefficient *= offAxisCoefficient;
// multiply the current attenuation coefficient by the calculated off axis coefficient
attenuationCoefficient *= offAxisCoefficient;
}
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
const float DISTANCE_SCALE = 2.5f;
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
const float DISTANCE_LOG_BASE = 2.5f;
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
// calculate the distance coefficient using the distance to this node
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
DISTANCE_SCALE_LOG +
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
distanceCoefficient = std::min(1.0f, distanceCoefficient);
// multiply the current attenuation coefficient by the distance coefficient
attenuationCoefficient *= distanceCoefficient;
// project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f;
// produce an oriented angle about the y-axis
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedSourcePosition),
glm::vec3(0.0f, 1.0f, 0.0f));
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
// figure out the number of samples of delay and the ratio of the amplitude
// in the weak channel for audio spatialization
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
// grab the TwoPole object for this source, add it if it doesn't exist
TwoPoleNodeMap& nodeTwoPoles = nodeRingBuffer->getTwoPoles();
TwoPoleNodeMap::iterator twoPoleIterator = nodeTwoPoles.find(otherNode->getNodeID());
if (twoPoleIterator == nodeTwoPoles.end()) {
// setup the freeVerb effect for this source for this client
otherNodeTwoPole = nodeTwoPoles[otherNode->getNodeID()] = new stk::TwoPole;
} else {
otherNodeTwoPole = twoPoleIterator->second;
}
// calculate the reasonance for this TwoPole based on angle to source
float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f;
float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f;
otherNodeTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY,
TWO_POLE_MAX_FILTER_STRENGTH
* fabsf(bearingRelativeAngleToSource) / 180.0f,
true);
}
}
int16_t* sourceBuffer = otherNodeBuffer->getNextOutput();
int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
int16_t* delayedChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
: clientSamples;
int16_t* delaySamplePointer = otherNodeBuffer->getNextOutput() == otherNodeBuffer->getBuffer()
? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
: otherNodeBuffer->getNextOutput() - numSamplesDelay;
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
// load up the stkFrameBuffer with this source's samples
stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s];
}
// perform the TwoPole effect on the stkFrameBuffer
if (otherNodeTwoPole) {
otherNodeTwoPole->tick(stkFrameBuffer);
}
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] * attenuationCoefficient * weakChannelAmplitudeRatio;
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
const float DISTANCE_SCALE = 2.5f;
const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f;
const float DISTANCE_LOG_BASE = 2.5f;
const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE);
// calculate the distance coefficient using the distance to this node
float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR,
DISTANCE_SCALE_LOG +
(0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1);
distanceCoefficient = std::min(1.0f, distanceCoefficient);
// multiply the current attenuation coefficient by the distance coefficient
attenuationCoefficient *= distanceCoefficient;
// project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f;
// produce an oriented angle about the y-axis
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedSourcePosition),
glm::vec3(0.0f, 1.0f, 0.0f));
const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5;
// figure out the number of samples of delay and the ratio of the amplitude
// in the weak channel for audio spatialization
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
// grab the TwoPole object for this source, add it if it doesn't exist
TwoPoleNodeMap& nodeTwoPoles = nodeRingBuffer->getTwoPoles();
TwoPoleNodeMap::iterator twoPoleIterator = nodeTwoPoles.find(otherNode->getNodeID());
if (twoPoleIterator == nodeTwoPoles.end()) {
// setup the freeVerb effect for this source for this client
otherNodeTwoPole = nodeTwoPoles[otherNode->getNodeID()] = new stk::TwoPole;
} else {
otherNodeTwoPole = twoPoleIterator->second;
delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
// calculate the reasonance for this TwoPole based on angle to source
float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f;
float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f;
otherNodeTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY,
TWO_POLE_MAX_FILTER_STRENGTH
* fabsf(bearingRelativeAngleToSource) / 180.0f,
true);
}
}
int16_t* sourceBuffer = otherNodeBuffer->getNextOutput();
int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples
: clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
int16_t* delayedChannel = (bearingRelativeAngleToSource > 0.0f)
? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL
: clientSamples;
int16_t* delaySamplePointer = otherNodeBuffer->getNextOutput() == otherNodeBuffer->getBuffer()
? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay
: otherNodeBuffer->getNextOutput() - numSamplesDelay;
for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) {
// load up the stkFrameBuffer with this source's samples
stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s];
}
// perform the TwoPole effect on the stkFrameBuffer
if (otherNodeTwoPole) {
otherNodeTwoPole->tick(stkFrameBuffer);
}
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] * attenuationCoefficient * weakChannelAmplitudeRatio;
delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient;
int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient;
goodChannel[s] = glm::clamp(goodChannel[s] + currentSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
goodChannel[s] = glm::clamp(goodChannel[s] + currentSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
int sumSample = delayedChannel[s + numSamplesDelay]
+ (currentSample * weakChannelAmplitudeRatio);
delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
int sumSample = delayedChannel[s + numSamplesDelay]
+ (currentSample * weakChannelAmplitudeRatio);
delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample,
MIN_SAMPLE_VALUE,
MAX_SAMPLE_VALUE);
}
if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) {
// this could be a delayed sample on the next pass
// so store the affected back in the ARB
otherNodeBuffer->getNextOutput()[s] = (int16_t) stkFrameBuffer[s];
if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) {
// this could be a delayed sample on the next pass
// so store the affected back in the ARB
otherNodeBuffer->getNextOutput()[s] = (int16_t) stkFrameBuffer[s];
}
}
}
}
@ -340,7 +342,6 @@ int main(int argc, const char* argv[]) {
if (nodeBuffer->getNextOutput() >= nodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES) {
nodeBuffer->setNextOutput(nodeBuffer->getBuffer());
}
nodeBuffer->setWillBeAddedToMix(false);
}
}
@ -350,14 +351,15 @@ int main(int argc, const char* argv[]) {
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData);
uint16_t sourceID;
memcpy(&sourceID, currentBuffer, sizeof(sourceID));
Node* avatarNode = nodeList->addOrUpdateNode(nodeAddress,
nodeAddress,
NODE_TYPE_AGENT,
nodeList->getLastNodeID());
if (avatarNode->getNodeID() == nodeList->getLastNodeID()) {
nodeList->increaseNodeID();
}
sourceID);
nodeList->updateNodeWithData(nodeAddress, packetData, receivedBytes);

View file

@ -36,7 +36,7 @@
const int AVATAR_LISTEN_PORT = 55444;
unsigned char *addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
currentPosition += packNodeId(currentPosition, nodeToAdd->getNodeID());
AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData();
@ -51,6 +51,53 @@ void attachAvatarDataToNode(Node* newNode) {
}
}
// NOTE: some additional optimizations to consider.
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
// if the avatar is not in view or in the keyhole.
// 2) after culling for view frustum, sort order the avatars by distance, send the closest ones first.
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
// determine which avatars are included in the packet stream
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE];
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0];
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA);
unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes;
int packetLength = currentBufferPosition - broadcastPacket;
int packetsSent = 0;
// send back a packet with other active node data to this node
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) {
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
} else {
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
// reset the packet
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
// copy the avatar that didn't fit into the next packet
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
}
}
}
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
}
int main(int argc, const char* argv[]) {
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT);
@ -67,14 +114,11 @@ int main(int argc, const char* argv[]) {
nodeList->startSilentNodeRemovalThread();
sockaddr *nodeAddress = new sockaddr;
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
sockaddr* nodeAddress = new sockaddr;
ssize_t receivedBytes = 0;
unsigned char *broadcastPacket = new unsigned char[MAX_PACKET_SIZE];
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA);
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
unsigned char* currentBufferPosition = NULL;
uint16_t nodeID = 0;
Node* avatarNode = NULL;
@ -104,17 +148,7 @@ int main(int argc, const char* argv[]) {
// parse positional data from an node
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
case PACKET_TYPE_INJECT_AUDIO:
currentBufferPosition = broadcastPacket + numHeaderBytes;
// send back a packet with other active node data to this node
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) {
currentBufferPosition = addNodeToBroadcastPacket(currentBufferPosition, &*node);
}
}
nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
broadcastAvatarData(nodeList, nodeAddress);
break;
case PACKET_TYPE_AVATAR_VOXEL_URL:
case PACKET_TYPE_AVATAR_FACE_VIDEO:

View file

@ -55,10 +55,10 @@ int main(int argc, const char * argv[])
// domain server
bool isLocalMode = cmdOptionExists(argc, argv, "--local");
if (isLocalMode) {
printf("NOTE: Running in Local Mode!\n");
printf("NOTE: Running in local mode!\n");
} else {
printf("--------------------------------------------------\n");
printf("NOTE: Running in EC2 Mode. \n");
printf("NOTE: Not running in local mode. \n");
printf("If you're a developer testing a local system, you\n");
printf("probably want to include --local on command line.\n");
printf("--------------------------------------------------\n");
@ -104,9 +104,10 @@ int main(int argc, const char * argv[])
// so hardcode the EC2 public address for now
if (nodePublicAddress.sin_addr.s_addr == serverLocalAddress) {
// If we're not running "local" then we do replace the IP
// with the EC2 IP. Otherwise, we use our normal public IP
// with 0. This designates to clients that the server is reachable
// at the same IP address
if (!isLocalMode) {
nodePublicAddress.sin_addr.s_addr = 895283510; // local IP in this format...
nodePublicAddress.sin_addr.s_addr = 0;
destinationSocket = (sockaddr*) &nodeLocalAddress;
}
}

View file

@ -40,11 +40,13 @@ bool hasInjectedAudioOnce = false;
float sleepIntervalMin = 1.00;
float sleepIntervalMax = 2.00;
char *sourceAudioFile = NULL;
const char *allowedParameters = ":sc::a::f::t::r:";
const char *allowedParameters = ":sc::a::f::t::r:l";
float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f};
unsigned char volume = DEFAULT_INJECTOR_VOLUME;
float triggerDistance = 0.0f;
float radius = 0.0f;
bool wantsLocalDomain = false;
void usage(void) {
std::cout << "High Fidelity - Interface audio injector" << std::endl;
@ -54,6 +56,7 @@ void usage(void) {
std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl;
std::cout << " -t FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl;
std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl;
std::cout << " -l Local domain mode." << std::endl;
}
bool processParameters(int parameterCount, char* parameterData[]) {
@ -96,6 +99,9 @@ bool processParameters(int parameterCount, char* parameterData[]) {
::radius = atof(optarg);
std::cout << "[DEBUG] Injector radius: " << optarg << std::endl;
break;
case 'l':
::wantsLocalDomain = true;
break;
default:
usage();
return false;
@ -111,6 +117,7 @@ void createAvatarDataForNode(Node* node) {
}
int main(int argc, char* argv[]) {
// new seed for random audio sleep times
srand(time(0));
@ -126,6 +133,11 @@ int main(int argc, char* argv[]) {
// create an NodeList instance to handle communication with other nodes
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT);
if (::wantsLocalDomain) {
printf("Local Domain MODE!\n");
nodeList->setDomainIPToLocalhost();
}
// start the node list thread that will kill off nodes when they stop talking
nodeList->startSilentNodeRemovalThread();

8
interface/CMakeLists.txt Executable file → Normal file
View file

@ -71,7 +71,6 @@ find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5Svg REQUIRED)
set(QUAZIP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/quazip)
@ -81,8 +80,6 @@ include_directories(external/fervor/)
# create the executable, make it a bundle on OS X
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS})
qt5_use_modules(${TARGET_NAME} Core Gui Network OpenGL WebKit Svg)
# link in the hifi shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
@ -111,6 +108,8 @@ if (OPENNI_FOUND)
target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES})
endif (OPENNI_FOUND)
qt5_use_modules(${TARGET_NAME} Core Gui Network OpenGL Svg)
# include headers for interface and InterfaceConfig.
include_directories(
${PROJECT_SOURCE_DIR}/src
@ -131,8 +130,7 @@ include_directories(
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENCV_INCLUDE_DIRS}")
target_link_libraries(
${TARGET_NAME}
${QT_LIBRARIES}
${TARGET_NAME}
${LIBVPX_LIBRARIES}
${MOTIONDRIVER_LIBRARIES}
${OPENCV_LIBRARIES}

View file

@ -3,6 +3,7 @@ project(Fervor)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5Widgets REQUIRED)
add_definitions(-DFV_GUI)
@ -31,4 +32,4 @@ include_directories(
add_library(fervor ${FERVOR_SOURCES} ${FERVOR_HEADERS} ${FERVOR_MOC_SOURCES} ${FERVOR_WRAPPED_UI})
target_link_libraries(fervor ${QUAZIP_LIBRARIES})
qt5_use_modules(fervor Core Network Widgets)
qt5_use_modules(fervor Core Network Widgets WebKit)

View file

@ -75,8 +75,6 @@ using namespace std;
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
static char STAR_CACHE_FILE[] = "cachedStars.txt";
static const bool TESTING_PARTICLE_SYSTEM = true;
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins in the world
@ -203,6 +201,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_mouseVoxelScale(1.0f / 1024.0f),
_justEditedVoxel(false),
_isLookingAtOtherAvatar(false),
_lookatIndicatorScale(1.0f),
_paintOn(false),
_dominantColor(0),
_perfStatsOn(false),
@ -406,7 +405,7 @@ void Application::paintGL() {
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
@ -565,6 +564,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
return;
}
//this is for switching between modes for the leap rave glove test
if (_simulateLeapHand->isChecked() || _testRaveGlove->isChecked()) {
_myAvatar.getHand().setRaveGloveEffectsMode((QKeyEvent*)event);
}
bool shifted = event->modifiers().testFlag(Qt::ShiftModifier);
switch (event->key()) {
@ -1496,11 +1500,11 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
args->packetsSent, args->bytesSent, elapsed, usecToSleep);
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
usleep(usecToSleep);
} else {
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
args->packetsSent, args->bytesSent, elapsed);
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
}
args->lastSendTime = now;
}
@ -1685,7 +1689,7 @@ void Application::pasteVoxels() {
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
qDebug("sending packet: %d\n", ++args.packetsSent);
args.bytesSent += args.bufferInUse;
qDebug("total bytes sent: %lld\n", args.bytesSent);
qDebug("total bytes sent: %lld\n", (long long int)args.bytesSent);
}
if (calculatedOctCode) {
@ -1750,6 +1754,8 @@ void Application::initMenu() {
_renderLookatOn->setChecked(false);
(_renderLookatIndicatorOn = renderMenu->addAction("Lookat Indicator"))->setCheckable(true);
_renderLookatIndicatorOn->setChecked(true);
(_renderParticleSystemOn = renderMenu->addAction("Particle System"))->setCheckable(true);
_renderParticleSystemOn->setChecked(true);
(_manualFirstPerson = renderMenu->addAction(
"First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P))->setCheckable(true);
(_manualThirdPerson = renderMenu->addAction(
@ -1854,6 +1860,11 @@ void Application::initMenu() {
(_simulateLeapHand = debugMenu->addAction("Simulate Leap Hand"))->setCheckable(true);
(_testRaveGlove = debugMenu->addAction("Test RaveGlove"))->setCheckable(true);
QMenu* audioDebugMenu = debugMenu->addMenu("Audio Debugging Tools");
audioDebugMenu->addAction("Listen Mode Normal", this, SLOT(setListenModeNormal()), Qt::CTRL | Qt::Key_1);
audioDebugMenu->addAction("Listen Mode Point/Radius", this, SLOT(setListenModePoint()), Qt::CTRL | Qt::Key_2);
audioDebugMenu->addAction("Listen Mode Single Source", this, SLOT(setListenModeSingleSource()), Qt::CTRL | Qt::Key_3);
QMenu* settingsMenu = menuBar->addMenu("Settings");
(_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true);
_settingsAutosave->setChecked(true);
@ -1865,6 +1876,30 @@ void Application::initMenu() {
_networkAccessManager = new QNetworkAccessManager(this);
}
void Application::setListenModeNormal() {
_audio.setListenMode(AudioRingBuffer::NORMAL);
}
void Application::setListenModePoint() {
_audio.setListenMode(AudioRingBuffer::OMNI_DIRECTIONAL_POINT);
_audio.setListenRadius(1.0);
}
void Application::setListenModeSingleSource() {
_audio.setListenMode(AudioRingBuffer::SELECTED_SOURCES);
_audio.clearListenSources();
glm::vec3 mouseRayOrigin = _myAvatar.getMouseRayOrigin();
glm::vec3 mouseRayDirection = _myAvatar.getMouseRayDirection();
glm::vec3 eyePositionIgnored;
uint16_t nodeID;
if (isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeID)) {
_audio.addListenSource(nodeID);
}
}
void Application::updateFrustumRenderModeAction() {
switch (_frustumDrawingMode) {
default:
@ -1953,7 +1988,13 @@ const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
const float HEAD_SPHERE_RADIUS = 0.07;
bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition) {
static uint16_t DEFAULT_NODE_ID_REF = 1;
bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
@ -1961,7 +2002,9 @@ bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& m
glm::vec3 headPosition = avatar->getHead().getPosition();
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS)) {
eyePosition = avatar->getHead().getEyeLevelPosition();
_lookatIndicatorScale = avatar->getScale();
_lookatOtherPosition = headPosition;
nodeID = avatar->getOwningNode()->getNodeID();
return true;
}
}
@ -1971,11 +2014,13 @@ bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& m
void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera) {
const float DISTANCE_FROM_HEAD_SPHERE = 0.1f;
const float DISTANCE_FROM_HEAD_SPHERE = 0.1f * _lookatIndicatorScale;
const float INDICATOR_RADIUS = 0.1f * _lookatIndicatorScale;
const float YELLOW[] = { 1.0f, 1.0f, 0.0f };
const int NUM_SEGMENTS = 30;
glm::vec3 haloOrigin(pointOfInterest.x, pointOfInterest.y + DISTANCE_FROM_HEAD_SPHERE, pointOfInterest.z);
glColor3f(YELLOW[0], YELLOW[1], YELLOW[2]);
renderCircle(haloOrigin, 0.1f, glm::vec3(0.0f, 1.0f, 0.0f), 30);
renderCircle(haloOrigin, INDICATOR_RADIUS, IDENTITY_UP, NUM_SEGMENTS);
}
void Application::update(float deltaTime) {
@ -2005,7 +2050,9 @@ void Application::update(float deltaTime) {
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 eyePosition;
if ((_isLookingAtOtherAvatar = isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePosition))) {
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePosition);
if (_isLookingAtOtherAvatar) {
// If the mouse is over another avatar's head...
glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
@ -2022,7 +2069,7 @@ void Application::update(float deltaTime) {
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 up = orientation * IDENTITY_UP;
glm::vec3 towardVoxel = getMouseVoxelWorldCoordinates(_mouseVoxelDragging)
- _myAvatar.getCameraPosition(); // is this an error? getCameraPosition dne
- _myAvatar.getCameraPosition();
towardVoxel = front * glm::length(towardVoxel);
glm::vec3 lateralToVoxel = glm::cross(up, glm::normalize(towardVoxel)) * glm::length(towardVoxel);
_voxelThrust = glm::vec3(0, 0, 0);
@ -2221,7 +2268,7 @@ void Application::update(float deltaTime) {
_audio.eventuallyAnalyzePing();
#endif
if (TESTING_PARTICLE_SYSTEM) {
if (_renderParticleSystemOn->isChecked()) {
updateParticleSystem(deltaTime);
}
}
@ -2270,7 +2317,9 @@ void Application::updateAvatar(float deltaTime) {
_viewFrustum.computePickRay(MIDPOINT_OF_SCREEN, MIDPOINT_OF_SCREEN, screenCenterRayOrigin, screenCenterRayDirection);
glm::vec3 eyePosition;
if ((_isLookingAtOtherAvatar = isLookingAtOtherAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition))) {
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(screenCenterRayOrigin, screenCenterRayDirection, eyePosition);
if (_isLookingAtOtherAvatar) {
glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
}
@ -2298,7 +2347,7 @@ void Application::updateAvatar(float deltaTime) {
// actually need to calculate the view frustum planes to send these details
// to the server.
loadViewFrustum(_myCamera, _viewFrustum);
_myAvatar.setCameraPosition(_viewFrustum.getPosition()); // setCameraPosition() dne
_myAvatar.setCameraPosition(_viewFrustum.getPosition());
_myAvatar.setCameraOrientation(_viewFrustum.getOrientation());
_myAvatar.setCameraFov(_viewFrustum.getFieldOfView());
_myAvatar.setCameraAspectRatio(_viewFrustum.getAspectRatio());
@ -2308,7 +2357,7 @@ void Application::updateAvatar(float deltaTime) {
NodeList* nodeList = NodeList::getInstance();
if (nodeList->getOwnerID() != UNKNOWN_NODE_ID) {
// if I know my ID, send head/hand data to the avatar mixer and voxel server
unsigned char broadcastString[200];
unsigned char broadcastString[MAX_PACKET_SIZE];
unsigned char* endOfBroadcastStringWrite = broadcastString;
endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA);
@ -2627,13 +2676,14 @@ void Application::displaySide(Camera& whichCamera) {
if (_mouseVoxel.s != 0) {
glDisable(GL_LIGHTING);
glPushMatrix();
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (_addVoxelMode->isChecked()) {
// use a contrasting color so that we can see what we're doing
glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128);
} else {
glColor3ub(_mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue);
}
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f,
_mouseVoxel.y + _mouseVoxel.s*0.5f,
_mouseVoxel.z + _mouseVoxel.s*0.5f);
@ -2668,25 +2718,23 @@ void Application::displaySide(Camera& whichCamera) {
_myAvatar.getHead().setLookAtPosition(_myCamera.getPosition());
}
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
if (_renderLookatIndicatorOn->isChecked() && _isLookingAtOtherAvatar) {
renderLookatIndicator(_lookatOtherPosition, whichCamera);
}
}
if (TESTING_PARTICLE_SYSTEM) {
if (_renderParticleSystemOn->isChecked()) {
if (_particleSystemInitialized) {
_particleSystem.render();
}
}
// Render the world box
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); }
// brad's frustum for debugging
if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum);
}
void Application::displayOverlay() {
@ -3609,56 +3657,57 @@ void Application::exportSettings() {
}
void Application::updateParticleSystem(float deltaTime) {
if (!_particleSystemInitialized) {
const int LIFESPAN_IN_SECONDS = 100000.0f;
const float EMIT_RATE_IN_SECONDS = 10000.0;
// create a stable test emitter and spit out a bunch of particles
_coolDemoParticleEmitter = _particleSystem.addEmitter();
if (_coolDemoParticleEmitter != -1) {
_particleSystem.setShowingEmitter(_coolDemoParticleEmitter, true);
glm::vec3 particleEmitterPosition = glm::vec3(5.0f, 1.0f, 5.0f);
_particleSystem.setEmitterPosition(_coolDemoParticleEmitter, particleEmitterPosition);
glm::vec3 velocity(0.0f, 0.1f, 0.0f);
float lifespan = 100000.0f;
_particleSystem.emitParticlesNow(_coolDemoParticleEmitter, 1500, velocity, lifespan);
_particleSystem.setEmitterPosition (_coolDemoParticleEmitter, particleEmitterPosition);
_particleSystem.setEmitterParticleLifespan(_coolDemoParticleEmitter, LIFESPAN_IN_SECONDS);
_particleSystem.setEmitterThrust (_coolDemoParticleEmitter, 0.0f);
_particleSystem.setEmitterRate (_coolDemoParticleEmitter, EMIT_RATE_IN_SECONDS); // to emit a pile o particles now
}
// signal that the particle system has been initialized
_particleSystemInitialized = true;
} else {
// update the particle system
static float t = 0.0f;
t += deltaTime;
static bool emitting = true;
static float effectsTimer = 0.0f;
effectsTimer += deltaTime;
if (_coolDemoParticleEmitter != -1) {
glm::vec3 tilt = glm::vec3
(
30.0f * sinf( t * 0.55f ),
0.0f,
30.0f * cosf( t * 0.75f )
);
_particleSystem.setEmitterRotation(_coolDemoParticleEmitter, glm::quat(glm::radians(tilt)));
_particleSystem.setEmitterDirection(_coolDemoParticleEmitter, glm::vec3(0.0f, 1.0f, 0.0f));
ParticleSystem::ParticleAttributes attributes;
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
attributes.gravity = 0.0f + 0.05f * sinf( t * 0.52f );
attributes.airFriction = 2.5 + 2.0f * sinf( t * 0.32f );
attributes.jitter = 0.05f + 0.05f * sinf( t * 0.42f );
attributes.emitterAttraction = 0.015f + 0.015f * cosf( t * 0.6f );
attributes.tornadoForce = 0.0f + 0.03f * sinf( t * 0.7f );
attributes.neighborAttraction = 0.1f + 0.1f * cosf( t * 0.8f );
attributes.neighborRepulsion = 0.2f + 0.2f * sinf( t * 0.4f );
attributes.gravity = 0.0f + 0.05f * sinf( effectsTimer * 0.52f );
attributes.airFriction = 2.5 + 2.0f * sinf( effectsTimer * 0.32f );
attributes.jitter = 0.05f + 0.05f * sinf( effectsTimer * 0.42f );
attributes.emitterAttraction = 0.015f + 0.015f * cosf( effectsTimer * 0.6f );
attributes.tornadoForce = 0.0f + 0.03f * sinf( effectsTimer * 0.7f );
attributes.neighborAttraction = 0.1f + 0.1f * cosf( effectsTimer * 0.8f );
attributes.neighborRepulsion = 0.2f + 0.2f * sinf( effectsTimer * 0.4f );
attributes.bounce = 1.0f;
attributes.usingCollisionSphere = true;
attributes.collisionSpherePosition = glm::vec3( 5.0f, 0.5f, 5.0f );
attributes.collisionSphereRadius = 0.5f;
attributes.usingCollisionPlane = true;
attributes.collisionPlanePosition = glm::vec3( 5.0f, 0.0f, 5.0f );
attributes.collisionPlaneNormal = glm::vec3( 0.0f, 1.0f, 0.0f );
if (attributes.gravity < 0.0f) {
attributes.gravity = 0.0f;
@ -3669,6 +3718,15 @@ void Application::updateParticleSystem(float deltaTime) {
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_particleSystem.simulate(deltaTime);
const float EMIT_RATE_IN_SECONDS = 0.0;
if (_coolDemoParticleEmitter != -1) {
if (emitting) {
_particleSystem.setEmitterRate(_coolDemoParticleEmitter, EMIT_RATE_IN_SECONDS); // stop emitter
emitting = false;
}
}
}
}

View file

@ -174,6 +174,10 @@ private slots:
void copyVoxels();
void pasteVoxels();
void runTests();
void setListenModeNormal();
void setListenModePoint();
void setListenModeSingleSource();
void renderCoverageMap();
void renderCoverageMapsRecursively(CoverageMap* map);
@ -203,7 +207,9 @@ private:
void init();
void update(float deltaTime);
bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, glm::vec3& eyePosition);
bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID);
void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera);
void updateAvatar(float deltaTime);
void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
@ -261,6 +267,7 @@ private:
QAction* _renderFrameTimerOn; // Whether to show onscreen text overlay with stats
QAction* _renderLookatOn; // Whether to show lookat vectors from avatar eyes if looking at something
QAction* _renderLookatIndicatorOn;
QAction* _renderParticleSystemOn;
QAction* _manualFirstPerson; // Whether to force first-person mode
QAction* _manualThirdPerson; // Whether to force third-person mode
QAction* _logOn; // Whether to show on-screen log
@ -378,6 +385,7 @@ private:
bool _isLookingAtOtherAvatar;
glm::vec3 _lookatOtherPosition;
float _lookatIndicatorScale;
bool _paintOn; // Whether to paint voxels as you fly around
unsigned char _dominantColor; // The dominant color of the voxel we're painting

View file

@ -112,7 +112,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
// we need the amount of bytes in the buffer + 1 for type
// + 12 for 3 floats for position + float for bearing + 1 attenuation byte
unsigned char dataPacket[BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes];
unsigned char dataPacket[MAX_PACKET_SIZE];
PACKET_TYPE packetType = (Application::getInstance()->shouldEchoAudio())
? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
@ -120,6 +120,33 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, packetType);
// pack Source Data
uint16_t ownerID = NodeList::getInstance()->getOwnerID();
memcpy(currentPacketPtr, &ownerID, sizeof(ownerID));
currentPacketPtr += (sizeof(ownerID));
leadingBytes += (sizeof(ownerID));
// pack Listen Mode Data
memcpy(currentPacketPtr, &_listenMode, sizeof(_listenMode));
currentPacketPtr += (sizeof(_listenMode));
leadingBytes += (sizeof(_listenMode));
if (_listenMode == AudioRingBuffer::OMNI_DIRECTIONAL_POINT) {
memcpy(currentPacketPtr, &_listenRadius, sizeof(_listenRadius));
currentPacketPtr += (sizeof(_listenRadius));
leadingBytes += (sizeof(_listenRadius));
} else if (_listenMode == AudioRingBuffer::SELECTED_SOURCES) {
int listenSourceCount = _listenSources.size();
memcpy(currentPacketPtr, &listenSourceCount, sizeof(listenSourceCount));
currentPacketPtr += (sizeof(listenSourceCount));
leadingBytes += (sizeof(listenSourceCount));
for (int i = 0; i < listenSourceCount; i++) {
memcpy(currentPacketPtr, &_listenSources[i], sizeof(_listenSources[i]));
currentPacketPtr += sizeof(_listenSources[i]);
leadingBytes += sizeof(_listenSources[i]);
}
}
// memcpy the three float positions
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
currentPacketPtr += (sizeof(headPosition));
@ -309,6 +336,24 @@ void Audio::reset() {
_ringBuffer.reset();
}
void Audio::addListenSource(int sourceID) {
_listenSources.push_back(sourceID);
}
void Audio::clearListenSources() {
_listenSources.clear();
}
void Audio::removeListenSource(int sourceID) {
for (int i = 0; i < _listenSources.size(); i++) {
if (_listenSources[i] == sourceID) {
_listenSources.erase(_listenSources.begin() + i);
return;
}
}
}
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_stream(NULL),
_ringBuffer(true),
@ -338,7 +383,9 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_collisionSoundNoise(0.0f),
_collisionSoundDuration(0.0f),
_proceduralEffectSample(0),
_heartbeatMagnitude(0.0f)
_heartbeatMagnitude(0.0f),
_listenMode(AudioRingBuffer::NORMAL),
_listenRadius(0.0f)
{
outputPortAudioError(Pa_Initialize());

View file

@ -9,6 +9,7 @@
#ifndef __interface__Audio__
#define __interface__Audio__
#include <vector>
#include <portaudio.h>
#include <AudioRingBuffer.h>
#include <StdDev.h>
@ -54,6 +55,11 @@ public:
// The results of the analysis are written to the log.
bool eventuallyAnalyzePing();
void setListenMode(AudioRingBuffer::ListenMode mode) { _listenMode = mode; };
void setListenRadius(float radius) { _listenRadius = radius; };
void addListenSource(int sourceID);
void removeListenSource(int sourceID);
void clearListenSources();
private:
PaStream* _stream;
@ -90,6 +96,10 @@ private:
float _collisionSoundDuration;
int _proceduralEffectSample;
float _heartbeatMagnitude;
AudioRingBuffer::ListenMode _listenMode;
float _listenRadius;
std::vector<int> _listenSources;
// Audio callback in class context.
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);

0
interface/src/LeapManager.cpp Executable file → Normal file
View file

View file

@ -11,77 +11,89 @@
#include "ParticleSystem.h"
#include "Application.h"
const float DEFAULT_PARTICLE_RADIUS = 0.01f;
const float DEFAULT_PARTICLE_BOUNCE = 1.0f;
const float DEFAULT_PARTICLE_AIR_FRICTION = 2.0f;
const float DEFAULT_PARTICLE_RADIUS = 0.01f;
const float DEFAULT_PARTICLE_BOUNCE = 1.0f;
const float DEFAULT_PARTICLE_AIR_FRICTION = 2.0f;
const float DEFAULT_PARTICLE_LIFESPAN = 1.0f;
const int DEFAULT_PARTICLE_SPHERE_RESOLUTION = 6;
const float DEFAULT_EMITTER_RENDER_LENGTH = 0.2f;
ParticleSystem::ParticleSystem() {
_timer = 0.0f;
_numEmitters = 0;
_numParticles = 0;
_upDirection = glm::vec3(0.0f, 1.0f, 0.0f); // default
for (unsigned int emitterIndex = 0; emitterIndex < MAX_EMITTERS; emitterIndex++) {
_emitter[emitterIndex].position = glm::vec3(0.0f, 0.0f, 0.0f);
_emitter[emitterIndex].rotation = glm::quat();
_emitter[emitterIndex].visible = false;
_emitter[emitterIndex].baseParticle.alive = false;
_emitter[emitterIndex].baseParticle.age = 0.0f;
_emitter[emitterIndex].baseParticle.lifespan = 0.0f;
_emitter[emitterIndex].baseParticle.radius = 0.0f;
_emitter[emitterIndex].baseParticle.emitterIndex = 0;
_emitter[emitterIndex].baseParticle.position = glm::vec3(0.0f, 0.0f, 0.0f);
_emitter[emitterIndex].baseParticle.velocity = glm::vec3(0.0f, 0.0f, 0.0f);
Emitter * e = &_emitter[emitterIndex];
e->position = glm::vec3(0.0f, 0.0f, 0.0f);
e->previousPosition = glm::vec3(0.0f, 0.0f, 0.0f);
e->direction = glm::vec3(0.0f, 1.0f, 0.0f);
e->visible = false;
e->particleResolution = DEFAULT_PARTICLE_SPHERE_RESOLUTION;
e->particleLifespan = DEFAULT_PARTICLE_LIFESPAN;
e->showingBaseParticle = false;
e->emitReserve = 0.0;
e->thrust = 0.0f;
e->rate = 0.0f;
e->currentParticle = 0;
e->particleRenderStyle = PARTICLE_RENDER_STYLE_SPHERE;
e->numParticlesEmittedThisTime = 0;
for (int lifeStage = 0; lifeStage<NUM_PARTICLE_LIFE_STAGES; lifeStage++) {
ParticleAttributes * a = &_emitter[emitterIndex].particleAttributes[lifeStage];
a->radius = DEFAULT_PARTICLE_RADIUS;
a->color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
a->bounce = DEFAULT_PARTICLE_BOUNCE;
a->airFriction = DEFAULT_PARTICLE_AIR_FRICTION;
a->gravity = 0.0f;
a->jitter = 0.0f;
a->emitterAttraction = 0.0f;
a->tornadoForce = 0.0f;
a->neighborAttraction = 0.0f;
a->neighborRepulsion = 0.0f;
a->collisionSphereRadius = 0.0f;
a->collisionSpherePosition = glm::vec3(0.0f, 0.0f, 0.0f);
a->usingCollisionSphere = false;
for (int lifeStage = 0; lifeStage < NUM_PARTICLE_LIFE_STAGES; lifeStage++) {
setParticleAttributesToDefault(&_emitter[emitterIndex].particleAttributes[lifeStage]);
}
};
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
_particle[p].alive = false;
_particle[p].age = 0.0f;
_particle[p].lifespan = 0.0f;
_particle[p].radius = 0.0f;
_particle[p].emitterIndex = 0;
_particle[p].position = glm::vec3(0.0f, 0.0f, 0.0f);
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
_particle[p].alive = false;
_particle[p].age = 0.0f;
_particle[p].radius = 0.0f;
_particle[p].emitterIndex = 0;
_particle[p].previousParticle = NULL_PARTICLE;
_particle[p].position = glm::vec3(0.0f, 0.0f, 0.0f);
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
}
}
int ParticleSystem::addEmitter() {
_numEmitters ++;
if (_numEmitters > MAX_EMITTERS) {
return -1;
if (_numEmitters < MAX_EMITTERS) {
_numEmitters ++;
return _numEmitters - 1;
}
return _numEmitters - 1;
return NULL_EMITTER;
}
void ParticleSystem::simulate(float deltaTime) {
_timer += deltaTime;
// emit particles
for (int e = 0; e < _numEmitters; e++) {
assert(e >= 0);
assert(e <= MAX_EMITTERS);
assert(_emitter[e].rate >= 0);
_emitter[e].emitReserve += _emitter[e].rate * deltaTime;
_emitter[e].numParticlesEmittedThisTime = (int)_emitter[e].emitReserve;
_emitter[e].emitReserve -= _emitter[e].numParticlesEmittedThisTime;
for (int p = 0; p < _emitter[e].numParticlesEmittedThisTime; p++) {
float timeFraction = (float)p / (float)_emitter[e].numParticlesEmittedThisTime;
createParticle(e, timeFraction);
}
}
// update particles
for (unsigned int p = 0; p < _numParticles; p++) {
if (_particle[p].alive) {
if (_particle[p].age > _particle[p].lifespan) {
for (int p = 0; p < MAX_PARTICLES; p++) {
if (_particle[p].alive) {
if (_particle[p].age > _emitter[_particle[p].emitterIndex].particleLifespan) {
killParticle(p);
} else {
updateParticle(p, deltaTime);
@ -90,55 +102,91 @@ void ParticleSystem::simulate(float deltaTime) {
}
}
void ParticleSystem::emitParticlesNow(int e, int num, glm::vec3 velocity, float lifespan) {
for (unsigned int p = 0; p < num; p++) {
createParticle(e, velocity, lifespan);
}
}
void ParticleSystem::createParticle(int e, glm::vec3 velocity, float lifespan) {
void ParticleSystem::createParticle(int e, float timeFraction) {
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
if (!_particle[p].alive) {
_particle[p].emitterIndex = e;
_particle[p].lifespan = lifespan;
_particle[p].alive = true;
_particle[p].age = 0.0f;
_particle[p].velocity = velocity;
_particle[p].position = _emitter[e].position;
_particle[p].radius = _emitter[e].particleAttributes[0].radius;
_particle[p].color = _emitter[e].particleAttributes[0].color;
_numParticles ++;
assert(_numParticles <= MAX_PARTICLES);
_particle[p].emitterIndex = e;
_particle[p].alive = true;
_particle[p].age = 0.0f;
_particle[p].velocity = _emitter[e].direction * _emitter[e].thrust;
_particle[p].position = _emitter[e].previousPosition + timeFraction * (_emitter[e].position - _emitter[e].previousPosition);
_particle[p].radius = _emitter[e].particleAttributes[PARTICLE_LIFESTAGE_0].radius;
_particle[p].color = _emitter[e].particleAttributes[PARTICLE_LIFESTAGE_0].color;
_particle[p].previousParticle = NULL_PARTICLE;
return;
if (_particle[_emitter[e].currentParticle].alive) {
if (_particle[_emitter[e].currentParticle].emitterIndex == e) {
_particle[p].previousParticle = _emitter[e].currentParticle;
}
}
_emitter[e].currentParticle = p;
break;
}
}
}
void ParticleSystem::killParticle(int p) {
assert( p >= 0);
assert( p < MAX_PARTICLES);
assert( _numParticles > 0);
assert(p >= 0);
assert(p < MAX_PARTICLES);
_particle[p].alive = false;
_numParticles --;
}
_particle[p].alive = false;
_particle[p].previousParticle = NULL_PARTICLE;
_particle[p].position = _emitter[_particle[p].emitterIndex].position;
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
_particle[p].age = 0.0f;
_particle[p].emitterIndex = NULL_PARTICLE;
_particle[p].color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
_particle[p].radius = 0.0f;
}
void ParticleSystem::setEmitterPosition(int emitterIndex, glm::vec3 position) {
_emitter[emitterIndex].previousPosition = _emitter[emitterIndex].position;
_emitter[emitterIndex].position = position;
}
void ParticleSystem::setParticleAttributes(int emitterIndex, ParticleAttributes attributes) {
for (int lifeStage = 0; lifeStage < NUM_PARTICLE_LIFE_STAGES; lifeStage ++ ) {
setParticleAttributes(emitterIndex, lifeStage, attributes);
for (int lifeStage = 0; lifeStage < NUM_PARTICLE_LIFE_STAGES; lifeStage ++) {
setParticleAttributes(emitterIndex, (ParticleLifeStage)lifeStage, attributes);
}
}
void ParticleSystem::setParticleAttributes(int emitterIndex, int lifeStage, ParticleAttributes attributes) {
void ParticleSystem::setParticleAttributesToDefault(ParticleAttributes * a) {
a->radius = DEFAULT_PARTICLE_RADIUS;
a->color = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
a->bounce = DEFAULT_PARTICLE_BOUNCE;
a->airFriction = DEFAULT_PARTICLE_AIR_FRICTION;
a->gravity = 0.0f;
a->jitter = 0.0f;
a->emitterAttraction = 0.0f;
a->tornadoForce = 0.0f;
a->neighborAttraction = 0.0f;
a->neighborRepulsion = 0.0f;
a->collisionSphereRadius = 0.0f;
a->collisionSpherePosition = glm::vec3(0.0f, 0.0f, 0.0f);
a->usingCollisionSphere = false;
a->collisionPlaneNormal = _upDirection;
a->collisionPlanePosition = glm::vec3(0.0f, 0.0f, 0.0f);
a->usingCollisionPlane = false;
a->modulationAmplitude = 0.0f;
a->modulationRate = 0.0;
a->modulationStyle = COLOR_MODULATION_STYLE_NULL;
}
void ParticleSystem::setParticleAttributes(int emitterIndex, ParticleLifeStage lifeStage, ParticleAttributes attributes) {
assert(lifeStage >= 0);
assert(lifeStage < NUM_PARTICLE_LIFE_STAGES);
ParticleAttributes * a = &_emitter[emitterIndex].particleAttributes[lifeStage];
@ -155,155 +203,196 @@ void ParticleSystem::setParticleAttributes(int emitterIndex, int lifeStage, Part
a->usingCollisionSphere = attributes.usingCollisionSphere;
a->collisionSpherePosition = attributes.collisionSpherePosition;
a->collisionSphereRadius = attributes.collisionSphereRadius;
a->usingCollisionPlane = attributes.usingCollisionPlane;
a->collisionPlanePosition = attributes.collisionPlanePosition;
a->collisionPlaneNormal = attributes.collisionPlaneNormal;
a->modulationAmplitude = attributes.modulationAmplitude;
a->modulationRate = attributes.modulationRate;
a->modulationStyle = attributes.modulationStyle;
}
void ParticleSystem::updateParticle(int p, float deltaTime) {
assert(_particle[p].age <= _particle[p].lifespan);
float ageFraction = _particle[p].age / _particle[p].lifespan;
int lifeStage = (int)( ageFraction * (NUM_PARTICLE_LIFE_STAGES-1) );
float lifeStageFraction = ageFraction * ( NUM_PARTICLE_LIFE_STAGES - 1 ) - lifeStage;
_particle[p].radius
= _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage ].radius * (1.0f - lifeStageFraction)
+ _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage+1].radius * lifeStageFraction;
_particle[p].color
= _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage ].color * (1.0f - lifeStageFraction)
+ _emitter[_particle[p].emitterIndex].particleAttributes[lifeStage+1].color * lifeStageFraction;
Emitter myEmitter = _emitter[_particle[p].emitterIndex];
// apply random jitter
float j = myEmitter.particleAttributes[lifeStage].jitter;
_particle[p].velocity +=
glm::vec3
(
-j * ONE_HALF + j * randFloat(),
-j * ONE_HALF + j * randFloat(),
-j * ONE_HALF + j * randFloat()
) * deltaTime;
// apply attraction to home position
glm::vec3 vectorToHome = myEmitter.position - _particle[p].position;
_particle[p].velocity += vectorToHome * myEmitter.particleAttributes[lifeStage].emitterAttraction * deltaTime;
// apply neighbor attraction
int neighbor = p + 1;
if (neighbor == _numParticles ) {
neighbor = 0;
}
if ( _particle[neighbor].emitterIndex == _particle[p].emitterIndex) {
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
_particle[p].velocity -= vectorToNeighbor * myEmitter.particleAttributes[lifeStage].neighborAttraction * deltaTime;
assert(_particle[p].age <= myEmitter.particleLifespan);
float distanceToNeighbor = glm::length(vectorToNeighbor);
if (distanceToNeighbor > 0.0f) {
_particle[neighbor].velocity += (vectorToNeighbor / ( 1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes[lifeStage].neighborRepulsion * deltaTime;
}
}
// apply tornado force
glm::vec3 emitterUp = myEmitter.rotation * IDENTITY_UP;
glm::vec3 tornadoDirection = glm::cross(vectorToHome, emitterUp);
_particle[p].velocity += tornadoDirection * myEmitter.particleAttributes[lifeStage].tornadoForce * deltaTime;
float ageFraction = 0.0f;
int lifeStage = 0;
float lifeStageFraction = 0.0f;
// apply air friction
float drag = 1.0 - myEmitter.particleAttributes[lifeStage].airFriction * deltaTime;
if (drag < 0.0f) {
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} else {
_particle[p].velocity *= drag;
}
// apply gravity
_particle[p].velocity -= _upDirection * myEmitter.particleAttributes[lifeStage].gravity * deltaTime;
// update position by velocity
_particle[p].position += _particle[p].velocity;
// collision with ground
if (_particle[p].position.y < _particle[p].radius) {
_particle[p].position.y = _particle[p].radius;
if (_emitter[_particle[p].emitterIndex].particleLifespan > 0.0) {
if (_particle[p].velocity.y < 0.0f) {
_particle[p].velocity.y *= -myEmitter.particleAttributes[lifeStage].bounce;
}
}
// collision with sphere
if (myEmitter.particleAttributes[lifeStage].usingCollisionSphere) {
glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - _particle[p].position;
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
float combinedRadius = myEmitter.particleAttributes[lifeStage].collisionSphereRadius + _particle[p].radius;
if (distanceToSphereCenter < combinedRadius) {
ageFraction = _particle[p].age / myEmitter.particleLifespan;
lifeStage = (int)(ageFraction * (NUM_PARTICLE_LIFE_STAGES - 1));
lifeStageFraction = ageFraction * (NUM_PARTICLE_LIFE_STAGES - 1) - lifeStage;
// adjust radius
_particle[p].radius
= myEmitter.particleAttributes[lifeStage ].radius * (1.0f - lifeStageFraction)
+ myEmitter.particleAttributes[lifeStage+1].radius * lifeStageFraction;
if (distanceToSphereCenter > 0.0f){
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
_particle[p].position = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - directionToSphereCenter * combinedRadius;
// apply random jitter
float j = myEmitter.particleAttributes[lifeStage].jitter;
_particle[p].velocity +=
glm::vec3
(
-j * ONE_HALF + j * randFloat(),
-j * ONE_HALF + j * randFloat(),
-j * ONE_HALF + j * randFloat()
) * deltaTime;
// apply attraction to home position
glm::vec3 vectorToHome = myEmitter.position - _particle[p].position;
_particle[p].velocity += vectorToHome * myEmitter.particleAttributes[lifeStage].emitterAttraction * deltaTime;
// apply neighbor attraction
int neighbor = p + 1;
if (neighbor == MAX_PARTICLES) {
neighbor = 0;
}
if (_particle[neighbor].emitterIndex == _particle[p].emitterIndex) {
glm::vec3 vectorToNeighbor = _particle[p].position - _particle[neighbor].position;
_particle[p].velocity -= vectorToNeighbor * myEmitter.particleAttributes[lifeStage].neighborAttraction * deltaTime;
float distanceToNeighbor = glm::length(vectorToNeighbor);
if (distanceToNeighbor > 0.0f) {
_particle[neighbor].velocity += (vectorToNeighbor / (1.0f + distanceToNeighbor * distanceToNeighbor)) * myEmitter.particleAttributes[lifeStage].neighborRepulsion * deltaTime;
}
}
// apply tornado force
glm::vec3 tornadoDirection = glm::cross(vectorToHome, myEmitter.direction);
_particle[p].velocity += tornadoDirection * myEmitter.particleAttributes[lifeStage].tornadoForce * deltaTime;
// apply air friction
float drag = 1.0 - myEmitter.particleAttributes[lifeStage].airFriction * deltaTime;
if (drag < 0.0f) {
_particle[p].velocity = glm::vec3(0.0f, 0.0f, 0.0f);
} else {
_particle[p].velocity *= drag;
}
// apply gravity
_particle[p].velocity -= _upDirection * myEmitter.particleAttributes[lifeStage].gravity * deltaTime;
// update position by velocity
_particle[p].position += _particle[p].velocity;
// collision with the plane surface
if (myEmitter.particleAttributes[lifeStage].usingCollisionPlane) {
glm::vec3 vectorFromParticleToPlanePosition = _particle[p].position - myEmitter.particleAttributes[lifeStage].collisionPlanePosition;
glm::vec3 normal = myEmitter.particleAttributes[lifeStage].collisionPlaneNormal;
float dot = glm::dot(vectorFromParticleToPlanePosition, normal);
if (dot < _particle[p].radius) {
_particle[p].position += normal * (_particle[p].radius - dot);
float planeNormalComponentOfVelocity = glm::dot(_particle[p].velocity, normal);
_particle[p].velocity -= normal * planeNormalComponentOfVelocity * (1.0f + myEmitter.particleAttributes[lifeStage].bounce);
}
}
// collision with sphere
if (myEmitter.particleAttributes[lifeStage].usingCollisionSphere) {
glm::vec3 vectorToSphereCenter = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - _particle[p].position;
float distanceToSphereCenter = glm::length(vectorToSphereCenter);
float combinedRadius = myEmitter.particleAttributes[lifeStage].collisionSphereRadius + _particle[p].radius;
if (distanceToSphereCenter < combinedRadius) {
if (distanceToSphereCenter > 0.0f){
glm::vec3 directionToSphereCenter = vectorToSphereCenter / distanceToSphereCenter;
_particle[p].position = myEmitter.particleAttributes[lifeStage].collisionSpherePosition - directionToSphereCenter * combinedRadius;
}
}
}
}
// adjust color
_particle[p].color
= myEmitter.particleAttributes[lifeStage ].color * (1.0f - lifeStageFraction)
+ myEmitter.particleAttributes[lifeStage+1].color * lifeStageFraction;
// apply color modulation
if (myEmitter.particleAttributes[lifeStage ].modulationAmplitude > 0.0f) {
float modulation = 0.0f;
float radian = _timer * myEmitter.particleAttributes[lifeStage ].modulationRate * PI_TIMES_TWO;
if (myEmitter.particleAttributes[lifeStage ].modulationStyle == COLOR_MODULATION_STYLE_LIGHNTESS_PULSE) {
if (sinf(radian) > 0.0f) {
modulation = myEmitter.particleAttributes[lifeStage].modulationAmplitude;
}
} else if (myEmitter.particleAttributes[lifeStage].modulationStyle == COLOR_MODULATION_STYLE_LIGHTNESS_WAVE) {
float a = myEmitter.particleAttributes[lifeStage].modulationAmplitude;
modulation = a * ONE_HALF + sinf(radian) * a * ONE_HALF;
}
_particle[p].color.r += modulation;
_particle[p].color.g += modulation;
_particle[p].color.b += modulation;
_particle[p].color.a += modulation;
if (_particle[p].color.r > 1.0f) {_particle[p].color.r = 1.0f;}
if (_particle[p].color.g > 1.0f) {_particle[p].color.g = 1.0f;}
if (_particle[p].color.b > 1.0f) {_particle[p].color.b = 1.0f;}
if (_particle[p].color.a > 1.0f) {_particle[p].color.a = 1.0f;}
}
// do this at the end...
_particle[p].age += deltaTime;
_particle[p].age += deltaTime;
}
void ParticleSystem::setEmitterBaseParticle(int emitterIndex, bool showing ) {
_emitter[emitterIndex].baseParticle.alive = true;
_emitter[emitterIndex].baseParticle.emitterIndex = emitterIndex;
void ParticleSystem::killAllParticles() {
for (int e = 0; e < _numEmitters; e++) {
_emitter[e].currentParticle = NULL_PARTICLE;
_emitter[e].emitReserve = 0.0f;
_emitter[e].previousPosition = _emitter[e].position;
_emitter[e].rate = 0.0f;
_emitter[e].currentParticle = 0;
_emitter[e].numParticlesEmittedThisTime = 0;
}
for (int p = 0; p < MAX_PARTICLES; p++) {
killParticle(p);
}
}
void ParticleSystem::setEmitterBaseParticle(int emitterIndex, bool showing, float radius, glm::vec4 color ) {
_emitter[emitterIndex].baseParticle.alive = true;
_emitter[emitterIndex].baseParticle.emitterIndex = emitterIndex;
_emitter[emitterIndex].baseParticle.radius = radius;
_emitter[emitterIndex].baseParticle.color = color;
}
void ParticleSystem::render() {
// render the emitters
for (int e = 0; e < _numEmitters; e++) {
if (_emitter[e].baseParticle.alive) {
glColor4f(_emitter[e].baseParticle.color.r, _emitter[e].baseParticle.color.g, _emitter[e].baseParticle.color.b, _emitter[e].baseParticle.color.a );
if (_emitter[e].showingBaseParticle) {
glColor4f(_particle[0].color.r, _particle[0].color.g, _particle[0].color.b, _particle[0].color.a);
glPushMatrix();
glTranslatef(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glutSolidSphere(_emitter[e].baseParticle.radius, 6, 6);
glutSolidSphere(_particle[0].radius, _emitter[e].particleResolution, _emitter[e].particleResolution);
glPopMatrix();
}
if (_emitter[e].visible) {
renderEmitter(e, 0.2f);
renderEmitter(e, DEFAULT_EMITTER_RENDER_LENGTH);
}
};
// render the particles
for (unsigned int p = 0; p < _numParticles; p++) {
// render the particles
for (int p = 0; p < MAX_PARTICLES; p++) {
if (_particle[p].alive) {
renderParticle(p);
if (_emitter[_particle[p].emitterIndex].particleLifespan > 0.0) {
renderParticle(p);
}
}
}
}
void ParticleSystem::renderParticle(int p) {
glColor4f(_particle[p].color.r, _particle[p].color.g, _particle[p].color.b, _particle[p].color.a );
glColor4f(_particle[p].color.r, _particle[p].color.g, _particle[p].color.b, _particle[p].color.a);
if (USE_BILLBOARD_RENDERING) {
if (_emitter[_particle[p].emitterIndex].particleRenderStyle == PARTICLE_RENDER_STYLE_BILLBOARD) {
glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
glm::vec3 viewVector = _particle[p].position - cameraPosition;
float distance = glm::length(viewVector);
@ -330,49 +419,89 @@ void ParticleSystem::renderParticle(int p) {
glVertex3f(p3.x, p3.y, p3.z);
glEnd();
}
} else {
} else if (_emitter[_particle[p].emitterIndex].particleRenderStyle == PARTICLE_RENDER_STYLE_SPHERE) {
glPushMatrix();
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glutSolidSphere(_particle[p].radius, 6, 6);
glTranslatef(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glutSolidSphere(_particle[p].radius, _emitter[_particle[p].emitterIndex].particleResolution, _emitter[_particle[p].emitterIndex].particleResolution);
glPopMatrix();
if (SHOW_VELOCITY_TAILS) {
glColor4f( _particle[p].color.x, _particle[p].color.y, _particle[p].color.z, 0.5f);
glm::vec3 end = _particle[p].position - _particle[p].velocity * 2.0f;
glBegin(GL_LINES);
glVertex3f(_particle[p].position.x, _particle[p].position.y, _particle[p].position.z);
glVertex3f(end.x, end.y, end.z);
glEnd();
} else if (_emitter[_particle[p].emitterIndex].particleRenderStyle == PARTICLE_RENDER_STYLE_RIBBON) {
if (_particle[p].previousParticle != NULL_PARTICLE) {
if ((_particle[p].alive)
&& (_particle[_particle[p].previousParticle].alive)
&& (_particle[_particle[p].previousParticle].emitterIndex == _particle[p].emitterIndex)) {
glm::vec3 vectorFromPreviousParticle = _particle[p].position - _particle[_particle[p].previousParticle].position;
float distance = glm::length(vectorFromPreviousParticle);
if (distance > 0.0f) {
vectorFromPreviousParticle /= distance;
glm::vec3 up = glm::normalize(glm::cross(vectorFromPreviousParticle, _upDirection)) * _particle[p].radius;
glm::vec3 right = glm::normalize(glm::cross(up, vectorFromPreviousParticle )) * _particle[p].radius;
glm::vec3 p0Left = _particle[p ].position - right;
glm::vec3 p0Right = _particle[p ].position + right;
glm::vec3 p0Down = _particle[p ].position - up;
glm::vec3 p0Up = _particle[p ].position + up;
glm::vec3 ppLeft = _particle[_particle[p].previousParticle].position - right;
glm::vec3 ppRight = _particle[_particle[p].previousParticle].position + right;
glm::vec3 ppDown = _particle[_particle[p].previousParticle].position - up;
glm::vec3 ppUp = _particle[_particle[p].previousParticle].position + up;
glBegin(GL_TRIANGLES);
glVertex3f(p0Left.x, p0Left.y, p0Left.z );
glVertex3f(p0Right.x, p0Right.y, p0Right.z);
glVertex3f(ppLeft.x, ppLeft.y, ppLeft.z );
glVertex3f(p0Right.x, p0Right.y, p0Right.z);
glVertex3f(ppLeft.x, ppLeft.y, ppLeft.z );
glVertex3f(ppRight.x, ppRight.y, ppRight.z);
glVertex3f(p0Up.x, p0Up.y, p0Up.z );
glVertex3f(p0Down.x, p0Down.y, p0Down.z );
glVertex3f(ppDown.x, ppDown.y, ppDown.z );
glVertex3f(p0Up.x, p0Up.y, p0Up.z );
glVertex3f(ppUp.x, ppUp.y, ppUp.z );
glVertex3f(ppDown.x, ppDown.y, ppDown.z );
glVertex3f(p0Up.x, p0Up.y, p0Left.z );
glVertex3f(p0Right.x, p0Right.y, p0Right.z);
glVertex3f(p0Down.x, p0Down.y, p0Down.z );
glVertex3f(p0Up.x, p0Up.y, p0Left.z );
glVertex3f(p0Left.x, p0Left.y, p0Left.z );
glVertex3f(p0Down.x, p0Down.y, p0Down.z );
glVertex3f(ppUp.x, ppUp.y, ppLeft.z );
glVertex3f(ppRight.x, ppRight.y, ppRight.z);
glVertex3f(ppDown.x, ppDown.y, ppDown.z );
glVertex3f(ppUp.x, ppUp.y, ppLeft.z );
glVertex3f(ppLeft.x, ppLeft.y, ppLeft.z );
glVertex3f(ppDown.x, ppDown.y, ppDown.z );
glEnd();
}
}
}
}
}
void ParticleSystem::renderEmitter(int e, float size) {
glm::vec3 r = _emitter[e].rotation * IDENTITY_FRONT * size;
glm::vec3 u = _emitter[e].rotation * IDENTITY_RIGHT * size;
glm::vec3 f = _emitter[e].rotation * IDENTITY_UP * size;
glLineWidth(2.0f);
glColor3f(0.8f, 0.4, 0.4);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + r.x, _emitter[e].position.y + r.y, _emitter[e].position.z + r.z);
glEnd();
glColor3f(0.4f, 0.8, 0.4);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + u.x, _emitter[e].position.y + u.y, _emitter[e].position.z + u.z);
glEnd();
glm::vec3 v = _emitter[e].direction * size;
glColor3f(0.4f, 0.4, 0.8);
glBegin(GL_LINES);
glVertex3f(_emitter[e].position.x, _emitter[e].position.y, _emitter[e].position.z);
glVertex3f(_emitter[e].position.x + f.x, _emitter[e].position.y + f.y, _emitter[e].position.z + f.z);
glVertex3f(_emitter[e].position.x + v.x, _emitter[e].position.y + v.y, _emitter[e].position.z + v.z);
glEnd();
}
@ -380,5 +509,3 @@ void ParticleSystem::renderEmitter(int e, float size) {

View file

@ -11,76 +11,121 @@
#include <glm/gtc/quaternion.hpp>
const int MAX_PARTICLES = 5000;
const int MAX_EMITTERS = 20;
const int NUM_PARTICLE_LIFE_STAGES = 4;
const bool USE_BILLBOARD_RENDERING = false;
const bool SHOW_VELOCITY_TAILS = false;
const int NULL_EMITTER = -1;
const int NULL_PARTICLE = -1;
const int MAX_EMITTERS = 100;
enum ParticleRenderStyle
{
PARTICLE_RENDER_STYLE_SPHERE = 0,
PARTICLE_RENDER_STYLE_BILLBOARD,
PARTICLE_RENDER_STYLE_RIBBON,
NUM_PARTICLE_RENDER_STYLES
};
enum ColorModulationStyle
{
COLOR_MODULATION_STYLE_NULL = -1,
COLOR_MODULATION_STYLE_LIGHNTESS_PULSE,
COLOR_MODULATION_STYLE_LIGHTNESS_WAVE,
NUM_COLOR_MODULATION_STYLES
};
enum ParticleLifeStage
{
PARTICLE_LIFESTAGE_0 = 0,
PARTICLE_LIFESTAGE_1,
PARTICLE_LIFESTAGE_2,
PARTICLE_LIFESTAGE_3,
NUM_PARTICLE_LIFE_STAGES
};
class ParticleSystem {
public:
struct ParticleAttributes {
float radius;
glm::vec4 color;
float bounce;
float gravity;
float airFriction;
float jitter;
float emitterAttraction;
float tornadoForce;
float neighborAttraction;
float neighborRepulsion;
bool usingCollisionSphere;
glm::vec3 collisionSpherePosition;
float collisionSphereRadius;
float radius; // radius of the particle
glm::vec4 color; // color (rgba) of the particle
float bounce; // how much reflection when the particle collides with floor/ground
float gravity; // force opposite of up direction
float airFriction; // continual dampening of velocity
float jitter; // random forces on velocity
float emitterAttraction; // an attraction to the emitter position
float tornadoForce; // force perpendicular to direction axis
float neighborAttraction; // causes particle to be pulled towards next particle in list
float neighborRepulsion; // causes particle to be repelled by previous particle in list
bool usingCollisionSphere; // set to true to allow collision with a sphere
glm::vec3 collisionSpherePosition; // position of the collision sphere
float collisionSphereRadius; // radius of the collision sphere
bool usingCollisionPlane; // set to true to allow collision with a plane
glm::vec3 collisionPlanePosition; // reference position of the collision plane
glm::vec3 collisionPlaneNormal; // the surface normal of the collision plane
float modulationAmplitude; // sets the degree (from 0 to 1) of the modulating effect
float modulationRate; // the period of modulation, in seconds
ColorModulationStyle modulationStyle; // to choose between color modulation styles
};
// public methods...
ParticleSystem();
int addEmitter(); // add (create new) emitter and get its unique id
void emitParticlesNow(int emitterIndex, int numParticles, glm::vec3 velocity, float lifespan);
void simulate(float deltaTime);
void killAllParticles();
void render();
void setUpDirection(glm::vec3 upDirection) {_upDirection = upDirection;} // tell particle system which direction is up
void setEmitterBaseParticle(int emitterIndex, bool showing );
void setEmitterBaseParticle(int emitterIndex, bool showing, float radius, glm::vec4 color );
void setParticleAttributes (int emitterIndex, ParticleAttributes attributes);
void setParticleAttributes (int emitterIndex, int lifeStage, ParticleAttributes attributes);
void setEmitterPosition (int emitterIndex, glm::vec3 position) { _emitter[emitterIndex].position = position; } // set position of emitter
void setEmitterRotation (int emitterIndex, glm::quat rotation) { _emitter[emitterIndex].rotation = rotation; } // set rotation of emitter
void setShowingEmitter (int emitterIndex, bool showing ) { _emitter[emitterIndex].visible = showing; } // set its visibiity
void setUpDirection(glm::vec3 upDirection) {_upDirection = upDirection;} // tell particle system which direction is up
void setParticleAttributesToDefault(ParticleAttributes * attributes); // set these attributes to their default values
void setParticleAttributes (int emitterIndex, ParticleAttributes attributes); // set attributes for whole life of particles
void setParticleAttributes (int emitterIndex, ParticleLifeStage lifeStage, ParticleAttributes attributes); // set attributes for this life stage
void setEmitterPosition (int emitterIndex, glm::vec3 position );
void setEmitterParticleResolution (int emitterIndex, int resolution ) {_emitter[emitterIndex].particleResolution = resolution; }
void setEmitterDirection (int emitterIndex, glm::vec3 direction ) {_emitter[emitterIndex].direction = direction; }
void setShowingEmitter (int emitterIndex, bool showing ) {_emitter[emitterIndex].visible = showing; }
void setEmitterParticleLifespan (int emitterIndex, float lifespan ) {_emitter[emitterIndex].particleLifespan = lifespan; }
void setParticleRenderStyle (int emitterIndex, ParticleRenderStyle renderStyle ) {_emitter[emitterIndex].particleRenderStyle = renderStyle; }
void setEmitterThrust (int emitterIndex, float thrust ) {_emitter[emitterIndex].thrust = thrust; }
void setEmitterRate (int emitterIndex, float rate ) {_emitter[emitterIndex].rate = rate; }
void setShowingEmitterBaseParticle(int emitterIndex, bool showing ) {_emitter[emitterIndex].showingBaseParticle = showing; }
private:
struct Particle {
bool alive; // is the particle active?
glm::vec3 position; // position
glm::vec3 velocity; // velocity
glm::vec4 color; // color (rgba)
float age; // age in seconds
float radius; // radius
float lifespan; // how long this particle stays alive (in seconds)
int emitterIndex; // which emitter created this particle?
bool alive; // is the particle active?
glm::vec3 position; // position
glm::vec3 velocity; // velocity
glm::vec4 color; // color (rgba)
float age; // age in seconds
float radius; // radius
int emitterIndex; // which emitter created this particle?
int previousParticle; // the last particle that this particle's emitter emitted;
};
struct Emitter {
glm::vec3 position;
glm::quat rotation;
bool visible;
Particle baseParticle; // a non-physical particle at the emitter position
glm::vec3 position; // the position of the emitter in world coordinates
glm::vec3 previousPosition; // the position of the emitter in the previous time step
glm::vec3 direction; // a normalized vector used as an axis for particle emission and other effects
bool visible; // whether or not a line is shown indicating the emitter (indicating its direction)
float particleLifespan; // how long the particle shall live, in seconds
int particleResolution; // for sphere-based particles
float emitReserve; // baed on 'rate', this is the number of particles that need to be emitted at a given time step
int numParticlesEmittedThisTime; //the integer number of particles to emit at the preent time step
float thrust; // the initial velocity upon emitting along the emitter direction
float rate; // currently, how many particles emitted during a simulation time step
bool showingBaseParticle; // if true, a copy of particle 0 is shown on the emitter position
int currentParticle; // the index of the most recently-emitted particle
ParticleAttributes particleAttributes[NUM_PARTICLE_LIFE_STAGES]; // the attributes of particles emitted from this emitter
};
ParticleRenderStyle particleRenderStyle;
};
glm::vec3 _upDirection;
Emitter _emitter[MAX_EMITTERS];
Particle _particle[MAX_PARTICLES];
int _numParticles;
int _numEmitters;
float _timer;
// private methods
void updateParticle(int index, float deltaTime);
void createParticle(int e, glm::vec3 velocity, float lifespan);
void createParticle(int e, float timeFraction);
void killParticle(int p);
void renderEmitter(int emitterIndex, float size);
void renderParticle(int p);

View file

@ -365,7 +365,33 @@ void renderGroundPlaneGrid(float size, float impact) {
glEnd();
}
void renderMouseVoxelGrid(const float& mouseVoxelX, const float& mouseVoxelY, const float& mouseVoxelZ, const float& mouseVoxelS) {
glm::vec3 origin = glm::vec3(mouseVoxelX, mouseVoxelY, mouseVoxelZ);
glLineWidth(3.0);
const int HALF_GRID_DIMENSIONS = 4;
glBegin(GL_LINES);
glm::vec3 xColor(0.0, 0.6, 0.0);
glColor3fv(&xColor.x);
glVertex3f(origin.x + HALF_GRID_DIMENSIONS * mouseVoxelS, 0, origin.z);
glVertex3f(origin.x - HALF_GRID_DIMENSIONS * mouseVoxelS, 0, origin.z);
glm::vec3 zColor(0.0, 0.0, 0.6);
glColor3fv(&zColor.x);
glVertex3f(origin.x, 0, origin.z + HALF_GRID_DIMENSIONS * mouseVoxelS);
glVertex3f(origin.x, 0, origin.z - HALF_GRID_DIMENSIONS * mouseVoxelS);
glm::vec3 yColor(0.6, 0.0, 0.0);
glColor3fv(&yColor.x);
glVertex3f(origin.x, 0, origin.z);
glVertex3f(origin.x, origin.y, origin.z);
glEnd();
}
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness) {

View file

@ -59,8 +59,9 @@ double diffclock(timeval *clock1,timeval *clock2);
void renderGroundPlaneGrid(float size, float impact);
void renderCollisionOverlay(int width, int height, float magnitude);
void renderMouseVoxelGrid(const float& mouseVoxelX, const float& mouseVoxelY, const float& mouseVoxelZ, const float& mouseVoxelS);
void renderCollisionOverlay(int width, int height, float magnitude);
void renderDiskShadow(glm::vec3 position, glm::vec3 upDirection, float radius, float darkness);

View file

@ -58,6 +58,32 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
_tree = new VoxelTree();
pthread_mutex_init(&_bufferWriteLock, NULL);
pthread_mutex_init(&_treeLock, NULL);
VoxelNode::addDeleteHook(this);
_abandonedVBOSlots = 0;
}
void VoxelSystem::nodeDeleted(VoxelNode* node) {
if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {
freeBufferIndex(node->getBufferIndex());
}
}
void VoxelSystem::freeBufferIndex(glBufferIndex index) {
_freeIndexes.push_back(index);
}
void VoxelSystem::clearFreeBufferIndexes() {
for (int i = 0; i < _freeIndexes.size(); i++) {
glBufferIndex nodeIndex = _freeIndexes[i];
glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
float voxelScale = 0;
_writeVoxelDirtyArray[nodeIndex] = true;
nodeColor color = {0, 0, 0, 0};
updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
_abandonedVBOSlots++;
}
_freeIndexes.clear();
}
VoxelSystem::~VoxelSystem() {
@ -70,6 +96,8 @@ VoxelSystem::~VoxelSystem() {
delete _tree;
pthread_mutex_destroy(&_bufferWriteLock);
pthread_mutex_destroy(&_treeLock);
VoxelNode::removeDeleteHook(this);
}
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
@ -173,6 +201,9 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
uint64_t start = usecTimestampNow();
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
// clear up the VBOs for any nodes that have been recently deleted.
clearFreeBufferIndexes();
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
@ -182,7 +213,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
&& !isViewChanging() && hasViewChanged()) {
&& !isViewChanging()) {
_lastViewCulling = start;
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
@ -212,6 +243,10 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
}
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
if (_writeRenderFullVBO) {
_abandonedVBOSlots = 0; // reset the count of our abandoned slots
}
// since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are
// ok again, until/unless we call removeOutOfView()
@ -240,12 +275,19 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
void VoxelSystem::cleanupRemovedVoxels() {
PerformanceWarning warn(_renderWarningsOn, "cleanupRemovedVoxels()");
// This handles cleanup of voxels that were culled as part of our regular out of view culling operation
if (!_removedVoxels.isEmpty()) {
while (!_removedVoxels.isEmpty()) {
delete _removedVoxels.extract();
}
_writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
}
// we also might have VBO slots that have been abandoned, if too many of our VBO slots
// are abandonded we want to rerender our full VBOs
const float TOO_MANY_ABANDONED_RATIO = 0.25f;
if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) {
_writeRenderFullVBO = true;
}
}
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
@ -323,7 +365,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
bool shouldRender = false; // assume we don't need to render it
// if it's colored, we might need to render it!
shouldRender = node->calculateShouldRender(Application::getInstance()->getViewFrustum());
node->setShouldRender(shouldRender && !node->isStagedForDeletion());
node->setShouldRender(shouldRender);
// let children figure out their renderness
if (!node->isLeaf()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
@ -339,13 +381,6 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
}
node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
// If the node has been asked to be deleted, but we've gotten to here, after updateNodeInArraysXXX()
// then it means our VBOs are "clean" and our vertices have been removed or not added. So we can now
// safely remove the node from the tree and actually delete it.
if (node->isStagedForDeletion()) {
_tree->deleteVoxelCodeFromTree(node->getOctalCode());
}
return voxelsUpdated;
}
@ -364,11 +399,13 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
// and RGB color for each added vertex
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
node->setBufferIndex(nodeIndex);
node->setVoxelSystem(this);
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
_voxelsInWriteArrays++; // our know vertices in the arrays
return 1; // rendered
} else {
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
node->setVoxelSystem(NULL);
}
return 0; // not-rendered
@ -393,6 +430,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
// and our scale as infinitely small
startVertex[0] = startVertex[1] = startVertex[2] = FLT_MAX;
voxelScale = 0;
_abandonedVBOSlots++;
}
// If this node has not yet been written to the array, then add it to the end of the array.
@ -402,6 +440,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
} else {
nodeIndex = _voxelsInWriteArrays;
node->setBufferIndex(nodeIndex);
node->setVoxelSystem(this);
_voxelsInWriteArrays++;
}
_writeVoxelDirtyArray[nodeIndex] = true;
@ -445,7 +484,6 @@ void VoxelSystem::init() {
_voxelsDirty = false;
_voxelsInWriteArrays = 0;
_voxelsInReadArrays = 0;
_unusedArraySpace = 0;
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[_maxVoxels];
@ -1111,7 +1149,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) {
pthread_mutex_lock(&_treeLock);
_tree->deleteVoxelAt(x, y, z, s, true);
_tree->deleteVoxelAt(x, y, z, s);
// redraw!
setupNewVoxelsForDrawing(); // do we even need to do this? Or will the next network receive kick in?
@ -1167,7 +1205,6 @@ struct FalseColorizeOccludedArgs {
long nonLeaves;
long nonLeavesOutOfView;
long nonLeavesOccluded;
long stagedForDeletion;
};
struct FalseColorizeSubTreeOperationArgs {
@ -1189,12 +1226,6 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
args->totalVoxels++;
// if this node is staged for deletion, then just return
if (node->isStagedForDeletion()) {
args->stagedForDeletion++;
return true;
}
// If we are a parent, let's see if we're completely occluded.
if (!node->isLeaf()) {
args->nonLeaves++;
@ -1275,7 +1306,6 @@ void VoxelSystem::falseColorizeOccluded() {
args.outOfView = 0;
args.subtreeVoxelsSkipped = 0;
args.nonLeaves = 0;
args.stagedForDeletion = 0;
args.nonLeavesOutOfView = 0;
args.nonLeavesOccluded = 0;
args.tree = _tree;
@ -1288,11 +1318,10 @@ void VoxelSystem::falseColorizeOccluded() {
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,
@ -1310,12 +1339,6 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraD
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
args->totalVoxels++;
// if this node is staged for deletion, then just return
if (node->isStagedForDeletion()) {
args->stagedForDeletion++;
return true;
}
// If we are a parent, let's see if we're completely occluded.
if (!node->isLeaf()) {
args->nonLeaves++;
@ -1404,7 +1427,6 @@ void VoxelSystem::falseColorizeOccludedV2() {
args.outOfView = 0;
args.subtreeVoxelsSkipped = 0;
args.nonLeaves = 0;
args.stagedForDeletion = 0;
args.nonLeavesOutOfView = 0;
args.nonLeavesOccluded = 0;
args.tree = _tree;
@ -1413,11 +1435,10 @@ void VoxelSystem::falseColorizeOccludedV2() {
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args);
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,

View file

@ -28,7 +28,7 @@ class ProgramObject;
const int NUM_CHILDREN = 8;
class VoxelSystem : public NodeData {
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook {
public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
~VoxelSystem();
@ -92,6 +92,8 @@ public:
CoverageMapV2 myCoverageMapV2;
CoverageMap myCoverageMap;
virtual void nodeDeleted(VoxelNode* node);
protected:
float _treeScale;
@ -155,7 +157,7 @@ private:
unsigned long _voxelsUpdated;
unsigned long _voxelsInReadArrays;
unsigned long _voxelsInWriteArrays;
unsigned long _unusedArraySpace;
unsigned long _abandonedVBOSlots;
bool _writeRenderFullVBO;
bool _readRenderFullVBO;
@ -187,6 +189,12 @@ private:
static ProgramObject* _perlinModulateProgram;
static GLuint _permutationNormalTextureID;
int _hookID;
std::vector<glBufferIndex> _freeIndexes;
void freeBufferIndex(glBufferIndex index);
void clearFreeBufferIndexes();
};
#endif

View file

@ -57,8 +57,11 @@ void Webcam::setEnabled(bool enabled) {
}
}
const float UNINITIALIZED_FACE_DEPTH = 0.0f;
void Webcam::reset() {
_initialFaceRect = RotatedRect();
_initialFaceDepth = UNINITIALIZED_FACE_DEPTH;
if (_enabled) {
// send a message to the grabber
@ -149,7 +152,10 @@ Webcam::~Webcam() {
delete _grabber;
}
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const RotatedRect& faceRect, const JointVector& joints) {
const float METERS_PER_MM = 1.0f / 1000.0f;
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float meanFaceDepth,
const RotatedRect& faceRect, const JointVector& joints) {
IplImage colorImage = color;
glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3);
if (_colorTextureID == 0) {
@ -232,22 +238,28 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const Rota
const float ROTATION_SMOOTHING = 0.95f;
_estimatedRotation.z = glm::mix(_faceRect.angle, _estimatedRotation.z, ROTATION_SMOOTHING);
// determine position based on translation and scaling of the face rect
// determine position based on translation and scaling of the face rect/mean face depth
if (_initialFaceRect.size.area() == 0) {
_initialFaceRect = _faceRect;
_estimatedPosition = glm::vec3();
_initialFaceDepth = meanFaceDepth;
} else {
float proportion = sqrtf(_initialFaceRect.size.area() / (float)_faceRect.size.area());
const float DISTANCE_TO_CAMERA = 0.333f;
float proportion, z;
if (meanFaceDepth == UNINITIALIZED_FACE_DEPTH) {
proportion = sqrtf(_initialFaceRect.size.area() / (float)_faceRect.size.area());
const float INITIAL_DISTANCE_TO_CAMERA = 0.333f;
z = INITIAL_DISTANCE_TO_CAMERA * proportion - INITIAL_DISTANCE_TO_CAMERA;
} else {
z = (meanFaceDepth - _initialFaceDepth) * METERS_PER_MM;
proportion = meanFaceDepth / _initialFaceDepth;
}
const float POSITION_SCALE = 0.5f;
float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA;
glm::vec3 position = glm::vec3(
_estimatedPosition = glm::vec3(
(_faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _textureSize.width,
(_faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _textureSize.width,
z);
const float POSITION_SMOOTHING = 0.95f;
_estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING);
}
}
@ -259,7 +271,7 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const Rota
}
FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0),
_depthOffset(0.0), _codec(), _frameCount(0) {
_smoothedMeanFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) {
}
FrameGrabber::~FrameGrabber() {
@ -367,9 +379,13 @@ void FrameGrabber::shutdown() {
cvReleaseCapture(&_capture);
_capture = 0;
}
if (_codec.name != 0) {
vpx_codec_destroy(&_codec);
_codec.name = 0;
if (_colorCodec.name != 0) {
vpx_codec_destroy(&_colorCodec);
_colorCodec.name = 0;
}
if (_depthCodec.name != 0) {
vpx_codec_destroy(&_depthCodec);
_depthCodec.name = 0;
}
_initialized = false;
@ -423,7 +439,6 @@ void FrameGrabber::grabFrame() {
_userID, (XnSkeletonJoint)parentJoint, parentOrientation);
rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation;
}
const float METERS_PER_MM = 1.0f / 1000.0f;
joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM,
rotation, xnToGLM(projected));
}
@ -480,31 +495,23 @@ void FrameGrabber::grabFrame() {
_searchWindow = Rect(clip(faceBounds.tl(), imageBounds), clip(faceBounds.br(), imageBounds));
}
#ifdef HAVE_OPENNI
if (_depthGenerator.IsValid()) {
// convert from 11 to 8 bits, centered about the mean face depth (if possible)
if (_searchWindow.area() > 0) {
const double DEPTH_OFFSET_SMOOTHING = 0.95;
const double EIGHT_BIT_MIDPOINT = 128.0;
double meanOffset = EIGHT_BIT_MIDPOINT - mean(depth(_searchWindow))[0];
_depthOffset = (_depthOffset == 0.0) ? meanOffset : glm::mix(meanOffset, _depthOffset, DEPTH_OFFSET_SMOOTHING);
}
depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, _depthOffset);
}
#endif
const int ENCODED_FACE_WIDTH = 128;
const int ENCODED_FACE_HEIGHT = 128;
int combinedFaceHeight = ENCODED_FACE_HEIGHT * (depth.empty() ? 1 : 2);
if (_codec.name == 0) {
// initialize encoder context
if (_colorCodec.name == 0) {
// initialize encoder context(s)
vpx_codec_enc_cfg_t codecConfig;
vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0);
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * combinedFaceHeight * codecConfig.rc_target_bitrate /
codecConfig.g_w / codecConfig.g_h;
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT *
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
codecConfig.g_w = ENCODED_FACE_WIDTH;
codecConfig.g_h = combinedFaceHeight;
vpx_codec_enc_init(&_codec, vpx_codec_vp8_cx(), &codecConfig, 0);
codecConfig.g_h = ENCODED_FACE_HEIGHT;
vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
if (!depth.empty()) {
int DEPTH_BITRATE_MULTIPLIER = 2;
codecConfig.rc_target_bitrate *= 2;
vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
}
}
// correct for 180 degree rotations
@ -541,9 +548,9 @@ void FrameGrabber::grabFrame() {
const int ENCODED_BITS_PER_VU = 2;
const int ENCODED_BITS_PER_PIXEL = ENCODED_BITS_PER_Y + 2 * ENCODED_BITS_PER_VU;
const int BITS_PER_BYTE = 8;
_encodedFace.fill(128, ENCODED_FACE_WIDTH * combinedFaceHeight * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE);
_encodedFace.resize(ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE);
vpx_image_t vpxImage;
vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, ENCODED_FACE_WIDTH, combinedFaceHeight, 1, (unsigned char*)_encodedFace.data());
vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, 1, (unsigned char*)_encodedFace.data());
uchar* yline = vpxImage.planes[0];
uchar* vline = vpxImage.planes[1];
uchar* uline = vpxImage.planes[2];
@ -571,9 +578,9 @@ void FrameGrabber::grabFrame() {
ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest[ENCODED_FACE_WIDTH] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest[ENCODED_FACE_WIDTH + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest += 2;
@ -590,37 +597,107 @@ void FrameGrabber::grabFrame() {
uline += vpxImage.stride[2];
}
// if we have depth data, warp that and just copy it in
if (!depth.empty()) {
_faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_8UC1);
warpAffine(_grayDepthFrame, _faceDepth, transform, _faceDepth.size());
uchar* dest = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT;
for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) {
memcpy(dest, _faceDepth.ptr(i), ENCODED_FACE_WIDTH);
dest += vpxImage.stride[0];
}
}
// encode the frame
vpx_codec_encode(&_codec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
// start the payload off with the aspect ratio
QByteArray payload(sizeof(float), 0);
*(float*)payload.data() = _smoothedFaceRect.size.width / _smoothedFaceRect.size.height;
// extract the encoded frame
vpx_codec_iter_t iterator = 0;
const vpx_codec_cx_pkt_t* packet;
while ((packet = vpx_codec_get_cx_data(&_codec, &iterator)) != 0) {
while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) {
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
// prepend the aspect ratio
QByteArray payload(sizeof(float), 0);
*(float*)payload.data() = _smoothedFaceRect.size.width / _smoothedFaceRect.size.height;
// prepend the length, which will indicate whether there's a depth frame too
payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz));
payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz);
QMetaObject::invokeMethod(Application::getInstance(), "sendAvatarFaceVideoMessage", Q_ARG(int, _frameCount),
Q_ARG(QByteArray, payload));
}
}
if (!depth.empty()) {
// warp the face depth without interpolation (because it will contain invalid zero values)
_faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_16UC1);
warpAffine(depth, _faceDepth, transform, _faceDepth.size(), INTER_NEAREST);
// find the mean of the valid values
qint64 depthTotal = 0;
qint64 depthSamples = 0;
ushort* src = _faceDepth.ptr<ushort>();
const ushort ELEVEN_BIT_MINIMUM = 0;
const ushort ELEVEN_BIT_MAXIMUM = 2047;
for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) {
for (int j = 0; j < ENCODED_FACE_WIDTH; j++) {
ushort depth = *src++;
if (depth != ELEVEN_BIT_MINIMUM && depth != ELEVEN_BIT_MAXIMUM) {
depthTotal += depth;
depthSamples++;
}
}
}
float mean = (depthSamples == 0) ? UNINITIALIZED_FACE_DEPTH : depthTotal / (float)depthSamples;
// smooth the mean over time
const float DEPTH_OFFSET_SMOOTHING = 0.95f;
_smoothedMeanFaceDepth = (_smoothedMeanFaceDepth == UNINITIALIZED_FACE_DEPTH) ? mean :
glm::mix(mean, _smoothedMeanFaceDepth, DEPTH_OFFSET_SMOOTHING);
// convert from 11 to 8 bits for preview/local display
const uchar EIGHT_BIT_MIDPOINT = 128;
double depthOffset = EIGHT_BIT_MIDPOINT - _smoothedMeanFaceDepth;
depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset);
// likewise for the encoded representation
uchar* yline = vpxImage.planes[0];
uchar* vline = vpxImage.planes[1];
uchar* uline = vpxImage.planes[2];
const uchar EIGHT_BIT_MAXIMUM = 255;
for (int i = 0; i < ENCODED_FACE_HEIGHT; i += 2) {
uchar* ydest = yline;
uchar* vdest = vline;
uchar* udest = uline;
for (int j = 0; j < ENCODED_FACE_WIDTH; j += 2) {
ushort tl = *_faceDepth.ptr<ushort>(i, j);
ushort tr = *_faceDepth.ptr<ushort>(i, j + 1);
ushort bl = *_faceDepth.ptr<ushort>(i + 1, j);
ushort br = *_faceDepth.ptr<ushort>(i + 1, j + 1);
uchar mask = EIGHT_BIT_MAXIMUM;
ydest[0] = (tl == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(tl + depthOffset);
ydest[1] = (tr == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(tr + depthOffset);
ydest[vpxImage.stride[0]] = (bl == ELEVEN_BIT_MINIMUM) ?
(mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(bl + depthOffset);
ydest[vpxImage.stride[0] + 1] = (br == ELEVEN_BIT_MINIMUM) ?
(mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(br + depthOffset);
ydest += 2;
*vdest++ = mask;
*udest++ = EIGHT_BIT_MIDPOINT;
}
yline += vpxImage.stride[0] * 2;
vline += vpxImage.stride[1];
uline += vpxImage.stride[2];
}
// encode the frame
vpx_codec_encode(&_depthCodec, &vpxImage, _frameCount, 1, 0, VPX_DL_REALTIME);
// extract the encoded frame
vpx_codec_iter_t iterator = 0;
const vpx_codec_cx_pkt_t* packet;
while ((packet = vpx_codec_get_cx_data(&_depthCodec, &iterator)) != 0) {
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz);
}
}
}
QMetaObject::invokeMethod(Application::getInstance(), "sendAvatarFaceVideoMessage",
Q_ARG(int, _frameCount), Q_ARG(QByteArray, payload));
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame",
Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame),
Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(float, _smoothedMeanFaceDepth),
Q_ARG(cv::RotatedRect, _smoothedFaceRect), Q_ARG(JointVector, joints));
}

View file

@ -19,8 +19,8 @@
#include <opencv2/opencv.hpp>
#ifdef HAVE_OPENNI
#include <XnCppWrapper.h>
#if defined(HAVE_OPENNI) && !defined(Q_MOC_RUN)
#include <XnCppWrapper.h>
#endif
#include <vpx_codec.h>
@ -62,7 +62,7 @@ public:
public slots:
void setEnabled(bool enabled);
void setFrame(const cv::Mat& color, int format, const cv::Mat& depth,
void setFrame(const cv::Mat& color, int format, const cv::Mat& depth, float meanFaceDepth,
const cv::RotatedRect& faceRect, const JointVector& joints);
private:
@ -77,6 +77,7 @@ private:
cv::Size2f _textureSize;
cv::RotatedRect _faceRect;
cv::RotatedRect _initialFaceRect;
float _initialFaceDepth;
JointVector _joints;
uint64_t _startTimestamp;
@ -117,9 +118,10 @@ private:
cv::Mat _backProject;
cv::Rect _searchWindow;
cv::Mat _grayDepthFrame;
double _depthOffset;
float _smoothedMeanFaceDepth;
vpx_codec_ctx_t _codec;
vpx_codec_ctx_t _colorCodec;
vpx_codec_ctx_t _depthCodec;
int _frameCount;
cv::Mat _faceColor;
cv::Mat _faceDepth;

View file

@ -370,7 +370,11 @@ glm::vec3 Avatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
glm::vec3 Avatar::getUprightEyeLevelPosition() const {
const float EYE_UP_OFFSET = 0.36f;
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
return _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET + glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//
@ -447,7 +451,7 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
}
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::quat orientation = getOrientation();
glm::vec3 front = orientation * IDENTITY_FRONT;
glm::vec3 right = orientation * IDENTITY_RIGHT;

View file

@ -164,9 +164,14 @@ public:
glm::quat getOrientation () const;
glm::quat getWorldAlignedOrientation() const;
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
glm::vec3 getGravity () const { return _gravity; }
glm::vec3 getUprightHeadPosition() const;
glm::vec3 getUprightEyeLevelPosition() const;
AvatarVoxelSystem* getVoxels() { return &_voxels; }

View file

@ -30,19 +30,25 @@ GLuint Face::_vboID;
GLuint Face::_iboID;
Face::Face(Head* owningHead) : _owningHead(owningHead), _renderMode(MESH),
_colorTextureID(0), _depthTextureID(0), _codec(), _frameCount(0) {
_colorTextureID(0), _depthTextureID(0), _colorCodec(), _depthCodec(), _frameCount(0) {
// we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread());
}
Face::~Face() {
if (_codec.name != 0) {
vpx_codec_destroy(&_codec);
if (_colorCodec.name != 0) {
vpx_codec_destroy(&_colorCodec);
// delete our textures, since we know that we own them
// delete our texture, since we know that we own it
if (_colorTextureID != 0) {
glDeleteTextures(1, &_colorTextureID);
}
}
if (_depthCodec.name != 0) {
vpx_codec_destroy(&_depthCodec);
// delete our texture, since we know that we own it
if (_depthTextureID != 0) {
glDeleteTextures(1, &_depthTextureID);
}
@ -55,9 +61,9 @@ void Face::setTextureRect(const cv::RotatedRect& textureRect) {
}
int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
if (_codec.name == 0) {
if (_colorCodec.name == 0) {
// initialize decoder context
vpx_codec_dec_init(&_codec, vpx_codec_vp8_dx(), 0, 0);
vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0);
}
// skip the header
unsigned char* packetPosition = packetData;
@ -85,14 +91,14 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
if ((_frameBytesRemaining -= payloadSize) <= 0) {
float aspectRatio = *(const float*)_arrivingFrame.constData();
vpx_codec_decode(&_codec, (const uint8_t*)_arrivingFrame.constData() + sizeof(float),
_arrivingFrame.size() - sizeof(float), 0, 0);
size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float));
const uint8_t* colorData = (const uint8_t*)(_arrivingFrame.constData() + sizeof(float) + sizeof(size_t));
vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0);
vpx_codec_iter_t iterator = 0;
vpx_image_t* image;
while ((image = vpx_codec_get_frame(&_codec, &iterator)) != 0) {
while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) {
// convert from YV12 to RGB
const int imageHeight = image->d_w;
Mat color(imageHeight, image->d_w, CV_8UC3);
Mat color(image->d_h, image->d_w, CV_8UC3);
uchar* yline = image->planes[0];
uchar* vline = image->planes[1];
uchar* uline = image->planes[2];
@ -100,7 +106,7 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
const int GREEN_V_WEIGHT = (int)(0.714 * 256);
const int GREEN_U_WEIGHT = (int)(0.344 * 256);
const int BLUE_U_WEIGHT = (int)(1.773 * 256);
for (int i = 0; i < imageHeight; i += 2) {
for (int i = 0; i < image->d_h; i += 2) {
uchar* ysrc = yline;
uchar* vsrc = vline;
uchar* usrc = uline;
@ -144,13 +150,44 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
uline += image->stride[2];
}
Mat depth;
if (image->d_h > imageHeight) {
// if the height is greater than the width, we have depth data
depth.create(imageHeight, image->d_w, CV_8UC1);
uchar* src = image->planes[0] + image->stride[0] * imageHeight;
for (int i = 0; i < imageHeight; i++) {
memcpy(depth.ptr(i), src, image->d_w);
src += image->stride[0];
const uint8_t* depthData = colorData + colorSize;
int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData());
if (depthSize > 0) {
if (_depthCodec.name == 0) {
// initialize decoder context
vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0);
}
vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0);
vpx_codec_iter_t iterator = 0;
vpx_image_t* image;
while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) {
depth.create(image->d_h, image->d_w, CV_8UC1);
uchar* yline = image->planes[0];
uchar* vline = image->planes[1];
const uchar EIGHT_BIT_MAXIMUM = 255;
const uchar MASK_THRESHOLD = 192;
for (int i = 0; i < image->d_h; i += 2) {
uchar* ysrc = yline;
uchar* vsrc = vline;
for (int j = 0; j < image->d_w; j += 2) {
if (*vsrc++ < MASK_THRESHOLD) {
*depth.ptr(i, j) = EIGHT_BIT_MAXIMUM;
*depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM;
*depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM;
*depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM;
} else {
*depth.ptr(i, j) = ysrc[0];
*depth.ptr(i, j + 1) = ysrc[1];
*depth.ptr(i + 1, j) = ysrc[image->stride[0]];
*depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1];
}
ysrc += 2;
}
yline += image->stride[0] * 2;
vline += image->stride[1];
}
}
}
QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color),

View file

@ -57,7 +57,8 @@ private:
cv::RotatedRect _textureRect;
float _aspectRatio;
vpx_codec_ctx_t _codec;
vpx_codec_ctx_t _colorCodec;
vpx_codec_ctx_t _depthCodec;
QByteArray _arrivingFrame;
int _frameCount;

View file

@ -14,19 +14,25 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
const bool SHOW_LEAP_HAND = false;
using namespace std;
Hand::Hand(Avatar* owningAvatar) :
HandData((AvatarData*)owningAvatar),
_raveGloveClock(0.0f),
_raveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR),
_raveGloveInitialized(false),
_isRaveGloveActive(false),
_owningAvatar(owningAvatar),
_renderAlpha(1.0),
_lookingInMirror(false),
_ballColor(0.0, 0.0, 0.4),
_particleSystemInitialized(false)
{
_ballColor(0.0, 0.0, 0.4)
{
// initialize all finger particle emitters with an invalid id as default
for (int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
_fingerParticleEmitter[f] = -1;
for (int f = 0; f< NUM_FINGERS; f ++ ) {
_raveGloveEmitter[f] = NULL_EMITTER;
}
}
@ -35,16 +41,18 @@ void Hand::init() {
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
_ballColor = glm::vec3(0.0, 0.4, 0.0);
}
else
else {
_ballColor = glm::vec3(0.0, 0.0, 0.4);
}
}
void Hand::reset() {
}
void Hand::simulate(float deltaTime, bool isMine) {
if (_isRaveGloveActive) {
updateFingerParticles(deltaTime);
updateRaveGloveParticles(deltaTime);
}
}
@ -76,6 +84,21 @@ void Hand::calculateGeometry() {
}
}
void Hand::setRaveGloveEffectsMode(QKeyEvent* event) {
switch (event->key()) {
case Qt::Key_0: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR); break;
case Qt::Key_1: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_TRAILS ); break;
case Qt::Key_2: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE ); break;
case Qt::Key_3: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_WATER ); break;
case Qt::Key_4: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FLASHY ); break;
case Qt::Key_5: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER ); break;
case Qt::Key_6: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER ); break;
case Qt::Key_7: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_SNAKE ); break;
case Qt::Key_8: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_PULSE ); break;
case Qt::Key_9: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROB ); break;
};
}
void Hand::render(bool lookingInMirror) {
@ -87,16 +110,19 @@ void Hand::render(bool lookingInMirror) {
if (_isRaveGloveActive) {
renderRaveGloveStage();
if (_particleSystemInitialized) {
_particleSystem.render();
if (_raveGloveInitialized) {
updateRaveGloveEmitters(); // do this after calculateGeometry
_raveGloveParticleSystem.render();
}
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_RESCALE_NORMAL);
renderFingerTrails();
renderHandSpheres();
if ( SHOW_LEAP_HAND ) {
renderFingerTrails();
renderHandSpheres();
}
}
void Hand::renderRaveGloveStage() {
@ -203,69 +229,61 @@ void Hand::renderFingerTrails() {
}
}
void Hand::updateFingerParticles(float deltaTime) {
if (!_particleSystemInitialized) {
for ( int f = 0; f< NUM_FINGERS_PER_HAND; f ++ ) {
_particleSystem.setShowingEmitter(f, true );
_fingerParticleEmitter[f] = _particleSystem.addEmitter();
assert( _fingerParticleEmitter[f] != -1 );
ParticleSystem::ParticleAttributes attributes;
// set attributes for each life stage of the particle:
attributes.radius = 0.0f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.5f, 0.5f);
attributes.gravity = 0.0f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.002f;
attributes.emitterAttraction = 0.0f;
attributes.tornadoForce = 0.0f;
attributes.neighborAttraction = 0.0f;
attributes.neighborRepulsion = 0.0f;
attributes.bounce = 1.0f;
attributes.usingCollisionSphere = false;
_particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 0, attributes);
attributes.radius = 0.01f;
attributes.jitter = 0.0f;
attributes.gravity = -0.005f;
attributes.color = glm::vec4( 1.0f, 0.2f, 0.0f, 0.4f);
_particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 1, attributes);
attributes.radius = 0.01f;
attributes.gravity = 0.0f;
attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 0.2f);
_particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 2, attributes);
attributes.radius = 0.02f;
attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 0.0f);
_particleSystem.setParticleAttributes(_fingerParticleEmitter[f], 3, attributes);
void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals) {
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (i < handPositions.size()) {
palm.setActive(true);
palm.setRawPosition(handPositions[i]);
palm.setRawNormal(handNormals[i]);
}
else {
palm.setActive(false);
}
}
}
_particleSystemInitialized = true;
} else {
// update the particles
// call this right after the geometry of the leap hands are set
void Hand::updateRaveGloveEmitters() {
bool debug = false;
if (_raveGloveInitialized) {
static float t = 0.0f;
t += deltaTime;
if(debug) printf( "\n" );
if(debug) printf( "------------------------------------\n" );
if(debug) printf( "updating rave glove emitters:\n" );
if(debug) printf( "------------------------------------\n" );
int emitterIndex = 0;
int fingerIndex = 0;
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if(debug) printf( "\n" );
if(debug) printf( "palm %d ", (int)i );
if (palm.isActive()) {
if(debug) printf( "is active\n" );
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if(debug) printf( "emitterIndex %d: ", emitterIndex );
if (finger.isActive()) {
if (_fingerParticleEmitter[fingerIndex] != -1) {
if ((emitterIndex >=0)
&& (emitterIndex < NUM_FINGERS)) {
assert(emitterIndex >=0 );
assert(emitterIndex < NUM_FINGERS );
if(debug) printf( "_raveGloveEmitter[%d] = %d\n", emitterIndex, _raveGloveEmitter[emitterIndex] );
glm::vec3 particleEmitterPosition = finger.getTipPosition();
glm::vec3 fingerDirection = particleEmitterPosition - leapPositionToWorldPosition(finger.getRootPosition());
glm::vec3 fingerDirection = finger.getTipPosition() - finger.getRootPosition();
float fingerLength = glm::length(fingerDirection);
if (fingerLength > 0.0f) {
@ -273,27 +291,391 @@ void Hand::updateFingerParticles(float deltaTime) {
} else {
fingerDirection = IDENTITY_UP;
}
glm::quat particleEmitterRotation = rotationBetween(palm.getNormal(), fingerDirection);
//glm::quat particleEmitterRotation = glm::angleAxis(0.0f, fingerDirection);
assert(_raveGloveEmitter[emitterIndex] >=0 );
assert(_raveGloveEmitter[emitterIndex] < NUM_FINGERS );
_particleSystem.setEmitterPosition(_fingerParticleEmitter[f], particleEmitterPosition);
_particleSystem.setEmitterRotation(_fingerParticleEmitter[f], particleEmitterRotation);
const glm::vec3 velocity = fingerDirection * 0.002f;
const float lifespan = 1.0f;
_particleSystem.emitParticlesNow(_fingerParticleEmitter[f], 1, velocity, lifespan);
_raveGloveParticleSystem.setEmitterPosition (_raveGloveEmitter[emitterIndex], finger.getTipPosition());
_raveGloveParticleSystem.setEmitterDirection(_raveGloveEmitter[emitterIndex], fingerDirection);
}
} else {
if(debug) printf( "BOGUS finger\n" );
}
emitterIndex ++;
}
} else {
if(debug) printf( "is NOT active\n" );
}
}
}
}
// call this from within the simulate method
void Hand::updateRaveGloveParticles(float deltaTime) {
if (!_raveGloveInitialized) {
//printf( "Initializing rave glove emitters:\n" );
//printf( "The indices of the emitters are:\n" );
// start up the rave glove finger particles...
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
_raveGloveEmitter[f] = _raveGloveParticleSystem.addEmitter();
assert( _raveGloveEmitter[f] >= 0 );
assert( _raveGloveEmitter[f] != NULL_EMITTER );
//printf( "%d\n", _raveGloveEmitter[f] );
}
setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE);
_raveGloveParticleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_raveGloveInitialized = true;
} else {
_particleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_particleSystem.simulate(deltaTime);
_raveGloveClock += deltaTime;
// this rave glove effect oscillates though various colors and radii that are meant to show off some effects
if (_raveGloveMode == RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR) {
ParticleSystem::ParticleAttributes attributes;
float red = 0.5f + 0.5f * sinf(_raveGloveClock * 1.4f);
float green = 0.5f + 0.5f * cosf(_raveGloveClock * 1.7f);
float blue = 0.5f + 0.5f * sinf(_raveGloveClock * 2.0f);
float alpha = 1.0f;
attributes.color = glm::vec4(red, green, blue, alpha);
attributes.radius = 0.01f + 0.005f * sinf(_raveGloveClock * 2.2f);
attributes.modulationAmplitude = 0.0f;
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
}
}
_raveGloveParticleSystem.simulate(deltaTime);
}
}
void Hand::setRaveGloveMode(int mode) {
_raveGloveMode = mode;
_raveGloveParticleSystem.killAllParticles();
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
ParticleSystem::ParticleAttributes attributes;
//-----------------------------------------
// throbbing color cycle
//-----------------------------------------
if (mode == RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0f );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.02f;
attributes.gravity = 0.0f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// trails
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_TRAILS) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 50.0f );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 5 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 1.0f, 0.5f, 0.2f, 1.0f);
attributes.gravity = 0.005f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.002f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.color = glm::vec4( 1.0f, 0.2f, 0.2f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.color = glm::vec4( 1.0f, 0.2f, 0.2f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
}
//-----------------------------------------
// Fire!
//-----------------------------------------
if (mode == RAVE_GLOVE_EFFECTS_MODE_FIRE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 120.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 6 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.005f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.5f, 0.5f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.003f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.jitter = 0.0f;
attributes.gravity = -0.005f;
attributes.color = glm::vec4( 1.0f, 0.2f, 0.0f, 0.4f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.gravity = 0.0f;
attributes.color = glm::vec4( 0.4f, 0.4f, 0.4f, 0.2f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.02f;
attributes.color = glm::vec4( 0.4f, 0.6f, 0.9f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// water
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_WATER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.6f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.001f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 5 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.5f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.004f;
attributes.bounce = 1.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.gravity = 0.01f;
attributes.jitter = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.2f);
attributes.radius = 0.002f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// flashy
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_FLASHY) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.1 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 12 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.05f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 0.0f, 1.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// Bozo sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.2 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 12 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.01f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 0.0f, .0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 0.0f, 0.0f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// long sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 7 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.4f);
attributes.radius = 0.0f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0001f;
attributes.bounce = 1.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.005f;
attributes.color = glm::vec4( 0.0f, 0.5f, 0.5f, 0.8f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.007f;
attributes.color = glm::vec4( 0.5f, 0.0f, 0.5f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.02f;
attributes.color = glm::vec4( 0.0f, 0.0f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// bubble snake
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_SNAKE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 7 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 0.5f, 1.0f, 0.5f, 1.0f);
attributes.airFriction = 0.01f;
attributes.jitter = 0.0f;
attributes.emitterAttraction = 0.0f;
attributes.tornadoForce = 1.1f;
attributes.neighborAttraction = 1.1f;
attributes.neighborRepulsion = 1.1f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.002f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.003f;
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.004f;
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// pulse
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_PULSE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.1f, 0.2f, 0.4f, 0.5f);
attributes.modulationAmplitude = 0.9;
attributes.modulationRate = 7.0;
attributes.modulationStyle = COLOR_MODULATION_STYLE_LIGHNTESS_PULSE;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// throb
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.5f, 0.4f, 0.3f, 0.5f);
attributes.modulationAmplitude = 0.3;
attributes.modulationRate = 1.0;
attributes.modulationStyle = COLOR_MODULATION_STYLE_LIGHTNESS_WAVE;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
}
}
}

View file

@ -8,6 +8,7 @@
#ifndef hifi_Hand_h
#define hifi_Hand_h
#include <QAction>
#include <glm/glm.hpp>
#include <AvatarData.h>
#include <HandData.h>
@ -22,6 +23,22 @@
class Avatar;
class ProgramObject;
enum RaveGloveEffectsMode
{
RAVE_GLOVE_EFFECTS_MODE_NULL = -1,
RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR,
RAVE_GLOVE_EFFECTS_MODE_TRAILS,
RAVE_GLOVE_EFFECTS_MODE_FIRE,
RAVE_GLOVE_EFFECTS_MODE_WATER,
RAVE_GLOVE_EFFECTS_MODE_FLASHY,
RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER,
RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER,
RAVE_GLOVE_EFFECTS_MODE_SNAKE,
RAVE_GLOVE_EFFECTS_MODE_PULSE,
RAVE_GLOVE_EFFECTS_MODE_THROB,
NUM_RAVE_GLOVE_EFFECTS_MODES
};
class Hand : public HandData {
public:
Hand(Avatar* owningAvatar);
@ -42,9 +59,10 @@ public:
void render(bool lookingInMirror);
void setBallColor (glm::vec3 ballColor ) { _ballColor = ballColor; }
void updateFingerParticles(float deltaTime);
void updateRaveGloveParticles(float deltaTime);
void updateRaveGloveEmitters();
void setRaveGloveActive(bool active) { _isRaveGloveActive = active; }
void setRaveGloveEffectsMode(QKeyEvent* event);
// getters
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
@ -55,20 +73,25 @@ private:
Hand(const Hand&);
Hand& operator= (const Hand&);
ParticleSystem _particleSystem;
ParticleSystem _raveGloveParticleSystem;
float _raveGloveClock;
int _raveGloveMode;
bool _raveGloveInitialized;
int _raveGloveEmitter[NUM_FINGERS];
bool _isRaveGloveActive;
Avatar* _owningAvatar;
float _renderAlpha;
bool _lookingInMirror;
bool _isRaveGloveActive;
glm::vec3 _ballColor;
std::vector<HandBall> _leapBalls;
bool _particleSystemInitialized;
int _fingerParticleEmitter[NUM_FINGERS_PER_HAND];
Avatar* _owningAvatar;
float _renderAlpha;
bool _lookingInMirror;
glm::vec3 _ballColor;
std::vector<HandBall> _leapBalls;
// private methods
void setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals);
void renderRaveGloveStage();
void setRaveGloveMode(int mode);
void renderHandSpheres();
void renderFingerTrails();
void calculateGeometry();

View file

@ -272,7 +272,7 @@ void Head::calculateGeometry() {
+ up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET
+ front * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_FRONT_OFFSET;
_eyeLevelPosition = _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET;
_eyeLevelPosition = _rightEyePosition - right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_RIGHT_OFFSET;
//calculate the eyebrow positions
_leftEyeBrowPosition = _leftEyePosition;

View file

@ -81,16 +81,16 @@ def hifiJob(String targetName, Boolean deploy) {
static Closure cmakeBuild(srcDir, instCommand) {
return { project ->
project / 'builders' / 'hudson.plugins.cmake.CmakeBuilder' {
sourceDir srcDir
sourceDir '.'
buildDir 'build'
installDir ''
buildType 'RelWithDebInfo'
generator 'Unix Makefiles'
makeCommand 'make'
makeCommand "make ${srcDir}"
installCommand instCommand
preloadScript ''
cmakeArgs ''
projectCmakePath '/usr/bin/cmake'
projectCmakePath '/usr/local/bin/cmake'
cleanBuild 'false'
cleanInstallDir 'false'
builderImpl ''
@ -138,4 +138,4 @@ parameterizedJob.with {
(project / publishers / 'hudson.plugins.postbuildtask.PostbuildTask' /
tasks / 'hudson.plugins.postbuildtask.TaskProperties' / script).setValue(curlCommand)
}
}
}

View file

@ -151,10 +151,6 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
std::vector<glm::vec3> fingerVectors;
_handData->encodeRemoteData(fingerVectors);
/////////////////////////////////
// Temporarily disable Leap finger sending, as it's causing a crash whenever someone's got a Leap connected
fingerVectors.clear();
/////////////////////////////////
if (fingerVectors.size() > 255)
fingerVectors.clear(); // safety. We shouldn't ever get over 255, so consider that invalid.
@ -268,8 +264,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
// leap hand data
if (sourceBuffer - startPosition < numBytes) // safety check
{
if (sourceBuffer - startPosition < numBytes) {
// check passed, bytes match
unsigned int numFingerVectors = *sourceBuffer++;
if (numFingerVectors > 0) {
std::vector<glm::vec3> fingerVectors(numFingerVectors);
@ -283,8 +279,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
}
// skeleton joints
if (sourceBuffer - startPosition < numBytes) // safety check
{
if (sourceBuffer - startPosition < numBytes) {
// check passed, bytes match
_joints.resize(*sourceBuffer++);
for (vector<JointData>::iterator it = _joints.begin(); it != _joints.end(); it++) {
it->jointID = *sourceBuffer++;

View file

@ -19,7 +19,10 @@ class AvatarData;
class FingerData;
class PalmData;
const int NUM_HANDS = 2;
const int NUM_FINGERS_PER_HAND = 5;
const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
const int LEAPID_INVALID = -1;
class HandData {

View file

@ -27,6 +27,14 @@ const short RING_BUFFER_LENGTH_SAMPLES = RING_BUFFER_LENGTH_FRAMES * BUFFER_LENG
class AudioRingBuffer : public NodeData {
public:
static int const DEFAULT_LISTEN_LIST_SIZE = 100;
typedef enum {
NORMAL,
OMNI_DIRECTIONAL_POINT,
SELECTED_SOURCES
} ListenMode;
AudioRingBuffer(bool isStereo);
~AudioRingBuffer();

View file

@ -354,6 +354,12 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte
readPtr += unpackSocket(readPtr, (sockaddr*) &nodePublicSocket);
readPtr += unpackSocket(readPtr, (sockaddr*) &nodeLocalSocket);
// if the public socket address is 0 then it's reachable at the same IP
// as the domain server
if (nodePublicSocket.sin_addr.s_addr == 0) {
inet_aton(_domainIP, &nodePublicSocket.sin_addr);
}
addOrUpdateNode((sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket, nodeType, nodeId);
}

View file

@ -14,12 +14,19 @@
PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
switch (type) {
case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO:
case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO:
return 1;
case PACKET_TYPE_HEAD_DATA:
return 2;
break;
case PACKET_TYPE_AVATAR_FACE_VIDEO:
return 1;
default:
return 0;
break;
}
}

View file

@ -59,10 +59,10 @@ PerfStat::~PerfStat() {
}
if (wantDebugOut) {
qDebug("PerfStats: %s elapsed:%f average:%lf count:%ld total:%lf ut:%d us:%d ue:%d t:%ld s:%ld e:%ld\n",
qDebug("PerfStats: %s elapsed:%f average:%lf count:%ld total:%lf ut:%ld us:%ld ue:%ld t:%ld s:%ld e:%ld\n",
this->group.c_str(),elapsed,average,count,totalTime,
(end.tv_usec-start.tv_usec),start.tv_usec,end.tv_usec,
(end.tv_sec-start.tv_sec),start.tv_sec,end.tv_sec
(long)(end.tv_usec-start.tv_usec), (long)start.tv_usec, (long)end.tv_usec,
(long)(end.tv_sec-start.tv_sec), (long)start.tv_sec, (long)end.tv_sec
);
}
};

View file

@ -48,9 +48,9 @@ void VoxelNode::init(unsigned char * octalCode) {
_subtreeLeafNodeCount = 0; // that's me
_glBufferIndex = GLBUFFER_INDEX_UNKNOWN;
_voxelSystem = NULL;
_isDirty = true;
_shouldRender = false;
_isStagedForDeletion = false;
markWithChangedTime();
calculateAABox();
}
@ -159,28 +159,18 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
}
// handles staging or deletion of all deep children
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion) {
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex) {
VoxelNode* childToDelete = getChildAtIndex(childIndex);
if (childToDelete) {
// If the child is not a leaf, then call ourselves recursively on all the children
if (!childToDelete->isLeaf()) {
// delete all it's children
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
childToDelete->safeDeepDeleteChildAtIndex(i, stagedForDeletion);
childToDelete->safeDeepDeleteChildAtIndex(i);
}
}
// if this node has a BufferIndex then we need to stage it for deletion
// instead of actually deleting it from the tree
if (childToDelete->isKnownBufferIndex()) {
stagedForDeletion = true;
}
if (stagedForDeletion) {
childToDelete->stageForDeletion();
_isDirty = true;
} else {
deleteChildAtIndex(childIndex);
_isDirty = true;
}
deleteChildAtIndex(childIndex);
_isDirty = true;
markWithChangedTime();
}
}
@ -190,7 +180,7 @@ void VoxelNode::setColorFromAverageOfChildren() {
int colorArray[4] = {0,0,0,0};
float density = 0.0f;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i] && !_children[i]->isStagedForDeletion() && _children[i]->isColored()) {
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
}
@ -279,7 +269,7 @@ bool VoxelNode::collapseIdenticalLeaves() {
int red,green,blue;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
// if no child, child isn't a leaf, or child doesn't have a color
if (!_children[i] || _children[i]->isStagedForDeletion() || !_children[i]->isLeaf() || !_children[i]->isColored()) {
if (!_children[i] || !_children[i]->isLeaf() || !_children[i]->isColored()) {
allChildrenMatch=false;
//qDebug("SADNESS child missing or not colored! i=%d\n",i);
break;
@ -410,43 +400,23 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
return distance;
}
VoxelNodeDeleteHook VoxelNode::_hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
void* VoxelNode::_hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
int VoxelNode::_hooksInUse = 0;
std::vector<VoxelNodeDeleteHook*> VoxelNode::_hooks;
int VoxelNode::addDeleteHook(VoxelNodeDeleteHook hook, void* extraData) {
// If first use, initialize the _hooks array
if (_hooksInUse == 0) {
memset(_hooks, 0, sizeof(_hooks));
memset(_hooksExtraData, 0, sizeof(_hooksExtraData));
}
// find first available slot
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
if (!_hooks[i]) {
_hooks[i] = hook;
_hooksExtraData[i] = extraData;
_hooksInUse++;
return i;
}
}
// if we got here, then we're out of room in our hooks, return error
return VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE;
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
_hooks.push_back(hook);
}
void VoxelNode::removeDeleteHook(int hookID) {
if (_hooks[hookID]) {
_hooks[hookID] = NULL;
_hooksExtraData[hookID] = NULL;
_hooksInUse--;
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
for (int i = 0; i < _hooks.size(); i++) {
if (_hooks[i] == hook) {
_hooks.erase(_hooks.begin() + i);
return;
}
}
}
void VoxelNode::notifyDeleteHooks() {
if (_hooksInUse > 0) {
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
if (_hooks[i]) {
_hooks[i](this, _hooksExtraData[i]);
}
}
for (int i = 0; i < _hooks.size(); i++) {
_hooks[i]->nodeDeleted(this);
}
}

View file

@ -14,18 +14,19 @@
#include "ViewFrustum.h"
#include "VoxelConstants.h"
class VoxelTree; // forward delclaration
class VoxelNode; // forward delclaration
class VoxelTree; // forward declaration
class VoxelNode; // forward declaration
class VoxelSystem; // forward declaration
typedef unsigned char colorPart;
typedef unsigned char nodeColor[4];
typedef unsigned char rgbColor[3];
// Callback function, for delete hook
typedef void (*VoxelNodeDeleteHook)(VoxelNode* node, void* extraData);
const int VOXEL_NODE_MAX_DELETE_HOOKS = 100;
const int VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE = -1;
// Callers who want delete hook callbacks should implement this class
class VoxelNodeDeleteHook {
public:
virtual void nodeDeleted(VoxelNode* node) = 0;
};
class VoxelNode {
public:
@ -38,7 +39,7 @@ public:
void deleteChildAtIndex(int childIndex);
VoxelNode* removeChildAtIndex(int childIndex);
VoxelNode* addChildAtIndex(int childIndex);
void safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion); // handles staging or deletion of all descendents
void safeDeepDeleteChildAtIndex(int childIndex); // handles deletion of all descendents
void setColorFromAverageOfChildren();
void setRandomColor(int minimumBrightness);
@ -77,15 +78,14 @@ public:
glBufferIndex getBufferIndex() const { return _glBufferIndex; };
bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); };
void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; };
VoxelSystem* getVoxelSystem() const { return _voxelSystem; };
void setVoxelSystem(VoxelSystem* voxelSystem) { _voxelSystem = voxelSystem; };
// Used by VoxelSystem for rendering in/out of view and LOD
void setShouldRender(bool shouldRender);
bool getShouldRender() const { return _shouldRender; }
// Used by VoxelSystem to mark a node as to be deleted on next render pass
void stageForDeletion() { _isStagedForDeletion = true; _isDirty = true; };
bool isStagedForDeletion() const { return _isStagedForDeletion; }
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
void setFalseColor(colorPart red, colorPart green, colorPart blue);
void setFalseColored(bool isFalseColored);
@ -105,8 +105,8 @@ public:
const nodeColor& getColor() const { return _trueColor; };
#endif
static int addDeleteHook(VoxelNodeDeleteHook hook, void* extraData = NULL);
static void removeDeleteHook(int hookID);
static void addDeleteHook(VoxelNodeDeleteHook* hook);
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
void recalculateSubTreeNodeCount();
unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; };
@ -124,10 +124,10 @@ private:
bool _falseColored;
#endif
glBufferIndex _glBufferIndex;
VoxelSystem* _voxelSystem;
bool _isDirty;
uint64_t _lastChanged;
bool _shouldRender;
bool _isStagedForDeletion;
AABox _box;
unsigned char* _octalCode;
VoxelNode* _children[8];
@ -135,10 +135,8 @@ private:
unsigned long _subtreeNodeCount;
unsigned long _subtreeLeafNodeCount;
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
static VoxelNodeDeleteHook _hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
static void* _hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
static int _hooksInUse;
static std::vector<VoxelNodeDeleteHook*> _hooks;
};
#endif /* defined(__hifi__VoxelNode__) */

View file

@ -13,11 +13,11 @@ VoxelNodeBag::VoxelNodeBag() :
_bagElements(NULL),
_elementsInUse(0),
_sizeOfElementsArray(0) {
_hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this);
VoxelNode::addDeleteHook(this);
};
VoxelNodeBag::~VoxelNodeBag() {
VoxelNode::removeDeleteHook(_hookID);
VoxelNode::removeDeleteHook(this);
deleteAll();
}
@ -126,9 +126,9 @@ void VoxelNodeBag::remove(VoxelNode* node) {
}
}
void VoxelNodeBag::voxelNodeDeleteHook(VoxelNode* node, void* extraData) {
VoxelNodeBag* theBag = (VoxelNodeBag*)extraData;
theBag->remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
void VoxelNodeBag::nodeDeleted(VoxelNode* node) {
remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
}

View file

@ -16,7 +16,7 @@
#include "VoxelNode.h"
class VoxelNodeBag {
class VoxelNodeBag : public VoxelNodeDeleteHook {
public:
VoxelNodeBag();
@ -34,6 +34,8 @@ public:
static void voxelNodeDeleteHook(VoxelNode* node, void* extraData);
virtual void nodeDeleted(VoxelNode* node);
private:
VoxelNode** _bagElements;

View file

@ -378,10 +378,10 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
void VoxelSceneStats::printDebugDetails() {
qDebug("\n------------------------------\n");
qDebug("VoxelSceneStats:\n");
qDebug(" start : %llu \n", _start);
qDebug(" end : %llu \n", _end);
qDebug(" elapsed : %llu \n", _elapsed);
qDebug(" encoding : %llu \n", _totalEncodeTime);
qDebug(" start : %llu \n", (long long unsigned int)_start);
qDebug(" end : %llu \n", (long long unsigned int)_end);
qDebug(" elapsed : %llu \n", (long long unsigned int)_elapsed);
qDebug(" encoding : %llu \n", (long long unsigned int)_totalEncodeTime);
qDebug("\n");
qDebug(" full scene: %s\n", debug::valueOf(_isFullScene));
qDebug(" moving: %s\n", debug::valueOf(_isMoving));
@ -457,12 +457,12 @@ char* VoxelSceneStats::getItemValue(int item) {
calcAverageFPS = (float)USECS_PER_SECOND / (float)elapsedAverage;
sprintf(_itemValueBuffer, "%llu usecs (%d fps) Average: %.0f usecs (%d fps)",
_elapsed, calcFPS, elapsedAverage, calcAverageFPS);
(long long unsigned int)_elapsed, calcFPS, elapsedAverage, calcAverageFPS);
break;
}
case ITEM_ENCODE:
calcFPS = (float)USECS_PER_SECOND / (float)_totalEncodeTime;
sprintf(_itemValueBuffer, "%llu usecs (%d fps)", _totalEncodeTime, calcFPS);
sprintf(_itemValueBuffer, "%llu usecs (%d fps)", (long long unsigned int)_totalEncodeTime, calcFPS);
break;
case ITEM_PACKETS: {
float elapsedSecs = ((float)_elapsed / (float)USECS_PER_SECOND);

View file

@ -311,8 +311,7 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
// now also check the childrenInTreeMask, if the mask is missing the bit, then it means we need to delete this child
// subtree/node, because it shouldn't actually exist in the tree.
if (!oneAtBit(childrenInTreeMask, i) && destinationNode->getChildAtIndex(i)) {
bool stagedForDeletion = false; // assume staging is not needed
destinationNode->safeDeepDeleteChildAtIndex(i, stagedForDeletion);
destinationNode->safeDeepDeleteChildAtIndex(i);
_isDirty = true; // by definition!
}
}
@ -366,15 +365,14 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
this->voxelsBytesReadStats.updateAverage(bufferSizeBytes);
}
void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) {
void VoxelTree::deleteVoxelAt(float x, float y, float z, float s) {
unsigned char* octalCode = pointToVoxel(x,y,z,s,0,0,0);
deleteVoxelCodeFromTree(octalCode, stage);
deleteVoxelCodeFromTree(octalCode);
delete[] octalCode; // cleanup memory
}
class DeleteVoxelCodeFromTreeArgs {
public:
bool stage;
bool collapseEmptyTrees;
unsigned char* codeBuffer;
int lengthOfCode;
@ -384,11 +382,10 @@ public:
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the node from the tree.
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, bool collapseEmptyTrees) {
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees) {
// recurse the tree while decoding the codeBuffer, once you find the node in question, recurse
// back and implement color reaveraging, and marking of lastChanged
DeleteVoxelCodeFromTreeArgs args;
args.stage = stage;
args.collapseEmptyTrees = collapseEmptyTrees;
args.codeBuffer = codeBuffer;
args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer);
@ -408,7 +405,6 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat
// matches, then we've reached our target node.
if (lengthOfNodeCode == args->lengthOfCode) {
// we've reached our target, depending on how we're called we may be able to operate on it
// if we're in "stage" mode, then we can could have the node staged, otherwise we can't really delete
// it here, we need to recurse up, and delete it there. So we handle these cases the same to keep
// the logic consistent.
args->deleteLastChild = true;
@ -468,11 +464,7 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat
// If the lower level determined it needs to be deleted, then we should delete now.
if (args->deleteLastChild) {
if (args->stage) {
childNode->stageForDeletion();
} else {
node->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this node
}
node->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this node
// track our tree dirtiness
_isDirty = true;
@ -602,7 +594,7 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe
int codeLength = numberOfThreeBitSectionsInCode(voxelCode);
int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA;
deleteVoxelCodeFromTree(voxelCode, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE);
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE);
voxelCode+=voxelDataSize;
atByte+=voxelDataSize;
@ -1071,6 +1063,9 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer,
int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel) const {
// you can't call this without a valid node
assert(node);
// How many bytes have we written so far at this level;
int bytesAtThisLevel = 0;
@ -1228,6 +1223,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
}
// track stats
// must check childNode here, because it could be we got here with no childNode
if (params.stats && childNode) {
params.stats->traversed(childNode);
}
@ -1243,7 +1239,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum)));
if (!childIsInView) {
if (params.stats) {
// must check childNode here, because it could be we got here because there was no childNode
if (params.stats && childNode) {
params.stats->skippedOutOfView(childNode);
}
} else {
@ -1253,6 +1250,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust);
if (!(distance < boundaryDistance)) {
// don't need to check childNode here, because we can't get here with no childNode
if (params.stats) {
params.stats->skippedDistance(childNode);
}
@ -1306,9 +1304,11 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
// track some stats
if (params.stats) {
// don't need to check childNode here, because we can't get here with no childNode
if (!shouldRender && childNode->isLeaf()) {
params.stats->skippedDistance(childNode);
}
// don't need to check childNode here, because we can't get here with no childNode
if (childIsOccluded) {
params.stats->skippedOccluded(childNode);
}
@ -1339,6 +1339,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
inViewWithColorCount++;
} else {
// otherwise just track stats of the items we discarded
// don't need to check childNode here, because we can't get here with no childNode
if (params.stats) {
if (childWasInView) {
params.stats->skippedWasInView(childNode);
@ -1368,6 +1369,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color
bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color
// don't need to check childNode here, because we can't get here with no childNode
if (params.stats) {
params.stats->colorSent(childNode);
}
@ -1407,6 +1409,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
} else {
bag.insert(node);
// don't need to check node here, because we can't get here with no node
if (params.stats) {
params.stats->didntFit(node);
}
@ -1474,11 +1477,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
if (params.includeColor && !params.includeExistsBits && childTreeBytesOut == 2) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
// If we've asked for existBits, this is also true, except that the tree will output 3 bytes
// NOTE: does this introduce a problem with detecting deletion??
if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
// We used to try to collapse trees that didn't contain any data, but this does appear to create a problem
// in detecting node deletion. So, I've commented this out but left it in here as a warning to anyone else
// about not attempting to add this optimization back in, without solving the node deletion case.
// We need to send these bitMasks in case the exists in tree bitmask is indicating the deletion of a tree
//if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
// childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
//}
bytesAtThisLevel += childTreeBytesOut;
availableBytes -= childTreeBytesOut;

View file

@ -22,23 +22,23 @@
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
#define NO_EXISTS_BITS false
#define WANT_EXISTS_BITS true
#define NO_COLOR false
#define WANT_COLOR true
#define IGNORE_VIEW_FRUSTUM NULL
#define JUST_STAGE_DELETION true
#define ACTUALLY_DELETE false
#define COLLAPSE_EMPTY_TREE true
#define DONT_COLLAPSE false
#define NO_OCCLUSION_CULLING false
#define WANT_OCCLUSION_CULLING true
#define IGNORE_COVERAGE_MAP NULL
#define DONT_CHOP 0
#define NO_BOUNDARY_ADJUST 0
#define LOW_RES_MOVING_ADJUST 1
#define IGNORE_LAST_SENT 0
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
const bool NO_COLOR = false;
const bool WANT_COLOR = true;
const bool COLLAPSE_EMPTY_TREE = true;
const bool DONT_COLLAPSE = false;
const bool NO_OCCLUSION_CULLING = false;
const bool WANT_OCCLUSION_CULLING = true;
const int DONT_CHOP = 0;
const int NO_BOUNDARY_ADJUST = 0;
const int LOW_RES_MOVING_ADJUST = 1;
const uint64_t IGNORE_LAST_SENT = 0;
#define IGNORE_SCENE_STATS NULL
#define IGNORE_VIEW_FRUSTUM NULL
#define IGNORE_COVERAGE_MAP NULL
class EncodeBitstreamParams {
public:
@ -113,12 +113,11 @@ public:
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
VoxelNode* destinationNode = NULL);
void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false);
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage = ACTUALLY_DELETE,
bool collapseEmptyTrees = DONT_COLLAPSE);
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void printTreeForDebugging(VoxelNode* startNode);
void reaverageVoxelColors(VoxelNode* startNode);
void deleteVoxelAt(float x, float y, float z, float s, bool stage = false);
void deleteVoxelAt(float x, float y, float z, float s);
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, bool destructive = false);