Merge branch 'master' of https://github.com/worklist/hifi into Remove-body

This commit is contained in:
Philip Rosedale 2013-08-02 16:08:16 -07:00
commit 639b6be01a
31 changed files with 1164 additions and 198 deletions

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="152px" height="152px" viewBox="0 0 152 152" enable-background="new 0 0 152 152" xml:space="preserve">
<path fill="#333333" d="M76,13.441C41.45,13.441,13.442,41.45,13.442,76S41.45,138.558,76,138.558S138.558,110.55,138.558,76
S110.55,13.441,76,13.441z M76,107.279c-17.275,0-31.279-14.004-31.279-31.279S58.725,44.721,76,44.721S107.279,58.725,107.279,76
S93.275,107.279,76,107.279z"/>
<g id="XMLID_46_">
<g>
<path fill="#EAEAEA" d="M41.465,38.653c0.483-0.558-1.506-5.81-0.071-6.338c1.041-0.38,0.971,1.524,1.041,2.08
c0.102,1.143,0.346,2.528,0.727,3.567c0.346-1.663,0.312-3.36,0.381-5.056c0.034-0.521,0.173-1.697,0.865-1.629
c1.628,0.105-0.242,5.576,0.832,6.269c0.727-0.832,0.935-2.979,1.212-4.052c0.104-0.45,0.245-1.873,1.246-1.281
c0.763,0.451,0.138,3.291-0.103,4.19c-0.244,0.935-0.244,1.592-0.521,2.667c-0.067,0.728-0.206,1.524,0.277,2.078
c0.867,0,2.859-2.46,3.829-1.699c0.658,0.485,0.331,1.109-0.12,1.403c-3.326,2.149-2.808,4.815-4.402,5.126
c0,2.73-0.165,2.833,0.11,5.551c-1.731,0.287-3.542,0.216-5.199,0.106c0-0.001,0-0.001,0-0.001
c0.113-1.432,0.067-0.342,0.173-1.931c0.079-1.235,0.103-2.339,0.139-3.551l-0.036-0.138c-1.038-0.278-1.349-2.095-1.696-3.307
c-0.38-1.318,0.069-2.495-0.416-3.775c-0.308-0.97-1.62-3.381-0.795-4.086c0.487-0.417,0.761-0.07,1.142,0.83
C40.392,36.408,40.549,38.549,41.465,38.653z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -852,6 +852,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
deleteVoxelUnderCursor();
}
}
_pieMenu.mouseMoveEvent(_mouseX, _mouseY);
}
}
@ -869,7 +871,12 @@ void Application::mousePressEvent(QMouseEvent* event) {
_mouseDragStartedY = _mouseY;
_mouseVoxelDragging = _mouseVoxel;
_mousePressed = true;
maybeEditVoxelUnderCursor();
if (!maybeEditVoxelUnderCursor()) {
_pieMenu.mousePressEvent(_mouseX, _mouseY);
}
if (MAKE_SOUND_ON_VOXEL_CLICK && _isHoverVoxel && !_isHoverVoxelSounding) {
_hoverVoxelOriginalColor[0] = _hoverVoxel.red;
_hoverVoxelOriginalColor[1] = _hoverVoxel.green;
@ -892,6 +899,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
_mouseY = event->y();
_mousePressed = false;
checkBandwidthMeterClick();
_pieMenu.mouseReleaseEvent(_mouseX, _mouseY);
}
}
}
@ -1383,6 +1392,10 @@ void Application::doFalseColorizeOccludedV2() {
_voxels.falseColorizeOccludedV2();
}
void Application::doFalseColorizeBySource() {
_voxels.falseColorizeBySource();
}
void Application::doTrueVoxelColors() {
_voxels.trueColorize();
}
@ -1804,6 +1817,7 @@ void Application::initMenu() {
_testPing->setChecked(true);
(_fullScreenMode = optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F))->setCheckable(true);
optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true);
optionsMenu->addAction("Toggle Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true);
optionsMenu->addAction("Cycle Webcam Send Mode", _webcam.getGrabber(), SLOT(cycleVideoSendMode()));
optionsMenu->addAction("Go Home", this, SLOT(goHome()));
@ -1832,7 +1846,6 @@ void Application::initMenu() {
(_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(
@ -1919,6 +1932,7 @@ void Application::initMenu() {
renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView()));
renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O);
renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P);
renderDebugMenu->addAction("FALSE Color By Source", this, SLOT(doFalseColorizeBySource()), Qt::CTRL | Qt::SHIFT | Qt::Key_S);
renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T);
(_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true);
@ -2057,7 +2071,15 @@ void Application::init() {
_palette.addTool(&_swatch);
_palette.addAction(_colorVoxelMode, 0, 2);
_palette.addAction(_eyedropperMode, 0, 3);
_palette.addAction(_selectVoxelMode, 0, 4);
_palette.addAction(_selectVoxelMode, 0, 4);
_pieMenu.init("./resources/images/hifi-interface-tools-v2-pie.svg",
_glWidget->width(),
_glWidget->height());
_followMode = new QAction(this);
connect(_followMode, SIGNAL(triggered()), this, SLOT(toggleFollowMode()));
_pieMenu.addAction(_followMode);
}
@ -2069,7 +2091,7 @@ const float HEAD_SPHERE_RADIUS = 0.07;
static uint16_t DEFAULT_NODE_ID_REF = 1;
bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) {
NodeList* nodeList = NodeList::getInstance();
@ -2082,11 +2104,11 @@ bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& m
_lookatIndicatorScale = avatar->getScale();
_lookatOtherPosition = headPosition;
nodeID = avatar->getOwningNode()->getNodeID();
return true;
return avatar;
}
}
}
return false;
return NULL;
}
void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera) {
@ -2101,6 +2123,7 @@ void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& which
}
void Application::update(float deltaTime) {
// Use Transmitter Hand to move hand if connected, else use mouse
if (_myTransmitter.isConnected()) {
const float HAND_FORCE_SCALING = 0.01f;
@ -2997,6 +3020,10 @@ void Application::displayOverlay() {
_swatch.checkColor();
}
if (_pieMenu.isDisplayed()) {
_pieMenu.render();
}
glPopMatrix();
}
@ -3009,19 +3036,35 @@ void Application::displayStats() {
drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats);
if (_testPing->isChecked()) {
int pingAudio = 0, pingAvatar = 0, pingVoxel = 0;
int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0;
NodeList *nodeList = NodeList::getInstance();
Node *audioMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
Node *avatarMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
Node *voxelServerNode = nodeList->soloNodeOfType(NODE_TYPE_VOXEL_SERVER);
NodeList* nodeList = NodeList::getInstance();
Node* audioMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
Node* avatarMixerNode = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER);
pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0;
pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0;
pingVoxel = voxelServerNode ? voxelServerNode->getPingMs() : 0;
// Now handle voxel servers, since there could be more than one, we average their ping times
unsigned long totalPingVoxel = 0;
int voxelServerCount = 0;
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
totalPingVoxel += node->getPingMs();
voxelServerCount++;
if (pingVoxelMax < node->getPingMs()) {
pingVoxelMax = node->getPingMs();
}
}
}
if (voxelServerCount) {
pingVoxel = totalPingVoxel/voxelServerCount;
}
char pingStats[200];
sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d ", pingAudio, pingAvatar, pingVoxel);
sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax);
drawtext(10, statsVerticalOffset + 35, 0.10f, 0, 1.0, 0, pingStats);
}
@ -3396,7 +3439,7 @@ void Application::shiftPaintingColor() {
}
void Application::maybeEditVoxelUnderCursor() {
bool Application::maybeEditVoxelUnderCursor() {
if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) {
if (_mouseVoxel.s != 0) {
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
@ -3467,7 +3510,11 @@ void Application::maybeEditVoxelUnderCursor() {
deleteVoxelUnderCursor();
} else if (_eyedropperMode->isChecked()) {
eyedropperVoxelUnderCursor();
} else {
return false;
}
return true;
}
void Application::deleteVoxelUnderCursor() {
@ -3510,6 +3557,22 @@ void Application::goHome() {
_myAvatar.setPosition(START_LOCATION);
}
void Application::toggleFollowMode() {
glm::vec3 mouseRayOrigin, mouseRayDirection;
_viewFrustum.computePickRay(_pieMenu.getX() / (float)_glWidget->width(),
_pieMenu.getY() / (float)_glWidget->height(),
mouseRayOrigin, mouseRayDirection);
glm::vec3 eyePositionIgnored;
uint16_t nodeIDIgnored;
Avatar* leadingAvatar = isLookingAtOtherAvatar(mouseRayOrigin,
mouseRayDirection,
eyePositionIgnored,
nodeIDIgnored);
_myAvatar.follow(leadingAvatar);
}
void Application::resetSensors() {
_headMouseX = _mouseX = _glWidget->width() / 2;
_headMouseY = _mouseY = _glWidget->height() / 2;
@ -3622,13 +3685,15 @@ void* Application::networkReceive(void* args) {
} // fall through to piggyback message
if (app->_renderVoxels->isChecked()) {
Node* voxelServer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_VOXEL_SERVER);
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
voxelServer->lock();
if (messageData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
app->_environment.parseData(&senderAddress, messageData, messageLength);
} else {
app->_voxels.setDataSourceID(voxelServer->getNodeID());
app->_voxels.parseData(messageData, messageLength);
app->_voxels.setDataSourceID(UNKNOWN_NODE_ID);
}
voxelServer->unlock();
}

View file

@ -39,6 +39,7 @@
#include "ViewFrustum.h"
#include "VoxelSystem.h"
#include "Webcam.h"
#include "PieMenu.h"
#include "avatar/Avatar.h"
#include "avatar/HandControl.h"
#include "ui/BandwidthDialog.h"
@ -149,6 +150,7 @@ private slots:
void doFalseColorizeByDistance();
void doFalseColorizeOccluded();
void doFalseColorizeOccludedV2();
void doFalseColorizeBySource();
void doFalseColorizeInView();
void doTrueVoxelColors();
void doTreeStats();
@ -186,6 +188,8 @@ private slots:
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
void goHome();
void toggleFollowMode();
private:
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
@ -205,7 +209,7 @@ private:
void init();
void update(float deltaTime);
bool isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
Avatar* isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, uint16_t& nodeID);
void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera);
@ -222,7 +226,7 @@ private:
void setupPaintingVoxel();
void shiftPaintingColor();
void maybeEditVoxelUnderCursor();
bool maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void resetSensors();
@ -290,6 +294,8 @@ private:
QAction* _simulateLeapHand; // When there's no Leap, use this to pretend there is one and feed fake hand data
QAction* _testRaveGlove; // Test fancy sparkle-rave-glove mode
QAction* _followMode;
BandwidthMeter _bandwidthMeter;
BandwidthDialog* _bandwidthDialog;
@ -429,6 +435,8 @@ private:
ToolsPalette _palette;
Swatch _swatch;
PieMenu _pieMenu;
VoxelSceneStats _voxelSceneStats;
};

View file

@ -27,6 +27,7 @@ ParticleSystem::ParticleSystem() {
for (unsigned int emitterIndex = 0; emitterIndex < MAX_EMITTERS; emitterIndex++) {
Emitter * e = &_emitter[emitterIndex];
e->active = false;
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);
@ -72,25 +73,16 @@ void ParticleSystem::simulate(float deltaTime) {
_timer += deltaTime;
// emit particles
for (int e = 0; e < _numEmitters; e++) {
// update emitters
for (int emitterIndex = 0; emitterIndex < _numEmitters; emitterIndex++) {
assert(emitterIndex <= MAX_EMITTERS);
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);
if (_emitter[emitterIndex].active) {
updateEmitter(emitterIndex, deltaTime);
}
}
// update particles
// update particles
for (int p = 0; p < MAX_PARTICLES; p++) {
if (_particle[p].alive) {
if (_particle[p].age > _emitter[_particle[p].emitterIndex].particleLifespan) {
@ -102,6 +94,20 @@ void ParticleSystem::simulate(float deltaTime) {
}
}
void ParticleSystem::updateEmitter(int emitterIndex, float deltaTime) {
_emitter[emitterIndex].emitReserve += _emitter[emitterIndex].rate * deltaTime;
_emitter[emitterIndex].numParticlesEmittedThisTime = (int)_emitter[emitterIndex].emitReserve;
_emitter[emitterIndex].emitReserve -= _emitter[emitterIndex].numParticlesEmittedThisTime;
for (int p = 0; p < _emitter[emitterIndex].numParticlesEmittedThisTime; p++) {
float timeFraction = (float)p / (float)_emitter[emitterIndex].numParticlesEmittedThisTime;
createParticle(emitterIndex, timeFraction);
}
}
void ParticleSystem::createParticle(int e, float timeFraction) {
for (unsigned int p = 0; p < MAX_PARTICLES; p++) {
@ -212,7 +218,6 @@ void ParticleSystem::setParticleAttributes(int emitterIndex, ParticleLifeStage l
}
void ParticleSystem::updateParticle(int p, float deltaTime) {
Emitter myEmitter = _emitter[_particle[p].emitterIndex];
@ -363,14 +368,16 @@ void ParticleSystem::killAllParticles() {
void ParticleSystem::render() {
// render the emitters
for (int e = 0; e < _numEmitters; e++) {
for (int e = 0; e < MAX_EMITTERS; e++) {
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(_particle[0].radius, _emitter[e].particleResolution, _emitter[e].particleResolution);
glPopMatrix();
if (_emitter[e].active) {
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(_particle[0].radius, _emitter[e].particleResolution, _emitter[e].particleResolution);
glPopMatrix();
}
}
if (_emitter[e].visible) {

View file

@ -10,10 +10,10 @@
#include <glm/gtc/quaternion.hpp>
const int MAX_PARTICLES = 5000;
const int NULL_EMITTER = -1;
const int NULL_PARTICLE = -1;
const int MAX_EMITTERS = 100;
const int MAX_PARTICLES = 5000;
enum ParticleRenderStyle
{
@ -78,6 +78,7 @@ public:
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 setEmitterActive (int emitterIndex, bool active ) {_emitter[emitterIndex].active = active; }
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; }
@ -101,6 +102,7 @@ private:
};
struct Emitter {
bool active; // if false, the emitter is disabled - allows for easy switching on and off
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
@ -124,6 +126,7 @@ private:
float _timer;
// private methods
void updateEmitter(int emitterIndex, float deltaTime);
void updateParticle(int index, float deltaTime);
void createParticle(int e, float timeFraction);
void killParticle(int p);

138
interface/src/PieMenu.cpp Normal file
View file

@ -0,0 +1,138 @@
//
// PieMenu.cpp
// hifi
//
// Created by Clement Brisset on 7/18/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "PieMenu.h"
#include <cmath>
#include <QAction>
#include <QSvgRenderer>
#include <QPainter>
#include <QGLWidget>
#include <SharedUtil.h>
PieMenu::PieMenu() :
_radiusIntern(30),
_radiusExtern(70),
_magnification(1.2f),
_isDisplayed(false) {
}
void PieMenu::init(const char *fileName, int screenWidth, int screenHeight) {
// Load SVG
switchToResourcesParentIfRequired();
QSvgRenderer renderer((QString) QString(fileName));
// Prepare a QImage with desired characteritisc
QImage image(2 * _radiusExtern, 2 * _radiusExtern, QImage::Format_ARGB32);
image.fill(0x0);
// Get QPainter that paints to the image
QPainter painter(&image);
renderer.render(&painter);
//get the OpenGL-friendly image
_textureImage = QGLWidget::convertToGLFormat(image);
glGenTextures(1, &_textureID);
glBindTexture(GL_TEXTURE_2D, _textureID);
//generate the texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
_textureImage.width(),
_textureImage.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE,
_textureImage.bits());
//texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
void PieMenu::addAction(QAction* action){
_actions.push_back(action);
}
void PieMenu::render() {
if (_actions.size() == 0) {
return;
}
float start = M_PI / 2.0f;
float end = start + 2.0f * M_PI;
float step = 2.0f * M_PI / 100.0f;
float distance = sqrt((_mouseX - _x) * (_mouseX - _x) +
(_mouseY - _y) * (_mouseY - _y));
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _textureID);
glColor3f(1.0f, 1.0f, 1.0f);
if (_radiusIntern < distance) {
float angle = atan2((_mouseY - _y), (_mouseX - _x)) - start;
angle = (0.0f < angle) ? angle : angle + 2.0f * M_PI;
_selectedAction = floor(angle / (2.0f * M_PI / _actions.size()));
start = start + _selectedAction * 2.0f * M_PI / _actions.size();
end = start + 2.0f * M_PI / _actions.size();
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0.5f, 0.5f);
glVertex2f(_x, _y);
for (float i = start; i < end; i += step) {
glTexCoord2f(0.5f + 0.5f * cos(i), 0.5f - 0.5f * sin(i));
glVertex2f(_x + _magnification * _radiusExtern * cos(i),
_y + _magnification * _radiusExtern * sin(i));
}
glTexCoord2f(0.5f + 0.5f * cos(end), 0.5f + - 0.5f * sin(end));
glVertex2f(_x + _magnification * _radiusExtern * cos(end),
_y + _magnification * _radiusExtern * sin(end));
glEnd();
} else {
_selectedAction = -1;
glBegin(GL_QUADS);
glTexCoord2f(1, 1);
glVertex2f(_x + _radiusExtern, _y - _radiusExtern);
glTexCoord2f(1, 0);
glVertex2f(_x + _radiusExtern, _y + _radiusExtern);
glTexCoord2f(0, 0);
glVertex2f(_x - _radiusExtern, _y + _radiusExtern);
glTexCoord2f(0, 1);
glVertex2f(_x - _radiusExtern, _y - _radiusExtern);
glEnd();
}
glDisable(GL_TEXTURE_2D);
}
void PieMenu::resize(int screenWidth, int screenHeight) {
}
void PieMenu::mouseMoveEvent(int x, int y) {
_mouseX = x;
_mouseY = y;
}
void PieMenu::mousePressEvent(int x, int y) {
_x = _mouseX = x;
_y = _mouseY = y;
_selectedAction = -1;
_isDisplayed = true;
}
void PieMenu::mouseReleaseEvent(int x, int y) {
if (0 <= _selectedAction && _selectedAction < _actions.size() && _actions[_selectedAction]) {
_actions[_selectedAction]->trigger();
}
_isDisplayed = false;
}

59
interface/src/PieMenu.h Normal file
View file

@ -0,0 +1,59 @@
//
// PieMenu.h
// hifi
//
// Created by Clement Brisset on 7/18/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__PieMenu__
#define __hifi__PieMenu__
#include <vector>
#include "InterfaceConfig.h"
#include "Util.h"
#include <QImage>
class QAction;
class PieMenu {
public:
PieMenu();
void init(const char* fileName, int screenWidth, int screenHeight);
void addAction(QAction* action);
void render();
void resize(int screenWidth, int screenHeight);
bool isDisplayed() const {return _isDisplayed;}
int getX () const {return _x;}
int getY () const {return _y;}
void mouseMoveEvent (int x, int y);
void mousePressEvent (int x, int y);
void mouseReleaseEvent(int x, int y);
private:
QImage _textureImage;
GLuint _textureID;
// position of the menu
int _x;
int _y;
int _radiusIntern;
int _radiusExtern;
float _magnification;
int _mouseX;
int _mouseY;
int _selectedAction;
bool _isDisplayed;
std::vector<QAction*> _actions;
};
#endif /* defined(__hifi__PieMenu__) */

View file

@ -23,6 +23,8 @@
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include "Application.h"
#include "CoverageMap.h"
@ -61,6 +63,8 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
VoxelNode::addDeleteHook(this);
_abandonedVBOSlots = 0;
_falseColorizeBySource = false;
_dataSourceID = UNKNOWN_NODE_ID;
}
void VoxelSystem::nodeDeleted(VoxelNode* node) {
@ -153,13 +157,15 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
case PACKET_TYPE_VOXEL_DATA: {
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
// ask the VoxelTree to read the bitstream into the tree
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, WANT_COLOR, WANT_EXISTS_BITS);
ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID());
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
}
break;
case PACKET_TYPE_VOXEL_DATA_MONOCHROME: {
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
// ask the VoxelTree to read the MONOCHROME bitstream into the tree
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, NO_COLOR, WANT_EXISTS_BITS);
ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID());
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
}
break;
case PACKET_TYPE_Z_COMMAND:
@ -747,6 +753,7 @@ void VoxelSystem::randomizeVoxelColors() {
_nodeCount = 0;
_tree->recurseTreeWithOperation(randomColorOperation);
qDebug("setting randomized true color for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -761,6 +768,7 @@ void VoxelSystem::falseColorizeRandom() {
_nodeCount = 0;
_tree->recurseTreeWithOperation(falseColorizeRandomOperation);
qDebug("setting randomized false color for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -775,6 +783,7 @@ void VoxelSystem::trueColorize() {
_nodeCount = 0;
_tree->recurseTreeWithOperation(trueColorizeOperation);
qDebug("setting true color for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -795,6 +804,80 @@ void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) {
_nodeCount = 0;
_tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum);
qDebug("setting in view false color for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
// helper classes and args for falseColorizeBySource
class groupColor {
public:
unsigned char red, green, blue;
groupColor(unsigned char red, unsigned char green, unsigned char blue) :
red(red), green(green), blue(blue) { };
groupColor() :
red(0), green(0), blue(0) { };
};
class colorizeBySourceArgs {
public:
std::map<uint16_t, groupColor> colors;
};
// Will false colorize voxels that are not in view
bool VoxelSystem::falseColorizeBySourceOperation(VoxelNode* node, void* extraData) {
colorizeBySourceArgs* args = (colorizeBySourceArgs*)extraData;
_nodeCount++;
if (node->isColored()) {
// pick a color based on the source - we want each source to be obviously different
uint16_t nodeID = node->getSourceID();
//printf("false colorizing from source %d, color: %d, %d, %d\n", nodeID,
// args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue);
node->setFalseColor(args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue);
}
return true; // keep going!
}
void VoxelSystem::falseColorizeBySource() {
_nodeCount = 0;
colorizeBySourceArgs args;
const int NUMBER_OF_COLOR_GROUPS = 3;
const unsigned char MIN_COLOR = 128;
int voxelServerCount = 0;
groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { groupColor(255, 0, 0),
groupColor(0, 255, 0),
groupColor(0, 0, 255)};
// create a bunch of colors we'll use during colorization
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
uint16_t nodeID = node->getNodeID();
int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS;
args.colors[nodeID] = groupColors[groupColor];
//printf("assigning color for source %d, color: %d, %d, %d\n", nodeID,
// args.colors[nodeID].red, args.colors[nodeID].green, args.colors[nodeID].blue);
if (groupColors[groupColor].red > 0) {
groupColors[groupColor].red = ((groupColors[groupColor].red - MIN_COLOR)/2) + MIN_COLOR;
}
if (groupColors[groupColor].green > 0) {
groupColors[groupColor].green = ((groupColors[groupColor].green - MIN_COLOR)/2) + MIN_COLOR;
}
if (groupColors[groupColor].blue > 0) {
groupColors[groupColor].blue = ((groupColors[groupColor].blue - MIN_COLOR)/2) + MIN_COLOR;
}
voxelServerCount++;
}
}
_tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args);
qDebug("setting false color by source for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -848,6 +931,7 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) {
_nodeCount = 0;
_tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum);
qDebug("setting in distance false color for %d nodes\n", _nodeCount);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -1027,6 +1111,7 @@ void VoxelSystem::falseColorizeRandomEveryOther() {
_tree->recurseTreeWithOperation(falseColorizeRandomEveryOtherOperation,&args);
qDebug("randomized false color for every other node: total %ld, colorable %ld, colored %ld\n",
args.totalNodes, args.colorableNodes, args.coloredNodes);
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -1331,6 +1416,7 @@ void VoxelSystem::falseColorizeOccluded() {
//myCoverageMap.erase();
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}
@ -1447,6 +1533,7 @@ void VoxelSystem::falseColorizeOccludedV2() {
//myCoverageMapV2.erase();
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
}

View file

@ -33,6 +33,9 @@ public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
~VoxelSystem();
void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; };
int getDataSourceID() const { return _dataSourceID; };
int parseData(unsigned char* sourceBuffer, int numBytes);
virtual void init();
@ -62,6 +65,8 @@ public:
void falseColorizeRandomEveryOther();
void falseColorizeOccluded();
void falseColorizeOccludedV2();
void falseColorizeBySource();
void killLocalVoxels();
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
@ -94,7 +99,7 @@ public:
CoverageMap myCoverageMap;
virtual void nodeDeleted(VoxelNode* node);
protected:
float _treeScale;
int _maxVoxels;
@ -134,7 +139,7 @@ private:
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData);
int updateNodeInArraysAsFullVBO(VoxelNode* node);
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
@ -195,6 +200,9 @@ private:
void freeBufferIndex(glBufferIndex index);
void clearFreeBufferIndexes();
bool _falseColorizeBySource;
int _dataSourceID;
};
#endif

View file

@ -33,7 +33,7 @@ int jointVectorMetaType = qRegisterMetaType<JointVector>("JointVector");
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
int rotatedRectMetaType = qRegisterMetaType<RotatedRect>("cv::RotatedRect");
Webcam::Webcam() : _enabled(false), _active(false), _colorTextureID(0), _depthTextureID(0) {
Webcam::Webcam() : _enabled(false), _active(false), _colorTextureID(0), _depthTextureID(0), _skeletonTrackingOn(false) {
// the grabber simply runs as fast as possible
_grabber = new FrameGrabber();
_grabber->moveToThread(&_grabberThread);
@ -197,7 +197,7 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midF
_aspectRatio = aspectRatio;
_faceRect = faceRect;
_sending = sending;
_joints = joints;
_joints = _skeletonTrackingOn ? joints : JointVector();
_frameCount++;
const int MAX_FPS = 60;

View file

@ -47,6 +47,7 @@ public:
FrameGrabber* getGrabber() { return _grabber; }
bool isActive() const { return _active; }
bool isSending() const { return _sending; }
GLuint getColorTextureID() const { return _colorTextureID; }
@ -62,13 +63,14 @@ public:
const JointVector& getEstimatedJoints() const { return _estimatedJoints; }
void reset();
void renderPreview(int screenWidth, int screenHeight);
void renderPreview(int screenWidth, int screenHeight);
public slots:
void setEnabled(bool enabled);
void setFrame(const cv::Mat& color, int format, const cv::Mat& depth, float midFaceDepth,
float aspectRatio, const cv::RotatedRect& faceRect, bool sending, const JointVector& joints);
void setSkeletonTrackingOn(bool toggle) { _skeletonTrackingOn = toggle; };
private:
@ -95,6 +97,8 @@ private:
glm::vec3 _estimatedPosition;
glm::vec3 _estimatedRotation;
JointVector _estimatedJoints;
bool _skeletonTrackingOn;
};
class FrameGrabber : public QObject {

View file

@ -101,7 +101,8 @@ Avatar::Avatar(Node* owningNode) :
_lastCollisionPosition(0, 0, 0),
_speedBrakes(false),
_isThrustOn(false),
_voxels(this)
_voxels(this),
_leadingAvatar(NULL)
{
// give the pointer to our head to inherited _headData variable from AvatarData
_headData = &_head;
@ -404,6 +405,33 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
_thrust += _scale * THRUST_JUMP * up;
_shouldJump = false;
}
// Add thrusts from leading avatar
if (_leadingAvatar != NULL) {
glm::vec3 toTarget = _leadingAvatar->getPosition() - _position;
if (.5f < up.x * toTarget.x + up.y * toTarget.y + up.z * toTarget.z) {
_thrust += _scale * THRUST_MAG_UP * deltaTime * up;
} else if (up.x * toTarget.x + up.y * toTarget.y + up.z * toTarget.z < -.5f) {
_thrust -= _scale * THRUST_MAG_UP * deltaTime * up;
}
if (glm::length(_position - _leadingAvatar->getPosition()) > _scale * _stringLength) {
_thrust += _scale * THRUST_MAG_FWD * deltaTime * front;
} else {
toTarget = _leadingAvatar->getHead().getLookAtPosition() - _position;
getHead().setLookAtPosition(_leadingAvatar->getHead().getLookAtPosition());
}
float yawAngle = angleBetween(front, glm::vec3(toTarget.x, 0.f, toTarget.z));
if (yawAngle < -10.f || 10.f < yawAngle){
if (right.x * toTarget.x + right.y * toTarget.y + right.z * toTarget.z > 0) {
_bodyYawDelta -= YAW_MAG * deltaTime;
} else {
_bodyYawDelta += YAW_MAG * deltaTime;
}
}
}
// Add thrusts from Transmitter
if (transmitter) {
@ -447,6 +475,18 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
_isThrustOn = (glm::length(_thrust) > EPSILON);
}
void Avatar::follow(Avatar* leadingAvatar) {
const float MAX_STRING_LENGTH = 2;
_leadingAvatar = leadingAvatar;
if (_leadingAvatar != NULL) {
_stringLength = glm::length(_position - _leadingAvatar->getPosition()) / _scale;
if (_stringLength > MAX_STRING_LENGTH) {
_stringLength = MAX_STRING_LENGTH;
}
}
}
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
glm::quat orientation = getOrientation();
@ -475,6 +515,13 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
if (isMyAvatar()) {
updateThrust(deltaTime, transmitter);
}
// Ajust, scale, thrust and lookAt position when following an other avatar
if (isMyAvatar() && _leadingAvatar && _scale != _leadingAvatar->getScale()) {
float scale = 0.95f * _scale + 0.05f * _leadingAvatar->getScale();
setScale(scale);
Application::getInstance()->getCamera()->setScale(scale);
}
// copy velocity so we can use it later for acceleration
glm::vec3 oldVelocity = getVelocity();
@ -681,7 +728,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
_head.setScale(_scale);
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
_head.simulate(deltaTime, isMyAvatar());
// use speed and angular velocity to determine walking vs. standing
if (_speed + fabs(_bodyYawDelta) > 0.2) {
_mode = AVATAR_MODE_WALKING;

View file

@ -114,6 +114,7 @@ public:
void reset();
void simulate(float deltaTime, Transmitter* transmitter);
void updateThrust(float deltaTime, Transmitter * transmitter);
void follow(Avatar* leadingAvatar);
void updateFromGyrosAndOrWebcam(bool gyroLook,
const glm::vec3& amplifyAngle,
float yawFromTouch,
@ -157,18 +158,16 @@ public:
float getElapsedTimeMoving () const { return _elapsedTimeMoving;}
float getElapsedTimeSinceCollision() const { return _elapsedTimeSinceCollision;}
const glm::vec3& getLastCollisionPosition () const { return _lastCollisionPosition;}
float getAbsoluteHeadYaw () const;
float getAbsoluteHeadPitch () const;
Head& getHead () {return _head; }
Hand& getHand () {return _hand; }
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; }
float getAbsoluteHeadYaw () const;
float getAbsoluteHeadPitch () const;
Head& getHead () {return _head; }
Hand& getHand () {return _hand; }
glm::quat getOrientation () const;
glm::quat getWorldAlignedOrientation() const;
const glm::vec3& getMouseRayOrigin () const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection () const { return _mouseRayDirection; }
Avatar* getLeadingAvatar () const { return _leadingAvatar; }
glm::vec3 getGravity () const { return _gravity; }
glm::vec3 getUprightHeadPosition() const;
glm::vec3 getUprightEyeLevelPosition() const;
@ -255,7 +254,10 @@ private:
glm::vec3 _lastCollisionPosition;
bool _speedBrakes;
bool _isThrustOn;
Avatar* _leadingAvatar;
float _stringLength;
AvatarVoxelSystem _voxels;
// private methods...

View file

@ -250,7 +250,8 @@ void AvatarVoxelSystem::handleVoxelDownloadProgress(qint64 bytesReceived, qint64
_voxelReply->deleteLater();
_voxelReply = 0;
_tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), WANT_COLOR, NO_EXISTS_BITS);
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
_tree->readBitstreamToTree((unsigned char*)entirety.data(), entirety.size(), args);
setupNewVoxelsForDrawing();
}

View file

@ -14,7 +14,7 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
const bool SHOW_LEAP_HAND = true;
const bool SHOW_LEAP_HAND = false;
using namespace std;
@ -51,6 +51,7 @@ void Hand::reset() {
void Hand::simulate(float deltaTime, bool isMine) {
if (_isRaveGloveActive) {
updateRaveGloveParticles(deltaTime);
}
@ -63,7 +64,8 @@ void Hand::calculateGeometry() {
_basePosition = head.getPosition() + head.getOrientation() * offset;
_baseOrientation = head.getOrientation();
_leapBalls.clear();
// generate finger tip balls....
_leapFingerTipBalls.clear();
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
@ -71,8 +73,8 @@ void Hand::calculateGeometry() {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
const float standardBallRadius = 0.01f;
_leapBalls.resize(_leapBalls.size() + 1);
HandBall& ball = _leapBalls.back();
_leapFingerTipBalls.resize(_leapFingerTipBalls.size() + 1);
HandBall& ball = _leapFingerTipBalls.back();
ball.rotation = _baseOrientation;
ball.position = finger.getTipPosition();
ball.radius = standardBallRadius;
@ -82,6 +84,27 @@ void Hand::calculateGeometry() {
}
}
}
// generate finger rot balls....
_leapFingerRootBalls.clear();
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
const float standardBallRadius = 0.01f;
_leapFingerRootBalls.resize(_leapFingerRootBalls.size() + 1);
HandBall& ball = _leapFingerRootBalls.back();
ball.rotation = _baseOrientation;
ball.position = finger.getRootPosition();
ball.radius = standardBallRadius;
ball.touchForce = 0.0;
ball.isCollidable = true;
}
}
}
}
}
void Hand::setRaveGloveEffectsMode(QKeyEvent* event) {
@ -120,8 +143,9 @@ void Hand::render(bool lookingInMirror) {
glEnable(GL_RESCALE_NORMAL);
if ( SHOW_LEAP_HAND ) {
renderFingerTrails();
renderHandSpheres();
//renderLeapHands();
renderLeapFingerTrails();
renderLeapHandSpheres();
}
}
@ -153,18 +177,64 @@ void Hand::renderRaveGloveStage() {
}
}
void Hand::renderHandSpheres() {
void Hand::renderLeapHands() {
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& hand = getPalms()[i];
if (hand.isActive()) {
renderLeapHand(hand);
}
}
}
void Hand::renderLeapHand(PalmData& hand) {
glPushMatrix();
const float palmThickness = 0.002f;
glColor4f(0.5f, 0.5f, 0.5f, 1.0);
glm::vec3 tip = hand.getPosition();
glm::vec3 root = hand.getPosition() + hand.getNormal() * palmThickness;
Avatar::renderJointConnectingCone(root, tip, 0.05, 0.03);
for (size_t f = 0; f < hand.getNumFingers(); ++f) {
FingerData& finger = hand.getFingers()[f];
if (finger.isActive()) {
glColor4f(_ballColor.r, _ballColor.g, _ballColor.b, 0.5);
glm::vec3 tip = finger.getTipPosition();
glm::vec3 root = finger.getRootPosition();
Avatar::renderJointConnectingCone(root, tip, 0.001, 0.003);
}
}
glPopMatrix();
}
void Hand::renderLeapHandSpheres() {
glPushMatrix();
// Draw the leap balls
for (size_t i = 0; i < _leapBalls.size(); i++) {
for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) {
float alpha = 1.0f;
if (alpha > 0.0f) {
glColor4f(_ballColor.r, _ballColor.g, _ballColor.b, alpha);
glPushMatrix();
glTranslatef(_leapBalls[i].position.x, _leapBalls[i].position.y, _leapBalls[i].position.z);
glutSolidSphere(_leapBalls[i].radius, 20.0f, 20.0f);
glTranslatef(_leapFingerTipBalls[i].position.x, _leapFingerTipBalls[i].position.y, _leapFingerTipBalls[i].position.z);
glutSolidSphere(_leapFingerTipBalls[i].radius, 20.0f, 20.0f);
glPopMatrix();
}
}
for (size_t i = 0; i < _leapFingerRootBalls.size(); i++) {
float alpha = 1.0f;
if (alpha > 0.0f) {
glColor4f(0.3f, 0.4f, 0.6f, alpha);
glPushMatrix();
glTranslatef(_leapFingerRootBalls[i].position.x, _leapFingerRootBalls[i].position.y, _leapFingerRootBalls[i].position.z);
glutSolidSphere(_leapFingerRootBalls[i].radius, 20.0f, 20.0f);
glPopMatrix();
}
}
@ -200,7 +270,7 @@ void Hand::renderHandSpheres() {
glPopMatrix();
}
void Hand::renderFingerTrails() {
void Hand::renderLeapFingerTrails() {
// Draw the finger root cones
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
@ -229,6 +299,7 @@ void Hand::renderFingerTrails() {
}
}
void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals) {
for (size_t i = 0; i < getNumPalms(); ++i) {
@ -244,69 +315,28 @@ void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
}
}
// call this right after the geometry of the leap hands are set
// call this soon after the geometry of the leap hands are set
void Hand::updateRaveGloveEmitters() {
bool debug = false;
for (size_t i = 0; i < NUM_FINGERS; i++) {
_raveGloveParticleSystem.setEmitterActive(_raveGloveEmitter[i], false);
}
if (_raveGloveInitialized) {
if(debug) printf( "\n" );
if(debug) printf( "------------------------------------\n" );
if(debug) printf( "updating rave glove emitters:\n" );
if(debug) printf( "------------------------------------\n" );
int emitterIndex = 0;
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) {
if (i < NUM_FINGERS) {
glm::vec3 fingerDirection = _leapFingerTipBalls[i].position - _leapFingerRootBalls[i].position;
float fingerLength = glm::length(fingerDirection);
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 ((emitterIndex >=0)
&& (emitterIndex < NUM_FINGERS)) {
assert(emitterIndex >=0 );
assert(emitterIndex < NUM_FINGERS );
if(debug) printf( "_raveGloveEmitter[%d] = %d\n", emitterIndex, _raveGloveEmitter[emitterIndex] );
glm::vec3 fingerDirection = finger.getTipPosition() - finger.getRootPosition();
float fingerLength = glm::length(fingerDirection);
if (fingerLength > 0.0f) {
fingerDirection /= fingerLength;
} else {
fingerDirection = IDENTITY_UP;
}
assert(_raveGloveEmitter[emitterIndex] >=0 );
assert(_raveGloveEmitter[emitterIndex] < NUM_FINGERS );
_raveGloveParticleSystem.setEmitterPosition (_raveGloveEmitter[emitterIndex], finger.getTipPosition());
_raveGloveParticleSystem.setEmitterDirection(_raveGloveEmitter[emitterIndex], fingerDirection);
}
} else {
if(debug) printf( "BOGUS finger\n" );
}
emitterIndex ++;
}
if (fingerLength > 0.0f) {
fingerDirection /= fingerLength;
} else {
if(debug) printf( "is NOT active\n" );
fingerDirection = IDENTITY_UP;
}
_raveGloveParticleSystem.setEmitterActive (_raveGloveEmitter[i], true);
_raveGloveParticleSystem.setEmitterPosition (_raveGloveEmitter[i], _leapFingerTipBalls[i].position);
_raveGloveParticleSystem.setEmitterDirection(_raveGloveEmitter[i], fingerDirection);
}
}
}
@ -317,16 +347,11 @@ 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();
_raveGloveEmitter[f] = _raveGloveParticleSystem.addEmitter();
assert( _raveGloveEmitter[f] >= 0 );
assert( _raveGloveEmitter[f] != NULL_EMITTER );
//printf( "%d\n", _raveGloveEmitter[f] );
}
setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE);
@ -339,13 +364,13 @@ void Hand::updateRaveGloveParticles(float 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 red = 0.5f + 0.5f * sinf(_raveGloveClock * 2.4f);
float green = 0.5f + 0.5f * cosf(_raveGloveClock * 2.7f);
float blue = 0.5f + 0.5f * sinf(_raveGloveClock * 3.0f);
float alpha = 1.0f;
attributes.color = glm::vec4(red, green, blue, alpha);
attributes.radius = 0.01f + 0.005f * sinf(_raveGloveClock * 2.2f);
attributes.radius = 0.01f + 0.003f * sinf(_raveGloveClock * 50.0f);
attributes.modulationAmplitude = 0.0f;
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
@ -360,6 +385,8 @@ void Hand::updateRaveGloveParticles(float deltaTime) {
}
}
void Hand::setRaveGloveMode(int mode) {
_raveGloveMode = mode;
@ -376,7 +403,7 @@ void Hand::setRaveGloveMode(int mode) {
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.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.03f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0f );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
@ -650,7 +677,7 @@ void Hand::setRaveGloveMode(int mode) {
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// throb
// long sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER) {
@ -672,6 +699,30 @@ void Hand::setRaveGloveMode(int mode) {
_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_THROB) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.03 );
_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.5;
attributes.modulationRate = 3.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

@ -65,8 +65,9 @@ public:
void setRaveGloveEffectsMode(QKeyEvent* event);
// getters
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
bool isRaveGloveActive () const { return _isRaveGloveActive; }
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
bool isRaveGloveActive() const { return _isRaveGloveActive; }
private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
@ -84,7 +85,8 @@ private:
float _renderAlpha;
bool _lookingInMirror;
glm::vec3 _ballColor;
std::vector<HandBall> _leapBalls;
std::vector<HandBall> _leapFingerTipBalls;
std::vector<HandBall> _leapFingerRootBalls;
// private methods
void setLeapHands(const std::vector<glm::vec3>& handPositions,
@ -92,8 +94,10 @@ private:
void renderRaveGloveStage();
void setRaveGloveMode(int mode);
void renderHandSpheres();
void renderFingerTrails();
void renderLeapHandSpheres();
void renderLeapHands();
void renderLeapHand(PalmData& hand);
void renderLeapFingerTrails();
void calculateGeometry();
};

17
libraries/avatars/src/AvatarData.cpp Executable file → Normal file
View file

@ -130,6 +130,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
// leap hand data
std::vector<glm::vec3> fingerVectors;
//printf("about to call _handData->encodeRemoteData(fingerVectors);\n");
_handData->encodeRemoteData(fingerVectors);
if (fingerVectors.size() > 255)
@ -244,17 +246,32 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
// hand state, stored as a semi-nibble in the bitItems
_handState = getSemiNibbleAt(bitItems,HAND_STATE_START_BIT);
//printf("about to call leap hand data code in AvatarData::parseData...\n");
// leap hand data
if (sourceBuffer - startPosition < numBytes) {
//printf("got inside of 'if (sourceBuffer - startPosition < numBytes)'\n");
// check passed, bytes match
unsigned int numFingerVectors = *sourceBuffer++;
//printf("numFingerVectors = %d\n", numFingerVectors);
if (numFingerVectors > 0) {
//printf("ok, we got fingers in AvatarData::parseData\n");
std::vector<glm::vec3> fingerVectors(numFingerVectors);
for (size_t i = 0; i < numFingerVectors; ++i) {
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].x), fingerVectorRadix);
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].y), fingerVectorRadix);
sourceBuffer += unpackFloatScalarFromSignedTwoByteFixed((int16_t*) sourceBuffer, &(fingerVectors[i].z), fingerVectorRadix);
}
//printf("about to call _handData->decodeRemoteData(fingerVectors);\n");
_handData->decodeRemoteData(fingerVectors);
}
}

3
libraries/avatars/src/HandData.cpp Executable file → Normal file
View file

@ -51,7 +51,9 @@ _owningHandData(owningHandData)
void HandData::encodeRemoteData(std::vector<glm::vec3>& fingerVectors) {
fingerVectors.clear();
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (!palm.isActive()) {
continue;
@ -60,6 +62,7 @@ void HandData::encodeRemoteData(std::vector<glm::vec3>& fingerVectors) {
fingerVectors.push_back(palm.getRawNormal());
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
fingerVectors.push_back(finger.getTipRawPosition());
fingerVectors.push_back(finger.getRootRawPosition());

View file

@ -24,10 +24,9 @@
#include <arpa/inet.h>
#endif
const char SOLO_NODE_TYPES[3] = {
const char SOLO_NODE_TYPES[2] = {
NODE_TYPE_AVATAR_MIXER,
NODE_TYPE_AUDIO_MIXER,
NODE_TYPE_VOXEL_SERVER
NODE_TYPE_AUDIO_MIXER
};
const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES] = "root.highfidelity.io";

View file

@ -32,7 +32,7 @@ const unsigned int NODE_SOCKET_LISTEN_PORT = 40103;
const int NODE_SILENCE_THRESHOLD_USECS = 2 * 1000000;
const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000;
extern const char SOLO_NODE_TYPES[3];
extern const char SOLO_NODE_TYPES[2];
const int MAX_HOSTNAME_BYTES = 256;

View file

@ -257,3 +257,42 @@ unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char*
return newCode;
}
bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild) {
if (!possibleAncestor || !possibleDescendent) {
return false;
}
int ancestorCodeLength = numberOfThreeBitSectionsInCode(possibleAncestor);
if (ancestorCodeLength == 0) {
return true; // this is the root, it's the anscestor of all
}
int descendentCodeLength = numberOfThreeBitSectionsInCode(possibleDescendent);
// if the caller also include a child, then our descendent length is actually one extra!
if (descendentsChild != CHECK_NODE_ONLY) {
descendentCodeLength++;
}
if (ancestorCodeLength > descendentCodeLength) {
return false; // if the descendent is shorter, it can't be a descendent
}
// compare the sections for the ancestor to the descendent
for (int section = 0; section < ancestorCodeLength; section++) {
char sectionValueAncestor = getOctalCodeSectionValue(possibleAncestor, section);
char sectionValueDescendent;
if (ancestorCodeLength <= descendentCodeLength) {
sectionValueDescendent = getOctalCodeSectionValue(possibleDescendent, section);
} else {
assert(descendentsChild != CHECK_NODE_ONLY);
sectionValueDescendent = descendentsChild;
}
if (sectionValueAncestor != sectionValueDescendent) {
return false; // first non-match, means they don't match
}
}
// they all match, so we are an ancestor
return true;
}

View file

@ -21,7 +21,6 @@ const int BLUE_INDEX = 2;
void printOctalCode(unsigned char * octalCode);
int bytesRequiredForCodeLength(unsigned char threeBitCodes);
bool isDirectParentOfChild(unsigned char *parentOctalCode, unsigned char * childOctalCode);
int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode);
unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber);
int numberOfThreeBitSectionsInCode(unsigned char * octalCode);
@ -29,6 +28,9 @@ unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels);
unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode,
bool includeColorSpace = false);
const int CHECK_NODE_ONLY = -1;
bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild = CHECK_NODE_ONLY);
// Note: copyFirstVertexForCode() is preferred because it doesn't allocate memory for the return
// but other than that these do the same thing.
float * firstVertexForCode(unsigned char * octalCode);

View file

@ -0,0 +1,178 @@
//
// JurisdictionMap.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 8/1/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QSettings>
#include <QString>
#include <QStringList>
#include "JurisdictionMap.h"
#include "VoxelNode.h"
JurisdictionMap::~JurisdictionMap() {
clear();
}
void JurisdictionMap::clear() {
if (_rootOctalCode) {
delete[] _rootOctalCode;
_rootOctalCode = NULL;
}
for (int i = 0; i < _endNodes.size(); i++) {
if (_endNodes[i]) {
delete[] _endNodes[i];
}
}
_endNodes.clear();
}
JurisdictionMap::JurisdictionMap() : _rootOctalCode(NULL) {
unsigned char* rootCode = new unsigned char[1];
*rootCode = 0;
std::vector<unsigned char*> emptyEndNodes;
init(rootCode, emptyEndNodes);
}
JurisdictionMap::JurisdictionMap(const char* filename) : _rootOctalCode(NULL) {
clear(); // clean up our own memory
readFromFile(filename);
}
JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes)
: _rootOctalCode(NULL) {
init(rootOctalCode, endNodes);
}
void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes) {
clear(); // clean up our own memory
_rootOctalCode = rootOctalCode;
_endNodes = endNodes;
}
JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const {
// to be in our jurisdiction, we must be under the root...
// if the node is an ancestor of my root, then we return ABOVE
if (isAncestorOf(nodeOctalCode, _rootOctalCode)) {
return ABOVE;
}
// otherwise...
bool isInJurisdiction = isAncestorOf(_rootOctalCode, nodeOctalCode, childIndex);
//printf("isInJurisdiction=%s rootOctalCode=",debug::valueOf(isInJurisdiction));
//printOctalCode(_rootOctalCode);
//printf("nodeOctalCode=");
//printOctalCode(nodeOctalCode);
// if we're under the root, then we can't be under any of the endpoints
if (isInJurisdiction) {
for (int i = 0; i < _endNodes.size(); i++) {
bool isUnderEndNode = isAncestorOf(_endNodes[i], nodeOctalCode);
if (isUnderEndNode) {
isInJurisdiction = false;
break;
}
}
}
return isInJurisdiction ? WITHIN : BELOW;
}
bool JurisdictionMap::readFromFile(const char* filename) {
QString settingsFile(filename);
QSettings settings(settingsFile, QSettings::IniFormat);
QString rootCode = settings.value("root","00").toString();
qDebug() << "rootCode=" << rootCode << "\n";
_rootOctalCode = hexStringToOctalCode(rootCode);
printOctalCode(_rootOctalCode);
settings.beginGroup("endNodes");
const QStringList childKeys = settings.childKeys();
QHash<QString, QString> values;
foreach (const QString &childKey, childKeys) {
QString childValue = settings.value(childKey).toString();
values.insert(childKey, childValue);
qDebug() << childKey << "=" << childValue << "\n";
unsigned char* octcode = hexStringToOctalCode(childValue);
printOctalCode(octcode);
_endNodes.push_back(octcode);
}
settings.endGroup();
return true;
}
bool JurisdictionMap::writeToFile(const char* filename) {
QString settingsFile(filename);
QSettings settings(settingsFile, QSettings::IniFormat);
QString rootNodeValue = octalCodeToHexString(_rootOctalCode);
settings.setValue("root", rootNodeValue);
settings.beginGroup("endNodes");
for (int i = 0; i < _endNodes.size(); i++) {
QString key = QString("endnode%1").arg(i);
QString value = octalCodeToHexString(_endNodes[i]);
settings.setValue(key, value);
}
settings.endGroup();
return true;
}
unsigned char* JurisdictionMap::hexStringToOctalCode(const QString& input) const {
const int HEX_NUMBER_BASE = 16;
const int HEX_BYTE_SIZE = 2;
int stringIndex = 0;
int byteArrayIndex = 0;
// allocate byte array based on half of string length
unsigned char* bytes = new unsigned char[(input.length()) / HEX_BYTE_SIZE];
// loop through the string - 2 bytes at a time converting
// it to decimal equivalent and store in byte array
bool ok;
while (stringIndex < input.length()) {
uint value = input.mid(stringIndex, HEX_BYTE_SIZE).toUInt(&ok, HEX_NUMBER_BASE);
if (!ok) {
break;
}
bytes[byteArrayIndex] = (unsigned char)value;
stringIndex += HEX_BYTE_SIZE;
byteArrayIndex++;
}
// something went wrong
if (!ok) {
delete[] bytes;
return NULL;
}
return bytes;
}
QString JurisdictionMap::octalCodeToHexString(unsigned char* octalCode) const {
const int HEX_NUMBER_BASE = 16;
const int HEX_BYTE_SIZE = 2;
QString output;
if (!octalCode) {
output = "00";
} else {
for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) {
output.append(QString("%1").arg(octalCode[i], HEX_BYTE_SIZE, HEX_NUMBER_BASE, QChar('0')).toUpper());
}
}
return output;
}

View file

@ -0,0 +1,46 @@
//
// JurisdictionMap.h
// hifi
//
// Created by Brad Hefta-Gaub on 8/1/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__JurisdictionMap__
#define __hifi__JurisdictionMap__
#include <vector>
#include <QString>
class JurisdictionMap {
public:
enum Area {
ABOVE,
WITHIN,
BELOW
};
JurisdictionMap();
JurisdictionMap(const char* filename);
JurisdictionMap(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
~JurisdictionMap();
Area isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const;
bool writeToFile(const char* filename);
bool readFromFile(const char* filename);
private:
void clear();
void init(unsigned char* rootOctalCode, const std::vector<unsigned char*>& endNodes);
unsigned char* hexStringToOctalCode(const QString& input) const;
QString octalCodeToHexString(unsigned char* octalCode) const;
unsigned char* _rootOctalCode;
std::vector<unsigned char*> _endNodes;
};
#endif /* defined(__hifi__JurisdictionMap__) */

View file

@ -1,5 +1,5 @@
//
// Tags.h
// Tags.cpp
// hifi
//
// Created by Clement Brisset on 7/3/13.

View file

@ -12,6 +12,8 @@
#include <QDebug>
#include <NodeList.h>
#include "AABox.h"
#include "OctalCode.h"
#include "SharedUtil.h"
@ -51,6 +53,7 @@ void VoxelNode::init(unsigned char * octalCode) {
_voxelSystem = NULL;
_isDirty = true;
_shouldRender = false;
_sourceID = UNKNOWN_NODE_ID;
markWithChangedTime();
calculateAABox();
}

View file

@ -93,8 +93,6 @@ public:
void setColor(const nodeColor& color);
const nodeColor& getTrueColor() const { return _trueColor; };
const nodeColor& getColor() const { return _currentColor; };
void setDensity(float density) { _density = density; };
float getDensity() const { return _density; };
#else
void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ };
void setFalseColored(bool isFalseColored) { /* no op */ };
@ -105,6 +103,11 @@ public:
const nodeColor& getColor() const { return _trueColor; };
#endif
void setDensity(float density) { _density = density; };
float getDensity() const { return _density; };
void setSourceID(uint16_t sourceID) { _sourceID = sourceID; };
uint16_t getSourceID() const { return _sourceID; };
static void addDeleteHook(VoxelNodeDeleteHook* hook);
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
@ -135,6 +138,7 @@ private:
unsigned long _subtreeNodeCount;
unsigned long _subtreeLeafNodeCount;
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
uint16_t _sourceID;
static std::vector<VoxelNodeDeleteHook*> _hooks;
};

