mirror of
https://github.com/overte-org/overte.git
synced 2025-08-13 08:36:46 +02:00
resolve conflicts on merge with upstream master
This commit is contained in:
commit
a4ea0f035a
46 changed files with 2326 additions and 805 deletions
22
interface/resources/images/hifi-interface-tools-v2-pie.svg
Normal file
22
interface/resources/images/hifi-interface-tools-v2-pie.svg
Normal 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 |
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
138
interface/src/PieMenu.cpp
Normal 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
59
interface/src/PieMenu.h
Normal 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__) */
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,9 @@ public:
|
|||
void setRaveGloveEffectsMode(QKeyEvent* event);
|
||||
|
||||
// getters
|
||||
const glm::vec3& getLeapBallPosition (int ball) const { return _leapBalls[ball].position;}
|
||||
bool isRaveGloveActive () const { return _isRaveGloveActive; }
|
||||
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
||||
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
|
||||
bool isRaveGloveActive() const { return _isRaveGloveActive; }
|
||||
|
||||
private:
|
||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
||||
|
@ -84,7 +85,8 @@ private:
|
|||
float _renderAlpha;
|
||||
bool _lookingInMirror;
|
||||
glm::vec3 _ballColor;
|
||||
std::vector<HandBall> _leapBalls;
|
||||
std::vector<HandBall> _leapFingerTipBalls;
|
||||
std::vector<HandBall> _leapFingerRootBalls;
|
||||
|
||||
// private methods
|
||||
void setLeapHands(const std::vector<glm::vec3>& handPositions,
|
||||
|
@ -92,8 +94,10 @@ private:
|
|||
|
||||
void renderRaveGloveStage();
|
||||
void setRaveGloveMode(int mode);
|
||||
void renderHandSpheres();
|
||||
void renderFingerTrails();
|
||||
void renderLeapHandSpheres();
|
||||
void renderLeapHands();
|
||||
void renderLeapHand(PalmData& hand);
|
||||
void renderLeapFingerTrails();
|
||||
void calculateGeometry();
|
||||
};
|
||||
|
||||
|
|
|
@ -17,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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#define __interface__world__
|
||||
|
||||
|
||||
const float WORLD_SIZE = 10.0;
|
||||
#define PIf 3.14159265f
|
||||
|
||||
const float GRAVITY_EARTH = 9.80665f;
|
||||
|
|
|
@ -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
17
libraries/avatars/src/AvatarData.cpp
Executable file → Normal 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
9
libraries/avatars/src/HandData.cpp
Executable file → Normal 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++]);
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
|
194
libraries/voxels/src/JurisdictionMap.cpp
Normal file
194
libraries/voxels/src/JurisdictionMap.cpp
Normal 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;
|
||||
}
|
47
libraries/voxels/src/JurisdictionMap.h
Normal file
47
libraries/voxels/src/JurisdictionMap.h
Normal 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__) */
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Tags.h
|
||||
// Tags.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Clement Brisset on 7/3/13.
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
86
voxel-server/src/README
Normal 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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue