resolve conflicts on merge with upstream master

This commit is contained in:
Stephen Birarda 2013-08-05 09:56:07 -07:00
commit a4ea0f035a
46 changed files with 2326 additions and 805 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

@ -17,9 +17,6 @@ uniform vec2 texCoordRight;
// the texture coordinate vector from bottom to the top
uniform vec2 texCoordUp;
// the aspect ratio of the image
uniform float aspectRatio;
// the depth texture
uniform sampler2D depthTexture;
@ -31,6 +28,5 @@ void main(void) {
const float MIN_VISIBLE_DEPTH = 1.0 / 255.0;
const float MAX_VISIBLE_DEPTH = 254.0 / 255.0;
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(MIN_VISIBLE_DEPTH, depth) * (1.0 - step(MAX_VISIBLE_DEPTH, depth)));
gl_Position = gl_ModelViewProjectionMatrix * vec4(0.5 - gl_Vertex.x,
(gl_Vertex.y - 0.5) / aspectRatio, depth * 2.0 - 2.0, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * vec4(0.5 - gl_Vertex.x, gl_Vertex.y - 0.5, depth - 0.5, 1.0);
}

View file

@ -196,8 +196,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_isTouchPressed(false),
_yawFromTouch(0.0f),
_pitchFromTouch(0.0f),
_groundPlaneImpact(0.0f),
_mousePressed(false),
_isHoverVoxel(false),
_isHoverVoxelSounding(false),
_mouseVoxelScale(1.0f / 1024.0f),
_justEditedVoxel(false),
_isLookingAtOtherAvatar(false),
@ -569,8 +570,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (_simulateLeapHand->isChecked() || _testRaveGlove->isChecked()) {
_myAvatar.getHand().setRaveGloveEffectsMode((QKeyEvent*)event);
}
bool shifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::MetaModifier);
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
switch (event->key()) {
case Qt::Key_BracketLeft:
_viewFrustumOffsetYaw -= 0.5;
@ -643,7 +645,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_C:
_myAvatar.setDriveKeys(DOWN, 1);
if (isShifted) {
_occlusionCulling->trigger();
} else if (isMeta) {
chooseVoxelPaintColor();
} else {
_myAvatar.setDriveKeys(DOWN, 1);
}
break;
case Qt::Key_W:
@ -651,7 +659,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_S:
_myAvatar.setDriveKeys(BACK, 1);
if (isShifted) {
doTreeStats();
} else {
_myAvatar.setDriveKeys(BACK, 1);
}
break;
case Qt::Key_Space:
@ -660,11 +672,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_G:
goHome();
if (isShifted) {
_gravityUse->trigger();
} else {
_eyedropperMode->trigger();
}
break;
case Qt::Key_A:
_myAvatar.setDriveKeys(ROT_LEFT, 1);
if (isShifted) {
_renderAtmosphereOn->trigger();
} else {
_myAvatar.setDriveKeys(ROT_LEFT, 1);
}
break;
case Qt::Key_D:
@ -680,23 +700,23 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_Up:
_myAvatar.setDriveKeys(shifted ? UP : FWD, 1);
_myAvatar.setDriveKeys(isShifted ? UP : FWD, 1);
break;
case Qt::Key_Down:
_myAvatar.setDriveKeys(shifted ? DOWN : BACK, 1);
_myAvatar.setDriveKeys(isShifted ? DOWN : BACK, 1);
break;
case Qt::Key_Left:
_myAvatar.setDriveKeys(shifted ? LEFT : ROT_LEFT, 1);
_myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1);
break;
case Qt::Key_Right:
_myAvatar.setDriveKeys(shifted ? RIGHT : ROT_RIGHT, 1);
_myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1);
break;
case Qt::Key_I:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(0.002f, 0, 0)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -706,7 +726,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_K:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(-0.002f, 0, 0)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -716,7 +736,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_J:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(0, 0.002f, 0)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -726,7 +746,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_M:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(0, -0.002f, 0)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -736,7 +756,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_U:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(0, 0, -0.002f)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -746,7 +766,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_Y:
if (shifted) {
if (isShifted) {
_myCamera.setEyeOffsetOrientation(glm::normalize(
glm::quat(glm::vec3(0, 0, 0.002f)) * _myCamera.getEyeOffsetOrientation()));
} else {
@ -754,12 +774,62 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
resizeGL(_glWidget->width(), _glWidget->height());
break;
case Qt::Key_N:
_noise->trigger();
break;
case Qt::Key_H:
_lookingInMirror->trigger();
break;
case Qt::Key_F:
if (isShifted) {
_frustumOn->trigger();
} else {
_fullScreenMode->trigger();
}
break;
case Qt::Key_V:
if (isShifted) {
_renderVoxels->trigger();
} else {
_addVoxelMode->trigger();
}
break;
case Qt::Key_P:
_manualFirstPerson->trigger();
break;
case Qt::Key_R:
if (isShifted) {
_frustumRenderModeAction->trigger();
} else {
_deleteVoxelMode->trigger();
}
break;
case Qt::Key_B:
_colorVoxelMode->trigger();
break;
case Qt::Key_O:
if (isShifted) {
_viewFrustumFromOffset->trigger();
} else {
_selectVoxelMode->trigger();
}
break;
case Qt::Key_Slash:
_renderStatsOn->trigger();
break;
case Qt::Key_Backspace:
case Qt::Key_Delete:
if (_selectVoxelMode->isChecked()) {
deleteVoxelUnderCursor();
}
break;
case Qt::Key_Plus:
increaseAvatarSize();
break;
case Qt::Key_Minus:
decreaseAvatarSize();
break;
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
@ -851,9 +921,16 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
deleteVoxelUnderCursor();
}
}
_pieMenu.mouseMoveEvent(_mouseX, _mouseY);
}
}
const bool MAKE_SOUND_ON_VOXEL_HOVER = false;
const bool MAKE_SOUND_ON_VOXEL_CLICK = true;
const float HOVER_VOXEL_FREQUENCY = 7040.f;
const float HOVER_VOXEL_DECAY = 0.999f;
void Application::mousePressEvent(QMouseEvent* event) {
if (activeWindow() == _window) {
if (event->button() == Qt::LeftButton) {
@ -863,7 +940,20 @@ 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;
_hoverVoxelOriginalColor[2] = _hoverVoxel.blue;
_hoverVoxelOriginalColor[3] = 1;
_audio.startCollisionSound(1.0, HOVER_VOXEL_FREQUENCY * _hoverVoxel.s * TREE_SCALE, 0.0, HOVER_VOXEL_DECAY);
_isHoverVoxelSounding = true;
}
} else if (event->button() == Qt::RightButton && checkedVoxelModeAction() != 0) {
deleteVoxelUnderCursor();
@ -878,6 +968,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
_mouseY = event->y();
_mousePressed = false;
checkBandwidthMeterClick();
_pieMenu.mouseReleaseEvent(_mouseX, _mouseY);
}
}
}
@ -918,8 +1010,10 @@ void Application::touchEndEvent(QTouchEvent* event) {
_isTouchPressed = false;
}
const bool USE_MOUSEWHEEL = false;
void Application::wheelEvent(QWheelEvent* event) {
if (activeWindow() == _window) {
// Wheel Events disabled for now because they are also activated by touch look pitch up/down.
if (USE_MOUSEWHEEL && (activeWindow() == _window)) {
if (checkedVoxelModeAction() == 0) {
event->ignore();
return;
@ -965,14 +1059,15 @@ void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& d
int headerSize = packetPosition - packet;
// break the data up into submessages of the maximum size
// break the data up into submessages of the maximum size (at least one, for zero-length packets)
*offsetPosition = 0;
while (*offsetPosition < data.size()) {
do {
int payloadSize = min(data.size() - (int)*offsetPosition, MAX_PACKET_SIZE - headerSize);
memcpy(packetPosition, data.constData() + *offsetPosition, payloadSize);
getInstance()->controlledBroadcastToNodes(packet, headerSize + payloadSize, &NODE_TYPE_AVATAR_MIXER, 1);
*offsetPosition += payloadSize;
}
} while (*offsetPosition < data.size());
}
// Every second, check the frame rates and other stuff
@ -1366,6 +1461,10 @@ void Application::doFalseColorizeOccludedV2() {
_voxels.falseColorizeOccludedV2();
}
void Application::doFalseColorizeBySource() {
_voxels.falseColorizeBySource();
}
void Application::doTrueVoxelColors() {
_voxels.trueColorize();
}
@ -1495,16 +1594,18 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
uint64_t now = usecTimestampNow();
// dynamically sleep until we need to fire off the next set of voxels
const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 10 milliseconds
uint64_t elapsed = now - args->lastSendTime;
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, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
//qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
// args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
Application::getInstance()->timer();
usleep(usecToSleep);
} else {
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
//qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
// args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
}
args->lastSendTime = now;
}
@ -1572,70 +1673,127 @@ void Application::importVoxelsToClipboard() {
void Application::importVoxels() {
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation,
QStringList fileNameStringList = QFileDialog::getOpenFileNames(_glWidget, tr("Import Voxels"), desktopLocation,
tr(IMPORT_FILE_TYPES));
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data();
// remember the "selected" voxel point before we do any importing...
float originalX = _mouseVoxel.x;
float originalZ = _mouseVoxel.z;
const int PNG_TYPE_NAME_LENGTH = 4;
const int SVO_TYPE_NAME_LENGTH = 4;
const int SCH_TYPE_NAME_LENGTH = 10;
for (int i = 0; i < fileNameStringList.size(); i++) {
QString fileNameString = fileNameStringList.at(i);
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data();
VoxelTree importVoxels;
if (fileNameString.endsWith(".png", Qt::CaseInsensitive)) {
QImage pngImage = QImage(fileName);
if (pngImage.height() != pngImage.width()) {
qDebug("ERROR: Bad PNG size: height != width.\n");
return;
int fileTypeNameLength = 0;
VoxelTree importVoxels;
if (fileNameString.endsWith(".png", Qt::CaseInsensitive)) {
QImage pngImage = QImage(fileName);
fileTypeNameLength = PNG_TYPE_NAME_LENGTH;
if (pngImage.height() != pngImage.width()) {
qDebug("ERROR: Bad PNG size: height != width.\n");
return;
}
const uint32_t* pixels;
if (pngImage.format() == QImage::Format_ARGB32) {
pixels = reinterpret_cast<const uint32_t*>(pngImage.constBits());
} else {
QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32);
pixels = reinterpret_cast<const uint32_t*>(tmp.constBits());
}
importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height());
} else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) {
importVoxels.readFromSVOFile(fileName);
fileTypeNameLength = SVO_TYPE_NAME_LENGTH;
} else if (fileNameString.endsWith(".schematic", Qt::CaseInsensitive)) {
importVoxels.readFromSchematicFile(fileName);
fileTypeNameLength = SCH_TYPE_NAME_LENGTH;
}
const uint32_t* pixels;
if (pngImage.format() == QImage::Format_ARGB32) {
pixels = reinterpret_cast<const uint32_t*>(pngImage.constBits());
int indexOfFirstPeriod = fileNameString.indexOf('.');
QString fileCoord = fileNameString.mid(indexOfFirstPeriod + 1,
fileNameString.length() - indexOfFirstPeriod - fileTypeNameLength - 1);
indexOfFirstPeriod = fileCoord.indexOf('.');
QString columnNumString = fileCoord.right(fileCoord.length() - indexOfFirstPeriod - 1);
QString rowNumString = fileCoord.left(indexOfFirstPeriod);
int columnNum = columnNumString.toFloat();
int rowNum = rowNumString.toFloat();
qDebug("columnNum: %d\t rowNum: %d\n", columnNum, rowNum);
_mouseVoxel.x = originalX + (columnNum - 1) * _mouseVoxel.s;
_mouseVoxel.z = originalZ + (rowNum - 1) * _mouseVoxel.s;
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
// Recurse the Import Voxels tree, where everything is root relative, and send all the colored voxels to
// the server as an set voxel message, this will also rebase the voxels to the new location
unsigned char* calculatedOctCode = NULL;
SendVoxelsOperationArgs args;
args.lastSendTime = usecTimestampNow();
args.packetsSent = 0;
args.bytesSent = 0;
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
*sequenceAt = 0;
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details.
if (selectedNode) {
args.newBaseOctCode = selectedNode->getOctalCode();
} else {
QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32);
pixels = reinterpret_cast<const uint32_t*>(tmp.constBits());
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
}
qDebug("column:%d, row:%d, voxel:%f,%f,%f,%f\n", columnNum, rowNum, _mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s );
// send the insert/paste of these voxels
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
// If we have voxels left in the packet, then send the packet
if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) {
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
args.packetsSent++;
args.bytesSent += args.bufferInUse;
uint64_t now = usecTimestampNow();
// dynamically sleep until we need to fire off the next set of voxels
uint64_t elapsed = now - args.lastSendTime;
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
//qDebug("after sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
// args.packetsSent, (long long int)args.bytesSent, (long long int)elapsed, usecToSleep);
usleep(usecToSleep);
} else {
//qDebug("after sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
// args.packetsSent, (long long int)args.bytesSent, (long long int)elapsed);
}
args.lastSendTime = now;
}
importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height());
} else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) {
importVoxels.readFromSVOFile(fileName);
} else if (fileNameString.endsWith(".schematic", Qt::CaseInsensitive)) {
importVoxels.readFromSchematicFile(fileName);
if (calculatedOctCode) {
delete[] calculatedOctCode;
}
}
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
// Recurse the Import Voxels tree, where everything is root relative, and send all the colored voxels to
// the server as an set voxel message, this will also rebase the voxels to the new location
unsigned char* calculatedOctCode = NULL;
SendVoxelsOperationArgs args;
args.lastSendTime = usecTimestampNow();
args.packetsSent = 0;
args.bytesSent = 0;
int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader];
*sequenceAt = 0;
args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details.
if (selectedNode) {
args.newBaseOctCode = selectedNode->getOctalCode();
} else {
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
}
importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args);
// If we have voxels left in the packet, then send the packet
if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) {
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
}
if (calculatedOctCode) {
delete[] calculatedOctCode;
}
// restore the main window's active state
_window->activateWindow();
}
@ -1702,19 +1860,21 @@ void Application::initMenu() {
_window->setMenuBar(menuBar);
QMenu* fileMenu = menuBar->addMenu("File");
fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q);
QAction* quitAction = fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q);
quitAction->setMenuRole(QAction::QuitRole);
QMenu* editMenu = menuBar->addMenu("Edit");
editMenu->addAction("Preferences...", this, SLOT(editPreferences()));
QAction* preferencesAction = editMenu->addAction("Preferences...", this, SLOT(editPreferences()), Qt::CTRL | Qt::Key_Comma);
preferencesAction->setMenuRole(QAction::PreferencesRole);
QMenu* pairMenu = menuBar->addMenu("Pair");
pairMenu->addAction("Pair", this, SLOT(pair()));
QMenu* optionsMenu = menuBar->addMenu("Options");
(_lookingInMirror = optionsMenu->addAction("Mirror", this, SLOT(setRenderMirrored(bool)), Qt::Key_H))->setCheckable(true);
(_echoAudioMode = optionsMenu->addAction("Echo Audio"))->setCheckable(true);
optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true);
(_noise = optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N))->setCheckable(true);
(_gyroLook = optionsMenu->addAction("Smooth Gyro Look"))->setCheckable(true);
_gyroLook->setChecked(true);
(_showHeadMouse = optionsMenu->addAction("Head Mouse"))->setCheckable(true);
@ -1728,7 +1888,13 @@ 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("Go Home", this, SLOT(goHome()));
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()), Qt::CTRL | Qt::Key_G);
QMenu* audioMenu = menuBar->addMenu("Audio");
(_echoAudioMode = audioMenu->addAction("Echo Audio"))->setCheckable(true);
_rawAudioMicrophoneMix = audioMenu->addAction("Mix RAW Song", this, SLOT(toggleMixedSong()));
QMenu* renderMenu = menuBar->addMenu("Render");
(_renderVoxels = renderMenu->addAction("Voxels", this, SLOT(setRenderVoxels(bool)), Qt::SHIFT | Qt::Key_V))->setCheckable(true);
@ -1755,13 +1921,12 @@ 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(
"Third Person", this, SLOT(setRenderThirdPerson(bool))))->setCheckable(true);
renderMenu->addAction("Increase Avatar Size", this, SLOT(increaseAvatarSize()), Qt::ALT | Qt::Key_Plus);
renderMenu->addAction("Decrease Avatar Size", this, SLOT(decreaseAvatarSize()), Qt::ALT | Qt::Key_Minus);
renderMenu->addAction("Increase Avatar Size", this, SLOT(increaseAvatarSize()), Qt::Key_Plus);
renderMenu->addAction("Decrease Avatar Size", this, SLOT(decreaseAvatarSize()), Qt::Key_Minus);
QMenu* toolsMenu = menuBar->addMenu("Tools");
@ -1798,8 +1963,8 @@ void Application::initMenu() {
"Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_G))->setCheckable(true);
_voxelModeActions->addAction(_eyedropperMode);
voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut);
voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), QKeySequence::ZoomIn);
voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut);
voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), QKeySequence::ZoomIn);
voxelMenu->addAction("Reset Swatch Colors", this, SLOT(resetSwatchColors()));
_voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this,
@ -1826,7 +1991,7 @@ void Application::initMenu() {
(_viewFrustumFromOffset = frustumMenu->addAction(
"Use Offset Camera", this, SLOT(setFrustumOffset(bool)), Qt::SHIFT | Qt::Key_O))->setCheckable(true);
_frustumRenderModeAction = frustumMenu->addAction(
"Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R);
"Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R);
updateFrustumRenderModeAction();
debugMenu->addAction("Run Timing Tests", this, SLOT(runTests()));
@ -1842,6 +2007,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);
@ -1849,8 +2015,8 @@ void Application::initMenu() {
debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true);
debugMenu->addAction("Use Lower Resolution While Moving", this, SLOT(setWantsLowResMoving(bool)))->setCheckable(true);
debugMenu->addAction("Disable Delta Sending", this, SLOT(disableDeltaSending(bool)))->setCheckable(true);
debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)),
Qt::SHIFT | Qt::Key_C)->setCheckable(true);
(_occlusionCulling = debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)),
Qt::SHIFT | Qt::Key_C))->setCheckable(true);
(_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true);
_renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O);
@ -1899,6 +2065,22 @@ void Application::setListenModeSingleSource() {
}
}
void Application::toggleMixedSong() {
if (_audio.getSongFileBytes() == 0) {
QString filename = QFileDialog::getOpenFileName(_glWidget,
tr("Choose RAW Audio file"),
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
tr("RAW Audio file (*.raw)"));
QByteArray filenameArray = filename.toLocal8Bit();
_audio.importSongToMixWithMicrophone(filenameArray.data());
_rawAudioMicrophoneMix->setText("Stop Mixing Song");
} else {
_audio.stopMixingSongWithMicrophone();
_rawAudioMicrophoneMix->setText("Mix RAW Song");
}
}
void Application::updateFrustumRenderModeAction() {
switch (_frustumDrawingMode) {
@ -1980,7 +2162,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);
}
@ -1992,7 +2182,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();
@ -2001,15 +2191,15 @@ bool Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3& m
Avatar* avatar = (Avatar *) node->getLinkedData();
glm::vec3 headPosition = avatar->getHead().getPosition();
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS)) {
eyePosition = avatar->getHead().getEyeLevelPosition();
eyePosition = avatar->getHead().getEyePosition();
_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) {
@ -2024,6 +2214,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;
@ -2049,18 +2240,59 @@ void Application::update(float deltaTime) {
_myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection);
// Set where I am looking based on my mouse ray (so that other people can see)
glm::vec3 eyePosition;
glm::vec3 lookAtSpot;
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, eyePosition);
_isLookingAtOtherAvatar = isLookingAtOtherAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot);
if (_isLookingAtOtherAvatar) {
// If the mouse is over another avatar's head...
glm::vec3 myLookAtFromMouse(eyePosition);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
} else if (_isHoverVoxel) {
// Look at the hovered voxel
lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel);
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
} else {
glm::vec3 myLookAtFromMouse(mouseRayOrigin + mouseRayDirection);
_myAvatar.getHead().setLookAtPosition(myLookAtFromMouse);
// Just look in direction of the mouse ray
const float FAR_AWAY_STARE = TREE_SCALE;
lookAtSpot = mouseRayOrigin + mouseRayDirection * FAR_AWAY_STARE;
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
}
// Find the voxel we are hovering over, and respond if clicked
float distance;
BoxFace face;
// If we have clicked on a voxel, update it's color
if (_isHoverVoxelSounding) {
VoxelNode* hoveredNode = _voxels.getVoxelAt(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
if (hoveredNode) {
float bright = _audio.getCollisionSoundMagnitude();
nodeColor clickColor = { 255 * bright + _hoverVoxelOriginalColor[0] * (1.f - bright),
_hoverVoxelOriginalColor[1] * (1.f - bright),
_hoverVoxelOriginalColor[2] * (1.f - bright), 1 };
hoveredNode->setColor(clickColor);
if (bright < 0.01f) {
hoveredNode->setColor(_hoverVoxelOriginalColor);
_isHoverVoxelSounding = false;
}
} else {
// Voxel is not found, clear all
_isHoverVoxelSounding = false;
_isHoverVoxel = false;
}
} else {
// Check for a new hover voxel
glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
_isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face);
if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) {
_hoverVoxelOriginalColor[0] = _hoverVoxel.red;
_hoverVoxelOriginalColor[1] = _hoverVoxel.green;
_hoverVoxelOriginalColor[2] = _hoverVoxel.blue;
_hoverVoxelOriginalColor[3] = 1;
_audio.startCollisionSound(1.0, HOVER_VOXEL_FREQUENCY * _hoverVoxel.s * TREE_SCALE, 0.0, HOVER_VOXEL_DECAY);
_isHoverVoxelSounding = true;
}
}
// If we are dragging on a voxel, add thrust according to the amount the mouse is dragging
const float VOXEL_GRAB_THRUST = 0.0f;
if (_mousePressed && (_mouseVoxel.s != 0)) {
@ -2086,8 +2318,6 @@ void Application::update(float deltaTime) {
(fabs(_myAvatar.getVelocity().x) +
fabs(_myAvatar.getVelocity().y) +
fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) {
float distance;
BoxFace face;
if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) {
if (distance < MAX_VOXEL_EDIT_DISTANCE) {
// find the nearest voxel with the desired scale
@ -2274,20 +2504,18 @@ void Application::update(float deltaTime) {
}
void Application::updateAvatar(float deltaTime) {
// When head is rotated via touch/mouse look, slowly turn body to follow
const float BODY_FOLLOW_HEAD_RATE = 0.5f;
// update body yaw by body yaw delta
// rotate body yaw for yaw received from multitouch
_myAvatar.setOrientation(_myAvatar.getOrientation()
* glm::quat(glm::vec3(0, _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE, 0) * deltaTime));
_yawFromTouch -= _yawFromTouch * deltaTime * BODY_FOLLOW_HEAD_RATE;
* glm::quat(glm::vec3(0, _yawFromTouch * deltaTime, 0)));
_yawFromTouch = 0.f;
// Update my avatar's state from gyros and/or webcam
_myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(),
glm::vec3(_headCameraPitchYawScale,
_headCameraPitchYawScale,
_headCameraPitchYawScale),
_yawFromTouch,
0.f,
_pitchFromTouch);
if (_serialHeadSensor.isActive()) {
@ -2601,7 +2829,7 @@ void Application::displaySide(Camera& whichCamera) {
glRotatef(-glm::angle(rotation), axis.x, axis.y, axis.z);
glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z);
// Setup 3D lights (after the camera transform, so that they are positioned in world space)
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
@ -2718,6 +2946,7 @@ 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);
@ -2731,7 +2960,7 @@ void Application::displaySide(Camera& whichCamera) {
}
// Render the world box
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); }
if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { renderWorldBox(); }
// brad's frustum for debugging
if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum);
@ -2746,8 +2975,12 @@ void Application::displayOverlay() {
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// Display a single screen-size quad to
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
// Display a single screen-size quad to create an alpha blended 'collision' flash
float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude();
const float VISIBLE_COLLISION_SOUND_MAGNITUDE = 0.5f;
if (collisionSoundMagnitude > VISIBLE_COLLISION_SOUND_MAGNITUDE) {
renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude());
}
#ifndef _WIN32
_audio.render(_glWidget->width(), _glWidget->height());
@ -2879,6 +3112,10 @@ void Application::displayOverlay() {
_swatch.checkColor();
}
if (_pieMenu.isDisplayed()) {
_pieMenu.render();
}
glPopMatrix();
}
@ -2891,19 +3128,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);
}
@ -3278,7 +3531,7 @@ void Application::shiftPaintingColor() {
}
void Application::maybeEditVoxelUnderCursor() {
bool Application::maybeEditVoxelUnderCursor() {
if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) {
if (_mouseVoxel.s != 0) {
PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ?
@ -3349,7 +3602,11 @@ void Application::maybeEditVoxelUnderCursor() {
deleteVoxelUnderCursor();
} else if (_eyedropperMode->isChecked()) {
eyedropperVoxelUnderCursor();
} else {
return false;
}
return true;
}
void Application::deleteVoxelUnderCursor() {
@ -3392,6 +3649,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;
@ -3462,6 +3735,7 @@ void* Application::networkReceive(void* args) {
}
if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) {
app->_packetCount++;
app->_bytesCount += bytesReceived;
@ -3503,16 +3777,16 @@ 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"
@ -109,8 +110,6 @@ public slots:
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
void setGroundPlaneImpact(float groundPlaneImpact) { _groundPlaneImpact = groundPlaneImpact; }
private slots:
@ -151,6 +150,7 @@ private slots:
void doFalseColorizeByDistance();
void doFalseColorizeOccluded();
void doFalseColorizeOccludedV2();
void doFalseColorizeBySource();
void doFalseColorizeInView();
void doTrueVoxelColors();
void doTreeStats();
@ -177,6 +177,7 @@ private slots:
void setListenModeNormal();
void setListenModePoint();
void setListenModeSingleSource();
void toggleMixedSong();
void renderCoverageMap();
@ -188,6 +189,8 @@ private slots:
glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint);
void goHome();
void toggleFollowMode();
private:
static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes,
@ -207,7 +210,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);
@ -224,7 +227,7 @@ private:
void setupPaintingVoxel();
void shiftPaintingColor();
void maybeEditVoxelUnderCursor();
bool maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void resetSensors();
@ -286,12 +289,17 @@ private:
QAction* _fullScreenMode; // whether we are in full screen mode
QAction* _frustumRenderModeAction;
QAction* _settingsAutosave; // Whether settings are saved automatically
QAction* _rawAudioMicrophoneMix; // Mixing of a RAW audio file with microphone stream for rave gloves
QAction* _noise;
QAction* _occlusionCulling;
QAction* _renderCoverageMapV2;
QAction* _renderCoverageMap;
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;
@ -371,14 +379,16 @@ private:
float _yawFromTouch;
float _pitchFromTouch;
float _groundPlaneImpact;
VoxelDetail _mouseVoxelDragging;
glm::vec3 _voxelThrust;
bool _mousePressed; // true if mouse has been pressed (clear when finished)
VoxelDetail _hoverVoxel; // Stuff about the voxel I am hovering or clicking
bool _isHoverVoxel;
bool _isHoverVoxelSounding;
nodeColor _hoverVoxelOriginalColor;
VoxelDetail _mouseVoxel; // details of the voxel under the mouse cursor
VoxelDetail _mouseVoxel; // details of the voxel to be edited
float _mouseVoxelScale; // the scale for adding/removing voxels
glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit
bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel
@ -429,6 +439,8 @@ private:
ToolsPalette _palette;
Swatch _swatch;
PieMenu _pieMenu;
VoxelSceneStats _voxelSceneStats;
};

View file

@ -8,13 +8,11 @@
#ifndef _WIN32
#include <cstring>
#include <fstream>
#include <iostream>
#include <pthread.h>
#include <sys/stat.h>
#include <AngleUtil.h>
#include <NodeList.h>
#include <NodeTypes.h>
@ -155,8 +153,38 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
currentPacketPtr += sizeof(headOrientation);
// check if we have a song to add to our audio
if (_songFileBytes > 0 && _songFileStream->tellg() <= _songFileBytes) {
// iterate over BUFFER_LENGTH_SAMPLES_PER_CHANNEL from the song file and add that to our audio
for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
int16_t songSample = 0;
_songFileStream->read((char*) &songSample, sizeof(songSample));
// attenuate the song samples since they will be loud
const float SONG_SAMPLE_ATTENUATION = 0.25;
songSample *= SONG_SAMPLE_ATTENUATION;
// add the song sample to the output and input buffersg
inputLeft[i] = inputLeft[i] + songSample;
outputLeft[i] = outputLeft[i] + songSample;
outputRight[i] = outputLeft[i] + songSample;
}
} else if (_songFileStream) {
// close the stream
_songFileStream->close();
// delete the _songFileStream
delete _songFileStream;
_songFileStream = NULL;
// reset the _songFileBytes back to zero
_songFileBytes = 0;
}
// copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet
memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL);
nodeList->getNodeSocket()->send((sockaddr*) &audioSocket,
dataPacket,
BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes);
@ -384,6 +412,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_collisionSoundDuration(0.0f),
_proceduralEffectSample(0),
_heartbeatMagnitude(0.0f),
_songFileStream(NULL),
_songFileBytes(0),
_listenMode(AudioRingBuffer::NORMAL),
_listenRadius(0.0f)
{
@ -456,6 +486,25 @@ Audio::~Audio() {
delete[] _echoSamplesLeft;
}
void Audio::importSongToMixWithMicrophone(const char* filename) {
_songFileStream = new std::ifstream(filename);
long begin = _songFileStream->tellg();
_songFileStream->seekg(0, std::ios::end);
long end = _songFileStream->tellg();
// go back to the beginning
_songFileStream->seekg(0);
_songFileBytes = end - begin;
}
void Audio::stopMixingSongWithMicrophone() {
qDebug("Stop mixing called!");
_songFileBytes = 0;
}
void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBytes) {
const int NUM_INITIAL_PACKETS_DISCARD = 3;
const int STANDARD_DEVIATION_SAMPLE_COUNT = 500;

View file

@ -9,8 +9,13 @@
#ifndef __interface__Audio__
#define __interface__Audio__
#include <fstream>
#include <vector>
#include <QObject>
#include <portaudio.h>
#include <AudioRingBuffer.h>
#include <StdDev.h>
@ -23,7 +28,8 @@ static const int PACKET_LENGTH_BYTES_PER_CHANNEL = PACKET_LENGTH_BYTES / 2;
static const int PACKET_LENGTH_SAMPLES = PACKET_LENGTH_BYTES / sizeof(int16_t);
static const int PACKET_LENGTH_SAMPLES_PER_CHANNEL = PACKET_LENGTH_SAMPLES / 2;
class Audio {
class Audio : public QObject {
Q_OBJECT
public:
// initializes audio I/O
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples);
@ -47,6 +53,7 @@ public:
void startCollisionSound(float magnitude, float frequency, float noise, float duration);
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; };
int getSongFileBytes() { return _songFileBytes; }
void ping();
@ -60,8 +67,13 @@ public:
void addListenSource(int sourceID);
void removeListenSource(int sourceID);
void clearListenSources();
void importSongToMixWithMicrophone(const char* filename);
public slots:
void stopMixingSongWithMicrophone();
private:
private:
PaStream* _stream;
AudioRingBuffer _ringBuffer;
Oscilloscope* _scope;
@ -96,6 +108,8 @@ private:
float _collisionSoundDuration;
int _proceduralEffectSample;
float _heartbeatMagnitude;
std::ifstream* _songFileStream;
int _songFileBytes;
AudioRingBuffer::ListenMode _listenMode;
float _listenRadius;

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

View file

@ -38,3 +38,7 @@ void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, fl
}
}
void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) {
}

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

@ -223,39 +223,54 @@ void noiseTest(int w, int h) {
glEnd();
}
void render_world_box() {
// Show edge of world
void renderWorldBox() {
// Show edge of world
float red[] = {1, 0, 0};
float green[] = {0, 1, 0};
float blue[] = {0, 0, 1};
float gray[] = {0.5, 0.5, 0.5};
glDisable(GL_LIGHTING);
glColor4f(1.0, 1.0, 1.0, 1.0);
glLineWidth(1.0);
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glColor3fv(red);
glVertex3f(0, 0, 0);
glVertex3f(WORLD_SIZE, 0, 0);
glColor3f(0, 1, 0);
glVertex3f(TREE_SCALE, 0, 0);
glColor3fv(green);
glVertex3f(0, 0, 0);
glVertex3f(0, WORLD_SIZE, 0);
glColor3f(0, 0, 1);
glVertex3f(0, TREE_SCALE, 0);
glColor3fv(blue);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, WORLD_SIZE);
glVertex3f(0, 0, TREE_SCALE);
glColor3fv(gray);
glVertex3f(0, 0, TREE_SCALE);
glVertex3f(TREE_SCALE, 0, TREE_SCALE);
glVertex3f(TREE_SCALE, 0, TREE_SCALE);
glVertex3f(TREE_SCALE, 0, 0);
glEnd();
// Draw little marker dots along the axis
// Draw marker dots at very end
glEnable(GL_LIGHTING);
glPushMatrix();
glTranslatef(WORLD_SIZE, 0, 0);
glColor3f(1, 0, 0);
glTranslatef(TREE_SCALE, 0, 0);
glColor3fv(red);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0, WORLD_SIZE, 0);
glColor3f(0, 1, 0);
glTranslatef(0, TREE_SCALE, 0);
glColor3fv(green);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
glPushMatrix();
glTranslatef(0, 0, WORLD_SIZE);
glColor3f(0, 0, 1);
glTranslatef(0, 0, TREE_SCALE);
glColor3fv(blue);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
glPushMatrix();
glColor3fv(gray);
glTranslatef(TREE_SCALE, 0, TREE_SCALE);
glutSolidSphere(0.125, 10, 10);
glPopMatrix();
}
double diffclock(timeval *clock1,timeval *clock2)
@ -339,6 +354,7 @@ void renderCollisionOverlay(int width, int height, float magnitude) {
}
void renderGroundPlaneGrid(float size, float impact) {
float IMPACT_SOUND_MAGNITUDE_FOR_RECOLOR = 1.f;
glLineWidth(2.0);
glm::vec4 impactColor(1, 0, 0, 1);
glm::vec3 lineColor(0.4, 0.5, 0.3);
@ -355,7 +371,12 @@ void renderGroundPlaneGrid(float size, float impact) {
}
// Draw the floor, colored for recent impact
glm::vec4 floorColor = impact * impactColor + (1.f - impact) * surfaceColor;
glm::vec4 floorColor;
if (impact > IMPACT_SOUND_MAGNITUDE_FOR_RECOLOR) {
floorColor = impact * impactColor + (1.f - impact) * surfaceColor;
} else {
floorColor = surfaceColor;
}
glColor4fv(&floorColor.x);
glBegin(GL_QUADS);
glVertex3f(0, 0, 0);

View file

@ -33,7 +33,7 @@ float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float
float randFloat();
const glm::vec3 randVector();
void render_world_box();
void renderWorldBox();
int widthText(float scale, int mono, char const* string);
float widthChar(float scale, int mono, char ch);
void drawtext(int x, int y, float scale, float rotate, float thick, int mono,

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

@ -19,6 +19,7 @@
#include "Application.h"
#include "Webcam.h"
#include "avatar/Face.h"
using namespace cv;
using namespace std;
@ -32,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);
@ -46,11 +47,11 @@ void Webcam::setEnabled(bool enabled) {
_grabberThread.start();
_startTimestamp = 0;
_frameCount = 0;
// let the grabber know we're ready for the first frame
QMetaObject::invokeMethod(_grabber, "reset");
QMetaObject::invokeMethod(_grabber, "grabFrame");
} else {
QMetaObject::invokeMethod(_grabber, "shutdown");
_active = false;
@ -62,7 +63,7 @@ const float UNINITIALIZED_FACE_DEPTH = 0.0f;
void Webcam::reset() {
_initialFaceRect = RotatedRect();
_initialFaceDepth = UNINITIALIZED_FACE_DEPTH;
if (_enabled) {
// send a message to the grabber
QMetaObject::invokeMethod(_grabber, "reset");
@ -79,7 +80,7 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height;
int top = screenHeight - 600;
int left = screenWidth - previewWidth - 10;
glTexCoord2f(0, 0);
glVertex2f(left, top);
glTexCoord2f(1, 0);
@ -89,7 +90,7 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glTexCoord2f(0, 1);
glVertex2f(left, top + PREVIEW_HEIGHT);
glEnd();
if (_depthTextureID != 0) {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glBegin(GL_QUADS);
@ -102,10 +103,10 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glTexCoord2f(0, 1);
glVertex2f(left, top);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (!_joints.isEmpty()) {
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(4.0f);
@ -124,7 +125,7 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_LOOP);
Point2f facePoints[4];
@ -136,7 +137,7 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glVertex2f(left + facePoints[2].x * xScale, top + facePoints[2].y * yScale);
glVertex2f(left + facePoints[3].x * xScale, top + facePoints[3].y * yScale);
glEnd();
const int MAX_FPS_CHARACTERS = 30;
char fps[MAX_FPS_CHARACTERS];
sprintf(fps, "FPS: %d", (int)(roundf(_frameCount * 1000000.0f / (usecTimestampNow() - _startTimestamp))));
@ -148,14 +149,14 @@ Webcam::~Webcam() {
// stop the grabber thread
_grabberThread.quit();
_grabberThread.wait();
delete _grabber;
}
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) {
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midFaceDepth,
float aspectRatio, const RotatedRect& faceRect, bool sending, const JointVector& joints) {
IplImage colorImage = color;
glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3);
if (_colorTextureID == 0) {
@ -165,13 +166,13 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean
0, format, GL_UNSIGNED_BYTE, colorImage.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qDebug("Capturing video at %gx%g.\n", _textureSize.width, _textureSize.height);
} else {
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format,
GL_UNSIGNED_BYTE, colorImage.imageData);
}
if (!depth.empty()) {
IplImage depthImage = depth;
glPixelStorei(GL_UNPACK_ROW_LENGTH, depthImage.widthStep);
@ -182,21 +183,23 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean
GL_LUMINANCE, GL_UNSIGNED_BYTE, depthImage.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else {
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, GL_LUMINANCE,
GL_UNSIGNED_BYTE, depthImage.imageData);
GL_UNSIGNED_BYTE, depthImage.imageData);
}
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// store our face rect and joints, update our frame count for fps computation
// store our various data, update our frame count for fps computation
_aspectRatio = aspectRatio;
_faceRect = faceRect;
_joints = joints;
_sending = sending;
_joints = _skeletonTrackingOn ? joints : JointVector();
_frameCount++;
const int MAX_FPS = 60;
const int MIN_FRAME_DELAY = 1000000 / MAX_FPS;
uint64_t now = usecTimestampNow();
@ -207,14 +210,14 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean
remaining -= (now - _lastFrameTimestamp);
}
_lastFrameTimestamp = now;
// see if we have joint data
if (!_joints.isEmpty()) {
_estimatedJoints.resize(NUM_AVATAR_JOINTS);
glm::vec3 origin;
if (_joints[AVATAR_JOINT_LEFT_HIP].isValid && _joints[AVATAR_JOINT_RIGHT_HIP].isValid) {
origin = glm::mix(_joints[AVATAR_JOINT_LEFT_HIP].position, _joints[AVATAR_JOINT_RIGHT_HIP].position, 0.5f);
} else if (_joints[AVATAR_JOINT_TORSO].isValid) {
const glm::vec3 TORSO_TO_PELVIS = glm::vec3(0.0f, -0.09f, -0.01f);
origin = _joints[AVATAR_JOINT_TORSO].position + TORSO_TO_PELVIS;
@ -232,28 +235,28 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean
}
_estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].rotation);
_estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position;
} else {
// roll is just the angle of the face rect
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/mean face depth
if (_initialFaceRect.size.area() == 0) {
_initialFaceRect = _faceRect;
_estimatedPosition = glm::vec3();
_initialFaceDepth = meanFaceDepth;
_initialFaceDepth = midFaceDepth;
} else {
float proportion, z;
if (meanFaceDepth == UNINITIALIZED_FACE_DEPTH) {
if (midFaceDepth == 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;
z = INITIAL_DISTANCE_TO_CAMERA * proportion - INITIAL_DISTANCE_TO_CAMERA;
} else {
z = (meanFaceDepth - _initialFaceDepth) * METERS_PER_MM;
proportion = meanFaceDepth / _initialFaceDepth;
z = (midFaceDepth - _initialFaceDepth) * METERS_PER_MM;
proportion = midFaceDepth / _initialFaceDepth;
}
const float POSITION_SCALE = 0.5f;
_estimatedPosition = glm::vec3(
@ -262,16 +265,16 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean
z);
}
}
// note that we have data
_active = true;
// let the grabber know we're ready for the next frame
QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame()));
}
FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0),
_smoothedMeanFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) {
FrameGrabber::FrameGrabber() : _initialized(false), _videoSendMode(FULL_FRAME_VIDEO), _capture(0), _searchWindow(0, 0, 0, 0),
_smoothedMidFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) {
}
FrameGrabber::~FrameGrabber() {
@ -286,21 +289,21 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) {
case XN_SKEL_HEAD: return AVATAR_JOINT_HEAD_TOP;
case XN_SKEL_NECK: return AVATAR_JOINT_HEAD_BASE;
case XN_SKEL_TORSO: return AVATAR_JOINT_CHEST;
case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_RIGHT_ELBOW;
case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_WRIST;
case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_LEFT_ELBOW;
case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_WRIST;
case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_RIGHT_KNEE;
case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_RIGHT_HEEL;
case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_RIGHT_TOES;
case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_LEFT_KNEE;
case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_LEFT_HEEL;
case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_LEFT_TOES;
default: return AVATAR_JOINT_NULL;
}
}
@ -309,19 +312,19 @@ static int getParentJoint(XnSkeletonJoint joint) {
switch (joint) {
case XN_SKEL_HEAD: return XN_SKEL_NECK;
case XN_SKEL_TORSO: return -1;
case XN_SKEL_LEFT_ELBOW: return XN_SKEL_LEFT_SHOULDER;
case XN_SKEL_LEFT_HAND: return XN_SKEL_LEFT_ELBOW;
case XN_SKEL_RIGHT_ELBOW: return XN_SKEL_RIGHT_SHOULDER;
case XN_SKEL_RIGHT_HAND: return XN_SKEL_RIGHT_ELBOW;
case XN_SKEL_LEFT_KNEE: return XN_SKEL_LEFT_HIP;
case XN_SKEL_LEFT_FOOT: return XN_SKEL_LEFT_KNEE;
case XN_SKEL_RIGHT_KNEE: return XN_SKEL_RIGHT_HIP;
case XN_SKEL_RIGHT_FOOT: return XN_SKEL_RIGHT_KNEE;
default: return XN_SKEL_TORSO;
}
}
@ -356,7 +359,7 @@ static void XN_CALLBACK_TYPE calibrationCompleted(SkeletonCapability& capability
if (status == XN_CALIBRATION_STATUS_OK) {
qDebug("Calibration completed for user %d.\n", id);
capability.StartTracking(id);
} else {
qDebug("Calibration failed to user %d.\n", id);
capability.RequestCalibration(id, true);
@ -364,6 +367,13 @@ static void XN_CALLBACK_TYPE calibrationCompleted(SkeletonCapability& capability
}
#endif
void FrameGrabber::cycleVideoSendMode() {
_videoSendMode = (VideoSendMode)((_videoSendMode + 1) % VIDEO_SEND_MODE_COUNT);
_searchWindow = cv::Rect(0, 0, 0, 0);
destroyCodecs();
}
void FrameGrabber::reset() {
_searchWindow = cv::Rect(0, 0, 0, 0);
@ -379,16 +389,9 @@ void FrameGrabber::shutdown() {
cvReleaseCapture(&_capture);
_capture = 0;
}
if (_colorCodec.name != 0) {
vpx_codec_destroy(&_colorCodec);
_colorCodec.name = 0;
}
if (_depthCodec.name != 0) {
vpx_codec_destroy(&_depthCodec);
_depthCodec.name = 0;
}
destroyCodecs();
_initialized = false;
thread()->quit();
}
@ -404,17 +407,17 @@ void FrameGrabber::grabFrame() {
int format = GL_BGR;
Mat color, depth;
JointVector joints;
#ifdef HAVE_OPENNI
if (_depthGenerator.IsValid()) {
_xnContext.WaitAnyUpdateAll();
color = Mat(_imageMetaData.YRes(), _imageMetaData.XRes(), CV_8UC3, (void*)_imageGenerator.GetImageMap());
format = GL_RGB;
depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap());
_userID = 0;
XnUInt16 userCount = 1;
XnUInt16 userCount = 1;
_userGenerator.GetUsers(&_userID, userCount);
if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(_userID)) {
joints.resize(NUM_AVATAR_JOINTS);
@ -461,244 +464,291 @@ void FrameGrabber::grabFrame() {
}
color = image;
}
// if we don't have a search window (yet), try using the face cascade
int channels = 0;
float ranges[] = { 0, 180 };
const float* range = ranges;
if (_searchWindow.area() == 0) {
vector<Rect> faces;
_faceCascade.detectMultiScale(color, faces, 1.1, 6);
if (!faces.empty()) {
_searchWindow = faces.front();
updateHSVFrame(color, format);
Mat faceHsv(_hsvFrame, _searchWindow);
Mat faceMask(_mask, _searchWindow);
int sizes = 30;
calcHist(&faceHsv, 1, &channels, faceMask, _histogram, 1, &sizes, &range);
double min, max;
minMaxLoc(_histogram, &min, &max);
_histogram.convertTo(_histogram, -1, (max == 0.0) ? 0.0 : 255.0 / max);
}
}
RotatedRect faceRect;
if (_searchWindow.area() > 0) {
updateHSVFrame(color, format);
calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range);
bitwise_and(_backProject, _mask, _backProject);
faceRect = CamShift(_backProject, _searchWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
Rect faceBounds = faceRect.boundingRect();
Rect imageBounds(0, 0, color.cols, color.rows);
_searchWindow = Rect(clip(faceBounds.tl(), imageBounds), clip(faceBounds.br(), imageBounds));
}
const int ENCODED_FACE_WIDTH = 128;
const int ENCODED_FACE_HEIGHT = 128;
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 * ENCODED_FACE_HEIGHT *
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
codecConfig.g_w = ENCODED_FACE_WIDTH;
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
if (faceRect.angle < -90.0f) {
faceRect.angle += 180.0f;
} else if (faceRect.angle > 90.0f) {
faceRect.angle -= 180.0f;
}
// compute the smoothed face rect
if (_smoothedFaceRect.size.area() == 0) {
_smoothedFaceRect = faceRect;
int encodedWidth;
int encodedHeight;
float colorBitrateMultiplier = 1.0f;
float depthBitrateMultiplier = 1.0f;
Mat faceTransform;
float aspectRatio;
if (_videoSendMode == FULL_FRAME_VIDEO) {
// no need to find the face if we're sending full frame video
_smoothedFaceRect = RotatedRect(Point2f(color.cols / 2.0f, color.rows / 2.0f), Size2f(color.cols, color.rows), 0.0f);
encodedWidth = color.cols;
encodedHeight = color.rows;
aspectRatio = FULL_FRAME_ASPECT;
colorBitrateMultiplier = 4.0f;
} else {
const float FACE_RECT_SMOOTHING = 0.9f;
_smoothedFaceRect.center.x = glm::mix(faceRect.center.x, _smoothedFaceRect.center.x, FACE_RECT_SMOOTHING);
_smoothedFaceRect.center.y = glm::mix(faceRect.center.y, _smoothedFaceRect.center.y, FACE_RECT_SMOOTHING);
_smoothedFaceRect.size.width = glm::mix(faceRect.size.width, _smoothedFaceRect.size.width, FACE_RECT_SMOOTHING);
_smoothedFaceRect.size.height = glm::mix(faceRect.size.height, _smoothedFaceRect.size.height, FACE_RECT_SMOOTHING);
_smoothedFaceRect.angle = glm::mix(faceRect.angle, _smoothedFaceRect.angle, FACE_RECT_SMOOTHING);
}
// resize/rotate face into encoding rectangle
_faceColor.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_8UC3);
Point2f sourcePoints[4];
_smoothedFaceRect.points(sourcePoints);
Point2f destPoints[] = { Point2f(0, ENCODED_FACE_HEIGHT), Point2f(0, 0), Point2f(ENCODED_FACE_WIDTH, 0) };
Mat transform = getAffineTransform(sourcePoints, destPoints);
warpAffine(color, _faceColor, transform, _faceColor.size());
// convert from RGB to YV12
const int ENCODED_BITS_PER_Y = 8;
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.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, ENCODED_FACE_HEIGHT, 1, (unsigned char*)_encodedFace.data());
uchar* yline = vpxImage.planes[0];
uchar* vline = vpxImage.planes[1];
uchar* uline = vpxImage.planes[2];
const int Y_RED_WEIGHT = (int)(0.299 * 256);
const int Y_GREEN_WEIGHT = (int)(0.587 * 256);
const int Y_BLUE_WEIGHT = (int)(0.114 * 256);
const int V_RED_WEIGHT = (int)(0.713 * 256);
const int U_BLUE_WEIGHT = (int)(0.564 * 256);
int redIndex = 0;
int greenIndex = 1;
int blueIndex = 2;
if (format == GL_BGR) {
redIndex = 2;
blueIndex = 0;
}
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) {
uchar* tl = _faceColor.ptr(i, j);
uchar* tr = _faceColor.ptr(i, j + 1);
uchar* bl = _faceColor.ptr(i + 1, j);
uchar* br = _faceColor.ptr(i + 1, j + 1);
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[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest += 2;
int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex];
int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex];
int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex];
int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8;
*vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128;
*udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128;
}
yline += vpxImage.stride[0] * 2;
vline += vpxImage.stride[1];
uline += vpxImage.stride[2];
}
// encode the frame
vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
// if we don't have a search window (yet), try using the face cascade
int channels = 0;
float ranges[] = { 0, 180 };
const float* range = ranges;
if (_searchWindow.area() == 0) {
vector<Rect> faces;
_faceCascade.detectMultiScale(color, faces, 1.1, 6);
if (!faces.empty()) {
_searchWindow = faces.front();
updateHSVFrame(color, format);
// 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(&_colorCodec, &iterator)) != 0) {
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
// 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);
}
}
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++;
}
Mat faceHsv(_hsvFrame, _searchWindow);
Mat faceMask(_mask, _searchWindow);
int sizes = 30;
calcHist(&faceHsv, 1, &channels, faceMask, _histogram, 1, &sizes, &range);
double min, max;
minMaxLoc(_histogram, &min, &max);
_histogram.convertTo(_histogram, -1, (max == 0.0) ? 0.0 : 255.0 / max);
}
}
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);
RotatedRect faceRect;
if (_searchWindow.area() > 0) {
updateHSVFrame(color, format);
calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range);
bitwise_and(_backProject, _mask, _backProject);
faceRect = CamShift(_backProject, _searchWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
Rect faceBounds = faceRect.boundingRect();
Rect imageBounds(0, 0, color.cols, color.rows);
_searchWindow = Rect(clip(faceBounds.tl(), imageBounds), clip(faceBounds.br(), imageBounds));
}
encodedWidth = ENCODED_FACE_WIDTH;
encodedHeight = ENCODED_FACE_HEIGHT;
depthBitrateMultiplier = 2.0f;
// correct for 180 degree rotations
if (faceRect.angle < -90.0f) {
faceRect.angle += 180.0f;
} else if (faceRect.angle > 90.0f) {
faceRect.angle -= 180.0f;
}
// compute the smoothed face rect
if (_smoothedFaceRect.size.area() == 0) {
_smoothedFaceRect = faceRect;
} else {
const float FACE_RECT_SMOOTHING = 0.9f;
_smoothedFaceRect.center.x = glm::mix(faceRect.center.x, _smoothedFaceRect.center.x, FACE_RECT_SMOOTHING);
_smoothedFaceRect.center.y = glm::mix(faceRect.center.y, _smoothedFaceRect.center.y, FACE_RECT_SMOOTHING);
_smoothedFaceRect.size.width = glm::mix(faceRect.size.width, _smoothedFaceRect.size.width, FACE_RECT_SMOOTHING);
_smoothedFaceRect.size.height = glm::mix(faceRect.size.height, _smoothedFaceRect.size.height, FACE_RECT_SMOOTHING);
_smoothedFaceRect.angle = glm::mix(faceRect.angle, _smoothedFaceRect.angle, FACE_RECT_SMOOTHING);
}
// use the face rect to compute the face transform, aspect ratio
Point2f sourcePoints[4];
_smoothedFaceRect.points(sourcePoints);
Point2f destPoints[] = { Point2f(0, encodedHeight), Point2f(0, 0), Point2f(encodedWidth, 0) };
faceTransform = getAffineTransform(sourcePoints, destPoints);
aspectRatio = _smoothedFaceRect.size.width / _smoothedFaceRect.size.height;
}
const ushort ELEVEN_BIT_MINIMUM = 0;
const uchar EIGHT_BIT_MIDPOINT = 128;
double depthOffset;
if (!depth.empty()) {
if (_videoSendMode == FACE_VIDEO) {
// warp the face depth without interpolation (because it will contain invalid zero values)
_faceDepth.create(encodedHeight, encodedWidth, CV_16UC1);
warpAffine(depth, _faceDepth, faceTransform, _faceDepth.size(), INTER_NEAREST);
} else {
_faceDepth = depth;
}
_smoothedFaceDepth.create(encodedHeight, encodedWidth, CV_16UC1);
// smooth the depth over time
const ushort ELEVEN_BIT_MAXIMUM = 2047;
const float DEPTH_SMOOTHING = 0.25f;
ushort* src = _faceDepth.ptr<ushort>();
ushort* dest = _smoothedFaceDepth.ptr<ushort>();
ushort minimumDepth = numeric_limits<ushort>::max();
for (int i = 0; i < encodedHeight; i++) {
for (int j = 0; j < encodedWidth; j++) {
ushort depth = *src++;
if (depth != ELEVEN_BIT_MINIMUM && depth != ELEVEN_BIT_MAXIMUM) {
minimumDepth = min(minimumDepth, depth);
*dest = (*dest == ELEVEN_BIT_MINIMUM) ? depth : (ushort)glm::mix(depth, *dest, DEPTH_SMOOTHING);
}
dest++;
}
}
const ushort MINIMUM_DEPTH_OFFSET = 64;
const float FIXED_MID_DEPTH = 640.0f;
float midFaceDepth = (_videoSendMode == FACE_VIDEO) ? (minimumDepth + MINIMUM_DEPTH_OFFSET) : FIXED_MID_DEPTH;
// smooth the mid face depth over time
const float MID_FACE_DEPTH_SMOOTHING = 0.5f;
_smoothedMidFaceDepth = (_smoothedMidFaceDepth == UNINITIALIZED_FACE_DEPTH) ? midFaceDepth :
glm::mix(midFaceDepth, _smoothedMidFaceDepth, MID_FACE_DEPTH_SMOOTHING);
// convert from 11 to 8 bits for preview/local display
const uchar EIGHT_BIT_MIDPOINT = 128;
double depthOffset = EIGHT_BIT_MIDPOINT - _smoothedMeanFaceDepth;
depthOffset = EIGHT_BIT_MIDPOINT - _smoothedMidFaceDepth;
depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset);
}
// likewise for the encoded representation
QByteArray payload;
if (_videoSendMode != NO_VIDEO) {
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 * ENCODED_FACE_HEIGHT * colorBitrateMultiplier *
codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h;
codecConfig.g_w = encodedWidth;
codecConfig.g_h = encodedHeight;
vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
if (!depth.empty()) {
codecConfig.rc_target_bitrate *= depthBitrateMultiplier;
vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
}
}
Mat transform;
if (_videoSendMode == FACE_VIDEO) {
// resize/rotate face into encoding rectangle
_faceColor.create(encodedHeight, encodedWidth, CV_8UC3);
warpAffine(color, _faceColor, faceTransform, _faceColor.size());
} else {
_faceColor = color;
}
// convert from RGB to YV12: see http://www.fourcc.org/yuv.php and
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
const int ENCODED_BITS_PER_Y = 8;
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.resize(encodedWidth * encodedHeight * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE);
vpx_image_t vpxImage;
vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, encodedWidth, encodedHeight, 1,
(unsigned char*)_encodedFace.data());
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) {
const int Y_RED_WEIGHT = (int)(0.299 * 256);
const int Y_GREEN_WEIGHT = (int)(0.587 * 256);
const int Y_BLUE_WEIGHT = (int)(0.114 * 256);
const int V_RED_WEIGHT = (int)(0.713 * 256);
const int U_BLUE_WEIGHT = (int)(0.564 * 256);
int redIndex = 0;
int greenIndex = 1;
int blueIndex = 2;
if (format == GL_BGR) {
redIndex = 2;
blueIndex = 0;
}
for (int i = 0; i < encodedHeight; 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);
for (int j = 0; j < encodedWidth; j += 2) {
uchar* tl = _faceColor.ptr(i, j);
uchar* tr = _faceColor.ptr(i, j + 1);
uchar* bl = _faceColor.ptr(i + 1, j);
uchar* br = _faceColor.ptr(i + 1, j + 1);
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[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] *
Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] *
Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8;
ydest += 2;
*vdest++ = mask;
*udest++ = EIGHT_BIT_MIDPOINT;
int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex];
int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex];
int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex];
int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8;
*vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128;
*udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128;
}
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);
vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
// start the payload off with the aspect ratio (zero for full frame)
payload.append((const char*)&aspectRatio, sizeof(float));
// 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) {
while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) {
if (packet->kind == VPX_CODEC_CX_FRAME_PKT) {
// 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);
}
}
if (!depth.empty()) {
// convert with mask
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 < encodedHeight; i += 2) {
uchar* ydest = yline;
uchar* vdest = vline;
uchar* udest = uline;
for (int j = 0; j < encodedWidth; j += 2) {
ushort tl = *_smoothedFaceDepth.ptr<ushort>(i, j);
ushort tr = *_smoothedFaceDepth.ptr<ushort>(i, j + 1);
ushort bl = *_smoothedFaceDepth.ptr<ushort>(i + 1, j);
ushort br = *_smoothedFaceDepth.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));
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(float, _smoothedMeanFaceDepth),
Q_ARG(cv::RotatedRect, _smoothedFaceRect), Q_ARG(JointVector, joints));
Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(float, _smoothedMidFaceDepth),
Q_ARG(float, aspectRatio), Q_ARG(cv::RotatedRect, _smoothedFaceRect), Q_ARG(bool, !payload.isEmpty()),
Q_ARG(JointVector, joints));
}
bool FrameGrabber::init() {
@ -720,19 +770,19 @@ bool FrameGrabber::init() {
_depthGenerator.GetMetaData(_depthMetaData);
_imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24);
_imageGenerator.GetMetaData(_imageMetaData);
XnCallbackHandle userCallbacks, calibrationStartCallback, calibrationCompleteCallback;
_userGenerator.RegisterUserCallbacks(newUser, lostUser, 0, userCallbacks);
_userGenerator.GetSkeletonCap().RegisterToCalibrationStart(calibrationStarted, 0, calibrationStartCallback);
_userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback);
_userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER);
// make the depth viewpoint match that of the video image
if (_depthGenerator.IsCapabilitySupported(XN_CAPABILITY_ALTERNATIVE_VIEW_POINT)) {
_depthGenerator.GetAlternativeViewPointCap().SetViewPoint(_imageGenerator);
}
_xnContext.StartGeneratingAll();
return true;
}
@ -747,7 +797,7 @@ bool FrameGrabber::init() {
const int IDEAL_FRAME_HEIGHT = 240;
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_WIDTH, IDEAL_FRAME_WIDTH);
cvSetCaptureProperty(_capture, CV_CAP_PROP_FRAME_HEIGHT, IDEAL_FRAME_HEIGHT);
#ifdef __APPLE__
configureCamera(0x5ac, 0x8510, false, 0.975, 0.5, 1.0, 0.5, true, 0.5);
#else
@ -767,6 +817,17 @@ void FrameGrabber::updateHSVFrame(const Mat& frame, int format) {
inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask);
}
void FrameGrabber::destroyCodecs() {
if (_colorCodec.name != 0) {
vpx_codec_destroy(&_colorCodec);
_colorCodec.name = 0;
}
if (_depthCodec.name != 0) {
vpx_codec_destroy(&_depthCodec);
_depthCodec.name = 0;
}
}
Joint::Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected) :
isValid(true), position(position), rotation(rotation), projected(projected) {
}

View file

@ -38,20 +38,26 @@ typedef QVector<Joint> JointVector;
class Webcam : public QObject {
Q_OBJECT
public:
Webcam();
~Webcam();
FrameGrabber* getGrabber() { return _grabber; }
bool isActive() const { return _active; }
bool isSending() const { return _sending; }
GLuint getColorTextureID() const { return _colorTextureID; }
GLuint getDepthTextureID() const { return _depthTextureID; }
const cv::Size2f& getTextureSize() const { return _textureSize; }
float getAspectRatio() const { return _aspectRatio; }
const cv::RotatedRect& getFaceRect() const { return _faceRect; }
const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; }
const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; }
const JointVector& getEstimatedJoints() const { return _estimatedJoints; }
@ -60,56 +66,66 @@ public:
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 meanFaceDepth,
const cv::RotatedRect& faceRect, const JointVector& joints);
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:
QThread _grabberThread;
FrameGrabber* _grabber;
bool _enabled;
bool _active;
bool _sending;
GLuint _colorTextureID;
GLuint _depthTextureID;
cv::Size2f _textureSize;
float _aspectRatio;
cv::RotatedRect _faceRect;
cv::RotatedRect _initialFaceRect;
float _initialFaceDepth;
JointVector _joints;
uint64_t _startTimestamp;
int _frameCount;
uint64_t _lastFrameTimestamp;
glm::vec3 _estimatedPosition;
glm::vec3 _estimatedRotation;
JointVector _estimatedJoints;
bool _skeletonTrackingOn;
};
class FrameGrabber : public QObject {
Q_OBJECT
public:
FrameGrabber();
virtual ~FrameGrabber();
public slots:
void cycleVideoSendMode();
void reset();
void shutdown();
void grabFrame();
private:
enum VideoSendMode { NO_VIDEO, FACE_VIDEO, FULL_FRAME_VIDEO, VIDEO_SEND_MODE_COUNT };
bool init();
void updateHSVFrame(const cv::Mat& frame, int format);
void destroyCodecs();
bool _initialized;
VideoSendMode _videoSendMode;
CvCapture* _capture;
cv::CascadeClassifier _faceCascade;
cv::Mat _hsvFrame;
@ -118,16 +134,17 @@ private:
cv::Mat _backProject;
cv::Rect _searchWindow;
cv::Mat _grayDepthFrame;
float _smoothedMeanFaceDepth;
float _smoothedMidFaceDepth;
vpx_codec_ctx_t _colorCodec;
vpx_codec_ctx_t _depthCodec;
int _frameCount;
cv::Mat _faceColor;
cv::Mat _faceDepth;
cv::Mat _smoothedFaceDepth;
QByteArray _encodedFace;
cv::RotatedRect _smoothedFaceRect;
#ifdef HAVE_OPENNI
xn::Context _xnContext;
xn::DepthGenerator _depthGenerator;
@ -141,10 +158,10 @@ private:
class Joint {
public:
Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected);
Joint();
bool isValid;
glm::vec3 position;
glm::quat rotation;

View file

@ -101,6 +101,7 @@ Avatar::Avatar(Node* owningNode) :
_lastCollisionPosition(0, 0, 0),
_speedBrakes(false),
_isThrustOn(false),
_leadingAvatar(NULL),
_voxels(this)
{
// give the pointer to our head to inherited _headData variable from AvatarData
@ -314,10 +315,7 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
estimatedPosition = webcam->getEstimatedPosition();
// apply face data
_head.getFace().setColorTextureID(webcam->getColorTextureID());
_head.getFace().setDepthTextureID(webcam->getDepthTextureID());
_head.getFace().setTextureSize(webcam->getTextureSize());
_head.getFace().setTextureRect(webcam->getFaceRect());
_head.getFace().setFrameFromWebcam();
// compute and store the joint rotations
const JointVector& joints = webcam->getEstimatedJoints();
@ -334,7 +332,7 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook,
}
}
} else {
_head.getFace().setColorTextureID(0);
_head.getFace().clearFrame();
}
_head.setPitch(estimatedRotation.x * amplifyAngle.x + pitchFromTouch);
_head.setYaw(estimatedRotation.y * amplifyAngle.y + yawFromTouch);
@ -371,11 +369,24 @@ glm::vec3 Avatar::getUprightHeadPosition() const {
}
glm::vec3 Avatar::getUprightEyeLevelPosition() const {
const float EYE_UP_OFFSET = 0.36f;
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);
}
glm::vec3 Avatar::getEyePosition() {
const float EYE_UP_OFFSET = 0.36f;
const float EYE_FRONT_OFFSET = 0.8f;
glm::quat orientation = getWorldAlignedOrientation();
glm::vec3 up = orientation * IDENTITY_UP;
glm::vec3 front = orientation * IDENTITY_FRONT;
float scale = _scale * BODY_BALL_RADIUS_HEAD_BASE;
return getHead().getPosition() + up * scale * EYE_UP_OFFSET + front * scale * EYE_FRONT_OFFSET;
}
void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//
// Gather thrust information from keyboard and sensors to apply to avatar motion
@ -407,6 +418,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) {
@ -450,6 +488,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();
@ -478,6 +528,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();
@ -684,7 +741,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;
@ -892,21 +951,15 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
}
void Avatar::updateCollisionWithEnvironment(float deltaTime) {
glm::vec3 up = getBodyUpDirection();
float radius = _height * 0.125f;
const float ENVIRONMENT_SURFACE_ELASTICITY = 1.0f;
const float ENVIRONMENT_SURFACE_DAMPING = 0.01;
const float ENVIRONMENT_COLLISION_FREQUENCY = 0.05f;
const float VISIBLE_GROUND_COLLISION_VELOCITY = 0.2f;
glm::vec3 penetration;
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
_position - up * (_pelvisFloatingHeight - radius),
_position + up * (_height - _pelvisFloatingHeight - radius), radius, penetration)) {
float velocityTowardCollision = glm::dot(_velocity, glm::normalize(penetration));
if (velocityTowardCollision > VISIBLE_GROUND_COLLISION_VELOCITY) {
Application::getInstance()->setGroundPlaneImpact(1.0f);
}
_lastCollisionPosition = _position;
updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY);
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
@ -1300,9 +1353,15 @@ float Avatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
}
void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
// Render the body as balls and cones
if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
if (_head.getFace().isFullFrame()) {
// Render the full-frame video
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
if (alpha > 0.0f) {
_head.getFace().render(1.0f);
}
} else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) {
// Render the body as balls and cones
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
float alpha = getBallRenderAlpha(b, lookingInMirror);

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,21 +158,20 @@ 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;
glm::vec3 getEyePosition();
AvatarVoxelSystem* getVoxels() { return &_voxels; }
@ -255,7 +255,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

@ -17,6 +17,7 @@
#include "Avatar.h"
#include "Head.h"
#include "Face.h"
#include "Webcam.h"
#include "renderer/ProgramObject.h"
using namespace cv;
@ -25,7 +26,6 @@ ProgramObject* Face::_program = 0;
int Face::_texCoordCornerLocation;
int Face::_texCoordRightLocation;
int Face::_texCoordUpLocation;
int Face::_aspectRatioLocation;
GLuint Face::_vboID;
GLuint Face::_iboID;
@ -55,17 +55,25 @@ Face::~Face() {
}
}
void Face::setTextureRect(const cv::RotatedRect& textureRect) {
_textureRect = textureRect;
_aspectRatio = _textureRect.size.width / _textureRect.size.height;
void Face::setFrameFromWebcam() {
Webcam* webcam = Application::getInstance()->getWebcam();
if (webcam->isSending()) {
_colorTextureID = webcam->getColorTextureID();
_depthTextureID = webcam->getDepthTextureID();
_textureSize = webcam->getTextureSize();
_textureRect = webcam->getFaceRect();
_aspectRatio = webcam->getAspectRatio();
} else {
clearFrame();
}
}
void Face::clearFrame() {
_colorTextureID = 0;
}
int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
if (_colorCodec.name == 0) {
// initialize decoder context
vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0);
}
// skip the header
unsigned char* packetPosition = packetData;
int frameCount = *(uint32_t*)packetPosition;
@ -89,110 +97,135 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) {
int payloadSize = dataBytes - (packetPosition - packetData);
memcpy(_arrivingFrame.data() + frameOffset, packetPosition, payloadSize);
if ((_frameBytesRemaining -= payloadSize) <= 0) {
float aspectRatio = *(const float*)_arrivingFrame.constData();
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(&_colorCodec, &iterator)) != 0) {
// convert from YV12 to RGB
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];
const int RED_V_WEIGHT = (int)(1.403 * 256);
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 < image->d_h; i += 2) {
uchar* ysrc = yline;
uchar* vsrc = vline;
uchar* usrc = uline;
for (int j = 0; j < image->d_w; j += 2) {
uchar* tl = color.ptr(i, j);
uchar* tr = color.ptr(i, j + 1);
uchar* bl = color.ptr(i + 1, j);
uchar* br = color.ptr(i + 1, j + 1);
int v = *vsrc++ - 128;
int u = *usrc++ - 128;
int redOffset = (RED_V_WEIGHT * v) >> 8;
int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8;
int blueOffset = (BLUE_U_WEIGHT * u) >> 8;
int ytl = ysrc[0];
int ytr = ysrc[1];
int ybl = ysrc[image->w];
int ybr = ysrc[image->w + 1];
ysrc += 2;
tl[0] = ytl + redOffset;
tl[1] = ytl - greenOffset;
tl[2] = ytl + blueOffset;
tr[0] = ytr + redOffset;
tr[1] = ytr - greenOffset;
tr[2] = ytr + blueOffset;
bl[0] = ybl + redOffset;
bl[1] = ybl - greenOffset;
bl[2] = ybl + blueOffset;
br[0] = ybr + redOffset;
br[1] = ybr - greenOffset;
br[2] = ybr + blueOffset;
}
yline += image->stride[0] * 2;
vline += image->stride[1];
uline += image->stride[2];
if ((_frameBytesRemaining -= payloadSize) > 0) {
return dataBytes; // wait for the rest of the frame
}
if (frameSize == 0) {
// destroy the codecs, if we have any
destroyCodecs();
// disables video data
QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, Mat()),
Q_ARG(cv::Mat, Mat()), Q_ARG(float, 0.0f));
return dataBytes;
}
// the switch from full frame to not (or vice versa) requires us to reinit the codecs
float aspectRatio = *(const float*)_arrivingFrame.constData();
bool fullFrame = (aspectRatio == FULL_FRAME_ASPECT);
if (fullFrame != _lastFullFrame) {
destroyCodecs();
_lastFullFrame = fullFrame;
}
if (_colorCodec.name == 0) {
// initialize decoder context
vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 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(&_colorCodec, &iterator)) != 0) {
// convert from YV12 to RGB: see http://www.fourcc.org/yuv.php and
// http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor
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];
const int RED_V_WEIGHT = (int)(1.403 * 256);
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 < image->d_h; i += 2) {
uchar* ysrc = yline;
uchar* vsrc = vline;
uchar* usrc = uline;
for (int j = 0; j < image->d_w; j += 2) {
uchar* tl = color.ptr(i, j);
uchar* tr = color.ptr(i, j + 1);
uchar* bl = color.ptr(i + 1, j);
uchar* br = color.ptr(i + 1, j + 1);
int v = *vsrc++ - 128;
int u = *usrc++ - 128;
int redOffset = (RED_V_WEIGHT * v) >> 8;
int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8;
int blueOffset = (BLUE_U_WEIGHT * u) >> 8;
int ytl = ysrc[0];
int ytr = ysrc[1];
int ybl = ysrc[image->w];
int ybr = ysrc[image->w + 1];
ysrc += 2;
tl[0] = saturate_cast<uchar>(ytl + redOffset);
tl[1] = saturate_cast<uchar>(ytl - greenOffset);
tl[2] = saturate_cast<uchar>(ytl + blueOffset);
tr[0] = saturate_cast<uchar>(ytr + redOffset);
tr[1] = saturate_cast<uchar>(ytr - greenOffset);
tr[2] = saturate_cast<uchar>(ytr + blueOffset);
bl[0] = saturate_cast<uchar>(ybl + redOffset);
bl[1] = saturate_cast<uchar>(ybl - greenOffset);
bl[2] = saturate_cast<uchar>(ybl + blueOffset);
br[0] = saturate_cast<uchar>(ybr + redOffset);
br[1] = saturate_cast<uchar>(ybr - greenOffset);
br[2] = saturate_cast<uchar>(ybr + blueOffset);
}
Mat depth;
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),
Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio));
yline += image->stride[0] * 2;
vline += image->stride[1];
uline += image->stride[2];
}
Mat depth;
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),
Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio));
}
return dataBytes;
@ -208,9 +241,22 @@ bool Face::render(float alpha) {
glm::quat orientation = _owningHead->getOrientation();
glm::vec3 axis = glm::axis(orientation);
glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z);
float scale = BODY_BALL_RADIUS_HEAD_BASE * _owningHead->getScale();
glScalef(scale, scale, scale);
float aspect, xScale, zScale;
if (_aspectRatio == FULL_FRAME_ASPECT) {
aspect = _textureSize.width / _textureSize.height;
const float FULL_FRAME_SCALE = 0.5f;
xScale = FULL_FRAME_SCALE * _owningHead->getScale();
zScale = xScale * 0.3f;
} else {
aspect = _aspectRatio;
xScale = BODY_BALL_RADIUS_HEAD_BASE * _owningHead->getScale();
zScale = xScale * 1.5f;
glTranslatef(0.0f, -xScale * 0.75f, -xScale);
}
glScalef(xScale, xScale / aspect, zScale);
glColor4f(1.0f, 1.0f, 1.0f, alpha);
Point2f points[4];
@ -243,7 +289,6 @@ bool Face::render(float alpha) {
_texCoordCornerLocation = _program->uniformLocation("texCoordCorner");
_texCoordRightLocation = _program->uniformLocation("texCoordRight");
_texCoordUpLocation = _program->uniformLocation("texCoordUp");
_aspectRatioLocation = _program->uniformLocation("aspectRatio");
glGenBuffers(1, &_vboID);
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
@ -292,7 +337,6 @@ bool Face::render(float alpha) {
(points[3].x - points[0].x) / _textureSize.width, (points[3].y - points[0].y) / _textureSize.height);
_program->setUniformValue(_texCoordUpLocation,
(points[1].x - points[0].x) / _textureSize.width, (points[1].y - points[0].y) / _textureSize.height);
_program->setUniformValue(_aspectRatioLocation, _aspectRatio);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, 0);
@ -324,13 +368,13 @@ bool Face::render(float alpha) {
glBegin(GL_QUADS);
glTexCoord2f(points[0].x / _textureSize.width, points[0].y / _textureSize.height);
glVertex3f(0.5f, -0.5f / _aspectRatio, -0.5f);
glVertex3f(0.5f, -0.5f, 0.0f);
glTexCoord2f(points[1].x / _textureSize.width, points[1].y / _textureSize.height);
glVertex3f(0.5f, 0.5f / _aspectRatio, -0.5f);
glVertex3f(0.5f, 0.5f, 0.0f);
glTexCoord2f(points[2].x / _textureSize.width, points[2].y / _textureSize.height);
glVertex3f(-0.5f, 0.5f / _aspectRatio, -0.5f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glTexCoord2f(points[3].x / _textureSize.width, points[3].y / _textureSize.height);
glVertex3f(-0.5f, -0.5f / _aspectRatio, -0.5f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
@ -348,23 +392,40 @@ void Face::cycleRenderMode() {
}
void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRatio) {
if (color.empty()) {
// release our textures, if any; there's no more video
if (_colorTextureID != 0) {
glDeleteTextures(1, &_colorTextureID);
_colorTextureID = 0;
}
if (_depthTextureID != 0) {
glDeleteTextures(1, &_depthTextureID);
_depthTextureID = 0;
}
return;
}
if (_colorTextureID == 0) {
glGenTextures(1, &_colorTextureID);
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
}
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
bool recreateTextures = (_textureSize.width != color.cols || _textureSize.height != color.rows);
if (recreateTextures) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, color.cols, color.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
_textureSize = color.size();
_textureRect = RotatedRect(Point2f(color.cols * 0.5f, color.rows * 0.5f), _textureSize, 0.0f);
} else {
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, color.cols, color.rows, GL_RGB, GL_UNSIGNED_BYTE, color.ptr());
}
if (!depth.empty()) {
if (_depthTextureID == 0) {
glGenTextures(1, &_depthTextureID);
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
}
glBindTexture(GL_TEXTURE_2D, _depthTextureID);
if (recreateTextures) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, depth.cols, depth.rows, 0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, depth.ptr());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -380,3 +441,13 @@ void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRati
_aspectRatio = aspectRatio;
}
void Face::destroyCodecs() {
if (_colorCodec.name != 0) {
vpx_codec_destroy(&_colorCodec);
_colorCodec.name = 0;
}
if (_depthCodec.name != 0) {
vpx_codec_destroy(&_depthCodec);
_depthCodec.name = 0;
}
}

View file

@ -20,6 +20,8 @@
class Head;
class ProgramObject;
const float FULL_FRAME_ASPECT = 0.0f;
class Face : public QObject {
Q_OBJECT
@ -28,10 +30,10 @@ public:
Face(Head* owningHead);
~Face();
void setColorTextureID(GLuint colorTextureID) { _colorTextureID = colorTextureID; }
void setDepthTextureID(GLuint depthTextureID) { _depthTextureID = depthTextureID; }
void setTextureSize(const cv::Size2f& textureSize) { _textureSize = textureSize; }
void setTextureRect(const cv::RotatedRect& textureRect);
bool isFullFrame() const { return _colorTextureID != 0 && _aspectRatio == FULL_FRAME_ASPECT; }
void setFrameFromWebcam();
void clearFrame();
int processVideoMessage(unsigned char* packetData, size_t dataBytes);
@ -49,6 +51,8 @@ private:
enum RenderMode { MESH, POINTS, RENDER_MODE_COUNT };
void destroyCodecs();
Head* _owningHead;
RenderMode _renderMode;
GLuint _colorTextureID;
@ -59,6 +63,7 @@ private:
vpx_codec_ctx_t _colorCodec;
vpx_codec_ctx_t _depthCodec;
bool _lastFullFrame;
QByteArray _arrivingFrame;
int _frameCount;
@ -68,7 +73,6 @@ private:
static int _texCoordCornerLocation;
static int _texCoordRightLocation;
static int _texCoordUpLocation;
static int _aspectRatioLocation;
static GLuint _vboID;
static GLuint _iboID;
};

View file

@ -14,7 +14,7 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
const bool SHOW_LEAP_HAND = false;
const bool SHOW_LEAP_HAND = true;
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 root 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,22 +177,55 @@ 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();
}
}
// Draw the finger root cones
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
@ -200,7 +257,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 +286,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 +302,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 +334,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 +351,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 +372,8 @@ void Hand::updateRaveGloveParticles(float deltaTime) {
}
}
void Hand::setRaveGloveMode(int mode) {
_raveGloveMode = mode;
@ -376,7 +390,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 +664,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 +686,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();
};

View file

@ -17,7 +17,7 @@
#include "renderer/ProgramObject.h"
using namespace std;
const int MOHAWK_TRIANGLES = 50;
const bool USING_PHYSICAL_MOHAWK = true;
const float EYE_RIGHT_OFFSET = 0.27f;
@ -36,6 +36,11 @@ const float HAIR_GRAVITY_FORCE = 0.05f;
const float HAIR_DRAG = 10.0f;
const float HAIR_LENGTH = 0.09f;
const float HAIR_THICKNESS = 0.03f;
const float NOSE_LENGTH = 0.025;
const float NOSE_WIDTH = 0.03;
const float NOSE_HEIGHT = 0.034;
const float NOSE_UP_OFFSET = -0.07;
const float NOSE_UPTURN = 0.005;
const float IRIS_RADIUS = 0.007;
const float IRIS_PROTRUSION = 0.0145f;
const char IRIS_TEXTURE_FILENAME[] = "resources/images/iris.png";
@ -54,7 +59,7 @@ Head::Head(Avatar* owningAvatar) :
_rotation(0.0f, 0.0f, 0.0f),
_leftEyePosition(0.0f, 0.0f, 0.0f),
_rightEyePosition(0.0f, 0.0f, 0.0f),
_eyeLevelPosition(0.0f, 0.0f, 0.0f),
_eyePosition(0.0f, 0.0f, 0.0f),
_leftEyeBrowPosition(0.0f, 0.0f, 0.0f),
_rightEyeBrowPosition(0.0f, 0.0f, 0.0f),
_leftEarPosition(0.0f, 0.0f, 0.0f),
@ -100,7 +105,7 @@ void Head::init() {
_irisProgram->setUniformValue("texture", 0);
_eyePositionLocation = _irisProgram->uniformLocation("eyePosition");
QImage image = QImage(IRIS_TEXTURE_FILENAME).convertToFormat(QImage::Format_ARGB32);
glGenTextures(1, &_irisTextureID);
@ -262,31 +267,40 @@ void Head::calculateGeometry() {
glm::vec3 up = orientation * IDENTITY_UP;
glm::vec3 front = orientation * IDENTITY_FRONT;
float scale = _scale * BODY_BALL_RADIUS_HEAD_BASE;
//calculate the eye positions
_leftEyePosition = _position
- right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_RIGHT_OFFSET
+ up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET
+ front * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_FRONT_OFFSET;
- right * scale * EYE_RIGHT_OFFSET
+ up * scale * EYE_UP_OFFSET
+ front * scale * EYE_FRONT_OFFSET;
_rightEyePosition = _position
+ right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_RIGHT_OFFSET
+ up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET
+ front * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_FRONT_OFFSET;
+ right * scale * EYE_RIGHT_OFFSET
+ up * scale * EYE_UP_OFFSET
+ front * scale * EYE_FRONT_OFFSET;
_eyeLevelPosition = _rightEyePosition - right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_RIGHT_OFFSET;
_eyePosition = _rightEyePosition - right * scale * EYE_RIGHT_OFFSET;
//calculate the eyebrow positions
_leftEyeBrowPosition = _leftEyePosition;
_rightEyeBrowPosition = _rightEyePosition;
//calculate the ear positions
_leftEarPosition = _position - right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EAR_RIGHT_OFFSET;
_rightEarPosition = _position + right * _scale * BODY_BALL_RADIUS_HEAD_BASE * EAR_RIGHT_OFFSET;
_leftEarPosition = _position - right * scale * EAR_RIGHT_OFFSET;
_rightEarPosition = _position + right * scale * EAR_RIGHT_OFFSET;
//calculate the mouth position
_mouthPosition = _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * MOUTH_UP_OFFSET
+ front * _scale * BODY_BALL_RADIUS_HEAD_BASE;
}
_mouthPosition = _position + up * scale * MOUTH_UP_OFFSET + front * scale;
// calculate nose geometry
glm::vec3 noseBase = _position + front * 0.95f * scale + up * NOSE_UP_OFFSET * scale;
_nose.top = noseBase + up * _scale * NOSE_HEIGHT;
_nose.left = noseBase - right * _scale * NOSE_WIDTH * ONE_HALF;
_nose.right = noseBase + right * _scale * NOSE_WIDTH * ONE_HALF;
_nose.front = noseBase + front * _scale * NOSE_LENGTH
+ up * _scale * NOSE_UPTURN;
}
void Head::render(float alpha) {
@ -302,7 +316,8 @@ void Head::render(float alpha) {
renderHeadSphere();
renderEyeBalls();
renderEars();
renderMouth();
renderMouth();
renderNose();
renderEyeBrows();
}
@ -441,6 +456,42 @@ void Head::renderEars() {
glPopMatrix();
}
void Head::renderNose() {
glm::vec3 bridgeVector = _nose.front - _nose.top;
glm::vec3 leftvector = _nose.front - _nose.left;
glm::vec3 rightvector = _nose.front - _nose.right;
glm::vec3 leftNormal (glm::normalize(glm::cross(leftvector, bridgeVector)));
glm::vec3 rightNormal (glm::normalize(glm::cross(bridgeVector, rightvector )));
glm::vec3 bottomNormal(glm::normalize(glm::cross(rightvector, leftvector )));
glColor4f(_skinColor.x, _skinColor.y, _skinColor.z, _renderAlpha);
glBegin(GL_TRIANGLES);
glNormal3f(leftNormal.x, leftNormal.y, leftNormal.z);
glVertex3f(_nose.top.x, _nose.top.y, _nose.top.z );
glVertex3f(_nose.left.x, _nose.left.y, _nose.left.z );
glVertex3f(_nose.front.x, _nose.front.y, _nose.front.z );
glNormal3f(rightNormal.x, rightNormal.y, rightNormal.z);
glVertex3f(_nose.top.x, _nose.top.y, _nose.top.z );
glVertex3f(_nose.right.x, _nose.right.y, _nose.right.z );
glVertex3f(_nose.front.x, _nose.front.y, _nose.front.z );
glNormal3f(bottomNormal.x, bottomNormal.y, bottomNormal.z);
glVertex3f(_nose.left.x, _nose.left.y, _nose.left.z );
glVertex3f(_nose.right.x, _nose.right.y, _nose.right.z );
glVertex3f(_nose.front.x, _nose.front.y, _nose.front.z );
glEnd();
}
void Head::renderMouth() {
float s = sqrt(_averageLoudness);

View file

@ -61,7 +61,7 @@ public:
float getScale() const { return _scale; }
glm::vec3 getPosition() const { return _position; }
const glm::vec3& getEyeLevelPosition() const { return _eyeLevelPosition; }
const glm::vec3& getEyePosition() const { return _eyePosition; }
glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
glm::vec3 getUpDirection () const { return getOrientation() * IDENTITY_UP; }
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
@ -92,6 +92,14 @@ private:
glm::vec3 endVelocity;
};
struct Nose
{
glm::vec3 top;
glm::vec3 left;
glm::vec3 right;
glm::vec3 front;
};
float _renderAlpha;
bool _returnHeadToCenter;
glm::vec3 _skinColor;
@ -99,12 +107,13 @@ private:
glm::vec3 _rotation;
glm::vec3 _leftEyePosition;
glm::vec3 _rightEyePosition;
glm::vec3 _eyeLevelPosition;
glm::vec3 _eyePosition;
glm::vec3 _leftEyeBrowPosition;
glm::vec3 _rightEyeBrowPosition;
glm::vec3 _leftEarPosition;
glm::vec3 _rightEarPosition;
glm::vec3 _mouthPosition;
Nose _nose;
float _scale;
float _browAudioLift;
glm::vec3 _gravity;
@ -141,6 +150,7 @@ private:
void renderEyeBalls();
void renderEyeBrows();
void renderEars();
void renderNose();
void renderMouth();
void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void calculateGeometry();

View file

@ -12,7 +12,6 @@
#define __interface__world__
const float WORLD_SIZE = 10.0;
#define PIf 3.14159265f
const float GRAVITY_EARTH = 9.80665f;

View file

@ -8,13 +8,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
set(TARGET_NAME avatars)
find_package(Qt5Core REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Core)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
@ -22,4 +18,4 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi voxels library
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})

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