View file

@ -231,7 +231,7 @@ VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char
}
int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, int bytesLeftToRead,
bool includeColor, bool includeExistsBits) {
ReadBitstreamToTreeParams& args) {
// give this destination node the child mask from the packet
const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF;
unsigned char colorInPacketMask = *nodeData;
@ -254,12 +254,13 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
// pull the color for this child
nodeColor newColor = { 128, 128, 128, 1};
if (includeColor) {
if (args.includeColor) {
memcpy(newColor, nodeData + bytesRead, 3);
bytesRead += 3;
}
bool nodeWasDirty = destinationNode->getChildAtIndex(i)->isDirty();
destinationNode->getChildAtIndex(i)->setColor(newColor);
destinationNode->getChildAtIndex(i)->setSourceID(args.sourceID);
bool nodeIsDirty = destinationNode->getChildAtIndex(i)->isDirty();
if (nodeIsDirty) {
_isDirty = true;
@ -273,11 +274,11 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
}
// give this destination node the child mask from the packet
unsigned char childrenInTreeMask = includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST;
unsigned char childMask = *(nodeData + bytesRead + (includeExistsBits ? sizeof(childrenInTreeMask) : 0));
unsigned char childrenInTreeMask = args.includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST;
unsigned char childMask = *(nodeData + bytesRead + (args.includeExistsBits ? sizeof(childrenInTreeMask) : 0));
int childIndex = 0;
bytesRead += includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask);
bytesRead += args.includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask);
while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
// check the exists mask to see if we have a child to traverse into
@ -300,13 +301,12 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
// tell the child to read the subsequent data
bytesRead += readNodeData(destinationNode->getChildAtIndex(childIndex),
nodeData + bytesRead, bytesLeftToRead - bytesRead, includeColor, includeExistsBits);
nodeData + bytesRead, bytesLeftToRead - bytesRead, args);
}
childIndex++;
}
if (includeExistsBits) {
if (args.includeExistsBits) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
// 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.
@ -319,14 +319,14 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
return bytesRead;
}
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
bool includeColor, bool includeExistsBits, VoxelNode* destinationNode) {
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes,
ReadBitstreamToTreeParams& args) {
int bytesRead = 0;
unsigned char* bitstreamAt = bitstream;
// If destination node is not included, set it to root
if (!destinationNode) {
destinationNode = rootNode;
if (!args.destinationNode) {
args.destinationNode = rootNode;
}
_nodesChangedFromBitstream = 0;
@ -336,14 +336,14 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
// if there are more bytes after that, it's assumed to be another root relative tree
while (bitstreamAt < bitstream + bufferSizeBytes) {
VoxelNode* bitstreamRootNode = nodeForOctalCode(destinationNode, (unsigned char *)bitstreamAt, NULL);
VoxelNode* bitstreamRootNode = nodeForOctalCode(args.destinationNode, (unsigned char *)bitstreamAt, NULL);
if (*bitstreamAt != *bitstreamRootNode->getOctalCode()) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
// Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial
// octal code is always relative to root!
bitstreamRootNode = createMissingNode(destinationNode, (unsigned char*) bitstreamAt);
bitstreamRootNode = createMissingNode(args.destinationNode, (unsigned char*) bitstreamAt);
if (bitstreamRootNode->isDirty()) {
_isDirty = true;
_nodesChangedFromBitstream++;
@ -353,8 +353,8 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
int theseBytesRead = 0;
theseBytesRead += octalCodeBytes;
theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes,
bufferSizeBytes - (bytesRead + octalCodeBytes), includeColor, includeExistsBits);
theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes,
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
@ -1078,6 +1078,15 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
if (currentEncodeLevel >= params.maxEncodeLevel) {
return bytesAtThisLevel;
}
// If we've been provided a jurisdiction map, then we need to honor it.
if (params.jurisdictionMap) {
// here's how it works... if we're currently above our root jurisdiction, then we proceed normally.
// but once we're in our own jurisdiction, then we need to make sure we're not below it.
if (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(node->getOctalCode(), CHECK_NODE_ONLY)) {
return bytesAtThisLevel;
}
}
// caller can pass NULL as viewFrustum if they want everything
if (params.viewFrustum) {
@ -1197,9 +1206,18 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
// if the caller wants to include childExistsBits, then include them even if not in view
if (params.includeExistsBits && childNode) {
childrenExistInTreeBits += (1 << (7 - i));
// if the caller wants to include childExistsBits, then include them even if not in view, if however,
// we're in a portion of the tree that's not our responsibility, then we assume the child nodes exist
// even if they don't in our local tree
bool notMyJurisdiction = false;
if (params.jurisdictionMap) {
notMyJurisdiction = (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(node->getOctalCode(), i));
}
if (params.includeExistsBits) {
// If the child is known to exist, OR, it's not my jurisdiction, then we mark the bit as existing
if (childNode || notMyJurisdiction) {
childrenExistInTreeBits += (1 << (7 - i));
}
}
if (params.wantOcclusionCulling) {
@ -1548,7 +1566,8 @@ bool VoxelTree::readFromSVOFile(const char* fileName) {
// read the entire file into a buffer, WHAT!? Why not.
unsigned char* entireFile = new unsigned char[fileLength];
file.read((char*)entireFile, fileLength);
readBitstreamToTree(entireFile, fileLength, WANT_COLOR, NO_EXISTS_BITS);
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
readBitstreamToTree(entireFile, fileLength, args);
delete[] entireFile;
file.close();
@ -1702,7 +1721,8 @@ void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinat
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
// ask destination tree to read the bitstream
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS);
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
destinationTree->readBitstreamToTree(&outputBuffer[0], bytesWritten, args);
}
}
@ -1722,7 +1742,8 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin
bytesWritten = sourceTree->encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
// ask destination tree to read the bitstream
readBitstreamToTree(&outputBuffer[0], bytesWritten, WANT_COLOR, NO_EXISTS_BITS, destinationNode);
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode);
readBitstreamToTree(&outputBuffer[0], bytesWritten, args);
}
}
@ -1877,4 +1898,4 @@ void VoxelTree::computeBlockColor(int id, int data, int& red, int& green, int& b
create = 0;
break;
}
}
}

View file

@ -13,6 +13,7 @@
#include <SimpleMovingAverage.h>
#include "CoverageMap.h"
#include "JurisdictionMap.h"
#include "ViewFrustum.h"
#include "VoxelNode.h"
#include "VoxelNodeBag.h"
@ -36,9 +37,10 @@ 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
#define IGNORE_SCENE_STATS NULL
#define IGNORE_VIEW_FRUSTUM NULL
#define IGNORE_COVERAGE_MAP NULL
#define IGNORE_JURISDICTION_MAP NULL
class EncodeBitstreamParams {
public:
@ -56,6 +58,7 @@ public:
bool forceSendScene;
VoxelSceneStats* stats;
CoverageMap* map;
JurisdictionMap* jurisdictionMap;
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
@ -70,7 +73,8 @@ public:
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
uint64_t lastViewFrustumSent = IGNORE_LAST_SENT,
bool forceSendScene = true,
VoxelSceneStats* stats = IGNORE_SCENE_STATS) :
VoxelSceneStats* stats = IGNORE_SCENE_STATS,
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) :
maxEncodeLevel (maxEncodeLevel),
maxLevelReached (0),
viewFrustum (viewFrustum),
@ -84,7 +88,27 @@ public:
lastViewFrustumSent (lastViewFrustumSent),
forceSendScene (forceSendScene),
stats (stats),
map (map)
map (map),
jurisdictionMap (jurisdictionMap)
{}
};
class ReadBitstreamToTreeParams {
public:
bool includeColor;
bool includeExistsBits;
VoxelNode* destinationNode;
uint16_t sourceID;
ReadBitstreamToTreeParams(
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
VoxelNode* destinationNode = NULL,
uint16_t sourceID = UNKNOWN_NODE_ID) :
includeColor (includeColor),
includeExistsBits (includeExistsBits),
destinationNode (destinationNode),
sourceID (sourceID)
{}
};
@ -109,9 +133,7 @@ public:
void eraseAllVoxels();
void processRemoveVoxelBitstream(unsigned char* bitstream, int bufferSizeBytes);
void readBitstreamToTree(unsigned char* bitstream, unsigned long int bufferSizeBytes,
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
VoxelNode* destinationNode = NULL);
void readBitstreamToTree(unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args);
void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false);
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void printTreeForDebugging(VoxelNode* startNode);
@ -169,7 +191,8 @@ public:
void recurseTreeWithOperationDistanceSortedTimed(PointerStack* stackOfNodes, long allowedTime,
RecurseVoxelTreeOperation operation,
const glm::vec3& point, void* extraData);
private:
void deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData);
void readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraData);
@ -181,8 +204,7 @@ private:
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const;
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes,
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS);
int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args);
bool _isDirty;
unsigned long int _nodesChangedFromBitstream;