@ -149,6 +149,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)
@ -263,17 +265,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);
}
}

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

@ -51,12 +51,18 @@ _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;
}
fingerVectors.push_back(palm.getRawPosition());
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());
@ -83,7 +89,8 @@ void HandData::decodeRemoteData(const std::vector<glm::vec3>& fingerVectors) {
palm.setRawPosition(fingerVectors[vectorIndex++]);
palm.setRawNormal(fingerVectors[vectorIndex++]);
for (size_t f = 0; f < NUM_FINGERS_PER_HAND; ++f) {
FingerData& finger = palm.getFingers()[i];
FingerData& finger = palm.getFingers()[f];
finger.setActive(true);
finger.setRawTipPosition(fingerVectors[vectorIndex++]);
finger.setRawRootPosition(fingerVectors[vectorIndex++]);
}

View file

@ -6,13 +6,9 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
set(TARGET_NAME shared)
project(${TARGET_NAME})
find_package(Qt5Core REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Core)
set(EXTERNAL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external)
if (WIN32)
@ -32,4 +28,4 @@ endif (UNIX AND NOT APPLE)
# include GLM
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include_glm(${TARGET_NAME} ${ROOT_DIR})

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

@ -33,7 +33,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

@ -8,9 +8,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
set(TARGET_NAME voxels)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Widgets)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})