80
voxel-server/src/README Normal file
View file

@ -0,0 +1,80 @@
NAME
voxel-server - the High Fidelity Voxel Server
SYNOPSIS
voxel-server [--local] [--jurisdictionFile <filename>] [--port <port>] [--voxelsPersistFilename <filename>]
[--displayVoxelStats] [--debugVoxelSending] [--debugVoxelReceiving] [--shouldShowAnimationDebug]
[--wantColorRandomizer] [--NoVoxelPersist] [--packetsPerSecond <value>]
[--AddRandomVoxels] [--AddScene] [--NoAddScene]
DESCRIPTION
voxel-server is a compact, portable, scalable, distributed sparse voxel octree server
OPTIONS
--local
This will run the voxel server in "local domain mode" and will look for a domain-server running on the same IP
address as the voxel server
--jurisdictionFile [filename]
Tells the server to load it's jurisdiction from the specified file. When a voxel server is running with a limited
"jurisdiction" it will only server voxels from that portion of the voxel tree. The jurisdiction file is a ".ini" style
file with the following options: [General/root] specifies the octal code for the node in the tree that this server will
use as it's "root". It will only server voxels under this root. [endNodes/...] a list or group of additional octalcodes
under the root, which will not be served.
The following example jurisdiction file will server all voxels from the root and below, and exclude voxels from the
voxel of scale 0.25 and 0,0,0.
****** example jurisdiction.ini **********************************************************************
[General]
root=00
[endNodes]
endnode0=0200
******************************************************************************************************
--port [port]
Specify the port the voxel-server will listen on. You must specify different ports to enable multiple voxel servers
running on a single machine.
--voxelsPersistFilename [filename]
Specify and alternate file that the voxel server will read and write persistant voxels to. By default the voxel server
will use one of the following files:
default: /etc/highfidelity/voxel-server/resources/voxels.svo
in local mode: ./resources/voxels.svo
--displayVoxelStats
Displays additional voxel stats debugging
--debugVoxelSending
Displays additional voxel sending debugging
--debugVoxelReceiving
Displays additional voxel receiving debugging
--shouldShowAnimationDebug
Displays additional verbose animation debugging
--wantColorRandomizer
Adds color randomization to inserts into the local voxel tree
--NoVoxelPersist
Disables voxel persisting
--packetsPerSecond [value]
Specifies the packets per second that this voxel server will send to attached clients
--AddRandomVoxels
Add random voxels to the surface on startup
--AddScene
OBSOLETE: Adds an arbitrary scene with several "planet" spheres
--NoAddScene
OBSOLETE: disables adding of scene

View file

@ -32,6 +32,8 @@
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
const int MAX_FILENAME_LENGTH = 1024;
char voxelPersistFilename[MAX_FILENAME_LENGTH];
const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
const int VOXEL_LISTEN_PORT = 40106;
@ -65,6 +67,8 @@ bool debugVoxelReceiving = false;
EnvironmentData environmentData[3];
int receivedPacketCount = 0;
JurisdictionMap* jurisdiction = NULL;
void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
// randomly generate children for this node
@ -292,7 +296,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats);
isFullScene, &nodeData->stats, ::jurisdiction);
nodeData->stats.encodeStarted();
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
@ -376,7 +380,7 @@ void persistVoxelsWhenDirty() {
"persistVoxelsWhenDirty() - writeToSVOFile()", ::shouldShowAnimationDebug);
printf("saving voxels to file...\n");
serverTree.writeToSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
serverTree.writeToSVOFile(::voxelPersistFilename);
serverTree.clearDirtyBit(); // tree is clean after saving
printf("DONE saving voxels to file...\n");
}
@ -427,14 +431,39 @@ void attachVoxelNodeDataToNode(Node* newNode) {
}
}
int receivedPacketCount = 0;
int main(int argc, const char * argv[]) {
pthread_mutex_init(&::treeLock, NULL);
qInstallMessageHandler(sharedMessageHandler);
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, VOXEL_LISTEN_PORT);
int listenPort = VOXEL_LISTEN_PORT;
// Check to see if the user passed in a command line option for setting listen port
const char* PORT_PARAMETER = "--port";
const char* portParameter = getCmdOption(argc, argv, PORT_PARAMETER);
if (portParameter) {
listenPort = atoi(portParameter);
if (listenPort < 1) {
listenPort = VOXEL_LISTEN_PORT;
}
printf("portParameter=%s listenPort=%d\n", portParameter, listenPort);
}
const char* JURISDICTION_FILE = "--jurisdictionFile";
const char* jurisdictionFile = getCmdOption(argc, argv, JURISDICTION_FILE);
if (jurisdictionFile) {
printf("jurisdictionFile=%s\n", jurisdictionFile);
printf("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
jurisdiction = new JurisdictionMap(jurisdictionFile);
printf("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
// test writing the file...
printf("about to writeToFile().... jurisdictionFile=%s\n", jurisdictionFile);
jurisdiction->writeToFile(jurisdictionFile);
printf("after writeToFile().... jurisdictionFile=%s\n", jurisdictionFile);
}
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort);
setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line
@ -480,8 +509,19 @@ int main(int argc, const char * argv[]) {
// if we want Voxel Persistance, load the local file now...
bool persistantFileRead = false;
if (::wantVoxelPersist) {
printf("loading voxels from file...\n");
persistantFileRead = ::serverTree.readFromSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
// Check to see if the user passed in a command line option for setting packet send rate
const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename";
const char* voxelsPersistFilenameParameter = getCmdOption(argc, argv, VOXELS_PERSIST_FILENAME);
if (voxelsPersistFilenameParameter) {
strcpy(voxelPersistFilename, voxelsPersistFilenameParameter);
} else {
strcpy(voxelPersistFilename, ::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
}
printf("loading voxels from file: %s...\n", voxelPersistFilename);
persistantFileRead = ::serverTree.readFromSVOFile(::voxelPersistFilename);
if (persistantFileRead) {
PerformanceWarning warn(::shouldShowAnimationDebug,
"persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug);
@ -707,6 +747,10 @@ int main(int argc, const char * argv[]) {
pthread_join(sendVoxelThread, NULL);
pthread_mutex_destroy(&::treeLock);
if (jurisdiction) {
delete jurisdiction;
}
return 0;
}