View file

@ -0,0 +1,194 @@
//
// 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);
}
JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHexCodes) {
_rootOctalCode = hexStringToOctalCode(QString(rootHexCode));
QString endNodesHexStrings(endNodesHexCodes);
QString delimiterPattern(",");
QStringList endNodeList = endNodesHexStrings.split(delimiterPattern);
for (int i = 0; i < endNodeList.size(); i++) {
QString endNodeHexString = endNodeList.at(i);
unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString);
//printOctalCode(endNodeOctcode);
_endNodes.push_back(endNodeOctcode);
}
}
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,47 @@
//
// 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(const char* rootHextString, const char* endNodesHextString);
~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

@ -41,4 +41,6 @@ const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
const float SIXTY_FPS_IN_MILLISECONDS = 1000.0f / 60.0f;
const float VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0f; // once a second is fine
const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 50 milliseconds
#endif

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;

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

@ -0,0 +1,86 @@
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
--jurisdictionRoot [hex string of root octcode]
Tells the server to honor jurisdiction from the specified root node and below
--jurisdictionEndNodes [<octcode>(<,octcode>...)]
Tells the server to honor jurisdiction from the root down to the octcodes included in the comma separated list
--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;
@ -61,9 +63,12 @@ bool wantColorRandomizer = false;
bool debugVoxelSending = false;
bool shouldShowAnimationDebug = false;
bool displayVoxelStats = false;
bool debugVoxelReceiving = false;
EnvironmentData environmentData[3];
int receivedPacketCount = 0;
JurisdictionMap* jurisdiction = NULL;
void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
// randomly generate children for this node
@ -291,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,
@ -375,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");
}
@ -431,7 +436,45 @@ int main(int argc, const char * argv[]) {
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);
} else {
const char* JURISDICTION_ROOT = "--jurisdictionRoot";
const char* jurisdictionRoot = getCmdOption(argc, argv, JURISDICTION_ROOT);
if (jurisdictionRoot) {
printf("jurisdictionRoot=%s\n", jurisdictionRoot);
}
const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes";
const char* jurisdictionEndNodes = getCmdOption(argc, argv, JURISDICTION_ENDNODES);
if (jurisdictionEndNodes) {
printf("jurisdictionEndNodes=%s\n", jurisdictionEndNodes);
}
if (jurisdictionRoot || jurisdictionEndNodes) {
jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes);
}
}
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort);
setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line
@ -455,6 +498,10 @@ int main(int argc, const char * argv[]) {
::debugVoxelSending = cmdOptionExists(argc, argv, DEBUG_VOXEL_SENDING);
printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending));
const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving";
::debugVoxelReceiving = cmdOptionExists(argc, argv, DEBUG_VOXEL_RECEIVING);
printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
::shouldShowAnimationDebug = cmdOptionExists(argc, argv, WANT_ANIMATION_DEBUG);
printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug));
@ -473,8 +520,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);
@ -584,12 +642,20 @@ int main(int argc, const char * argv[]) {
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
::shouldShowAnimationDebug);
::receivedPacketCount++;
unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
if (::shouldShowAnimationDebug) {
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
receivedBytes,itemNumber);
}
if (::debugVoxelReceiving) {
printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
::receivedPacketCount, receivedBytes,itemNumber);
}
int atByte = numBytesPacketHeader + sizeof(itemNumber);
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
while (atByte < receivedBytes) {
@ -692,6 +758,10 @@ int main(int argc, const char * argv[]) {
pthread_join(sendVoxelThread, NULL);
pthread_mutex_destroy(&::treeLock);
if (jurisdiction) {
delete jurisdiction;
}
return 0;
}