From d6c70ea6ca1bae1ff8fc020442515f8d138be713 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 25 Jun 2013 00:24:21 +0200 Subject: [PATCH 01/63] #19368 - Improve editing/building workflow in Interface --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 75 ++++++++++++++++++-- interface/src/Application.h | 5 ++ interface/src/Swatch.cpp | 125 +++++++++++++++++++++++++++++++++ interface/src/Swatch.h | 30 ++++++++ interface/src/Tool.cpp | 45 ++++++++++++ interface/src/Tool.h | 41 +++++++++++ interface/src/ToolsPalette.cpp | 60 ++++++++++++++++ interface/src/ToolsPalette.h | 34 +++++++++ interface/src/Util.h | 3 + 10 files changed, 413 insertions(+), 7 deletions(-) create mode 100644 interface/src/Swatch.cpp create mode 100644 interface/src/Swatch.h create mode 100644 interface/src/Tool.cpp create mode 100644 interface/src/Tool.h create mode 100644 interface/src/ToolsPalette.cpp create mode 100644 interface/src/ToolsPalette.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 148e6819be..428a686900 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -65,7 +65,7 @@ if (APPLE) endif (APPLE) -find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL) +find_package(Qt4 REQUIRED QtCore QtGui QtNetwork QtOpenGL QtSvg) include(${QT_USE_FILE}) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${QT_QTGUI_INCLUDE_DIR}") diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6954339cee..e2dc5a8490 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8,6 +8,7 @@ #include #include +#include #ifdef _WIN32 #include "Syssocket.h" @@ -60,6 +61,7 @@ #include "Util.h" #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" +#include "Swatch.h" using namespace std; @@ -636,7 +638,16 @@ void Application::keyPressEvent(QKeyEvent* event) { deleteVoxelUnderCursor(); } break; - + case Qt::Key_1: + case Qt::Key_2: + case Qt::Key_3: + case Qt::Key_4: + case Qt::Key_5: + case Qt::Key_6: + case Qt::Key_7: + case Qt::Key_8: + _swatch->handleEvent(event->key(), _eyedropperMode->isChecked()); + break; default: event->ignore(); break; @@ -1296,10 +1307,10 @@ void Application::initMenu() { _voxelModeActions->setExclusive(false); // exclusivity implies one is always checked (_addVoxelMode = voxelMenu->addAction( - "Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_A))->setCheckable(true); + "Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_V))->setCheckable(true); _voxelModeActions->addAction(_addVoxelMode); (_deleteVoxelMode = voxelMenu->addAction( - "Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_D))->setCheckable(true); + "Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_R))->setCheckable(true); _voxelModeActions->addAction(_deleteVoxelMode); (_colorVoxelMode = voxelMenu->addAction( "Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_B))->setCheckable(true); @@ -1310,7 +1321,7 @@ void Application::initMenu() { (_eyedropperMode = voxelMenu->addAction( "Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | 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); @@ -1437,6 +1448,15 @@ void Application::init() { loadSettings(); sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); + + + _swatch = new Swatch(_voxelPaintColor); + _palette.init(); + _palette.addAction(_addVoxelMode, 0, 0); + _palette.addAction(_deleteVoxelMode, 0, 1); + _palette.addTool(_swatch); + _palette.addAction(_colorVoxelMode, 0, 2); + _palette.addAction(_eyedropperMode, 0, 3); } const float MAX_AVATAR_EDIT_VELOCITY = 1.0f; @@ -1643,7 +1663,7 @@ void Application::update(float deltaTime) { } } } - + // Update audio stats for procedural sounds #ifndef _WIN32 _audio.setLastAcceleration(_myAvatar.getThrust()); @@ -2158,7 +2178,50 @@ void Application::displayOverlay() { // render the webcam input frame _webcam.renderPreview(_glWidget->width(), _glWidget->height()); - + + _palette.render(_glWidget->width(), _glWidget->height()); + + if (_eyedropperMode->isChecked()) { + QColor color(_voxelPaintColor->data().value()); + TextRenderer textRenderer(SANS_FONT_FAMILY, -1, 100); + const char* line1("Assign this color to a swatch"); + const char* line2("by choosing a key from 1 to 8."); + double step(0.05f); + int left(_glWidget->width()/100.0f); + int top(_glWidget->height()*7.0f/10.0f); + double margin(10.0f); + + glBegin(GL_POLYGON); + glColor3f(0.0f, 0.0f, 0.0f); + for (double a(M_PI); a < 1.5f*M_PI; a += step) { + glVertex2f(left + margin*cos(a), top + margin*sin(a)); + } + for (double a(1.5f*M_PI); a < 2.0f*M_PI; a += step) { + glVertex2f(left + 300 + margin*cos(a), top + margin*sin(a)); + } + for (double a(0.0f); a < 0.5f*M_PI; a += step) { + glVertex2f(left + 300 + margin*cos(a), top + 30 + margin*sin(a)); + } + for (double a(0.5f*M_PI); a < 1.0f*M_PI; a += step) { + glVertex2f(left + margin*cos(a), top + 30 + margin*sin(a)); + } + glEnd(); + + glBegin(GL_QUADS); + glColor3f(color.redF(), + color.greenF(), + color.blueF()); + glVertex2f(left, top); + glVertex2f(left + 64, top); + glVertex2f(left + 64, top + 30); + glVertex2f(left, top + 30); + glEnd(); + + glColor3f(1.0f, 1.0f, 1.0f); + textRenderer.draw(left + 74, top + 10, line1); + textRenderer.draw(left + 74, top + 30, line2); + } + glPopMatrix(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 54fdf1c347..de4a8e685e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -33,6 +33,8 @@ #include "VoxelSystem.h" #include "Webcam.h" #include "ui/ChatEntry.h" +#include "ToolsPalette.h" +#include "Swatch.h" class QAction; class QActionGroup; @@ -309,6 +311,9 @@ private: int _packetsPerSecond; int _bytesPerSecond; int _bytesCount; + + ToolsPalette _palette; + Swatch* _swatch; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp new file mode 100644 index 0000000000..572046d5e1 --- /dev/null +++ b/interface/src/Swatch.cpp @@ -0,0 +1,125 @@ +#include "Swatch.h" +#include + +Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), + _selected(1), + _margin(4), + _textRenderer(SANS_FONT_FAMILY, -1, 100) { + _width = 62; + _height = 30; + + _colors[0].setRgb(128, 128, 128); + _colors[1].setRgb(255, 0, 0); + _colors[2].setRgb(0, 255, 0); + _colors[3].setRgb(0, 0, 255); + _colors[4].setRgb(255, 0, 255); + _colors[5].setRgb(255, 255, 0); + _colors[6].setRgb(0, 255, 255); + _colors[7].setRgb(0, 0, 0); + + QPixmap map(16, 16); + map.fill(_colors[0]); + _action->setData(_colors[_selected - 1]) ; + _action->setIcon(map); +} + +void Swatch::handleEvent(int key, bool getColor) { + switch (key) { + case Qt::Key_1: + _selected = 1; + break; + case Qt::Key_2: + _selected = 2; + break; + case Qt::Key_3: + _selected = 3; + break; + case Qt::Key_4: + _selected = 4; + break; + case Qt::Key_5: + _selected = 5; + break; + case Qt::Key_6: + _selected = 6; + break; + case Qt::Key_7: + _selected = 7; + break; + case Qt::Key_8: + _selected = 8; + break; + default: + break; + } + + if (getColor) { + _colors[_selected - 1] = _action->data().value(); + } + else { + QPixmap map(16, 16); + map.fill(_colors[_selected - 1]); + _action->setData(_colors[_selected - 1]) ; + _action->setIcon(map); + } +} + +void Swatch::render(int screenWidth, int screenHeight) { + char str[2]; + + glBegin(GL_QUADS); + glColor3f(0.0f, 0.0f, 0.0f); + glVertex2f(0, 8*(_height - _margin) + _margin); + glVertex2f(_width, 8*(_height - _margin) + _margin); + glVertex2f(_width, 0); + glVertex2f(0, 0); + glEnd(); + + for (unsigned int i(0); i < SWATCH_SIZE; ++i) { + glBegin(GL_QUADS); + glColor3f(_colors[i].redF(), + _colors[i].greenF(), + _colors[i].blueF()); + glVertex2f(_margin, (i + 1)*(_height - _margin)); + glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); + glVertex2f(_width - _margin, i*(_height - _margin) + _margin); + glVertex2f(_margin, i*(_height - _margin) + _margin); + glEnd(); + + if (_colors[i].lightness() < 100) { + glBegin(GL_LINES); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex2f(_margin, (i + 1)*(_height - _margin)); + glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); + + glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); + glVertex2f(_width - _margin, i*(_height - _margin) + _margin); + + glVertex2f(_width - _margin, i*(_height - _margin) + _margin); + glVertex2f(_margin, i*(_height - _margin) + _margin); + + glVertex2f(_margin, i*(_height - _margin) + _margin); + glVertex2f(_margin, (i + 1)*(_height - _margin)); + glEnd(); + + + } + else { + glColor3f(0.0f, 0.0f, 0.0f); + } + + if (_selected == i + 1) { + glBegin(GL_TRIANGLES); + glVertex2f(_margin, (i + 1)*(_height - _margin) - _margin); + glVertex2f(_width/4 - _margin, i*(_height - _margin) + _height/2.0f); + glVertex2f(_margin, i*(_height - _margin) + _margin + _margin); + glEnd(); + } + + sprintf(str, "%d", i + 1); + _textRenderer.draw(3*_width/4, (i + 1)*(_height - _margin) - 6, str); + } + + glTranslated(0, 8*(_height - _margin) + _margin + 5, 0); + +} diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h new file mode 100644 index 0000000000..292b011882 --- /dev/null +++ b/interface/src/Swatch.h @@ -0,0 +1,30 @@ +// +// Swatch.h +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Swatch__ +#define __interface__Swatch__ + +#define SWATCH_SIZE 8 + +#include "Tool.h" +#include "ui/TextRenderer.h" + +class Swatch : public Tool { +public: + Swatch(QAction* action); + void render(int screenWidth, int screenHeight); + void handleEvent(int key, bool getColor); + +private: + TextRenderer _textRenderer; + QColor _colors[SWATCH_SIZE]; + int _selected; + + int _margin; +}; + +#endif /* defined(__interface__Swatch__) */ diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp new file mode 100644 index 0000000000..67b0262f7d --- /dev/null +++ b/interface/src/Tool.cpp @@ -0,0 +1,45 @@ +#include "Tool.h" + +#include +#include +#include + +Tool::Tool(QAction *action, GLuint texture, int x, int y) : _texture(texture), + _action(action), + _x(x), + _y(y), + _width(62), + _height(40) { +} + +void Tool::render(int screenWidth, int screenHeight) { + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, _texture); + + + if (_action == 0 || _action->isChecked()) { + glColor3f(1.0f, 1.0f, 1.0f); // reset gl color + } + else { + glColor3f(0.3f, 0.3f, 0.3f); + } + + glBegin(GL_QUADS); + glTexCoord2f(_x/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); + glVertex2f(0, _height); + + glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); + glVertex2f(_width, _height); + + glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); + glVertex2f(_width, 0); + + glTexCoord2f(_x/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); + glVertex2f(0, 0); + glEnd(); + + glDisable(GL_TEXTURE_2D); + + glTranslated(0, _height + 5, 0); +} diff --git a/interface/src/Tool.h b/interface/src/Tool.h new file mode 100644 index 0000000000..8fdc723fd9 --- /dev/null +++ b/interface/src/Tool.h @@ -0,0 +1,41 @@ +// +// Tool.h +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Tool__ +#define __interface__Tool__ + +#include + +#include "InterfaceConfig.h" +#include "Util.h" + +class QAction; + +// tool size +static double _width; +static double _height; + +class Tool { +public: + Tool(QAction *action, GLuint texture, int x, int y); + + virtual void render(int screenWidth, int screenHeight); + +protected: + QAction* _action; + GLuint _texture; + + // position in the SVG grid + double _x; + double _y; + + // tool size + double _width; + double _height; +}; + +#endif /* defined(__interface__Tool__) */ diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp new file mode 100644 index 0000000000..570d9e3f2c --- /dev/null +++ b/interface/src/ToolsPalette.cpp @@ -0,0 +1,60 @@ +#include "ToolsPalette.h" + +#include +#include +#include + +ToolsPalette::ToolsPalette() { + // Load SVG + QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); + + // Prepare a QImage with desired characteritisc + QImage image(124, 400, QImage::Format_ARGB32); + + // Get QPainter that paints to the image + QPainter painter(&image); + renderer.render(&painter); + + //get the OpenGL-friendly image + _textureImage = QGLWidget::convertToGLFormat(image); +} + +void ToolsPalette::init(int top, int left) { + _top = top; + _left = left; + + 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 ToolsPalette::addAction(QAction* action, int x, int y) { + Tool* tmp = new Tool(action, _textureID, x, y); + _tools.push_back(tmp); +} + +void ToolsPalette::addTool(Tool *tool) { + _tools.push_back(tool); +} + +void ToolsPalette::render(int screenWidth, int screenHeight) { + glPushMatrix(); + glTranslated(_left, _top, 0); + + for (unsigned int i(0); i < _tools.size(); ++i) { + _tools[i]->render(screenWidth, screenHeight); + } + + glPopMatrix(); +} diff --git a/interface/src/ToolsPalette.h b/interface/src/ToolsPalette.h new file mode 100644 index 0000000000..4856048a01 --- /dev/null +++ b/interface/src/ToolsPalette.h @@ -0,0 +1,34 @@ +// +// ToolsPalette.h +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__ToolsPalette__ +#define __interface__ToolsPalette__ + +#include + +#include + +class ToolsPalette { +public: + ToolsPalette(); + + void init(int top = 200, int left = 10); + void addAction(QAction* action, int x, int y); + void addTool(Tool *tool); + void render(int screenWidth, int screenHeight); + + +private: + QImage _textureImage; + GLuint _textureID; + std::vector _tools; + + int _top; + int _left; +}; + +#endif /* defined(__interface__ToolsPalette__) */ diff --git a/interface/src/Util.h b/interface/src/Util.h index 24cbad9e68..4a914eb565 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -25,6 +25,9 @@ // the standard mono font family #define MONO_FONT_FAMILY "Courier" +// Number of rows and columns in the SVG file for the tool palette +#define TOOLS_ROWS 10 +#define TOOLS_COLS 2 void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); From ee15739e7e4b2477d80c6d38968b934f0d787f2b Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 25 Jun 2013 01:28:31 +0200 Subject: [PATCH 02/63] few fixes on the job 19368 --- interface/src/Application.cpp | 17 +++++++++-------- interface/src/Swatch.cpp | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a8cc750cae..970344e41f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1359,7 +1359,7 @@ void Application::initMenu() { QMenu* renderMenu = menuBar->addMenu("Render"); (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); _renderVoxels->setChecked(true); - _renderVoxels->setShortcut(Qt::Key_V); + _renderVoxels->setShortcut(Qt::CTRL | Qt::Key_V); (_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true); (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); _renderStarsOn->setChecked(true); @@ -1395,19 +1395,19 @@ void Application::initMenu() { _voxelModeActions->setExclusive(false); // exclusivity implies one is always checked (_addVoxelMode = voxelMenu->addAction( - "Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_V))->setCheckable(true); + "Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_V))->setCheckable(true); _voxelModeActions->addAction(_addVoxelMode); (_deleteVoxelMode = voxelMenu->addAction( - "Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_R))->setCheckable(true); + "Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_R))->setCheckable(true); _voxelModeActions->addAction(_deleteVoxelMode); (_colorVoxelMode = voxelMenu->addAction( - "Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_B))->setCheckable(true); + "Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_B))->setCheckable(true); _voxelModeActions->addAction(_colorVoxelMode); (_selectVoxelMode = voxelMenu->addAction( - "Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_S))->setCheckable(true); + "Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_O))->setCheckable(true); _voxelModeActions->addAction(_selectVoxelMode); (_eyedropperMode = voxelMenu->addAction( - "Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::CTRL | Qt::Key_G))->setCheckable(true); + "Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_G))->setCheckable(true); _voxelModeActions->addAction(_eyedropperMode); voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut); @@ -1552,6 +1552,7 @@ void Application::init() { _palette.addTool(_swatch); _palette.addAction(_colorVoxelMode, 0, 2); _palette.addAction(_eyedropperMode, 0, 3); + _palette.addAction(_selectVoxelMode, 0, 4); } const float MAX_AVATAR_EDIT_VELOCITY = 1.0f; @@ -2297,8 +2298,8 @@ void Application::displayOverlay() { const char* line1("Assign this color to a swatch"); const char* line2("by choosing a key from 1 to 8."); double step(0.05f); - int left(_glWidget->width()/100.0f); - int top(_glWidget->height()*7.0f/10.0f); + int left((_glWidget->width() - 320)/2); + int top(_glWidget->height()/40.0f); double margin(10.0f); glBegin(GL_POLYGON); diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index 572046d5e1..5989a2e512 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -86,7 +86,7 @@ void Swatch::render(int screenWidth, int screenHeight) { glVertex2f(_margin, i*(_height - _margin) + _margin); glEnd(); - if (_colors[i].lightness() < 100) { + if (_colors[i].lightness() < 50) { glBegin(GL_LINES); glColor3f(1.0f, 1.0f, 1.0f); glVertex2f(_margin, (i + 1)*(_height - _margin)); From 1d578d5b0e883fd7e033c6426a8a516a4121ebbd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Jun 2013 14:27:13 -0700 Subject: [PATCH 03/63] Lots of Kinect bits. --- cmake/modules/FindFreenect.cmake | 44 + cmake/modules/FindLibUSB.cmake | 44 + cmake/modules/FindSkeltrack.cmake | 44 + interface/CMakeLists.txt | 11 +- interface/external/LibUSB/include/libusb.h | 1443 ++++++++++++ interface/external/LibUSB/include/libusbi.h | 935 ++++++++ .../external/LibUSB/include/os/darwin_usb.h | 169 ++ .../external/LibUSB/include/os/linux_usbfs.h | 139 ++ .../external/LibUSB/include/os/poll_posix.h | 10 + .../external/LibUSB/include/os/poll_windows.h | 115 + .../LibUSB/include/os/threads_posix.h | 48 + .../LibUSB/include/os/threads_windows.h | 86 + .../external/LibUSB/include/os/windows_usb.h | 608 +++++ interface/external/LibUSB/include/version.h | 18 + .../external/LibUSB/lib/UNIX/libusb-1.0.a | Bin 0 -> 372942 bytes .../external/LibUSB/lib/UNIX/libusb-1.0.la | 41 + .../freenect/include/libfreenect-audio.h | 115 + .../include/libfreenect-registration.h | 126 + .../external/freenect/include/libfreenect.h | 619 +++++ .../external/freenect/lib/UNIX/libfreenect.a | Bin 0 -> 65072 bytes .../freenect/lib/UNIX/libfreenect_sync.a | Bin 0 -> 12952 bytes interface/external/skeltrack/AUTHORS | 1 + interface/external/skeltrack/CMakeLists.txt | 14 + interface/external/skeltrack/COPYING | 165 ++ .../skeltrack/include/skeltrack-joint.h | 90 + .../skeltrack/include/skeltrack-skeleton.h | 95 + .../skeltrack/include/skeltrack-smooth.h | 36 + .../skeltrack/include/skeltrack-util.h | 127 ++ .../external/skeltrack/include/skeltrack.h | 28 + .../skeltrack/lib/UNIX/libskeltrack.a | Bin 0 -> 53760 bytes .../external/skeltrack/src/skeltrack-joint.c | 159 ++ .../skeltrack/src/skeltrack-skeleton.c | 2027 +++++++++++++++++ .../external/skeltrack/src/skeltrack-smooth.c | 226 ++ .../external/skeltrack/src/skeltrack-util.c | 626 +++++ interface/src/Webcam.cpp | 81 +- interface/src/Webcam.h | 6 + 36 files changed, 8268 insertions(+), 28 deletions(-) create mode 100644 cmake/modules/FindFreenect.cmake create mode 100644 cmake/modules/FindLibUSB.cmake create mode 100644 cmake/modules/FindSkeltrack.cmake create mode 100644 interface/external/LibUSB/include/libusb.h create mode 100644 interface/external/LibUSB/include/libusbi.h create mode 100644 interface/external/LibUSB/include/os/darwin_usb.h create mode 100644 interface/external/LibUSB/include/os/linux_usbfs.h create mode 100644 interface/external/LibUSB/include/os/poll_posix.h create mode 100644 interface/external/LibUSB/include/os/poll_windows.h create mode 100644 interface/external/LibUSB/include/os/threads_posix.h create mode 100644 interface/external/LibUSB/include/os/threads_windows.h create mode 100644 interface/external/LibUSB/include/os/windows_usb.h create mode 100644 interface/external/LibUSB/include/version.h create mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.a create mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.la create mode 100644 interface/external/freenect/include/libfreenect-audio.h create mode 100644 interface/external/freenect/include/libfreenect-registration.h create mode 100644 interface/external/freenect/include/libfreenect.h create mode 100644 interface/external/freenect/lib/UNIX/libfreenect.a create mode 100644 interface/external/freenect/lib/UNIX/libfreenect_sync.a create mode 100644 interface/external/skeltrack/AUTHORS create mode 100644 interface/external/skeltrack/CMakeLists.txt create mode 100644 interface/external/skeltrack/COPYING create mode 100644 interface/external/skeltrack/include/skeltrack-joint.h create mode 100644 interface/external/skeltrack/include/skeltrack-skeleton.h create mode 100644 interface/external/skeltrack/include/skeltrack-smooth.h create mode 100644 interface/external/skeltrack/include/skeltrack-util.h create mode 100644 interface/external/skeltrack/include/skeltrack.h create mode 100644 interface/external/skeltrack/lib/UNIX/libskeltrack.a create mode 100644 interface/external/skeltrack/src/skeltrack-joint.c create mode 100644 interface/external/skeltrack/src/skeltrack-skeleton.c create mode 100644 interface/external/skeltrack/src/skeltrack-smooth.c create mode 100644 interface/external/skeltrack/src/skeltrack-util.c diff --git a/cmake/modules/FindFreenect.cmake b/cmake/modules/FindFreenect.cmake new file mode 100644 index 0000000000..97adc4302a --- /dev/null +++ b/cmake/modules/FindFreenect.cmake @@ -0,0 +1,44 @@ +# Try to find the Freenect library to read from Kinect +# +# You must provide a FREENECT_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# FREENECT_FOUND - system found Freenect +# FREENECT_INCLUDE_DIRS - the Freenect include directory +# FREENECT_LIBRARIES - Link this to use Freenect +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) + # in cache already + set(FREENECT_FOUND TRUE) +else (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) + find_path(FREENECT_INCLUDE_DIRS libfreenect.h ${FREENECT_ROOT_DIR}/include) + + if (APPLE) + find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/UNIX/) + endif () + + if (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) + set(FREENECT_FOUND TRUE) + endif (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) + + if (FREENECT_FOUND) + if (NOT FREENECT_FIND_QUIETLY) + message(STATUS "Found Freenect: ${FREENECT_LIBRARIES}") + endif (NOT FREENECT_FIND_QUIETLY) + else (FREENECT_FOUND) + if (FREENECT_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Freenect") + endif (FREENECT_FIND_REQUIRED) + endif (FREENECT_FOUND) + + # show the FREENECT_INCLUDE_DIRS and FREENECT_LIBRARIES variables only in the advanced view + mark_as_advanced(FREENECT_INCLUDE_DIRS FREENECT_LIBRARIES) + +endif (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) diff --git a/cmake/modules/FindLibUSB.cmake b/cmake/modules/FindLibUSB.cmake new file mode 100644 index 0000000000..f9599752a1 --- /dev/null +++ b/cmake/modules/FindLibUSB.cmake @@ -0,0 +1,44 @@ +# Try to find the LibUSB library to interact with USB devices +# +# You must provide a LIBUSB_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# LIBUSB_FOUND - system found LibUSB +# LIBUSB_INCLUDE_DIRS - the LibUSB include directory +# LIBUSB_LIBRARIES - Link this to use LibUSB +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) + # in cache already + set(LIBUSB_FOUND TRUE) +else (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) + find_path(LIBUSB_INCLUDE_DIRS libusb.h ${LIBUSB_ROOT_DIR}/include) + + if (APPLE) + find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/UNIX/) + endif () + + if (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) + set(LIBUSB_FOUND TRUE) + endif (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) + + if (LIBUSB_FOUND) + if (NOT LIBUSB_FIND_QUIETLY) + message(STATUS "Found LibUSB: ${LIBUSB_LIBRARIES}") + endif (NOT LIBUSB_FIND_QUIETLY) + else (LIBUSB_FOUND) + if (LIBUSB_FIND_REQUIRED) + message(FATAL_ERROR "Could not find LibUSB") + endif (LIBUSB_FIND_REQUIRED) + endif (LIBUSB_FOUND) + + # show the LIBUSB_INCLUDE_DIRS and LIBUSB_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES) + +endif (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) diff --git a/cmake/modules/FindSkeltrack.cmake b/cmake/modules/FindSkeltrack.cmake new file mode 100644 index 0000000000..a05e0c2990 --- /dev/null +++ b/cmake/modules/FindSkeltrack.cmake @@ -0,0 +1,44 @@ +# Try to find the Skeltrack library to perform skeleton tracking via depth camera +# +# You must provide a SKELTRACK_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# SKELTRACK_FOUND - system found Skeltrack +# SKELTRACK_INCLUDE_DIRS - the Skeltrack include directory +# SKELTRACK_LIBRARIES - Link this to use Skeltrack +# +# Created on 6/25/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) + # in cache already + set(SKELTRACK_FOUND TRUE) +else (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) + find_path(SKELTRACK_INCLUDE_DIRS skeltrack.h ${SKELTRACK_ROOT_DIR}/include) + + if (APPLE) + find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/MacOS/) + elseif (UNIX) + find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/UNIX/) + endif () + + if (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) + set(SKELTRACK_FOUND TRUE) + endif (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) + + if (SKELTRACK_FOUND) + if (NOT SKELTRACK_FIND_QUIETLY) + message(STATUS "Found Skeltrack: ${SKELTRACK_LIBRARIES}") + endif (NOT SKELTRACK_FIND_QUIETLY) + else (SKELTRACK_FOUND) + if (SKELTRACK_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Skeltrack") + endif (SKELTRACK_FIND_REQUIRED) + endif (SKELTRACK_FOUND) + + # show the SKELTRACK_INCLUDE_DIRS and SKELTRACK_LIBRARIES variables only in the advanced view + mark_as_advanced(SKELTRACK_INCLUDE_DIRS SKELTRACK_LIBRARIES) + +endif (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 148e6819be..234f365687 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -13,6 +13,9 @@ set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio) set(SPEEX_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Speex) set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV) set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl) +set(LIBUSB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibUSB) +set(FREENECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/freenect) +set(SKELTRACK_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/skeltrack) if (APPLE) set(GL_HEADERS "#include \n#include ") @@ -91,6 +94,9 @@ find_package(SpeexDSP REQUIRED) find_package(OpenCV) find_package(ZLIB) find_package(UVCCameraControl) +find_package(LibUSB) +find_package(Freenect) +find_package(Skeltrack) # include headers for interface and InterfaceConfig. include_directories( @@ -106,9 +112,12 @@ include_directories( ${LIBOVR_INCLUDE_DIRS} ${OPENCV_INCLUDE_DIRS} ${SPEEXDSP_INCLUDE_DIRS} + ${FREENECT_INCLUDE_DIRS} + ${SKELTRACK_INCLUDE_DIRS} ) -target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES}) +target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES} + ${FREENECT_LIBRARIES} ${SKELTRACK_LIBRARIES} ${LIBUSB_LIBRARIES}) if (APPLE) # link in required OS X frameworks and include the right GL headers diff --git a/interface/external/LibUSB/include/libusb.h b/interface/external/LibUSB/include/libusb.h new file mode 100644 index 0000000000..58b406f247 --- /dev/null +++ b/interface/external/LibUSB/include/libusb.h @@ -0,0 +1,1443 @@ +/* + * Public libusb header file + * Copyright (C) 2007-2008 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#ifdef _MSC_VER +/* on MS environments, the inline keyword is available in C++ only */ +#define inline __inline +/* ssize_t is also not available (copy/paste from MinGW) */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 + typedef __int64 ssize_t; +#else + typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +/* stdint.h is also not usually available on MS */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#include +#include +#include + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) +#include +#endif + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) +#include +#if defined(interface) +#undef interface +#endif +#endif + +/** \def LIBUSB_CALL + * \ingroup misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to funcions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** \def libusb_cpu_to_le16 + * \ingroup misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = x >> 8; + _tmp.b8[0] = x & 0xff; + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 7, + + /** Image class */ + LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + LIBUSB_CLASS_IMAGE = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +/** \ingroup desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully opreation. Expressed in units + * of 2 mA. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup lib + * Structure representing the libusb version. + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. This field is only nonzero on Windows. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** Output of `git describe --tags` at library build time. */ + const char *describe; +}; + +/** \ingroup lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_device_ref() and + * libusb_device_unref(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; + +/** \ingroup misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call \ref libusb_error_name() to retrieve a string representation + * of an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB! Remember to update libusb_error_name() + when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99, +}; + +/** \ingroup asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW, +}; + +/** \ingroup asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer() */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, +}; + +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup misc + * Capabilities supported by this instance of libusb. Test if the loaded + * library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0, +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, + int interface_number); + +/* async I/O */ + +/** \ingroup asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +/** \ingroup asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + (uint16_t) length, 1000); +} + +/** \ingroup desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/interface/external/LibUSB/include/libusbi.h b/interface/external/LibUSB/include/libusbi.h new file mode 100644 index 0000000000..976be0d13b --- /dev/null +++ b/interface/external/LibUSB/include/libusbi.h @@ -0,0 +1,935 @@ +/* + * Internal header for libusb + * Copyright (C) 2007-2009 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSBI_H +#define LIBUSBI_H + +#include + +#include +#include +#include +#include +#ifdef HAVE_POLL_H +#include +#endif + +#include +#include + +/* Inside the libusb code, mark all public functions as follows: + * return_type API_EXPORTED function_name(params) { ... } + * But if the function returns a pointer, mark it as follows: + * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } + * In the libusb public header, mark all declarations as: + * return_type LIBUSB_CALL function_name(params); + */ +#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY + +#define DEVICE_DESC_LENGTH 18 + +#define USB_MAXENDPOINTS 32 +#define USB_MAXINTERFACES 32 +#define USB_MAXCONFIG 8 + +struct list_head { + struct list_head *prev, *next; +}; + +/* Get an entry from the list + * ptr - the address of this list_head element in "type" + * type - the data type that contains "member" + * member - the list_head element in "type" + */ +#define list_entry(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) + +/* Get each entry from a list + * pos - A structure pointer has a "member" element + * head - list head + * member - the list_head element in "pos" + * type - the type of the first parameter + */ +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_entry((head)->next, type, member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, type, member)) + +#define list_for_each_entry_safe(pos, n, head, member, type) \ + for (pos = list_entry((head)->next, type, member), \ + n = list_entry(pos->member.next, type, member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, type, member)) + +#define list_empty(entry) ((entry)->next == (entry)) + +static inline void list_init(struct list_head *entry) +{ + entry->prev = entry->next = entry; +} + +static inline void list_add(struct list_head *entry, struct list_head *head) +{ + entry->next = head->next; + entry->prev = head; + + head->next->prev = entry; + head->next = entry; +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + entry->next = head; + entry->prev = head->prev; + + head->prev->next = entry; + head->prev = entry; +} + +static inline void list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *mptr = (ptr); \ + (type *)( (char *)mptr - offsetof(type,member) );}) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) + +enum usbi_log_level { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR, +}; + +void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, ...); + +void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, + const char *function, const char *format, va_list args); + +#if !defined(_MSC_VER) || _MSC_VER >= 1400 + +#ifdef ENABLE_LOGGING +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) +#else +#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0) +#endif + +#ifdef ENABLE_DEBUG_LOGGING +#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) +#else +#define usbi_dbg(...) do {} while(0) +#endif + +#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) + +#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +/* Old MS compilers don't support variadic macros. The code is simple, so we + * repeat it for each loglevel. Note that the debug case is special. + * + * Support for variadic macros was introduced in Visual C++ 2005. + * http://msdn.microsoft.com/en-us/library/ms177415%28v=VS.80%29.aspx + */ + +static inline void usbi_info(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_INFO, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_warn(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_WARNING, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_err(struct libusb_context *ctx, const char *fmt, ...) +{ +#ifdef ENABLE_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(ctx, LOG_LEVEL_ERROR, "", fmt, args); + va_end(args); +#else + (void)ctx; +#endif +} + +static inline void usbi_dbg(const char *fmt, ...) +{ +#ifdef ENABLE_DEBUG_LOGGING + va_list args; + va_start(args, fmt); + usbi_log_v(NULL, LOG_LEVEL_DEBUG, "", fmt, args); + va_end(args); +#else + (void)fmt; +#endif +} + +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ + +#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) +#define IS_EPOUT(ep) (!IS_EPIN(ep)) +#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) +#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) + +/* Internal abstractions for thread synchronization and poll */ +#if defined(THREADS_POSIX) +#include +#elif defined(OS_WINDOWS) +#include +#endif + +#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) +#include +#include +#elif defined(OS_WINDOWS) +#include +#endif + +#if defined(OS_WINDOWS) && !defined(__GCC__) +#undef HAVE_GETTIMEOFDAY +int usbi_gettimeofday(struct timeval *tp, void *tzp); +#define LIBUSB_GETTIMEOFDAY_WIN32 +#define HAVE_USBI_GETTIMEOFDAY +#else +#ifdef HAVE_GETTIMEOFDAY +#define usbi_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#define HAVE_USBI_GETTIMEOFDAY +#endif +#endif + +extern struct libusb_context *usbi_default_context; + +struct libusb_context { + int debug; + int debug_fixed; + + /* internal control pipe, used for interrupting event handling when + * something needs to modify poll fds. */ + int ctrl_pipe[2]; + + struct list_head usb_devs; + usbi_mutex_t usb_devs_lock; + + /* A list of open handles. Backends are free to traverse this if required. + */ + struct list_head open_devs; + usbi_mutex_t open_devs_lock; + + /* this is a list of in-flight transfer handles, sorted by timeout + * expiration. URBs to timeout the soonest are placed at the beginning of + * the list, URBs that will time out later are placed after, and urbs with + * infinite timeout are always placed at the very end. */ + struct list_head flying_transfers; + usbi_mutex_t flying_transfers_lock; + + /* list of poll fds */ + struct list_head pollfds; + usbi_mutex_t pollfds_lock; + + /* a counter that is set when we want to interrupt event handling, in order + * to modify the poll fd set. and a lock to protect it. */ + unsigned int pollfd_modify; + usbi_mutex_t pollfd_modify_lock; + + /* user callbacks for pollfd changes */ + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + /* ensures that only one thread is handling events at any one time */ + usbi_mutex_t events_lock; + + /* used to see if there is an active thread doing event handling */ + int event_handler_active; + + /* used to wait for event completion in threads other than the one that is + * event handling */ + usbi_mutex_t event_waiters_lock; + usbi_cond_t event_waiters_cond; + +#ifdef USBI_TIMERFD_AVAILABLE + /* used for timeout handling, if supported by OS. + * this timerfd is maintained to trigger on the next pending timeout */ + int timerfd; +#endif +}; + +#ifdef USBI_TIMERFD_AVAILABLE +#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) +#else +#define usbi_using_timerfd(ctx) (0) +#endif + +struct libusb_device { + /* lock protects refcnt, everything else is finalized at initialization + * time */ + usbi_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t device_address; + uint8_t num_configurations; + enum libusb_speed speed; + + struct list_head list; + unsigned long session_data; + unsigned char os_priv[0]; +}; + +struct libusb_device_handle { + /* lock protects claimed_interfaces */ + usbi_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + unsigned char os_priv[0]; +}; + +enum { + USBI_CLOCK_MONOTONIC, + USBI_CLOCK_REALTIME +}; + +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ + +struct usbi_transfer { + int num_iso_packets; + struct list_head list; + struct timeval timeout; + int transferred; + uint8_t flags; + + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend + * should also take this lock in the handle_events path, to prevent the user + * cancelling the transfer from another thread while you are processing + * its completion (presumably there would be races within your OS backend + * if this were possible). */ + usbi_mutex_t lock; +}; + +enum usbi_transfer_flags { + /* The transfer has timed out */ + USBI_TRANSFER_TIMED_OUT = 1 << 0, + + /* Set by backend submit_transfer() if the OS handles timeout */ + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1, + + /* Cancellation was requested via libusb_cancel_transfer() */ + USBI_TRANSFER_CANCELLING = 1 << 2, + + /* Operation on the transfer failed because the device disappeared */ + USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 3, +}; + +#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + +/* bus structures */ + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +}; + +/* shared data and functions */ + +int usbi_io_init(struct libusb_context *ctx); +void usbi_io_exit(struct libusb_context *ctx); + +struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, + unsigned long session_id); +struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, + unsigned long session_id); +int usbi_sanitize_device(struct libusb_device *dev); +void usbi_handle_disconnect(struct libusb_device_handle *handle); + +int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + enum libusb_transfer_status status); +int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); + +int usbi_parse_descriptor(unsigned char *source, const char *descriptor, + void *dest, int host_endian); +int usbi_get_config_index_by_value(struct libusb_device *dev, + uint8_t bConfigurationValue, int *idx); + +/* polling */ + +struct usbi_pollfd { + /* must come first */ + struct libusb_pollfd pollfd; + + struct list_head list; +}; + +int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); +void usbi_remove_pollfd(struct libusb_context *ctx, int fd); +void usbi_fd_notification(struct libusb_context *ctx); + +/* device discovery */ + +/* we traverse usbfs without knowing how many devices we are going to find. + * so we create this discovered_devs model which is similar to a linked-list + * which grows when required. it can be freed once discovery has completed, + * eliminating the need for a list node in the libusb_device structure + * itself. */ +struct discovered_devs { + size_t len; + size_t capacity; + struct libusb_device *devices[0]; +}; + +struct discovered_devs *discovered_devs_append( + struct discovered_devs *discdevs, struct libusb_device *dev); + +/* OS abstraction */ + +/* This is the interface that OS backends need to implement. + * All fields are mandatory, except ones explicitly noted as optional. */ +struct usbi_os_backend { + /* A human-readable name for your backend, e.g. "Linux usbfs" */ + const char *name; + + /* Perform initialization of your backend. You might use this function + * to determine specific capabilities of the system, allocate required + * data structures for later, etc. + * + * This function is called when a libusb user initializes the library + * prior to use. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*init)(struct libusb_context *ctx); + + /* Deinitialization. Optional. This function should destroy anything + * that was set up by init. + * + * This function is called when the user deinitializes the library. + */ + void (*exit)(void); + + /* Enumerate all the USB devices on the system, returning them in a list + * of discovered devices. + * + * Your implementation should enumerate all devices on the system, + * regardless of whether they have been seen before or not. + * + * When you have found a device, compute a session ID for it. The session + * ID should uniquely represent that particular device for that particular + * connection session since boot (i.e. if you disconnect and reconnect a + * device immediately after, it should be assigned a different session ID). + * If your OS cannot provide a unique session ID as described above, + * presenting a session ID of (bus_number << 8 | device_address) should + * be sufficient. Bus numbers and device addresses wrap and get reused, + * but that is an unlikely case. + * + * After computing a session ID for a device, call + * usbi_get_device_by_session_id(). This function checks if libusb already + * knows about the device, and if so, it provides you with a libusb_device + * structure for it. + * + * If usbi_get_device_by_session_id() returns NULL, it is time to allocate + * a new device structure for the device. Call usbi_alloc_device() to + * obtain a new libusb_device structure with reference count 1. Populate + * the bus_number and device_address attributes of the new device, and + * perform any other internal backend initialization you need to do. At + * this point, you should be ready to provide device descriptors and so + * on through the get_*_descriptor functions. Finally, call + * usbi_sanitize_device() to perform some final sanity checks on the + * device. Assuming all of the above succeeded, we can now continue. + * If any of the above failed, remember to unreference the device that + * was returned by usbi_alloc_device(). + * + * At this stage we have a populated libusb_device structure (either one + * that was found earlier, or one that we have just allocated and + * populated). This can now be added to the discovered devices list + * using discovered_devs_append(). Note that discovered_devs_append() + * may reallocate the list, returning a new location for it, and also + * note that reallocation can fail. Your backend should handle these + * error conditions appropriately. + * + * This function should not generate any bus I/O and should not block. + * If I/O is required (e.g. reading the active configuration value), it is + * OK to ignore these suggestions :) + * + * This function is executed when the user wishes to retrieve a list + * of USB devices connected to the system. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*get_device_list)(struct libusb_context *ctx, + struct discovered_devs **discdevs); + + /* Open a device for I/O and other USB operations. The device handle + * is preallocated for you, you can retrieve the device in question + * through handle->dev. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_pollfd() function. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to obtain a device + * handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since + * discovery + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*open)(struct libusb_device_handle *handle); + + /* Close a device such that the handle cannot be used again. Your backend + * should destroy any resources that were allocated in the open path. + * This may also be a good place to call usbi_remove_pollfd() to inform + * libusb of any file descriptors associated with this device that should + * no longer be monitored. + * + * This function is called when the user closes a device handle. + */ + void (*close)(struct libusb_device_handle *handle); + + /* Retrieve the device descriptor from a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. Alternatively, you may be able + * to retrieve it from a kernel interface (some Linux setups can do this) + * still without generating bus I/O. + * + * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into + * buffer, which is guaranteed to be big enough. + * + * This function is called when sanity-checking a device before adding + * it to the list of discovered devices, and also when the user requests + * to read the device descriptor. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_device_descriptor)(struct libusb_device *device, + unsigned char *buffer, int *host_endian); + + /* Get the ACTIVE configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. You may also have to keep track + * of which configuration is active when the user changes it. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * - another LIBUSB_ERROR code on other failure + */ + int (*get_active_config_descriptor)(struct libusb_device *device, + unsigned char *buffer, size_t len, int *host_endian); + + /* Get a specific configuration descriptor for a device. + * + * The descriptor should be retrieved from memory, NOT via bus I/O to the + * device. This means that you may have to cache it in a private structure + * during get_device_list enumeration. + * + * The requested descriptor is expressed as a zero-based index (i.e. 0 + * indicates that we are requesting the first descriptor). The index does + * not (necessarily) equal the bConfigurationValue of the configuration + * being requested. + * + * This function is expected to write len bytes of data into buffer, which + * is guaranteed to be big enough. If you can only do a partial write, + * return an error code. + * + * This function is expected to return the descriptor in bus-endian format + * (LE). If it returns the multi-byte values in host-endian format, + * set the host_endian output parameter to "1". + * + * Return 0 on success or a LIBUSB_ERROR code on failure. + */ + int (*get_config_descriptor)(struct libusb_device *device, + uint8_t config_index, unsigned char *buffer, size_t len, + int *host_endian); + + /* Get the bConfigurationValue for the active configuration for a device. + * Optional. This should only be implemented if you can retrieve it from + * cache (don't generate I/O). + * + * If you cannot retrieve this from cache, either do not implement this + * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause + * libusb to retrieve the information through a standard control transfer. + * + * This function must be non-blocking. + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without + * blocking + * - another LIBUSB_ERROR code on other failure. + */ + int (*get_configuration)(struct libusb_device_handle *handle, int *config); + + /* Set the active configuration for a device. + * + * A configuration value of -1 should put the device in unconfigured state. + * + * This function can block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence + * configuration cannot be changed) + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure. + */ + int (*set_configuration)(struct libusb_device_handle *handle, int config); + + /* Claim an interface. When claimed, the application can then perform + * I/O to an interface's endpoints. + * + * This function should not generate any bus I/O and should not block. + * Interface claiming is a logical operation that simply ensures that + * no other drivers/applications are using the interface, and after + * claiming, no other drivers/applicatiosn can use the interface because + * we now "own" it. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist + * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*claim_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Release a previously claimed interface. + * + * This function should also generate a SET_INTERFACE control request, + * resetting the alternate setting of that interface to 0. It's OK for + * this function to block as a result. + * + * You will only ever be asked to release an interface which was + * successfully claimed earlier. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*release_interface)(struct libusb_device_handle *handle, int interface_number); + + /* Set the alternate setting for an interface. + * + * You will only ever be asked to set the alternate setting for an + * interface which was successfully claimed earlier. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*set_interface_altsetting)(struct libusb_device_handle *handle, + int interface_number, int altsetting); + + /* Clear a halt/stall condition on an endpoint. + * + * It's OK for this function to block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*clear_halt)(struct libusb_device_handle *handle, + unsigned char endpoint); + + /* Perform a USB port reset to reinitialize a device. + * + * If possible, the handle should still be usable after the reset + * completes, assuming that the device descriptors did not change during + * reset and all previous interface state can be restored. + * + * If something changes, or you cannot easily locate/verify the resetted + * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application + * to close the old handle and re-enumerate the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device + * has been disconnected since it was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*reset_device)(struct libusb_device_handle *handle); + + /* Determine if a kernel driver is active on an interface. Optional. + * + * The presence of a kernel driver on an interface indicates that any + * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. + * + * Return: + * - 0 if no driver is active + * - 1 if a driver is active + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*kernel_driver_active)(struct libusb_device_handle *handle, + int interface_number); + + /* Detach a kernel driver from an interface. Optional. + * + * After detaching a kernel driver, the interface should be available + * for claim. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - another LIBUSB_ERROR code on other failure + */ + int (*detach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Attach a kernel driver to an interface. Optional. + * + * Reattach a kernel driver to the device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it + * was opened + * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, + * preventing reattachment + * - another LIBUSB_ERROR code on other failure + */ + int (*attach_kernel_driver)(struct libusb_device_handle *handle, + int interface_number); + + /* Destroy a device. Optional. + * + * This function is called when the last reference to a device is + * destroyed. It should free any resources allocated in the get_device_list + * path. + */ + void (*destroy_device)(struct libusb_device *dev); + + /* Submit a transfer. Your implementation should take the transfer, + * morph it into whatever form your platform requires, and submit it + * asynchronously. + * + * This function must not block. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * - another LIBUSB_ERROR code on other failure + */ + int (*submit_transfer)(struct usbi_transfer *itransfer); + + /* Cancel a previously submitted transfer. + * + * This function must not block. The transfer cancellation must complete + * later, resulting in a call to usbi_handle_transfer_cancellation() + * from the context of handle_events. + */ + int (*cancel_transfer)(struct usbi_transfer *itransfer); + + /* Clear a transfer as if it has completed or cancelled, but do not + * report any completion/cancellation to the library. You should free + * all private data from the transfer as if you were just about to report + * completion or cancellation. + * + * This function might seem a bit out of place. It is used when libusb + * detects a disconnected device - it calls this function for all pending + * transfers before reporting completion (with the disconnect code) to + * the user. Maybe we can improve upon this internal interface in future. + */ + void (*clear_transfer_priv)(struct usbi_transfer *itransfer); + + /* Handle any pending events. This involves monitoring any active + * transfers and processing their completion or cancellation. + * + * The function is passed an array of pollfd structures (size nfds) + * as a result of the poll() system call. The num_ready parameter + * indicates the number of file descriptors that have reported events + * (i.e. the poll() return value). This should be enough information + * for you to determine which actions need to be taken on the currently + * active transfers. + * + * For any cancelled transfers, call usbi_handle_transfer_cancellation(). + * For completed transfers, call usbi_handle_transfer_completion(). + * For control/bulk/interrupt transfers, populate the "transferred" + * element of the appropriate usbi_transfer structure before calling the + * above functions. For isochronous transfers, populate the status and + * transferred fields of the iso packet descriptors of the transfer. + * + * This function should also be able to detect disconnection of the + * device, reporting that situation with usbi_handle_disconnect(). + * + * When processing an event related to a transfer, you probably want to + * take usbi_transfer.lock to prevent races. See the documentation for + * the usbi_transfer structure. + * + * Return 0 on success, or a LIBUSB_ERROR code on failure. + */ + int (*handle_events)(struct libusb_context *ctx, + struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + + /* Get time from specified clock. At least two clocks must be implemented + by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. + + Description of clocks: + USBI_CLOCK_REALTIME : clock returns time since system epoch. + USBI_CLOCK_MONOTONIC: clock returns time since unspecified start + time (usually boot). + */ + int (*clock_gettime)(int clkid, struct timespec *tp); + +#ifdef USBI_TIMERFD_AVAILABLE + /* clock ID of the clock that should be used for timerfd */ + clockid_t (*get_timerfd_clockid)(void); +#endif + + /* Number of bytes to reserve for per-device private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_priv_size; + + /* Number of bytes to reserve for per-handle private backend data. + * This private data area is accessible through the "os_priv" field of + * struct libusb_device. */ + size_t device_handle_priv_size; + + /* Number of bytes to reserve for per-transfer private backend data. + * This private data area is accessible by calling + * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + */ + size_t transfer_priv_size; + + /* Mumber of additional bytes for os_priv for each iso packet. + * Can your backend use this? */ + /* FIXME: linux can't use this any more. if other OS's cannot either, + * then remove this */ + size_t add_iso_packet_size; +}; + +extern const struct usbi_os_backend * const usbi_backend; + +extern const struct usbi_os_backend linux_usbfs_backend; +extern const struct usbi_os_backend darwin_backend; +extern const struct usbi_os_backend openbsd_backend; +extern const struct usbi_os_backend windows_backend; + +#endif + diff --git a/interface/external/LibUSB/include/os/darwin_usb.h b/interface/external/LibUSB/include/os/darwin_usb.h new file mode 100644 index 0000000000..59d0a694ce --- /dev/null +++ b/interface/external/LibUSB/include/os/darwin_usb.h @@ -0,0 +1,169 @@ +/* + * darwin backend for libusb 1.0 + * Copyright (C) 2008-2009 Nathan Hjelm + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined(LIBUSB_DARWIN_H) +#define LIBUSB_DARWIN_H + +#include "libusbi.h" + +#include +#include +#include +#include + +/* IOUSBInterfaceInferface */ +#if defined (kIOUSBInterfaceInterfaceID300) + +#define usb_interface_t IOUSBInterfaceInterface300 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 +#define InterfaceVersion 300 + +#elif defined (kIOUSBInterfaceInterfaceID245) + +#define usb_interface_t IOUSBInterfaceInterface245 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 +#define InterfaceVersion 245 + +#elif defined (kIOUSBInterfaceInterfaceID220) + +#define usb_interface_t IOUSBInterfaceInterface220 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 +#define InterfaceVersion 220 + +#elif defined (kIOUSBInterfaceInterfaceID197) + +#define usb_interface_t IOUSBInterfaceInterface197 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 +#define InterfaceVersion 197 + +#elif defined (kIOUSBInterfaceInterfaceID190) + +#define usb_interface_t IOUSBInterfaceInterface190 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 +#define InterfaceVersion 190 + +#elif defined (kIOUSBInterfaceInterfaceID182) + +#define usb_interface_t IOUSBInterfaceInterface182 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 +#define InterfaceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +/* IOUSBDeviceInterface */ +#if defined (kIOUSBDeviceInterfaceID320) + +#define usb_device_t IOUSBDeviceInterface320 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 +#define DeviceVersion 320 + +#elif defined (kIOUSBDeviceInterfaceID300) + +#define usb_device_t IOUSBDeviceInterface300 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 +#define DeviceVersion 300 + +#elif defined (kIOUSBDeviceInterfaceID245) + +#define usb_device_t IOUSBDeviceInterface245 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 +#define DeviceVersion 245 + +#elif defined (kIOUSBDeviceInterfaceID197) + +#define usb_device_t IOUSBDeviceInterface197 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 +#define DeviceVersion 197 + +#elif defined (kIOUSBDeviceInterfaceID187) + +#define usb_device_t IOUSBDeviceInterface187 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 +#define DeviceVersion 187 + +#elif defined (kIOUSBDeviceInterfaceID182) + +#define usb_device_t IOUSBDeviceInterface182 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 +#define DeviceVersion 182 + +#else + +#error "IOUSBFamily is too old. Please upgrade your OS" + +#endif + +#if !defined(IO_OBJECT_NULL) +#define IO_OBJECT_NULL ((io_object_t) 0) +#endif + +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; +typedef IONotificationPortRef io_notification_port_t; + +/* private structures */ +struct darwin_device_priv { + IOUSBDeviceDescriptor dev_descriptor; + UInt32 location; + char sys_path[21]; + usb_device_t **device; + int open_count; + UInt8 first_config, active_config; +}; + +struct darwin_device_handle_priv { + int is_open; + CFRunLoopSourceRef cfSource; + int fds[2]; + + struct darwin_interface { + usb_interface_t **interface; + uint8_t num_endpoints; + CFRunLoopSourceRef cfSource; + uint64_t frames[256]; + uint8_t endpoint_addrs[USB_MAXENDPOINTS]; + } interfaces[USB_MAXINTERFACES]; +}; + +struct darwin_transfer_priv { + /* Isoc */ + IOUSBIsocFrame *isoc_framelist; + size_t num_iso_packets; + + /* Control */ +#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) + IOUSBDevRequestTO req; +#else + IOUSBDevRequest req; +#endif + + /* Bulk */ +}; + +enum { + MESSAGE_DEVICE_GONE, + MESSAGE_ASYNC_IO_COMPLETE +}; + + + +#endif diff --git a/interface/external/LibUSB/include/os/linux_usbfs.h b/interface/external/LibUSB/include/os/linux_usbfs.h new file mode 100644 index 0000000000..487acb5ac2 --- /dev/null +++ b/interface/external/LibUSB/include/os/linux_usbfs.h @@ -0,0 +1,139 @@ +/* + * usbfs header structures + * Copyright (C) 2007 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_USBFS_H +#define LIBUSB_USBFS_H + +#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" + +struct usbfs_ctrltransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + + uint32_t timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_bulktransfer { + /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + + /* pointer to data */ + void *data; +}; + +struct usbfs_setinterface { + /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ + unsigned int interface; + unsigned int altsetting; +}; + +#define USBFS_MAXDRIVERNAME 255 + +struct usbfs_getdriver { + unsigned int interface; + char driver[USBFS_MAXDRIVERNAME + 1]; +}; + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 +#define USBFS_URB_ZERO_PACKET 0x40 + +enum usbfs_urb_type { + USBFS_URB_TYPE_ISO = 0, + USBFS_URB_TYPE_INTERRUPT = 1, + USBFS_URB_TYPE_CONTROL = 2, + USBFS_URB_TYPE_BULK = 3, +}; + +struct usbfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +#define MAX_ISO_BUFFER_LENGTH 32768 +#define MAX_BULK_BUFFER_LENGTH 16384 +#define MAX_CTRL_BUFFER_LENGTH 4096 + +struct usbfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; + void *usercontext; + struct usbfs_iso_packet_desc iso_frame_desc[0]; +}; + +struct usbfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +struct usbfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +struct usbfs_hub_portinfo { + unsigned char numports; + unsigned char port[127]; /* port to device num mapping */ +}; + +#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) +#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) +#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) +#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) +#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) +#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) +#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) +#define IOCTL_USBFS_DISCARDURB _IO('U', 11) +#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) +#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) +#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) +#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) +#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) +#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) +#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) +#define IOCTL_USBFS_RESET _IO('U', 20) +#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) + +#endif diff --git a/interface/external/LibUSB/include/os/poll_posix.h b/interface/external/LibUSB/include/os/poll_posix.h new file mode 100644 index 0000000000..0e5e7f5b72 --- /dev/null +++ b/interface/external/LibUSB/include/os/poll_posix.h @@ -0,0 +1,10 @@ +#ifndef LIBUSB_POLL_POSIX_H +#define LIBUSB_POLL_POSIX_H + +#define usbi_write write +#define usbi_read read +#define usbi_close close +#define usbi_pipe pipe +#define usbi_poll poll + +#endif /* LIBUSB_POLL_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/poll_windows.h b/interface/external/LibUSB/include/os/poll_windows.h new file mode 100644 index 0000000000..6e5bf2bcef --- /dev/null +++ b/interface/external/LibUSB/include/os/poll_windows.h @@ -0,0 +1,115 @@ +/* + * Windows compat: POSIX compatibility wrapper + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#endif + +// Handle synchronous completion through the overlapped structure +#if !defined(STATUS_REPARSE) // reuse the REPARSE status code +#define STATUS_REPARSE ((LONG)0x00000104L) +#endif +#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE +#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) + +#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2) + +enum windows_version { + WINDOWS_UNSUPPORTED, + WINDOWS_XP, + WINDOWS_2003, // also includes XP 64 + WINDOWS_VISTA_AND_LATER, +}; +extern enum windows_version windows_version; + +#define MAX_FDS 256 + +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +// access modes +enum rw_type { + RW_NONE, + RW_READ, + RW_WRITE, +}; + +// fd struct that can be used for polling on Windows +struct winfd { + int fd; // what's exposed to libusb core + HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it + OVERLAPPED* overlapped; // what will report our I/O status + enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) +}; +extern const struct winfd INVALID_WINFD; + +int usbi_pipe(int pipefd[2]); +int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); +ssize_t usbi_write(int fd, const void *buf, size_t count); +ssize_t usbi_read(int fd, void *buf, size_t count); +int usbi_close(int fd); + +void init_polling(void); +void exit_polling(void); +struct winfd usbi_create_fd(HANDLE handle, int access_mode); +void usbi_free_fd(int fd); +struct winfd fd_to_winfd(int fd); +struct winfd handle_to_winfd(HANDLE handle); +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); + +/* + * Timeval operations + */ +#if defined(DDKBUILD) +#include // defines timeval functions on DDK +#endif + +#if !defined(TIMESPEC_TO_TIMEVAL) +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (long)(ts)->tv_sec; \ + (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ +} +#endif +#if !defined(timersub) +#define timersub(a, b, result) \ +do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + diff --git a/interface/external/LibUSB/include/os/threads_posix.h b/interface/external/LibUSB/include/os/threads_posix.h new file mode 100644 index 0000000000..9752208936 --- /dev/null +++ b/interface/external/LibUSB/include/os/threads_posix.h @@ -0,0 +1,48 @@ +/* + * libusb synchronization using POSIX Threads + * + * Copyright (C) 2010 Peter Stuge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_POSIX_H +#define LIBUSB_THREADS_POSIX_H + +#include + +#define usbi_mutex_static_t pthread_mutex_t +#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define usbi_mutex_static_lock pthread_mutex_lock +#define usbi_mutex_static_unlock pthread_mutex_unlock + +#define usbi_mutex_t pthread_mutex_t +#define usbi_mutex_init pthread_mutex_init +#define usbi_mutex_lock pthread_mutex_lock +#define usbi_mutex_unlock pthread_mutex_unlock +#define usbi_mutex_trylock pthread_mutex_trylock +#define usbi_mutex_destroy pthread_mutex_destroy + +#define usbi_cond_t pthread_cond_t +#define usbi_cond_init pthread_cond_init +#define usbi_cond_wait pthread_cond_wait +#define usbi_cond_timedwait pthread_cond_timedwait +#define usbi_cond_broadcast pthread_cond_broadcast +#define usbi_cond_destroy pthread_cond_destroy +#define usbi_cond_signal pthread_cond_signal + +extern int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr); + +#endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/threads_windows.h b/interface/external/LibUSB/include/os/threads_windows.h new file mode 100644 index 0000000000..7bb144af58 --- /dev/null +++ b/interface/external/LibUSB/include/os/threads_windows.h @@ -0,0 +1,86 @@ +/* + * libusb synchronization on Microsoft Windows + * + * Copyright (C) 2010 Michael Plante + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_THREADS_WINDOWS_H +#define LIBUSB_THREADS_WINDOWS_H + +#define usbi_mutex_static_t volatile LONG +#define USBI_MUTEX_INITIALIZER 0 + +#define usbi_mutex_t HANDLE + +struct usbi_cond_perthread { + struct list_head list; + DWORD tid; + HANDLE event; +}; +struct usbi_cond_t_ { + // Every time a thread touches the CV, it winds up in one of these lists. + // It stays there until the CV is destroyed, even if the thread + // terminates. + struct list_head waiters; + struct list_head not_waiting; +}; +typedef struct usbi_cond_t_ usbi_cond_t; + +// We *were* getting timespec from pthread.h: +#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) +#define HAVE_STRUCT_TIMESPEC 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + long tv_sec; + long tv_nsec; +}; +#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ + +// We *were* getting ETIMEDOUT from pthread.h: +#ifndef ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +#define usbi_mutexattr_t void +#define usbi_condattr_t void + +// all Windows mutexes are recursive +#define usbi_mutex_init_recursive(mutex, attr) usbi_mutex_init((mutex), (attr)) + +int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); +int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); + + +int usbi_mutex_init(usbi_mutex_t *mutex, + const usbi_mutexattr_t *attr); +int usbi_mutex_lock(usbi_mutex_t *mutex); +int usbi_mutex_unlock(usbi_mutex_t *mutex); +int usbi_mutex_trylock(usbi_mutex_t *mutex); +int usbi_mutex_destroy(usbi_mutex_t *mutex); + +int usbi_cond_init(usbi_cond_t *cond, + const usbi_condattr_t *attr); +int usbi_cond_destroy(usbi_cond_t *cond); +int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); +int usbi_cond_timedwait(usbi_cond_t *cond, + usbi_mutex_t *mutex, + const struct timespec *abstime); +int usbi_cond_broadcast(usbi_cond_t *cond); +int usbi_cond_signal(usbi_cond_t *cond); + +#endif /* LIBUSB_THREADS_WINDOWS_H */ + diff --git a/interface/external/LibUSB/include/os/windows_usb.h b/interface/external/LibUSB/include/os/windows_usb.h new file mode 100644 index 0000000000..ddbd68075b --- /dev/null +++ b/interface/external/LibUSB/include/os/windows_usb.h @@ -0,0 +1,608 @@ +/* + * Windows backend for libusb 1.0 + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#pragma warning(disable:4100) // unreferenced formal parameter +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union +#endif + +// Windows API default is uppercase - ugh! +#if !defined(bool) +#define bool BOOL +#endif +#if !defined(true) +#define true TRUE +#endif +#if !defined(false) +#define false FALSE +#endif + +// Missing from MSVC6 setupapi.h +#if !defined(SPDRP_ADDRESS) +#define SPDRP_ADDRESS 28 +#endif +#if !defined(SPDRP_INSTALL_STATE) +#define SPDRP_INSTALL_STATE 34 +#endif + +#if defined(__CYGWIN__ ) +// cygwin produces a warning unless these prototypes are defined +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern char *_strdup(const char *strSource); +// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread +#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) +#endif +#define safe_free(p) do {if (p != NULL) {free((void*)p); p = NULL;}} while(0) +#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) +#define safe_min(a, b) min((size_t)(a), (size_t)(b)) +#define safe_strcp(dst, dst_max, src, count) do {memcpy(dst, src, safe_min(count, dst_max)); \ + ((char*)dst)[safe_min(count, dst_max)-1] = 0;} while(0) +#define safe_strcpy(dst, dst_max, src) safe_strcp(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, safe_min(count, dst_max - safe_strlen(dst) - 1)) +#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, safe_strlen(src)+1) +#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2)) +#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2), count) +#define safe_strlen(str) ((str==NULL)?0:strlen(str)) +#define safe_sprintf _snprintf +#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) +#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) +static inline void upperize(char* str) { + size_t i; + if (str == NULL) return; + for (i=0; ios_priv; +} + +static inline void windows_device_priv_init(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + p->depth = 0; + p->port = 0; + p->parent_dev = NULL; + p->path = NULL; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->composite_api_flags = 0; + p->active_config = 0; + p->config_descriptor = NULL; + memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); + for (i=0; iusb_interface[i].path = NULL; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; + p->usb_interface[i].nb_endpoints = 0; + p->usb_interface[i].endpoint = NULL; + } +} + +static inline void windows_device_priv_release(libusb_device* dev) { + struct windows_device_priv* p = _device_priv(dev); + int i; + safe_free(p->path); + if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { + for (i=0; i < dev->num_configurations; i++) + safe_free(p->config_descriptor[i]); + } + safe_free(p->config_descriptor); + for (i=0; iusb_interface[i].path); + safe_free(p->usb_interface[i].endpoint); + } +} + +struct interface_handle_t { + HANDLE dev_handle; // WinUSB needs an extra handle for the file + HANDLE api_handle; // used by the API to communicate with the device +}; + +struct windows_device_handle_priv { + int active_interface; + struct interface_handle_t interface_handle[USB_MAXINTERFACES]; + int autoclaim_count[USB_MAXINTERFACES]; // For auto-release +}; + +static inline struct windows_device_handle_priv *_device_handle_priv( + struct libusb_device_handle *handle) +{ + return (struct windows_device_handle_priv *) handle->os_priv; +} + +// used for async polling functions +struct windows_transfer_priv { + struct winfd pollable_fd; + uint8_t interface_number; +}; + +// used to match a device driver (including filter drivers) against a supported API +struct driver_lookup { + char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names + const DWORD reg_prop; // SPDRP registry key to use to retreive list + const char* designation; // internal designation (for debug output) +}; + +/* + * API macros - from libusb-win32 1.x + */ +#define DLL_DECLARE_PREFIXNAME(api, ret, prefixname, name, args) \ + typedef ret (api * __dll_##name##_t)args; \ + static __dll_##name##_t prefixname = NULL + +#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ + do { \ + HMODULE h = GetModuleHandleA(#dll); \ + if (!h) \ + h = LoadLibraryA(#dll); \ + if (!h) { \ + if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\ + else { break; } \ + } \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name); \ + if (prefixname) break; \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ + if (prefixname) break; \ + prefixname = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ + if (prefixname) break; \ + if(ret_on_failure) \ + return LIBUSB_ERROR_NOT_FOUND; \ + } while(0) + +#define DLL_DECLARE(api, ret, name, args) DLL_DECLARE_PREFIXNAME(api, ret, name, name, args) +#define DLL_LOAD(dll, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, name, name, ret_on_failure) +#define DLL_DECLARE_PREFIXED(api, ret, prefix, name, args) DLL_DECLARE_PREFIXNAME(api, ret, prefix##name, name, args) +#define DLL_LOAD_PREFIXED(dll, prefix, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, prefix##name, name, ret_on_failure) + +/* OLE32 dependency */ +DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); + +/* SetupAPI dependencies */ +DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, + const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); +DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, + PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); +DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); + +/* + * Windows DDK API definitions. Most of it copied from MinGW's includes + */ +typedef DWORD DEVNODE, DEVINST; +typedef DEVNODE *PDEVNODE, *PDEVINST; +typedef DWORD RETURN_TYPE; +typedef RETURN_TYPE CONFIGRET; + +#define CR_SUCCESS 0x00000000 +#define CR_NO_SUCH_DEVNODE 0x0000000D + +#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE +#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG +#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING +#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE +#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT + +#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS +#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE +#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE +#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS +#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR +#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR +#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION +#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION +#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE +#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE +#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME + +#define USB_GET_NODE_INFORMATION 258 +#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 +#define USB_GET_NODE_CONNECTION_NAME 261 +#define USB_GET_HUB_CAPABILITIES 271 +#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) +#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 +#endif +#if !defined(USB_GET_HUB_CAPABILITIES_EX) +#define USB_GET_HUB_CAPABILITIES_EX 276 +#endif + +#ifndef METHOD_BUFFERED +#define METHOD_BUFFERED 0 +#endif +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0x00000000 +#endif +#ifndef FILE_DEVICE_UNKNOWN +#define FILE_DEVICE_UNKNOWN 0x00000022 +#endif +#ifndef FILE_DEVICE_USB +#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN +#endif + +#ifndef CTL_CODE +#define CTL_CODE(DeviceType, Function, Method, Access)( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) +#endif + +typedef enum USB_CONNECTION_STATUS { + NoDeviceConnected, + DeviceConnected, + DeviceFailedEnumeration, + DeviceGeneralFailure, + DeviceCausedOvercurrent, + DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, + DeviceHubNestedTooDeeply, + DeviceInLegacyHub +} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; + +typedef enum USB_HUB_NODE { + UsbHub, + UsbMIParent +} USB_HUB_NODE; + +/* Cfgmgr32.dll interface */ +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); +DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); + +#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_HUB_CAPABILITIES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_ROOT_HUB_NAME \ + CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_INFORMATION \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ + CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// Most of the structures below need to be packed +#pragma pack(push, 1) + +typedef struct USB_INTERFACE_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bInterfaceNumber; + UCHAR bAlternateSetting; + UCHAR bNumEndpoints; + UCHAR bInterfaceClass; + UCHAR bInterfaceSubClass; + UCHAR bInterfaceProtocol; + UCHAR iInterface; +} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { + struct { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; + } req; + USB_CONFIGURATION_DESCRIPTOR data; +} USB_CONFIGURATION_DESCRIPTOR_SHORT; + +typedef struct USB_ENDPOINT_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + UCHAR bEndpointAddress; + UCHAR bmAttributes; + USHORT wMaxPacketSize; + UCHAR bInterval; +} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; + +typedef struct USB_DESCRIPTOR_REQUEST { + ULONG ConnectionIndex; + struct { + UCHAR bmRequest; + UCHAR bRequest; + USHORT wValue; + USHORT wIndex; + USHORT wLength; + } SetupPacket; +// UCHAR Data[0]; +} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; + +typedef struct USB_HUB_DESCRIPTOR { + UCHAR bDescriptorLength; + UCHAR bDescriptorType; + UCHAR bNumberOfPorts; + USHORT wHubCharacteristics; + UCHAR bPowerOnToPowerGood; + UCHAR bHubControlCurrent; + UCHAR bRemoveAndPowerMask[64]; +} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; + +typedef struct USB_ROOT_HUB_NAME { + ULONG ActualLength; + WCHAR RootHubName[1]; +} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; + +typedef struct USB_ROOT_HUB_NAME_FIXED { + ULONG ActualLength; + WCHAR RootHubName[MAX_PATH_LENGTH]; +} USB_ROOT_HUB_NAME_FIXED; + +typedef struct USB_NODE_CONNECTION_NAME { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[1]; +} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; + +typedef struct USB_NODE_CONNECTION_NAME_FIXED { + ULONG ConnectionIndex; + ULONG ActualLength; + WCHAR NodeName[MAX_PATH_LENGTH]; +} USB_NODE_CONNECTION_NAME_FIXED; + +typedef struct USB_HUB_NAME_FIXED { + union { + USB_ROOT_HUB_NAME_FIXED root; + USB_NODE_CONNECTION_NAME_FIXED node; + } u; +} USB_HUB_NAME_FIXED; + +typedef struct USB_HUB_INFORMATION { + USB_HUB_DESCRIPTOR HubDescriptor; + BOOLEAN HubIsBusPowered; +} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; + +typedef struct USB_MI_PARENT_INFORMATION { + ULONG NumberOfInterfaces; +} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; + +typedef struct USB_NODE_INFORMATION { + USB_HUB_NODE NodeType; + union { + USB_HUB_INFORMATION HubInformation; + USB_MI_PARENT_INFORMATION MiParentInformation; + } u; +} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; + +typedef struct USB_PIPE_INFO { + USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; + ULONG ScheduleOffset; +} USB_PIPE_INFO, *PUSB_PIPE_INFO; + +typedef struct USB_NODE_CONNECTION_INFORMATION_EX { + ULONG ConnectionIndex; + USB_DEVICE_DESCRIPTOR DeviceDescriptor; + UCHAR CurrentConfigurationValue; + UCHAR Speed; + BOOLEAN DeviceIsHub; + USHORT DeviceAddress; + ULONG NumberOfOpenPipes; + USB_CONNECTION_STATUS ConnectionStatus; +// USB_PIPE_INFO PipeList[0]; +} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; + +typedef struct USB_HUB_CAP_FLAGS { + ULONG HubIsHighSpeedCapable:1; + ULONG HubIsHighSpeed:1; + ULONG HubIsMultiTtCapable:1; + ULONG HubIsMultiTt:1; + ULONG HubIsRoot:1; + ULONG HubIsArmedWakeOnConnect:1; + ULONG ReservedMBZ:26; +} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; + +typedef struct USB_HUB_CAPABILITIES { + ULONG HubIs2xCapable : 1; +} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; + +typedef struct USB_HUB_CAPABILITIES_EX { + USB_HUB_CAP_FLAGS CapabilityFlags; +} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; + +#pragma pack(pop) + +/* winusb.dll interface */ + +#define SHORT_PACKET_TERMINATE 0x01 +#define AUTO_CLEAR_STALL 0x02 +#define PIPE_TRANSFER_TIMEOUT 0x03 +#define IGNORE_SHORT_PACKETS 0x04 +#define ALLOW_PARTIAL_READS 0x05 +#define AUTO_FLUSH 0x06 +#define RAW_IO 0x07 +#define MAXIMUM_TRANSFER_SIZE 0x08 +#define AUTO_SUSPEND 0x81 +#define SUSPEND_DELAY 0x83 +#define DEVICE_SPEED 0x01 +#define LowSpeed 0x01 +#define FullSpeed 0x02 +#define HighSpeed 0x03 + +typedef enum USBD_PIPE_TYPE { + UsbdPipeTypeControl, + UsbdPipeTypeIsochronous, + UsbdPipeTypeBulk, + UsbdPipeTypeInterrupt +} USBD_PIPE_TYPE; + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; +} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; + +#pragma pack(1) +typedef struct { + UCHAR request_type; + UCHAR request; + USHORT value; + USHORT index; + USHORT length; +} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; +#pragma pack() + +typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; + +DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); +DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); diff --git a/interface/external/LibUSB/include/version.h b/interface/external/LibUSB/include/version.h new file mode 100644 index 0000000000..62446da871 --- /dev/null +++ b/interface/external/LibUSB/include/version.h @@ -0,0 +1,18 @@ +/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ +#ifndef LIBUSB_MAJOR +#define LIBUSB_MAJOR 1 +#endif +#ifndef LIBUSB_MINOR +#define LIBUSB_MINOR 0 +#endif +#ifndef LIBUSB_MICRO +#define LIBUSB_MICRO 9 +#endif +/* LIBUSB_NANO may be used for Windows internal versioning. 0 means unused. */ +#ifndef LIBUSB_NANO +#define LIBUSB_NANO 0 +#endif +/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ +#ifndef LIBUSB_RC +#define LIBUSB_RC "" +#endif diff --git a/interface/external/LibUSB/lib/UNIX/libusb-1.0.a b/interface/external/LibUSB/lib/UNIX/libusb-1.0.a new file mode 100644 index 0000000000000000000000000000000000000000..871391de51946ef9a5ef085809f7b5727e1df978 GIT binary patch literal 372942 zcmeFadw5jU)d#%KoJklGn1p~D@xmyf29=N?A}A;!2}~e_mZ z2+9!aG{%C}*0xr(H*Kp{t*^CeZM=f5w$!#(dr_*iqG+{vsjb$0&)R#fbN1|+Y2Wwh z_kPd!$Ge{=Icxv++H0@9_S(0z&zy70!ayR}dw$7mUv{WF>zp}t3(h`k{yd+rmM6*o zeZDz!YUha@7nd-`+87&u|Nn3Qw6oUz>%TXbvi~ptT(5rF{?l82#)@vNNF{qBMj#rE z2aS-~9|@X_Bb!VM<3`U`BWWg+k$B9Agjh7vlS=mR^qy4Gh^4mlmCTs|-A#)IVYJVV_%Bo6E-W)F8i*neg?offGm59XT z@{K?;7>N*z7(J{+AeIa}V*wYdg2`0RmWY)D2LrL78FgS@z1~196g6$o2*$VcMNQrz z^QUtm(Jarh5?hUEJh<5~`^}hTk4AZ*R4fn3a|uPj0P)RKWho)2Pdajpfk4DE6Lzt3 z8EP7^8sT_Ch^BJ-;?Zb0l(cOT*(c$el>_WqE5XHE5)gJ>g__@86QL%t5Y{kuZa)L=uMR z+3M2f--Noe=OD+$C8$`PQENm4vx4!2SrebFpw4)bn-+=ZV#%$sU@jVU>Xe&k^(M?f zC~5S?laWD4o-Gz*b`Yzzxo6FpX3LdtMgrGKPhaZBbkf2PClAL%OhXF3*hIMY<=&$t9z<^}(7w5r@cyrIg| zG90R^Y#EML`TXf8svj?##{8L=>wd=>kNmo9+G1AU)iM(I)ps`49qRID-tX{dK5EIl z?N7hy!9p7<{prWc{R;nix3tXWY7Pv& zmFo5n7olrdMgGjoV+Z`hm(}{y`+TB8!wr7_Ff{IEwf;;wC)4|U{>&0=7OHsq36DSX z^7@UhgD_}MK-O;rV+i|Z=`&~O1IyMz+cReANR z>$~bZ$yI&~2K1-jEEif2jap^?eY{=$`xbjV-1mRyFKavr>*~)mR(Z;H{Rk39tSZob z>=;NJ8ZEne51_(p&saXP@GZ!`GV>;hZq7VqZC^R`*7ALB4sD;_RCj3i^5Jc-iqcnP z+<$IPKcR6ED;NAJb*N={dP^q7Rt_DqPN;he{bP9Bk#c|fNll17?~KG-k}=C$GKYny z)?f6v@F%WN@NmoU^dt8-?|bt7v9YlucM|c;ZvXJI#j?L*yN(?!n|1}SH25R;L*BDs zsNjP~`pxR}C)4UP@7F*4$=KM~j_F*VAHe|7n0dXa?$9s)m7n+M>N1Gk=kcTAz6$^{9XR=ISF4{YU!L-}e#d^Q3>Mp*r=S>hrf>L(NXvTz%yJ z@NTfrVK9lXPlyktmFZ8WW!|sP{A!1vo85HP9GZcmo+W|*BM^s9vV%#GUvQ2%g;oGKf(M1#(M$gpLc!?8Co)yn2C6$IMkeZ zf=B#AZp!a27UO9B=UH^Bj6YD;B5bkWy_j}tq!wc zWy`RIQ=&i3ZOzJdz4FP}7|)mHBeP}~`Y@j-AK^X!TQ>ht4>(kYk8GIZ^;KhIV__cb z`K@krXsqn|(Z^{#=1qo;_#})Ahk5(eXWpaM^Ghz!oO!-E^Q?cEmohwVXtZqC^Ng|d zC$6$xQ#By8)cEv=uCi-EbN|S;s&X`yW2drR`=MQi8>=e)!+fyy54Tj6w=8&Ud#OMD zy4HN<=;9@1%s=zd=9vfT(;sX83?ye7t19b%2ySK7paLSAm{y(d|Wf_{Lz zU|Hk-2Z4BmJ4)u|y3v-*+$!D_1o<*{EyT4#dYVE$?W%MCJMjjE!L8G?DctLV`F^WPJgJCeeTF7V`E2dhX%3R!++(Q zFEGY-jY0(%w`5wX{MI>TGaIYg%4W7yU0gP^v#PIb=1o;Fc$dxmM%8Y9y`$K(C3`VZ81F)JD$s?EHG9vTxdcHD{ z>MwlB=^#Qx`mi=MD$KZj!g`*{Gw$Z3e;+q61YW@F-Qei`rSYs~q@`+FQ{8CY(+|RY{|MOKpLzMI#;R#xO3z`P zv3>3;@FrgL(5N*j{h^k+tL*;9s%e=fRyTU&YyRPe#gegfkTE=1i`IHzY;0`o^-H;D zu=+LitD7^QtQ>g+TDHDx<;Y!7WPN9I=Dn^CfBH?)QZ1P=UR?9=Rab+wk=_se!yC)} z=_8)z1y81YN8+|W!a&ycp?{w6&GuXpr`iaVx%xkpbsW)&L*~n)E&_Q?~7?cY8 zpy^Dvb&8Y-uGy0L8yfrd3Sno70do_>Uq2ZebNms8zz_K%pgHrgSO=uvto9F$J=IuM zrj_mbEN{zYi~Ymhwf>Rseh5*VPSWXj7-JemhcBA|Ys#_*ZC~*Zjc(sWQ^N3Np4{ZJ zYhl&YlKC@m{qdhLpo^(0{oO3tBm~^J9Nf4PhA6SA67A#Ew{Fy*d6`=UZOw)6DlGiS zx67tU`IWx@`bE&!?D31%^Oa;GE9oEk-v8JveyUMkvuuz2J3FMGs9e7h_LEM%6}<~d z5IODrNx}TDZ-sjE_2B{V3%<@d43p$L&9L*{R`ntH&OsPHZJ zt#F_ne>6q^y>GJzC9km?JG_}!&yg73+*`mM#9wxc!mf>wUIP!6|CG&Pu=40+b)Kqjm z@_^XK*FXH#u`w}B9r-4$FX|uW>y#VpcvpSq*Y%zCnfJQt-h%OMKJ)^ge_vyjQ_H&N z{6nK#Jx4AI@8;XU;cYMxpE%n;^gHW3zi?nM$3>=Fx**H4MV>_a{zAz*edYij{H*O<9o}0c4Xd#T_wmC!zJtwU* zwki)_u9w}v#=Yw~Ye(7rZ?p^>t+c(d{eKhqYD16DBv+ zjkb)0t|)4L^pM_AcE6>$hd!`QTQS_Cm7lxTWi9uQ)cfk5JK_a@d(@vE^_|=OiZwU= zn6}{g9d9+KN2^yZIGB2>IsI69^MW^0w{iurp6Msbp+ffhWaw$@VA&5M?y~#W>b&ad zSM|){8DrxZOMj$WMd^>U)K9{XzEui2GjFU9iU@3Hy~kNbx&udN%E`UH%f zT*?E`)<^d2{ulPSm+bQ+*v^#Qzs$XB)Y@8h|Kb1GK1=f2=gflk;R^8j@!d4P?=VtE=j|QU6G16(9f0>4w6f z7nTU8Ec6VVaCO7Eb)(z`L;ka-@NB8m{OKoZ*FO$-SPEDimJLUq=K4D0Z+c&lFLcvm z!2yqd*!@rF7wUg-Pd}Ye zSxxQjtJ;lKo&KiwY^Zfrr_s^X*0!p>v#C){GFCRN%*L8qJDb|uyV`P?+nU>&vRPNH zZfalJvT98>+S$CaX;oKeHn^m#V{JA9YAs#W)takPW7F#92BoO_hK8n&j%*3dt*h%> znj4L_`u2LY603-{qf6uzA<)+s#REZK)C^m`WPFS16K9}FA3q!9;ZidZ_w{VG%%m@2 zTB$_L4EZ84U(iZKeRR%r0o=sl$Jjn`&fyEf>0`(@&>M-GHk)rCkn|<3NHpr}F(a`} zzP>~}XeN_p=uBTv%JNygq<}9%j6rdhV1|5wq%Ryuz-7Snfwu_#>V5JFlrJ1l_yRFs zBsMD?jcn?*oWgqp$=GR@&puP(MS`B7iBzEY(fUu-mY1^{3kTVZ{kk4UpVoEhruM=)h7H%SO;T|2;&I1 zo$#1w?Q&M)IY9?U0k?32w?MtHdz5&KXpy_h)!7Uo#u9<{@zn$M(?R!;gHB!wrCbHmR$fO zNi16|VjfoF0a;wfvU(u7iK@$5@3eEaq0@7Nm0c)$z3As0*orxlBgYr7K9m<=dd=BqzQ1$Y;P@o_$gMpHhlV$+P+siYl zx~iD5ig1WcI$f^*q%(fbT}L$6q%%dVm`&yut@!)-#f(h>`U;I-7~{t`?uv0*h>`?@ zNOy5Aow$zE?#suebFL!(znK3Y$Nziy|MC2P3IAWp|4$HuZAHbk+V$gbu&vmw{eZWX z7;GzUz^Lfk6<^XGJ-(pl3 z<@^Da!s;HxRIJ}2jAH#>!zkA82-m?yI{pQR{7P2hIp4#W7aWk!ebp5oXhq{m-49U; zs{2ojqPol_8Vl9cF^cMr!zij-f>CT8FGf-O3XG!m$6yq-Ki&l@+1j7v0)x76lCAK$ zg!B7YiKlctV_tA4UYYWWPq`M_m6^$Dn#e28#B4AeE`e+f9@_?UUH`DN%|l(#2IpZE zZE(IT%O&eE6)W9{QLOYbjAEsiV-zdB3Zq!*4z7b;={4DvUME$Wl`!TlACKBsR_t;` zOQ;2};*_@sjQ1{FGwI_oRjhmT5=<2f z9le~V+N%?N5>K{Q8~PMX$JK&<45sfF&8Hu)@j-U_I~bpe=^tYJByD0DtX6Ij2GdX0 z&gSvkF+Pn}Cm25!^FM&`8Mrz*i1C@2ei-AYYcMb9(|3zbqtB+*jE)9=(DhO&Hf*=P zK-)P18&+S)B~iobi?Bym9~-m=nSOzG2e+wMTI%)KamCV7UxLvEq5&E(daj5zYOhUz z23RPfOR)jOI#ge#!35;Og{SV5?uI=MdKTSbDaZR3xQ&gUbicc?Y;{BtmKbv<4Pv6e}U(Ww;Zski zSYO~UvmT?^GcVGi60jN+{VuA%KM|IeA5CQJcx{}Qv8nKnm3St4(fo@mEd3=fiSOq$ zxl6@XT@2kg)@;*Z0&q=ifbhrCd(aMOyfVP4^;72nj~;He8)i3c3N!~CW}^uZ{0^fN^?gay_~tBqIZ>3WqG~c(SNC3Q$h9mwI*g8tk*&9 zrV6MCN_tKE29FA#)2F(A%A>-`^m(q=cvNh&^~J8g^C;GOjqCWyj+5yz*ZDk(W8qb< zULM7v<}0o=kK%N5hwEk@729n6#~8)g=Kw~kP{9QV!oI{kQ_j{9c4UvK1b znhCGeO&-U6vp%GMn#XBo9M;7?`vA^_yD|O(#=n5^gBZUF?H6JCyCO(VW)%Bh;$erSc&I}$xvfxeXbTO@w{v^!@x3?k1i!17&b~gf92fJ z_JmKILRC8-P-v2=oO%`Nhx+}c2r^HzSq)(2!zECx#B(pd8M1r|V<&63aeDI<#-@Ra z+I|<`GiYy5VGN#CW7^X$nTCp-!ttN-W$URT@+@EB3i@Yq!gGA3E)sqv5~iDdO#2Pr z2S6zCEE~@le|(F%@D`DF`Z0|0pGvq^!q|-@R02t8M)oQAr*~_5E3xa{Fr;%HK_bs) zP2XhmYz7{7nz*^zscYKjj$y1CRx4Vk4zy5rHJ`#*i3gU#!a6T%8aTIh-!Y6$PnZ_d ze#Zee6Ex9WE=_y!7&4s81-#QEOmn-qx~KCJ-6v|iQ|W&vPWOp?z(iF$G`-?jszV1? zRT!t}Vv$cuzaHEyE_~dW< z)g*4L*}OR_TrdPcn4MtSWbQu@YQaC`pv`dAEkUgA96J&9T3T4Idu_dzDtaxm^%A-? zOS(YIFSp}tj;Q`!E`N}7AqZdnvh9KAs+nd-n0AYuEL`SR$3<9)2O1vDy4l6MSH3X*1nMZ8 zT|h$+g-2aw7(Wj{Eyl+e*B<|D?vHWTF_;W}rO#0%!n9Ajpf^JhE#kx%##0v4M#Om& zV{Fk4l9?cAG1oVzGGW|#x7ms3^F%59bSlGIU+vWT0vIUOpk3>#dkpa$GtT>&lcU7* z=@Kv;v_XjnMgr_;*SqsNvunV!>I5c!i>F=hh8>gB(T=(cb($N%;@tm_@;nfP#oy)U zQaoV8O)APO1;RgUF_Vg*Xb3_~MG>EwaTJ?k$As-qwI7_OLgp1N#5j?3ou?FXjx83N ze}Nqng*4|B5-eQE{dOTirI3&op?-%Z?@wNurN}CR$u(_$&A&#NoftR zC{lyAy7<&-jMWvxDh0v?dAZgUI~=UU1C~K|x;4KcTmznwX*6EkTKsGgn6fb#=wsT~ zi{Z9})-5BI3U-hUFH7LUFzZv**a)7sE=;iT}aQ z-v|$KX)hJ?y90X`i@D%s9vzQGFxCJ|NHu5^$0dD?)zxBJ-3(&7pn)hUXyxPTqQn{? zVzteQljt$ya(ooRiKmchju}TDB&vSQxNOrLJ1*NaPW%i}xyo_hfOY|%)A#f@4LQWjw@610@K+sR6u6w64Q{3g|J5(Iv@3!X+ zUi#!yvr5mI^JZIH`k3|uH*8Wx;Kz)Rjq6v*yioE|NYf_q-`C}YL!cV(u#?=`=B{w_ zO}Fh?Q{3Vf7Q=FnvEy9X_p+5;1{t3`m6mQNyZOCISc2!QvF5m?HCAr&5;>Vy5Q^dz z^e`{zj}Emue+_S)PJL&!ZG16Ab=x%-{<4!lhCPogtC^nK`WF^=~>ENRQ2Cb zc0~X8%7P%S>=fn&Rmj!#f)hmP4BOT1O@ZjXcj=G@L0JE8yFy}he~(W7CbrS{^1DgU z6yFyc>b(26pXvGbZzUdBNujoX(KT9Eso^ka%f0_?J$upowhkv;5QO$lT$ul_I;@&q zdln1jkLvb@ldlNhwc~>QJzhCjb4Qr=KCc-BsNDy4?ZlF6f}1BXTsw>WAM0@7uJ5^J zhh42x3pc{iPf;VBsx-oM?llmE-ZON#Ndut~?yh`Fhf`LqT_m2S!(##vX7>l8OslqS z0(#H1_2wPKby}9s)run%Cuno@x*M=8zT=vAs~nqu*A&L)J(vaU1L&7BH18nBMBp6? z3g$j~GV?;kvDN>Yf3pkWucwiD{>pc|5JW2<<@c!|2n)Q&?_ojU=6RpreA3!^v)LOZ zUVW|l-&B9h^}kbnD417&D)U0cu==wKSAV3Msz1x#aY2n{+cgqCSL*^!*Pb?Ei#mQb z4M8N&;q0zGExi6;&91*V#Q>1&Hc1R0xU4#DoVahL1lNG)a5c5WY2#?`BTAn+4vzbH z@bi&UYa6GH=eN(YgKNN3;whWSn762ev1Ulr=7B6{8W-R>aK`L_%Yn1>+wdTmrisXR z{WWPo7=MSTS;X z85=iQ)4ipozzZy%iFU^MC5(+PW2L2xmB0l=mD=1zz&!!Ei<&w2L@Jdr@242>mmQ}$ zvdbOWj7^#>rBqC0x<{xBrA~Hd=`^L9>E073GIorO^U{ySTv+;XSb7l@T*(zJqG}y~ zjCR@t(0FPQ*XsnZ1Y;)xENpYqG3>MnjGYV@NSaooaSIkf)W>b(D-yQh1)MThikw=i zF*bd!s02^0s?jc(Aeul3n9GZwfg-puKZQkndh!KExSYAPaXCKetqa2xI3-LQ5E1Sb`Rs zq%pQgRu~jIU*rPv0!RZ9qB4u6nwKzEpHnKY(2^2tW{}<>O8}9LlDSG4YXZ%hC(oS# zk)=*!Ih9xjHHDo18ixMC9gK6pID~a8ZJrD=mOIf4Ccr}$D`WsTS`a8|Mh-jR%{t~{ z10JFu##9#@s3jU+BLD&Thtc%ER+t6xwzYYYFq%pYppEt}#RAZe) z@}IE5h|D(T3fibjp$!k8V0f-JB5$>2caWeb9f-)MZKw+=JMc{rW9-w2XsnF{Il15! zL|I`c#;ACm^_TC>;$X~Xg7-Q=&~oJ*z@+E!3T;F*yv6~7iw%?${d)(iBpO~10l~!v zd_=$DVAVv!v&hI-OSIT@@VwPTpX>1Xh=wyE%v(wH`3_c2^kN6|5Z&ZpIle~q|FaMe zzDDHVY^Z}sDn)0PU%dAM6iebchm-t@rm@wks64*>2?~ID0D{Kai9FVZ zE+kT%fpIP(XF6O&*4j{et_Bxkfp(y(P;M8i-AgnziN-b%NfI^IMkHAZa@t}sT!Fe= ziC1t$qQB%|mZUO<>?)_pn!*@!ib)XCk`;u5k1Ah za+D%jKXH^I@((uDLF8*T)J9|x3fEW%kyPHlQ2jYuP$!XPwxFDWj{lgQF-X%X2-P)D2k>E0r{EPFgy>TpteohX z4pvF@bq?ku`U?(LN%R*Tteogu9Lz)XHyq4I^fw)>lIU+aSUJ(RIarRbP+e<~M`K+? zl8PGZAo42?*G))L!5ZsO+vNgep?c*pwI5lifc#~tg2PHKH%pGPy(EK_bhFeb%jx}h zW8Qo43XMzjy$)7R^bZ}ZlITA>n2+c;9ITS)BMw$h^g9mbA^JTB^AY{NgH;m!frFJ3 z{Wk~8Xe6|AuiBB?wrcYQ&{J!=cD_)CDG#?%tv&ogH;oKoP*U8{W4-Y8yK}CuS$$WA9Ap2qW|n*KBD2(PY_&eppxh} z9ITw^cO1+^bQ#{CxElVJ4DL%(eZO9VFEBGDJ-*Ye@(GNJ7hv=U>n2+cK4(1{H_YTINca;O_(T6-5>mu@2 z8|onPZX4>l2gyfms6#C;kJYI-%g)tF zNE&?C7PNs#M}rG8_%PQjM{o_2^BhPmD>qkXZmyiN7NfwGj^I`#S38heR&K7& z+*~iC5^SME{)s+=egBYKcDJU>>4R!4*D?d#Y9>|0YLqA|FOk8e6SO%CjZA z7X{pJ3u+^>$`-VN1c_hD!?GdAJrAIO7aT!E-fau&cn?WOP#cj{-8|i&l$?sWVeTQ? zXA5r&Ai2$fb|Co~2O`qZ>LRszAU{{FoWb}oR#m+EBd=8!W4WuG@`+rJkc%bxPfO*X z^5j`cEk^GMI$q)-dJtcdq_cqocH{~K!4--AzRaiwDj|j?XsqipByV+~ucHVOm1jLg zg1&C&>Ll_K%&oBvTXO{E_!tSg%@%Yak(Mp!qFg~26{;HL)>u1{aa+*FTtPXrIB7Iw zYt&7oX$xAND=24vq2fQb1$9hE0T4GbLA zDAI%=FsciX@5p7pm6TxjKogx1rU!f^x%%2^-$1GN{w@P`H2Y9iqY83-EdAaa%stv(0IgahRkm(%Y)QHm>O z{pH2;Puwehu+5}0a|Uoys|pKrv;G&@Ueb)z5U8q&K-I<+zvX6jUVi+gGF^|wky&-N zt#&U(Ucp42rE-r^+t881c!lYY=t=mS9_TGZS2~!7=za&w+0c;ut5Ckix`>3AJwVXd zh6*G}Ql5RtHWWa)i{V_TczP|xKa1(bY#YZZ{)t_1M{X%O%Z59U`vE&w8|A99)y>&3 z{1CaREI8=sQmT~i@PqO_iurZ6$Jk4epV%sN5=rtopIQJ1RmY$bNP>$cb5=K3pr%*i z6Sz^m>BXW@q8B|=thT4c6vLS=W8*C_NOr4BLB-_pU_vbzbKwNc& z1>KEF_u>`&iRd3VSUJ%TIanppk2si*=*Jwan&`!oge7%0aL|r4BIsfR)kHTrn2+dX z4pvEYvxAirz1+b(M6W}P7pw-dz^_o_Jlj`d*=MtK5=p9PY=c@_o@tr{Ib~f)B}^E11(&E=KMiyn=@l9dxjAqBl8MCDB_P%t!PT zcy$f#mWV#a!F)s?>tK~cS2|cZ(LM+B5Z&rvwM1X&U_PR|9jub*jSf~$^hFNlA$o^{ zWG9VvoQ(pgS{myh*`#Hj9=8`|5^KJW`$gor(=NZQ9t$ITX>2u-VY?zZJ&pvN zZnNamT6lpIDA!Y1e7+t>X|MbX9!mQ};vss1t zBdDF)sLAHaxqE&ECS8eF=y60}?O^3Z{|qs{Goi@OB}SqTIGBg%59G{hAWd#VCXICw zNisFop^D1mW4EJ#du%~DosnS7Li zkbSG+Q(3=&b;6}c!k2O(4B<6{SNH@rgq!fH273DVT6_+a9@M@8|1>{6 z#~j1*5_sK;*URyGC0^Cw#fRGHaZ7r3v=(tS{7?Dm?xT2jBeU-()16y2&>ctd+DOK_ z@c!Wjyc&27;#CcFGxBCkzZI`{7T_<%b#NJ8kHza$yq=2JS$I{$LBwfkeH7!gTTsI; zoVIB_Lu-y35&sHa@5k#8@%mf59>i-Aj#Fy*-!!G!-&u56+)w%$qwa8i}O_ zXKjk5X7@y_B$HW0BpT_Ny(t*X<)2$KkA!7q%jHaNO~QK!g7Jh|69j#eRw5GHbXH9- zs3ktX6{+bJDWPbjr=}M^_>;6kk@y_Jl!`@?R!Gnbds308H7gS1?_5h-p+I6&O)os~ zkhDS}Gc0IpYoD1E8Mq)JGaBiMAZbOmm;yvfW312Wg)gBgd1IE~>x)ODHNDJDI@k0v z{yg1yd+63b=hi%Xljpy-;DWlnZkPV>w&iY3e^y`E;&$ox@W|78&5G0tw@bfoo8{K@ zpX=RjUB7?ZPPeB2RA0Wrt?Tz~TPPxKU4LNP&K1pWO@CTnxK2-ao?kR;tv)L}XKC%Z zSIuu->zV9X^LFEqK6prfg&m!|;<3pq^zjc)UZn5T_WKsCU9n5Q>GC@~dlxs=>*KqB za_;$OeCFv^{nsv6wf6b-YZsljY2C_)*12{4>Pz>#HT@q&JKef|)uqc9xi$UoMNs-@ zE(P`fUbKAOkpKKMuhYM`{pg(^=B`d_v$U%xh` zU#IQ-p1yUV5&6y4=_?nlJ#W$6^U}=acJ;!)Ull)JyjcHe`|_vPtXQ*N|BSX%zr?fr z&a-uEx4zeX^W+u!AJ~Ia^plQyzBa$!tXr#Z-Pv&8E`6)3N_%M5qS<=oeoRQ8rf1CuqofE){pCrL>yC&>hJ8RM0J#FjGU8g4&KBSk| zq<8JQ1q`VFc!%fEi);GNTf135ZRZOqeUay8?J|ANZrAkV^{sboy6l$gfUUT@`1*%D z_pDjFqJPo(%hqlP=@V;GdLkUvFAeJzVRw=KV&af~vd7YUJfPV<6Zh-uTr;$_YxeR| z20RP(IUdWi+;z?|={26;tkYlG?x~q_uJ8Ogx_9?p&#v@CwR`p~n#P&ULtFRi$9ih? zsPCRNck18VcJ#M>m#nSTr*!La&rVNho#*_O>#khpd0Kyd`~J)Ht(IO=qxV~PuOD*F zKQ^LIyxFbm4-a}O+?u{`99ZFpgWcduPmKd#dT63Tr14nhA=a6g0F^T4eJ?AZo?47k}^Dehb|NK^H$0Nnv&|$CV+21bi-ZN{_ zJh!foY<F*WuPCL96s`p+oRP}RPMW;Plyl`#fkRElNpn5` zy+gN-u3xcY%>}7jTKu&q_nv&d9$V7uU`oNjpy}KcI(DM{gmap z2|_Eob<-AkcK1g8(&yK&Sf^Wecv@Dk)9>AWbmxcfu3X!;Xtuu9)9u-Fss7sdLsRr= zp5^yVaTPO7i?k);p;XXf2{RmwS*)i%YMF^xz%n~b%YrYt;x}H!cUSTIq=u2S5@uiv zCAK$R*wxh0X>>Go8cUn%JGtGKWJ(AQUn~MzDt^BbS7@>$OxJJNh8qLXU0M#Mf&oi^eIZi*c^$4R3#c(n(EtaHBkM|_WIV2 zrA_Td=i0U=V@X%b3TE~hFq_cK0$+p-8DNNhGiD|EloAdZTjHTec&ibO2RB2tQGLS* zZ?Ra9_%3Qks;40uNG6$K;4EcCBT37!n9;n-NCx^%LsTVc8NFs8!~#((DXeQmLW3+A zH3JEwHxRX$0bi>OZjOK=h7mIdt!zD*VT1x!fD7gy6g7GRNt3K3K6z;veF?Ll@vpp! zuUxWlJQWKuQHi7#G7|~P(r+e`k$B9YFNrex(y7sxi1Zs!T{5BkMlh8yg7Ii- zON<%DKwz_(>NAq&rX_u#$%g&@VUg084jeP7MVq~VNY|+G86EIETNQvK8wySSFOIW zu^m1s46e8(kl1X(SDE`GK@*I{dmcE7=yTl3#h0709`t2assC}8;BK5XGVsqPhvSJY z0ZV+A7HVU}Qd@eQrZut8%LX%ySSo2Ik|fRUs12)DwzV|D=b5F#q4N``@JHwS%EBm6 ztZiKQ{H_`D1$zUwdu=uoF*9m}5|Ms0VTiBXvQ#8y&By+_w7I2;g<-5RKrzwZ@)(wX zKzeC?Llbl|?y*ky-;%P-K_kg}f*~o zY>N+=2^QhrkuN#vq9L(6+vQO|hQ2)zv3iaENXX~|3-odO z8q~0c6|kpu+d25WXnz!)>VUI%!rT(?7xOobyQHT*)~s!9@Ec3p>sL0hc#_W^tS1!? zn+cXQlVXkuF~bP-F&YM#)el`P$odjyI5J2QJKCC>8sSs!P3RTI{T3VVKSt2kXMlVSxbRIET@NZ?qc#CMyRzorz<|5M>p-93M zTJn4_QSvngOdIxC1M^1@j3B&^cSJ5VYjgS&crlA~n2AUr+A2nCP$Jv1IL8{i`^;hC zNGxQl;&dH8oAjlyyw6JTFq8^YGb0`ik@IYc1QT&F zE1Myw4<}h5v56LitfymZEZ9p+S8R0Q6-&EXTA)v-!mMXYy=5gLJt@nioJnXdC>Cph z+T;%FJ;6{{#}Zf%_KLNDy&#~`IoFS9LYIWvUBL)MVR#WsdhRmH!a`X$bvCbTYUJHv zAj^TMvNYdxHnq2RwZSZnrAo^|7@Nf+RV;sFsX-%^>Fc^FapuY zrWlK?48&4lm}nAaf^D(~;Di}914+}V9ciVEW*w^<{Ozk+S9Nvpc>%s35U{L3us3@Z zkdxc-J@BlMnY0q|tzuGVFiG&5^`yeW=B)Rj^0_;*qe+%s16nzfzTXrIZIsTip$x|NP_!C?4jB)k}%H3W1*yBu`Q8U++GCY z`q5eJ8%8+9U?Uce#s`c*Vv`X_m;n}Ph|8;vNN6>mU~x#GY74Ib-+_S?Z9)CH6UOwm zRqc|qgv>mxVHiuhS{pi>SG5`jU)D1NZ6A&#k`{w4naNi|S>8>~EjyqQjHhCj@b_dO z7O`OSiJe~2&RE&BQfwl)Q=&I>I%M|K;y)|XlCF-m+_4g2dlQ=Nfeou#JKI;auz}9F z6^OQorIIb$GQ})qh>?jeJY%UX(tw%7^*u9VR$?pVOj?0hD3Ayl3G>q_Gih-hl6|JV zGH$4EGyL@(aIIg`+|t~+mK3*V>!wanZfWzfuJ(E`H7v-um!oyUAl;ty`G8@&Qx8=H zCY#(D$1uXM{ACGqli2)n2U6B?4A+e?Uf0dz!*x6yHY~ARqnc8YVxyjAe0y00T_?*q zlc2nPzX8U7SQI8;Ne@#*B*Y>iy6eF!77iJ)xD^RUf&o77_la8~uz19_QYVp+*^}DD zq*bcH%P=fnoY++06e=(N#A*tM{w;w^;tA$#_qUi^q{XFO^I(q=j=?w-hiz#{+UqO( z#^zOgSWWdQw*;Jf3(4Mi!r~)?J=E}ZxN!TB8Kvq{;ZQAbTt3NW&6qTc!S=0ZE6i`= zW`zN*IBzc8;IYfFZ;ya)JGVYq&`Ly&zDS>4rQBh?H=eX$@`watEEeXYS5LbcPMFDF z($#4idsK$m25-%XA(K|dNyb-kd>3O31R|E1NJ2A(Shz0{iCJN3)3T<%y|uY@nedRf zGi7CU1+gHK>WV9xU31S@!Q9kS|k|O)Hnuc%*sGR)8Vj==IfmE0=i7mv}u( zN*O$0u?^+p`$jmnI}ZjQGJ$~a)#0DprkCkXg@k%ruX5FUE7STiug?`J@m4l^%j>df-diGjV3-L%Be`UtouMIKM3)(q^7<~%_u)#doHF+?4;)?Qu ze)l2Ytx%Vh*SN1L>h{)N>l)G1#ro+QSC5zRd*q{M$RIDhhcLO;u`3SSHT#Pb}#kTx^64sV@Ew7JN%`w60fhp zTe-|z-UOZ~#-ROJ{!uJnm2s=cBxE#}whNIBTqMYVzCrKX;_rauW6Cn`V%O3VJ^<7U zpQ1O4@z0P$UxCkKV8_IFzKBqh+`7dI^st(wl@Qlnq7r5_P{Z%j*YK&KrYD(XHHkPx zfW5A!E;~A(i9CV@BYfp=L}KAMLpspY1IKNGS0uw7YB?TA1Y(=aB;}wO*9db&VkRXg ztpuwH#sjzB9qx`gcEQ>^8cj(?Er3&WlL$EQJ(t2X>{52dsh$*;d{$7)Y2e2|<F)U!~AOheE{=!EB2@QQ+TC-{oXJDN5T_S@Y#}8Qd!zVb^@*z2I`M6*i)=y(@dz`IC1y17a?B;7p2qY0c7zO(OehP>=(1}^($j6iu~}Aa|qZ>{1+J@=LW=SuOP;$aeTalKwmJ0{J>A+ z9DZh%-G?~;Xe(p(hhUyUoaP~t10N3oIrMQ5pX3xepLueFZfl(#bcSzpQG#w;D?8|X zWXuhER%|zNGyS3pK0N_A zz4VyhPhgDw2yuETF29Gs7<&nEdJip)1F#bDi_|S4>%23wEu^G3^={#mGFg# z(>qxShkZEkUySPp!Xt>&+fw1^=V%$YCFGP0hKEw2!oHS>g~39(^P% z?=zgp|F6zP^PcTUs)?h21h`9WGA%RK507(N@u;v`B%bvYiSn4dbRLICd3Ycf4~GI< z=>dJiz=J3w*b6^BpmQl64fmz2pe=~h;RkVCdL%9)s3$mE){z+{z@y8FcF&;HbhoMs9mcXF&pp1CdPf9NM z3zL0xREJ=U@Pf$qDj7lPvGxwa|UhpF5QBDXa z^Z#OR4tp)yFVKAs*t5f@%Re3yVTr^KpKu{OCgIa1oaQT#1Lriv|5b?}zMxJx?E4@9 zKisn={D_3ha%SQB9{6QBLlQ3Y?~!ns|CbUDA6B-@m2kMnLpZG4Nl#h+VhNY!uaI!C z^YvICShwesvs1!lIiHnq_y8&C^9u=w&wdk5=UtEt_l^la2lvf@!>vogJ0u+Rq}$_3 z376%3LBeJG{6xZKeSRb1a{ErleGBLVPLBZ+1Qx?X?AQxS&WL5)uw`2z`J0N=eQNNq$DcS4#M9 z377lVEfOyGuWuK?AD3`h|NV%=1?)rZ`>ez-k5|8xaNq|UL3pVE{%6GDBFq140sKQr z4(RzI_FoNeRf9js{qYnD2i-}ZZ4wS=x8x6BMjVQP_M&r#y%G-XMdt(GljO+l^{Rx! znFPr{BH^;#8gXC*{bf8V;c~sU6~M2Sa5&4La(7F(EdMSEm*xLJ!ohBk2g0KgF0Wgj zmT=kMjv!9(?3y zm*p%fAm=j@F5B}95-!{Gb_s_U$x*xfmxRlHvQNTc>>>VFC0s7|sD#Vq{zJlLe>fgr zUQPC-`~4?NIM{*CuVx{x+Htqjc(a7d<=!db za=Aa2aM}LPNVshOmn2-a{}Bn7%l(Ih%jLT8Ezn>$+5VFdC;J~pKRi~#!4B|T1%w%r z9N7-DBwW^Kp@hr&Tqwy0`d1Dek#N}#3nUy~!$lUIgJu3127SHMjr+IqN0dM4bGv z78n>~mqNxl8cAyjt#R#MN@wOE{EE=iX6Cj$H0HBpmz!o06(4UtNieeyL|Sc^L~|oumC@;3snBA3-H5lDDstiV*&h@0{HC(@Vg7( zw2pve@LTe?(E|MY3*ZL|;4c)w4;H{_K8IvkANn1Fiho!@4j&lu`9F>isvMfnRlKqQ zKRutJ@>dt&r};tUpI?BV?)R$v^yXLL3*dbPaQa<_TJ9AE_^&R2 z?=FDTIIqg5-+8F`?FHn}^D!#_0|od;3*h?;;0FreFBHHJ7Qhb|!0C4=s{S7q;HT$) zRDKWMCs*G@Vw{uKrIuP%VY^FR5Hi|)qYC~u z;` z{@*E=zLZX{k*9Kh4x3K+NtMEX1n;NwSKvx=zKa$8w8DQn@{=Et{9|z+KBDli$NkmK z3f_SCN4}}x_u_pzy3a@QNe}#4jg9xhB+nRoLXmST8vdsW{u12mWQ;%e4uRxv!2JgK zFX7aGUgF#ie+KLIx`MC5{TTT*$)QgY(Q}l9{|W6d75$Fz&tV)NgST<=e_HnvzaJfT zks@ap_wBUqC4Mj3txMsj`${1N_h7qIzacrB(f@ZU{0+ds7^8D-;{O5i(fX6{=dr!M zr^ulv{!$1~;bg zoUJV9UbHj)2A1U0R~nyC_(zfdR|-z|RSzln&yfE;1^+&dC-7XZtWO&4c8r3LqW_<$ z;PAXD1X@>+{>|to^A!GHqdtua-iPf@^DN2v4(k1Nh5sAajx?VV{}tFS-&gpb#D4l? z1%CtQi)R)5^Eh7pUcnEb9cUh;a!Jky3jgKU?nO8*5iVUvQV(f%m~zY^nV1*iFAkAnB3K6@2>7q-j&3Qqgs zM-}|HU?_NANx^Tx`Sh@Y(}!z6QgE`vc+`vR-+}YaaSDDi`tuA0KMC9WECv4|>Iu*H zLLm9yL_4oi@D(`DZ&dJ|*e_xVz7X}iOu@-+S1CBzZI6PJ-R@Ly9sTM71uw?<;|hKS z>hm)N{}Ai-qJkel`y5j6@1ULER`A)_-#ys>$evGO|24xG33jRf`Z;OIAqd&JR_&)T{ZUz6Na1VBog8v=c-Bj>9QGP4wq3#D zcVZBBDfrLOpFgYM^pPp}(gXyu|GPLod_lnnvE6S{@WZJ8*A)C%)c21jf?tI8q34>Y+^?d)ZBqCt^ecEVWP~p*DL8$`^aceF<9JHXFHyN< z=X(@>vh%2d528XpSMUvJ|Cbc}RIJxq3VuJ@`C|pY2gj3A^cT`|9?GAp;Cs-{GZp+5 zZ0|+|@5J&hRB*Dx#R^V#h$}dKplrK>qdIIv!7s%2x=F#mhU+VOK8otQ7VYq$!v9lj z$EOwieOyPqpx_q9UsG_h+Xo6xcAJF$PWmKq95`9Q;dgEjW+`|S^{3wplbmO9oas{d z$^Ib)C;KNAJdFD6RB(D8VvmA%;dph6f>&ez{hor8KR=@2w_yK$R>6OTcKp49lbw$! zIN6!{G1-UC`6dG(joX)E`<|-c@1p(78?{g?hkAZV!GDD1-lpJ}p`YBZ;O8Lzq=J7M>-8%I{}b}Rs^B{Im!k^)PxJ>D%7bL6 z@7Iy9Lct>#KUKl$L%VYn{A;+LS)$-{U!z^Ye~kl2kAhbso=|XF@9tFa8XWhoQ}F+X zxHpfFqSzY0tEUqf63j#v35XJ8kboL8ibz-_6DH8X0YVlC0uEUwFqe?T%ml(3Fhm*0 zD7arY(CaSOUBo@?!R3m(kKU_@3m~|mA}%oR=Tx85Q#nbV=lQ+A_w#w{kEH6`r%#kA7%UvwDV8Kv73m#IB$?W z^f~7=#(%+lFJb%{TxU*X{Cec{MLg2K3CDqQCjTzV2N^HI{@%#=F!b{co$$LDCqK~r zMY5+4`sGC?Pj-IHIN5WEak`KHE8`lr|34W|#|qGJTp@crXirzhzrpgV@4rDmx(@x* zi^)&HdLPL6v#4hX;-$YFdqw)4vvpM>pX zI^&zMpUz}_Eb5=jxC{MW#yGvd6<~Z0=3C9U6UXNzj6aQhE#udsJ=Ze+Z}jJS#zWZt z>3u?K57V%n+{NVScfho+A^CWeqxTPq)BE>NFg=^h>Jl$Az7+lZ2IHf#U46p%xmfSt zGQI)lsU-9(*^`9h-kFR$u{{i9oUS)A8K-rZ!8kqloyj=$+X}|X{yN6V4{I4GKitIl z9oQc3WBg4V2cBYlF81FR#@C}C-e&xMY**BN$)A(4op;6ai2n=INsPlgTM$Mvo`Uww zVtgUav-Ex->HnI>Z6^N~@^y?qfaA~2j9-rJ>JG-&;(BHi5S8NgHsu&b~~JLnvcdaPT$e?GM<3-o6q=9=;va_Z$^Fv z2p0SL7f%bSAUySq69L9e~PS?$3&*|7NRxo)VmW%G&k^IHT zuf_3<_~%$Jw=z!i)P0Pv!T$9Gcx0h?5F24KGGZxg^Te^ zu$|;E-UF{gXEIKDN*O0Tix{W-bgLP6VgI_3@zZe}yPNSDc)jy5<5}1bpJV)b>=)aR zLou!M$oo=41V#UBcX>#`l#4o}C- zNI&gkGl21_SYK(#sa(rYe}*DY@9;nvt8j?YypY5ABdC9h!okjKv7OICPWjS)V9gnu&r8}err4*Db5uUn8)x#)fC zR>tXlltWC+^u?H0n`p?C7@)L5Z7kW?ncg7#Wa%tEJ z$#08MPZHy$IRExx{8i+s$SGfXPdZJJhba9nW`x4Q{x48ZrotggzayK3ob*?q{nMCy zi`g8+Y=wh<`n|ee;o#2^nC~K{pWaulWBfPFccr2S^cUc`dbPqqKV5%qP&mlbbBbFP z4)T9P|KF={kbfNe-Gj)<58Kh6#~D9>yoKrc3ia$`ygxJm_`S2j!JYxQzWSEwr}vwW zG47!2dPNV|zZ~@^;meoQ-X6elsi(qKzn!gc(9;XAF9s_0mw} zqd?K|6p@!!#&3y@R$DM$Sy6b|}n{g%b_&<__3#_9dD zGDQ#Ae+}wipm5Mn?J%ftkf-O`%M=dsOR)c5jhy^X`{rKH_()tQJfi3U{Xe5Uk1JfY z=LLm>{7}5UYf(7Jr^7+G9zss`(0;w|Gd=_D`9aYGDtxH_7lo_#L=_J5x1f9%+`ta% z5#(RO>xCrbWRJB^Fypk29l`X_@3%dSr{Fk~$Mn#C!^Mmb#BqL}q6h4M1MLqe9PFoW z&V&>W@^rsxE#o09@6C+Ag8V+l`{H%`Lkfp{>3xXDk(1vZ#C7lUOnxJl>s2Oy56bUh z@&j;P@h>KSJ<5N|_#td3Uo!q1^4}Ew;GZY4TpDgHPxenh{oNS94Eb5eX&iVD^$cP1 z(@=gm<1>+uXL=IADEN$(!okm%;Cg!&(?k17moPpG{d^_U^Dd49H!%Jc*86QtPZsLg z$oMn3&VNYJ1Eybq_P@mV4ai#*4&|c$@`l1e^kbCYr*M$(iTQr2a1b4c*Pr_p4)RlR z{cuF#ApbX%|54!}KMd#JKNSx0&!9a0$e+fIXJ{EBYbdmoeWe#t$K{??lgP#%H7c z^-ND9uGjz0csla?7@v;(QH6saXrG9ukyAgV{m5TrJO>;ugxId=f%1k>|A&lkL;k74 zAzx~r-zXgPe1-By6b|y#K94ILCfT(?T!5E|r}rVw zMNa;qec96)Pr!CQQqco;PQiR9GyVbcX$l8K&-E`?^urWI`?f7(^3P!Vzgm%pc1!!g zU(fg}Xy>hp9$yi6r*-NR3I{vsdDDvu2R%Qao>s<3;kw~1g{$TL7&-VE%1gh8 z{6gVSt~|8=FymCNlZt+@=QYf?FTOZL<3@j+zXl?w`fWk|Lm8il^QKGD1NzU#`7ej@ z8XWH?D;&y8*SSRs2R$cH{&Izb{HK_&U*RC{#&%!F_>;(&DO~k)6LRu1?T2=~A`e3q z-Dkc{;h@Kb?d?8=gFV!5A5-*0eHnP)<{3pE^wa(wuPGe#jKlhSo$0w5^}MUdgC5#P z<1>YWo>J8Fr^3Ph-B57&77%Rqq+F-adrgUqFUI-3CvqB3?n3?j6%P8TeupR=?D-4j zFJYY8%~*wlo`rZnr%>Ubhx|KB;UG`rMmch-U)opgN`*tc(C-5M%pqpIysmJNr{|lWD;(rEq5L7_P-)OF6%KkX#*H}R z(cjcgW+U&ZaIj}5u2%;jr+n$VB|{j$9Pirt5E+0##iG;2!_HT-;Z(rDo{A+ z`3K4uD;(r|VZLPw2l=BYzd+$2-yhr662`xQ0tz9nQn>WnFq7ZNcmwj=6b|~S{XC&? z(6b-qUtv5BKkT;`Ir;elEZ4h=Jk&4k2lSD`Az#`c_*=$P@Ve$W(@)=%ImLJb-k0lw z?V8%jJhZbP<9FePaRU?%_RxK{VG0L7e2wzM6%O*W?i#Idkaxpo`LOS@!a@E|6N{M& z2YElrQ~xIW*CDTC@~$y*K-y!okj8 zG2h1&F6Vvhzt1Wh3=eARk2e&lL{xw7<+D zg@gPnDBlgoC-5`)?HJzw?2Vk-GktgHEXHf`KH*?R59sd;n?%C<@QfGXha)akIOI$1 zEmz^7=N^=ws&J5}_BKo5ApZx-hZsK_Hk%YeELFJLPSzr)c0$jWHz*vc_I~VNe^>O# zddGTsK;dA|e9ZSzh0A)!e*KKXLH=%(->PtsUyt&;6%O*-Q2q^tOM6iMzZ4GgU!(kI z3I}<)Nt+Pjpu$0ZIBa4H-;u+4fZEB|xPIt`+-fJ-9{MwW9Jy1`1NtMVe-z`rVRLKv z&K=`dAiqrEP>%t;FL62J^m`n?!Xe)^sON7A2R-*-{Vrvk#*?+ksh!|ujkur5_eXo4 zROIE*fb-W@#`_>|WqKaMap^sUgZ*!!{U0kF?C*i~{-wfI`EL~t@}J>#({bcfkMtd| zQ;gF-NWF1>ruLAF_eBRH2R&d4>`f107?Zyj`^!isPyQUwW?JjO~9# zeh4v+%~Pyr9B)&Jw;9J_o*qZZ`a+kwc1_asC%3PtUJu|00s7=fAX0Bu>vyX+I+3^c<7+dn8U3z5;;a zA9@Z*`!td~J&&V(hlta2G1^~|I6cpz{SS%La|+r=h&Vl8pnZXe(|ujqKae=x&zytf z1aZ252;b#Y%SHFcXkR;$r~4;InLJ(BJ8`@udAdIJGEUb`>lmkXIPEh-dT70rjO!_j z<2*Qmahewl#%UZLe&L0wYH(e2$%U@W5f=e=Wn^S1e@BiSk(xTI# zWN&I&wKT_dJjeB&V}M79sm;zyaw998DMnK?6fbu|&W(7S$wpI4D9gQig)>fsGF;!e zK6MO`!-El7k(Lay2{Bo>V@shku5pKEH10~x{G{PiBfLi*bvN$PavOK2c4;&H}70g^L6Za9E8VR3qbdb!zLMY>~v`PC#qJ1Synk7G+0S4+;?me5!ueBww? z_v#Z%LZgkw-5MMLevW(fiPE}*9>*2|*l29gJnL+5)M(tU<*aR~`)ufmpUM8*@IH6r ze$A_GZ`xaTj>oaZ07b&R?F)NOx7G?z*k&}g*o?;Qwj*~q24o43<9Pw}$e{{ZAMTl0 zc}DmHBYdpDSaWn8OwHjH&QV4<5;Lp5x4LTo0eO zH^Topk`Cn?t1UL5c}#Df{MYK=wZ#pq|FDIYt^Pv`)j77b~4(O_n2d*ip{@Cv8lYH^1@G9rb}RRIXEL$JF>DmuA<~aGyaT54^KT6jjnfx|C2NH>ztbPdQE=11S7Dv-{oC2H}tmGX*Zf$jIcY!v38>k8pknj_?uYc@Hp+B@PgFC=}|aWj+;4-Er)Wq zeI4(`mJPXs64@W!$&R%%Z9=$?d&A$hQM0-p`dnHREMMWw+T(G`XJb>s2WdR+pi*R= zWbSfY_l_n62-L7z1A!WkW6O`8%wJ6@S&vt`SD%P?G{J~D*6z3_R}*4v7soXLO$c}R zJK0@-HMEc2jVCoD>~W^8T9F&xdBi2hEl_zSwxii&ZK0WCwNL@Iqa0{@J9D(%aO$eD zs!7!2qyaUuAwAo+A;}u3pz+&tUb!G6jDz|qa@ zJXSRvzWY=Zis%vlgIwLIX!L*VVux@4OO-nEvHP`OqtWQ$HGf5;vbY|{mK`xI?fdBA zSO17c+xp`01%E}Ou1{U>9^Um=G>Qsu`y(19?;|=ZcC`8r@Na<;{@G|eno({<;s(R@ zscbo}79%1TPkTI0c@YLpqPx)?awbRfGMZW(>tME)<6Xq->~4g0h^H9g33kWx9%pJr zctWzPrD<>7Jje6PZLW92rz*C6Z#Tj}t@~Hjx<4GxFSl2O3p(Nn72yJV2lCx3!UYMO zSA+|?Lz7A?H!kWWjj!usH15opy+bw$Szj=peC!QZIuGTBPkO^EoF`xw-1iz373RqO z-f+nIqtSS@xQz!=TrIgYoWMx3iyYxt>(YeqM&usFMzh!1UGDBAUE>Y^;Ev81)zlK+ z;aCT&Lg}046)K)qjH3PtC%D7PJb=v$J!Z}9)bGK+ zDVBf3AJ>hA*)z?6DcTz;caHK#DxF#0NXTh;BP*PFM&r)n9gYFwC1}#LOgQp_Odff< zV!g~qS>MUpkQMO~eBRKk4XCr+@L#gp%sOj4x}Z({q`6wMMlBg_gbST^clbDsyN;{B zg!ciUa%Dzl-8t66m9jT74wfc4jxEQ%%^qj}7vOFUwChw)__#NG&>fwbWma8pv+DXo z)usOTRkzEr_JFLq{~{BlZX>*%EB%KM0@lG$mHp*F^!=eHex<5h5IR#H?5r+-z`8U? z(D1Of%|85b-EydwQBW;eP%Q>jOCD59F;vSuqjBeg9WXx1Tsmz473-y%@z|Yn|}H8COfz+T&YK!F072KG5uK_BxYuTrDra-IW)?JFmh* zW{<~dhkbJQ#5tqX3t+t73QMmOy5oB2ATY7@lrwsdEXS6&q^i|#=`k(d@Ij_%+kU&d zTT71Pd2^&_YN-#bUg11lcQoB76^5M2nY$e~G{FG0*VWQ^%C^emZv0NmY5H!}n8xq* zr!?2Q?|grxkCAz7cU;f$;nvVOaCz3aP4i?PTIpWBqOhl^JK)&jDePfn9$OdJGu&GD znNhZPcU+<{A~#DHSi?|Vnz3dlc*(437;lWSRuDb%9=5}+Q0qr_$(k%JT~Z1`gsQ7k z7nM{kO|`yAA52XfTs~BkmI^7+R?1&hUQ->Y3Ryy?A}PA0R0OIPmsAGIQ)6E=Pp$M< z%?~X|9b6813gtj~NvI?>7z$KYrdIkZLgK$Uxw-<*??8EVg`&T}u#hzj_#tTlIly^1M*~7rnkm=i7We|PJf+KRvFJS@Yqz6~@^IHVu#c>~WuDx%zPo1LZx&sbflAvTdG}d# zT@E^IgX}11A8;`V+6StF_H$rri3xTa2fM+;69>_4JVZ%jw=6Sd7x8w`C2i^EhN!e9 zt=rVktTXduanY7;(-+As;!Lgi<}tgODm0IEH&eytv1BuKxtTJzJLxtXZXeqQrHI6| zZeI8_kz9aw^H*yWt%%Y2P<+wl9UAw5AskRxSt|*Hp<&5du_vUB(S=B^C>IIC)IuhlH$thD zg!9c*7xUX5ar#@UbbMVxlTSlz@~v0tCK&@|{2cjrko-H8RQwK~laOe&sY3{P zUp7wfqrprh51D_>dYd~aY9%l8f@v3#Fk zQa2U<2aUqw?#H88zOOKe5n9w-9X zBA+trkJu)6HMb`btyZcprnM-4NXfJckXWU@S}4_a;dDe-i9SRb?=)4lNcR!sNC%*K~^wzfp3AI9_m4MuZ)(36-x2hZP%>A|uG=!5oSdMF-0gy~@# zjM@62^=4~M9FFoWIy(488|(`$n^|#UrUsiXnk64AHBmE59EV*kEu}0}FA~RV-%Fon zn8I%CQRbdUiP@OUG>=Zep7~lgnKWo_c-^+YTL*JP zoB4-izp%lVcjR1P{xx6BTP4X#`PbZ9%iM@~nIuc)-+K9XiTt}*{;iXL%}>wX-5}!a zP$BVl7}v}W5s9Cj0Hh>Ah`zG1^npI^|)?quyF!Qk`-mY!7^7%W; zVm^0ZQZr?f6L)I|6NN~r^jC?*J=)dE9G=*UNwjINl(Y>>7G0pY$J>_f$He!v_fMnJ zeW3jyM;y#`zxLH>P#n~BP&+D<=DeHO&(D$C}UNi6$JTZv3!H>|Q< zEtA*{ueLoYli14dw7nsdIMQvleIt|D(O$zO4t)DCInVU!ep?p@Shc`Res3EnlQm{i z(@i7m&7?yI&9Omq0H)XBpqGJZ8bmYoiyR<-J072?Pm}3Qn4XI1hcSJ*ULub_iRs1q zbuvwZ@ERSyiJ=c_!Q)N(3o=cEazuxk)d%gvMsQidT+90ZC}SB?Qd@h2Xf><}G4Y8{70V;+ZLLd4sXwDoA<(6~tcDU&jZ@?%Z}O|OT~>^MtD z34JLXa+bPA3Vj(YpybD~g}%HLo^y493coAay4siT24SHeTz{oHtH4KWh&H5Qoq%*NlMB zvZZg#5N}`JMF@DdB7`oBCHAr&Ldcg~wPD~Pt;j6)Fc|$In91|x6f8*eJSe)B ze_pUgXcxd~5H2dGls5gmB|=M=6$;KOs}b68QfM{}?RjaC1{>%Og4LP!JVa#^(zb%n zF$&r-3YnVG7a%$!Ahefc1g1XiWf`p%+A9!|zdO)gl`OQVMrf}=vovW7%;6(s9c_a; zGU;~6>0*CvtQ0`4Gyyu4lF5ROZF zQbDJh1^vV-=v3(OW;v$Cy6Lo7LFFwVQ$R+|-1EU|Gg<($X0$LCEsFVK21I2UzNIpl zCPGr0m?RuvQKl@6SL?8KKyaTS#AsPRUb%XPAm5$WrdTbJ(qKyLxLIxp&@l6?95QN! zHr+~rp+d{I(iyU{3go&4!dQ8{P%gXl4eJ|B`67*arF;N2{R|}M(R8bw+u z@*YMeeAYtRIh=Oq*?Ci}pbuA#5A+%%nMv*fw~fU2V0hcM?of zJZRgrZheGs&%$HvhR|&K{+sAT!FFx(|DA#zTI7GL!050DJGK9n`6Av9_Z{NplPh^G z^K~Drlhdg>KI*(W?n$BQ_=r`<#~K`iV0Oh%tYIKmcHK|4`%}zDlu=q&A+!-82~ z|B+RsZ`j-b7514`VP>b*aMlrK$KILJrelkEJIsEvIM1eFak`pK4n$>>8zdZX44Yhv z&B}p?8~aijQ*3RUTwi%N2!h!J``X%;`Ai#4!e*KK+uD}-EE^4lW|_~HH;kyv17r$< zS>{P?%Y0wowq=$b3ZN`qf8hYTv2+EUmoBRxm9D@BOK=EgX$q~*=-6m$0`ukOHYFJ^JVj8}}s)k%? z_$ryNZNnUBNewY0CYz(pdYd%^sNuJMDu6=o{4(WnMtQ>J+x%Fz3dc;iI*noa0QN0yAW|_2t8?Z;+VwTo4X4Mo(cdFY=t=!g6QZZZ<00Q(mKieH8?%bfl}gUQGQ~p%eWYkS zS)79Tp=jUU+K}#`v0uD2=S(QC5d8t09yqJF7}8ycv*8~c8j!A;4ja-vRr(+`-tvJE z1Jkwf-RXF4&(y>bQu{fvlK?`7gW|1%3*n4;L5RU46ou#FnemXjdFBv2Qx-1`bC-#s zv238$v${Y=!(#T?x(IRJ2rFMmo}Z|JsDRz>TsBAU3ocYFND$&8#exJOM#Pc=z9Ar+L%a|p!40`RM|AI>B*Z0&nh>MVF|w?g zF#~hWyku!d$D9Kuj)|QD$+1c@L5Ojb1tcz|tWVd37@w#WbrB-#EX~z3Un}ZToH#39 zz!!R5Qcv~>j7*S`1R*>L(tSeYfQ%3mEoaD@i}`R8s6;=@#%riRh+M^~2|`R(^~AJX z7Awg3?ql%>XEM-4XA?`{d9w`o!#QBd6qmPp;XbBVl0tEu!hoexoOIj768Zp|z>Qyk zWl$Win1PL=I6TRRpo=9fR^kbSAV_iAQU?@L{OOon8pYw&atL4`#ka&*3dP~IAqY@l zipzJ@Ool&rY#-zuXc#_45k={AP58Jx?%yNuclE+1GB(mQ4-c9kXksqLtS|**v+zHh zwi4s;A`%2m6kyB>a3dc=O!FKhR-hfLw`s>=ZX|hnZDI@pEEwDHM0cSTe;2$5^{6qq4q%Wza+sMadjZ%sB%!kVdV& z)97c4|3*bn9Y0|Fl%>2~b4UAoHc^t12^(B$%1! z;So&~Q8aemh7+qgJMXY{ULi$)ve0yjDysH(s_O07c@&+96OkrnP_!OVve>YHRTpY? zi^Vw`>Bx1~Nd*)=X`vbPBhsPhDNW3v=ve&yq%Jo6xQY@*2sKea(E*l2H~hGY61fO9 zF@vIhRgMxhF*%AV-ubap?~p4skxx8t7^==WvFcpcKnbc=P53Cfuzem>(~YVMN`w)1 zuuw=wk5J>+L;>YODm$3);iWqWnU+8EC`zqC6LTm^^{k5x`&NpNG|0W!3Bqas-{Hi5X@AVu7R;yVf>zVAt9PGJ=%qqJz#s>Zna=!fIY; z$yW_wAvI$VM)P8521cn7YGMX&Z^{5bjYJbOC`xYAgpXVL-xQndlp_-*Xxjiy6i}3Q z4A8_JipDH$H-2fNfLuuCX`+ClWU0)vy{+xmHRMyJiuQHfbht9vo?>-vh7Q!7=t$j( zj?}fEuU^Md<1qe*;jjl@(!eWi5OlHO-|g(!f;Ik?)d=QNjc>4~hIUK!O(^|@bymAU zlnO}I2^LX{3&&0)4MVKVd=!mU?Q}AUil+%5MSI1@oYhvBp-R?-kD^rHU`Gcvw7T6y zL7$bUj5@et`35^VycY{W6ZsS+59wk<>nbXwhJ}=n5(BJ~TP;{AQ>)b|s37v>4ocA4 zu5R1Adk(im6LZM7&scWMY2R(yT~a-QMScqZ$HDO02VyOJZG#;fgyO{i<*-TWDp9Q_W>EBY3(dU~qbFmCde|Ja0|rEl zmRV@-%@`$XH8F$R+QAsv-lzgHDri}dN71h>G^f2$1??>;Xm3Hgk&i4OyLDJF^YbL^ zpjEaXsurRfuCY90&8#%3Y1ZD}Rv;s&?9h!V9;=Bfc%eazs)9N%ZOrrSGn~Uc)4}yNm4>QPi`6O>P!}5xt>W4x0K#no$_NG=^gGW*`woGu`a8LN1iT1r3 zjcob%hkn_AFae{gdj-q|562cpd132gC>e7x&Y79#GDlvB~=}a@uX;xrKpgi zlPpD7kfQfvs56V`Y)wq3C}ppU4Y#*j)P^wQyDbCqC>k?hMtcL=HPFeZU?2X621@Z` zG36AEDW6NqU$IIzm!ec)P{vDqA1bA<252IWqT8+f3Vy?AvZXwaqGwpBfT9@|%A@FL z3l)%|f45K`MW41%0ond~3~|%<J14?b;ZL$3*hHC{6qPX(Deqq9#jGyS31IJhTD-Lt~}*-(xJ9;+tYD zh2n3=SO&%S#aIf(--)qgioY9U?N&-;+aoBWi6V-U$_^HQ?aylW;DtmUuoO?HDB0CP z<9QUNpSR9x=gs|i2z~$o0lZ0Z_+0SPI3@jn#wvn{M&rcxe04Y(8u!F_;m#9~uh)rDBZZXhaCSzkal&8jMj zW-mQ>ApN&9fBv7IPj@Bh23-cb#d{RfbT9hNExPK;{CNxd{#thQK>w>kz165^}b>;ry^`l1mMvl0kGEh}l zf5H5!y5XgPP*9``Rme3=+m?lI`XhE&N zq+F4zsjjR{UjQGIFx3ar7nttqW-Tc6do;~{hOotHFLk-9%Ri@VoCWcXU9J+gI9nI} z@wm+p(SKT23I(-?KsOhJT?eY~wy4<99n>=gGrn%$B z=!?eAw_p0P{qk}>y?B#;nY~r-SK)o>=P{t}A5z_ywh#4xuDa@SW3he1oYCc%-gNVv z)uS&RH>PpsO${HuEvzI3ur@Al%XF*i=TEO@D21togP{;-}> zJgZS(uWi`3LecCZwS}*;1&32Ba zzr5<6m;Cxmn!Q-xAvW7L_-E-)H5BVFG}zPi`gHvPEu=rSYICE0QroN#YQ5@2&DDG3 zZ2E~6SH)@ib$W4}P5))ZgQMXUlm!)48r|U1RoV+(q(|6g%=EhRH&D4wLu{~z$ z1uHd0?_vMSK7Zzi_TfH#>DcjC2kbAOpAoRndTGqKh4ypK&v4m?j~VV7Zolc$^Jk6I z>+jJM?O&~3t5?`YY5Ez(`n-E|=X$-@F}>S8`jTS%J%8x+ldrxyuyM?|fX&&9G*ymi z1YH69IH~L2OV6Kmt$w8~QPaD6ywJ2jUTt-GU0F!f`YXz+LZZ}N8S>Xwm4y5S{!j>Z zYV+o17Zqgta`N-1<@?+dCgczgoEuEM4rc3I*lx zQ&H|)R9zmZSn8{+E?X!{J^sakGJio`>4eG>sh!TP3NS9S*5?E*0-RfG9-MyGRP3c6!3+tEu%b z7S%QWDq-#oN4Zwi`e{!)HCuGGr_h%>wJ;}tqI*IP4C|rV>ZQJNSuzo-EvX7t_-lPN zwSmPx@G6~UR?xIUBPU;!REC0BeLl+tau2(zl1k|d;qz55sq)v-*`7jQL1BLG)JdYM zZjnzp-z>aoeqC)zC{SHRrRdOizLH>BAV6x>7UIb%n2?{FS2!(S_&C3z8 zrC=$@R<(dWUsYYuUmI+D>eOk4zKPR{rh3rCijuNe$%29TRsQnSvIQlzqApMs8s!U# z3h0GC&}6pF4oVPfbFntFs4nEM_XS01S-IJ@L|v7t-scO}1ge;ExzogylKQ;rC2g9Q zCuc_Pgd7p{hgfTBk1x-i@17z|cbl%0#UV%4)>hXF@3cuiZ_bPyug{Z{ zT{KCQmDH4!1wu=O91Bpbr_g8QdgM9=>d{{xsx9%E+hB@dbzNrJtTs>+s;(_uTGMX#%i5A9vS3tTGQ?LN zsP&hbV+C|8tITQnt_^DhMp|XNW+7zcx@(Ikj$4slQghpwf1jC-^%LwzDoaMf29btQ6vK8GyGNMF!-p; zqQcxMIZFSbhF1uEFK=2tcwIIhpIKZv2wD}V&bH=mwALZ0^y<2*5as46^i7yHbz<%$ za&%cuoi9}FtMt1@k_=_;o02o7Ez2${m?c8+J94*Qvduwl`=i1Tm3#5Dyb?jsV(=_`me0>2SYN0V2$4rz~x}Ae^K>fb3usyvqrZZ zDq2y#yD)d!R1$#kyC@sxFXd!eM^<+)#RACHkn9}o2c6h#Km(re`6^&`gOL%sc6lIJ zR=wC?>o1q{wZJ(7h6LA0U&x#}NRR2{X)|*2C(2=RYTcqX?IZ{WTZ><4#prTcKZ-y( zt-Qr3#bS!kcHq%ttSO8DyoA*D}Q3+l2^ZKba!P-6`*obVg4>IYx9#A) zpgI_WX(3QjC8{c9$1BbESJe803ur}Vc35knB&tdl`OTT%x1=Nx^4A7IXE}7=+A?L9 zoaxS=3JX@*7^;KT0ELsUGIS_IX>9zk9E1~3a6xr#NDfV`X->*1RM)PtX*zf6#Az_) zR>73pVRod-Q|Qajfq}qUzt{T9&5HL`1nOnHgVEFnmWKqolnRC;r8#inDmT_?eZDI6 z8Z?Ennx*JUQ`}m`bWlcfWTEaiX=+jGgwzq~BhxP;;esOb>MypssR*lwL| zjJBj`>Sa@>&9r(pb$#$}Z0*^BVFiWoEizG7QdQ=!jCFgTZ(`BZ2{2apeCk9Ut8J{) zHmeWWQ=oA`hitbb5X1fTHPyAD;R^y4f#H@{hldu`z>T`PVCe;}^o;aOOvt{4GihZ> z)%-xY*&5AZTrtGF7NHT#T)dgrEIwF_%AvMR#|h%vSIEoDpg$N4R9Bfx_p-|Bpr0xM znjJO>B|CZD8CvXvhAG$3R!@?PKrv~;gwd&KMWuCBp}OJTKvhX?wOQ2{xauUlcqmqV z?1!?;@`YBPtex4tt9`vD<(<21U5}dYSHvdw1lKl zo_1#Cz-u?=u2H7)PvM_CDY?-$DJjeLWg@pyf;JM%)dF7 ze?6vu=h+q|bmZXdL;;_dr(aIW{m9)($yeKqq?AT|Qc^~>bgDbaF6;A0%u{YT=YjH0 zO-Qm&O@!m{on-ojll+Eec2aUP)cJL~Cn@#nxCu#Vjq#I{_Sy7vwS=TJcT%dGs#%s_ zhVXksb`yn*SG2tkz#yk}j{9(&c4wdTB5y(rc?B0phN7SKH($N(%E_IhFbX zRTb3&<0Yk~a1RDkmDOc!Bui>bs^!<|JMQ99#jRsglz@-RyBVBM>~*Efbad{v!*s-AN~M+ z9=;w4yFo!d^mq@qBa+(#0>|ffR(KYl@bbX(f(M77^Z)Jn@abyEA3qsw1ixA$le#`zCs~9f3xRdh_;5Wq=!Jwi9em-mv$Qgub z_-Y55Zv`&9rjzo|d)_>S&WG~D+km|Mw{((!1LjZPvZcWDhi?;e2Em7$InX!PDR5c% zoD%O75Z0spR8L?(bSZ8>Yy^eo)2S7#?=;p!3PE(9KvAmtj+l z`5!(qss?U9q?w5_v@S9K+tE1455C9E^M~QJqx|F^A=gP^yYz0C0(^wT~Da-AlGm?+yzSF63# zt}MmK=|x1E=l+I#9_CBkVFhvuG)!HO9IM~j(glKU?VHm!=vJ+^4Z8USf_6c-_SI<{ zbZc*&wn3*CB3aO_(zFdaO+GB>=0^1Gf^K=SZP0NKcQtH+wVetuJRpSVVbiQlL1?Z0 zQYZ4T3r$CQ_IASG?S%hVC;SVrM`+d$uH?F02)ThvNBP2bQXTRBnyH6=DI?eJ@GWfQ z^uq?)U1TnBn`XThE!X6@Mwj;B3np@XEri^51vtICD%aCO$aW8$-b18s(SO;Aoo(;8 znb(olbt$IXTsd}l8%DiWP9Jf}s7y{SZbvR0$z(>k+K~%K33!y*$wlv?l1f-`;~gow zuS&NiYW?P6<#tW`3t4#*-Ff!Gea5ndzOn@ieeek|_0&#plPT&=H>uajQoVBLhTUWS z@6L_gNkXYkp6Mz&+C68+XQe0tPm5}+l^Yx~$bBPmE`oXHFZga;G#XuK9+q>I%EO&- zdU+D&G*zA^63)NI2?>6sgztRG@I7*nhieS7rx#9^@DG-@#78O|w$UQK9XaWz{_(mZ zuj)Ub$kSX4;h@4{4xshY?}{FnyC~mmoRs0PYGe_Y|P#wWfLIoU5akQPGx zq{yrGm*G@P@>Jf1$iW^}ezhVG<)!P3CWS+JXo|m3Ui1q$?ccsb6Oy=l-0k$b%laTdWYG zK;f$W@KqWJpa=RQ_3Qccvr!?$Aca4!aJ9bnDqPj`2Xbn+@?$tch^Qj3)>k43qCKkq zLgb{U-V_tF6b|L0;8*mh^-`^Hb^Kqa=mD3kLq*LBSM~p0(WC0WU*W3$&7JU{I^i1r z-jVzNZwgoavr^%z|DQt+*+6?B|G%intN!1ua8-XQ{?w54kpBlO9Q;Gq6)x+iiEaD) z7=^3;2`hS3|6i|gRsS7|o;LnhxT=41CwyBc`~!un?LQgUx4gfsQ@ASspu%AnVCr9c z6b^GH@fv*L8uY_gGKlZ!gx6rBYPqZ1*WqZpMz<79!2@`O-YG= zhUK9hK#BK8JLy>!aoXhQT9)rdod0fT{C7-0$oRe3KA&YAz9kNUb`zz1$D=*811NFY zwf`W~Q-tYbjHh6`I?1>L2Zp}bnMwarY**(qPJS58_@8hhd@`Q#L)flnFuoYm<&2x( zeiovZ@hbH5YR2i;zt=Oq9NW)E#@|6ZH#1Itd!BLfn|gf*<01L&GbVq(=?3v992IKRv{Y+*2A@pY<!lat*J1mlE$>L4_VBon$^Xl&cHw6HTeOGv#3Vhm zDVLAQXJ9+8Vtgr%ODh>4g6ZoRkH_)wPR1$Fyh`>oq90yh@)I%Ny^LRt{{4_~^7Ge> zlb?TKyc!MCu|CP30Q$cN<8L4z$oSc4PX^<()6{szb5YM!#)n~f=Q92y+Fj51XiPUT zPJXzVaq`1I82=r|lcyP{d*|C2{|DCBKE_+I-alu&KkEOU@onh0lZ=0ie&~+*P`&Iz zxic9jzYSxY{FcKwU2o?z{vnRrv_7SLdtg6W!Q@BaxDjTY{7?HVk{{>KH~#X&zFo-dp^eaOl(&gwnwt_NAxp&MuIr`c>tG3J>walfqE|Mgcmd3 zi1l8<_zE1)LyQl{cGbvu7vwiGz6#U#GEUdwPclyP_iKztvEJWi{3!bObH*2A`Ul48 zegxf5BR@~VcId!*CJxWtAkYuyiNmK{APi^n4}$=FRF84m)^!@=_h3Ex82=LeU(NUd zTz54vemnaA2F8zL``pC%TiCxgcfzS3PBzg=Q&KCIORj~>14bb^$%nG z6ZB^W<8Pq;k&I78K9=zvIL_oTPMc`briO4B{5B8e?qu@xA(e+1{}9{%bBy1M{B_2A zpq;eeB^-u)U&i|SHc>cMJ_eXv)ep)+_oMvvjMI99Ha{eLXua`ICZB-my^PcILRx>3o(nNw z+K-U<4)k*z{wIDDrh76jZ*s#k7smTwn(m*I9$IH)G5IoVx6>Gh?{-7Dg7H=y9~LoA z{-WzD$~PC=RfNfJz;S@?!;}1#*ghX&^6(jQ2wNHNiS1+$j>kFP_Zof#?LG#uYTO#Ua- zvzBo`miK1H>7yL?GyVsb>siL1#B%Lq{3)#87POb_{1of)Eyn43WIyBoM!m-vzZ>~U z#vjA_v*QI9<+}#udNckG`e6v;r2j(3AI14%9OI-vmvP#!cMaoX&~JA${<~Sd;w8q% zqy2jrpN#xH#@Aqb_?+>6Sl*+IZ$p3nj2z6vs*>SP#_4@V9UtU?pOsHVi|z`CdFK`M z+ZhT6`ElrnL5v5HXE6RK@{5s^o%CF19OLwUc^=b4_w#2ld3qnSgmHTAxlGXmS^PG4xdQPBf_h(kShDjf7L!16w*aFG8IEmqAFs`GXT})3Vwv+c1 z4*5>UcJ&$4LmxH!hH?6M*>R@lB#!4gHV*2K^dYd*6b{iEw5Nx{!5$6!b$^9J^d^)a zsBn;({$#^oZE3Q;H=%;#Lp>WWD5z1f3_+8i^Hz*wRP{T;mZxs zPC$g-8%R<(=zjnN;CCL3|AIV~@w;%HFqClv+w+Bt-$vuD!oePz2eT9o_I!i#xr`6M zbx1zrMaX9}UXQ$-@k|`w7BfBvc>{9tCvE(A9h3hP))6ggBS+N07UagCC$s^uB1ZA`j6Q zu)O7pJeWuymZ@Z%zD5&L^gx!0I9{(~oaUQmg+ug9>@T+~9Q15OJ$EY{qL<>hx|wmB z*LEr#^l0F0A;eqAsXfpaYCd3`?!z8tdYq`|dnQkMjx$brbohatQojc3NkUHcbjR`Y z494jjHp7^nRj6k;lP5iwFiv_rOwWC&XBv}tqCGPir!U_uWO_bEJvB_8^wcv>dYYJ? zzfjM5CO;PKxt(z@@`sro8fX8>yxFZzYpV|BJZzo$d}e9Llh3}+==b)e1!v|`w^EY9OM_F{7lBTAfKafK-9nL z6b^cZU^&+yr}j*rnYmVx2R*bNxJ8l2p+|-{m^?in`bd!n*VFH#K4+Xh)^bqM19l!j zJO5iTDB~)mJmhCo3G}sb0=hIOO{w%BLzE~lUsqYB$b%lLm#`ua^+M~SN0|H_xSo7Yk%xMr&vv}R zIDIl?m!b#kY=oC#g%JP6_y_QEv=HJmg@c{+-1)G=LC@Ln(zFobcgE)bZBkW+c-_lF-c zPT#-yOwj}Ru0s6>6|UNOOyMAZ4CQ}UILKd&^%#%si0q-?J)Xumz5m=BIkl4#_z{c{ zVkqNf_=Bi)g+sn{zcEwcVCQC(zf|EMPxl*h6%O*hqWn~agFM}boTYG(zZ75SzCz(3 z{}axSYZVUiPoexxjDLXqc7=l;x?lPag@c}Ad|Jw#AXPsVp4@6R~-XE5U$ z{xCe9@k@~BC>;Dh7{^;&{z`lBl0ycHQ@JS6d)l1S@A;sZSX3G4J$K@C9SXnChCrO| zXTm-aD!1;>K!%DuT}PxbPV1#Y#%X?E%{a|3Z!%8f>MxAb_@n+l6v{y3!0?MRQq|zP z=#mRvnIkT`@S@aIS4KvL@^|FO5vi#oFS_vJk@AFj2@N^ki8i_az8N%esq`}s_FH4b z{5@Kh=qt_;M&r>mBl0}_sFR z3%qj!C!`z#d$yez(76Au#um+J+?lq+F(3UZ{IKhal-w*%F_85)Z zx*Luswt1WHx7{v;@P-dcjYfEftHo&gG<3E%d;o0u+{DexGhD~R=V4+(u@UL-G$NHw zyEjtqOg0*KrtC19q6J3y&(QGWlkB!ok`ZahFq)#Fc=MdbBgLQuGx>b>VJNd>fJcbg z-~x{j?wRfx?jm=gw|V}FgFr%rW%_41n|Oxy{m;m0OK) z-ID-}aGu==&#)iKRNBMJR#A7A5qTQS%5C;I?cQ+CR>(%4B(V{mXg9*ra3lP-ESEQY z&>g)MMv$f!cX+3xsi!7A6Do3>jkYOTKiP=nv>1`1R@Yu^zr1}x%S|-`+hr?mE_5aX zNRt`3!#kT=w%#3$Mm-K=ztuzf4Bg|*2)qlqxrcrLd1bf*Z)IgWgrR$#ql`7b zi`VZMV}!lVQLB0zYYxj4^d=xu+z98i7%#s4TfvtP+_^K=2)7!)eQRv{eY~;lkFLhh z*Nt#d>*23YMx&O0bJy&FcHs?2u{q>yH6le%dc#p8oD4A|oNbqf4yQz;QKNBXi!j0~ zx5}C@B2UYKB)sxTvt1bBt4z?v(NudX8ii_t3UoVe*>$+&RFpC)5}t_pSW>?8?rL$r z44yP1`B_F}S%&-plbPv1PDP^?6&2~$_yAqoXs&+P?A)@qLk|!C>23CBSREKUeE5gx zdf3&dsU^It{(Q%l3GX+y#(BdB*G+g|st+IPpbN`>*m&a4(A!2tcZUzj$EVUHX{_0! z>N8v|Qbps*zd&!@gN?2BBloU%&v3Os|1C0_pMZ*o!N6!dnrvh~0iJQJ{aTJd@=1Uz zYJ|UuuBQg^hCJeR+H+r_3iL*tPN>9fzmL!T?K@XXZukQuauX=Z-FC8T?$Ec4%nyv_ z%TLtxJ`4{}Wxtw`Y-H{-nu||_mK%`?G=48n6`_TWEd}Qv->%nvlAF16#oW3s-Z2YP zR}?kAt{KhCi`NyLFAD_yW%ttOW!#7q6ni4}obZpX7U_mpVIFH=O-3x&x@)EXXXqw#i0h zj?*qj+luw>nMU(9uyGR%n>nuU+=aQ}Uy7vH!+X5VFKq>TWdrdz&30@=ZUT?HqTb9u zyv?;ILW8~GpRE31MBK@_jk~nm%soc4aiZ>r$<1TGgHhE8_rejgsl^EIG9ta4jSl5i3(YCx1|f<`MLQB;uYfdoz~ydu`Bwccv&tF>66pi-@ftu_B=&8*+vlR3%%ectc; zo^Kw=S@YX#)~s1`pFMk~eB!@ig>Lv@_sNOuVAdmx{q_5&rwUP@sPLC{FU}5i?sLi} zzC{tn{}k%pz{-?Seuthweh0fJ%l!WG6PfELriBu3HohfWpQpY_3ne}co%6i)S*Y>2 zgwFX#dE%2$=T}zeJE~&ItjpR)=IA`p8@rmCnL8{Y>N_{4irCQ7;)9(htk}%@{j@VY z*!hXoy@&emuHKG}`p%63k=6Bv%wAbKOA`MmPy9WU_#~8guXiWeky}zaZX<*|>U=h* z-}X-CSLXzO*0yvxLijY4@aG5j(lH)fvi$j_iDPEnO1xLn?QqRH@*^tGT1275dni!B z@Zn!lGqy9BPz6(~pNaQaz~RNcy}jK29=iL>-d>e}9n*Syd&_(F^lA4J?;Q$$%^V#Q z$otyy-rn*=Z!qz!RJgS3VAhV5UTV`020Op8vbLR|rfGj^K$-IJbKgjQ$$a=-Dz}u& z!M$G~$%kK2g*mwQxC`w?n~3)9@b6`Vr?q!0^53TV@;)_jiMsql<`EXxh~W_v!lG{iX|n9XhW!LUo53O@oDd? z!OnyJcyp-dcADsy79Vsw$DZHNF{c}!K3c)xEGT;AQ3pHnW!3N$bb9)H$~-Oydx zwB_p6qG!g5tddu|XBLzeAD{SiFzb8IA8wafJY7KfBRlHJx%N9g`#dY2E*ow+*pdyH zq%6s=`+_*;!`;gLeYqw(r;H%zcbXcX9CjPwJ-k1{hh1m z;w(CCqUhrzFYc_Qs1pD7nm~`;W0iwL9v}bT+fTAMN2!97V}o;f=-H!X!l$Y&m91*$ zvpLEzy8Zr#_;k~B|Bx*s<15}LrFQ*E#*lRrjq`J!(Z=WhTYF2XkCNp|yAH-vyQLxf zmL1h6^wnSHN=jaO%8Q1()inxq?*^8(?@{i z-SZskC_;9%C~JI5HrjRx4Zw%zho-2pTuHvO`P@*?uQ=x@Z$gO=WMVr{G-utkFY94x zy5%e7d?_?m4f>w8XuKyS9eIk{$vzTPj(0wrQ@n5aO4WB_z*W4jPgAL^N`tQ>->3PQ zW{>XgP&@GDt4|#o-M9aKpW35CoPWotJ)%l8K?RjPdGW_(iGP-K-$(*VyI$)!gKEc$ z{2VG0xincSplV(e>U?$r&wI*M-;Zh>>h6@*?YWT#QK5;iW^KzN%S?Pa)YEVxYuoce zh~uT_*;!pbqvxF)b3{BN)VVQNWNrJt9M7Wl>u;p>dkWJX%XhS)GEIRJ$3jniGmVDK z-K9Bd$oyGo;#1SJZht;B@u{qBbQAmZZO7xwLg%~?>YkS?n}D)}KRID$Lw9;EpB(;Db_%80NJZBr)Da~M!r`~R>Fq7U+_{V=(=ra( zPsnjqXi{}9>2o-y!m5zBqZ>gk_sQepeZQd$R;)BE*V@PGP~wfsP~vknM*5rN>Dm{Y zAd79+Yq15PZaXyb>8#tH3Qc@Ewqo0X*lD3|e|{)IQ%hL@OXW~Z7Bo5jwAHhjPAF08 z$YG!=yO`f^$ahpVjvdrx$A286FcVAiovhoQI&w4j+wS;TYmpNlQ13CLm%63+$)UvG z$ou&hG&^}Vd$}6F6SUKXy#9bj<}$@S)15>hj>}8T9|mDch^W zdx_^%hJ4&{ap!-m_{$XX@qem>1{2RNUs;lPKS+Vy8m|eI^Uh%6=#g8r{8st=8AY?- zpFf#Wh!UqFrB>SDFMjQRV)yJ@{Mx|nYvPey*}C0A3v!{vb+n%MjBKvG@YLV*Gv0ZU zmV4Y0PvVPU;*DV9O{H&fGZPkVp=KaE&;9Y>D)j=UDEF7Nrd zoS#s^`%ih|(@I&{Fc;~fxk#v|N6u3wK2_ec>O?Ge;*t~b^dsY`J)Sr+c3xRGEx7em zoCr;Psx0w({79(tS*JYlsTvITwYTIgqeY!^8gnbsox^+c<1U#8UHf9Ehq`}F$s?H< zU-!Q6&SwZ#@u+%L+1>7h691M)>b_c~qWkJh<^Po&I3Dj9h7zvk7s{e)zzv#A0-6R9 zpxwbzsAms#iNL8}RLf;X|DITp|7xh`J8I(i@AAZk{1fFpQatriZ_|+Zed?kP6(6Va zb1anDPpfI=J--0j9qz?U>Lg)#*25a&4TNo$1M;q-LM9;@eS zCzMcqSb`B*x!}I^~-t69YP*KZWiSvmLo%#J1$i7F!*QWBVDDZ|!WKY&mR4 z{`l!2{J0Pzg{~m6)Qdg;_vm*lr7cp=92G~EdZzZKr7cm;rRT_ew;_tV=VdS@UFc{c0ZKzas>(&96GNNIZ?eLMQ-yPnv5(Dy3kV-2rXz8t0D z26~z2%ga^1jY>*^@@-PS^H3Txi_)S|9}=;d&T>}i+2Wz!mOII0B8ZZS7>7cl?7y|l z$64VNPeQIZS5tmwIFzUihmJwYB16a!DJy71tnzx$X!_(u>Nn^Ny_^g>lR8H#Cz3HY z<*pGn3e??nTraXV<;TjGEwcUTrxx3M2;G(~xpGhH7Kx9U4xEc}#u&G}IT;1C&0%PL zoya&>&xDL`(XnqnE;JeEDPO8OPTZfFX59;+>fAd^M(C4&`Ec>fpZb1f3w2c83zq#g zRW6xW`#*Y2_BYDsr+4YX_F443k}uCuUcdFEL*8uVO|hsr`SNm=cMv}fDQ`MIRXb?y zZ28pA+hVE5L*!#SZzubQ^Ycg9e-b}`f&C*aDl~T9G4_w-=O@^IniX^C`JFx`|8(m% z>A#Qtqb(|scHV>RKa;~h%KkAHrQFVYpZ(|X^F!=E*Sf|>&v)8N{y65}XUn@G#1ea= zb$^Q56esM-GPEyG?nw~#6v^z%E6AyhbqIT!MaLER@`{wGp!Gdjt0pM#4E9b`-kI#Z zM0rcBA*mF0vhvPiqfb}f*%p;1UtWdshO7}Xq9t;#hRA%*_h^c`i$R3!xl)p1oYz<1 z`7EYDJ+0tKigM~AZNgq?Q9bq{&P7(C^oCF#U-V5$#R-hE4E8CH67hrgFTOkb@@|&T zH_5*n<=+kR@740J`aEvEq&wu_cKNqW{*BAOG5PoQ&4^;C{dV6d`cX#)Ns)iuYBkpW z2I2G&iq}!Oo|(DWo|wkDp1H(U4VyA*=5qF`%9MGTO-ZGOMDE?qY_dz!s7<0HS4LQL z@WM!W`a*`YGL187dS=X~6Lw`bWVT5fg(`Est%{ARoS7SJYV&+~gB0^-_NFUuC+m`_ zyw|cWswQV%XRB(jcGG9x#NHgme5*_%>Uri5ZPm1^!jXB0Jzh2nsxW8%h{LMFocR-* zzF7914L0P!-OpuM?RaDcmY44K}541IVW9PTAs+ ztnCh&jw<>{;vY#|Bjqj?vs2ENIq)36rW^K?Y=$!kb|c1LL$+e!$*BQg_IkO z{PtB;b!9>6r)-N1hgw-brIG50N?wLTt*r#7xZr7iyV|NWWT7cpW9=D)rlf(}4z8(< zTo<`XzTDa`+YXgg_7!Moq}}W$ZadCX-Z`lmQ^_`X-zv={!7S+`>qQw4cS|FEbSR+@Cb`JBLwdQFn(m_`0eooMF7-Vwz1$Qx z``(jYZi=`21`Q@=Ze4%qqtEVr=;7}7O_N@3Y@cK=cX+R|ca^gJ`#w4%(3jVsyr1~4 zlHPU7YuQT44&}|VNwU{jj%NQ3?(B-#kIr+VP5XxJyj%JCEc-s`zmxs**?%AVm)VcY z=MS>K&HhmO(fNMcP7RQ}ef+%39xDCljJs{hSv&7le!i3aZ?peq_P@{m+t`1|5~s@& zO8GZZp4pS(P&O-+Hfc+Aq};BZ;nZhREvM3#;mmc}wh|lt5g88kXBkdwCba{UvB2qE zW+jyJcO;dtkA$Z+m-pHqfw&naPG+UGJ+F;Gm@<|;`#Cmr{oqN5Tbm4J|} z!FWV@svc2eB{H0I-A7b5yhnIm>OQB!qMlRJA~KxFU`jFNx>QSKI9pui9}!A_M215x zOonrV&&KBZ4 zX}ERLaO7kGv|3YSDG@>xdSE<-0+7Sm{H`l%71kzYX4;L?7!%fJX@B4K^u1z+Lw(Wt zl%xH*(rh6v*c1@NJ^mUnT`054deJXOK>fM@I={-UONO8r)JYb~u6n7P(rb;MDu$vP z{4^$%MPkm7K1G7OvPcXOStL=#v5n(+!i{5_ila8(Dy;32R+7Ktr;HjSb9RRx#U`QY zagC%^5!&sapn~m^!2}HN3|w&b4TIsG6j-K6MwjjuBqcLL79=VjgWcjWk<4rbq~BV3 zi7dzCV>j9>113rqJYJd>8x1<2%yvI2dEf^eCVM2_5kU6GP z`=&eV$NpdyV?pff7h+Np-~Il|VM6r7J3Yq~xziskf=C>mM&D29@shi17}v;rStIT1 zcWxHbso~R|by`aBT^Hl3H)G%uaL#a)fEjKHP<5lkZ8|8|XSum9@70<(TnJ0cAGvBn zpe*hui!73>+O4ChvP$*mMxBIq>u4&KZgLdaC$fhgqr~Lnr zEj2l`SLCG-+TNzdRWhn&iqjFV-fkNpA8;2 zI@(7_+!HcO8T{5{<$dkWG&lUMZg_;ayWbF3x*_iNLU3xI9$3*sZm_4_V6sJcIu#9` zYBZnaMzex!@tm6sS*1^m=u@RteIZcRmy<*m#li)w+v&Ic_O(03BZZhPM+@B!WwBKa z{eRhi7;=Y3!qC5zfyhbyf8{`bRn~v)V0fp}^RS$YxShZwawtkpu|Mhtr}RE1-v)S3 zA9wvK{UiHJ|42^%DI$xsRE_h!12@hsry?o$rlE+-q}=N^&gaQN(S5F8CFQ@;CBIka1VC%i+Mj7sFy;XgZzuHZ%im+B1%Xj@MMDgapsaiP-sd}?-s@`%_p<2VY z(&S8=Tem;D$xuNnJyoOGPp(t%U^OIMXrH?=D6Sq|bZrrt1Qrf=N9 zLcer{j&p@dA;qo`F8QbSFT~gGOg;@ZI@OgSbAFVY4XFI~prq!}*Y4bW8mf*xNL3x0 zLkP=1h!#jK`I?Y?wD4)k7mC748AMZCYY_~WJ*eM`NiOen=$AbR1FQ3>3(g)SYYQFY zB;(5-L<_3qR4p1h2m^=Yq3_5+{S0T(q~|3klib~|B6)ok$#WG^5sz^rR{pcx=gC9d ziw1ceCE{P$|J3hIH~xik{v;h2yb)?1)}QMf4f~C7gHf|`syjWsB+}X{ESza9cvMuv zXKV>EhbH`{v-13_Mhg+V3eRTt%a!lagd_vP7;(4yP6l8o^9;kT@^Vb(AqQqB-%!^UL@k$SSQ1D9k>$|xHq zL|T@}$PmIAX4!#^3_^)_&|sHkLb?#?CyBFZt&(^tQ06I?$2-{L6(TT9dy;jQAnt6x zh7BnYc3{+CA%+eXwxhJ5D2Dl=Knf`^oOMhg0Vhf2QlQ+)9AQeC40(#Hx(u1aa{1|q zldQ8dN#e*9sm!Tl1tCVc(aH3l#wtn)r&ADRv(c1r;?FI#l+Di0%#|U|NO3JB#F_jw zg_zazycCr@nSlK9idlNbjMtu=RcKAigk%akUMhRGCs2ti;OL};b4Xm-u<@Cr0)md@ zmUc`L;#*#*vSCyzrl(TM&+}CAr3x|DQ--{S8CG?w5aTi|3L!+1rye~n*1YLLoL^`a zYoae0FN%i|K3)>3LR{!3NE*Y_;vz2v#CNgI=W$J-o3bZr8m0?z2~s%35@OP6R`IY! zR(0x~I zIYzQdbzRxA=_GlU{y-)|!m~Alrwb8+EFsD?Bcuy4M~{>Q&gGP6lntAfIXP2^a?cbB za2;A(pJ;bL(|!+fKG9wTnzmsw=75$j2$>J`Es9ObV=~EAF=C18YnenaOH`Zee3pXh zZK?Mb3*}cIbecANP3+x%;}C(pSK zM;hP?FtR7;cbJRp2^v?>5uYoB-5>b(P5$jCI{MC?+!y>yVnBy^wB!@*xuEIWBy#;l z2U)Z)nw%x7K`wTo#p;>oaHivOmj$;gx(>%Poa?ewfL!Q8l`cCwsEW(vNoCOA;Jbkt{UZLh*mWcO5W6KR zK|+y!%KAnQg_!&zdf(<<@F6BkRDwjL{m8nLW&MbM$v&We>|xoUaao~HwC8}n&*RGl zz1zbIK>xzSia_HqVxMTA0J@f!&}`AZ#e!!IgTx5B!NZC`U+!TApmD<-m37eWshpxc z2Q*GJrh)?+x6P4ZKtIH7pi~eg5)KxnjoIWZQ4JD0T4M2)tR_TR$#v%i7Jzd{9ML9H z^QNcf6#==Fg;*jCd8e>ymI#By{g8gK`Pimi@Ocr3p*h@_yp}e^6<-0e+=W(vtaPCY zkg&TYR)BF~^l&ybE-#ixusH7Pt!2CyL#I<;gEjk`GzSS(Yu< zGooY`oFD7Dd0g(0(tdPFW=WDVnx!CL z{G#o_%}MFr$6RmmFEvA;&*WEt)U1In@Gu8-*u#=$wvhi9mT!sbubCXq!7Ndk$0Q_K z$sMvO$S>meZQcd3kfpjR_)cfBw%Aay%Zx*@x<+UBFIG;t%2j|I;X;c+;%Y)mRDkr7 zkTh6X#R69IFWDM&y@zFkZt$=i(2X9J3;IeAD*%m44QaFox|!d3*rI)(>uF(-7(uV| zumaGn9+nF_=3zOY+dM2Abi0Qo4|-U6+_q?mYLK|F$`X|zp^aa(ZAmW7x3H{V^KU;5 zCN5)qg;`QXySUN0(SiTZjHgL_<&6%2%Q+3HV#SSwqrkvHL-6%egdyq($6P`3-{Zw(uj|)I$Xq^zZl?H4>m- z_OK*7LDp?YK`>U?!QU+<13VMl$6@gynVFjR9 zcvvpz%RDRx^ePX_2HoIc4(Pp%Nd@8A=P^zI{gj6lfqurr3P3;SVY#69dsq(W10I$Q z`c)5eK;uF_si5hQH^IM_SaBnh(5;_A?FamFKj21J0W#=9mw}w^ zLKPr!R;X*4wBB7SJ3<>m7lhh0i<` zXNg6Nm_*vGWI@O)gr=ct0SEIWB@NL-%z_P8mZ$)UdS{8n$YPXqODu*_ytE~a4WJSP z+G0@eE|>?lk)T#~+j|(KuSGU9pkP|#EkfkoP;xZ;9E>r;$mhGp`JjmRC;9u$sK>wW1 zOy#vk&<}c;0~%K+(xA$;7~~&ywWAov?W$R)CO1j3Xq=kqzU;>=DwbP{L4V+3Ny&k%yO_rk)gW<7za=U`zRg_yC+87<`UL;VLJ!ZM z-3XG}q+|TZ#~V;BQ4KPcq5ktVo1dQP1q6B4|21F{KP~28D!!mE^ssEulRYd4^kW`Y z1e!j!AxBH~p!axKHfZ{~hWX@|Hsn6_up-c(c~}nU&pj*~^glh!0Znhg$@MpKK>BBM zI7?K6gxZ#<+{;f9vn47`;r%oXkiE>6T?G=!x5Q$|MhYykI9Ybm*p@zkB1fM+k+Vbv z?q2_;2SLyY4^r%&o6g2r?itAeeLZ7xaSNVXHAc`W{56AQG#z57=YW=|21%d8kh4VP z4NO8*KlX&6b~jWN$Q>@U0)p;wp(>D`?4+9a9CI(?-A9&KyoJe|J?Ks*ANHUL_ywO3-h$dAmx{+{G@TW#PY|)mp3xT1w1Wm;y zXsRwjGY*TD9_^;D0^$DKgUnE7?$KZldG!%%O&DmHfZkJpMnZ@WG%P}VprH=&fkx&N zpQ#%**AC%wOsybjTxm=%7(tKn_;Nrm@vtJ$%RMXy^a>Bl2EEF|9MEwOD+1l-VL71N zJuDma77uek|J1{h=1)j}9`p1!;N(Z6U6v}4NSP%nSMpORXNk%Vh7f;06&td>=of;7 zg)FfcvXMwjEKZi4RMXV;144{<^{zmuZVxgG#Do0k7yL^F0`#LEmJRwZ9+nIG0}smv zeaOQc(Es$XT+m;5ST^Y69+p(?q3<)yV~J{zh}sgBf90o;*iTkK(C=J9*wf>>m-*4V zE=vUjf9gRlI~;#CrjBJ%`&B^WmTRhFpwqxF+H*h$JS-RVP7f;reUpdfg8rU|<$%7$ z!?Ho&>0u7&7d@;9^vfQW3;Gog%K`nmhh>9)+ru2tfAO#)&>wnOE@<2gO?Cl&$m7cf zeayog&|i31QWJpu8Noc3s0IlWTB7n+eu@-ZqVf%fpr9ox4>N@L`YC6K4i}SK$>j_z zqg;&^f^2o6#n1>}C0b%JG=h2hS&=x01>pX3dfCX(wXWyeME=v}a^oHPV z*5q?nqY99xx`I}KJc}V)tn1j7EIqkfVE!yu)IyLD=@T2WK|{PxtV=3P5Cxuo-bupn ztsEP5>!7dpux!xu5j8n#Y(aN=ST5* z<#+O&4Lm5Pw%FjfIf3{j=-FGj>8xLnf2kCM{*8xagMQk>l1d$9&1D`-RD(otOH_h< zin;tEer!XMUa#^~hcERdy+EJDpRCLG?(nEHozz$w^iOq0(;)^w$6$$SkSAQI5+p?N z`^TNhFX(UNK>y_iuJ}2Vdp!u`Q!cdd2$TPCp^B4P@iF{si3*V3?c@u?{1g`xTcTnE zL)+beNn@5Z9L*LlI*>OoWQ*ty-uUS`lc}FBEPk=+(d1VMkTu4QuV@$O^BA*41xTo8i7-fL z+D|Tj#oB!1YE=pHBv8dpe59x*3K($%7F(l5Rbzmz1WgQe7IA+m077I zg1*YbvO#b1upH2Dc~~y!w>>Ne^gAAw4f>deIiN@Iz1CE+&16Df#BGUckch?-i~q(_ zA*r9q+%K4A5C2-C66EtPv=HR$E>sEfO&3}S5{b7&WwHiIxq1@|04wDfXixE^VCTs? zPp)sxKoHa6lP0>Th8#ZEx`F+s`yDrEzvo|bXFa}+$mR{Y*c^>5^``s(S{`=zAL1?E z*j5caO!vR#-@$UP zH8$5a#p@#DIwo8czG(c|rpD%Y$JjN^@o}pgV^N{AC{I)4>TzpoYm@oM7hVKmeS#%( zM%PE_l%B?x!dgmQG}hYKyyk+!22x5LG~ZY#FISGOUl)lgoKB@LY@qvyqp`ZWNPS_0 zu!UF`YoPP~HNI%9uCY0$I5~_ZB-L79SJ*&bgwh%Nic*mkHmIHWgSdtWVINDk=GiB` zXpE|>$%a`43uiI&77uuC?R;=^ooxE(?>}mGFO_QCue_A)G z{D}v~`hE7d+n4w)d)uHFBIB0MwTC-}!!OC5S}Y~ir(VDOz?9Lu?5wQ`d;OfSy?)8u z8TM6^ci3-lT4MjyniIGGxM_}k+GkHV?atAg?F`@9mc4$mv&L`R&$Pekx9lgKIey#T z*S=(K`P?$UWk2W~^xO8!?J>V)?{{K;+kR9={}ajgZu{i&-G0k{-ig^q#nxS8r!+}D zmZbiCZuynYnzriY+kUmp-n+@!`qmC-%5_Wkwk{hNFI&3Y-eviUEx&F5zU`pjvVSpX ztKYU?X`4KSq8zl<{(RFNe%n4Eqk33I^}F^Zt$xdXM0#FouaEjI`ypqt-BE9^afaGI zv7DIwr%my6hb1al4^#`nnG1C1l7=oQ`q!()tPWrz{;iWt@GAZ>(i!Il1GV<<9Wk zA9hX}x6D4>DZI$;h|QaL&lI~owrH2|`F*L*3-V_!{Vhw~nY#V@NmEGP-2}Wtf0nv` zB1DxsBk%N$9bvDZy!=NK+n1d;wQ+gk>PaitR7frENVQM7-{~AXdPBucPz0dZ?#M3*pnvPrFYmD#q93Qz5;9M*eQ)4b&j2Ke%Hil_8-KOn=kpv8vCxz zJMB|mSQ=Ssk6bb(ved3xVt=&3dEB|;;u~jAvtNMCY^HQH}M$d#?$FkAfAX% zR`j^t+jcEmI(EwWQ!X86XHK4KFPUSHnKN(c@`m<_oqM*~4bH(eQ+L@}&Kx`7#D+Kh zz&`aeXW4@v#-~*;wKJC3S2^{2=Iydiu0Lbal+Hc7?2P-JVZUmL?poQoXUZG)2q)JW z{@{mey3VgUziPTYxLyYBTD)?NeX{e}u-*0`XU?$Q_G!+XCw70&nY*K`^Re>C(hHnr z*W2swUsJZ!Zrw>0!d^Sa9#&|#PIf-G_uBUl-~9_a_YUWt2kf>&d(dQi+~anY^Vx^a z+5F3%*?{IQt7IYl6 z!!K;JM=o)8U%J$OapPxuJIkra*r{)(&UHqVjjR3JILR10NIz@n&!y+n-_$o#?VnrD zob9`JFW7zGcKgVt&)(U+d+Zc@Xl$FEHQBy$vg9gE{q2o*)7CxGvm~|sZF}@qJMK*W z`NZO7<&*n_Or1ScvcHL+57t?QpGjy1J$og#9?sz96UIUjD8we`S|ol86S#LLD-cH6hKeRjsADN8%I z?dtsb9&+)~_G6o!!sSff$iyE^=~{XoYYzh%FY_NL#qf6+mW z%xh_HF8$8uW5-?YY&)-^;X^xPjy<^0UgjHT*`t>n=p<)fUjNo^J7e;0yTcj!BfBU4 zO(*AP#h+6}O7)#NRQ|ekZ{99d_o#vNT~B`|Im5dW&M)mV3hi*rJ}Kr*b>4Eu+JVpP z_Bp;YPuYFImm(~yHBwLKl2Ch2ht`#s&8V)N5iVV{Xu+azaOTX?%1U5Wi-PkjXO%7r z&stPk8lF)-YgXwZ+-ahMhimHUB6Z=~)gsz>RU{n4)25c%E7WZ%Q9zB+mhifo+AAWl za9t!?+uFD;*3v4%;VT-O>yjd@T3SI7md`b=SBXl=kveo?;o6qwx^PUiM%p6Hv8d`C zS+%t-b&d7wWdIS6u5JvsM%K2pMObqY4s$mcZfcCir0UUFxFJ$gC#q(Zgy%1qKcjrX z%()P^b5N;s~8X{~9F)<;^y>Q0%anpk5?vk1%U zUqmByt*y~@ky;TB*VV*oNQu@+n`l%jiMb~Z>|%rpmnW{Z7QBVIkliFRJuq@ zL}PO-(%M|pBvT`Y03sZ2X>X3S3OA2xYnqzq3La#M8%H!&6N^($MPrnsqPlWMnV0(| zrHjjEmWE5pDuWdjrNKp|B_bSduel-;Ul)!>t`xQL)^IGUe%eH1OzF~A(LP88wGmO9B+k3~AdQ4w#}!-d1qb&bt#!c>hf zTOguIxzoo+W%CyY%gai_6~RTpdC;G7v{}E>$V@bMNR8kc_UJ5 zeVx2rsgFF=C#o^^b>UcRjkw=a&Sv!w7#ikO;lVFX>E=)h3i@y+aj&un%Y=nTZD^P zR7B&e*EYu7>VjBmTbg5$4!6yds&R>PMTEnvTU%=CYHOk~SyxOmEm&N-Xjb`xOEqPc zab$~CQ&)%Pic{t_tnyYWDyMagQO%M`G03L0Y<^YgqD9peReiYKMh4Q|20Xs&YxLYUS3+FtjmqAZ0uBRUQ`+^uPU2YD!kOl z*2k?!)C_T4RBO1WX>N%%);CgKP|raE7KMW|Dy1ZDT3aKr*7XP)YgOHn(L>g^Mj~z& zfH&UkWqFjEa=jy!-B0V4bZ11LDm#AxH`=1UX?k2 zCL*n^)YC^J;dPDcxPPyU)YrtDWH%-&ecvvUvbrR^pt?%tP^=})c9&-AlQnKlEG-eu zH2#vNZjCfWYN8R(R0!mD2&x)aE|?iwv|#>%>PnP8D~^iE=Bwl z6*q`jeE~DFMU{k^3+7iXT2L;U<7>4BAV!_`!v2hGM3}`SPPF$ zL|r5rYi(JNVOf12HknawM$0;t+){?a^)wI>VO0<68l$x>ZIRYUovfvTJMKtx-MSX7 zVTIH%txqBKstCBfWxY1@Ae&*Bz~ggK7g-%&BUZXlaefzReplmCc{EfI9JJ>cq*!YW%KS3+Zq5hVa@Lh`ODYD^yN| z>l-^Fb%vd&73inwV&nGhRCrV%^>#gBQpW9txMNb`XcSL_0qT=AoxE|GGz!OobfD{^ z8Qbjn)wwfs#}{5ycmaxFWp$s)vFeyYWwS#rgB;^j&!0Pg!KL$&4o~>%aD6jSWq%P4 z&#In3v#M;t{BT$rnG>N+kX7FFX|=r(zbvBEXpnu9`g$=g(y^|kH8!rHvA%Jf+w6^t ztzAd!(edc&vBiZ&g%jB$2QM?r7nCk3o!Q4evfef~M`~j{8Fte|4L4HnwNGwe$kRWX z9QN%lc_2omHB94onl!f6G{w1uM`hnEqLFB{v87o}Zm6N_Glh&&>Z1`MC(yCBFqPB3 zu`H+zhl2A<%1bNVzC)D-E*Ywtt3psG#O#?fFUc*aUL9|a#mAL5HrKSar~)vyI4;2p z&*1{0?ntAJc~>nGR!6pVYG#^4S4PuKXY#f)A=K%GC4nN}chUn+X~r!2(w}x|&@Jrb zrqZ91Kv7%3kzug$P+sI-zTNKhmj!aK_4zL6=eWD}5*04H(|37#z?qW~3^;-gbiGMR z6=FD@b2TH7eVuPcAm_XGj6iOWe@39-+LY2j(e~7mz=Y1UvcMM0{;8E7m{1ZZ3I+;h z1afBta%KdwX9S!OeLzSrTo1B5`Hs14yFEXU+v&g7=bM`zaAswwbg0{Fg*b)Y63z%@ zf7e$M$myXtuk{B51=~|XfuhdT*@5Z4<1(V477@MP4e{H>`4rc@?Y>UCB#^ry;LwG` zINt9KdcPpkQ|6|pl+sf|=?PML76vNp_mZbzYBs zt-mZ#^j)9t-SmJH%n-CU{hzGsNVW^v$l-S%o0a}wOAnC6DZa_f^ZlgwHn>rgWK_FRmu096fnpqP7KiKf%?{)?rc0G(Wt7Wb+9P@! z^U0GhX9TjZ^#ubt+o@o6`YBOc(*wCBGF>wQPFcp~uD3Mclw_2-UepDuv&UGbyxY4Z zkh?0NuA;>0sPZnKl0a^lZJ*0;Vfmy6Y5Vbv$&0guN)z7x$^)Zi*;_)zj>@0!Hf1t- zWZ60n`&$Y)Baq!gb>Uh&7|7l3pB*UZOqmrZ@=Z?<6qE#VgQ&^#Gd89N3TA4^+>DX{ z)j^kq%GgURd#h_#-v%mx1$3zZKl>x)=Yr0(^6Pvn0~O!3d;Hg?Y)`cZTj>F3ZiYIg z8>f6d%sM8uKVE$;2~79h(qH34yBb#T2~0m_IoJ9^ZvOd}s$wxC;4GFOIu}OQuZ`8L z7KPDRtNLvK)f#E4DU?&SLb=phxH=jYg{>{*A@<_J34OiA@Cp@Hjv>R1&GjvU>6+E6 zX|wb@G}bB#Yg^W?jWowZVRK6?QaF2l_1I{P z)@N0qNJF^3m4Z zIQ&xkO*A*PuA9!`>0WK!nf9k|P9aNoZ*%xND1$X;^3s{ercKH1dsGE_Uk)nNb+VLp zq#xNs+X_sX35brTPV0ceikO)0w<~=J00yxs}83;64Km z>4$rJ0Q((6{&W73{iwf3c-WQ9V`Q;(xl}NO5Fe=@*O~g0-_)I4=r$GPK^`bje5dyl zKW&ogFa8r$FvvDW`px(Ua>VBs|LE=SxjldsyI)O6zvLQ5{f$qPgA?8J7{>wW+zd!( z#?J%7cM)Be_OF2IgLe8OxpXL|jxZ9HL3-RMLL^&T} zXD9WVLLPEY{5U2|&QXMr2fdSgY_R2T)8&|maP+sl6`1Qqnws;`_?hFR1K~+NzHXLd zWp=h^2i@6jsqO1*Y^^h#jiZ7LXUi5a+1YMt`a0XSVqa(DBTU2D_}I#DwmYlt>uh{$ zsJSCTjj2udZ&@}h_w;%>mli_MO$%n9O78Rl@G%46=M8{Qv#3m3?ve4_EsClE_?HfV zx3C->DlX^pLWu1H@YCt>{iW-H0q`dW!0GI9(jTY#Vqf`t1Mt&axW9P6A~`oGVki#5k%XZiVR}T9Wfu+VwsF|9=gD?;QYtY5@FIidR_fXBcuWEQI*$0Q`T49XMloI>noOwYU1o z`y=|k^)GCUw$>CD^;u9Zyzs*D=bOlcB6AfU@6`3R=)wyx8b97-gkP?q(mM=q?Yi*7 zizbXWkqagm$i){-l#ASbUcBPXjeHTeHi89m<0Y?LuEz`Gwelg}%JkjyBHy&}D}=u9 z)5&Kr^BXSkn{S}|N|x`0`ak-K&`HAjnu^4O0B=$roD(QwJI7mj`p63-BtWc zb0?Y?ech|vD+AysG@RxH;6H;WL?nm$Cg2k^oGy_Et}d!nb2gf*0AH{1Q(p<3#%JV6 zKCN{DKcL~Xo(i1CRp8h4%;ZTT{pkD$HT)b+PAN|c>9KB~0zuVtJ*&cPUf+&vnue^~P~O%AOUzz!d1xSrm_8m`+Xo#(lvzpm$a#-R_^IVNg2 z>4SOVJWY%Cyl_f5twlorb2VJI&m_j756-Tbr{Saz=Anx;Il4Z#YB+tt zhj{PRaNTZ?X}E5mZ!}!zpURm>>C)>(w}$I-{;1(}VL0?Tix*5xyUo>by}mSRxNf&? z8m{y2WSs2D=MhTxJq;&&V%~kPCP%mDLmIBje_fM9NnEGIi$7|(F8`1wN0*N~VaX1< zoiF6C+z8k0xq1M+eE|F#4cGO*NyBw}eyriTKBLKy++OMWbZEHFzfHrbZXw^V)o@+T zyBbb&2k?*O`5f6nFVB-TT#vUx!*%%`8h(K$|A!i`%fDB{b@^{=xX%BDhU@YEN5geF zgK+pNUB#;D6Vh;9{-qkO%ZX^X9`EfMuFJV!!*w}(G+dYSrH1QrPT@*r=F7|h@JbEW z+sXShe3E8|k2IY2KcGB(q2YSD{SV_*jHo_hozTzoV8W?BVm)n$dkCbu$r19;)^I(& zs|UdE)^OdPM>Jfw=VuzO+u;^oSTyVV?+3vDq~W?9{>nJ)0Cz~k$qrcO`dX8tw@bOa zSVQ{IS}W}Hq=xJEIf>r~nB`%-hELGs%-8UXHGG+d(;6<~U9I8tP7U}D4X0N^!0**? zT3ZAD3k}!h|8W3(6t7QEx~S|x&fEd;b`96<^RkBP{pK+Z*UL{HWl}%w?KK*%^Z!f3 z_44N9V_ryqz5NeqIIa00T@@Nm_r(KWG5~(LhU;?T8m`OvwubBD-kt&Q?+<|gWB~jZ z1K^|hR(;T!H^O4WFms_iA{lhCiy|db{zW zhSNI;$p1*gbvcFnB7y9n+y9~g@Tmjfvj)KD4}dS$@G?!$%Qd`D`O$E_{B#b0cMpKS zpy7Ibd`H9edUPtUtC4+Z3=Vr%Xt-X#;u@~!%T5i~<)6@ST~0Q?$f0=0Y4KjC&HMEF z`?WS+()m-h@rI6%*7|WhUR&!=bv^SmT<5RV+AF<1{KEk4&l6gG)bss=0qnrj4%vTk z5tfeCmpHBYkKulcn{5|=SFyWc$xOumcjo8q5AqEo*%}V(&tz`GvA>1VPLAXtEII14 z$U%M~&vWoz68I6GS5qC-`5$0?s7=uEZ+QHT<69u-*DPn5!9SG86Ey~oQ%7hY1v$w1 zf(?l~sDWQjgb)H3U<1E|%ij;xV;9F2lNe`#|7$LP_Z$4L^SI%01OFE5|9b<+ybSpX z`Ph4T$l$-0^*nCiQO4;ecXE)E#{QEH{EuwsGY$OPTri6ad6w~RCMBI1AmIk18(w#{Bu~&Zw&qdw&x25p2q2Y-M~@rJ}~fl zF3=w;HSpKj{#y%#8Tbsw9~uBhxj=dk zvYxLP{LNhc|7_qp*=|P-{3^!(ZQ$>)J=3-ISn4A--$ixQO9)1IIe}ZUcXn?fKXM`11x1JG^1wST842S~&rk#JV*89Xa9rIo#=xIp|F;Yr>&wLkKAr2!#RiV6o~Ii4yIgMO8aVE9S!m!E z^IvMWs2e^D;uELVVxAALIV+E(5nY-Ukev_T!Ly$iSm)&&LhCg6q*f1OGew zpEvMxSkIRX{2-?nmm0%98@YV{(cmA>eq34%{>7~4QG=h>EXaLk;2EqBE;WW6^oy9V z0mplgOfE0L6P&-N8u$U$=UfACX8$DyzJ?3vWd=Tj`->|K9P9p98F&`kEn(n0S1BV^{YT*Ckct11nr`Qe_`fVORpU?S+aWwGXalIIA z@L$U1d5(c^Wcd*T$M`C4;M2JL+-~40Y)@Pz4SjMrT`w8@uW)^F$0MAsA2a@`!H>H^ z|83xSA8c{{Kn|{FOEd77x%><^@SC}u%r@{DY`1v^p3V7GW#FZ3hZ+M%zN|BF^i!J+ z9Odo1242Sf=IsWKa(=&oqu<Q4rq$L%U^TSdP3 z*>0a2{21qBT?G8F4^I08zMk!TE&2oLZk1%k&S1vGctXE!a;Nv*G^nI|NUu)TJ)dqeN=U2qQui$sm4_&sdTF)R=GUansw*iOJlvYuxd_;J>+*uekA{8J44Pb5$XF~`6mr`o_Fr^div zW&3Y7@NXEu&A`v+a`J$IA7KAu2L1)tyJrpjyPS`hf51KyS^l35ex&QLfiGhIFAaPZ z+sEeohx`oguSXboH~Y^taLAux;MhmiY~Z-*_-X@({@*e11lRW)8K>gJSuWiV4IJO+ z+^xysS1ZyzV&FKFV6TQ#djdP`*Km^aUrLO4NyG8Y3pu6*#%4;wg6J^!~MXDrvRQ@OlA4%WeOl_2o{X8GS@e$tclsbD$N4g5C7XKOgw=P@p~ zl}5bpvwZJbM3rBwSWc}bhvLOLZzMz)G@STv;CAenj8k;Piu68i;6Gq}p4Q}${2#FV-)cC?M>%{` z!-+q@>BV>lL>Nf_Cc!%=~EyzEK#}&B-j&b05299>)LIX#< z6$Xw|+OfX^@j{=g41UCWn}HvvfI+BfV+U1s3ew}bbv(7&7ek+{ZBdOpSY7A;~1?`C|gAs@FtT&T$*`Dn*VHJs$X z%5rcoALM_|_!2`tZi`r@$szf4<`B6i4JY|0b2}3^aNOpw(U3ow>s^ z_%UDoQp1TK?F05%Az!{l1buVL`yYT~9(a<5lN_|$xND8{r>JO83At$o{{xKU{x75# z-!E4(Kk#;L2bODcC|!83+G5}~&mT4#_yWeaYB=eCIrnGR8~7DmA8*reiWl#%@6vGH z{`YG*@&A_N#r`g`CzT%zP9N6zNe=oQT!IGsf5LKJWPaFZ7~AJH1IO(rA87JPAH0YC z$l!;ZPYoP$zBc5{<@rJiZ+3y6S2CW>ILZU||DUGuQ+luB`NlaKKbaoirxzRe%bebc znjA{ky&Ug61OJ@yOyPOsUkv^Sng6hXe?STgA^xGsA@U|} zFaD+BBp>}w3h$$%;KcGzv@aS?{CFRME8W0f$>nUM!H@AXF0}*y)69RN#!vB{&H7!U z;S?{{pG!5ISZ`$hN(28bC!|cd-8s4JZDEB#_oE7>9hESMW;%$GYm%h8)=ccN$LdKFEIDCP!*h zyjQcH?;G-Q9>O65zmDgjxH}K|i;Lv^vU64YHz?j&>>q02*Rg-JhLb*Yo(j3M8HXKk z-oiKoe}wh9Sd&BXe$4*420oEDV=mHgiWl$amNAZaah}6!1JC2}z~!18inoR3Z!+*b zjCW}`#f$aL>kauhFXHoBiqILZGT^S`d)#E)^pdyJF)sY}E6P`w8K z4o+_d1=h~e!nq>D4E$mqr(M7}+NG(yo;=Oq$2lak4LpU%bqh2(l&*2~v9b`N*1)f4 zyw$+pVZ2?#iA1})Rl`aCMWdDc?`SxY3%P%}*}yTsxJ$!H&QDm*JsM7=!~Om*G@SUq zVg4r#d^Jz}p4D(7u|D>ShLfCIng4YSC(_M##*yz>NBYL#FXnob&h-HG6z56}HE`_j z8p$}?f$_YdzR<+!qj@336a&AM@p%URE5;XTIH`>Cf4PQ}9kO^M%as~V{P_NPBjZRH z&b{d}@Z($#do(#Dzm(w>O7aRB(K49r0 z4cF6KYT$8}KhMDN9d#Asu*034-mt-seJxiQ{8;~u8u%EVuU)Olp>*wG{jW3dH2P?U z#_1Za+h>=Clbmyz{~--0{tvmG*~d82h4YkNFz_nY^Hog_$zR3t4;uLWjDMx!6z?uB zZz!Q^C-DbEFBo{7@wW{83C7>m zaIz2fS$(A8BtMNl_N8%|h7&*5F}~Dr;;&$SdDw^6kK;C6hxAqsbvX&!mt4=>52XH#7dWhEu$wINpPXe4K}N)WCmAkx)Ks zatL~yU2n*FkoEt*f!FiA{Vq)ov9_@M zUmN)SeBktR27ZF^7d4#pLA&vWhLikiIwXV6p)>G182{YBpJu$5apYq;>wgB1PpLdm zykmHLbq?d;$GLXJ29EDBCu(v?&#yS%85&OV|IFjqc^XdqKcx!=Xnn%KKVux@Zs>VB z&x_X@{4qX%I;!CmZ!wq8Ee77n^^1pbQa)E*>DU~u9pLEvP;#&j9E+E0x7=h)hxr^~ z^dI|DmT*Ae*f-K);CTOt^TolB_7LA)0>^u|Ox6=P-b3O0N8orLf$toF z=?OzV*3@n=cn=IYSU#5={F!gy7$4$0FYsgh zR&MZP92GNgj0fl(Og&%F-yJn@^hY^7UPHWSKT+<0qdggSaZ#@BiZ8hM{Njn@FF5~# z+}z@#q9X0@MHh|F%{~932^XI)1FTBlTv|TMXK?|Pb#bb!kEVN@4isUh>fv-TQk)_} z-Lnh!%Mj0GjV=*M{@|ta68nRTgNc`e)n$n{f>pu9KdUQ4iGLUOhB}WHbeH6>DoJGK zCra`&W%Ubn?F%KI+h39|$MPlJnfY1UPP2q4Pc#&j_cY~?D%r7WP$-d8k_csn5}%eN z)(tODG!%sPPnWGm*25+FnLA3;rL3-f!Njv23#DG&CHbSe_Qh9)x@S%=Pka*U?kMtj z*8ipR#PQg>p~R;W%NjjhbXVn%>Uu3cJehy}LGXn-pDid!yuW;9a7A!s@Umd|nfje2 z-KE*xr8&XQ6RE*OT3Zo?e~j!VKMsF_vM#vy_TJuJ(x7;sjPJ+;^@UlZw@K@kB=+^@ z%MWUUdr$QC_8z$*xR=gpIC4#J@0awuHMsYy-rnBB?Y+IdrNsvixAgY*2KRnJP!o}t z1{cpGV+WIso%Kjb{;K*!d46VnNune_P@gEtSC;B)^{j1wu!P8Zq$)o>)N_>+N(|4J zdWO38l_mCvx`*dyZQCn3%JVZzcI1}qXt0X+)jxIE+3`}=Bjx#lYfADn>w}5eJ{?WJ zhN286X8RRdy6xbOm#!(vPbVz3BeSe$vs0cpE@OJWbdY$yG+l%ep9ZrYxiq!AB)_3N zv9YMUXG8v|P-3MB?Jt%2{d}q8OO$37?<-GiEJ*xb>9FI~tVi~B?sF2Q!=*GT2hX7# zl;@A?IuLJA`T9+ta`0S#9_vcetJv902-ri8>vpLUXjh4%5 z+H-1U4~@cenrAe(MrvEuG&e>gb-C)M*WB8cx=8LAyxIT%-|>l}bUATri*`9BTwO~z zy1uzKygJ@=g?q-VxFQRiFVvl0Ui;XO^Z8TgzDC=U;~!gnePTPZ5tl8zznE?_v(@KJ z3OT=?5T9Hqu+{e=wj+J=dCKdwmpH|t2_H*h@u>vS#^E&Lu!N%)U$axEyuoj@MCvpm zeR*;mCQ_#>pCeKnI=Dj0PYn_&hjRs~^Q$d2W)rCuB;5_UaIN(DX}l;>7pZ5C@>Qv4 znaa0BJ z8%WQ=MXB*&KIq%tN8gS<`mQH7AN0LS`B=lNl`ltWxPk2H%ga^1jY>*^@@-PS^N>9m zMX6Cb-$4$YQ#aEVTt#m2&~M9wMr0zYe4L1JoQSRTQ9_Cn@d*)-uhIR!ueRVE_bnC= zmF0q;-$;safR@&D~{cP^E|LEpU`$qXx=ro`811VcAzNGoB57S7IY$-b}#iH!= z<>e~xAbuKB-gJJdHu|My%cpkU7NvID5c$~7+sXdn{QOb&pTy5!VE+h??vwo09&+k-rNITtnTKex}|7eQ}gq`;w`_JU?kFtM^MJc!Q-e>04}HlZuME#8wRpSJdU~<*Im@O-c3TIh+|y_RXo(Y}}SA zM2>Y|st_aSk4SOo8*Z6N(|s{}a~j0ANg9R9x87F8MpaJV2Aeu0U)~^@XTHtsRr|Jl zovcfydU`GEqH415I$Kry;p*v4?9EZ$TV)ba&wW3%Rnw{phwl!%OEwCsF#CSQVO3%F z{lun&c71u1MT$e!BgG*tQ^~SaU)Akaa>>QlopMhaG2EX<_MqB3g0vbzr)-X(D4Fk; zl$RwReUY3aOQzzJEVBE{P8(0x`~4sG-aS65>RKG%=gcHI5MY8pjEXSIAVDEy6fiuZ z88QQBV0aV~MMcLvCJ+rtnwf#1DA37RPRA(TORIjZTCLSSylSmQ>jQ&4qV{nqt*x!S zm0DX9BWi8M_<;F+*4k^GIddkl_TJy0-Jg6id#$tf+H0@9_S(;LHah!)IS}RUk0Izd z!Iw#IU2q+=N3?+<3|8c6a1mra2o8oXWzN&Ug(9-Sf`s;kJ6P%_1stve&Qnk96zV4M z=Y^`;d7mc4ne`Py-2#u!toP0p>UBUWSG?8hapff+>h?L!91`FvmAcibIA)BXL$>Nx zC)f)PnbjMdU?&_0QDyu_C)5tdL7yB4vz75}PEZ}k!GtM0uTycrd!q7@*TU+2C&(x9 z*>NZd<1_IKFpgWS>KrG&dzz;~KPW%127P%Z*WrFno_=roykz)uZ8m4 z%R>1k*dzqptx@g5C<_Q?kpb_4>?|M}1bux`DE9y>^)~#5t5Cj$9t)05!9V3b z;N-aN*=^@_2<3iei#s3i{+=>|7IB1_3ASww%0p$7X(_UdzcF3LFTI!X+wx`nI{mCa zYeJO$mY9_N)Z~UEG{_9c(i6mZP>OsBLeYoS+{!VmDjnJp1gW_o*!l1vw%ZJGHn^14 zj9bjgQh2J~#t?QDw`sT?rG!|I5?szHrCL{5)w(iEt>H0QYW0G5umqVQBD^K4Y+nGmDLS)Po6Ch2l!aVjbmjxiHQ3o+Kj=Rppqm>D8Cj+o{JF~jla z2#|XM66LW}#iNBon_tL8r&1B~fV5PR7`Wkdq7G?OGa4in`=qUn}I`9hp)Nt7?dd9%#|12_Yo2FJ}F1q^4N zsd&e&R%-Lt7G72;L|LBjBGD{}2vMG0vP_O%YdR3&{6eKRUx*846NA|n0<7UVR)i39 zP0o2xH5twqX4DMQRt!bQqaoM{CZDB>%^Jt)kPhIAI8Fxz09V3s+B^lEo8x?;tSUB_ za(r$kk8+Olmu^(Cxq{=o_~j6rkD7p!#w0+<@w^PKoa6i{QplL&BQkVuj`Jr>p&%R| zouMn@cu@wYahzZKlLFcTjxUA4@9#rUL@i7bAh6tuSTly$GAqUWMy(l`5nqm{2zJ3{ z$R+s_Q-7I=6mca-`KyYmXfNkDe~DHV@pArZXGSJIcW#E<3|Di8tVBgzm0P&0Ob!%s z8LeeTta(L*a~Xx10f$^hm+>PG{Pqr)SrMx^%EebiEl1gq6mdyz_RPu*_fd?$(5r}5 z9OaLcDqBgZXVf&BG&mS${zx7h!J<+%K(4%RuStsT2J&2F=Fsu z2Cm58ZM?&-1h9Yd%2`+ zaZ0YG*%OZBvp;akj%4EG)@PQyG1&Q7%#)KVx#r2Kxas^0t>~W%U^f1Y0Q`Y!Gq3<` zsGo1MtX+%EW_ah=EVu9fAT@%|=QvOOz~^%uZaIYju9D;TWN>bd-=D#ia-6@AtcuO$ z9Dg`NSHW@Kxks1Ez$kNDeRBoJ`BiCP$Z=POu9V~aNe$?fI6f{zSHkfL8C(&^-5H$5 zak#1z!jL|nHTqpDnIdXA%5p1WO);@xRzvuFX2chY4&n22Nxmc{%Y^ z=38COkehDp5a}NI0_iT}bgX7YT$Nk=oL+&yM?PTcaTQ0ot`%`fE~6Y1|9}|q2eB2g zilh8(E=7bn`fb>FL<8Sc{uEWxLHbq1Dvq-5l-yqEd&GectSDj?M}JO_6mca-Uo%mw zI0}n+5EOAG7jLSGTE$V_L|wUtqP6skJp%(D@O%)`{8 ze@(wIp5S<22IuB@e+HLR!OZK=M5BmWQ#uN{BsuesxUJjxeLOZ!6Ioio3$b$6EzR|_);Bcd($6lN%gls;BWh(0AiX@#R<;51jK(^e zTi0JuwgK8~G}hSM8Y|lX6Z2@Su`$wAwgJu@(UsJ(E$xx0q?9b0%c${=FzdqMQO4ir z7!Xb4@4Hq@%v`!GxNKcTWzwlS{#0;JXie~9r{Z|1bE#8xyjHMq*)pf%xUcgoU6;+Q z6sl9%lKA_~%2}5~^qGWXlD4qRu|ZoHtPEB<#+GU2jgB_0$x(RE`ttRTEoJM^{hp)Y zD~<=ZJ*-_ebE&rM$_p<_Hfv`ssC251Y4i9eCH%I08e zW295WqmhnqV_mFHgjOyIhXR)bLg7n&t5*b9ED_;wD_qYW>4=JG^L3GMOoYSj9g$5U z9Ik6=Uf(JZ$lR754g;Bhs^xQz&C#}SdtJk|kr-TE-q6w99&76u=8&W10(Tkb>*euI zcM%FMs$H`vyk=D(;192mwaZ9$3A4TyuLNk=x*3h z*TIQm9d)hIrbtIvs@x=xw-Pa=BWYL>HK4myD_7S9{QSribWM#f6cR1X(U{B|lGB0J z#>l4Th6vpSFS1Ujf~XrK8yniU&+bs2ANjd|xvgQDy0 zA=hwBG_`DLZe4GdESke|#mcZha7nN_AZ)gkLs`9Y`KnL=${7xGE*s;qNM|?-7u#EE zjoexN#?_H8$0N~L4OnoN-hwM?0;^Zot^#h2kxi_JaJVJXDn4J`i&xgJ@QX-mV|$xL zpJvrH)Q6i|rMidWW7bX0Wvc?=;F^`y`s$S{R@SaD+gw9kOG|xS!?k9WQ#;>K*V@<;=?K>~#F{sm z{)+j9<#F+Bo{MVNTqeTd&2864TCJi=mjn%*lXK}!*k(+Z_(DN{c$IIpZ@CCdqZI9J zEiFxr;f-yL%}rZmGYN;=Hn(Pcnarnl#j+JEFI|z-Qe+ZRd$c_Q8ZlkEKd`8F2}@yC zgU{~|UmjS!GQ7%Hy(~~8!r-J$k0G;LTpJ2m7@>BB^nK(L>KYq6BGHVl*dU?VlQ>j1 z)3aq7WOHjQ($QMig6&^4M(X41ML4#xon>XW#f^SQ>!@D2qGt8V5IE0{$i}u!(tU=* zo9nKP#M`l|qy$kG2+cd&lbfv%JuKxTb)W~A=w#!TCM=6D8d~a_H%1y|Nt)^!B2kOF zf-4rUWL~D)Wm9I4BI+7qaZo^{b$x6D6U%Z3SBiS_Nh~=F&88Jz4}B|i;HbQCQ*)=i zEvyQz3W#vHp)KATZPKw@PM&Z71e=IDB#C_uL8ThR?uNoNH@GWB<9GEt>%@mRK(;#MVHk62P&J>OkgTOV(Y#b8%BIS%x1sZHHz)4p6Y=)rMiV??aW{iV-iv2WwYKP_g zMPrI?RRd#6ZgB+0xVxQ!F{L->`Nx#sl7p_2MPo`9 zjVW3*MyoDFUb~3b0+@XuuSK83%dt)wlG(CCc+Z&cQN_w_0gy(>u3W2$h~D=A&Df80O<#sTbtSh#p~+p zVc0Hd<>QvNhAgSl(n|SB4tZf)Xt2? zpre(EA{)X@9d#Qc_9^cFGQdVYRI`0ilHb9$uL#K#(U|}Tn`v1rD?CO<)K0Tb_$~+J z!|P4-DKJ}qDd&Y#;12cU((zi=lq#LdYy zg?1A3K}Jxp>kss7FB1g!5}APSSO9%N9oh8<3xq6-H5Ajk#clKrPY!lkxVL?%{M*41 zz_(ZETL_RpxGT3QhtKW`;a0`8NVY^LXcG{L<O#wDW{;5#|5B&9LmW z-ySA?8GUmG{jMe0)88|U{HsWQZl~}IRbiL^QOcjo$)TAJrmn^}E;51h=U;eT8shf! z&){iPA*dK;;B@>8@1t_u8vaV@?ZHm_Dy6rnS@cnQ`Ce!yaQQjFfuf=6e*@`{`#;bh zylKbjS+DR0pxt*tc)vpC@CIi@!7e|*cG)4UB?n$Y%%N|DZ-?0PhjY(E<=;-1%JALk z9PIfI7l6(w{+1rDt81U$P9#zSxja*CD!hwFi!547Q! zhrTJPhR)NIAigC3Pk^(1YX#hk4^^c5s+l z$PV;+Voq?FqsQ#vknf1*1c!X7Ei1qq;H-OVxNaEyZNuOn8wTGy3?9aYL*@Gy0q@Nz z<{=yQhbM-ihr^;n@qu%ez=yXUajXgbG~s#Ejq$wpHN;Y?Uk$CZvSZ7b^2sy-zRfZ7#f^UPi2VXpZkRm*^Yk0}#MSqmR% zlZ@xPV1NhI8ROp>hJMh3hi@b?{Zw*@(9O|XLKx1q;Ncnf1672Fd|~X&d@dn8l*`&> zzF?uZ>Y>kqhtK6PpVh#abOWC?WBm1mXT5RYI}qWqRc{9@e5`tV)q=O`?H?Arm7ZNX zJhtj>Hg!67z0I@W;Thk>yOQw0$Evrh2+w+>Zc}tv=&gEq!GgES!}y1dobL(JKi3cASr2TdjGqP(%iV~KzmV*Q?`UKE8&r?4+2pApz4hDh2H`)l;pvmn zB9H2a`STr|#WwsAO3!yFGd=6A%tp_8n`gtb-uM6{^BJV}wcbWQmFkO+(b z4S$m4zQTt84z-tSYXL zS^i=hp7k)*hG#v@w&DA@UTk;*#cCV=R?>5w4L_R7+it_ZK=yW*4c|oN+;79bN&Nq6 z!}IqwKCt0=&zmRwtRG&NnLzv)|3{MZEE}HN^E4ZN9pyLEhUdBH1vdOlD%UkO{BFwk zejENSqTg%7mr%X@%!aQeJABoKXFFv5ae3L^-nY?nJ$`1xv;FX$LClBkWIUCF@uVK% zw&B@sXWH-|QF#~G@GQ>~8=l9>YixKPCx>l#9w+l%jVvdJ%{F=-Cns!p_FuQ#@GHok zzh}dbr*g5M=6v}Z>d)Kg1MD|!_+teB&W7iD|BDUJ_0D$`a=xt36E=FT#}PniSqI>H zoM6LqJ)UL5vwq5K_#YCZDjS~VUv9%6kYb1$!b5jMD#ozRhKKb+2rV`~%zujw&)ll0o0ptHfd|tBg;d#+-ZFu;CDTJdo{2hcpVZnnQ4p4ui(SkDQ z0m{pEUSz}bi7A1C@17Cg{%f3((u2l`it{z?lT=sB#n;DLTL>8-_r2m14=zBXC#Kp!Cb>n(Vo z=l;&H;kg~%Zo_lCxX*$I{t3m)kC^L{_J;DMgo(a$V+pyzSGFD-bW z=XUf5!gD*~b@qM>9_)(WQ2d*P540oRr{PTwwyRo*z_+()^Mmo+Pn=@IzeMy?ZFmRS z{kaxA+!2>;?gZobl9_ZgB`g;h^`SKf%4_fH4yg&f$%z_6# z1yrw3TJTt2qVKWbf!;^-KPNor%WpQmVxhQ~Zc^_wng&y@xd@3w>i2jQ5t+e2Q zzK7@+TksGaLvWJ?5AFA28jp4mp5@{58Q0nHysr8c3m=I32~Jt?z@Pp9H!OIF?jrau z3m)is{PUm%578eH`~wRf=-J=yvEU*48-ky);DMgUKZh)Mh<;4)Pc3+$=kX8oWB)Ue z&V#;Yqi4VUR|_7Z7Zd$43m*8#$!~N1tbaZSqfk1=^E&ki!m~W}1j~=)XZBTD|EJmL zA0c>>1rPGD{-;~;Ao4E>F0}6pal=~r-JdqSD-C;plAJG zX2Apfe4@X~f(Lrmf3pP-^j8x7!_+^sJ_#*e90va>`3a`ya4NMwJD#SUSTB?m1LtF> z{|zy;<9Yr8&6%1s2D+Ne#4}$$pTP8-p1;7wc=p@8ZpwIW@7D7<@Qm$n);xIw5(o3n zq}i6+lH|=vcxRG#PWky2c+JumzcWeaO<+1V2scunAxcCF$FBRE_|6~SrHQu{-z7og zq_5UjQ@aN4SkW5_=6ztqJkC&R{aT~OGpg6`!KS5mJF7z7&wdpSHo0!zCxp-&-Zp*} z>Yi~7^3nJGB|kL%*uYlTWT6{x2OX#SQd>Q`w`Xr*t&lgIC3|Aa@zxiAO863k#j*9i z#NasK4~K$c-e79+THBRHdaA0*a$ynB!lgBICrn~M2_}wddg55cymcPuwpqUIH`Xih z?ZH&R($x6U{_BvmN-S9C8THhs00jIV&G37)fdhEglHa4j9W&Mobeftncj(l5d*H?> zP)ll?Zp`-R#v6N2sV(a1^?RJB)(WAsaQ8CD z#K)B}zc2A|aV+3Vd^|3;*q8XY=&4Vj^uL4~dVEHoMFB9gvJ@WpJq4w|;>!J4nt{8Y zIte#3ed8(6-@vWAK3Cr29C?j{-k#*KSb*w8igyu;cQJ^EY-8vAQ^8OuDygH9fcN!Xv#ygjH! z@4qf#YB%Q6s9!K1vyG3`cN_p&uDEhP=2i5GL(_PBzF_kA+PsPT+9UfB1^ zQdb(O4WGA_g3N1qqhos*e}u1oRfH433!hgH6FM3SpSVe2EO{(e zl)^)d$s@6RUn1nuoS-GG>m^qbZp`wfw&`%K-5YyH)>cTDm+W!vg!fVs2bEyrKt*uJ zK{(#Ipv5!Z)wKZbOIyAl_iAeZ#)TO264RlYR%Pk2DY(~N2}^D!U2MS z5dl_?KcxkY1AZ76WL(=cJymTTm&k58Yh3aaoCHrEi`DrO3q4zOsA5;wQoL-j##7Q; zg{AZ!2f20QbrjuryYGhMc*R|Xx5rWf7?+@Fd5_tQtN)%8aC_rcPpK&{YD4dZ_6!$I zYW>GfrqfU1U3-5%33u(S^YrOPi|2jg!;sPF8PpT+Y=EZK9rINAj9(k?Jq^QbOF!&~ z5gVIu(CBgPyaoE_#9-d0dgu>|^zN;mD!=iky^tjmx}J=APBGql8WgQ3-p%LKx^d98 zGYR$317hlCx9dv6J8pVvooAJvYVoWEv%XqS9N4hm+vEEQ2xN15?B-C*1CK+?lGPQ0 zGW$~+(tCU4O_x|b~9UPjKPJw^pMBjVZy%_sD!=dg* zPtnsb?=yNs#$QQqLE{j{f{ByDb@TUx5V5xHTRr21D+%w|Y~SiB60v45MeS*re6#Ni z&1>{%uC9l`6&V5HeGQE~ql;Hv$;Zil++aVYU_a%dREwt~l!|$(^uz%jrZ|2fe7yky zs_?!x@U)(~q1>h>Qu;GMGH|C!Fz`PpH^Basw%m9>CmE(dqW&;8@*B#R=XBLxD_I4b zVAgmTw0{)pp)Y8B237Dr)X?FeaU3e?)sV3jo9xO-86SCHqXN`;vi0_)Kz11!=fvPCuC68+ z=O5V%OAI`EL7L7+$gKz+5?zO@L%)N*A~Bffx(O@-dYTCf{GL&+u0wFQEjpdvLok-h zB<`6m83W{qLM)HvmtuY6o+w7BxqLVKw*v*q$tRl9pA~$&=|c z<`6W7c4t}CmjZR-ME+CY_fP{t*W!a%iF$+dQSf-vIm^3m_$1i9Dc^W6WPBp)H!&!- zw#&LG2+r#@_HSNDO6V6S(`n;%d?xMmy%SV;1nnB=|JWk*@*IES?MeJp@)@`%EB#Qa zU{bspHrv0}dXK>xgLK|FM%o{9v#-{ZJ+VpKJI9s9j^X&q#M9t*f734Sv4MW;d=gsA zQD`cC=+7F#pS5@f^&Ri5wfkc5o!%bswr<_1@n|rc&++EuoPUL8C3Or5GJPGI>Vep1 z=n5z5MlU`gHxe@5@)-v)l`nBZaV4SWl(#>-cK#SW)@~HTbY-q2*sz}Zrl$frh1A0y z7z&0`4|sI^eb}=K@H-AbX{_rAJT_5dKY$*A+PgbYme#x;G%0gy*d?qOXj z@lJ_fPFi55U*+19){Vm;g0avOk^|E~vL`-G_VBvVtM~4M-T@5)h2w4;ZYJI($p(JQ z`Zd=#?AE8rf~S^L>BbwbJ?-;cdpaxnHbU3bgR?Q6CJrzFFsk*=IWTFHU25jaa9qH! zme7@a1a!FL0A$D`1Aj`x8DHY#(J+pQE%zlpR%0G-Pi|vHl}a1=HjF#HJ;9uAOwzyt zQ~%ax_i@zH`gn{ zcjyfTe%GEQ^)M6kZ68#5HW;gqx_hheyQ_MlLc|(Ut44W`gY)R0FhF-q`?e1{V!wy6 zSirR>r_I=g^suxB$@gd02NIo{GyeYu?&bQHi(S7r<%65%Kg5MpoW5%Q)EGTg5J;U%EU>& zoG0w({K5ad5B?7Z_ONnm)r_Kde-r1)vCimRy}PZPor0cPV40W`bK^Z2kZUk7C<5a~ z#e=>!9l)_GJOrg7f!%eOnln?%@mE%^i3K8dhu$3>1&;m5$u!(7f2nQ^>WQAUzDs;~ zb-Y}I?$nK~Fg@-C?@$8n+6`m=3e->_dE9l&6)NastH&)|T`M7;TIVUnUIS)?`*pG} zIX&ujzTkSYGruXBjz#oT$W!V|ysPM`FRk*vq|f_RsJs4ftUOijn-}xwdiT;2SvPp+ z_`D8X@77CVV}mKbr$X;uby%ObKWO|q{@27?TFeL7s+1R68H&au*g*2Q-d(G>Zg~Pm zJvg~dd=H{VpKiR;-;_?LX?zMRH-U8_||8yHOcjDxPOKf=Oik89@@1<)z9 zcs49gU0EJ*?WwN!Z68!zU2uZgm4NljV1r|M_x0r=W1U9}8EI%5%ex;)z)HOo3~JAp z3QY7V)uq0~R*$9zQURx{tJf)nue+)xG1%g|=?ztgVD?lOMsjiuD^tgqONMPQwa{Zh z8R=lJ6ECLBkob*vLo}r7|C$pQE9813lE`)OLRZ&8TsixAoU7{@9F6!rRe_xKaNo1w zk1Z=#Fd>$fp(hS>`i-NOxir|X>!#nq`V4Z*ZS=lpVL{!(Zs1YZWD)SbIq*=h!4d3U zQ?47o1q-Z_O5Kf0^`#b#3K=z?A}qM!_sIH(VPz-CvIhEmK^-5h4wfodAfI4C_WP-3-9`Ss-~Nj!Q|n)B`0P<(pUIDROn~c7Sz% zSpnl4`aE5l9r81&fYYdU`nvrdSRYe-^A>wtw+sj&mUTNDgWWAR zRrpeY5`QY7=?#YyJ&N8vezngCl=u>ZPY1i#-M+B58W;aI-Q!CP9(8p+0Ov3|eBD(S zCkB;G|7kM~Xp|W(W!fIegevN9PN&nme2Kw3TwPbeBG*^%27SST=Sp1u8rquzx=IB%puS)#a81CtY1089U}mZh;jzq8(%Uuik2`hW^h^LbbmQeS^N-_z;(N z{Hd5{tuM8x$d_7**7lOmSX307x4`cycXh#Ai55$K`4d?79(>r<^=)(lgFkj9^JSJW zFY3SRWI7E-1qF(YzVSw1V(?@v4~A;n0^T?Ke*}#sF?iCIgtvFIJrcVkwoz0MyQ9S5 zXRh5ZBnB1N?tLR(?mFU1R^fcnWmJGf#)1C3KSgIP{Z-n7Z-e;~gS&mtg5F{aho~F5 zePFF2_7rHxQUGuhsgP&gypX5dmHZnxyzY)sA>+5QJ|F)`F7?Tlj!FE+Us>#ae>$DU z%7g{XAwNtqPiYlbU zX4b#^vve9`-AmKHc`wDz@hu3HxVn#hDk*Wnw!iH&sJj+usK;DgAkXJlknEqZ$gZw6 zU=FF6XS`YTAYj-5^nU_b!Ln+y$CZR{OQ3{M3Mt_S@EkfkN6a9)_;#ehGIj8P)SuG& zw|{J@1hdLYkEhdETWGE*mM?Lgrp6X#S{`EYps*?C{U=BkRO3jnWxr<~X1eoJ8U^K4 z#GbTGCH;LTd3=Ud3M$f#*E1^Wul)I63_{ z%FFZqwy6O3qKudIM9-+uycgqt(i>Vle`IM&$qL;p-{4tfH&vj1{kbr0q>hngX{wnGxnUF zV&37+MYy4QK$-qRP&qj7=kY%V8xBbQjZcC>;oxh$hwUn89MV(cJ+7{=!?r^9mf8wq z_Mp+{pMG>+-wjhj#s|`lU&P*aR4{dO3WPM4R_KWrM$PMszZo>HD)$}A7r{iY;_F?E z6G#8Nt)B5;L3rI*ntq{=b|6ztG~*o(4go7lMve zPsKp3u*#G;G^%glZ|SseFYd?u2e?7+n*(E9lLZVC{PP+;<8R!Cdqrsce&a7oQ{#^U zqBPaX+c0S8TYh1hGg>%KzwvHgiuPPwJK@4nSez{Pz66Joz>Ok1*OTi!Di>WYSGv03oqE@v z8qW-188lwijeWqal6P=6>!}+ooAn_hvO#XURG5ot#;fsW7}x1A$Xf+l2W!Fl<-Q%S zNA&tVcp0$wNdLt!{)Fs&hJB%hT=>8}yLiGp@So2>8yUD67pw=8vLtYhtOfQ*8a;im zA&uKrG0!0B`c*ygPK7_UK66eblxnNmn^)`H?l)J{e8!>Jmyw<93yNJ`FN3>Id|YJ4 z#yx|_&!*_9Kvf`Tx93OJ0~f}tQ2Y{5L=ot~4LT@=?UQooCiq+ntucCEgAwXaVMdbp zBpsWlr*5e78-MLPh`T9U9@kT~RjeKy)x%Edb>+B?i82q|D(x&t)gyP~gS`QKiKf3b zot9fM{f!WV&bt3{IXIW&RXj_f8$YKFN?2vC+PS%6=cV&3=Gnz-?l#Na4b7rY4Ny5%Q|5S2$^ zrJ@TiB&*yx4mv)#?z6WVU+n>Z;7`F9!$iP)ERcN7m4t;2zdWO$_g3SJ1->6EdT%ur zJCHmQ^T19?37GlxKSP!<7})87qY~a@iLIWJw9obRgRVW%d9FRzS3p~H_ldV*D6NaHvmN|N5pInt zT2rszqY42(u)NXh_v9;hRuQf`ql`d8_(lNiU`@W$XhTIQ+1ovC9 z2#Jc(u=U?{Dh@@jFZZXGi`;`u-X0(BQezUFt-2UdV_12wY`4E~0tR6^tm&x-ae&a2` zn}bh3IEVgkn&U^a=6G9fhfF8!Fth3JGY&9+mk~XYPWSHz9=m+c;m`qc1EGS$xl$Mc zXSB(=_K)M8OGxZbX+zZlmSCb-Yv}DzcUU)CwCa!@~iufYMJfp5ijEI zwcN12Lr=W3RyY2x8;5+xE4VY~_mudIUtp67cx4{mBea=zxt>}8y?hNF>d=!tF#2+J z;mH%jkvtNg<~Ig$i5I-@@cY)8{^Y-@e@?|bMXY?vZZRwYm-vkj@XTIs->W#`_ZhvN z`?xy-uh851IF3*HarK1#>GN10*TAxfj5dI~&72>^qtuzxAviRW`vHH~cf3=MyLx#1 zX(<1j%165}#(Qw539|>4@tg;rV*T(G3s$1!f+&wi-s9)7WpXQ3@9rGuJ&p$~@KLfS z_B(W*0dJ3M&x?NJ?U3|&>pqUgm+LzYK=I7M z8~Otr-0Fs7K<>c#QyOk3T3RzSQ`qe8k;TAV2kyon*JR;a3kbS_qi>xs%9;q>M2WX2 zGn7gUs`2MsdqSRxFm@`z&G`TfMBeNBJ05uO83#JQNjoRzKsSDS#vVBTpfOob94O6R zoOVqX*%jmJ3c-*QPC>Z#9ENNj#({#*II!i;fgiahi|274C{>mz?tWKSslrCED{I_$ z$kp{T>_A#bxS(tACFAsz2qq4VJMLelXz{Vg5Wf@^GLHZK!T8?2^JRr_4)KT4-G_|h zAkWQ5v46kAg2MrF4Donu&b4!MI-RbB&sVx`ZGnceV^9KkQU(g=bL~0o+VkRa<9*=y zUF@q|$toOfzYZuG99k4*wPo0e zg>BTI_ucv@IpD}T-L=275zk)eM!(Os=QqoZKI2f|k3WFJ8gzC5nwQ)^rrz1;i#Ng< z0XLlyfUX#UUIu}-g^I5BqHqo_XLPhe~m z%<9C!l~|9~4BUhz;-fCUoD&aL;{Ho;zG9x2eC8i;_JNK}mL$;0C_&>h-S{wms-D_b z)%O@nt$ANd+#u4i(}yf5`a#@%vgzNl=hQ2|;W@Ck);O^S4;|?{-hqqb!Q*F}*B$tl z1QsmW(;0?i6!3vHsN+%K#We6@POBG7_S{&J*y?er&p;(@oADzNFHH<8u_>@Bn*SqZ z+vz_N@#4gw8uNNzThQVe<+{h5&w(vMj@DV=czwqKknM^q_wVw)mOSR#y?@}h(suBC zp&T9P#;2Ll0eTtB0Aa^FK;QT7Yf$g#OI%&|!A=;eIjtLbJlWNCCY-mHI9FGd6<7m< zRhsr@4u>RrVzs?!oDecGm{xt$16>=OvFX7Tss}tT;_t&Ac!3z&4;*j~rJnV`3tjq- z10W*L=iodqoi?cwB7PSZ2+Pbpv0nuz3?_)<)=WR_>bf2Hb*~*qTKJQ0{PFJ(#{Yn} z500$&59u@mKBKqq$t?I_-$PmOp}yPy0BirpVy6d^>DYv1Pi(9#DO4}5)4?8j*EX1A zp*G+NpSpH+fgk4magtNcweY}s@|P#m>5S_7Uyy@YIiHl}p8I>KH&=3uA_#-s5=;;e z%I=8y!i=R0K7vZqIkIt!8(%FYCs88F`T>8$?DPS8@Qy^im$Z`w0)M zNLo$6wY!IHW>1g&NT@3b=NF;>gqQjHzVTT)ofu5>vxiQk)7Ul#+|R?^B7K{(pYP`9 zO}mCXVhkI|{dd9Q{VufS{y7lGb}xq+P#JH-puUsmNv3`>1(OoU({eI)aWey z6ZO^BEBxfOkgaVocWb<*Wo9hWv9YHbg z+F4V}FKEPnlBd-240l6YW5msRhB}D1UfbHXxz)`VjJe?=PpX`DiyUnoW-<^ct779F z^=^6bnB-`wa~s=E(a;ueX>^0OTiP1xVi8Du7OA2w9&3-stQu)*?uZfsuWZn9}2L%zCD_)kuPR=rBfTHqXCs)AT`?9+~J-!H9F1ItXWs_ zDBc3w8s7-l6T%PNAqVvJ@n|HsZmktZ^2S@)>>^P2WwKVmklCoktoDw!1{2C?wW+za z(H)Q0H${!wIYthsf)G|^ZaO1nPNA#)XQ7p&XjKXZK(g{EjjU279a*b#}vJK*-;sZ!~!SO|GH zg&WPUbz01g<*8k>h#TJ2=zRAoaHx1cpt~{B+8k*t<2$?E9g#1`n>!*=_vVh~Sj1h| z&=846L0k)q*0#n-l)uUTMew7&7%8 zZ*K%I)lk>4A<{^_fu&o@7$Bz0lwfMq-3(f*Yn<8E+OlPVyP>WXyk28Ob}&#{bl}|5 z@j6)xXXFPrYi1LWWcG@uU{=JJxM|fwP3AHZQvwZuEn0Nxiv0z5{HoRX>DwY2)YXnF6gF< z;NS-COrYm@ahrvJRy@M-j{0bZ7``=%Y|ZQ7EJnWoIkKwtTYKE_kbhJiV!gA7R@@Q)h%ctoBfKAahCTPR>|1{bimMml){V0j8xCPtg0VVE6A#n|%*!vH)isp?`eD{BmG z?U*=gYGKVF>-o?kW`$wo88&5xURI!$u&o`XABI2C;}!Fn5Dvr4W%vR&w>HO`>sp$x zgPb-s=NJSon0$^tGtY<4Up|M)a=1PoZES{>yf9=c4(t4NH$1e6OWwlVGrBVqH8KRL4EE*>wj1uK zMxQw17KrA-ooqjc`?P^Zp+{fOP~D!1-UGA+_}YaS@jwRuy-f58h-w;~zoF8MaGvVD zPLbcz6Wdk%vg>xI{L$o3k^FJXpC0_|p4h%!kuar^kW4R=YeLiDBzhs_hM(yxQAVEn zJB2}GJE}Ebq3Gy!f95+9i$-_CD}$Kp z=<7i3EZ^wsL38R9O@0ipXv}2zI5!fGIm=2o=4{w3$`HCL?_)yO#SmqVt||$Ent&II z2@bP_<^tFM;W(Q=IbIYWYlR&Q&9yjgrsS%4brKuFEAfUOu}5 zuI*B%l!&pVt`J<&2m7YN6?t6cXj2Be9*QqD4!2pM#;1|Y8$eF1j7|K=A;y)v;&5Mz z%xiO&{GD0yZvir{dDnFkB8jh;P>JNS6^?bNQ`{1|L2@aT(2WwBA*ivAEq6uTPAG#4;55u183guc%t;%p;!#WDc^lMN4ICksrD(TJb)n ztB{ndK1HZXLRY}Yh1Ds#gw{!Dt%RXW6y(|I@vJC&X7>PWN?bNJ5OFKE?ylsR=K=D-jyrHouUvAytaDW zsmfSv-`Wpp;U zBpJOP8V_Cr>n#5G+ZuPbveT584GU*+f$|bRlAWxzQ+41Kz`|L4s&d*0dTyn1V0WB{ z|MT&G0sfbrxV-vAUQ|_x_)+q@T6ggQ<+%~m7K;xlP&RdnY>UM|B~Vs<@h=Hf(Nc?l zPoP7B|D$Zcv`z^grKkB4e3QTe3BE<3Y~{rRD1%C^y!c&eF(pFN_Ky&v5cG>DKT3;_ zD_>Mfr&MS^fmG-v1X7`w5=ez!Ngx$^4a#5^`qJz|Uv4QhT>nw{ z^^r6Pd{I$9dwmo&QS}7*er#vzNeM~mR8@HnyAzqA>Qt^R%4k{5Q(z#XlIvGT&{OHY z)sgg6`fIfapPEiq9gB}mr>Tyo=j8O%3H1C$sYdlQ1=_M>%KHSLPR|DkK2w1sc#bLG zl6qDrDR9=#G3C1iPi7}e@Y$69DT1exlRZlCbb8)L@VN@yjq8}QOB$~_i=DDVTGCo| zo{|_%CZR6G)NDy=CAFr~Ga6!@LcLgd9`z`DMb$@*M)r#8A_C`OJ5Z|$oG(GY(l;8E zzEFaTN$Ii+RhKB>=2Yr4Rb4rV8Lbr)VenXdP`!0DS-!3=Lr#*-@@#om5F6QxtE(uJ z@-rGEn}qr$<=4oS@?5R_9zmVm@|@V~rmsT1Ry@&6|LAIJYO{D1d0&f*luquE(x`E(?f(r zAESydaTK}O5v+B{Auu_DD+r{few71?3iA?K87&U@y1u5J?GoZl1->{x2|qipX|r8a zNma!$M_v)L--K~Cs^TpU>0HShY<0*v+6b)C;%x+ulwg8n870A+NftS#E56ksoBeqC zbT@%=h*x|E7LnU|@m&r%36RE7e2=5hV$8+&Q(9@v#ouv^#it8}ra|-2G>|e6Gz;x@ zGMFprBb7I!2nfzE0)0U1odi-%f-&qQ$dc%8&#OVY%aIN|lB7ct90?Vvvv(JUtEg+kzH2Mk}ztZCpv5!r7d-A=VuN!xg_e^{@q zAItt>133h0e$CXt$j@G*z#N#nFZEjX9#}hS3ps=t5{y&REvFt$J*3<}R){lNBCSI0 z#i1mdjQS#hl+Q~@sZJ>pc~Gz)j^)CkZNIPnO1X6$OZe~p)If7cHeuf~G=Tddzcwj}|uvPUTNZ#-!^;!hU+1#bxgdlY? z53BbfDCY?3^8`|#bC^ImM^KNd`;b`95!83pKOiXQ2&&?cLdr>j>T&?*On)_*U>fwR z`fqpf4~Hr_pir6>xBwt{QD*~)bk{;v`OGHCc(TyXqq~nGlhz` zJcj}@(b*;un1dt&_a`}7DVLa(U@0gBGx@Ul2$$S{b zNR|tz@08F&xy0xs4_y+dEB+i0jb49LN$3uf+wv@KD=1&dd!>YEA+e(5k}FPSAug4D zC<(DvLZqF`WFJXPE+;0E39XZqa?PydN(sporjn~9Bv+V9!V;1zOeI%KNUkuI z)X5xXNS^gF2f5x<(f}f>Qz}Fr=;fMI*>TJ6ujJbM%o4Roo>ZcZ5~4D+NuH#L5~b}` zPNuZWB_Nqh`LgWdWnq;L2~l-BmG}pz%C#tE6JBQG&lQ@*yagw_U? zR3>`!BG{qSG^kWf13Z?_tr*`f5yAtk*|3ybK?GjGRCY-*5DB=>RBqD9Q<- zhrzLO2lzK6Qtp7H;#}lh(5pz+(@zuPJV;j1i#;mG0WUNS);RGkedT^7bea(OPjbE= zI15dKx|QNOymC^beDyTS)8U0G#q1s4!v$s(Bp2wflqDlj5aov~h_Yk^XrV0D4pDR` zNU3%!lKo+(RJ#@QWm3S0lw%+u{FCMVE_DQ8MKZ4&3S?dtr&FPo8w!BKEY#b!0>I1o z_5v9D;`MkdPUjsWP18$g4GNIBGIfe5yy$dRK>oQ@UpJmkLgb$dHG}fxjhchn2z%zUtFE{vmsYP0qXuD6OzZB}Gzv%*rF zPinGQ51M&Bsey*eFoUPC*~oG|t!1?R3@+oLw)dRD+Q!lVh^0AA6ar5&G+Lrm-Zw>C zo)K+16iM3La*JpisPd;zWHoHCRQ@%Y3I_sgB-W~RPEp_zJcrUSk(fKDnE5!Tn7KQr zSh9RsmD+e^q7Y>;)mC0sp+Ch+ei;`;u)Qfi#g!C}gFh>S@v`z$6}WlZnwo_2b2U#D zXb8VhK@?QiUrbq1T>zrGCWu1FRGR&FWLsCxI+JbfJ2+*Q`8}vWezH=&i|w0|oup)I z&15Gn$rB@38%}76lJw6`AO&;&ixc#RrTD>_tZ^&_fLMz0 zq7b-9z9UGPe>cS$p+bw1d`DWeHWIZaOEgLax5aj|9vAH$%9)ecLe_&*l*Y0inkr@1 zPK9Wh#5LFMgqo9?ec1`4P>w?~nOPKzB;zrRle^U>lDoZm5~b57qPr#E^(fg1i={kj ziGsqUN5d%IMW*}^lxK+wO-m}w?j#Pm6m_x?-dI#9iUPwiP`cD171Bi&9G}dEaiJWt zV7YN`9GHz&V2xcCQts4zk+{GMRg{_$D4IQ}o3**35E)Bw-6T+5xRYmfk7EV z!SJ;up?s9b_vg(+_~SfqJxW;0{b?R_8S|Tka>C>@zatXMg3*1Q%#;362AMYfwJUtK4tm(6S*3TJ|VW2<4H5?j63+MQ3p#d-Je6GYkDOO#9!v!sw;jF#)N-f!6lDRf!^pCFcv&7d zWm#>LWeu*zKtNe)&D^9cm*hdN>TT;lluONrD9Q^qQP6k*iN-Tp6hf}D#^w(%+LcpS zP4jJ{%{Q}?Eoy-Yqo|c8C`G*}4<>A6Oc$FEQPho7vbBby0un_n5``dz6!pB}MeR35 zJQo zK6dXykh4BFKigT)z_F^jxkP3-GatOQ^y{-|yvlGn4rU>Mmd?*W%|f}rgk;*;`8ZB9 z)B0@B7nv~X5ahSit}n;e&&Y2)M~IBS$Ax6sZLBrL6mmQxwY7-);vvG9rU_Anq@jqO5NtO6@&xHyDdfMO3E(v2lykGD z833Zeo-GPN7-?^Nhu8aE=ds@R+VuWBN)4e3=WWlURcq>oG&BKQ+R1@}`#|8GFQ zM$po?rbDB6Y}?YmPZuIHs$FJx1Q3*4)yDIL_`Dhq4VmuyYQ{KBbIJG-A(kWiy~Fk+ zpPeVf3Q2>rH6g@GINGc9z!M0tJtvfXFn`K`c*F>Uros78S=BEL?^^yigFWyIxH%3% zR^@)vwb)_lUJjCb=?Two63QV3I6;6esn;x*?1VBfHnM-r$Zljg-sdId^F2>b_+FR@ z!dX1*r>&Peb>4)xCiti~+Iu5-gAo6>caKqVjL_aJ8LE zxg%I6L;#*EtJdVG`!00o%Bo?ME<}wz2?XVyO6VRSRARpX5$G9YATJ}=7lsN2v$ziS znq8xGYduaF`$&E7b8;_+rUQwr{yC@nIq1ahN3DeJM~1>hP9bVxt=1mY&01`h5S4e* zv&!#KBs3JYvSfrwaEYDOrNrY)e;*Wp%SUE2y? z13}Gcc4(+`S=k|6E<~{442eW18$5*|9{Ai@Fyz4J zPvu;@cjD?J1nCU!!WBpevWD-*yjTG&S#SQvD(gSaFs*M&c}EZcs9%ej*JQ_Wl?2X?Z&IGd1pT|{{~ z&gZ-?vMdH)Y~kudPLgZ2WntKlQb>G2O4KZr#TM)m4K`4)dxvaLhP5~gdR)6eCS0d! z=W_wC)W+a~IIfOr8thhS8l3)+67Qik;@eG$_h2o_;AyNmiGQX5 z)}k(hYEt5FC7rBhPZ=5JfION)ZT4k7SoIr*&6rFF^LDo0Hl0Gt&<~}^nk)5B3OeUlrOxraLBqn% zTzg+gjS$*`T#8<3*{He5((o>}G(2Acx-ML(t;U85Zp@F(Q5xf1(?!9)m~8M5$|Vb> z^g_<-L0q4ZjrqH{79r!`$8`u9{{b#S$oPNd!@jVLKZ1)2GX5woAb`7i3}X`eLreT| zOZ-Qc_!Ify@+JL`^T9Wu{2(H_jVpdRmmlxLWa5f5E)?XeU&F-)nfMPDrvGD!{}Gvb zuMx`Yi1l78l%tZ<1)#Lis5Bv`@n3>HwxPm`c|ugGH0KMBp~%wHCt8hS*-)vK4FykL zXi1!77dmjWaT3A@uFo2jwt@>PgsA)h;iXkSIR~wJKOy!1o&(byf`V^RWa;1LSmn(1 z2p3s9RIxA*9djlq@q)@z2`5|Q6oQt{nk$65*{v^_D@1S!VS{Tp66VOY9Eo#e2S>U& zawkW=Ly@IFoNJXkx6yq+{}|}RbQSAR=6ON{4-gB9S_r7$F#9(+@-9d6u@ey@SVR&1 z6L=)cI+1V?z63j>LxtdcAu8j%D6oSgH*@3;jyyn-;A0$lmLf|JWm(fgdsfQJs+l^& z&&-N5N1=7#z7#ksYcB;M?9A;mR^{kFo2{a*l#y~Pf2dHnfL9Dg;_f~KT*w=Vd-)J# zUtMUzvgwX?z`Cj&2##^cMZI0LsONJ07Tztot!jRKyh$iUc;FSn{N~0^p^PhBqlGRybs#D7G|1tL_;8j)U`uN&sorFz-F@i&CYmGR?fg}t9N}>b^kO6YY zL;``sNzMs563z){NCGOcM6Fs>P-_P(L9}XHwA9*O=h9nm3vF+&_txUjL9}{nM=e@A z-ui#OwZC_-mAy}DdxziiUk_xx``zFA*0;Vj?6uckd!JUd`B?k2&3BCD0>{qP%D>LE zrNecg0CJ1#N70?-pOMq1r99RH%Q{}%LPGKazD!o&|JuKC_eG3))I#H`n; z`5#HOBh>9lKl0SP_xQ(dOK_^|Wl38}DLBVRrE-yb{EAx-m#8NouevRds`sbvS!dpy zr{3;T!Rb}UPu#0+x(sDHUhOTQ;FPIY?JqEoX6mI6W(Bz1*=Ci zuNJ19$=LP0>0;7CY`VDK*mQA&a*ES^Kc))g;GN1D4!%hhoaQh$D-+bjiPV|*TX(3$ z!rUQK#W~;O=3HguTy5lhnaaV*ZK08KjZ#WEFVZ=aI^&Ks9^^Z|dc*t%CC(>SsRP*N zw4g6c+=9Nm2rcLfo))yxtkmYlNsipZV$!GjN86xqMW&2w)|^8U|}p z3@Fq`7n&0iN68gP@=*qA7V_;HA?j{tri!-0n89ieR)3kf>IWFCzU#x7b3Dyd-C>Y@f(!qKxSbc!G>U$YDMnB0%b%y|+131c{{uh_|3;Rbt*+{#JyutcR zuzDHu)e8VB82DzsbuE0RF3i9QPE+{R#Lp&nF2<$tSDt4RzgBz8C^&=5uRYHye(iZy z@oV>4h0~ee4B&F8o|nC)o~>9dG?M&QVG8Qv_L;@wMiT!O;Lic)8JNCaIZIWPP%TxJ zh(aoeGaS6e+mqg0j0Z)p8T-#(SNqS@!=az4)n@8@Z>UXrs@K0q#wgp=QaBx|EIud- zN#QEm43{hXW{D8hv+)e(GJpmK4Y|@;f3-4{3_>~?6p5<47mv@)3IwXZ#$5H688{8` zJA71sgv!ufe{`|g==}54-&*R??9?1rVVa-Q{=Hmbv{s1f%Td5V26a0a)bBja%(gL? z8tc_yEhV(tYe|vSBLV;@|I)hz0`d`@NPt#bF1BTB9z=QEh7wR)Kl z)$|;OLNz^xQ9+II-&%%=RIi0eiZij=Xkk*K3gVC>o+o1OcYL)gFGS-H#|@EAu<9*; z@qVRBwRfcM_d11o`Vpo?)^chJ`jMwYHqc_JmsD?3c**WulD+iOi&ygUT$1V?2ra38 z!EEa1=nDy!ZV!KgvdWb)J>}>6k^ixqOed1nwvXC}%I)0jvMO9OTC{}4)l_#8H=%1<; z2r+G%apuJ1TwSF4Bv(lu*>y6f%d1moPeBodC^?5dvk)XR-7HUa4^`q1vT9EJAx@k} z1w8E}>)a`n!-w-!flntl2~o;UQR&Yh8OMv$PZH-&5#mgWNbaoTgk#a!<<@0W9JkY% z(!x1P>p6LDsf0K;&-I=VW%Ru8ymAX^N|pc5ch%%MYRI$;IGr;1LY;)Nq4P7CGLn>Y zzA8z@94B9OP)P@M)8~Qb=Q!r6GqbcBNAK)HONcp~Shbal%2^W`N3C#Sfe>?b zIwteUs(IS#K)w+3wGni9v2j=+#3kAgI=cWy(z&YBteMlAt+xD*!p=N;lqhB@p-T(c zc-jGTnKnY%rA%0;y+B%POhshkqC%@JUx>v<<^@75p$tA$C`7HwsX&OOipdjVnd(G& zLM)#nW=<2Ljv`WBuRwti4U`92K*mxYjV2+)3bs?V5^d>9-84vD6I)bRrw&894(RNv za?ZmkWwTK%)gD?*LWowwfS}cA2VW=UpqouVvA`Sutd*&o>F- ze09d*iccYcce5R?gclP92LFHuHyOUealRN4`B#bQeCP&m(f_`xtK`No(`XdHb2L{Byg+j$z$wjD z0;e@s0!+VCN+BSIih+kUX9NGU<|=^?X|4qLu;z+^zpXhN_+`yG&-aXg@?Xj_mS{^b zgxQv8$ufnUEz#mto}+0%cb%)d6>vRM0WoA4;{=!j87b2Aqf+BTlof1pb%y}qT1&LU z?klwvARK0ic7IWP6Jz?RT?(&oOoJ-$Ynm$t{-x$hfH&~+HVu}*5zUnVcWbT~xJPp~ z@D|PasulMBhV8IKTNjHX8%wl!HRY&7DEgzTs1>kC=Rdw~gmCkPOwKwyffY>S*b*T? zl-m;Rej|Ogb2TgYh_=WE-l4f-;GLQ)0WRT9%2Es+2;krYOa{bI3Gk_!D+d0c=4{|n zQ=!*Eu=o}}V~I9E;Z3T}yZ zzmdNBfh*`^mlUY?0GDa57`Q@nCBVBhR|&jZb0xsHYOWagHqF_cOWgdo_-Y54K_+MIEMo;Z%58~uzmdM$*~<#ur`%+#0Clq6t=@Dn7cx03^%>UrO;>ja5bm->yT5F{N~JHg zQaD39(gvnqQ>Q@l5a0_mR|0&U<|=_ds<{&2>or#le1qnElWf>l&N7y01BA*PwS`pb z=k3*8v=vZiw%ggnVFV;Y4pL+fxd#i`f}C}+m`~QaW`+P!O3Ux~T`awuV;bv#Z`WKg z@SU0~aWU^kq}~K9HCF;0)Lb!ep5|=eX`1u(CfK`&?XW}}AhNMUi&s;Q`hlYTuA){z zoxi_+kj%}L>IVl0SXa*a`7)pUiko2w5YDhfyWetuUt#x5*KV?b&(&NpaJlA6fN3#< z0^KnK&(&NR@Q*ZC3H-9=%7Bk*t`zuH&6NPZrnzF^H#BDh*QoEWEBky+0Oeo6=`7I( z2y-ma@;aY|rW{S+VirMaRhKw$ei)SQA=?4z^R*N#(dGahysGH7atVvqyUEt|F&xs= z8C=qHIJQLlJcgHPO6TN++gQO}uA&g&vo6(jjNzYLO1(s51{jW1WH>M?(h}`}@Tw(3 zfN(ISG&NHQaS2v)Ors0%HJU31-laJkc(>+?fp68EZ@5OD>siJUZGgze5-m6IS!m4B zJod84J|i>Wdo@=Ke4pk@fFICYDe&hsR|fof%~b+_NptgopXcIAG4z&u@B)*xG6npi z<|=`IsJSxWA8W1@_+`zN0KcNSV&FG4X9ItN_m?XJaqwv-$w1&cHCG9Im*&cV$23<8 ze7ELGfbY>n+iCoccis;X2t8Ew6I9Q4~wG+{;>_{ckiU z$qFse3W!uv?EZ>xjmc}1vqlCBQQtw4enE_REpu3}aSrP(`xdrq)U_`J_f>9FxYT5VMtFXOLWM2`tTlW03D$jQd)W_KuinKn7av|?=Ct(O|78oMSO{48aWsH z?DCEI^bK_i&v0ysuK(mr;C7351skE5rI&MDAkx1Qw+9Qv!VivtGwS!=5wEW&i2W?)M>Q{Vn_L$lTA>tIE+50elUfcgKi6$cz8eNO*ZHWhvseK<=6q__ z8S8Mcmr1%715Yh_UHL1@F0vLV(AymvY=joy*b}xT#tJmop{B?$8(x zT3xv|K$xlIyd{Qy7;ODrekx^y&)`@sWHFwrxnkf2n&UR819V-;GC3x}Wqb-EYCeqv zxz)L1pwUAHMa7>m1cWAB{xLb22z~5j_ilKw0IC#bo1pXJz6$5`$b2ji_O@-Ps zPJqRuEM$qc`xzo1OSE`Z<>)0)biJ#n74TNqfZd1GW|nb5{qT|yWgL^sfPc=MdZ3Ae zw+umG`gAr0CFl#BAjGjH+5nHZRQoR&LRF650!3%JsagS#y3{(qBHyn1&$2W`)m&sX zmjMPS(pwg!{HmK@27>}b(3ddB2#NeqOiTQ~F7Yld)uV3dhHT(-ikxyvF?7^D zsAQ5l0C0*Qw<qdUv&3;AAUbp>#ErA)9w&*vmk-of;3XZX~k8~5)o{_$ESri`}BXr+y@kF7NAt+z|eTH=qcWV(I$Piq`uT`HY4lovO>9%2$pWuDZ1x zPZk5hEzIkrRy@G~wI2HfLP zq1PC`t0@?EitF$&;JGf<0a)Qu;omU)qf3PVwfDmCp3UX5Lq}aWG zWHB3!cA!YFYfxAeDhH1gYOdjra7@EH@HLt%2EJZ%CBTnq zt`zu5&6NN@rMY6@gPOB}pV6FeLI`_zvP_N%^KE?U=e2`Poy)y7uTutp>O(f=R2+y+kZ(LPFHt>_QjoFz~9xcVeK_;mdfj98J zKe|Z!+=(3WIiV%m06*(e?N2d;svNByiq3XZwF1s$D%Un2B%9$TTLpNROSSm(_s!sz zv2=%Of*KjTs%KwVtl<)nwD=@&I9SRw?t-KEz3f#F)N0?U8lPO%8g zkYe|f;n^$?0~G0GqytWFWMQgv;NR;yKX?WX{$L0K|CcGKbvpq$+{!|hXaoGb*5ys* zCcDo~)(VJZQtUqDyRZv>u{6jr6$p5x=1PG>nzMmBH0NvU(0?AwSYj1mtxJUfX+t^% zOSCmHgz_A>Fi=#%q6LDV-3$K~ORsED6o_J9-}g%8$aDi|7U7s22HdT=V&Hzwl>q;k zIok15ii4jRg1|3pt{C{J=4{{_PIUaH6gp4`SbQ@lv_u;q^07pVS5=Oo`8HMni!HGl z5UGQLJ}rqOm}Lok)yECH*y2xfY>Boph7W7X73Y9ddt8xLr249+AdPT4pE6S66v9Ck zd4^-ERp56uR}74e!z2fM5$_x}|=^V+~7>c=*CaTs_b zpCiu$(@#OMTnTVobEUuonkxhTy5=f@|53<;X#Xt3hcxAsS%>X>3aOnwLk`&ap)p4ec$XokOX>vJ;rxP^x{v^# z%E_b{42>brFq4*OyPV;WrT~$nO8S0TI$b44u4TSl9Z_$_4x1zIGGE>>>{hm!+zx!F z=8A#$YOVx$pXN$|@6lWt@V%O=1b#qs^MPOG&q&g>$il&YGD(lkfq$mCO5mSst_=8> znkxnVwdP8I-_%?&@LQU*f$!#bXq188+Jr|BaLFvu2B_2R{sZ2Ua0AkP!%f!;Xrz1N z??|^QN4mHEj&#jP_Z*km66;aqS^Rx0ORNXn!fzZ&vHO9MyZGQKll&1MoWL(zTVnk@ zhRZbduMDTNS1qwV$?!%^xqjjR8&SuW=mLbzIp$t)^3UC7=f8dXBcD5u-?GY~ri4#v zkuJcC-Bhapm%CIKV7*JN0^I0QT{kj(SW_S6Dmm;{O&5w;!mlPanTrFy}iKY=8OT)b-B4?z{r&JAuj~92y`k-tV3xoafk4AfVeDMVm%-(&X!mQ zi0iZ^)&s&RmRRTQQkGcf?KYNJ=j|Ff*2!SHF=zmyG`d5fP%uQF@!F%$c+Jvhyw>S6 z@GFAXNT&;V?R1-?SFw8n`ViLe;0H|sv26-4N-xFkt4F*ASERShiu4v&kzRuo2_q0- z3^Jx`HS)z*y@>-xC8%dXIc(XV%a;AFEtt8HN?;73IUg^D*;&_eObLNM?p{SgpJezM zO`#M;ZeIuiqA*enjJPGMVmZp8hw&Djwu>jN=scDP!NDj9rG#G*{+54lW|fo<9T5H{ z4(`2{uXV%$-{NvJ$AB@|TB6I_ASfx!g};e|nFw%#E_nMk_Cb`bwM09N#SoF>34&iy z%codTzpJRlugLc(01XoZwk6hiFZY&MhjDT{kD-=WcZeab@;Qbf=r*P!fVfhs(S8Zf zQDB22KkCt+^SXfPHPkv|2#$z5{UYCwL>2>b7lW0r`Nspcwy7iz8yc#-BxftP5m1bC_Dih)C#vw;`$ z$3k*$nMIX9%dWRX8z3BOiI%gt+$f4AS^(=gM@zIdFhqtqo-uf{a~8wAIXc_^y_q_! zH(y7CH|yLl4*Vwv9N|yLnL=zn^-FC<`n`&!W;wkzp?bIIu)Q! z<-2=C>6f1AbaqR$gcxQubv47kQ9WKoPSL!&9M-EYw;TSzek|b6idZ592y4|6l{XJX zdb3a@#xsOBIEPc+9NNFc@JW|i1!;tvIn|pSOOf8?TP?huPd?2tU90D^&WkzDb^V5N zGu;u^0duX1ZPLkRjy>l|mh;U7KYlknc-(OWpSoc+{}IJQSg9Ko!479 z7-Usa?Ed5k4$wv{v2HHw)h<{E7j*L}mDKM9Ur$8UeTSVeNfd~yw}=nO^%#zBb?sVpCqwPARbKZv zY0vZHL)uQ<*n^E#h+{)?6|0dd-yp zcWbT`_;Ssa0jD)r3H({+=*0#1;66hT_#rxz$=yaCmgHEzYV?-7B&pI88!o=5m}-Ox>1#u=<@6KJu>m@?tpGIJWH&HZdAV|*86q) zy6Q073-4GW^cSY`xUE`xXK!?7hofH2V#EpYPnS_%-hTA~H;6Iu!o zg|I{mJoSK<@|NZRpZX7usf7Y>IMpse$k~m9%hJA=NNq7Vv%DUQ*8miqq$<>-)XJ{_>Y<^ z1(s!wd+C$3z~`RtaB9yx4$7D$LE!T=R|RAcjwT34@<+09Gh>V>^@Y2B|<3e zDmVKOV4F*I_#F`PJHR)tm2q~z;h3J#0{_VLD3~@`T!k0=yu$MFK zg?0qJ#dcV4!Dn;PKIs;*3;CkLbG#0`oTbrEq}ctnFw&tFC=ym+Bvqu>i0!QI2G@uX zpth?W`v2lOE(EAe_0R5ECn_u;7P(CsmJ$a;Pvy=*oYzk1!T@RgqhlqM=NK!Y8+KZv z3#EdYmgw?YnWJtXjn^V`)rfzyCEC+C(Nn$({XUr5Cm>lKcS>P z&~Vh7ft$m}IH4ul0Ke%{?JqNgsvK^HsaxDst$<&5sTRK?-_0Hr!HgX1^L`QEI<#M; z0}4@y+#h*hTOV_64$Wu(HE^6`WW9!^k%c8ffJSZyeD^l6@(xZAI^6@D!H{ z0m3>={(^HX5%wDzhM}h!-U2>Hn+15GON9a9Q%kpqrCQ2sd@e6AV0i`2&)U{kG| zt_!NRX$tTGrgDA<0{WkFOWX$d4W^t6AdbGJ1pznOTw#2%d zSS?iL*n5Vm`LV006%fW*BJ5Y>oAUk#D?n;XE4qTsFcdwdsUPrHgMY@cHL-3K(I~Cf z?Uq*SUgy{CyFvT|+kKX6Q3!AbQ&MC%k45;PSIfaGcWTO8QLnM~w_Nvz0JY`qP^2wy zhvhH2UI_teNBKsupLwzih!HpIF4xKyzzs}E`oIYev|TNJyINqEHo`Y#`;Dj^L!p~p zBf0>QuO&i&+Jr8c@R&{rsJ#{PT9s=S027c*71~>0&Tjw<(WpK1=*VaI;LENJzIx7) zj(@<>2qm1pmE!_&%s4wf{3Ei+yqfjqoM1y}z83OZ2;ab;mIzxs4Cyh%*Y*9W*7;NU zI^H2RWseD@KqI!NWX z_AOxrYhB&Ge%-~VkY|p2qf%}sOF5>=4e%Sx(Ki}Paqx3P5P145$9^T~Eedj2#R)CZ z2KetT)#X){ViVDq5T(gI zv99C=WFWF-@r1oQ=ehNrMl7#p(#hZhN31nRV!f3rP}?9eC@iGrPsTu zLP&L$ro4?7mfg)FG$a7utGQy}1DY!VZdAXvu5Nn|N*t_Ul75U1c%|lwfkT?Jf!CV~ zC$jh}ZlT%$q07Zi@7#+`uVs z;+R|wTzQcrO|O$!IGE2Q2?Bp-o*_6<4p+M7w*mgqrCMgP+9=1CXqh+HQFS?Mw?xY* zLu8oa={^cH#=6aKi9usCYc|h;JCx(xdd?P>nCW2BK9(`hP2>vLMpIPaFT1{21vB@s zDI^B`_n1fWUbBevnlr%qZ{gSyA;3>ErB(!>>lU{nd=0Rh<@>ZMKv-;vb|`|caNcB!Hxu)mH#hT~ zH(T?Zw`?80{u4r1(q(J+=4Od@WOARbTtHo1-?%=&*?rROt|7q5ZqrF`_7zVO>9u_; zE4i0rs&n86bOwOPz!G79d3^WmQ2vOa9QaYK98fD?=U48#wTJR&4CTOwv~obLe7#?} z@7@E-4;#vXwWg3?ldpHa%2o0+-8;Q?K#$j)-zXjb5i_&_Z{U5$g13~3Y8Jq6S$hM1 zEb0;dy`6vN7{9;c4M+JWRTi>2_#qPfER5h^AGwWV{H)Wb_!z(Qg5O#32AhBE0zdXq z%*Xi64*a;qh0IrQJd0y*zz;#J=AT2za(o-d-T?k?K7S9#pPfJ+-@M<%-`d~I@c_r( zfRB^^jK6yRbB^)hX>a&P<-L0GQG0wD8=u9-=cm2lf12LnFTdh5xn+Dji(_xVw_Vfx zS=H+}{sPC|P{^OioW}9V9G}Ip_mipKaN0j8k56*$<8=3Od>_Z&fNxrUi@yc=U5=mS z_&JX8tw?W3^Y{G*IlhA9eH?#|sEz`2UqYd`xE-f6&JLegJ>O z0pF>BefahSz8c^S|5y6(d99yuec-cT-hleRCm+1!z_&I~4(|svydTf-hQC+;U8qNn zk>5G|g1Gk+NE6jJ^1~;=s<_=P=hzz-a=mTj7$5k+=S;j`;_wFe4ebE!5q?9x<3knR za(Ki4l|FnA?Z@l~e1O9n@C}7ZeqDAy#}9G*D94X;><#~0=`Y|#lZ!aUEB@YqPb$2h zop^;8@3G>QdvEyf>ce}%8~L5mevSt?{u9UVaUA4{na%P0+0(-Qj&VFVf&ahEJnHEn zALEU0Z&=7L-IlUlXLEcB$MDztl@D(`-^J8rQrSB=W=OD-5;Mf;V2U%;_9VAlwqEMoJ^L4J&ihl+SS=?(wW^#8?n;gS4m&KEl*y!Lp*@hm@$ zWA9^oZ!tp4_ z-hk&ZS$-nneUjx3|EN43V|aQcz1c9$P*&7bs&~@gro6PEI}eti9}<) z<$WSwiv)$p#5cz{or9zh1DU>5ED|*oaZYqD+28Nx8BaP%`{Uh!*-SbSQ7Rp?;wt|< z6xAtd52;4dL>4aTBbT@~tKYGo?5v&9^QPyfS|`apTaU?tayeKP49Gi&UJF|CO}k1C zmWNg@3kKxJhjuIrTJpE{4w>zh$J;yl>=iFHUOg)?{Y1I>HG5iC795j>c1AutV!tLQ zm&&2?(S-{~MsMw0P$7%Tuan6=FZA^_te$EANp7oOF9)gy2W9IHQ&JXXWV*cJ1ts-P zm&{}a~I7zs?brZ-$Ldg%w7=6O2Q#w! z(7AGJ`RKyx29y}R!ldp+9N-Y<)*zGii&T{F6%GbrUpGrd7e{-yBH;f8E>i~M&{ zB_A0n56nFELHl}@u0DS(?x@MX&aRMsJM5v=oejIkR(C$J`#T~Kl(NF^rFfYByd(?D z<=zpyK01>U$+M5`eaWs~9S=-Dah*J+{@qqtkdfQULvmZydO0~$J6rA>d98JpoNHG- z|Abt%Oa5+Xk6j-O%Aa3R6}03hf*l6VD10nv$uY&hqGZN`Rq=j%qusEedQ*i=2g)q_ z`2ksI_uAEW*=Ni5f_C}zy6OijPM80_bx$x)?!Ky&+GW7**sqT4@?gHq+J}Oc+#!zy z^W|T!ELXVvBDu{z5}1Bs``%gd1(C6TEVp*lzhl>Q&Zw9f49bsP*%7qlyYjVQP~NE0 zyeq3Z_s#j@R{M-EHB`v{z$Mn`Dtq^U9NcgJXZsp?LWiAQ-MLQgu3yfTk|^PJtKid*5Mfym7S~S9-dLr^!$SAP4_&%pgI_o_g_Iq z{xoP$z5U)d?25I|&!~{mOyHbp+57Bw+V|Pdua;By1f{&`id{iVz8&mPxISpf-vz5? z1f|@mocFt6$Hrh#j$W}NXvx0@_gp@7rCj!S#cKI4VvqdlNQazf?~+>s=UDQ#D+5&) z^@w$y3#zGZXCA)1>l&HdFLwlv*@uF8^1rUM$)-^mILlIpRD(ClJ+CjQmIdW@^@8fo zeFx>Hz#{9~woUfoUj)vZcDH?apZvG&9l<=gYkMYW$+xV>6}HQRljQX>aJCh+_Q2UzV|L`SV4l2hgmk~G8C_Q*Hw$EP(uy5Rx&r7NlX-IgJL9xJ5L?v@N?_uInyU_kCv{5yp^ny-v^*2&F# zWMQwI9Js)eiPz-u$K>F0 zb! zoL435>gBACk5V*mzdU1CVD5=>>aMXDz9jFm?5Xm-?HO5|36!5APu=qq`5CKMKDB+< z*sYDrV-1bV)@0>rc5mR^6J>-<4@|e@&=EP=K4hOIOYCEf%P+mKai%Xa{CWgoLomIZ;ct-ysR2Ltksj7^UIQejn7 zFd)B^=_MEbQ{j%Gh6PWJEvODkd3}nS|DTUL)F3DClEYQEHOXxqjq><3WE+o+{Jl6NQ=l;7P(ZSj%3s+sHSDn4dEDEl+ASY31E`SuZe&!6OQ)spz( z85Mo++8bw7Y_h8lFQ|^&eHT{F-rdi z?eYdWSS6>}<#KD)4HfpZ1r0$duiZvo|49CRds;)_?32HoT_7j#+9Ja{qyguHiTzbxpG>HTYE!7e#iE(-$F zt2dBYHdOJ`(euQmPyC%kdyZ`UUF&0>ocl1UwTPrqr7b-aK3d1 zxmA`>VJ6#C?FVC>a?K7|eoU?klv(e{&SNjh7loaX_m5P`=S7FT^t$iL;i?-uW!bUO z(O|xOZJ4fAKgy>|^}Wi-7xSyGl`V(lPsD!viA$rMqcrGk3!H0J$2#R19rkQlU@4pT zkbmfJx%0PP7FEqx+>Wc{Q_2_6b>Q{yT(%-`Zn5k?c6dScOlm%rqcf_{lZAGv z%zaZ+Z?xp>;= zekUHZ<-1#Hoc&n-<4rPScgPc}=z4nMp*7b8E;wGs_SnDcSt~zSF5ebAWT9OpuOB(` z^!)0}K7M#@T>f-8Bk!72FMm4RA@@zP_t>{yc&SX4%hRf4Plr6it{SDuT+o)U4y(cZ zYx$3V^z9kd^1RII>aO_<|JANu6F6_04BMq|udu7XYY)j1yH{@AA&;wCEi=>)>pQFE z)E#o?NQeEl{EpZ!e|)8VByi4DIi*Tw4#_EYM=)PLxs3|;%{;1&$G1_F`DR}E9J_Ny z#Tq#rsI>xTp4h&3j{NggcDdadqrvFQS5ldumGwa>4_rx2<@?gk$XHtDaz7)wy<~{ru`J_S>uFTY3BMZo4(rH*@!lin-_Y+aI%^mwShIh2*x5tu6NK zTLWiKThY0C=8RcuI%mwBF>9?o&3@%JIV~gaq>=k|(LrEpcC~y{^vZ3SZE-o}P;ip` zt)Lm;XGZK&d2#*N-4&{pE(%;`y?xKT8mGz9G}ro+eTqHn@JFgU)xR}F_iq&>zdhlL?W|gnKga%}Cu`yQ;0Wr8V4A+ZtY4ThrRsT+8>z zVY=z;jfNx9Xe=7;=@#kuwpchLx@!~Bfn+?9sfk8Yv2+^Z@pLjg5b4t`bWbWi zkV&RQZ#)ro?!ChUkxZWmhd0F&Q4vdIH^)+uOe`FY4aR$7UUL>V*48w;265@#lD2k+ zsHBYzB3#$9F%=o;!`(BLI2z7SDZBeOk;M*7_H0UvRBSMo$fVWXbZ<1gIT?-jZVl5p z5e}!hc(~-eg zIF^VCRb(aC7mGxNT7z)>p!~8qnMh`miFi-AM_EBP{N34JrE)-o!yDsNmY)7tBo*$9 z^k+oa5maRuqj1$6xV8JQI)fQ;XmV%%)>0YHpNE zCKXAfdt<5aKq@}ym@%#aH?=OWZ5HXR>E3jBV=NPn^km|LsrC1GL}c z3Dr6JRRySp7v(tZc`ikAC=pAEzDU~8n5LBuS}LJdt9UeD*q%&R7#=O}5j~Oqe)6jb z_w+^L34w;USuzDV7}wr(;`0R7oWVl#85-FiJuvQn9q^ZK?-_sV2gtgO-uPj&~!OOp07b zT`!VJ#s#_C(Una_)PTU{NDstfQ5Q&X}>dX=Q73Q=@7snScu$N*IW?G(T>A~xg=(?pCR7*7rn!_icHFh(_%pnjA_cJ7F`xM@YGv9`5#33qq4TD6e2mPK{p#f?pi8^SA^RyMUZ zt*l!tJfkrbIv0?YO|9XjO>HZeu%oGZLaAgX*^}&tq?>ikk|p6awarc8P|f0o+E!Im zuI*%RZ#tF{saRwn+?%8^imo~$9p9LUMN517B2*$Chr=0>jVChmsfE@xg_qVf){1C6 z-4jVg!`W1KTJ+LICQQY5My1|(e@yk%bS9jQM`4#)%XKSTYnzwWEUq<1ht1hcY&e|e zv6h-U`^vZ|WfP`}-J6?ZTe7iqrgiH8jehBYc*3m#$6<9%WJM|(8x$)d!=dC*EG6Qq zQ4h68`m-_7lS%ap>Wt(2*0@$vx3ayav2IB?RMT9u0;gOzw=8dJZdEnY)F7hC&5?LQ zm7Z0&a>^jl6Y1%TjUP9S5>GguM#HoQtR=bG4XQFEY1r|W!0C8SXNdGXceNr?G)5F(qQdnN$Qu@p!j68C4A~#bZ2O%XO8m zToP)kTiF_}Td6Ei!zB$vUfS5S+VHAV^*nH;Y3?(SP4r}FOpm4p z>B^?Y`Sbwgor+NtqPgSv&g#ua&81XFYHka)ibOK%^g6!o_&bj~xUM_f+Z#)XbS#~YCllc~=1RWVfx5oZ&?+*6G+4MJTT7_6 zc1gIUE#%HDqVbeSWH*OXu}nM_i;AH}r{RWqXy?((tuCQ*Gv;V)x7%Kt+FEHC*r*31 zCsV4W?j`P=R}CXxH!rSPxwy8mv33cK67fW)*BrK+Yik-?>sHV(7LBLUnN%!7^E!G; zWn4<)saTJ5#iq-pJBE=j(wS7xz*aR4%BF==6SG7nlG+$kb73_Tq+wUJbu~ZgUYXsD znLN#koW2=O#D+v98Z~G0DluR4-8$uYL3g%)lX-XHc+V+f%(?7`vvf_1&VoyD2?{T% zZLeEgtF9+p{k{ELS-aVaqcZptL}WloHNz6G_l*gbBc5(lFEd8Q#74ca{!(Khr>O6 z&YaGj*y)KIUDj#n$_@p8Y`Rl7Kd(+crqPNL}SD1Rz~#n#d7cIj!>!1}ojhPyJCD*PZQk`lXuId$V_L-o|x# zuTFLM5Bq#)TUu)x8{L~^XO5H+n>NQ2Np}iNRpE3R8XMFMoUUT-d|&l-G!h|YAPSxz+DiRL)bMRXxmO*@>X1|D}chQnzZ(36R1I-C)XY4i|2!IxgM zL-L3g?o}6xc(^~gG29;;jP;8|EEWxC6RB7)w_5KAVBU07D~m5y_qezn>gLHHkD~CT z)5>U&rmMiXrbn~smbOr+shJv|>IgIeOD5EvKR;R*J^h>LverxEyHFDpHD7eEK4Ift zHyw*@3TKk4)_Lp@{WMXa3C%zxMYrB!Q##x26q#5xuM6|-MLH8s^v4sMM6$P64>In+ zyP|f52=lP)3vQ+96~RJ{WCSag>*Zrlqy z)6AjPWG2$j*J2t{lq&Ds34PRdL- zNI5foRi5-ftcQ!=vx%AsjXy}ii(jc2-H$rUU*i^r_`;XS#1mQPJ~~|266qi4i-_)) zt%;sKtd8-86Dx6UYiHlNlcL&I^4!w8Wo^wht#wT+MKqSqq>@|tYDBk3hB35%MeFKj zZ>r(eKRXzzQv)4MqG_lOhkNNBiRM9cMWjoAaxj*PMb))K@bsRpq%-HLyS0|?{z!t> z$eiXxRiNrj&Agp*)9l=HudUYSymLg=Ovg1Zcb;o5HE!A*xjdO7)oL;;+*y#io_nto zdLfBttEybeM6%VH|E5JW)}7r*>tSK%3NFG*Q`k{D;XRYn5_m4+*u@tp8l_^J=?>Ui z%%1LWZ$b>lQZ$?8B`9?VL)Rf%Ov(;8qrK|Y1O3@_IGKovaJny9dn$U*_ z;&k(v>gM@cIGKuX6n)8bhUzCCNvLZrscVk)ref(n_sT{WT~bJwyR9Ob&4jZFb;rjy zpzf**HO|c$q1vgs^$ZV1;+a@V4JA>!M5KC*JEGM!%`59xE_1p=Qm=o8>stENO}Z}F zN;PW}J;}|RBZ;U`o>p^bPe)u<+iI@VIc+Q56H%)|YNgt}%dK0vw25v*5_B7)riaGz znmKZDv&3i^i@G;agjKr?_r`}~QO=HXOU3&2@_1Uf3qax8n&!q%5gyD$y8C08ljfWb zCI`Z_PODZ1(`x-XJV2!HrpmOHZ-Tk)hBwkktR5c3)V0DhDuv@|nl;fS-5GFclxEw#S`jo%X<^;JZKo#MVxjY8;;|-58UmXNK>;;(m2SEinu9Z;;&|?OPrMowV?0b z(TRgR>Q8Do>0%cO-~uHcn-_4EkKGiu4ZN6i}5qoT}Un1;Zxb7SEd zxYanXI~kYOsP6IIE}aQS=&_bqws`R+rDbj1*+eE=(HKueQc0(q&6t^00%=ywMWaJt>Tbb+j!kY#_wvU^?G z8p@--=s+N_(-0=THh->Un=8AF?FmE-QDws%mK}0sVSAw4l%+RpHn42mFS{1yr2MaE zxlzB|MpNf}A;cprdj`deii&TdI@l?f6_s8WtSKrR&0AJf8Mv>YsBCdjX$_pdq)^r8 zTbyuNpvZ14tSPcZR0vVb`Q7V<*46fMFaAjKtZk=URD_R zVnNaT#lEVqrMKGYjk{saXFA!_c%2*#))fux3dB|r9NS_-z7z*TMCLw7ON(^xKOAz z5M^2QYqKk^3y$WkDmq$vQ(&hIe6N6-xp2z6ljZJm>gM1XfnCZQo_*?_^@nDcL6yg)~XGu|UN)`~BfocXx`UK%Wl`yMfl(P)QBY*pvT~|=A;dK-cLIH~ zLAANMiN}-!mb`=--4b8hQeR%6HvAmxyN%-aZNq`KoZ4y&7Ww#FC(7Z3oa z#8H-8#J;$R`twew?v~J4QD1blRGt+m7+-zVbXD6e<YC74bWjF9VMEJnDhl(e9lY()iyPI~gII_;^{$$U%<47u!*ceMA1&)+OoPs89jMJHvD5u-U zSRy0J6Uj`heA&vj8R-n|4RI31`gk?`e<@JiQq^-aa09hf3f|YbXlyhCU2}LC85fpc zL0W})mVbtV-gYMjAIssdHVFF3IO2J@b;A_8fD0kKUu*H^Me0eXH+>1GM?MJ1AA#OB zr2P49KQ|!#%^t;0shl(v$WOV_Kd``2^&5}D$RB!Wd?d`5UN`LJ3fSgx0KbLrhNcPp zU-AIwzz!h)Q+GoC@Jqxfiy2N`;T%0j1C9}3Hv;__pr?o`lpJ&<4_xaUQ~q?>^5(yJ zg8YB;aVOnTk8*GRG}d@I3g7#*lm2bb2mHLUS4?34p<1U#=5qqFpQ^%Z|E>x0|N1FT zLfi)+c=P|*1o?|)PX0(w`4{ELpS}&|)k)#Eoc}`4=Y71g4^NPOrkW%PvHW;g?gVf8 z2Pd$8KP7OsY#I#zePqI zy!O+v*LDha%#l36pTRm+2+^A(f9lh@@_#htq~8UDC&+)IK&gY`x9{aTFMXwOg3M_N z+2uM1BM<{lXE@NB{EY==hfA5f!A20g-xq?%$4O#j}SE&1aA;c>zf0X5EOrr2M%kO1*b2yus!*R23(Kg;iBd9@Be{#5e1#Xqg#9x?4L%!9bBNnN{4kICw^%>gTOIS2ERXiTmi34Db3JOUK?t#h<#%zrh5e(D=U*r1%cb}P z%MW<^*XLkA|HidiPY^;p&GI3ZNB@16YF$AH@v4#!y8YiB2UrjK9oqSEd5(Wd z`91)CI6cqtCw_lVtvk>>ih2Axnp$U|Wdhc-f%QOtE6d~8p46Ix5Mm?CFJyW6Z7cH! zx&4Fx80*2WEvWSbA;i5bU&-<`kD>5YmdAJe)w+TZ;@?<)7t3R9{T1lv&wZ;k1|h_| zEMLs>@V}k!_y=D`$GCSc^Z4quT3Zl8ROCB;PH}m;*{LA$?Nm>Yf;065iAwYY>0T|z z2kG{<@j<$y^7tU#UNSyNxAfzKbZcpRkZ$kMp(fvXrr{0CCg3|J;3E_8sR{UNC*VIl z0Y5eYf8PZBgA?!%Prx6VfdAeE{3{diKbwGmcLM&;6Yzmax$9-}1pEmT@E@3fpFRO! zHUVET0Y7&F{?ZBfl@st?6Y%K?_}eDn@0@^tY6AX86YxKsfd9n=ylv;MuhS>sXHUS_ zPQb63fR9YTCnw+sC*ZHNg*YKFNi5_Wqqi_1jDK^Dw|aQDOL;dyj(ZEod^}%1_u@Gh zk(QjBT0N4p=96U3eJEbZN|Ma^vV})-<}8vN_x_{aJWxJ&{``yS{P?{d<5kR`Ka)iK z8+tur^EjvRZ>)KQ<|=25+gvqX;^In`n`e)V^X%LE(i-mt7{94@{2oK|<eb!Gqp{DUYX9k@8B~k>Y)&NWCP3mlVfaGJlS0o8vctP!sg*a~YRv zUgeyN#&3*uGFPo7*9Ln}>Wk)47S3KKyy!SyWaezzN^Lk`-pq?=JGIGFUOeB+%%4Mw zy_-%v$!62`>~V@_`IwnAC)n6FF6G>Ll{0g`nC6k1rONAn)oi@j{EO!2c+1%7OF8z( zjZZx9qPbpX_C@}e&U0;7GF<o-=xQcaVGXy4X_{bLY=gC8T8jyW{2J~=ynJ=?&PrQAu?mgz(y*29UIcDHI7FC^iOW;3~#Q^QKX z;bdLA&f6*B?ybCSKAlO?wn~1}OQ|zjP50I?HfP17-YwnX$N>2R`w`X45xftJ3#~pE z;OxwDm~@0*@YiqmryY-l!|Ee~;coRTm2BxtiLBbKYfL@d&k$hknqpdd{L6bzpyxUi zh^KXH@YfjpMuWeXdAg0qS}+A#H>E)QrL+#lH-=9!Px@(%2Kv|XdaO6!tp;!A`&s5m zKQAGu@Ogu$e6jxfxS_|)w~#W-QQi`RH}k!edE|@dii-`N^2IuGi=oHN_aTEf{rt4S zoB19!^i$q=&iuN;Q@&V_{+*%6%=bgQ&P=Ci?HK;AF?iFSl?HFxlVBcxpgbs~4W8`5 zdiSWI$F%2ZgE#a2j=`Jx{?gD-dDC-53fOl<9OaAkUn~?+dCh#Qc>#%dGv9iHH}j1$ zkMcgs`Nj>N^2Pf5Rzr`O?}G+!=KHw8oB857F7kbY^L^cuFV^vYXXr8W9p?3XD(~e+ zz29i?rahlFc+;Lon1?+j+^-%pc=7|D6MWauW7_ka!EZ9``KzIa^1yRs$VbecVb*`t;7$F%HuRYK z|6=f_{z<%~NvBCa?eCya#5~oPIo?(ne9G{H+8D>zG}428%yl09H#zFXEry!<4BoW=B||@z0nd?MHhA(6o_oD*=rR5CCxbW3i*&w`G!2@eSrI2wZWVD zt~B(R`GyU7(u3#IQG+-2WGB!wV(_N_A2)b&Jp6^h)3axcVD+elz>; ztrPHHFnF_mj~cwGNAkWI(r@~C8uPH{r)*D&!IOXR-1IC%kLl+N4Bl*4ml(X6Z@r

DUneVV6PkK({c5;=$n|f}ZK+nSlZ`Rkh4c_$g>jrO@_uUD2JW?V5 zo9!o$A8dN-$BQwG1);J;+>w6_EDC-6o9v`>uNr+9db+vUuYyUcNWzDK?srqXtiY#y*2z8+uGXTm0bE+kT3eNBgPd z_A|}kP5oyZdQANb40&n~4eW=725;(FF@c^ggE!lM+|Wb%QNKwKkNUme&|}u`(+0oY z@c$nT-pqFrKiDU?(726xW2uMd%c8h|d9=eJ_QO&`-n6ILBaeCG9z))oKRjXZw3iC) z>J@`G+fRTu8o(afFGOLI!Bf3pU&sl}Q+dt$J;#tIT?e_HTww5~o{J{Xv&@huJt5Z9 zXz-?UNV+h_3Rebze$Z{~Y0Zy167FS4I6G&ot6)63HVx$-Gzp*A3qE!%NJ2{d_xb{2|`7|1E=zhDtBT$7!qA0h^Cu4{eM;LY*#WrH{U_A7%o{gB5$=SX(af6O-u44&-7 z{>3wx_uBa@gE#G*#6Jf~^5**6V+L=^f8XFu`7iRuThe2e_X&eH%XNf#Z@GSG@Ki4B zzdUB>G5w$6eVCN5d0+gP!JGGYFEfvNFQbI?PO`zP_D=%zUW1{>%s0UMIVrhWuH%`9 zeynSqZ1AT3GYvha{&@y(>R)2$q46K-|jL zk4nkSc00^G%Da&DUuE!AUhH$d!O&xt_jZFf^?%Ob&H3K14Bl*Se=vB{Z|CqvY1rAp zc3xocWGD95{(q&Nd7Mw>`~S~-Moe}|Y58O@70Qe)Nr}leWZ#;K#x~4k%uF*Q<3k}O zLrQ7UqO?;gqE9|bDJ@!Qr$s6i`BX$jl<(tupVw<#UgrGs`<=(5yx-4xy|4SauX{Q7 zxzBy>(*fLM-p_H4`+1?eoE=M}3HtfR9ryN^Iy>I}tB(6Pq}oI7m`|SR%J*EyeR=hD z{6?qG1ZNzI5Qlr6-skIS;XG>K^`rK4f4$SdabA1bakb;VyzY10`@h<8-+upb+|SRX z<29$u7xy#h=D5Y57k&f4wR}f7?(5Gr&W^RaI}x|*9QXEbcXoXFu6BC!bvWwD%Z__H z??l+y@3{A~CLTmH+xFiw*strj+2{OnBXEtwSx#?uw!%&e$Gx9jBJ2!x+?Q8L1fSx# zum86>?(5q+aP!g1Wd+J*z2n}`kDVP~F5f!t)BVYD@Bd!Mef$&g95Un2=Z=yb_x>CY zuJKQC+^3uBxbF{VIX=s^_Xiy>b9|fQvmM{>xIHt<^GIX7R@eNz$?>t^jQ{(H|4gU1 zet`2Ws~z|ADz7>2=M}d(?&lQ`IqvUkoQ>D%nj2o9?zo?aob0$S$64Ua*QdzWT*s{( z`JBT;&W^8FOC0y*^@iiVpZr8PzFb}bXS$pZe9P&5zP35;)BVfwo1LG>;lWg;%b-gVr^;Zw(bz1kx7DfY%qBd)|H(aEpWe zmw|$><+$1Bb2XEl9pA4ma@_YnKRNEx{m0q2baRkyA~rlQX6f?zoD;##f1j_@9QW-d z!*Mgs`gyCfZ~Z^-yFKaje*Ey9<33*>i5=c=+T^(T&*z19I6L0|U5@+s1bCjv;$VLA zzSuF2d;2GVYkb-}ZgIdev_scAJ7&KH>eUR#%|4%Bn(yp*|KD=l)(SDsJDnY~-v#z} zIlb>UF2if9&3|8xrQnQ1Z`ipbf7XM0&K zoa+rXgPWhe-R%+1IQ%V~$MJamw%PIhf;UBJ1;N$IT9wGX*b!+x(%mJLYSP)7vwOtZzFU zH~R=futzxcweVVaD;Lu;4=00Lob5k851%G_9_>ZH7KS=IZv79Rd+g`z`2J^v(_4O7 zzC})Nrs@Apj{7*zcihMGKC#dF*oU3o_uEf8y~Tm&(`Oy``Fh#e@%ggn4(+fw`|FEy z-TEJYKDyW4&+_HFB7&do?)&)p&oPdBe^_pId%}FGiisrFJMu=j&w<?JFN1p|=>?p?aG`tBCV_qIy18tlX~M zDzd|MyQ*Ihq36CEs$U$T=RTgQ=ek|x+!uhnE$Ywu2>Tl&_@)THHG*?JFVkiHqKaWoa=eXX}^=$A?JD))!Y6h z75SeT!MRVW+Tr~Na{5^)cF0SG)6Z!UocpfQKJ{E*seD0%okbCx+m)#uu0vG5D#8x7 z9Xm?DK0?1Sf^Ul8T+e8Kw*HxM*dC$hKC-Ic7om?Et)M=0J(}9#KD5gD+=udHbGici zyx*dFu47W(F2WA)GpIfzLeF(8s^@(G<-^5JGWvlW;VhTJ2+nzEwa@*LlyhB-@_7;V z7ew$y5qxn3Ulzf+udn*U^+d|oiye+DHVS9lHbrngzoYiIN9cD&a9$6o9S)3@Ct<*> zymkbyAHlhwmD=HbA?3VYSI&Knm3N9rHzR^`-zeIthx(iuq33-G>JwQ1ML!6SPeO}Af! zJ~M(3kKmlwRQrVy`qBtKErQRA;Jp53y6aJY7DVV5MexNDd|3ov6~TFZPk&f{)<@`V zpQ?)5?Nx8Nr7~@SF%<7{U1*rN&`egnm{8pBKRwMDRrsd~pO{7Qwl% zyZXO2LcczOZ;arZA~^Sn*L1f>=yyf%eG!~ngQ|Vo&#a<)SUZB(kKo*=UhOxD(6MuMtlBAz(3eK=X%T!@1fLhd7YOILg8PN4 zpNmD$crJ_Jt0MT?2yXkCRTR&S5uEP@P(Qau=(k7kT@id=1dmTHyX#ZNA;~74E8w*w zc>M^TES%#p+vltz`z?f1&wa5hJ|?*i<2kW};`cO$ z5xV=RcCTZkt*65C0C-HToO6E)JpNM7AH_O*{H)v`9$1E4&iUR4EbEKgv3CXV;C%u5 zGZy*BZI*Ig8R^=+7(1vx4L2^j3O@rkjO~3lc2NHY0y9wfm3Z!jzES@t>Y=S^w1fNt za9fk?c@_f0*9A~-`!L&KYc=g4?~UhvZxh}P`wnnFV(N$ExdqPilP|=5qZMK&ANPme zSB`?+Ec|c8bDMC^_i-P1ru!k{c~JB};5q6lR$*O!xqr>c!k3~RpDBDh;=|`YnQjKk z>m1R)i0AGu5dJylD>@6G3_p7c=W}HPgx`w(2J$sj^t5xO z@UwA$carc^;QtN6`F+0d%kZ7^xAf;>#F6V>$iU7YvJ#pU*-H8?X*XI8z_3tQ)db9f_67vctgZtvhePxhh@SqL40iAbYFk!pI z{2|oy6~bRYeRxHOWAZzQ|}+FMKE z-{8E%c?sHIhj{iCeToT!Ah<&KLf9WKybZ=DQ-pUzeC7zh8|7~Mn%hBteun)ggg3+a zeYNmI$k*G#A4dE?7oLZ9_ml8f(eC~d{uX#uv_JaE_j8;q{4?mA3Eu+VUify@!=A!F zM%*qJ{wv0rBZYI`GhcXpq&r3U&)~C#e~$Xk>ptdpDeCL*q922PI1zqPf1Q1>cU=je zj(UNqf_1`2A^vX*?~i`+GvODYT)q;{_lJBZ+}7~f zu|s%U)DzZI)`t>|SAQ41y@$n)1Hx}Zxx^4x+Sv*Js|Y_4{?`zGD(Y)p;h!QtCkuZC z{+}uQT$EQ^;j2-;U4?TW)j`6KLwgw|{4|trk#OGcnJ)ZI^#9zKl<|2T@p(Y>Ps9JG zg}1@?w}iih@1F{19KILMIP4YPALX8ic+*c5OK_aH4)+=9DEh0= z5A+d!EaEmycw>}fo^Zx(vT(-jPT_x{{XQ(5>ocAeJ_r81C;WZX|IdXp{yT&-{`-V) zgFk*hLmSWBhp^80F~qhqQWrHT>@` zc6xyi5xxQKC|mdgyxtMM+Z?cWo(q2m<6++4pr7OmMb9`a75+TFzaX4`ZWc~|b_g#) z{QnT1fd1s5@Fi#u38>ffe>ZeV!hb~lsVSWOQ9a=tS2Ys;GVC-FJ_r3(s&L*%X(JqF zg7(6{LOtsw{4%tcZo+RweeNZEKI+K;;Xj}~W(w#2&Q}O;j&a3k;X{z_mBJSz-9q7? zqdzGXz7+lN4Z@E_e&-0Ui}rY{@K&h*cL{$R^<<&&9yotJEc`R%cZu)=7?&&){uJ8R z8sR%pAJz%SW8J}9!q0*~8-!nvID9JnIdnkZ3BMlY`;}M@d!p}h*_6z?X+9_|uG7e2qpKHKha>n^o z;XLo0C7k8mUic39-$OX-ZKiOZ2Xlm%z|ZT2|A2V1Kc@fpp1s;m_B?tD~O$ zD*PUt_YVnQh&b0me?Wg~pj;XVKMv!}G~qMRk98E@2<6pR_|<5iS;D!0ae?qR5znc@ zXChydkq-S|XCLf3QuqXv`yIk(pd23-{w(y%g%3fTUlu+L{mgs9**|$r=3*cyYan)@GO+C-)GgXFF3C1EBYMNtHHwiqyAhj zd=26`QuqRlGq}!z`Q`a{is*U%oh_W_@wk8BqHE9&_k;p@;p91^|_ z`8o#ipr5DU0<4Ckp3#7tRpg3;jY%;k(hl_Yi&%--iijzQzfE6zy@6 z@cxMNjlx%>9^N5*2>O9Xg(pJ)p6~?3m-_=Vp4TGX-$c*nLJtY&{q#C02ih5e^ZUPq zH$l0y5WW?52MMo!?uPL{T2SMHkNu5k_%MtQMhoY4&|KkT zk?!5XISzSKIPh%~vyt#QRuHy-n{e9cC442~aH;SQ z(7%ln&i-wp@LMs?nJb)exKlXoFBjecHm+y8HeA6x5N0q3ZCO({bc<=PB`29 zX~J*AdFKM*U&7A};lps==kpUxm+zGvF8cRjCtvt5oM)#A=W~X0g)hf>^M2tM<9xJ4 zIL9$7g;zm2a=&!-|83FN$9eNh;Xh&AnCSL1xB5H-e%2HIAjV@2g;ztnZZ4etZ&%@b z?zF$~B-kG&ya3nHxx&xJ`Q>Wif1*FR4&1Vdkj0PL!qKdQJH^hg7>7S3dbkrjA-pl- z`K+^ZMVMs#c)@XtGyCB;95;Oj*m=is(|?ONf9kmDi=qF*antWcJb!T9^iM*+({aqHPzb5QxgPaG;cKAp?6{@N{;ChSxo6LG z@O`+KJ8mYhJ$o?9+2P(zb`&^nesVvEIgVSpe6I2?vCrpv9uWRD;`X?+WA;-qPrl4? zv(J9}1;^uh#B;6Vrsw>|JC2)vHuRqh=XzGYUx4K`68-0&qW=MQ4m$4RUj+~9F#dcm z@Co3wevBd51=;d4^WgkObnZ{_S*z6RsE{(Q%M+`2h#`eV?3FLvC=ZHVKh?*o0d zaPG@?rQ>FYal6iOv%__hH#+X)b{jb3#`QxFiasCXx2K%mLc{0Io)gaJ*VZ^Y=FcAF z>kY?!+&*yJ^pB!L{M2zDw{ILb{V&k(5`H?)o4-45cG_CSu=jZ4JrVZR?DT}buH!y# zr-3tWd@plz(GS45?L4QqxG_HI!tX(Q?BVQ~Kl92HF5itu&d(;YWE_0gZ-?YP<54*mU(`?xIuXWaPy==GxShU?*vo!;Wc=Ow=sej@sZ zZO)GQ(-P(Tv*SK)`y4m@H_-p>xapUoUM0EreVD!(u4ijFZhFoS*LU3XT<3JAaIRZA z+i|nA4feY@Zg$py_i)_wJRc1LXPo)I>|)U;VgNDK>Erbi@w`zupL4jy*)e}AWBk0p zar1}gk%t{O{X@_%cHH!g^9sjJzX|$Rh1bG$$422jz&~-^$7d@zMV5gVx z9pL>P_wgAD&iHVhErbWahNJR9sU1}&W`!t3fIf?9QX0L*YUU>_4Wzj zlfhRBe*^pt;cYpN6+RXGTX2>y_r=)h^geEXIK7Pn`JVK8?!7jqe*o7t{{p9dK7W0> z@Y@i#=FX1Af%l2ih4;txNH@nVU0(kV6n-b-lLbzHxG%$4;p|TeoE`J$9;939xTVYM ziy4laelD(mXE|>Awy-nbantXBeu3kr=k>zFj+?$A&QpsWH$ATxo)^yRg>}L?|Np+@ zX1@&fKXTmc@5gz3i{qx>2|eFi#PLP}=2iX@{T}c`j+=e%8(0~0H~qKJPj}q>tb%;a1!p~HeZE8ZaX8N|bau>sCyY;)3x5cFt?-@T zuRCu3a6WCL<7S`FPkrLJ={aw9$Z^wOgL&=R?)^=^TuuRJxh%str-|cME=k}moE?iX z_s_k^aZC3NqpTj;8Gr8MF;V#Wi2qb)$I=}N`?DN3`&_p+-*M9~ zhW;Vp?|?6M-0XA(UkOe>JHek9h5rNonzLi+o`CWHCgJPBzjEBtO~?3oyW?i34(2O= zcHH#r&-V+jj`8XV?)_P&XFF{O&ib$m<<9qZF~8}^S6gSt>I3%|&Tw`tNxmOB$8oDy z^I@k@>~LS;MUI=FC1@{8#SWjxUnzVB#(OV1I~KPdh}%ZtPlA8qxTVYXwbgO6!}m&l z@3`sLp`YL7xam7!ezO)f3Ss{|5&SgaPl2a7?#rt^ILm7z%B#EM7UxSaPVVdMSb1^3 zt@bSVQ0H5Tz`T4UY9t6Q%!Z{DWQTT-@-!FwT{(FV9-u^9|>DG7e zBQt+Sq5n?-XMN`S__Ku~lT9T5$UF zBm7w}`gkP`g5YDv%^%Kpd?oxgr2B*LFTi&?ZvJqeWxj8Y?XC{4-xBa1H1g@-wS|ww zI6eiO?QS^wv6hZo)xRItQ|Afi^;d7Pe;@1*5xyNfOL%*X|8m9tYq)N@O8EQW)5Q+Q z-?N3Uf&KZy_kiE$xYf@|u>UwX%Y7Q!(KEtd1ApGxv2t&Uam-rBf4T+elzr& zg&zdp;<(x2{jBYdo1LyWU;ga4=|9Ch+n95*{lpugR5)3cv{0G#b^E85*s$F1GPaGk!=*|C0r^U|+6y&s?a z<@A>BX0+eRc%Xyj$nvTw{BM-w3E(Wp?~tzsj+=ef=Vp#u9J-?aN^{)wjDLH_O+N+t zj*grDkU1O#L0{qQp9{d5ud(odqSIU4xUYJd)BCtBaopnnBbU>=St*vukhp1KK~XwTxWO;-ZN-+EMMH8{S1NXh^}amw21-GR&XcF+}OS>d?pccUG>ES&XugK)NYzAw`Jw|TRrXzxEl&wR~CJb!g|tbEJ& zi+AsBG(We(&pP0=!~F(N68=2m(_HMFje6Er_(<@MVu$;rbr-$}_Ae1T3t(rc=xJxP zaN3z9ocl3O7k&xmoyx%3zFt7OcM9)}_&z550r2I{ete!2=gpTKw|4Ow^81Ff6YrmK ze*e^QE3e(Kzg6s1hX31zw*=oOcG~0n-=e3TV=&Rg`anCUIqvh7>bT`=IMQt^d_DMu z;EXfhOM0MPwsp6h2!4;KOHxJ{)73+W8Hf~jk7xxyK@&cYd=zQUQ`OyTrrl<>1~ zoi#x?)151v>E11z@qbh}=Z%*O=YCjk3g>#%kAyQ{Uka!Hd{3*j7mIUW%!9GMkdH&Z zQO~{S)bvka-s4Q+j6+A^%blmjpH?DBp^v~e^mt(={|2*8UDR6r8pYOw%?6}#f zj*W+>IUes1aeww^$4$Qy`bENr;>Ctbgs(X-yzW>D&i=Ut`sY_1xBht({O5antzDZx zeBZ-nv46(-q5Un6o1X7W_{DMa{~_r2yL9b*F%SM!Mm=IY=}%2?`os4Vob0&Sr$1+i zo#vR&Om%wmlkXL{&~dYK!UbWzx(ojRyqDudU|xx0`YQVu9mkq7&l$sN+`djL%cfzU7Pi;IDDq^2Io;b37hTy9l%S zRL4z!C0?-kiSQNR-+7hS~Xy2?q+|RwP<7Q_y z^rs4Ma8YRIba484Bkl{d5}t|sZXJbx2R+}{Y<8?%xbN}hqGudNfzyBP&tB}f`Oo}L z5<3mx&vfCv!EY8jFC)MHz0cMkeuV40rA}|0=kZm-c^-dFIM3r7h4cRYZ^C*0Js_O^ zR7QVn@iBiM!+Ey8aQc}doc^>BPX8|uPCqk*a~;AU;am?eTzG$shp!f%jQg9{3Fmn4 zX5oV^v-_-e%AG2weLKH>YL88^oH1<^B}uM6*rbiWgxf^ovH!XLr7{eW`lTjBi1 z_Qm%a@O{waoiPu85%lJU)&Fl%Z~F@Wq)Ql|OyOOyk^d-{uC>R07~d8;Zv74v{%HF1D(v4XdfK^LIPE;;xcBEp$IYK6NcS~xOV|2Ip5H%q+~V^+{Mqcd=?igQ z*x|V8le&d*`%`!-IA5sD{Eh~%hVcmdw_UJb-*NAMGC2L`_2b!&d;i;soqo7q*2(ED zUt5uGFUQT!J@B)?^08A=y6II&jBL-y8Z9g>L{)7T)sW&`wj~w}7`2z5=|1#Lj%!DHPtbPw4+7;g5k|Cp@8VsGlQzCiv~bzXe|?{6f6& z{4wD-f-eVW{b77wa@^|AGQ{BxXUF0hU_SXn$1R@k!TuM*Pv{@!Yn$*a@SlY*2LH`* zOSd1=t=T@br~aSfxc9#?IP2#k*hzET{HLGogfn01!s&lc$89{*6#du`@bY?v`g4Ww zr*OY@jI(3@>_!~^EqvgBF#l!3p8>zwar1}IgWv7A*{_Bd-Q4fE>A4Q-3E^B%^0DJ) zCmnXS37-xAoA6)2|8(5a<#;9m*PSfiLbTsH;P%z@*C5XSa{73=Bc7)V{}K0vn>#z^ z|BHjdc(!%i>~sDh-Eq^OHaOIGbKLZdPk+ZvKNk8Sj+>rw&U4)K_e1}0;s1b7b=>SQ zZnMD|w|zo&L0xmc}w^mmxTO%$IVYZhr8Kv zv$F;IEsmR>&*$!N-1M=`(Ecxuo1Xiq?04MsDbWApxak@Hs+cEWz4{*OE9yCJb{>PB zlN~oZyf56wanmPU8v1#jSV4vfrXTOwa7l(Kshv`$GPjcM! z^D#cJ>$vHMLSNr;({nu5*m2X}27MF9P2U;od|Eqh`nRF)EPMc7eAi$2U*N;QS-#Jq ze8)PyrMnjMSH+?qj&h$Y{3+bOp5g46pR3}T4uZRce~TCA+~>GSSsxyE-0Yk+IxLT+ zj+>tK;d#eRUjqG`!tVlq*KxDc0q5T@g>xOLs{`CkTqE^$0wrEp($ zsN?1*+flx=Z>Il*KUWJ+M17d)>{zjqwTcC0>}hx)Kt_?)p}9JV-a^@Q=<;keoP z82aD9>CcC#hkrS})$R0$IZ`|V81~4ci<(Co1I;-Gu?5s za|&LBGaHCd-Fw}$Zgd13h;=eXJ7{_LkXZvOXyK1KLi z@aDo#9Ut0nD|{|^I=IEb>d#t?=X*N6`Lh-I8tAzB6Uz_n4|R6zywC>gibe}x0A3V-HxBjOt?)$72`+Se~df|<*o@1l4 zV{tp7AdK5K;icd^9Jh42ui9S6&CV;(?|0nvd>*G#=P(`C!>Wbh_v3{R1V71fv%}|c zQXIE*?}NUnME?GDsgtKe%8i%o0*PV zy1ygcha9(bH{(A3TH%b_H^S-XPr_;ESlkC=f5P{$*AdoZOQXSs~W3#iW!{vLRm z@TNs!IkppC1fC8~e+Hr+_7Ki>TEoQ7V%QlY{2lND;lF@Sim*T3>8-pvp}gigZsk>{ zILz0b!n=dt>$uq|gq=qmH#^rr{~vJHt9htb>m0XwwFvQf$JsIai(vmN$Ibp`*#ANJ zLGV3dKN;nIKseVMCF1@e>qBNq7>C-zSAm}-JT@WJHxk|xyg4}IP#bY*@3_UG6n8uIn1@K#rcad=+%WbhA#F9+Y| zxb+VohGv4_g>(L}I__JVKc?q+K1FyRth;Lg&h|JQ`D!Qp9o!G@%^Z!Noxk7km zY3Tnd$Ibpc*m=cqv-2bLZwqfVDYWwuIO9AC>mj}nen0q6VrMe!?1|t9g+Bm&4ELEe z-D8C_-R8pILw(?VZ`%19ya)8G|DPe<0m57VJB&l7;}*}gi025$t)B3DInQy6^9tCx z%5h)Mr#Nokin@a6iKn!Ob5VlA?HnWYHhOeYdkk4;-8$`uk8W=}vE`17Rjldw)~mGCy!Dcb%Y&i<`5 zZqJ2ap5DS@^KTOopMkjkv#)k5VhXMsrwYFkd;|JR+Ubq@yi@pP;8oBsQhyD2W8u_y z6i)qP5xg7v1KNiy7#YE@jo|l0a4cVo*K^#ei65-jmM%gcKRz&E*Pp_%Y%8wydUT`Z zoOaR7$~pHDT^X7$=iDcAt8hMVTOIybn)V-`qiiRf`}mC(&VA=@63+eBRtaZ4-z%K^ zXdREt(;x0A#0992wA!pC_Ep zBlA6bw8Q6W*Q*}=*k0j${xJz12<`Cs!6w4_yxVZ$e7!zR64s&9KCg3|2cLx`p(rE_!2idVOd7mcRO#Z)u2;AzH?z*8w^Sa+gcDL#WR060u>wyvG}PxAt7D> z31N9AB!%B%@lsDnh*z$}O2_2pmjnsntGDsDc(tyb(BTf-+t=1H!){?LoF0B%yKj@Nb#n-)e{7 zE(^aMAAY<1-ng=UkkllhSI1Zo#P820WIq=0!>VJx0r1;@n+K@h(bi#4>5cHnD&e65{bR20M-(?EQ@6H76#6K?GkAN9sC z_hH+aCRrdVC)uBaSe@AEHS1POta(b!Ic$dl zR;$*&+WFODb%WrT8bM-B`_iLsyQ)D@xmHlET93LxQ00Vj9t24z#t#4D*u%dFf~v7t zkGf{3TEn16-Lz^!P`#4V)@TwW);zUF5Y$YKb*vsHbY}G)bhz^ zZWAOXh3?qbI&BXBwQBsu>!FQ0zS&^(7bFA|llU1Ummo2ixVHSuv@qszAwNG478gbx zxK0RS!C-#GR9_GahT^KG+*Is{LD3Nhe;q4-HX^`U(;yZMq@j8EO)MD5kB^k2A^iAk zIl{8P!w1H@&UQa0W7ki1#DZb`IJ+G6dP;4j;C}A#4o1iU|+?M31F$y^U~Vu>&=01hH8Bq!J5)SV3`+HlesEEjNEu z-h|QFX{GJjX0>h8JTEtYLTU4{`4iGc=9U~O34)TGqU;f)i?a#~igQa-N12_%kd;g- z%q~vNG3P*1bK+N9!Lew(L6F!mmURE`=jERBR`U*Rh9*u*Xc#k0CD}#!Bl5EHa*Ipo6pWSxSy`hBCgj_{Bl2>` z<_9B6N{WJl;xx@+R#stA_SHdFR(^J=smJB!j}Gz*^2a8}|7VUAmy8%SE-P-JoE_<0 zpS}Znr}qlRM^9N z{bfRaaqigs?9s`iaz+#d{_|l^%Qfcm5ntkQ4zjX}bN|iw9`X0M(X6ce3B}n(#X(wj zX<cjScPkU&QGFT{{mRFc4mM?=v{L zb8?&1wyCXMv4y2x9D06)ABT(9N;FGrz~Nv?qJw($?%Kt|l$BL5F+aN~$O@~(k!4iw zn*F(8)VLt%-nn!8qY@@wrR+@24pPHTKCft;mkX5y2QnzsSI<vJLgVf@ZqVV4w zek#h&8MOqG6^uGUIihGp{@Cmz zf1Q_`pG|#nNl}nGs$l&1?EI1-HNT)FJGFbC!Oi0}9EP%Uvc?pR7>_kK|G$S_RY#yq$c~xb0)JKKDE%x$5wYCJeqWmZGUIl(>`M2S-=HG5XsZBdFkp8mr zl*2={cFVe=^!p)w)-y}r8oH)$<1DjDKY8p!`g0=6pMK1V`27s5<-`c4$g z-|g6rdqSKmv;N(djcEE7ri#*EkGa@;m@p1aKk6`DrC|4=ivFG$VI{FbPt3oHB5XW< zoq5!Oi@&3e#PZx9c_fAxrbiu#Tp%n*A{S=Lkr-a?9d#rg)$4~xV!T9-JS^G-4d;c+anhhacl1t@v;bh)QCBHWXo}Dd;k<578Ylh*s%J@zr+W`N20jc z#>o}n(b>f%MFo>=co!e0j>;LAHD*LEmRJQ?5*lv@aSz zCb#Phay#{qv;CRg`ap7??``^upY6b{+c>ws{SQTn=2XtFjjMwIx6;b_L(pSuh@;v! z9sw@rtRHyfA#TTaee9Rb7yGX*?BmvUIiC)WCDi47EI4kxm-DLVbnpmgIqw2JuUVNc z{l?Pwa{e;nfF+vc{C3#E(ztStTQUJ|xt4R@_hGp+-373JAO0mj75+aid>_(fxl?~V z^sk5>OGSeX!iPcsmGHY^f2Z&V@O{5Veqp9$J<9i$7oag8#9FIZ; zqlD8=zHr)^Eqogsx=T3IyI;7?4cftX@-e^Nkl*E^=e?)rh4U1%M);}tzE1dhr2Cfe zJfyopcyn-$59uezD_@BoQ|!TZaQg=);>RxGY}c%hW}i0fh~dXfw* +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Structure to represent a single 16-bit signed little-endian PCM sample. +typedef struct { + int16_t left; + int16_t right; + int16_t center; + int16_t lfe; + int16_t surround_left; + int16_t surround_right; +} freenect_sample_51; + +/** + * Typedef for "you wanted this microphone data, here it is" event callbacks. + * TODO: Timestamp details + * The format of the unknown stream is as of yet undetermined. + * + * @param dev Device which triggered this callback + * @param num_samples Number of samples provided in each of the audio data arrays (mic[1-4] and cancelled) + * @param mic1 Microphone data for the leftmost microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic2 Microphone data for the left-middle microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic3 Microphone data for the right-middle microphone: 32-bit PCM little-endian samples at 16kHz. + * @param mic4 Microphone data for the rightmost microphone: 32-bit PCM little-endian samples at 16kHz. + * @param cancelled Noise-cancelled audio data: 16-bit PCM little-endian samples at 16kHz. + */ +typedef void (*freenect_audio_in_cb)(freenect_device *dev, int num_samples, + int32_t* mic1, int32_t* mic2, + int32_t* mic3, int32_t* mic4, + int16_t* cancelled, void *unknown/*, timestamp_t timestamp*/); + +/** + * Typedef for "you're playing audio, the library needs you to fill up the outgoing audio buffer" event callbacks + * The library will request samples at a rate of 48000Hz. + * + * @param dev Device this callback was triggered for + * @param samples Pointer to the memory where the library expects you to copy the next sample_count freenect_sample_51's to. + * @param sample_count Bidirectional. in: maximum number of samples the driver wants (don't copy in more than this, you'll clobber memory). out: actual number of samples provided to the driver. + */ +typedef void (*freenect_audio_out_cb)(freenect_device *dev, freenect_sample_51* samples, int* sample_count); + +/** + * Set the audio in callback. This is the function called when the library + * has new microphone samples. It will be called approximately 62.5 times per + * second (16kHz sample rate, expect 512 samples/callback) + * + * @param dev Device for which to set the callback + * @param callback Callback function to set + */ +FREENECTAPI void freenect_set_audio_in_callback(freenect_device *dev, freenect_audio_in_cb callback); + +/** + * Set the audio out callback. This is the "tell me what audio you're about + * to play through the speakers so the Kinect can subtract it out" callback for + * a given device. If you choose not set an audio_out_callback, the library + * will send silence to the Kinect for you - it requires data either way. + * + * @param dev Device for which to set the callback + * @param callback Callback function to set + */ +FREENECTAPI void freenect_set_audio_out_callback(freenect_device *dev, freenect_audio_out_cb callback); + +/** + * Start streaming audio for the specified device. + * + * @param dev Device for which to start audio streaming + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_start_audio(freenect_device* dev); + +/** + * Stop streaming audio for the specified device. + * + * @param dev Device for which to stop audio streaming + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_stop_audio(freenect_device* dev); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/include/libfreenect-registration.h b/interface/external/freenect/include/libfreenect-registration.h new file mode 100644 index 0000000000..7ad2b5bb80 --- /dev/null +++ b/interface/external/freenect/include/libfreenect-registration.h @@ -0,0 +1,126 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2011 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Internal Kinect registration parameters. +/// Structure matches that of the line protocol +/// of the Kinect. +typedef struct { + int32_t dx_center; // not used by mapping algorithm + + int32_t ax; + int32_t bx; + int32_t cx; + int32_t dx; + + int32_t dx_start; + + int32_t ay; + int32_t by; + int32_t cy; + int32_t dy; + + int32_t dy_start; + + int32_t dx_beta_start; + int32_t dy_beta_start; + + int32_t rollout_blank; // not used by mapping algorithm + int32_t rollout_size; // not used by mapping algorithm + + int32_t dx_beta_inc; + int32_t dy_beta_inc; + + int32_t dxdx_start; + int32_t dxdy_start; + int32_t dydx_start; + int32_t dydy_start; + + int32_t dxdxdx_start; + int32_t dydxdx_start; + int32_t dxdxdy_start; + int32_t dydxdy_start; + + int32_t back_comp1; // not used by mapping algorithm + + int32_t dydydx_start; + + int32_t back_comp2; // not used by mapping algorithm + + int32_t dydydy_start; +} freenect_reg_info; + +/// registration padding info (?) +typedef struct { + uint16_t start_lines; + uint16_t end_lines; + uint16_t cropping_lines; +} freenect_reg_pad_info; + +/// internal Kinect zero plane data +typedef struct { + float dcmos_emitter_dist; // Distance between IR camera and IR emitter, in cm. + float dcmos_rcmos_dist; // Distance between IR camera and RGB camera, in cm. + float reference_distance; // The focal length of the IR camera, in mm. + float reference_pixel_size; // The size of a single pixel on the zero plane, in mm. +} freenect_zero_plane_info; + +/// all data needed for depth->RGB mapping +typedef struct { + freenect_reg_info reg_info; + freenect_reg_pad_info reg_pad_info; + freenect_zero_plane_info zero_plane_info; + + double const_shift; + + uint16_t* raw_to_mm_shift; + int32_t* depth_to_rgb_shift; + int32_t (*registration_table)[2]; // A table of 640*480 pairs of x,y values. + // Index first by pixel, then x:0 and y:1. +} freenect_registration; + + +// These allow clients to export registration parameters; proper docs will +// come later +FREENECTAPI freenect_registration freenect_copy_registration(freenect_device* dev); +FREENECTAPI int freenect_destroy_registration(freenect_registration* reg); + +// convenience function to convert a single x-y coordinate pair from camera +// to world coordinates +FREENECTAPI void freenect_camera_to_world(freenect_device* dev, + int cx, int cy, int wz, double* wx, double* wy); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/include/libfreenect.h b/interface/external/freenect/include/libfreenect.h new file mode 100644 index 0000000000..13c7c4c538 --- /dev/null +++ b/interface/external/freenect/include/libfreenect.h @@ -0,0 +1,619 @@ +/* + * This file is part of the OpenKinect Project. http://www.openkinect.org + * + * Copyright (c) 2010 individual OpenKinect contributors. See the CONTRIB file + * for details. + * + * This code is licensed to you under the terms of the Apache License, version + * 2.0, or, at your option, the terms of the GNU General Public License, + * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, + * or the following URLs: + * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.gnu.org/licenses/gpl-2.0.txt + * + * If you redistribute this file in source form, modified or unmodified, you + * may: + * 1) Leave this header intact and distribute it under the same terms, + * accompanying it with the APACHE20 and GPL20 files, or + * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or + * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file + * In all cases you must keep the copyright notice intact and include a copy + * of the CONTRIB file. + * + * Binary distributions must follow the binary distribution requirements of + * either License. + */ + +#pragma once + +#include + +/* We need struct timeval */ +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define FREENECT_COUNTS_PER_G 819 /**< Ticks per G for accelerometer as set per http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf */ + +/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED frame callbacks +#define FREENECT_DEPTH_MM_MAX_VALUE 10000 +/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED depth modes +#define FREENECT_DEPTH_MM_NO_VALUE 0 +/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED frame callbacks +#define FREENECT_DEPTH_RAW_MAX_VALUE 2048 +/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED +#define FREENECT_DEPTH_RAW_NO_VALUE 2047 + +/// Flags representing devices to open when freenect_open_device() is called. +/// In particular, this allows libfreenect to grab only a subset of the devices +/// in the Kinect, so you could (for instance) use libfreenect to handle audio +/// and motor support while letting OpenNI have access to the cameras. +/// If a device is not supported on a particular platform, its flag will be ignored. +typedef enum { + FREENECT_DEVICE_MOTOR = 0x01, + FREENECT_DEVICE_CAMERA = 0x02, + FREENECT_DEVICE_AUDIO = 0x04, +} freenect_device_flags; + +/// A struct used in enumeration to give access to serial numbers, so you can +/// open a particular device by serial rather than depending on index. This +/// is most useful if you have more than one Kinect. +struct freenect_device_attributes; +struct freenect_device_attributes { + struct freenect_device_attributes *next; /**< Next device in the linked list */ + const char* camera_serial; /**< Serial number of this device's camera subdevice */ +}; + +/// Enumeration of available resolutions. +/// Not all available resolutions are actually supported for all video formats. +/// Frame modes may not perfectly match resolutions. For instance, +/// FREENECT_RESOLUTION_MEDIUM is 640x488 for the IR camera. +typedef enum { + FREENECT_RESOLUTION_LOW = 0, /**< QVGA - 320x240 */ + FREENECT_RESOLUTION_MEDIUM = 1, /**< VGA - 640x480 */ + FREENECT_RESOLUTION_HIGH = 2, /**< SXGA - 1280x1024 */ + FREENECT_RESOLUTION_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_resolution; + +/// Enumeration of video frame information states. +/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. +typedef enum { + FREENECT_VIDEO_RGB = 0, /**< Decompressed RGB mode (demosaicing done by libfreenect) */ + FREENECT_VIDEO_BAYER = 1, /**< Bayer compressed mode (raw information from camera) */ + FREENECT_VIDEO_IR_8BIT = 2, /**< 8-bit IR mode */ + FREENECT_VIDEO_IR_10BIT = 3, /**< 10-bit IR mode */ + FREENECT_VIDEO_IR_10BIT_PACKED = 4, /**< 10-bit packed IR mode */ + FREENECT_VIDEO_YUV_RGB = 5, /**< YUV RGB mode */ + FREENECT_VIDEO_YUV_RAW = 6, /**< YUV Raw mode */ + FREENECT_VIDEO_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_video_format; + +/// Enumeration of depth frame states +/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. +typedef enum { + FREENECT_DEPTH_11BIT = 0, /**< 11 bit depth information in one uint16_t/pixel */ + FREENECT_DEPTH_10BIT = 1, /**< 10 bit depth information in one uint16_t/pixel */ + FREENECT_DEPTH_11BIT_PACKED = 2, /**< 11 bit packed depth information */ + FREENECT_DEPTH_10BIT_PACKED = 3, /**< 10 bit packed depth information */ + FREENECT_DEPTH_REGISTERED = 4, /**< processed depth data in mm, aligned to 640x480 RGB */ + FREENECT_DEPTH_MM = 5, /**< depth to each pixel in mm, but left unaligned to RGB image */ + FREENECT_DEPTH_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ +} freenect_depth_format; + +/// Structure to give information about the width, height, bitrate, +/// framerate, and buffer size of a frame in a particular mode, as +/// well as the total number of bytes needed to hold a single frame. +typedef struct { + uint32_t reserved; /**< unique ID used internally. The meaning of values may change without notice. Don't touch or depend on the contents of this field. We mean it. */ + freenect_resolution resolution; /**< Resolution this freenect_frame_mode describes, should you want to find it again with freenect_find_*_frame_mode(). */ + union { + int32_t dummy; + freenect_video_format video_format; + freenect_depth_format depth_format; + }; /**< The video or depth format that this freenect_frame_mode describes. The caller should know which of video_format or depth_format to use, since they called freenect_get_*_frame_mode() */ + int32_t bytes; /**< Total buffer size in bytes to hold a single frame of data. Should be equivalent to width * height * (data_bits_per_pixel+padding_bits_per_pixel) / 8 */ + int16_t width; /**< Width of the frame, in pixels */ + int16_t height; /**< Height of the frame, in pixels */ + int8_t data_bits_per_pixel; /**< Number of bits of information needed for each pixel */ + int8_t padding_bits_per_pixel; /**< Number of bits of padding for alignment used for each pixel */ + int8_t framerate; /**< Approximate expected frame rate, in Hz */ + int8_t is_valid; /**< If 0, this freenect_frame_mode is invalid and does not describe a supported mode. Otherwise, the frame_mode is valid. */ +} freenect_frame_mode; + +/// Enumeration of LED states +/// See http://openkinect.org/wiki/Protocol_Documentation#Setting_LED for more information. +typedef enum { + LED_OFF = 0, /**< Turn LED off */ + LED_GREEN = 1, /**< Turn LED to Green */ + LED_RED = 2, /**< Turn LED to Red */ + LED_YELLOW = 3, /**< Turn LED to Yellow */ + LED_BLINK_GREEN = 4, /**< Make LED blink Green */ + // 5 is same as 4, LED blink Green + LED_BLINK_RED_YELLOW = 6, /**< Make LED blink Red/Yellow */ +} freenect_led_options; + + +/// Enumeration of tilt motor status +typedef enum { + TILT_STATUS_STOPPED = 0x00, /**< Tilt motor is stopped */ + TILT_STATUS_LIMIT = 0x01, /**< Tilt motor has reached movement limit */ + TILT_STATUS_MOVING = 0x04, /**< Tilt motor is currently moving to new position */ +} freenect_tilt_status_code; + +/// Data from the tilt motor and accelerometer +typedef struct { + int16_t accelerometer_x; /**< Raw accelerometer data for X-axis, see FREENECT_COUNTS_PER_G for conversion */ + int16_t accelerometer_y; /**< Raw accelerometer data for Y-axis, see FREENECT_COUNTS_PER_G for conversion */ + int16_t accelerometer_z; /**< Raw accelerometer data for Z-axis, see FREENECT_COUNTS_PER_G for conversion */ + int8_t tilt_angle; /**< Raw tilt motor angle encoder information */ + freenect_tilt_status_code tilt_status; /**< State of the tilt motor (stopped, moving, etc...) */ +} freenect_raw_tilt_state; + +struct _freenect_context; +typedef struct _freenect_context freenect_context; /**< Holds information about the usb context. */ + +struct _freenect_device; +typedef struct _freenect_device freenect_device; /**< Holds device information. */ + +// usb backend specific section +typedef void freenect_usb_context; /**< Holds libusb-1.0 context */ +// + +/// If Win32, export all functions for DLL usage +#ifndef _WIN32 + #define FREENECTAPI /**< DLLExport information for windows, set to nothing on other platforms */ +#else + /**< DLLExport information for windows, set to nothing on other platforms */ + #ifdef __cplusplus + #define FREENECTAPI extern "C" __declspec(dllexport) + #else + // this is required when building from a Win32 port of gcc without being + // forced to compile all of the library files (.c) with g++... + #define FREENECTAPI __declspec(dllexport) + #endif +#endif + +/// Enumeration of message logging levels +typedef enum { + FREENECT_LOG_FATAL = 0, /**< Log for crashing/non-recoverable errors */ + FREENECT_LOG_ERROR, /**< Log for major errors */ + FREENECT_LOG_WARNING, /**< Log for warning messages */ + FREENECT_LOG_NOTICE, /**< Log for important messages */ + FREENECT_LOG_INFO, /**< Log for normal messages */ + FREENECT_LOG_DEBUG, /**< Log for useful development messages */ + FREENECT_LOG_SPEW, /**< Log for slightly less useful messages */ + FREENECT_LOG_FLOOD, /**< Log EVERYTHING. May slow performance. */ +} freenect_loglevel; + +/** + * Initialize a freenect context and do any setup required for + * platform specific USB libraries. + * + * @param ctx Address of pointer to freenect context struct to allocate and initialize + * @param usb_ctx USB context to initialize. Can be NULL if not using multiple contexts. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_init(freenect_context **ctx, freenect_usb_context *usb_ctx); + +/** + * Closes the device if it is open, and frees the context + * + * @param ctx freenect context to close/free + * + * @return 0 on success + */ +FREENECTAPI int freenect_shutdown(freenect_context *ctx); + +/// Typedef for logging callback functions +typedef void (*freenect_log_cb)(freenect_context *dev, freenect_loglevel level, const char *msg); + +/** + * Set the log level for the specified freenect context + * + * @param ctx context to set log level for + * @param level log level to use (see freenect_loglevel enum) + */ +FREENECTAPI void freenect_set_log_level(freenect_context *ctx, freenect_loglevel level); + +/** + * Callback for log messages (i.e. for rerouting to a file instead of + * stdout) + * + * @param ctx context to set log callback for + * @param cb callback function pointer + */ +FREENECTAPI void freenect_set_log_callback(freenect_context *ctx, freenect_log_cb cb); + +/** + * Calls the platform specific usb event processor + * + * @param ctx context to process events for + * + * @return 0 on success, other values on error, platform/library dependant + */ +FREENECTAPI int freenect_process_events(freenect_context *ctx); + +/** + * Calls the platform specific usb event processor until either an event occurs + * or the timeout parameter time has passed. If a zero timeval is passed, this + * function will handle any already-pending events, then return immediately. + * + * @param ctx Context to process events for + * @param timeout Pointer to a timeval containing the maximum amount of time to block waiting for events, or zero for nonblocking mode + * + * @return 0 on success, other values on error, platform/library dependant + */ +FREENECTAPI int freenect_process_events_timeout(freenect_context *ctx, struct timeval* timeout); + +/** + * Return the number of kinect devices currently connected to the + * system + * + * @param ctx Context to access device count through + * + * @return Number of devices connected, < 0 on error + */ +FREENECTAPI int freenect_num_devices(freenect_context *ctx); + +/** + * Scans for kinect devices and produces a linked list of their attributes + * (namely, serial numbers), returning the number of devices. + * + * @param ctx Context to scan for kinect devices with + * @param attribute_list Pointer to where this function will store the resultant linked list + * + * @return Number of devices connected, < 0 on error + */ +FREENECTAPI int freenect_list_device_attributes(freenect_context *ctx, struct freenect_device_attributes** attribute_list); + +/** + * Free the linked list produced by freenect_list_device_attributes(). + * + * @param attribute_list Linked list of attributes to free. + */ +FREENECTAPI void freenect_free_device_attributes(struct freenect_device_attributes* attribute_list); + +/** + * Answer which subdevices this library supports. This is most useful for + * wrappers trying to determine whether the underlying library was built with + * audio support or not, so the wrapper can avoid calling functions that do not + * exist. + * + * @return Flags representing the subdevices that the library supports opening (see freenect_device_flags) + */ +FREENECTAPI int freenect_supported_subdevices(void); + +/** + * Set which subdevices any subsequent calls to freenect_open_device() + * should open. This will not affect devices which have already been + * opened. The default behavior, should you choose not to call this + * function at all, is to open all supported subdevices - motor, cameras, + * and audio, if supported on the platform. + * + * @param ctx Context to set future subdevice selection for + * @param subdevs Flags representing the subdevices to select + */ +FREENECTAPI void freenect_select_subdevices(freenect_context *ctx, freenect_device_flags subdevs); + +/** + * Opens a kinect device via a context. Index specifies the index of + * the device on the current state of the bus. Bus resets may cause + * indexes to shift. + * + * @param ctx Context to open device through + * @param dev Device structure to assign opened device to + * @param index Index of the device on the bus + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_open_device(freenect_context *ctx, freenect_device **dev, int index); + +/** + * Opens a kinect device (via a context) associated with a particular camera + * subdevice serial number. This function will fail if no device with a + * matching serial number is found. + * + * @param ctx Context to open device through + * @param dev Device structure to assign opened device to + * @param camera_serial Null-terminated ASCII string containing the serial number of the camera subdevice in the device to open + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_open_device_by_camera_serial(freenect_context *ctx, freenect_device **dev, const char* camera_serial); + +/** + * Closes a device that is currently open + * + * @param dev Device to close + * + * @return 0 on success + */ +FREENECTAPI int freenect_close_device(freenect_device *dev); + +/** + * Set the device user data, for passing generic information into + * callbacks + * + * @param dev Device to attach user data to + * @param user User data to attach + */ +FREENECTAPI void freenect_set_user(freenect_device *dev, void *user); + +/** + * Retrieve the pointer to user data from the device struct + * + * @param dev Device from which to get user data + * + * @return Pointer to user data + */ +FREENECTAPI void *freenect_get_user(freenect_device *dev); + +/// Typedef for depth image received event callbacks +typedef void (*freenect_depth_cb)(freenect_device *dev, void *depth, uint32_t timestamp); +/// Typedef for video image received event callbacks +typedef void (*freenect_video_cb)(freenect_device *dev, void *video, uint32_t timestamp); + +/** + * Set callback for depth information received event + * + * @param dev Device to set callback for + * @param cb Function pointer for processing depth information + */ +FREENECTAPI void freenect_set_depth_callback(freenect_device *dev, freenect_depth_cb cb); + +/** + * Set callback for video information received event + * + * @param dev Device to set callback for + * @param cb Function pointer for processing video information + */ +FREENECTAPI void freenect_set_video_callback(freenect_device *dev, freenect_video_cb cb); + +/** + * Set the buffer to store depth information to. Size of buffer is + * dependant on depth format. See FREENECT_DEPTH_*_SIZE defines for + * more information. + * + * @param dev Device to set depth buffer for. + * @param buf Buffer to store depth information to. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_depth_buffer(freenect_device *dev, void *buf); + +/** + * Set the buffer to store depth information to. Size of buffer is + * dependant on video format. See FREENECT_VIDEO_*_SIZE defines for + * more information. + * + * @param dev Device to set video buffer for. + * @param buf Buffer to store video information to. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_video_buffer(freenect_device *dev, void *buf); + +/** + * Start the depth information stream for a device. + * + * @param dev Device to start depth information stream for. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_start_depth(freenect_device *dev); + +/** + * Start the video information stream for a device. + * + * @param dev Device to start video information stream for. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_start_video(freenect_device *dev); + +/** + * Stop the depth information stream for a device + * + * @param dev Device to stop depth information stream on. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_stop_depth(freenect_device *dev); + +/** + * Stop the video information stream for a device + * + * @param dev Device to stop video information stream on. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_stop_video(freenect_device *dev); + +/** + * Updates the accelerometer state using a blocking control message + * call. + * + * @param dev Device to get accelerometer data from + * + * @return 0 on success, < 0 on error. Accelerometer data stored to + * device struct. + */ +FREENECTAPI int freenect_update_tilt_state(freenect_device *dev); + +/** + * Retrieve the tilt state from a device + * + * @param dev Device to retrieve tilt state from + * + * @return The tilt state struct of the device + */ +FREENECTAPI freenect_raw_tilt_state* freenect_get_tilt_state(freenect_device *dev); + +/** + * Return the tilt state, in degrees with respect to the horizon + * + * @param state The tilt state struct from a device + * + * @return Current degree of tilt of the device + */ +FREENECTAPI double freenect_get_tilt_degs(freenect_raw_tilt_state *state); + +/** + * Set the tilt state of the device, in degrees with respect to the + * horizon. Uses blocking control message call to update + * device. Function return does not reflect state of device, device + * may still be moving to new position after the function returns. Use + * freenect_get_tilt_status() to find current movement state. + * + * @param dev Device to set tilt state + * @param angle Angle the device should tilt to + * + * @return 0 on success, < 0 on error. + */ +FREENECTAPI int freenect_set_tilt_degs(freenect_device *dev, double angle); + +/** + * Return the movement state of the tilt motor (moving, stopped, etc...) + * + * @param state Raw state struct to get the tilt status code from + * + * @return Status code of the tilt device. See + * freenect_tilt_status_code enum for more info. + */ +FREENECTAPI freenect_tilt_status_code freenect_get_tilt_status(freenect_raw_tilt_state *state); + +/** + * Set the state of the LED. Uses blocking control message call to + * update device. + * + * @param dev Device to set the LED state + * @param option LED state to set on device. See freenect_led_options enum. + * + * @return 0 on success, < 0 on error + */ +FREENECTAPI int freenect_set_led(freenect_device *dev, freenect_led_options option); + +/** + * Get the axis-based gravity adjusted accelerometer state, as laid + * out via the accelerometer data sheet, which is available at + * + * http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf + * + * @param state State to extract accelerometer data from + * @param x Stores X-axis accelerometer state + * @param y Stores Y-axis accelerometer state + * @param z Stores Z-axis accelerometer state + */ +FREENECTAPI void freenect_get_mks_accel(freenect_raw_tilt_state *state, double* x, double* y, double* z); + +/** + * Get the number of video camera modes supported by the driver. This includes both RGB and IR modes. + * + * @return Number of video modes supported by the driver + */ +FREENECTAPI int freenect_get_video_mode_count(); + +/** + * Get the frame descriptor of the nth supported video mode for the + * video camera. + * + * @param mode_num Which of the supported modes to return information about + * + * @return A freenect_frame_mode describing the nth video mode + */ +FREENECTAPI freenect_frame_mode freenect_get_video_mode(int mode_num); + +/** + * Get the frame descriptor of the current video mode for the specified + * freenect device. + * + * @param dev Which device to return the currently-set video mode for + * + * @return A freenect_frame_mode describing the current video mode of the specified device + */ +FREENECTAPI freenect_frame_mode freenect_get_current_video_mode(freenect_device *dev); + +/** + * Convenience function to return a mode descriptor matching the + * specified resolution and video camera pixel format, if one exists. + * + * @param res Resolution desired + * @param fmt Pixel format desired + * + * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. + */ +FREENECTAPI freenect_frame_mode freenect_find_video_mode(freenect_resolution res, freenect_video_format fmt); + +/** + * Sets the current video mode for the specified device. If the + * freenect_frame_mode specified is not one provided by the driver + * e.g. from freenect_get_video_mode() or freenect_find_video_mode() + * then behavior is undefined. The current video mode cannot be + * changed while streaming is active. + * + * @param dev Device for which to set the video mode + * @param mode Frame mode to set + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_set_video_mode(freenect_device* dev, freenect_frame_mode mode); + +/** + * Get the number of depth camera modes supported by the driver. This includes both RGB and IR modes. + * + * @return Number of depth modes supported by the driver + */ +FREENECTAPI int freenect_get_depth_mode_count(); + +/** + * Get the frame descriptor of the nth supported depth mode for the + * depth camera. + * + * @param mode_num Which of the supported modes to return information about + * + * @return A freenect_frame_mode describing the nth depth mode + */ +FREENECTAPI freenect_frame_mode freenect_get_depth_mode(int mode_num); + +/** + * Get the frame descriptor of the current depth mode for the specified + * freenect device. + * + * @param dev Which device to return the currently-set depth mode for + * + * @return A freenect_frame_mode describing the current depth mode of the specified device + */ +FREENECTAPI freenect_frame_mode freenect_get_current_depth_mode(freenect_device *dev); + +/** + * Convenience function to return a mode descriptor matching the + * specified resolution and depth camera pixel format, if one exists. + * + * @param res Resolution desired + * @param fmt Pixel format desired + * + * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. + */ +FREENECTAPI freenect_frame_mode freenect_find_depth_mode(freenect_resolution res, freenect_depth_format fmt); + +/** + * Sets the current depth mode for the specified device. The mode + * cannot be changed while streaming is active. + * + * @param dev Device for which to set the depth mode + * @param mode Frame mode to set + * + * @return 0 on success, < 0 if error + */ +FREENECTAPI int freenect_set_depth_mode(freenect_device* dev, const freenect_frame_mode mode); + +#ifdef __cplusplus +} +#endif diff --git a/interface/external/freenect/lib/UNIX/libfreenect.a b/interface/external/freenect/lib/UNIX/libfreenect.a new file mode 100644 index 0000000000000000000000000000000000000000..604faaee0ad87514725d76ef9c2d7291b9f31de7 GIT binary patch literal 65072 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 - Creator diff --git a/interface/external/skeltrack/CMakeLists.txt b/interface/external/skeltrack/CMakeLists.txt new file mode 100644 index 0000000000..21ef04931b --- /dev/null +++ b/interface/external/skeltrack/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8) +include_directories(include) + +# grab the implementation and header files from src dirs +file(GLOB SKELTRACK_SRCS src/*.c include/*.h) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB2 glib-2.0) + +string(REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB2_STATIC_CFLAGS} ${GLIB2_STATIC_LDFLAGS}") +message("${CMAKE_C_FLAGS}") + +add_library(skeltrack ${SKELTRACK_SRCS}) + diff --git a/interface/external/skeltrack/COPYING b/interface/external/skeltrack/COPYING new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/interface/external/skeltrack/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/interface/external/skeltrack/include/skeltrack-joint.h b/interface/external/skeltrack/include/skeltrack-joint.h new file mode 100644 index 0000000000..4a9e0ba28c --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-joint.h @@ -0,0 +1,90 @@ +/* + * skeltrak-joint.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_JOINT_H__ +#define __SKELTRACK_JOINT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define SKELTRACK_TYPE_JOINT (skeltrack_joint_get_type ()) +#define SKELTRACK_JOINT_MAX_JOINTS 7 + +typedef struct _SkeltrackJoint SkeltrackJoint; +typedef SkeltrackJoint **SkeltrackJointList; + +/** + * SkeltrackJointId: + * @SKELTRACK_JOINT_ID_HEAD: The head + * @SKELTRACK_JOINT_ID_LEFT_SHOULDER: The left shoulder + * @SKELTRACK_JOINT_ID_RIGHT_SHOULDER: The right shoulder + * @SKELTRACK_JOINT_ID_LEFT_ELBOW: The left elbow + * @SKELTRACK_JOINT_ID_RIGHT_ELBOW: The right elbow + * @SKELTRACK_JOINT_ID_LEFT_HAND: The left hand + * @SKELTRACK_JOINT_ID_RIGHT_HAND: The right hand + * + * Available joint ids. + **/ +typedef enum { + SKELTRACK_JOINT_ID_HEAD, + SKELTRACK_JOINT_ID_LEFT_SHOULDER, + SKELTRACK_JOINT_ID_RIGHT_SHOULDER, + SKELTRACK_JOINT_ID_LEFT_ELBOW, + SKELTRACK_JOINT_ID_RIGHT_ELBOW, + SKELTRACK_JOINT_ID_LEFT_HAND, + SKELTRACK_JOINT_ID_RIGHT_HAND +} SkeltrackJointId; + +/** + * SkeltrackJoint: + * @id: The id of the joint + * @x: The x coordinate of the joint in the space (in mm) + * @y: The y coordinate of the joint in the space (in mm) + * @z: The z coordinate of the joint in the space (in mm) + * @screen_x: The x coordinate of the joint in the screen (in pixels) + * @screen_y: The y coordinate of the joint in the screen (in pixels) + **/ +struct _SkeltrackJoint +{ + SkeltrackJointId id; + + gint x; + gint y; + gint z; + + gint screen_x; + gint screen_y; +}; + +GType skeltrack_joint_get_type (void); +gpointer skeltrack_joint_copy (SkeltrackJoint *joint); +void skeltrack_joint_free (SkeltrackJoint *joint); +void skeltrack_joint_list_free (SkeltrackJointList list); +SkeltrackJointList skeltrack_joint_list_new (void); +SkeltrackJoint * skeltrack_joint_list_get_joint (SkeltrackJointList list, + SkeltrackJointId id); + +G_END_DECLS + +#endif /* __SKELTRACK_JOINT_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-skeleton.h b/interface/external/skeltrack/include/skeltrack-skeleton.h new file mode 100644 index 0000000000..11e4344025 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-skeleton.h @@ -0,0 +1,95 @@ +/* + * skeltrack.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_SKELETON_H__ +#define __SKELTRACK_SKELETON_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define SKELTRACK_TYPE_SKELETON (skeltrack_skeleton_get_type ()) +#define SKELTRACK_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeleton)) +#define SKELTRACK_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) +#define SKELTRACK_IS_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SKELTRACK_TYPE_SKELETON)) +#define SKELTRACK_IS_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SKELTRACK_TYPE_SKELETON)) +#define SKELTRACK_SKELETON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) + +typedef struct _SkeltrackSkeleton SkeltrackSkeleton; +typedef struct _SkeltrackSkeletonClass SkeltrackSkeletonClass; +typedef struct _SkeltrackSkeletonPrivate SkeltrackSkeletonPrivate; + +struct _SkeltrackSkeleton +{ + GObject parent; + + /*< private >*/ + SkeltrackSkeletonPrivate *priv; +}; + +/** + * SkeltrackSkeletonClass: + **/ +struct _SkeltrackSkeletonClass +{ + GObjectClass parent_class; +}; + +GType skeltrack_skeleton_get_type (void) G_GNUC_CONST; + +SkeltrackSkeleton * skeltrack_skeleton_new (void); + +void skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +SkeltrackJointList skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, + GAsyncResult *result, + GError **error); + +SkeltrackJointList skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GError **error); + +void skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, + gint *x, + gint *y, + gint *z); + +void skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, + gint x, + gint y, + gint z); + +G_END_DECLS + +#endif /* __SKELTRACK_SKELETON_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-smooth.h b/interface/external/skeltrack/include/skeltrack-smooth.h new file mode 100644 index 0000000000..ea99bfc7ae --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-smooth.h @@ -0,0 +1,36 @@ +/* + * skeltrack-smooth.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include "skeltrack-joint.h" + +typedef struct { + SkeltrackJointList smoothed_joints; + SkeltrackJointList trend_joints; + guint joints_persistency; + gfloat smoothing_factor; + guint joints_persistency_counter[SKELTRACK_JOINT_MAX_JOINTS]; +} SmoothData; + +void reset_joints_persistency_counter (SmoothData *smooth_data); + +void smooth_joints (SmoothData *data, + SkeltrackJointList new_joints); diff --git a/interface/external/skeltrack/include/skeltrack-util.h b/interface/external/skeltrack/include/skeltrack-util.h new file mode 100644 index 0000000000..540aee7f33 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack-util.h @@ -0,0 +1,127 @@ +/* + * skeltrack-util.h + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include +#include "skeltrack-joint.h" + +typedef struct _Label Label; +typedef struct _Node Node; + +struct _Label { + gint index; + Label *parent; + GList *nodes; + Node *bridge_node; + Node *to_node; + gint lower_screen_y; + gint higher_z; + gint lower_z; + gdouble normalized_num_nodes; +}; + +struct _Node { + gint i; + gint j; + gint x; + gint y; + gint z; + GList *neighbors; + GList *linked_nodes; + Label *label; +}; + +Node * get_closest_node_to_joint (GList *extremas, + SkeltrackJoint *joint, + gint *distance); + +Node * get_closest_node (GList *node_list, Node *from); + +Node * get_closest_torso_node (GList *node_list, + Node *from, + Node *head); + +Label * get_main_component (GList *node_list, + Node *from, + gdouble min_normalized_nr_nodes); + +Label * label_find (Label *label); + +void label_union (Label *a, Label *b); + +gint get_distance (Node *a, Node *b); + +void free_label (Label *label); + +void clean_labels (GList *labels); + +void free_node (Node *node, + gboolean unlink_node_first); + +void clean_nodes (GList *nodes); + +GList * remove_nodes_with_label (GList *nodes, + Node **node_matrix, + gint width, + Label *label); + +Label * get_lowest_index_label (Label **neighbor_labels); + +Label * new_label (gint index); + +void join_components_to_main (GList *nodes, + Label *lowest_component_label, + guint horizontal_max_distance, + guint depth_max_distance, + guint graph_distance_threshold); + +void set_joint_from_node (SkeltrackJointList *joints, + Node *node, + SkeltrackJointId id, + gint dimension_reduction); + +gint * create_new_dist_matrix (gint matrix_size); + +gboolean dijkstra_to (GList *nodes, + Node *source, + Node *target, + gint width, + gint height, + gint *distances, + Node **previous); + +void convert_screen_coords_to_mm (guint width, + guint height, + guint dimension_reduction, + guint i, + guint j, + gint z, + gint *x, + gint *y); + +void convert_mm_to_screen_coords (guint width, + guint height, + guint dimension_reduction, + gint x, + gint y, + gint z, + guint *i, + guint *j); diff --git a/interface/external/skeltrack/include/skeltrack.h b/interface/external/skeltrack/include/skeltrack.h new file mode 100644 index 0000000000..8e1c572303 --- /dev/null +++ b/interface/external/skeltrack/include/skeltrack.h @@ -0,0 +1,28 @@ +/* + * skeltrack.h + * + * skeltrack - A GObject wrapper of the libfreenect library + * Copyright (C) 2011 Igalia S.L. + * + * Authors: + * Joaquim Manuel Pereira Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 3, or (at your option) any later version as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#ifndef __SKELTRACK_H__ +#define __SKELTRACK_H__ + +#include + +#endif /* __SKELTRACK_H__ */ diff --git a/interface/external/skeltrack/lib/UNIX/libskeltrack.a b/interface/external/skeltrack/lib/UNIX/libskeltrack.a new file mode 100644 index 0000000000000000000000000000000000000000..f34c83224bfaeed07d09a6a02e47e802ba075ea5 GIT binary patch literal 53760 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 literal 0 HcmV?d00001 diff --git a/interface/external/skeltrack/src/skeltrack-joint.c b/interface/external/skeltrack/src/skeltrack-joint.c new file mode 100644 index 0000000000..6310c6a789 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-joint.c @@ -0,0 +1,159 @@ +/* + * skeltrack-joint.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +/** + * SECTION:skeltrack-joint + * @short_description: Data structure that holds information about + * a skeleton joint. + * + * A #SkeltrackJoint is built automatically by #SkeltrackSkeleton when + * it finds a skeleton joint and can be used to get information about it. + * Each #SkeltrackJoint holds an id, given by #SkeltrackJointId that indicates + * which of the human skeleton joints it represents. + * + * Spacial information about a joint is given by the @x, @y and @z coordinates. + * To represent the joint in a 2D, the variables @screen_x and + * @screen_y will indicate the joint's position in the screen and are calculated + * taking into account the #SkeltrackSkeleton:dimension-reduction (it will + * be multiplied by this value). + * + * The tracked list of joints is represented by #SkeltrackJointList and given + * by skeltrack_skeleton_track_joints_finish(). + * To get a #SkeltrackJoint from a #SkeltrackJointList object, use the + * skeltrack_joint_list_get_joint() indicating the needed #SkeltrackJointId. + * + * A #SkeltrackJointList can be freed by using skeltrack_joint_list_free(). + * A #SkeltrackJoint can be copied by skeltrack_joint_copy() and freed by + * skeltrack_joint_free(). + **/ + +#include +#include "skeltrack-joint.h" + +/** + * skeltrack_joint_get_type: + * + * Returns: The registered #GType for #SkeltrackJoint boxed type + **/ +GType +skeltrack_joint_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + type = g_boxed_type_register_static ("SkeltrackJoint", + (GBoxedCopyFunc) skeltrack_joint_copy, + (GBoxedFreeFunc) skeltrack_joint_free); + return type; +} + +/** + * skeltrack_joint_copy: + * @joint: The #SkeltrackJoint to copy + * + * Makes an exact copy of a #SkeltrackJoint object. + * + * Returns: (transfer full): A newly created #SkeltrackJoint. Use + * skeltrack_joint_free() to free it. + **/ +gpointer +skeltrack_joint_copy (SkeltrackJoint *joint) +{ + SkeltrackJoint *new_joint; + + if (joint == NULL) + return NULL; + + new_joint = g_slice_new0 (SkeltrackJoint); + memcpy (new_joint, joint, sizeof (SkeltrackJoint)); + + return new_joint; +} + +/** + * skeltrack_joint_free: + * @joint: The #SkeltrackJoint to free + * + * Frees a #SkeltrackJoint object. + **/ +void +skeltrack_joint_free (SkeltrackJoint *joint) +{ + g_slice_free (SkeltrackJoint, joint); +} + + +/** + * skeltrack_joint_list_free: + * @list: The #SkeltrackJointList to free + * + * Frees a #SkeltrackJointList object and each #SkeltrackJoint + * in it. + **/ +void +skeltrack_joint_list_free (SkeltrackJointList list) +{ + gint i; + + if (list == NULL) + return; + + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + g_slice_free (SkeltrackJoint, list[i]); + } + g_slice_free1 (SKELTRACK_JOINT_MAX_JOINTS * sizeof (SkeltrackJoint *), list); +} + +/** + * skeltrack_joint_list_get_joint: + * @list: The #SkeltrackJointList + * @id: The #SkeltrackJointId of the joint to get + * + * Gets a joint from a list of skeleton joints. The joint + * returned needs to be freed by using skeltrack_joint_free() or, + * alternatively, the whole list and its joints can be freed by using + * skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJoint that corresponds to + * the given @id or %NULL if that joint wasn't found. + **/ +SkeltrackJoint * +skeltrack_joint_list_get_joint (SkeltrackJointList list, SkeltrackJointId id) +{ + return list[id]; +} + +/** + * skeltrack_joint_list_new: + * + * Created a new list of #SkeltrackJointsList with its joints as #NULL. + * When it is no longer needed, free it with skeltrack_joint_list_free(). + * + * Returns: (transfer full): A newly allocated #SkeltrackJointList + **/ +SkeltrackJointList +skeltrack_joint_list_new (void) +{ + return (SkeltrackJointList) g_slice_alloc0 (SKELTRACK_JOINT_MAX_JOINTS * + sizeof (SkeltrackJoint *)); +} diff --git a/interface/external/skeltrack/src/skeltrack-skeleton.c b/interface/external/skeltrack/src/skeltrack-skeleton.c new file mode 100644 index 0000000000..9373af56a8 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-skeleton.c @@ -0,0 +1,2027 @@ +/* + * skeltrack-skeleton.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +/** + * SECTION:skeltrack-skeleton + * @short_description: Object that tracks the joints in a human skeleton + * + * This object tries to detect joints of the human skeleton. + * + * To track the joints, first create an instance of #SkeltrackSkeleton using + * skeltrack_skeleton_new() and then set a buffer from where the joints will + * be retrieved using the asynchronous function + * skeltrack_skeleton_track_joints() and get the list of joints using + * skeltrack_skeleton_track_joints_finish(). + * + * A common use case is to use this library together with a Kinect device so + * an easy way to retrieve the needed buffer is to use the GFreenect library. + * + * It currently tracks the joints identified by #SkeltrackJointId . + * + * Tracking the skeleton joints can be computational heavy so it is advised that + * the given buffer's dimension is reduced before setting it. To do it, + * simply choose the reduction factor and loop through the original buffer + * (using this factor as a step) and set the reduced buffer's values accordingly. + * The #SkeltrackSkeleton:dimension-reduction property holds this reduction + * value and should be changed to the reduction factor used (alternatively you + * can retrieve its default value and use it in the reduction, if it fits your + * needs). + * + * The skeleton tracking uses a few heuristics that proved to work well for + * tested cases but they can be tweaked by changing the following properties: + * #SkeltrackSkeleton:graph-distance-threshold , + * #SkeltrackSkeleton:graph-minimum-number-nodes , + * #SkeltrackSkeleton:hands-minimum-distance , + * #SkeltrackSkeleton:shoulders-arc-start-point , + * #SkeltrackSkeleton:shoulders-arc-length , + * #SkeltrackSkeleton:shoulders-circumference-radius , + * #SkeltrackSkeleton:shoulders-search-step . + **/ +#include +#include +#include + +#include "skeltrack-skeleton.h" +#include "skeltrack-smooth.h" +#include "skeltrack-util.h" + +#define SKELTRACK_SKELETON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ + SKELTRACK_TYPE_SKELETON, \ + SkeltrackSkeletonPrivate)) + +#define DIMENSION_REDUCTION 16 +#define GRAPH_DISTANCE_THRESHOLD 150 +#define GRAPH_MINIMUM_NUMBER_OF_NODES 5 +#define HANDS_MINIMUM_DISTANCE 550 +#define SHOULDERS_CIRCUMFERENCE_RADIUS 300 +#define SHOULDERS_ARC_START_POINT 100 +#define SHOULDERS_ARC_LENGTH 250 +#define SHOULDERS_SEARCH_STEP 0.05 +#define JOINTS_PERSISTENCY_DEFAULT 3 +#define SMOOTHING_FACTOR_DEFAULT .5 +#define ENABLE_SMOOTHING_DEFAULT TRUE +#define DEFAULT_FOCUS_POINT_Z 1000 +#define TORSO_MINIMUM_NUMBER_NODES_DEFAULT 16.0 +#define EXTREMA_SPHERE_RADIUS 300 + +/* private data */ +struct _SkeltrackSkeletonPrivate +{ + guint16 *buffer; + guint buffer_width; + guint buffer_height; + + GAsyncResult *track_joints_result; + GMutex track_joints_mutex; + + GList *graph; + GList *labels; + Node **node_matrix; + gint *distances_matrix; + GList *main_component; + + guint16 dimension_reduction; + guint16 distance_threshold; + guint16 min_nr_nodes; + + guint16 hands_minimum_distance; + + guint16 shoulders_circumference_radius; + guint16 shoulders_arc_start_point; + guint16 shoulders_arc_length; + gfloat shoulders_search_step; + + guint16 extrema_sphere_radius; + + Node *focus_node; + + gboolean enable_smoothing; + SmoothData smooth_data; + + gfloat torso_minimum_number_nodes; + + SkeltrackJoint *previous_head; +}; + +/* Currently searches for head and hands */ +static const guint NR_EXTREMAS_TO_SEARCH = 3; + +/* properties */ +enum + { + PROP_0, + PROP_DIMENSION_REDUCTION, + PROP_GRAPH_DISTANCE_THRESHOLD, + PROP_GRAPH_MIN_NR_NODES, + PROP_HANDS_MINIMUM_DISTANCE, + PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, + PROP_SHOULDERS_ARC_START_POINT, + PROP_SHOULDERS_ARC_LENGTH, + PROP_SHOULDERS_SEARCH_STEP, + PROP_EXTREMA_SPHERE_RADIUS, + PROP_SMOOTHING_FACTOR, + PROP_JOINTS_PERSISTENCY, + PROP_ENABLE_SMOOTHING, + PROP_TORSO_MINIMUM_NUMBER_NODES + }; + + +static void skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class); +static void skeltrack_skeleton_init (SkeltrackSkeleton *self); +static void skeltrack_skeleton_finalize (GObject *obj); +static void skeltrack_skeleton_dispose (GObject *obj); + +static void skeltrack_skeleton_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void skeltrack_skeleton_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec); + + +static void clean_tracking_resources (SkeltrackSkeleton *self); + +G_DEFINE_TYPE (SkeltrackSkeleton, skeltrack_skeleton, G_TYPE_OBJECT) + +static void +skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class) +{ + GObjectClass *obj_class; + + obj_class = G_OBJECT_CLASS (class); + + obj_class->dispose = skeltrack_skeleton_dispose; + obj_class->finalize = skeltrack_skeleton_finalize; + obj_class->get_property = skeltrack_skeleton_get_property; + obj_class->set_property = skeltrack_skeleton_set_property; + + /* install properties */ + + /** + * SkeltrackSkeleton:dimension-reduction + * + * The value by which the dimension of the buffer was reduced + * (in case it was). + **/ + g_object_class_install_property (obj_class, + PROP_DIMENSION_REDUCTION, + g_param_spec_uint ("dimension-reduction", + "Dimension reduction", + "The dimension reduction value", + 1, + 1024, + DIMENSION_REDUCTION, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:graph-distance-threshold + * + * The value (in mm) for the distance threshold between each node and its + * neighbors. This means that a node in the graph will only be connected + * to another if they aren't farther apart then this value. + **/ + g_object_class_install_property (obj_class, + PROP_GRAPH_DISTANCE_THRESHOLD, + g_param_spec_uint ("graph-distance-threshold", + "Graph's distance threshold", + "The distance threshold between " + "each node.", + 1, + G_MAXUINT16, + GRAPH_DISTANCE_THRESHOLD, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:graph-minimum-number-nodes + * + * The minimum number of nodes each of the graph's components + * should have (when it is not fully connected). + **/ + g_object_class_install_property (obj_class, + PROP_GRAPH_MIN_NR_NODES, + g_param_spec_uint ("graph-minimum-number-nodes", + "Graph's minimum number of nodes", + "The minimum number of nodes " + "of the graph's components ", + 1, + G_MAXUINT16, + GRAPH_MINIMUM_NUMBER_OF_NODES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:hands-minimum-distance + * + * The minimum distance (in mm) that each hand should be from its + * respective shoulder. + **/ + g_object_class_install_property (obj_class, + PROP_HANDS_MINIMUM_DISTANCE, + g_param_spec_uint ("hands-minimum-distance", + "Hands' minimum distance from the " + "shoulders", + "The minimum distance (in mm) that " + "each hand should be from its " + "respective shoulder.", + 300, + G_MAXUINT, + HANDS_MINIMUM_DISTANCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-circumference-radius + * + * The radius of the circumference (in mm) from the head with which + * to look for the shoulders. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, + g_param_spec_uint ("shoulders-circumference-radius", + "Shoulders' circumference radius", + "The radius of the circumference " + "(in mm) from the head with which " + "to look for the shoulders.", + 1, + G_MAXUINT16, + SHOULDERS_CIRCUMFERENCE_RADIUS, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-arc-start-point + * + * The starting point (in mm) of the arc (from the bottom of the + * shoulders' circumference) where the shoulders will be searched for. + * This point is used together with the + * SkeltrackSkeleton::shoulders-arc-length to determine the arc + * where the shoulders' points will be looked for. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_ARC_START_POINT, + g_param_spec_uint ("shoulders-arc-start-point", + "Shoulders' arc start point", + "The starting point (in mm) of the " + "arc from the bottom of the " + "shoulders' circumference where " + "the shoulders will be searched for.", + 1, + G_MAXUINT16, + SHOULDERS_ARC_START_POINT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:shoulders-arc-length + * + * The length (in mm) of the arc where the shoulders will be searched. + * This length is used together with the + * SkeltrackSkeleton::shoulders-arc-start-point to determine the arc + * where the shoulders' points will be looked for. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_ARC_LENGTH, + g_param_spec_uint ("shoulders-arc-length", + "Shoulders' arc length", + "The length (in mm) of the arc " + "where the shoulders will be " + "searched.", + 1, + G_MAXUINT16, + SHOULDERS_ARC_LENGTH, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + + /** + * SkeltrackSkeleton:shoulders-search-step + * + * The step considered for sampling the shoulders' circumference + * when searching for the shoulders. + **/ + g_object_class_install_property (obj_class, + PROP_SHOULDERS_SEARCH_STEP, + g_param_spec_float ("shoulders-search-step", + "Shoulders' search step", + "The step considered for sampling " + "the shoulders' circumference " + "when searching for the shoulders.", + .01, + M_PI, + .01, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:smoothing-factor + * + * The factor by which the joints should be smoothed. This refers to + * Holt's Double Exponential Smoothing and determines how the current and + * previous data and trend will be used. A value closer to 0 will produce smoother + * results but increases latency. + **/ + g_object_class_install_property (obj_class, + PROP_SMOOTHING_FACTOR, + g_param_spec_float ("smoothing-factor", + "Smoothing factor", + "The factor by which the joints values" + "should be smoothed.", + .0, + 1.0, + .5, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:joints-persistency + * + * The number of times that a joint can be null until its previous + * value is discarded. For example, if this property is 3, the last value for + * a joint will keep being used until the new value for this joint is null for + * 3 consecutive times. + * + **/ + g_object_class_install_property (obj_class, + PROP_JOINTS_PERSISTENCY, + g_param_spec_uint ("joints-persistency", + "Joints persistency", + "The number of times that a joint " + "can be null until its previous " + "value is discarded", + 0, + G_MAXUINT16, + JOINTS_PERSISTENCY_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:enable-smoothing + * + * Whether smoothing the joints should be applied or not. + * + **/ + g_object_class_install_property (obj_class, + PROP_ENABLE_SMOOTHING, + g_param_spec_boolean ("enable-smoothing", + "Enable smoothing", + "Whether smoothing should be " + "applied or not", + ENABLE_SMOOTHING_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:torso-minimum-number-nodes + * + * Minimum number of nodes for a component to be considered torso. + * + **/ + g_object_class_install_property (obj_class, + PROP_TORSO_MINIMUM_NUMBER_NODES, + g_param_spec_float ("torso-minimum-number-nodes", + "Torso minimum number of nodes", + "Minimum number of nodes for a " + "component to be considered " + "torso", + 0, + G_MAXUINT16, + TORSO_MINIMUM_NUMBER_NODES_DEFAULT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * SkeltrackSkeleton:extrema-sphere-radius + * + * The radius of the sphere around the extremas (in mm). + * + * Points inside this sphere are considered for calculating the average position + * of the extrema. If the value is 0, no averaging is done. + **/ + g_object_class_install_property (obj_class, + PROP_EXTREMA_SPHERE_RADIUS, + g_param_spec_uint ("extrema-sphere-radius", + "Extrema sphere radius", + "The radius of the sphere around " + "the extremas (in mm).", + 0, + G_MAXUINT16, + EXTREMA_SPHERE_RADIUS, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + + /* add private structure */ + g_type_class_add_private (obj_class, sizeof (SkeltrackSkeletonPrivate)); +} + +static void +skeltrack_skeleton_init (SkeltrackSkeleton *self) +{ + guint i; + SkeltrackSkeletonPrivate *priv; + + priv = SKELTRACK_SKELETON_GET_PRIVATE (self); + self->priv = priv; + + priv->buffer = NULL; + priv->buffer_width = 0; + priv->buffer_height = 0; + + priv->graph = NULL; + priv->labels = NULL; + priv->main_component = NULL; + priv->node_matrix = NULL; + priv->distances_matrix = NULL; + + priv->dimension_reduction = DIMENSION_REDUCTION; + priv->distance_threshold = GRAPH_DISTANCE_THRESHOLD; + + priv->min_nr_nodes = GRAPH_MINIMUM_NUMBER_OF_NODES; + + priv->hands_minimum_distance = HANDS_MINIMUM_DISTANCE; + + priv->shoulders_circumference_radius = SHOULDERS_CIRCUMFERENCE_RADIUS; + priv->shoulders_arc_start_point = SHOULDERS_ARC_START_POINT; + priv->shoulders_arc_length = SHOULDERS_ARC_LENGTH; + priv->shoulders_search_step = SHOULDERS_SEARCH_STEP; + + priv->extrema_sphere_radius = EXTREMA_SPHERE_RADIUS; + + priv->focus_node = g_slice_new0 (Node); + priv->focus_node->x = 0; + priv->focus_node->y = 0; + priv->focus_node->z = DEFAULT_FOCUS_POINT_Z; + + priv->track_joints_result = NULL; + + g_mutex_init (&priv->track_joints_mutex); + + priv->enable_smoothing = ENABLE_SMOOTHING_DEFAULT; + priv->smooth_data.smoothing_factor = SMOOTHING_FACTOR_DEFAULT; + priv->smooth_data.smoothed_joints = NULL; + priv->smooth_data.trend_joints = NULL; + priv->smooth_data.joints_persistency = JOINTS_PERSISTENCY_DEFAULT; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + priv->smooth_data.joints_persistency_counter[i] = JOINTS_PERSISTENCY_DEFAULT; + + priv->torso_minimum_number_nodes = TORSO_MINIMUM_NUMBER_NODES_DEFAULT; + + priv->previous_head = NULL; +} + +static void +skeltrack_skeleton_dispose (GObject *obj) +{ + /* TODO: cancel any cancellable to interrupt joints tracking operation */ + + G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->dispose (obj); +} + +static void +skeltrack_skeleton_finalize (GObject *obj) +{ + SkeltrackSkeleton *self = SKELTRACK_SKELETON (obj); + + g_mutex_clear (&self->priv->track_joints_mutex); + + skeltrack_joint_list_free (self->priv->smooth_data.smoothed_joints); + skeltrack_joint_list_free (self->priv->smooth_data.trend_joints); + + skeltrack_joint_free (self->priv->previous_head); + + clean_tracking_resources (self); + + g_slice_free (Node, self->priv->focus_node); + + G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->finalize (obj); +} + +static void +skeltrack_skeleton_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SkeltrackSkeleton *self; + + self = SKELTRACK_SKELETON (obj); + + switch (prop_id) + { + case PROP_DIMENSION_REDUCTION: + self->priv->dimension_reduction = g_value_get_uint (value); + break; + + case PROP_GRAPH_DISTANCE_THRESHOLD: + self->priv->distance_threshold = g_value_get_uint (value); + break; + + case PROP_GRAPH_MIN_NR_NODES: + self->priv->min_nr_nodes = g_value_get_uint (value); + break; + + case PROP_HANDS_MINIMUM_DISTANCE: + self->priv->hands_minimum_distance = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: + self->priv->shoulders_circumference_radius = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_ARC_START_POINT: + self->priv->shoulders_arc_start_point = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_ARC_LENGTH: + self->priv->shoulders_arc_length = g_value_get_uint (value); + break; + + case PROP_SHOULDERS_SEARCH_STEP: + self->priv->shoulders_circumference_radius = g_value_get_float (value); + break; + + case PROP_EXTREMA_SPHERE_RADIUS: + self->priv->extrema_sphere_radius = g_value_get_uint (value); + break; + + case PROP_SMOOTHING_FACTOR: + self->priv->smooth_data.smoothing_factor = g_value_get_float (value); + break; + + case PROP_JOINTS_PERSISTENCY: + self->priv->smooth_data.joints_persistency = g_value_get_uint (value); + reset_joints_persistency_counter (&self->priv->smooth_data); + break; + + case PROP_ENABLE_SMOOTHING: + self->priv->enable_smoothing = g_value_get_boolean (value); + break; + + case PROP_TORSO_MINIMUM_NUMBER_NODES: + self->priv->torso_minimum_number_nodes = g_value_get_float (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +skeltrack_skeleton_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SkeltrackSkeleton *self; + + self = SKELTRACK_SKELETON (obj); + + switch (prop_id) + { + case PROP_DIMENSION_REDUCTION: + g_value_set_uint (value, self->priv->dimension_reduction); + break; + + case PROP_GRAPH_DISTANCE_THRESHOLD: + g_value_set_uint (value, self->priv->distance_threshold); + break; + + case PROP_GRAPH_MIN_NR_NODES: + g_value_set_uint (value, self->priv->min_nr_nodes); + break; + + case PROP_HANDS_MINIMUM_DISTANCE: + g_value_set_uint (value, self->priv->hands_minimum_distance); + break; + + case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: + g_value_set_uint (value, self->priv->shoulders_circumference_radius); + break; + + case PROP_SHOULDERS_ARC_START_POINT: + g_value_set_uint (value, self->priv->shoulders_arc_start_point); + break; + + case PROP_SHOULDERS_ARC_LENGTH: + g_value_set_uint (value, self->priv->shoulders_arc_length); + break; + + case PROP_SHOULDERS_SEARCH_STEP: + g_value_set_float (value, self->priv->shoulders_search_step); + break; + + case PROP_EXTREMA_SPHERE_RADIUS: + g_value_set_uint (value, self->priv->extrema_sphere_radius); + break; + + case PROP_SMOOTHING_FACTOR: + g_value_set_float (value, self->priv->smooth_data.smoothing_factor); + break; + + case PROP_JOINTS_PERSISTENCY: + g_value_set_uint (value, self->priv->smooth_data.joints_persistency); + break; + + case PROP_ENABLE_SMOOTHING: + g_value_set_boolean (value, self->priv->enable_smoothing); + break; + + case PROP_TORSO_MINIMUM_NUMBER_NODES: + g_value_set_float (value, self->priv->torso_minimum_number_nodes); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static gint +join_neighbor (SkeltrackSkeleton *self, + Node *node, + Label **neighbor_labels, + gint index, + gint i, + gint j) +{ + Node *neighbor; + if (i < 0 || i >= self->priv->buffer_width || + j < 0 || j >= self->priv->buffer_height) + { + return index; + } + + neighbor = self->priv->node_matrix[self->priv->buffer_width * j + i]; + if (neighbor != NULL) + { + gint distance; + distance = get_distance (neighbor, node); + if (distance < self->priv->distance_threshold) + { + neighbor->neighbors = g_list_append (neighbor->neighbors, + node); + node->neighbors = g_list_append (node->neighbors, + neighbor); + neighbor_labels[index] = neighbor->label; + index++; + } + } + return index; +} + +GList * +make_graph (SkeltrackSkeleton *self, GList **label_list) +{ + SkeltrackSkeletonPrivate *priv; + gint i, j, n; + Node *node; + GList *nodes = NULL; + GList *labels = NULL; + GList *current_label; + Label *main_component_label = NULL; + gint index = 0; + gint next_label = -1; + guint16 value; + guint16 *buffer; + gint width, height; + + buffer = self->priv->buffer; + width = self->priv->buffer_width; + height = self->priv->buffer_height; + + priv = self->priv; + + + if (priv->node_matrix == NULL) + { + priv->node_matrix = g_slice_alloc0 (width * height * sizeof (Node *)); + } + else + { + memset (self->priv->node_matrix, + 0, + width * height * sizeof (Node *)); + } + + for (i = 0; i < width; i++) + { + for (j = 0; j < height; j++) + { + gint south, north, west; + Label *lowest_index_label = NULL; + Label *neighbor_labels[4] = {NULL, NULL, NULL, NULL}; + + value = buffer[j * width + i]; + if (value == 0) + continue; + + node = g_slice_new0 (Node); + node->i = i; + node->j = j; + node->z = value; + convert_screen_coords_to_mm (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + i, j, + node->z, + &(node->x), + &(node->y)); + node->neighbors = NULL; + node->linked_nodes = NULL; + + index = 0; + + south = j + 1; + north = j - 1; + west = i - 1; + + /* West */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, j); + /* South West*/ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, south); + /* North */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + i, north); + + /* North West */ + index = join_neighbor (self, + node, + neighbor_labels, + index, + west, north); + + lowest_index_label = get_lowest_index_label (neighbor_labels); + + /* No neighbors */ + if (lowest_index_label == NULL) + { + Label *label; + next_label++; + label = new_label (next_label); + labels = g_list_append (labels, label); + lowest_index_label = label; + } + else + { + for (index = 0; index < 4; index++) + { + if (neighbor_labels[index] != NULL) + { + label_union (neighbor_labels[index], lowest_index_label); + } + } + } + + node->label = lowest_index_label; + nodes = g_list_append(nodes, node); + priv->node_matrix[width * node->j + node->i] = node; + } + } + + for (n = 0; n < g_list_length (nodes); n++) + { + Node *node = (Node *) g_list_nth_data (nodes, n); + node->label = label_find (node->label); + node->label->nodes = g_list_append (node->label->nodes, + node); + + /* Assign lower node so we can extract the + lower graph's component */ + if (node->label->lower_screen_y == -1 || + node->j > node->label->lower_screen_y) + { + node->label->lower_screen_y = node->j; + } + + /* Assign farther to the camera node so we + can extract the main graph component */ + if (node->label->higher_z == -1 || + node->z > node->label->higher_z) + { + node->label->higher_z = node->z; + } + + /* Assign closer to the camera node so we + can extract the main graph component */ + if (node->label->lower_z == -1 || + node->z < node->label->lower_z) + { + node->label->lower_z = node->z; + } + } + + for (current_label = g_list_first (labels); + current_label != NULL; + current_label = g_list_next (current_label)) + { + Label *label; + GList *current_nodes; + + label = (Label *) current_label->data; + current_nodes = label->nodes; + + label->normalized_num_nodes = g_list_length (current_nodes) * + ((label->higher_z - label->lower_z)/2 + + label->lower_z) * + (pow (DIMENSION_REDUCTION, 2)/2) / + 1000000; + } + + main_component_label = get_main_component (nodes, + priv->focus_node, + priv->torso_minimum_number_nodes); + + current_label = g_list_first (labels); + while (current_label != NULL) + { + Label *label; + label = (Label *) current_label->data; + + /* Remove label if number of nodes is less than + the minimum required */ + if (g_list_length (label->nodes) < priv->min_nr_nodes) + { + nodes = remove_nodes_with_label (nodes, + priv->node_matrix, + priv->buffer_width, + label); + + GList *link = current_label; + current_label = g_list_next (current_label); + labels = g_list_delete_link (labels, link); + free_label (label); + continue; + } + + current_label = g_list_next (current_label); + } + + if (main_component_label) + { + join_components_to_main (labels, + main_component_label, + priv->distance_threshold, + priv->hands_minimum_distance, + priv->distance_threshold); + + current_label = g_list_first (labels); + while (current_label != NULL) + { + Label *label; + label = (Label *) current_label->data; + if (label == main_component_label) + { + current_label = g_list_next (current_label); + continue; + } + + if (label->bridge_node == NULL) + { + nodes = remove_nodes_with_label (nodes, + priv->node_matrix, + priv->buffer_width, + label); + + GList *link = current_label; + current_label = g_list_next (current_label); + labels = g_list_delete_link (labels, link); + free_label (label); + continue; + } + + label->bridge_node->neighbors = + g_list_append (label->bridge_node->neighbors, label->to_node); + label->to_node->neighbors = g_list_append (label->to_node->neighbors, + label->bridge_node); + + current_label = g_list_next (current_label); + } + + priv->main_component = main_component_label->nodes; + } + + *label_list = labels; + + return nodes; +} + +Node * +get_centroid (SkeltrackSkeleton *self) +{ + gint avg_x = 0; + gint avg_y = 0; + gint avg_z = 0; + gint length; + GList *node_list; + Node *cent = NULL; + Node *centroid = NULL; + + if (self->priv->main_component == NULL) + return NULL; + + for (node_list = g_list_first (self->priv->main_component); + node_list != NULL; + node_list = g_list_next (node_list)) + { + Node *node; + node = (Node *) node_list->data; + avg_x += node->x; + avg_y += node->y; + avg_z += node->z; + } + + length = g_list_length (self->priv->main_component); + cent = g_slice_new0 (Node); + cent->x = avg_x / length; + cent->y = avg_y / length; + cent->z = avg_z / length; + cent->linked_nodes = NULL; + + centroid = get_closest_node (self->priv->graph, cent); + + g_slice_free (Node, cent); + + return centroid; +} + +static Node * +get_lowest (SkeltrackSkeleton *self, Node *centroid) +{ + Node *lowest = NULL; + /* @TODO: Use the node_matrix instead of the lowest + component to look for the lowest node as it's faster. */ + if (self->priv->main_component != NULL) + { + GList *node_list; + for (node_list = g_list_first (self->priv->main_component); + node_list != NULL; + node_list = g_list_next (node_list)) + { + Node *node; + node = (Node *) node_list->data; + if (node->i != centroid->i) + continue; + if (lowest == NULL || + lowest->j < node->j) + { + lowest = node; + } + } + } + return lowest; +} + +static Node * +get_longer_distance (SkeltrackSkeleton *self, gint *distances) +{ + GList *current; + Node *farthest_node; + + current = g_list_first (self->priv->graph); + farthest_node = (Node *) current->data; + current = g_list_next (current); + + while (current != NULL) + { + Node *node; + node = (Node *) current->data; + if (node != NULL && + (distances[farthest_node->j * + self->priv->buffer_width + + farthest_node->i] != -1 && + distances[farthest_node->j * + self->priv->buffer_width + + farthest_node->i] < distances[node->j * + self->priv->buffer_width + + node->i])) + { + farthest_node = node; + } + current = g_list_next (current); + } + return farthest_node; +} + +static void +set_average_extremas (SkeltrackSkeletonPrivate *priv, GList *extremas) +{ + GList *current_extrema, *averaged_extremas = NULL; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + GList *current_node; + Node *extrema, *node = NULL, *cent = NULL, *node_centroid = NULL; + gint avg_x = 0, avg_y = 0, avg_z = 0, length = 0; + + extrema = (Node *) current_extrema->data; + + for (current_node = g_list_first (priv->graph); + current_node != NULL; + current_node = g_list_next (current_node)) + { + node = (Node *) current_node->data; + + if ((get_distance (extrema, node) < + priv->extrema_sphere_radius)) + { + avg_x += node->x; + avg_y += node->y; + avg_z += node->z; + + length++; + } + } + + /* if the length is 1 then it is because no other + nodes were considered for the average */ + if (length > 1) + { + cent = g_slice_new0 (Node); + cent->x = avg_x / length; + cent->y = avg_y / length; + cent->z = avg_z / length; + cent->linked_nodes = NULL; + + node_centroid = get_closest_node (priv->graph, cent); + + /* If the new averaged extrema is not already an extrema + set it for addition */ + if (g_list_find (averaged_extremas, node_centroid) == NULL && + g_list_find (extremas, node_centroid) == NULL) + { + current_extrema->data = node_centroid; + } + + g_slice_free (Node, cent); + } + } +} + +static GList * +get_extremas (SkeltrackSkeleton *self, Node *centroid) +{ + SkeltrackSkeletonPrivate *priv; + gint i, nr_nodes, matrix_size; + Node *lowest, *source, *node; + GList *extremas = NULL; + + priv = self->priv; + lowest = get_lowest (self, centroid); + source = lowest; + + matrix_size = priv->buffer_width * priv->buffer_height; + if (priv->distances_matrix == NULL) + { + priv->distances_matrix = g_slice_alloc0 (matrix_size * sizeof (gint)); + } + + for (i = 0; i < matrix_size; i++) + { + priv->distances_matrix[i] = -1; + } + + for (nr_nodes = NR_EXTREMAS_TO_SEARCH; + source != NULL && nr_nodes > 0; + nr_nodes--) + { + dijkstra_to (priv->graph, + source, + NULL, + priv->buffer_width, + priv->buffer_height, + priv->distances_matrix, + NULL); + + node = get_longer_distance (self, priv->distances_matrix); + + if (node == NULL) + continue; + + if (node != source) + { + priv->distances_matrix[node->j * priv->buffer_width + node->i] = 0; + source->linked_nodes = g_list_append (source->linked_nodes, node); + node->linked_nodes = g_list_append (node->linked_nodes, source); + source = node; + extremas = g_list_append (extremas, node); + } + } + + if (self->priv->extrema_sphere_radius != 0) + { + set_average_extremas (priv, extremas); + } + + return extremas; +} + +static Node * +get_shoulder_node (SkeltrackSkeletonPrivate *priv, + gfloat alpha, + gfloat step, + gint x_node, + gint y_node, + gint z_centroid) +{ + guint radius, arc_start_point, arc_length, current_i, current_j; + gfloat start_angle, last_node_arc, current_arc, angle, current_x, current_y; + Node *current_node = NULL; + Node *last_node = NULL; + + radius = priv->shoulders_circumference_radius; + arc_start_point = priv->shoulders_arc_start_point; + arc_length = priv->shoulders_arc_length; + + start_angle = M_PI_2; + + angle = start_angle + alpha; + current_x = x_node + radius * cos (angle); + current_y = y_node + radius * sin (angle); + current_arc = 0; + last_node_arc = 0; + current_node = NULL; + last_node = NULL; + + while (current_arc <= (arc_start_point + arc_length)) + { + convert_mm_to_screen_coords (priv->buffer_width, + priv->buffer_height, + priv->dimension_reduction, + current_x, + current_y, + z_centroid, + ¤t_i, + ¤t_j); + + if (current_i >= priv->buffer_width || current_j >= priv->buffer_height) + break; + + current_node = priv->node_matrix[current_j * priv->buffer_width + + current_i]; + + if (current_node != NULL) + { + last_node = current_node; + last_node_arc = current_arc; + } + + angle += step; + current_x = x_node + radius * cos (angle); + current_y = y_node + radius * sin (angle); + current_arc = ABS (angle - start_angle) * radius; + } + + if (last_node_arc < arc_start_point) + return NULL; + + return last_node; +} + +static gboolean +check_if_node_can_be_head (SkeltrackSkeleton *self, + Node *node, + Node *centroid, + Node **left_shoulder, + Node **right_shoulder) +{ + gfloat alpha; + Node *found_right_shoulder = NULL, *found_left_shoulder = NULL; + + SkeltrackSkeletonPrivate *priv; + + *left_shoulder = NULL; + *right_shoulder = NULL; + + priv = self->priv; + + if (node->j > centroid->j) + return FALSE; + + if ((node->y - centroid->y) != 0) + alpha = atan( ABS (node->x - centroid->x) / ABS (node->y - centroid->y)); + else + return FALSE; + + /* too much tilt, cannot be the head */ + if (alpha >= M_PI_4) + return FALSE; + + if (node->x < centroid->x) + alpha = -alpha; + + found_right_shoulder = get_shoulder_node (priv, + alpha, + priv->shoulders_search_step, + node->x, + node->y, + centroid->z); + if (found_right_shoulder == NULL) + return FALSE; + + found_left_shoulder = get_shoulder_node (priv, + alpha, + -priv->shoulders_search_step, + node->x, + node->y, + centroid->z); + + if (found_left_shoulder == NULL) + return FALSE; + + *right_shoulder = found_right_shoulder; + *left_shoulder = found_left_shoulder; + + return TRUE; +} + +static gboolean +get_head_and_shoulders (SkeltrackSkeleton *self, + GList *extremas, + Node *centroid, + Node **head, + Node **left_shoulder, + Node **right_shoulder) +{ + Node *node; + GList *current_extrema; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + node = (Node *) current_extrema->data; + + if (check_if_node_can_be_head (self, + node, + centroid, + left_shoulder, + right_shoulder)) + { + *head = node; + return TRUE; + } + } + return FALSE; +} + +static void +identify_arm_extrema (gint *distances, + Node **previous_nodes, + gint width, + gint hand_distance, + Node *extrema, + Node **elbow_extrema, + Node **hand_extrema) +{ + gint total_dist; + + if (extrema == NULL) + return; + + total_dist = distances[width * extrema->j + extrema->i]; + if (total_dist < hand_distance) + { + *elbow_extrema = extrema; + *hand_extrema = NULL; + } + else + { + Node *previous; + gint elbow_dist; + + previous = previous_nodes[extrema->j * width + extrema->i]; + elbow_dist = total_dist / 2; + while (previous && + distances[previous->j * width + previous->i] > elbow_dist) + { + previous = previous_nodes[previous->j * width + previous->i]; + } + *elbow_extrema = previous; + *hand_extrema = extrema; + } +} + +static void +set_left_and_right_from_extremas (SkeltrackSkeleton *self, + GList *extremas, + Node *head, + Node *left_shoulder, + Node *right_shoulder, + SkeltrackJointList *joints) +{ + gint *dist_left_a = NULL; + gint *dist_left_b = NULL; + gint *dist_right_a = NULL; + gint *dist_right_b = NULL; + gint total_dist_left_a = -1; + gint total_dist_right_a = -1; + gint total_dist_left_b = -1; + gint total_dist_right_b = -1; + gint *distances_left[2] = {NULL, NULL}; + gint *distances_right[2] = {NULL, NULL}; + gint index_left = -1; + gint index_right = -1; + Node *elbow_extrema, *hand_extrema; + Node **previous_left_a = NULL; + Node **previous_left_b = NULL; + Node **previous_right_a = NULL; + Node **previous_right_b = NULL; + Node **previous_left[2] = {NULL, NULL}; + Node **previous_right[2] = {NULL, NULL}; + Node *ext_a = NULL; + Node *ext_b = NULL; + Node *left_extrema[2] = {NULL, NULL}; + Node *right_extrema[2] = {NULL, NULL}; + GList *current_extrema; + gint width, height, matrix_size; + + for (current_extrema = g_list_first (extremas); + current_extrema != NULL; + current_extrema = g_list_next (current_extrema)) + { + Node *node; + node = (Node *) current_extrema->data; + if (node != head) + { + if (ext_a == NULL) + ext_a = node; + else + ext_b = node; + } + } + + if (head == NULL) + return; + + width = self->priv->buffer_width; + height = self->priv->buffer_height; + matrix_size = width * height; + + previous_left_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_left_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_right_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); + previous_right_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); + + dist_left_a = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + left_shoulder, + ext_a, + width, + height, + dist_left_a, + previous_left_a); + + dist_left_b = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + left_shoulder, + ext_b, + width, + height, + dist_left_b, + previous_left_b); + + dist_right_a = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + right_shoulder, + ext_a, + width, + height, + dist_right_a, previous_right_a); + + dist_right_b = create_new_dist_matrix(matrix_size); + dijkstra_to (self->priv->graph, + right_shoulder, + ext_b, + width, + height, + dist_right_b, + previous_right_b); + + total_dist_left_a = dist_left_a[ext_a->j * width + ext_a->i]; + total_dist_right_a = dist_right_a[ext_a->j * width + ext_a->i]; + total_dist_left_b = dist_left_b[ext_b->j * width + ext_b->i]; + total_dist_right_b = dist_right_b[ext_b->j * width + ext_b->i]; + + if (total_dist_left_a < total_dist_right_a) + { + index_left++; + left_extrema[index_left] = ext_a; + distances_left[index_left] = dist_left_a; + previous_left[index_left] = previous_left_a; + } + else + { + index_right++; + right_extrema[index_right] = ext_a; + distances_right[index_right] = dist_right_a; + previous_right[index_right] = previous_right_a; + } + + if (total_dist_left_b < total_dist_right_b) + { + index_left++; + left_extrema[index_left] = ext_b; + distances_left[index_left] = dist_left_b; + previous_left[index_left] = previous_left_b; + } + else + { + index_right++; + right_extrema[index_right] = ext_b; + distances_right[index_right] = dist_right_b; + previous_right[index_right] = previous_right_b; + } + + elbow_extrema = NULL; + hand_extrema = NULL; + identify_arm_extrema (distances_left[0], + previous_left[0], + width, + self->priv->hands_minimum_distance, + left_extrema[0], + &elbow_extrema, + &hand_extrema); + + /* Two left extremas */ + if (index_left == 1) + { + if (hand_extrema == NULL) + { + hand_extrema = left_extrema[1]; + elbow_extrema = left_extrema[0]; + } + else + { + hand_extrema = left_extrema[0]; + elbow_extrema = left_extrema[1]; + } + } + + set_joint_from_node (joints, + elbow_extrema, + SKELTRACK_JOINT_ID_LEFT_ELBOW, + self->priv->dimension_reduction); + set_joint_from_node (joints, + hand_extrema, + SKELTRACK_JOINT_ID_LEFT_HAND, + self->priv->dimension_reduction); + + + elbow_extrema = NULL; + hand_extrema = NULL; + identify_arm_extrema (distances_right[0], + previous_right[0], + width, + self->priv->hands_minimum_distance, + right_extrema[0], + &elbow_extrema, + &hand_extrema); + + /* Two right extremas */ + if (index_right == 1) + { + if (hand_extrema == NULL) + { + hand_extrema = right_extrema[1]; + elbow_extrema = right_extrema[0]; + } + else + { + hand_extrema = right_extrema[0]; + elbow_extrema = right_extrema[1]; + } + } + + set_joint_from_node (joints, + elbow_extrema, + SKELTRACK_JOINT_ID_RIGHT_ELBOW, + self->priv->dimension_reduction); + set_joint_from_node (joints, + hand_extrema, + SKELTRACK_JOINT_ID_RIGHT_HAND, + self->priv->dimension_reduction); + + g_slice_free1 (matrix_size * sizeof (Node *), previous_left_a); + g_slice_free1 (matrix_size * sizeof (Node *), previous_left_b); + g_slice_free1 (matrix_size * sizeof (Node *), previous_right_a); + g_slice_free1 (matrix_size * sizeof (Node *), previous_right_b); + + g_slice_free1 (matrix_size * sizeof (gint), dist_left_a); + g_slice_free1 (matrix_size * sizeof (gint), dist_left_b); + g_slice_free1 (matrix_size * sizeof (gint), dist_right_a); + g_slice_free1 (matrix_size * sizeof (gint), dist_right_b); +} + +static Node * +get_adjusted_shoulder (guint buffer_width, + guint buffer_height, + guint dimension_reduction, + GList *graph, + Node *centroid, + Node *head, + Node *shoulder) +{ + Node *virtual_shoulder, *adjusted_shoulder = NULL; + virtual_shoulder = g_slice_new (Node); + virtual_shoulder->x = shoulder->x; + virtual_shoulder->y = shoulder->y; + virtual_shoulder->z = centroid->z; + + convert_mm_to_screen_coords (buffer_width, + buffer_height, + dimension_reduction, + virtual_shoulder->x, + virtual_shoulder->y, + virtual_shoulder->z, + (guint *) &virtual_shoulder->i, + (guint *) &virtual_shoulder->j); + + adjusted_shoulder = get_closest_torso_node (graph, + virtual_shoulder, + head); + g_slice_free (Node, virtual_shoulder); + + return adjusted_shoulder; +} + +static SkeltrackJoint ** +track_joints (SkeltrackSkeleton *self) +{ + Node * centroid; + Node *head = NULL; + Node *right_shoulder = NULL; + Node *left_shoulder = NULL; + GList *extremas; + SkeltrackJointList joints = NULL; + SkeltrackJointList smoothed = NULL; + + self->priv->graph = make_graph (self, &self->priv->labels); + centroid = get_centroid (self); + extremas = get_extremas (self, centroid); + + if (g_list_length (extremas) > 2) + { + if (self->priv->previous_head) + { + gint distance; + gboolean can_be_head = FALSE; + head = get_closest_node_to_joint (extremas, + self->priv->previous_head, + &distance); + if (head != NULL && + distance < GRAPH_DISTANCE_THRESHOLD) + { + can_be_head = check_if_node_can_be_head (self, + head, + centroid, + &left_shoulder, + &right_shoulder); + } + + if (!can_be_head) + head = NULL; + } + + if (head == NULL) + { + get_head_and_shoulders (self, + extremas, + centroid, + &head, + &left_shoulder, + &right_shoulder); + } + + if (joints == NULL) + joints = skeltrack_joint_list_new (); + + set_joint_from_node (&joints, + head, + SKELTRACK_JOINT_ID_HEAD, + self->priv->dimension_reduction); + + if (left_shoulder && head && head->z > left_shoulder->z) + { + Node *adjusted_shoulder; + adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + self->priv->graph, + centroid, + head, + left_shoulder); + + if (adjusted_shoulder) + left_shoulder = adjusted_shoulder; + } + + set_joint_from_node (&joints, + left_shoulder, + SKELTRACK_JOINT_ID_LEFT_SHOULDER, + self->priv->dimension_reduction); + + if (right_shoulder && head && head->z > right_shoulder->z) + { + Node *adjusted_shoulder; + adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, + self->priv->buffer_height, + self->priv->dimension_reduction, + self->priv->graph, + centroid, + head, + right_shoulder); + + if (adjusted_shoulder) + right_shoulder = adjusted_shoulder; + } + set_joint_from_node (&joints, + right_shoulder, + SKELTRACK_JOINT_ID_RIGHT_SHOULDER, + self->priv->dimension_reduction); + + set_left_and_right_from_extremas (self, + extremas, + head, + left_shoulder, + right_shoulder, + &joints); + } + + self->priv->buffer = NULL; + + self->priv->main_component = NULL; + + clean_nodes (self->priv->graph); + g_list_free (self->priv->graph); + self->priv->graph = NULL; + + clean_labels (self->priv->labels); + g_list_free (self->priv->labels); + self->priv->labels = NULL; + + if (self->priv->enable_smoothing) + { + smooth_joints (&self->priv->smooth_data, joints); + + if (self->priv->smooth_data.smoothed_joints != NULL) + { + guint i; + smoothed = skeltrack_joint_list_new (); + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *smoothed_joint, *smooth, *trend; + smoothed_joint = NULL; + smooth = self->priv->smooth_data.smoothed_joints[i]; + if (smooth != NULL) + { + if (self->priv->smooth_data.trend_joints != NULL) + { + trend = self->priv->smooth_data.trend_joints[i]; + if (trend != NULL) + { + smoothed_joint = g_slice_new0 (SkeltrackJoint); + smoothed_joint->x = smooth->x + trend->x; + smoothed_joint->y = smooth->y + trend->y; + smoothed_joint->z = smooth->z + trend->z; + smoothed_joint->screen_x = smooth->screen_x + trend->screen_x; + smoothed_joint->screen_y = smooth->screen_y + trend->screen_y; + } + else + smoothed_joint = skeltrack_joint_copy (smooth); + } + else + smoothed_joint = skeltrack_joint_copy (smooth); + } + smoothed[i] = smoothed_joint; + } + } + skeltrack_joint_list_free (joints); + + joints = smoothed; + } + + if (joints) + { + SkeltrackJoint *joint = skeltrack_joint_list_get_joint (joints, + SKELTRACK_JOINT_ID_HEAD); + if (joint != NULL) + { + skeltrack_joint_free (self->priv->previous_head); + self->priv->previous_head = skeltrack_joint_copy (joint); + } + } + + g_list_free (extremas); + + return joints; +} + +static void +clean_tracking_resources (SkeltrackSkeleton *self) +{ + g_slice_free1 (self->priv->buffer_width * + self->priv->buffer_height * sizeof (gint), + self->priv->distances_matrix); + self->priv->distances_matrix = NULL; + + g_slice_free1 (self->priv->buffer_width * + self->priv->buffer_height * sizeof (Node *), + self->priv->node_matrix); + self->priv->node_matrix = NULL; +} + +static void +track_joints_in_thread (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + SkeltrackSkeleton *self = SKELTRACK_SKELETON (object); + SkeltrackJointList joints; + + joints = track_joints (self); + + g_mutex_lock (&self->priv->track_joints_mutex); + self->priv->track_joints_result = NULL; + g_mutex_unlock (&self->priv->track_joints_mutex); + + g_simple_async_result_set_op_res_gpointer (res, + joints, + NULL); + + g_object_unref (res); +} + +/* public methods */ + +/** + * skeltrack_skeleton_new: + * + * Constructs and returns a new #SkeltrackSkeleton instance. + * + * Returns: (transfer full): The newly created #SkeltrackSkeleton. + */ +SkeltrackSkeleton * +skeltrack_skeleton_new (void) +{ + return g_object_new (SKELTRACK_TYPE_SKELETON, NULL); +} + +/** + * skeltrack_skeleton_set_focus_point: + * @self: The #SkeltrackSkeleton + * @x: The x coordinate of the focus point. + * @y: The y coordinate of the focus point. + * @z: The z coordinate of the focus point. + * + * Gets the focus point which is the origin from where the tracking will + * start. The coordinates will be in mm. + * + **/ +void +skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, + gint *x, + gint *y, + gint *z) +{ + *x = self->priv->focus_node->x; + *y = self->priv->focus_node->y; + *z = self->priv->focus_node->z; +} + +/** + * skeltrack_skeleton_set_focus_point: + * @self: The #SkeltrackSkeleton + * @x: The x coordinate of the focus point. + * @y: The y coordinate of the focus point. + * @z: The z coordinate of the focus point. + * + * Sets the focus point which is the origin from where the tracking will + * start. The coordinates should be in mm. + * + * If this method is not called the default values are @x = 0, @y = 0, + * @z = 1000, that is, in the center of the screen and at 1m from the + * camera. + * + **/ +void +skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, + gint x, + gint y, + gint z) +{ + self->priv->focus_node->x = x; + self->priv->focus_node->y = y; + self->priv->focus_node->z = z; +} + +/** + * skeltrack_skeleton_track_joints: + * @self: The #SkeltrackSkeleton + * @buffer: The buffer containing the depth information, from which + * all the information will be retrieved. + * @width: The width of the @buffer + * @height: The height of the @buffer + * @cancellable: (allow-none): A cancellable object, or %NULL (currently + * unused) + * @callback: (scope async): The function to call when the it is finished + * tracking the joints + * @user_data: (allow-none): An arbitrary user data to pass in @callback, + * or %NULL + * + * Tracks the skeleton's joints. + * + * It uses the depth information contained in the given @buffer and tries to + * track the skeleton joints. The @buffer's depth values should be in mm. + * Use skeltrack_skeleton_track_joints_finish() to get a list of the joints + * found. + * + * If this method is called while a previous attempt of tracking the joints + * is still running, a %G_IO_ERROR_PENDING error occurs. + * + **/ +void +skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = NULL; + + g_return_if_fail (SKELTRACK_IS_SKELETON (self) && + callback != NULL && + buffer != NULL); + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + skeltrack_skeleton_track_joints); + + if (self->priv->track_joints_result != NULL) + { + g_simple_async_result_set_error (result, + G_IO_ERROR, + G_IO_ERROR_PENDING, + "Currently tracking joints"); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } + + g_mutex_lock (&self->priv->track_joints_mutex); + + self->priv->track_joints_result = G_ASYNC_RESULT (result); + + /* @TODO: Set the cancellable */ + + self->priv->buffer = buffer; + + if (self->priv->buffer_width != width || + self->priv->buffer_height != height) + { + clean_tracking_resources (self); + + self->priv->buffer_width = width; + self->priv->buffer_height = height; + } + + g_simple_async_result_run_in_thread (result, + track_joints_in_thread, + G_PRIORITY_DEFAULT, + cancellable); + + g_mutex_unlock (&self->priv->track_joints_mutex); +} + +/** + * skeltrack_skeleton_track_joints_finish: + * @self: The #SkeltrackSkeleton + * @result: The #GAsyncResult provided in the callback + * @error: (allow-none): A pointer to a #GError, or %NULL + * + * Gets the list of joints that were retrieved by a + * skeltrack_skeleton_track_joints() operation. + * + * Use skeltrack_joint_list_get_joint() with #SkeltrackJointId + * to get the respective joints from the list. + * Joints that could not be found will appear as %NULL in the list. + * + * The list should be freed using skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJointList with the joints found. + */ +SkeltrackJointList +skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *res; + + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); + + res = G_SIMPLE_ASYNC_RESULT (result); + + if (! g_simple_async_result_propagate_error (res, error)) + { + SkeltrackJointList joints = NULL; + joints = g_simple_async_result_get_op_res_gpointer (res); + return joints; + } + else + return NULL; +} + +/** + * skeltrack_skeleton_track_joints_sync: + * @self: The #SkeltrackSkeleton + * @buffer: The buffer containing the depth information, from which + * all the information will be retrieved. + * @width: The width of the @buffer + * @height: The height of the @buffer + * @cancellable: (allow-none): A cancellable object, or %NULL (currently + * unused) + * @error: (allow-none): A pointer to a #GError, or %NULL + * + * Tracks the skeleton's joints synchronously. + * + * Does the same as skeltrack_skeleton_track_joints() but synchronously + * and returns the list of joints found. + * Ideal for off-line skeleton tracking. + * + * If this method is called while a previous attempt of asynchronously + * tracking the joints is still running, a %G_IO_ERROR_PENDING error occurs. + * + * The joints list should be freed using skeltrack_joint_list_free(). + * + * Returns: (transfer full): The #SkeltrackJointList with the joints found. + **/ +SkeltrackJointList +skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, + guint16 *buffer, + guint width, + guint height, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (SKELTRACK_IS_SKELETON (self), NULL); + + if (self->priv->track_joints_result != NULL && error != NULL) + { + *error = g_error_new (G_IO_ERROR, + G_IO_ERROR_PENDING, + "Currently tracking joints"); + return NULL; + } + + self->priv->buffer = buffer; + + if (self->priv->buffer_width != width || + self->priv->buffer_height != height) + { + clean_tracking_resources (self); + + self->priv->buffer_width = width; + self->priv->buffer_height = height; + } + + return track_joints (self); +} diff --git a/interface/external/skeltrack/src/skeltrack-smooth.c b/interface/external/skeltrack/src/skeltrack-smooth.c new file mode 100644 index 0000000000..1e6658f578 --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-smooth.c @@ -0,0 +1,226 @@ +/* + * skeltrack-smooth.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include + +#include "skeltrack-smooth.h" + +static gfloat +holt_double_exp_formula_st (gfloat alpha, + gfloat previous_trend, + gfloat current_value, + gfloat previous_smoothed_value) +{ + return alpha * current_value + + (1.0 - alpha) * (previous_smoothed_value + previous_trend); +} + +static gfloat +holt_double_exp_formula_bt (gfloat beta, + gfloat previous_trend, + gfloat current_smoothed_value, + gfloat previous_smoothed_value) +{ + return beta * (current_smoothed_value - previous_smoothed_value) + + (1.0 - beta) * previous_trend; +} + +static void +holt_double_exp_joint (gfloat alpha, + gfloat beta, + SkeltrackJoint *smoothed_joint, + SkeltrackJoint *current_joint, + SkeltrackJoint *trend_joint) +{ + gfloat new_x, new_y, new_z, new_screen_x, new_screen_y; + new_x = holt_double_exp_formula_st (alpha, + trend_joint->x, + current_joint->x, + smoothed_joint->x); + new_y = holt_double_exp_formula_st (alpha, + trend_joint->y, + current_joint->y, + smoothed_joint->y); + new_z = holt_double_exp_formula_st (alpha, + trend_joint->z, + current_joint->z, + smoothed_joint->z); + new_screen_x = holt_double_exp_formula_st (alpha, + trend_joint->screen_x, + current_joint->screen_x, + smoothed_joint->screen_x); + new_screen_y = holt_double_exp_formula_st (alpha, + trend_joint->screen_y, + current_joint->screen_y, + smoothed_joint->screen_y); + trend_joint->x = holt_double_exp_formula_bt (beta, + trend_joint->x, + new_x, + smoothed_joint->x); + trend_joint->y = holt_double_exp_formula_bt (beta, + trend_joint->y, + new_y, + smoothed_joint->y); + trend_joint->z = holt_double_exp_formula_bt (beta, + trend_joint->z, + new_z, + smoothed_joint->z); + trend_joint->screen_x = holt_double_exp_formula_bt (beta, + trend_joint->screen_x, + new_screen_x, + smoothed_joint->screen_x); + trend_joint->screen_y = holt_double_exp_formula_bt (beta, + trend_joint->screen_y, + new_screen_y, + smoothed_joint->screen_y); + smoothed_joint->x = new_x; + smoothed_joint->y = new_y; + smoothed_joint->z = new_z; + smoothed_joint->screen_x = new_screen_x; + smoothed_joint->screen_y = new_screen_y; +} + +void +reset_joints_persistency_counter (SmoothData *smooth_data) +{ + guint i; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + smooth_data->joints_persistency_counter[i] = + smooth_data->joints_persistency; + } +} + +static void +decrease_joints_persistency (SmoothData *smooth_data) +{ + guint i; + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *smoothed_joint = NULL; + SkeltrackJoint *trend_joint = NULL; + + if (smooth_data->smoothed_joints) + smoothed_joint = smooth_data->smoothed_joints[i]; + if (smooth_data->trend_joints) + trend_joint = smooth_data->trend_joints[i]; + + if (smoothed_joint != NULL || trend_joint != NULL) + { + if (smooth_data->joints_persistency_counter[i] > 0) + smooth_data->joints_persistency_counter[i]--; + else + { + skeltrack_joint_free (smoothed_joint); + skeltrack_joint_free (trend_joint); + if (smoothed_joint) + smooth_data->smoothed_joints[i] = NULL; + if (trend_joint) + smooth_data->trend_joints[i] = NULL; + smooth_data->joints_persistency_counter[i] = smooth_data->joints_persistency; + } + } + } +} + +void +smooth_joints (SmoothData *data, + SkeltrackJointList new_joints) +{ + guint i; + + if (new_joints == NULL) + { + decrease_joints_persistency (data); + return; + } + + if (data->smoothed_joints == NULL) + { + data->smoothed_joints = skeltrack_joint_list_new (); + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + data->smoothed_joints[i] = skeltrack_joint_copy (new_joints[i]); + } + return; + } + if (data->trend_joints == NULL) + { + data->trend_joints = skeltrack_joint_list_new (); + } + + for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) + { + SkeltrackJoint *joint, *smoothed_joint, *trend_joint; + + smoothed_joint = data->smoothed_joints[i]; + trend_joint = data->trend_joints[i]; + joint = new_joints[i]; + if (joint == NULL) + { + if (smoothed_joint != NULL) + { + if (data->joints_persistency_counter[i] > 0) + data->joints_persistency_counter[i]--; + else + { + skeltrack_joint_free (smoothed_joint); + skeltrack_joint_free (trend_joint); + data->smoothed_joints[i] = NULL; + data->trend_joints[i] = NULL; + data->joints_persistency_counter[i] = data->joints_persistency; + } + } + continue; + } + data->joints_persistency_counter[i] = data->joints_persistency; + + if (smoothed_joint == NULL) + { + data->smoothed_joints[i] = skeltrack_joint_copy (joint); + continue; + } + + if (trend_joint == NULL) + { + /* First case (when there are only initial values) */ + trend_joint = g_slice_new0 (SkeltrackJoint); + trend_joint->x = joint->x - smoothed_joint->x; + trend_joint->y = joint->y - smoothed_joint->y; + trend_joint->z = joint->z - smoothed_joint->z; + trend_joint->screen_x = joint->screen_x - smoothed_joint->screen_x; + trend_joint->screen_y = joint->screen_y - smoothed_joint->screen_y; + data->trend_joints[i] = trend_joint; + } + else + { + /* @TODO: Check if we should give the control of each factor + independently (data-smoothing-factor and trend-smoothing-factor). + */ + holt_double_exp_joint (data->smoothing_factor, + data->smoothing_factor, + smoothed_joint, + joint, + trend_joint); + } + } +} diff --git a/interface/external/skeltrack/src/skeltrack-util.c b/interface/external/skeltrack/src/skeltrack-util.c new file mode 100644 index 0000000000..482f26069a --- /dev/null +++ b/interface/external/skeltrack/src/skeltrack-util.c @@ -0,0 +1,626 @@ +/* + * skeltrack-util.c + * + * Skeltrack - A Free Software skeleton tracking library + * Copyright (C) 2012 Igalia S.L. + * + * Authors: + * Joaquim Rocha + * Eduardo Lima Mitev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt + * for more details. + */ + +#include + +#include "skeltrack-util.h" + +/* @TODO: Expose these to the user */ +static const gfloat SCALE_FACTOR = .0021; +static const gint MIN_DISTANCE = -10.0; + +static SkeltrackJoint * +node_to_joint (Node *node, SkeltrackJointId id, gint dimension_reduction) +{ + SkeltrackJoint *joint; + + if (node == NULL) + return NULL; + + joint = g_slice_new0 (SkeltrackJoint); + joint->id = id; + joint->x = node->x; + joint->y = node->y; + joint->z = node->z; + joint->screen_x = node->i * dimension_reduction; + joint->screen_y = node->j * dimension_reduction; + + return joint; +} + +static guint +get_distance_from_joint (Node *node, SkeltrackJoint *joint) +{ + guint dx, dy, dz; + dx = ABS (node->x - joint->x); + dy = ABS (node->y - joint->y); + dz = ABS (node->z - joint->z); + return sqrt (dx * dx + dy * dy + dz * dz); +} + +static void +unlink_node (Node *node) +{ + Node *neighbor; + GList *current_neighbor; + + for (current_neighbor = g_list_first (node->neighbors); + current_neighbor != NULL; + current_neighbor = g_list_next (current_neighbor)) + { + neighbor = (Node *) current_neighbor->data; + neighbor->neighbors = g_list_remove (neighbor->neighbors, node); + } + + for (current_neighbor = g_list_first (node->linked_nodes); + current_neighbor != NULL; + current_neighbor = g_list_next (current_neighbor)) + { + neighbor = (Node *) current_neighbor->data; + neighbor->linked_nodes = g_list_remove (neighbor->linked_nodes, node); + } + + g_list_free (node->neighbors); + g_list_free (node->linked_nodes); + node->neighbors = NULL; + node->linked_nodes = NULL; +} + +static Node * +get_closest_node_with_distances (GList *node_list, + Node *from, + guint x_dist, + guint y_dist, + guint z_dist, + gint *closest_node_dist) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + guint dx, dy, dz; + Node *node; + gint current_distance; + node = (Node *) current_node->data; + + dx = ABS (from->x - node->x); + dy = ABS (from->y - node->y); + dz = ABS (from->z - node->z); + + if (dx > x_dist || dy > y_dist || dz > z_dist) + continue; + + current_distance = sqrt (dx * dx + dy * dy + dz * dz); + if (closest == NULL || distance > current_distance) + { + closest = node; + distance = current_distance; + } + } + + *closest_node_dist = distance; + return closest; +} + +Node * +get_closest_node_to_joint (GList *extremas, + SkeltrackJoint *joint, + gint *distance) +{ + GList *current_node; + gint dist = -1; + Node *closest_node = NULL; + + for (current_node = g_list_first (extremas); + current_node != NULL; + current_node = g_list_next (current_node)) + { + guint current_dist; + Node *node = (Node *) current_node->data; + if (node == NULL) + continue; + + current_dist = get_distance_from_joint (node, joint); + if (dist == -1 || current_dist < dist) + { + closest_node = node; + dist = current_dist; + } + } + *distance = dist; + return closest_node; +} + +gint +get_distance (Node *a, Node *b) +{ + guint dx, dy, dz; + dx = ABS (a->x - b->x); + dy = ABS (a->y - b->y); + dz = ABS (a->z - b->z); + return sqrt (dx * dx + dy * dy + dz * dz); +} + +Node * +get_closest_torso_node (GList *node_list, Node *from, Node *head) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + if (node->z >= head->z && + node->y >= from->y) + { + current_distance = get_distance (node, from); + if (closest == NULL || current_distance < distance) + { + closest = node; + distance = current_distance; + } + } + } + return closest; +} + +Node * +get_closest_node (GList *node_list, Node *from) +{ + Node *closest = NULL; + gint distance = -1; + GList *current_node; + + /* @TODO: Replace this and use closest pair of points + algorithm and ensure O(n log n) instead of brute-force */ + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + if (closest == NULL) + { + closest = node; + distance = get_distance (node, from); + continue; + } + current_distance = get_distance (node, from); + if (current_distance < distance) + { + closest = node; + distance = current_distance; + } + } + return closest; +} + +Label * +get_main_component (GList *node_list, Node *from, gdouble min_normalized_nr_nodes) +{ + Label *main_component = NULL; + gint distance = -1; + GList *current_node; + + for (current_node = g_list_first (node_list); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + Label *label; + gint current_distance; + node = (Node *) current_node->data; + label = node->label; + + if (main_component == NULL && + label->normalized_num_nodes > min_normalized_nr_nodes) + { + main_component = label; + distance = get_distance (node, from); + continue; + } + + current_distance = get_distance (node, from); + if (current_distance < distance && + label->normalized_num_nodes > min_normalized_nr_nodes) + { + main_component = label; + distance = current_distance; + } + } + + return main_component; +} + +Label * +label_find (Label *label) +{ + Label *parent; + + g_return_val_if_fail (label != NULL, NULL); + + parent = label->parent; + if (parent == label) + return parent; + else + return label_find (parent); +} + +void +label_union (Label *a, Label *b) +{ + Label *root_a, *root_b; + root_a = label_find (a); + root_b = label_find (b); + if (root_a->index < root_b->index) + { + b->parent = root_a; + } + else + { + a->parent = root_b; + } +} + +void +free_label (Label *label) +{ + g_list_free (label->nodes); + label->nodes = NULL; + g_slice_free (Label, label); +} + +void +clean_labels (GList *labels) +{ + GList *current = g_list_first (labels); + while (current != NULL) + { + Label *label; + label = (Label *) current->data; + free_label (label); + current = g_list_next (current); + } +} + +void +free_node (Node *node, gboolean unlink_node_first) +{ + if (unlink_node_first) + { + unlink_node (node); + } + else + { + g_list_free (node->neighbors); + g_list_free (node->linked_nodes); + node->neighbors = NULL; + node->linked_nodes = NULL; + } + g_slice_free (Node, node); +} + +void +clean_nodes (GList *nodes) +{ + GList *current = g_list_first (nodes); + while (current != NULL) + { + Node *node; + node = (Node *) current->data; + free_node (node, FALSE); + current = g_list_next (current); + } +} + +GList * +remove_nodes_with_label (GList *nodes, + Node **node_matrix, + gint width, + Label *label) +{ + Node *node; + GList *link_to_delete, *current_node; + + current_node = g_list_first (nodes); + while (current_node != NULL) + { + node = (Node *) current_node->data; + if (node->label == label) + { + link_to_delete = current_node; + current_node = g_list_next (current_node); + nodes = g_list_delete_link (nodes, link_to_delete); + node_matrix[width * node->j + node->i] = NULL; + free_node (node, TRUE); + continue; + } + current_node = g_list_next (current_node); + } + return nodes; +} + +Label * +get_lowest_index_label (Label **neighbor_labels) +{ + guint index; + Label *lowest_index_label = NULL; + + lowest_index_label = neighbor_labels[0]; + for (index = 1; index < 4; index++) + { + if (neighbor_labels[index] == NULL) + continue; + + if (lowest_index_label == NULL || + lowest_index_label->index < neighbor_labels[index]->index) + { + lowest_index_label = neighbor_labels[index]; + } + } + + return lowest_index_label; +} + +Label * +new_label (gint index) +{ + Label *label = g_slice_new (Label); + label->index = index; + label->parent = label; + label->nodes = NULL; + label->bridge_node = NULL; + label->to_node = NULL; + label->lower_screen_y = -1; + label->higher_z = -1; + label->lower_z = -1; + label->normalized_num_nodes = -1; + + return label; +} + +void +join_components_to_main (GList *labels, + Label *main_component_label, + guint horizontal_max_distance, + guint depth_max_distance, + guint graph_distance_threshold) +{ + GList *current_label; + + for (current_label = g_list_first (labels); + current_label != NULL; + current_label = g_list_next (current_label)) + { + gint closer_distance = -1; + Label *label; + GList *current_node, *nodes; + + label = (Label *) current_label->data; + if (label == main_component_label) + continue; + + /* Skip nodes behind main component */ + if (label->higher_z > main_component_label->higher_z + + graph_distance_threshold) + continue; + + nodes = label->nodes; + for (current_node = g_list_first (nodes); + current_node != NULL; + current_node = g_list_next (current_node)) + { + Node *node; + gint current_distance; + node = (Node *) current_node->data; + /* Skip nodes that belong to the same component or + that a not in the edge of their component */ + if (g_list_length (node->neighbors) == 8) + continue; + + Node *closest_node = + get_closest_node_with_distances (main_component_label->nodes, + node, + horizontal_max_distance, + horizontal_max_distance, + depth_max_distance, + ¤t_distance); + if (closest_node && + (current_distance < closer_distance || + closer_distance == -1)) + { + node->label->bridge_node = node; + node->label->to_node = closest_node; + closer_distance = current_distance; + } + } + } +} + +void +set_joint_from_node (SkeltrackJointList *joints, + Node *node, + SkeltrackJointId id, + gint dimension_reduction) +{ + (*joints)[id] = node_to_joint (node, id, dimension_reduction); +} + +gint * +create_new_dist_matrix (gint matrix_size) +{ + guint i; + gint *distances; + + distances = g_slice_alloc0 (matrix_size * sizeof (gint)); + for (i = 0; i < matrix_size; i++) + { + distances[i] = -1; + } + + return distances; +} + +gboolean +dijkstra_to (GList *nodes, Node *source, Node *target, + gint width, gint height, + gint *distances, Node **previous) +{ + gint nr; + GList *unvisited_nodes, *current; + + for (current = g_list_first (nodes); + previous != NULL && current != NULL; + current = g_list_next (current)) + { + Node *node; + node = (Node *) current->data; + previous[node->j * width + node->i] = NULL; + } + distances[source->j * width + source->i] = 0; + + unvisited_nodes = g_list_copy (nodes); + nr = 0; + while (unvisited_nodes != NULL) + { + Node *node; + GList *current_neighbor, *shorter_dist_node, *cur_node; + + shorter_dist_node = g_list_first (unvisited_nodes); + cur_node = g_list_next (shorter_dist_node); + while (cur_node != NULL) + { + Node *value, *shorter_dist; + value = (Node *) cur_node->data; + shorter_dist = (Node *) shorter_dist_node->data; + if (distances[shorter_dist->j * width + shorter_dist->i] == -1 || + (distances[value->j * width + value->i] != -1 && + distances[value->j * width + + value->i] < distances[shorter_dist->j * width + + shorter_dist->i])) + { + shorter_dist_node = cur_node; + } + cur_node = g_list_next (cur_node); + } + + node = (Node *) shorter_dist_node->data; + if (distances[node->j * width + node->i] == -1) + { + break; + } + + current_neighbor = g_list_first (node->neighbors); + while (current_neighbor) + { + gint dist; + Node *neighbor; + + neighbor = (Node *) current_neighbor->data; + dist = get_distance (node, neighbor) + + distances[node->j * width + node->i]; + + if (distances[neighbor->j * width + neighbor->i] == -1 || + dist < distances[neighbor->j * width + neighbor->i]) + { + distances[neighbor->j * width + neighbor->i] = dist; + if (previous != NULL) + { + previous[neighbor->j * width + neighbor->i] = node; + } + nr++; + } + if (target != NULL && neighbor == target) + { + g_list_free (unvisited_nodes); + return TRUE; + } + + current_neighbor = g_list_next (current_neighbor); + } + unvisited_nodes = g_list_delete_link (unvisited_nodes, shorter_dist_node); + } + g_list_free (unvisited_nodes); + return FALSE; +} + +void +convert_screen_coords_to_mm (guint width, + guint height, + guint dimension_reduction, + guint i, + guint j, + gint z, + gint *x, + gint *y) +{ + gfloat width_height_relation = + width > height ? (gfloat) width / height : (gfloat) height / width; + /* Formula from http://openkinect.org/wiki/Imaging_Information */ + *x = round((i * dimension_reduction - width * dimension_reduction / 2.0) * + (z + MIN_DISTANCE) * SCALE_FACTOR * width_height_relation); + *y = round((j * dimension_reduction - height * dimension_reduction / 2.0) * + (z + MIN_DISTANCE) * SCALE_FACTOR); +} + +void +convert_mm_to_screen_coords (guint width, + guint height, + guint dimension_reduction, + gint x, + gint y, + gint z, + guint *i, + guint *j) +{ + gfloat width_height_relation = + width > height ? (gfloat) width / height : (gfloat) height / width; + + if (z + MIN_DISTANCE == 0) + { + *i = 0; + *j = 0; + return; + } + + *i = round (width / 2.0 + x / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * + dimension_reduction * width_height_relation)); + *j = round (height / 2.0 + y / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * + dimension_reduction)); +} diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 586e806ce3..f01b9a5804 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -176,11 +176,15 @@ void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) { QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame())); } -FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) { +FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0), _freenectContext(0) { } FrameGrabber::~FrameGrabber() { - if (_capture != 0) { + if (_freenectContext != 0) { + freenect_close_device(_freenectDevice); + freenect_shutdown(_freenectContext); + + } else if (_capture != 0) { cvReleaseCapture(&_capture); } } @@ -190,32 +194,14 @@ void FrameGrabber::reset() { } void FrameGrabber::grabFrame() { - if (_capture == 0) { - if ((_capture = cvCaptureFromCAM(-1)) == 0) { - printLog("Failed to open webcam.\n"); - return; - } - const int IDEAL_FRAME_WIDTH = 320; - 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 - cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5); - cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5); -#endif - - switchToResourcesParentIfRequired(); - if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { - printLog("Failed to load Haar cascade for face tracking.\n"); - } + if (_capture == 0 && _freenectContext == 0 && !init()) { + return; } + if (_freenectContext != 0) { + + return; + } + IplImage* image = cvQueryFrame(_capture); if (image == 0) { // try again later @@ -264,6 +250,47 @@ void FrameGrabber::grabFrame() { Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect)); } +bool FrameGrabber::init() { + // first try for a Kinect + if (freenect_init(&_freenectContext, 0) >= 0) { + if (freenect_num_devices(_freenectContext) > 0) { + if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { + return true; + } + } + freenect_shutdown(_freenectContext); + _freenectContext = 0; + } + + // next, an ordinary webcam + if ((_capture = cvCaptureFromCAM(-1)) == 0) { + printLog("Failed to open webcam.\n"); + return false; + } + const int IDEAL_FRAME_WIDTH = 320; + 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 + cvSetCaptureProperty(_capture, CV_CAP_PROP_EXPOSURE, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_CONTRAST, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_SATURATION, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_BRIGHTNESS, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_HUE, 0.5); + cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5); +#endif + + switchToResourcesParentIfRequired(); + if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { + printLog("Failed to load Haar cascade for face tracking.\n"); + return false; + } + return true; +} + void FrameGrabber::updateHSVFrame(const Mat& frame) { cvtColor(frame, _hsvFrame, CV_BGR2HSV); inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask); diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 4bf6dea53e..59110d35a2 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -15,6 +15,8 @@ #include +#include + #include #include "InterfaceConfig.h" @@ -82,6 +84,7 @@ public slots: private: + bool init(); void updateHSVFrame(const cv::Mat& frame); CvCapture* _capture; @@ -91,6 +94,9 @@ private: cv::SparseMat _histogram; cv::Mat _backProject; cv::Rect _searchWindow; + + freenect_context* _freenectContext; + freenect_device* _freenectDevice; }; Q_DECLARE_METATYPE(cv::Mat) From 198d84abceaed43e9a161415658351653c6de8fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Jun 2013 14:44:21 -0700 Subject: [PATCH 04/63] Mac libraries. --- .../external/LibUSB/lib/MacOS/libusb-1.0.a | Bin 0 -> 264096 bytes .../external/freenect/lib/MacOS/libfreenect.a | Bin 0 -> 67216 bytes .../freenect/lib/MacOS/libfreenect_sync.a | Bin 0 -> 10496 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/external/LibUSB/lib/MacOS/libusb-1.0.a create mode 100644 interface/external/freenect/lib/MacOS/libfreenect.a create mode 100644 interface/external/freenect/lib/MacOS/libfreenect_sync.a diff --git a/interface/external/LibUSB/lib/MacOS/libusb-1.0.a b/interface/external/LibUSB/lib/MacOS/libusb-1.0.a new file mode 100644 index 0000000000000000000000000000000000000000..ac9236f47353cc049fae6717d2af18dde3f4c0d0 GIT binary patch literal 264096 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 -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// Structure to represent a single 16-bit signed little-endian PCM sample. -typedef struct { - int16_t left; - int16_t right; - int16_t center; - int16_t lfe; - int16_t surround_left; - int16_t surround_right; -} freenect_sample_51; - -/** - * Typedef for "you wanted this microphone data, here it is" event callbacks. - * TODO: Timestamp details - * The format of the unknown stream is as of yet undetermined. - * - * @param dev Device which triggered this callback - * @param num_samples Number of samples provided in each of the audio data arrays (mic[1-4] and cancelled) - * @param mic1 Microphone data for the leftmost microphone: 32-bit PCM little-endian samples at 16kHz. - * @param mic2 Microphone data for the left-middle microphone: 32-bit PCM little-endian samples at 16kHz. - * @param mic3 Microphone data for the right-middle microphone: 32-bit PCM little-endian samples at 16kHz. - * @param mic4 Microphone data for the rightmost microphone: 32-bit PCM little-endian samples at 16kHz. - * @param cancelled Noise-cancelled audio data: 16-bit PCM little-endian samples at 16kHz. - */ -typedef void (*freenect_audio_in_cb)(freenect_device *dev, int num_samples, - int32_t* mic1, int32_t* mic2, - int32_t* mic3, int32_t* mic4, - int16_t* cancelled, void *unknown/*, timestamp_t timestamp*/); - -/** - * Typedef for "you're playing audio, the library needs you to fill up the outgoing audio buffer" event callbacks - * The library will request samples at a rate of 48000Hz. - * - * @param dev Device this callback was triggered for - * @param samples Pointer to the memory where the library expects you to copy the next sample_count freenect_sample_51's to. - * @param sample_count Bidirectional. in: maximum number of samples the driver wants (don't copy in more than this, you'll clobber memory). out: actual number of samples provided to the driver. - */ -typedef void (*freenect_audio_out_cb)(freenect_device *dev, freenect_sample_51* samples, int* sample_count); - -/** - * Set the audio in callback. This is the function called when the library - * has new microphone samples. It will be called approximately 62.5 times per - * second (16kHz sample rate, expect 512 samples/callback) - * - * @param dev Device for which to set the callback - * @param callback Callback function to set - */ -FREENECTAPI void freenect_set_audio_in_callback(freenect_device *dev, freenect_audio_in_cb callback); - -/** - * Set the audio out callback. This is the "tell me what audio you're about - * to play through the speakers so the Kinect can subtract it out" callback for - * a given device. If you choose not set an audio_out_callback, the library - * will send silence to the Kinect for you - it requires data either way. - * - * @param dev Device for which to set the callback - * @param callback Callback function to set - */ -FREENECTAPI void freenect_set_audio_out_callback(freenect_device *dev, freenect_audio_out_cb callback); - -/** - * Start streaming audio for the specified device. - * - * @param dev Device for which to start audio streaming - * - * @return 0 on success, < 0 if error - */ -FREENECTAPI int freenect_start_audio(freenect_device* dev); - -/** - * Stop streaming audio for the specified device. - * - * @param dev Device for which to stop audio streaming - * - * @return 0 on success, < 0 if error - */ -FREENECTAPI int freenect_stop_audio(freenect_device* dev); - -#ifdef __cplusplus -} -#endif - -#endif // - - diff --git a/interface/external/freenect/include/libfreenect-registration.h b/interface/external/freenect/include/libfreenect-registration.h deleted file mode 100644 index 2ef5d60981..0000000000 --- a/interface/external/freenect/include/libfreenect-registration.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of the OpenKinect Project. http://www.openkinect.org - * - * Copyright (c) 2011 individual OpenKinect contributors. See the CONTRIB file - * for details. - * - * This code is licensed to you under the terms of the Apache License, version - * 2.0, or, at your option, the terms of the GNU General Public License, - * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, - * or the following URLs: - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/gpl-2.0.txt - * - * If you redistribute this file in source form, modified or unmodified, you - * may: - * 1) Leave this header intact and distribute it under the same terms, - * accompanying it with the APACHE20 and GPL20 files, or - * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or - * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file - * In all cases you must keep the copyright notice intact and include a copy - * of the CONTRIB file. - * - * Binary distributions must follow the binary distribution requirements of - * either License. - */ - -#ifndef LIBFREENECT_REGISTRATION_H -#define LIBFREENECT_REGISTRATION_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// Internal Kinect registration parameters. -/// Structure matches that of the line protocol -/// of the Kinect. -typedef struct { - int32_t dx_center; // not used by mapping algorithm - - int32_t ax; - int32_t bx; - int32_t cx; - int32_t dx; - - int32_t dx_start; - - int32_t ay; - int32_t by; - int32_t cy; - int32_t dy; - - int32_t dy_start; - - int32_t dx_beta_start; - int32_t dy_beta_start; - - int32_t rollout_blank; // not used by mapping algorithm - int32_t rollout_size; // not used by mapping algorithm - - int32_t dx_beta_inc; - int32_t dy_beta_inc; - - int32_t dxdx_start; - int32_t dxdy_start; - int32_t dydx_start; - int32_t dydy_start; - - int32_t dxdxdx_start; - int32_t dydxdx_start; - int32_t dxdxdy_start; - int32_t dydxdy_start; - - int32_t back_comp1; // not used by mapping algorithm - - int32_t dydydx_start; - - int32_t back_comp2; // not used by mapping algorithm - - int32_t dydydy_start; -} freenect_reg_info; - -/// registration padding info (?) -typedef struct { - uint16_t start_lines; - uint16_t end_lines; - uint16_t cropping_lines; -} freenect_reg_pad_info; - -/// internal Kinect zero plane data -typedef struct { - float dcmos_emitter_dist; // Distance between IR camera and IR emitter, in cm. - float dcmos_rcmos_dist; // Distance between IR camera and RGB camera, in cm. - float reference_distance; // The focal length of the IR camera, in mm. - float reference_pixel_size; // The size of a single pixel on the zero plane, in mm. -} freenect_zero_plane_info; - -/// all data needed for depth->RGB mapping -typedef struct { - freenect_reg_info reg_info; - freenect_reg_pad_info reg_pad_info; - freenect_zero_plane_info zero_plane_info; - - double const_shift; - - uint16_t* raw_to_mm_shift; - int32_t* depth_to_rgb_shift; - int32_t (*registration_table)[2]; // A table of 640*480 pairs of x,y values. - // Index first by pixel, then x:0 and y:1. -} freenect_registration; - - -// These allow clients to export registration parameters; proper docs will -// come later -FREENECTAPI freenect_registration freenect_copy_registration(freenect_device* dev); -FREENECTAPI int freenect_destroy_registration(freenect_registration* reg); - -// convenience function to convert a single x-y coordinate pair from camera -// to world coordinates -FREENECTAPI void freenect_camera_to_world(freenect_device* dev, - int cx, int cy, int wz, double* wx, double* wy); - -#ifdef __cplusplus -} -#endif - -#endif // LIBFREENECT_REGISTRATION_H diff --git a/interface/external/freenect/include/libfreenect.h b/interface/external/freenect/include/libfreenect.h deleted file mode 100644 index 470558c33a..0000000000 --- a/interface/external/freenect/include/libfreenect.h +++ /dev/null @@ -1,632 +0,0 @@ -/* - * This file is part of the OpenKinect Project. http://www.openkinect.org - * - * Copyright (c) 2010 individual OpenKinect contributors. See the CONTRIB file - * for details. - * - * This code is licensed to you under the terms of the Apache License, version - * 2.0, or, at your option, the terms of the GNU General Public License, - * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses, - * or the following URLs: - * http://www.apache.org/licenses/LICENSE-2.0 - * http://www.gnu.org/licenses/gpl-2.0.txt - * - * If you redistribute this file in source form, modified or unmodified, you - * may: - * 1) Leave this header intact and distribute it under the same terms, - * accompanying it with the APACHE20 and GPL20 files, or - * 2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or - * 3) Delete the GPL v2 clause and accompany it with the APACHE20 file - * In all cases you must keep the copyright notice intact and include a copy - * of the CONTRIB file. - * - * Binary distributions must follow the binary distribution requirements of - * either License. - */ - -#ifndef LIBFREENECT_H -#define LIBFREENECT_H - -#include - -/* We need struct timeval */ -#ifdef _WIN32 -#include -#else -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#define FREENECT_COUNTS_PER_G 819 /**< Ticks per G for accelerometer as set per http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf */ - -/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED frame callbacks -#define FREENECT_DEPTH_MM_MAX_VALUE 10000 -/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_MM or FREENECT_DEPTH_REGISTERED depth modes -#define FREENECT_DEPTH_MM_NO_VALUE 0 -/// Maximum value that a uint16_t pixel will take on in the buffer of any of the FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED frame callbacks -#define FREENECT_DEPTH_RAW_MAX_VALUE 2048 -/// Value indicating that this pixel has no data, when using FREENECT_DEPTH_11BIT, FREENECT_DEPTH_10BIT, FREENECT_DEPTH_11BIT_PACKED, or FREENECT_DEPTH_10BIT_PACKED -#define FREENECT_DEPTH_RAW_NO_VALUE 2047 - -/// Flags representing devices to open when freenect_open_device() is called. -/// In particular, this allows libfreenect to grab only a subset of the devices -/// in the Kinect, so you could (for instance) use libfreenect to handle audio -/// and motor support while letting OpenNI have access to the cameras. -/// If a device is not supported on a particular platform, its flag will be ignored. -typedef enum { - FREENECT_DEVICE_MOTOR = 0x01, - FREENECT_DEVICE_CAMERA = 0x02, - FREENECT_DEVICE_AUDIO = 0x04, -} freenect_device_flags; - -/// A struct used in enumeration to give access to serial numbers, so you can -/// open a particular device by serial rather than depending on index. This -/// is most useful if you have more than one Kinect. -struct freenect_device_attributes; -struct freenect_device_attributes { - struct freenect_device_attributes *next; /**< Next device in the linked list */ - const char* camera_serial; /**< Serial number of this device's camera subdevice */ -}; - -/// Enumeration of available resolutions. -/// Not all available resolutions are actually supported for all video formats. -/// Frame modes may not perfectly match resolutions. For instance, -/// FREENECT_RESOLUTION_MEDIUM is 640x488 for the IR camera. -typedef enum { - FREENECT_RESOLUTION_LOW = 0, /**< QVGA - 320x240 */ - FREENECT_RESOLUTION_MEDIUM = 1, /**< VGA - 640x480 */ - FREENECT_RESOLUTION_HIGH = 2, /**< SXGA - 1280x1024 */ - FREENECT_RESOLUTION_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ -} freenect_resolution; - -/// Enumeration of video frame information states. -/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. -typedef enum { - FREENECT_VIDEO_RGB = 0, /**< Decompressed RGB mode (demosaicing done by libfreenect) */ - FREENECT_VIDEO_BAYER = 1, /**< Bayer compressed mode (raw information from camera) */ - FREENECT_VIDEO_IR_8BIT = 2, /**< 8-bit IR mode */ - FREENECT_VIDEO_IR_10BIT = 3, /**< 10-bit IR mode */ - FREENECT_VIDEO_IR_10BIT_PACKED = 4, /**< 10-bit packed IR mode */ - FREENECT_VIDEO_YUV_RGB = 5, /**< YUV RGB mode */ - FREENECT_VIDEO_YUV_RAW = 6, /**< YUV Raw mode */ - FREENECT_VIDEO_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ -} freenect_video_format; - -/// Enumeration of depth frame states -/// See http://openkinect.org/wiki/Protocol_Documentation#RGB_Camera for more information. -typedef enum { - FREENECT_DEPTH_11BIT = 0, /**< 11 bit depth information in one uint16_t/pixel */ - FREENECT_DEPTH_10BIT = 1, /**< 10 bit depth information in one uint16_t/pixel */ - FREENECT_DEPTH_11BIT_PACKED = 2, /**< 11 bit packed depth information */ - FREENECT_DEPTH_10BIT_PACKED = 3, /**< 10 bit packed depth information */ - FREENECT_DEPTH_REGISTERED = 4, /**< processed depth data in mm, aligned to 640x480 RGB */ - FREENECT_DEPTH_MM = 5, /**< depth to each pixel in mm, but left unaligned to RGB image */ - FREENECT_DEPTH_DUMMY = 2147483647, /**< Dummy value to force enum to be 32 bits wide */ -} freenect_depth_format; - -/// Structure to give information about the width, height, bitrate, -/// framerate, and buffer size of a frame in a particular mode, as -/// well as the total number of bytes needed to hold a single frame. -typedef struct { - uint32_t reserved; /**< unique ID used internally. The meaning of values may change without notice. Don't touch or depend on the contents of this field. We mean it. */ - freenect_resolution resolution; /**< Resolution this freenect_frame_mode describes, should you want to find it again with freenect_find_*_frame_mode(). */ - union { - int32_t dummy; - freenect_video_format video_format; - freenect_depth_format depth_format; - }; /**< The video or depth format that this freenect_frame_mode describes. The caller should know which of video_format or depth_format to use, since they called freenect_get_*_frame_mode() */ - int32_t bytes; /**< Total buffer size in bytes to hold a single frame of data. Should be equivalent to width * height * (data_bits_per_pixel+padding_bits_per_pixel) / 8 */ - int16_t width; /**< Width of the frame, in pixels */ - int16_t height; /**< Height of the frame, in pixels */ - int8_t data_bits_per_pixel; /**< Number of bits of information needed for each pixel */ - int8_t padding_bits_per_pixel; /**< Number of bits of padding for alignment used for each pixel */ - int8_t framerate; /**< Approximate expected frame rate, in Hz */ - int8_t is_valid; /**< If 0, this freenect_frame_mode is invalid and does not describe a supported mode. Otherwise, the frame_mode is valid. */ -} freenect_frame_mode; - -/// Enumeration of LED states -/// See http://openkinect.org/wiki/Protocol_Documentation#Setting_LED for more information. -typedef enum { - LED_OFF = 0, /**< Turn LED off */ - LED_GREEN = 1, /**< Turn LED to Green */ - LED_RED = 2, /**< Turn LED to Red */ - LED_YELLOW = 3, /**< Turn LED to Yellow */ - LED_BLINK_GREEN = 4, /**< Make LED blink Green */ - // 5 is same as 4, LED blink Green - LED_BLINK_RED_YELLOW = 6, /**< Make LED blink Red/Yellow */ -} freenect_led_options; - - -/// Enumeration of tilt motor status -typedef enum { - TILT_STATUS_STOPPED = 0x00, /**< Tilt motor is stopped */ - TILT_STATUS_LIMIT = 0x01, /**< Tilt motor has reached movement limit */ - TILT_STATUS_MOVING = 0x04, /**< Tilt motor is currently moving to new position */ -} freenect_tilt_status_code; - -/// Data from the tilt motor and accelerometer -typedef struct { - int16_t accelerometer_x; /**< Raw accelerometer data for X-axis, see FREENECT_COUNTS_PER_G for conversion */ - int16_t accelerometer_y; /**< Raw accelerometer data for Y-axis, see FREENECT_COUNTS_PER_G for conversion */ - int16_t accelerometer_z; /**< Raw accelerometer data for Z-axis, see FREENECT_COUNTS_PER_G for conversion */ - int8_t tilt_angle; /**< Raw tilt motor angle encoder information */ - freenect_tilt_status_code tilt_status; /**< State of the tilt motor (stopped, moving, etc...) */ -} freenect_raw_tilt_state; - -struct _freenect_context; -typedef struct _freenect_context freenect_context; /**< Holds information about the usb context. */ - -struct _freenect_device; -typedef struct _freenect_device freenect_device; /**< Holds device information. */ - -// usb backend specific section -typedef void freenect_usb_context; /**< Holds libusb-1.0 context */ -// - -/// If Win32, export all functions for DLL usage -#ifndef _WIN32 - #define FREENECTAPI /**< DLLExport information for windows, set to nothing on other platforms */ -#else - /**< DLLExport information for windows, set to nothing on other platforms */ - #ifdef __cplusplus - #define FREENECTAPI extern "C" __declspec(dllexport) - #else - // this is required when building from a Win32 port of gcc without being - // forced to compile all of the library files (.c) with g++... - #define FREENECTAPI __declspec(dllexport) - #endif -#endif - -/// Enumeration of message logging levels -typedef enum { - FREENECT_LOG_FATAL = 0, /**< Log for crashing/non-recoverable errors */ - FREENECT_LOG_ERROR, /**< Log for major errors */ - FREENECT_LOG_WARNING, /**< Log for warning messages */ - FREENECT_LOG_NOTICE, /**< Log for important messages */ - FREENECT_LOG_INFO, /**< Log for normal messages */ - FREENECT_LOG_DEBUG, /**< Log for useful development messages */ - FREENECT_LOG_SPEW, /**< Log for slightly less useful messages */ - FREENECT_LOG_FLOOD, /**< Log EVERYTHING. May slow performance. */ -} freenect_loglevel; - -/** - * Initialize a freenect context and do any setup required for - * platform specific USB libraries. - * - * @param ctx Address of pointer to freenect context struct to allocate and initialize - * @param usb_ctx USB context to initialize. Can be NULL if not using multiple contexts. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_init(freenect_context **ctx, freenect_usb_context *usb_ctx); - -/** - * Closes the device if it is open, and frees the context - * - * @param ctx freenect context to close/free - * - * @return 0 on success - */ -FREENECTAPI int freenect_shutdown(freenect_context *ctx); - -/// Typedef for logging callback functions -typedef void (*freenect_log_cb)(freenect_context *dev, freenect_loglevel level, const char *msg); - -/** - * Set the log level for the specified freenect context - * - * @param ctx context to set log level for - * @param level log level to use (see freenect_loglevel enum) - */ -FREENECTAPI void freenect_set_log_level(freenect_context *ctx, freenect_loglevel level); - -/** - * Callback for log messages (i.e. for rerouting to a file instead of - * stdout) - * - * @param ctx context to set log callback for - * @param cb callback function pointer - */ -FREENECTAPI void freenect_set_log_callback(freenect_context *ctx, freenect_log_cb cb); - -/** - * Calls the platform specific usb event processor - * - * @param ctx context to process events for - * - * @return 0 on success, other values on error, platform/library dependant - */ -FREENECTAPI int freenect_process_events(freenect_context *ctx); - -/** - * Calls the platform specific usb event processor until either an event occurs - * or the timeout parameter time has passed. If a zero timeval is passed, this - * function will handle any already-pending events, then return immediately. - * - * @param ctx Context to process events for - * @param timeout Pointer to a timeval containing the maximum amount of time to block waiting for events, or zero for nonblocking mode - * - * @return 0 on success, other values on error, platform/library dependant - */ -FREENECTAPI int freenect_process_events_timeout(freenect_context *ctx, struct timeval* timeout); - -/** - * Return the number of kinect devices currently connected to the - * system - * - * @param ctx Context to access device count through - * - * @return Number of devices connected, < 0 on error - */ -FREENECTAPI int freenect_num_devices(freenect_context *ctx); - -/** - * Scans for kinect devices and produces a linked list of their attributes - * (namely, serial numbers), returning the number of devices. - * - * @param ctx Context to scan for kinect devices with - * @param attribute_list Pointer to where this function will store the resultant linked list - * - * @return Number of devices connected, < 0 on error - */ -FREENECTAPI int freenect_list_device_attributes(freenect_context *ctx, struct freenect_device_attributes** attribute_list); - -/** - * Free the linked list produced by freenect_list_device_attributes(). - * - * @param attribute_list Linked list of attributes to free. - */ -FREENECTAPI void freenect_free_device_attributes(struct freenect_device_attributes* attribute_list); - -/** - * Answer which subdevices this library supports. This is most useful for - * wrappers trying to determine whether the underlying library was built with - * audio support or not, so the wrapper can avoid calling functions that do not - * exist. - * - * @return Flags representing the subdevices that the library supports opening (see freenect_device_flags) - */ -FREENECTAPI int freenect_supported_subdevices(void); - -/** - * Set which subdevices any subsequent calls to freenect_open_device() - * should open. This will not affect devices which have already been - * opened. The default behavior, should you choose not to call this - * function at all, is to open all supported subdevices - motor, cameras, - * and audio, if supported on the platform. - * - * @param ctx Context to set future subdevice selection for - * @param subdevs Flags representing the subdevices to select - */ -FREENECTAPI void freenect_select_subdevices(freenect_context *ctx, freenect_device_flags subdevs); - -/** - * Returns the devices that are enabled after calls to freenect_open_device() - * On newer kinects the motor and audio are automatically disabled for now - * - * @param ctx Context to set future subdevice selection for - * @return Flags representing the subdevices that were actually opened (see freenect_device_flags) - */ -FREENECTAPI freenect_device_flags freenect_enabled_subdevices(freenect_context *ctx); - -/** - * Opens a kinect device via a context. Index specifies the index of - * the device on the current state of the bus. Bus resets may cause - * indexes to shift. - * - * @param ctx Context to open device through - * @param dev Device structure to assign opened device to - * @param index Index of the device on the bus - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_open_device(freenect_context *ctx, freenect_device **dev, int index); - -/** - * Opens a kinect device (via a context) associated with a particular camera - * subdevice serial number. This function will fail if no device with a - * matching serial number is found. - * - * @param ctx Context to open device through - * @param dev Device structure to assign opened device to - * @param camera_serial Null-terminated ASCII string containing the serial number of the camera subdevice in the device to open - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_open_device_by_camera_serial(freenect_context *ctx, freenect_device **dev, const char* camera_serial); - -/** - * Closes a device that is currently open - * - * @param dev Device to close - * - * @return 0 on success - */ -FREENECTAPI int freenect_close_device(freenect_device *dev); - -/** - * Set the device user data, for passing generic information into - * callbacks - * - * @param dev Device to attach user data to - * @param user User data to attach - */ -FREENECTAPI void freenect_set_user(freenect_device *dev, void *user); - -/** - * Retrieve the pointer to user data from the device struct - * - * @param dev Device from which to get user data - * - * @return Pointer to user data - */ -FREENECTAPI void *freenect_get_user(freenect_device *dev); - -/// Typedef for depth image received event callbacks -typedef void (*freenect_depth_cb)(freenect_device *dev, void *depth, uint32_t timestamp); -/// Typedef for video image received event callbacks -typedef void (*freenect_video_cb)(freenect_device *dev, void *video, uint32_t timestamp); - -/** - * Set callback for depth information received event - * - * @param dev Device to set callback for - * @param cb Function pointer for processing depth information - */ -FREENECTAPI void freenect_set_depth_callback(freenect_device *dev, freenect_depth_cb cb); - -/** - * Set callback for video information received event - * - * @param dev Device to set callback for - * @param cb Function pointer for processing video information - */ -FREENECTAPI void freenect_set_video_callback(freenect_device *dev, freenect_video_cb cb); - -/** - * Set the buffer to store depth information to. Size of buffer is - * dependant on depth format. See FREENECT_DEPTH_*_SIZE defines for - * more information. - * - * @param dev Device to set depth buffer for. - * @param buf Buffer to store depth information to. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_set_depth_buffer(freenect_device *dev, void *buf); - -/** - * Set the buffer to store depth information to. Size of buffer is - * dependant on video format. See FREENECT_VIDEO_*_SIZE defines for - * more information. - * - * @param dev Device to set video buffer for. - * @param buf Buffer to store video information to. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_set_video_buffer(freenect_device *dev, void *buf); - -/** - * Start the depth information stream for a device. - * - * @param dev Device to start depth information stream for. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_start_depth(freenect_device *dev); - -/** - * Start the video information stream for a device. - * - * @param dev Device to start video information stream for. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_start_video(freenect_device *dev); - -/** - * Stop the depth information stream for a device - * - * @param dev Device to stop depth information stream on. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_stop_depth(freenect_device *dev); - -/** - * Stop the video information stream for a device - * - * @param dev Device to stop video information stream on. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_stop_video(freenect_device *dev); - -/** - * Updates the accelerometer state using a blocking control message - * call. - * - * @param dev Device to get accelerometer data from - * - * @return 0 on success, < 0 on error. Accelerometer data stored to - * device struct. - */ -FREENECTAPI int freenect_update_tilt_state(freenect_device *dev); - -/** - * Retrieve the tilt state from a device - * - * @param dev Device to retrieve tilt state from - * - * @return The tilt state struct of the device - */ -FREENECTAPI freenect_raw_tilt_state* freenect_get_tilt_state(freenect_device *dev); - -/** - * Return the tilt state, in degrees with respect to the horizon - * - * @param state The tilt state struct from a device - * - * @return Current degree of tilt of the device - */ -FREENECTAPI double freenect_get_tilt_degs(freenect_raw_tilt_state *state); - -/** - * Set the tilt state of the device, in degrees with respect to the - * horizon. Uses blocking control message call to update - * device. Function return does not reflect state of device, device - * may still be moving to new position after the function returns. Use - * freenect_get_tilt_status() to find current movement state. - * - * @param dev Device to set tilt state - * @param angle Angle the device should tilt to - * - * @return 0 on success, < 0 on error. - */ -FREENECTAPI int freenect_set_tilt_degs(freenect_device *dev, double angle); - -/** - * Return the movement state of the tilt motor (moving, stopped, etc...) - * - * @param state Raw state struct to get the tilt status code from - * - * @return Status code of the tilt device. See - * freenect_tilt_status_code enum for more info. - */ -FREENECTAPI freenect_tilt_status_code freenect_get_tilt_status(freenect_raw_tilt_state *state); - -/** - * Set the state of the LED. Uses blocking control message call to - * update device. - * - * @param dev Device to set the LED state - * @param option LED state to set on device. See freenect_led_options enum. - * - * @return 0 on success, < 0 on error - */ -FREENECTAPI int freenect_set_led(freenect_device *dev, freenect_led_options option); - -/** - * Get the axis-based gravity adjusted accelerometer state, as laid - * out via the accelerometer data sheet, which is available at - * - * http://www.kionix.com/Product%20Sheets/KXSD9%20Product%20Brief.pdf - * - * @param state State to extract accelerometer data from - * @param x Stores X-axis accelerometer state - * @param y Stores Y-axis accelerometer state - * @param z Stores Z-axis accelerometer state - */ -FREENECTAPI void freenect_get_mks_accel(freenect_raw_tilt_state *state, double* x, double* y, double* z); - -/** - * Get the number of video camera modes supported by the driver. This includes both RGB and IR modes. - * - * @return Number of video modes supported by the driver - */ -FREENECTAPI int freenect_get_video_mode_count(); - -/** - * Get the frame descriptor of the nth supported video mode for the - * video camera. - * - * @param mode_num Which of the supported modes to return information about - * - * @return A freenect_frame_mode describing the nth video mode - */ -FREENECTAPI freenect_frame_mode freenect_get_video_mode(int mode_num); - -/** - * Get the frame descriptor of the current video mode for the specified - * freenect device. - * - * @param dev Which device to return the currently-set video mode for - * - * @return A freenect_frame_mode describing the current video mode of the specified device - */ -FREENECTAPI freenect_frame_mode freenect_get_current_video_mode(freenect_device *dev); - -/** - * Convenience function to return a mode descriptor matching the - * specified resolution and video camera pixel format, if one exists. - * - * @param res Resolution desired - * @param fmt Pixel format desired - * - * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. - */ -FREENECTAPI freenect_frame_mode freenect_find_video_mode(freenect_resolution res, freenect_video_format fmt); - -/** - * Sets the current video mode for the specified device. If the - * freenect_frame_mode specified is not one provided by the driver - * e.g. from freenect_get_video_mode() or freenect_find_video_mode() - * then behavior is undefined. The current video mode cannot be - * changed while streaming is active. - * - * @param dev Device for which to set the video mode - * @param mode Frame mode to set - * - * @return 0 on success, < 0 if error - */ -FREENECTAPI int freenect_set_video_mode(freenect_device* dev, freenect_frame_mode mode); - -/** - * Get the number of depth camera modes supported by the driver. This includes both RGB and IR modes. - * - * @return Number of depth modes supported by the driver - */ -FREENECTAPI int freenect_get_depth_mode_count(); - -/** - * Get the frame descriptor of the nth supported depth mode for the - * depth camera. - * - * @param mode_num Which of the supported modes to return information about - * - * @return A freenect_frame_mode describing the nth depth mode - */ -FREENECTAPI freenect_frame_mode freenect_get_depth_mode(int mode_num); - -/** - * Get the frame descriptor of the current depth mode for the specified - * freenect device. - * - * @param dev Which device to return the currently-set depth mode for - * - * @return A freenect_frame_mode describing the current depth mode of the specified device - */ -FREENECTAPI freenect_frame_mode freenect_get_current_depth_mode(freenect_device *dev); - -/** - * Convenience function to return a mode descriptor matching the - * specified resolution and depth camera pixel format, if one exists. - * - * @param res Resolution desired - * @param fmt Pixel format desired - * - * @return A freenect_frame_mode that matches the arguments specified, if such a valid mode exists; otherwise, an invalid freenect_frame_mode. - */ -FREENECTAPI freenect_frame_mode freenect_find_depth_mode(freenect_resolution res, freenect_depth_format fmt); - -/** - * Sets the current depth mode for the specified device. The mode - * cannot be changed while streaming is active. - * - * @param dev Device for which to set the depth mode - * @param mode Frame mode to set - * - * @return 0 on success, < 0 if error - */ -FREENECTAPI int freenect_set_depth_mode(freenect_device* dev, const freenect_frame_mode mode); - -#ifdef __cplusplus -} -#endif - -#endif // - diff --git a/interface/external/freenect/lib/MacOS/libfreenect.a b/interface/external/freenect/lib/MacOS/libfreenect.a deleted file mode 100644 index 19a9fc1806dd4b8736eda1b53a85d47e41ed31e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68160 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 diff --git a/interface/external/freenect/lib/UNIX/libfreenect.a b/interface/external/freenect/lib/UNIX/libfreenect.a deleted file mode 100644 index 0b50cca696e785a7c81fc5dda536ec2fa7f5c8be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65880 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

-?yD@>w5T-IF)@+OxVR-{6C*d-4_^T-}rR`rt}_ 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 LCF7{zYX11&?VcKr diff --git a/interface/external/freenect/lib/UNIX/libfreenect_sync.a b/interface/external/freenect/lib/UNIX/libfreenect_sync.a deleted file mode 100644 index 85b7479a75d0e853c60e539d3a1f409cf3f3ca39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12952 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 From 9c6e5b23e08e21d86a2f70c16d9779d08fa145ff Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Jul 2013 22:44:47 -0700 Subject: [PATCH 41/63] Alignment fixes. --- interface/src/Avatar.cpp | 126 +++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 27ccf0db4b..6fa6f48707 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -148,121 +148,121 @@ void Avatar::initializeBodyBalls() { _bodyBall[ BODY_BALL_HEAD_BASE ].radius = 0.07; _bodyBall[ BODY_BALL_LEFT_COLLAR ].radius = 0.04; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].radius = 0.03; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02; _bodyBall[ BODY_BALL_LEFT_WRIST ].radius = 0.02; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].radius = 0.01; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].radius = 0.04; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02; _bodyBall[ BODY_BALL_RIGHT_WRIST ].radius = 0.02; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].radius = 0.01; - _bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04; + _bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].radius = 0.03; - _bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025; - _bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025; - _bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025; // specify the parent joint for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS; + _bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS; _bodyBall[ BODY_BALL_TORSO ].parentJoint = AVATAR_JOINT_TORSO; - _bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST; - _bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE; + _bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST; + _bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE; _bodyBall[ BODY_BALL_HEAD_BASE ].parentJoint = AVATAR_JOINT_HEAD_BASE; _bodyBall[ BODY_BALL_HEAD_TOP ].parentJoint = AVATAR_JOINT_HEAD_TOP; _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentJoint = AVATAR_JOINT_LEFT_COLLAR; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentJoint = AVATAR_JOINT_LEFT_SHOULDER; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW; - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW; + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentJoint = AVATAR_JOINT_RIGHT_COLLAR; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW; _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentJoint = AVATAR_JOINT_RIGHT_WRIST; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS; - _bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP; - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE; - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL; - _bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES; - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL; - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES; + _bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP; + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE; + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL; + _bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES; + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL; + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES; - //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentJoint = AVATAR_JOINT_LEFT_HIP; + //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentJoint = AVATAR_JOINT_LEFT_HIP; // specify the parent offset for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_TORSO ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_HEAD_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_HEAD_TOP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentOffset = glm::vec3(-0.1, -0.1, 0.0); + //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentOffset = glm::vec3(-0.1, -0.1, 0.0); // specify the parent BALL for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL; + _bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL; _bodyBall[ BODY_BALL_TORSO ].parentBall = BODY_BALL_PELVIS; - _bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO; - _bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST; + _bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO; + _bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST; _bodyBall[ BODY_BALL_HEAD_BASE ].parentBall = BODY_BALL_NECK_BASE; _bodyBall[ BODY_BALL_HEAD_TOP ].parentBall = BODY_BALL_HEAD_BASE; _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentBall = BODY_BALL_CHEST; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentBall = BODY_BALL_LEFT_COLLAR; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER; - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER; + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentBall = BODY_BALL_LEFT_WRIST; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentBall = BODY_BALL_CHEST; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER; _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentBall = BODY_BALL_RIGHT_ELBOW; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentBall = BODY_BALL_RIGHT_WRIST; - _bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS; + _bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS; - //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP; + //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP; - // _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH; - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP; + //_bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH; + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP; - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE; - _bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL; - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE; - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL; + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE; + _bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL; + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE; + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL; /* // to aid in hand-shaking and hand-holding, the right hand is not collidable - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false; - _bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false; - _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS].isCollidable = false; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false; + _bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false; + _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].isCollidable = false; */ } From 357ac05d35de71bae1371bba117e5098c8529ea6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Jul 2013 22:58:57 -0700 Subject: [PATCH 42/63] Removed debug logging. --- interface/src/Webcam.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index a81bec8ddd..28026697a9 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -108,16 +108,11 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) { glPointSize(4.0f); glBegin(GL_POINTS); float projectedScale = PREVIEW_HEIGHT / (float)_depthHeight; - int idx = 0; foreach (const Joint& joint, _joints) { if (joint.isValid) { - if (joint.projected.x == 0.0f && joint.projected.y == 0.0f) { - printLog("%d\n", idx); - } glVertex2f(depthLeft + joint.projected.x * projectedScale, top - PREVIEW_HEIGHT + joint.projected.y * projectedScale); } - idx++; } glEnd(); glPointSize(1.0f); From 6c840a9ec3a93da9148989fd03e7f7825540fa8a Mon Sep 17 00:00:00 2001 From: atlante45 Date: Mon, 8 Jul 2013 13:37:47 +0200 Subject: [PATCH 43/63] Functionnal version of minecraft import --- interface/src/Application.cpp | 16 +- libraries/voxels/src/VoxelTree.cpp | 510 +++++++++++++++++++---------- libraries/voxels/src/VoxelTree.h | 1 + 3 files changed, 342 insertions(+), 185 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 70f7287aab..3a8bb48450 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1322,7 +1322,7 @@ void Application::exportVoxels() { void Application::importVoxels() { QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation, - tr("Sparse Voxel Octree Files, Square PNG (*.svo *.png)")); + tr("Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)")); QByteArray fileNameAscii = fileNameString.toAscii(); const char* fileName = fileNameAscii.data(); @@ -1343,8 +1343,10 @@ void Application::importVoxels() { } importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height()); - } else { + } else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) { importVoxels.readFromSVOFile(fileName); + } else { + importVoxels.readFromSchematicsFile(fileName); } VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); @@ -1534,11 +1536,11 @@ void Application::initMenu() { _voxelPaintColor->setIcon(createSwatchIcon(paintColor)); (_destructiveAddVoxel = voxelMenu->addAction("Create Voxel is Destructive"))->setCheckable(true); - voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E); - voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I); - voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X); - voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C); - voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V); + voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E); + voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I); + voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X); + voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C); + voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V); QMenu* debugMenu = menuBar->addMenu("Debug"); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b16425370e..7ecba71f3c 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -24,10 +24,12 @@ #include "VoxelConstants.h" #include "CoverageMap.h" #include "SquarePixelMap.h" +#include "Tags.h" #include + int boundaryDistanceForRenderLevel(unsigned int renderLevel) { float voxelSizeScale = 50000.0f; return voxelSizeScale / powf(2, renderLevel); @@ -73,17 +75,17 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati // Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node. // stops recursion if operation function returns false. -void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation, +void VoxelTree::recurseTreeWithOperationDistanceSorted(RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { recurseNodeWithOperationDistanceSorted(rootNode, operation, point, extraData); } // Recurses voxel node with an operation function -void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, +void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseVoxelTreeOperation operation, const glm::vec3& point, void* extraData) { if (operation(node, extraData)) { // determine the distance sorted order of our children - + VoxelNode* sortedChildren[NUMBER_OF_CHILDREN]; float distancesToChildren[NUMBER_OF_CHILDREN]; int indexOfChildren[NUMBER_OF_CHILDREN]; // not really needed @@ -93,15 +95,15 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseV VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { // chance to optimize, doesn't need to be actual distance!! Could be distance squared - float distanceSquared = childNode->distanceSquareToPoint(point); + float distanceSquared = childNode->distanceSquareToPoint(point); //printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance); //childNode->printDebugDetails(""); currentCount = insertIntoSortedArrays((void*)childNode, distanceSquared, i, - (void**)&sortedChildren, (float*)&distancesToChildren, + (void**)&sortedChildren, (float*)&distancesToChildren, (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); } } - + for (int i = 0; i < currentCount; i++) { VoxelNode* childNode = sortedChildren[i]; if (childNode) { @@ -114,20 +116,20 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseV } -VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, +VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const { // find the appropriate branch index based on this ancestorNode if (*needleCode > 0) { int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode); VoxelNode* childNode = ancestorNode->getChildAtIndex(branchForNeedle); - + if (childNode) { if (*childNode->getOctalCode() == *needleCode) { - - // If the caller asked for the parent, then give them that too... - if (parentOfFoundNode) { - *parentOfFoundNode = ancestorNode; - } + + // If the caller asked for the parent, then give them that too... + if (parentOfFoundNode) { + *parentOfFoundNode = ancestorNode; + } // the fact that the number of sections is equivalent does not always guarantee // that this is the same node, however due to the recursive traversal // we know that this is our node @@ -138,7 +140,7 @@ VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, } } } - + // we've been given a code we don't have a node for // return this node as the last created parent return ancestorNode; @@ -168,7 +170,7 @@ VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char } } -int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, int bytesLeftToRead, +int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, int bytesLeftToRead, bool includeColor, bool includeExistsBits) { // give this destination node the child mask from the packet const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF; @@ -205,22 +207,22 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, if (!nodeWasDirty && nodeIsDirty) { _nodesChangedFromBitstream++; } - this->voxelsColored++; - this->voxelsColoredStats.updateAverage(1); + this->voxelsColored++; + this->voxelsColoredStats.updateAverage(1); } } // give this destination node the child mask from the packet - unsigned char childrenInTreeMask = includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST; + unsigned char childrenInTreeMask = includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST; unsigned char childMask = *(nodeData + bytesRead + (includeExistsBits ? sizeof(childrenInTreeMask) : 0)); int childIndex = 0; bytesRead += 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 - - if (oneAtBit(childMask, childIndex)) { + + if (oneAtBit(childMask, childIndex)) { if (!destinationNode->getChildAtIndex(childIndex)) { // add a child at that index, if it doesn't exist bool nodeWasDirty = destinationNode->isDirty(); @@ -235,14 +237,14 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, this->voxelsCreated++; this->voxelsCreatedStats.updateAverage(this->voxelsCreated); } - + // tell the child to read the subsequent data bytesRead += readNodeData(destinationNode->getChildAtIndex(childIndex), nodeData + bytesRead, bytesLeftToRead - bytesRead, includeColor, includeExistsBits); } childIndex++; } - + if (includeExistsBits) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -253,21 +255,21 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, destinationNode->safeDeepDeleteChildAtIndex(i, stagedForDeletion); _isDirty = true; // by definition! } - } + } } return bytesRead; } -void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes, +void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int bufferSizeBytes, bool includeColor, bool includeExistsBits, VoxelNode* destinationNode) { int bytesRead = 0; unsigned char* bitstreamAt = bitstream; - + // If destination node is not included, set it to root if (!destinationNode) { destinationNode = rootNode; } - + _nodesChangedFromBitstream = 0; // Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes @@ -292,8 +294,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), includeColor, includeExistsBits); // skip bitstream to new startPoint bitstreamAt += theseBytesRead; @@ -333,7 +335,7 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, b args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer); args.deleteLastChild = false; args.pathChanged = false; - + VoxelNode* node = rootNode; deleteVoxelCodeFromTreeRecursion(node, &args); } @@ -343,11 +345,11 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat int lengthOfNodeCode = numberOfThreeBitSectionsInCode(node->getOctalCode()); - // Since we traverse the tree in code order, we know that if our code + // Since we traverse the tree in code order, we know that if our code // matches, then we've reached our target node. if (lengthOfNodeCode == args->lengthOfCode) { // we've reached our target, depending on how we're called we may be able to operate on it - // if we're in "stage" mode, then we can could have the node staged, otherwise we can't really delete + // if we're in "stage" mode, then we can could have the node staged, otherwise we can't really delete // it here, we need to recurse up, and delete it there. So we handle these cases the same to keep // the logic consistent. args->deleteLastChild = true; @@ -357,9 +359,9 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat // Ok, we know we haven't reached our target node yet, so keep looking int childIndex = branchIndexWithDescendant(node->getOctalCode(), args->codeBuffer); VoxelNode* childNode = node->getChildAtIndex(childIndex); - + // If there is no child at the target location, and the current parent node is a colored leaf, - // then it means we were asked to delete a child out of a larger leaf voxel. + // then it means we were asked to delete a child out of a larger leaf voxel. // We support this by breaking up the parent voxel into smaller pieces. if (!childNode && node->isLeaf() && node->isColored()) { // we need to break up ancestors until we get to the right level @@ -375,7 +377,7 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat } } int lengthOfancestorNode = numberOfThreeBitSectionsInCode(ancestorNode->getOctalCode()); - + // If we've reached the parent of the target, then stop breaking up children if (lengthOfancestorNode == (args->lengthOfCode - 1)) { break; @@ -388,11 +390,11 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat } _isDirty = true; args->pathChanged = true; - + // ends recursion, unwinds up stack return; } - + // if we don't have a child and we reach this point, then we actually know that the parent // isn't a colored leaf, and the child branch doesn't exist, so there's nothing to do below and // we can safely return, ending the recursion and unwinding @@ -415,12 +417,12 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat // track our tree dirtiness _isDirty = true; - + // track that path has changed args->pathChanged = true; // If we're in collapseEmptyTrees mode, and this was the last child of this node, then we also want - // to delete this node. This will collapse the empty tree above us. + // to delete this node. This will collapse the empty tree above us. if (args->collapseEmptyTrees && node->getChildCount() == 0) { // Can't delete the root this way. if (node == rootNode) { @@ -430,7 +432,7 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything } } - + // If the lower level did some work, then we need to let this node know, so it can // do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc if (args->pathChanged) { @@ -460,9 +462,9 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool d args.destructive = destructive; args.pathChanged = false; - + VoxelNode* node = rootNode; - + readCodeColorBufferToTreeRecursion(node, &args); } @@ -472,7 +474,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD int lengthOfNodeCode = numberOfThreeBitSectionsInCode(node->getOctalCode()); - // Since we traverse the tree in code order, we know that if our code + // Since we traverse the tree in code order, we know that if our code // matches, then we've reached our target node. if (lengthOfNodeCode == args->lengthOfCode) { // we've reached our target -- we might have found our node, but that node might have children. @@ -488,7 +490,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD printLog("WARNING! operation would require deleting children, add Voxel ignored!\n "); } } - + // If we get here, then it means, we either had a true leaf to begin with, or we were in // destructive mode and we deleted all the child trees. So we can color. if (node->isLeaf()) { @@ -499,7 +501,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD memcpy(newColor, args->codeColorBuffer + octalCodeBytes, SIZE_OF_COLOR_DATA); newColor[SIZE_OF_COLOR_DATA] = 1; node->setColor(newColor); - + // It's possible we just reset the node to it's exact same color, in // which case we don't consider this to be dirty... if (node->isDirty()) { @@ -515,7 +517,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD // Ok, we know we haven't reached our target node yet, so keep looking int childIndex = branchIndexWithDescendant(node->getOctalCode(), args->codeColorBuffer); VoxelNode* childNode = node->getChildAtIndex(childIndex); - + // If the branch we need to traverse does not exist, then create it on the way down... if (!childNode) { childNode = node->addChildAtIndex(childIndex); @@ -525,7 +527,7 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD readCodeColorBufferToTreeRecursion(childNode, args); // Unwinding... - + // If the lower level did some work, then we need to let this node know, so it can // do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc if (args->pathChanged) { @@ -534,18 +536,18 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelNode* node, void* extraD } void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) { - //unsigned short int itemNumber = (*((unsigned short int*)&bitstream[sizeof(PACKET_HEADER)])); - int atByte = sizeof(short int) + sizeof(PACKET_HEADER); - unsigned char* voxelCode = (unsigned char*)&bitstream[atByte]; - while (atByte < bufferSizeBytes) { - int codeLength = numberOfThreeBitSectionsInCode(voxelCode); - int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA; + //unsigned short int itemNumber = (*((unsigned short int*)&bitstream[sizeof(PACKET_HEADER)])); + int atByte = sizeof(short int) + sizeof(PACKET_HEADER); + unsigned char* voxelCode = (unsigned char*)&bitstream[atByte]; + while (atByte < bufferSizeBytes) { + int codeLength = numberOfThreeBitSectionsInCode(voxelCode); + int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA; - deleteVoxelCodeFromTree(voxelCode, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE); + deleteVoxelCodeFromTree(voxelCode, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE); - voxelCode+=voxelDataSize; - atByte+=voxelDataSize; - } + voxelCode+=voxelDataSize; + atByte+=voxelDataSize; + } } void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { @@ -591,7 +593,7 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { printTreeForDebugging(startNode->getChildAtIndex(l)); } } - } + } } // Note: this is an expensive call. Don't call it unless you really need to reaverage the entire tree (from startNode) @@ -622,15 +624,15 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { char octets; unsigned int lengthInBytes; - + int totalBytesRead = 0; if(file.is_open()) { - printLog("loading file...\n"); + printLog("loading file...\n"); bool bail = false; while (!file.eof() && !bail) { file.get(octets); totalBytesRead++; - lengthInBytes = bytesRequiredForCodeLength(octets) - 1; + lengthInBytes = bytesRequiredForCodeLength(octets) - 1; unsigned char * voxelData = new unsigned char[lengthInBytes + 1 + 3]; voxelData[0]=octets; char byte; @@ -658,7 +660,7 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { voxelData[lengthInBytes+2] = std::max(0,std::min(255,green + colorRandomizer)); voxelData[lengthInBytes+3] = std::max(0,std::min(255,blue + colorRandomizer)); printLog("voxel color after rand red:%d, green:%d, blue:%d\n", - voxelData[lengthInBytes+1], voxelData[lengthInBytes+2], voxelData[lengthInBytes+3]); + voxelData[lengthInBytes+1], voxelData[lengthInBytes+2], voxelData[lengthInBytes+3]); //printVoxelCode(voxelData); this->readCodeColorBufferToTree(voxelData); @@ -678,7 +680,7 @@ VoxelNode* VoxelTree::getVoxelAt(float x, float y, float z, float s) const { return node; } -void VoxelTree::createVoxel(float x, float y, float z, float s, +void VoxelTree::createVoxel(float x, float y, float z, float s, unsigned char red, unsigned char green, unsigned char blue, bool destructive) { unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue); this->readCodeColorBufferToTree(voxelData, destructive); @@ -698,13 +700,13 @@ void VoxelTree::createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, r } } -void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float voxelSize, - bool solid, creationMode mode, bool destructive, bool debug) { - +void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float voxelSize, + bool solid, creationMode mode, bool destructive, bool debug) { + bool wantColorRandomizer = (mode == RANDOM); bool wantNaturalSurface = (mode == NATURAL); bool wantNaturalColor = (mode == NATURAL); - + // About the color of the sphere... we're going to make this sphere be a mixture of two colors // in NATURAL mode, those colors will be green dominant and blue dominant. In GRADIENT mode we // will randomly pick which color family red, green, or blue to be dominant. In RANDOM mode we @@ -729,7 +731,7 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v g2 = (dominantColor2 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100); b2 = (dominantColor2 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100); } - + // We initialize our rgb to be either "grey" in case of randomized surface, or // the average of the gradient, in the case of the gradient sphere. unsigned char red = wantColorRandomizer ? 128 : (r1 + r2) / 2; // average of the colors @@ -737,7 +739,7 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v unsigned char blue = wantColorRandomizer ? 128 : (b1 + b2) / 2; // I want to do something smart like make these inside circles with bigger voxels, but this doesn't seem to work. - float thisVoxelSize = voxelSize; // radius / 2.0f; + float thisVoxelSize = voxelSize; // radius / 2.0f; float thisRadius = 0.0; if (!solid) { thisRadius = radius; // just the outer surface @@ -750,63 +752,63 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v while (!lastLayer) { lastLayer = (thisRadius + (voxelSize * 2.0) >= radius); - // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize + // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize // small enough to not skip any voxels we can calculate theta from our desired arc length // lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R - // lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r + // lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r float angleDelta = (thisVoxelSize / thisRadius); if (debug) { int percentComplete = 100 * (thisRadius/radius); - printLog("percentComplete=%d\n",percentComplete); + printLog("percentComplete=%d\n",percentComplete); } - + for (float theta=0.0; theta <= 2 * M_PI; theta += angleDelta) { for (float phi=0.0; phi <= M_PI; phi += angleDelta) { bool naturalSurfaceRendered = false; float x = xc + thisRadius * cos(theta) * sin(phi); float y = yc + thisRadius * sin(theta) * sin(phi); float z = zc + thisRadius * cos(phi); - - // if we're on the outer radius, then we do a couple of things differently. - // 1) If we're in NATURAL mode we will actually draw voxels from our surface outward (from the surface) up + + // if we're on the outer radius, then we do a couple of things differently. + // 1) If we're in NATURAL mode we will actually draw voxels from our surface outward (from the surface) up // some random height. This will give our sphere some contours. // 2) In all modes, we will use our "outer" color to draw the voxels. Otherwise we will use the average color if (lastLayer) { - if (false && debug) { + if (false && debug) { printLog("adding candy shell: theta=%f phi=%f thisRadius=%f radius=%f\n", - theta, phi, thisRadius,radius); + theta, phi, thisRadius,radius); } switch (mode) { - case RANDOM: { - red = randomColorValue(165); - green = randomColorValue(165); - blue = randomColorValue(165); - } break; - case GRADIENT: { - float gradient = (phi / M_PI); - red = r1 + ((r2 - r1) * gradient); - green = g1 + ((g2 - g1) * gradient); - blue = b1 + ((b2 - b1) * gradient); - } break; - case NATURAL: { - glm::vec3 position = glm::vec3(theta,phi,radius); - float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) - + .125f * glm::perlin(position * 16.f); - float gradient = (1.0f + perlin)/ 2.0f; - red = (unsigned char)std::min(255, std::max(0, (int)(r1 + ((r2 - r1) * gradient)))); - green = (unsigned char)std::min(255, std::max(0, (int)(g1 + ((g2 - g1) * gradient)))); - blue = (unsigned char)std::min(255, std::max(0, (int)(b1 + ((b2 - b1) * gradient)))); - if (debug) { - printLog("perlin=%f gradient=%f color=(%d,%d,%d)\n",perlin, gradient, red, green, blue); - } - } break; + case RANDOM: { + red = randomColorValue(165); + green = randomColorValue(165); + blue = randomColorValue(165); + } break; + case GRADIENT: { + float gradient = (phi / M_PI); + red = r1 + ((r2 - r1) * gradient); + green = g1 + ((g2 - g1) * gradient); + blue = b1 + ((b2 - b1) * gradient); + } break; + case NATURAL: { + glm::vec3 position = glm::vec3(theta,phi,radius); + float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) + + .125f * glm::perlin(position * 16.f); + float gradient = (1.0f + perlin)/ 2.0f; + red = (unsigned char)std::min(255, std::max(0, (int)(r1 + ((r2 - r1) * gradient)))); + green = (unsigned char)std::min(255, std::max(0, (int)(g1 + ((g2 - g1) * gradient)))); + blue = (unsigned char)std::min(255, std::max(0, (int)(b1 + ((b2 - b1) * gradient)))); + if (debug) { + printLog("perlin=%f gradient=%f color=(%d,%d,%d)\n",perlin, gradient, red, green, blue); + } + } break; } if (wantNaturalSurface) { // for natural surfaces, we will render up to 16 voxel's above the surface of the sphere glm::vec3 position = glm::vec3(theta,phi,radius); - float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) - + .125f * glm::perlin(position * 16.f); + float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f) + + .125f * glm::perlin(position * 16.f); float gradient = (1.0f + perlin)/ 2.0f; int height = (4 * gradient)+1; // make it at least 4 thick, so we get some averaging @@ -835,8 +837,8 @@ int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const // call the recursive version, this will add all found colored node roots to the bag int currentSearchLevel = 0; - - int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, + + int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode, viewFrustum, bag, deltaViewFrustum, lastViewFrustum); return levelReached; } @@ -890,7 +892,7 @@ public: bool findSpherePenetrationOp(VoxelNode* node, void* extraData) { SphereArgs* args = static_cast(extraData); - + // coarse check against bounds const AABox& box = node->getAABox(); if (!box.expandedContains(args->center, args->radius)) { @@ -904,7 +906,7 @@ bool findSpherePenetrationOp(VoxelNode* node, void* extraData) { if (box.findSpherePenetration(args->center, args->radius, nodePenetration)) { args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE); args->found = true; - } + } } return false; } @@ -927,7 +929,7 @@ public: bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) { CapsuleArgs* args = static_cast(extraData); - + // coarse check against bounds const AABox& box = node->getAABox(); if (!box.expandedIntersectsSegment(args->start, args->end, args->radius)) { @@ -941,7 +943,7 @@ bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) { if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) { args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE); args->found = true; - } + } } return false; } @@ -953,36 +955,36 @@ bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& return args.found; } -int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, +int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag, bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) { - // Keep track of how deep we've searched. + // Keep track of how deep we've searched. currentSearchLevel++; // If we've passed our max Search Level, then stop searching. return last level searched if (currentSearchLevel > maxSearchLevel) { return currentSearchLevel-1; - } + } // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! if (!node->isInView(viewFrustum)) { return currentSearchLevel; } - + // Ok, this is a little tricky, each child may have been deeper than the others, so we need to track // how deep each child went. And we actually return the maximum of each child. We use these variables below // when we recurse the children. int thisLevel = currentSearchLevel; int maxChildLevel = thisLevel; - + VoxelNode* inViewChildren[NUMBER_OF_CHILDREN]; float distancesToChildren[NUMBER_OF_CHILDREN]; int positionOfChildren[NUMBER_OF_CHILDREN]; int inViewCount = 0; int inViewNotLeafCount = 0; int inViewWithColorCount = 0; - + // for each child node, check to see if they exist, are colored, and in view, and if so // add them to our distance ordered array of children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -992,22 +994,22 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe bool childIsLeaf = (childNode && childNode->isLeaf()); if (childIsInView) { - + // track children in view as existing and not a leaf if (!childIsLeaf) { inViewNotLeafCount++; } - + // track children with actual color if (childIsColored) { inViewWithColorCount++; } - + float distance = childNode->distanceToCamera(viewFrustum); - + if (distance < boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1)) { - inViewCount = insertIntoSortedArrays((void*)childNode, distance, i, - (void**)&inViewChildren, (float*)&distancesToChildren, + inViewCount = insertIntoSortedArrays((void*)childNode, distance, i, + (void**)&inViewChildren, (float*)&distancesToChildren, (int*)&positionOfChildren, inViewCount, NUMBER_OF_CHILDREN); } } @@ -1023,7 +1025,7 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe } else { // at this point, we need to iterate the children who are in view, even if not colored // and we need to determine if there's a deeper tree below them that we care about. We will iterate - // these based on which tree is closer. + // these based on which tree is closer. for (int i = 0; i < inViewCount; i++) { VoxelNode* childNode = inViewChildren[i]; thisLevel = currentSearchLevel; // reset this, since the children will munge it up @@ -1035,8 +1037,8 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe return maxChildLevel; } -int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, - EncodeBitstreamParams& params) const { +int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, + EncodeBitstreamParams& params) const { // How many bytes have we written so far at this level; int bytesWritten = 0; @@ -1062,10 +1064,10 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, codeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(node->getOctalCode())); memcpy(outputBuffer, node->getOctalCode(), codeLength); } - + outputBuffer += codeLength; // move the pointer bytesWritten += codeLength; // keep track of byte count - availableBytes -= codeLength; // keep track or remaining space + availableBytes -= codeLength; // keep track or remaining space int currentEncodeLevel = 0; int childBytesWritten = encodeTreeBitstreamRecursion(node, outputBuffer, availableBytes, bag, params, currentEncodeLevel); @@ -1081,7 +1083,7 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, // if we wrote child bytes, then return our result of all bytes written if (childBytesWritten) { - bytesWritten += childBytesWritten; + bytesWritten += childBytesWritten; } else { // otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code bytesWritten = 0; @@ -1089,7 +1091,7 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, return bytesWritten; } -int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, +int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel) const { // How many bytes have we written so far at this level; @@ -1119,8 +1121,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (!node->isInView(*params.viewFrustum)) { return bytesAtThisLevel; } - - + + // If the user also asked for occlusion culling, check if this node is occluded, but only if it's not a leaf. // leaf occlusion is handled down below when we check child nodes if (params.wantOcclusionCulling && !node->isLeaf()) { @@ -1142,7 +1144,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp //args->subtreeVoxelsSkipped += (subArgs.voxelsTouched - 1); //args->totalVoxels += (subArgs.voxelsTouched - 1); - + return bytesAtThisLevel; } } else { @@ -1153,7 +1155,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } } } - + bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more! // At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level @@ -1168,7 +1170,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp const int BYTES_PER_COLOR = 3; const int CHILD_TREE_EXISTS_BYTES = sizeof(childrenExistInTreeBits) + sizeof(childrenExistInPacketBits); const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES; - + // Make our local buffer large enough to handle writing at this level in case we need to. unsigned char thisLevelBuffer[MAX_LEVEL_BYTES]; unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0]; @@ -1193,14 +1195,14 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (params.wantOcclusionCulling) { if (childNode) { // chance to optimize, doesn't need to be actual distance!! Could be distance squared - //float distanceSquared = childNode->distanceSquareToPoint(point); + //float distanceSquared = childNode->distanceSquareToPoint(point); //printLog("recurseNodeWithOperationDistanceSorted() CHECKING child[%d] point=%f,%f center=%f,%f distance=%f...\n", i, point.x, point.y, center.x, center.y, distance); //childNode->printDebugDetails(""); float distance = params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0; currentCount = insertIntoSortedArrays((void*)childNode, distance, i, - (void**)&sortedChildren, (float*)&distancesToChildren, + (void**)&sortedChildren, (float*)&distancesToChildren, (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); } } else { @@ -1218,7 +1220,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp int originalIndex = indexOfChildren[i]; bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum))); - + if (childIsInView) { // Before we determine consider this further, let's see if it's in our LOD scope... float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0; @@ -1226,7 +1228,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp if (distance < boundaryDistance) { inViewCount++; - + // track children in view as existing and not a leaf, if they're a leaf, // we don't care about recursing deeper on them, and we don't consider their // subtree to exist @@ -1244,13 +1246,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp AABox voxelBox = childNode->getAABox(); voxelBox.scale(TREE_SCALE); VoxelProjectedPolygon* voxelPolygon = new VoxelProjectedPolygon( - params.viewFrustum->getProjectedPolygon(voxelBox)); - + params.viewFrustum->getProjectedPolygon(voxelBox)); + // In order to check occlusion culling, the shadow has to be "all in view" otherwise, we will ignore occlusion // culling and proceed as normal if (voxelPolygon->getAllInView()) { CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, true); - + // In all cases where the shadow wasn't stored, we need to free our own memory. // In the case where it is stored, the CoverageMap will free memory for us later. if (result != STORED) { @@ -1269,8 +1271,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool childWasInView = (childNode && params.deltaViewFrustum && - (params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum))); - + (params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum))); + // track children with actual color, only if the child wasn't previously in view! if (childNode && childNode->isColored() && !childWasInView && !childIsOccluded) { childrenColoredBits += (1 << (7 - originalIndex)); @@ -1309,31 +1311,31 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // We only need to keep digging, if there is at least one child that is inView, and not a leaf. keepDiggingDeeper = (inViewNotLeafCount > 0); - + // If we have enough room to copy our local results into the buffer, then do so... if (availableBytes >= bytesAtThisLevel) { memcpy(outputBuffer, &thisLevelBuffer[0], bytesAtThisLevel); - + outputBuffer += bytesAtThisLevel; availableBytes -= bytesAtThisLevel; } else { bag.insert(node); return 0; } - + if (keepDiggingDeeper) { // at this point, we need to iterate the children who are in view, even if not colored - // and we need to determine if there's a deeper tree below them that we care about. + // and we need to determine if there's a deeper tree below them that we care about. // - // Since this recursive function assumes we're already writing, we know we've already written our + // Since this recursive function assumes we're already writing, we know we've already written our // childrenExistInPacketBits. But... we don't really know how big the child tree will be. And we don't know if // we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is - // write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they + // write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they // write something, we keep them in the bits, if they don't, we take them out. // // we know the last thing we wrote to the outputBuffer was our childrenExistInPacketBits. Let's remember where that was! unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistInPacketBits); - + // we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the // final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes, // and then later reshuffle these sections of our output buffer back into normal order. This allows us to make @@ -1350,19 +1352,19 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp int originalIndex = indexOfChildren[indexByDistance]; if (oneAtBit(childrenExistInPacketBits, originalIndex)) { - + int thisLevel = currentEncodeLevel; - // remember this for reshuffling + // remember this for reshuffling recursiveSliceStarts[originalIndex] = outputBuffer; - - int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag, + + int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, outputBuffer, availableBytes, bag, params, thisLevel); - - // remember this for reshuffling + + // remember this for reshuffling recursiveSliceSizes[originalIndex] = childTreeBytesOut; allSlicesSize += childTreeBytesOut; - + // if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space, // basically, the children below don't contain any info. @@ -1370,9 +1372,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // byte and the child exist byte. // assert(childTreeBytesOut != 1); - + // if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because... - // if it had colors it would write 1 byte for the color mask, + // if it had colors it would write 1 byte for the color mask, // and at least a color's worth of bytes for the node of colors. // if it had child trees (with something in them) then it would have the 1 byte for child mask // and some number of bytes of lower children... @@ -1402,9 +1404,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // reshuffle here... if (params.wantOcclusionCulling) { unsigned char tempReshuffleBuffer[MAX_VOXEL_PACKET_SIZE]; - + unsigned char* tempBufferTo = &tempReshuffleBuffer[0]; // this is our temporary destination - + // iterate through our childrenExistInPacketBits, these will be the sections of the packet that we copied subTree // details into. Unfortunately, they're in distance sorted order, not original index order. we need to put them // back into original distance order @@ -1417,32 +1419,32 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp tempBufferTo += thisSliceSize; } } - + // now that all slices are back in the correct order, copy them to the correct output buffer memcpy(firstRecursiveSlice, &tempReshuffleBuffer[0], allSlicesSize); } - - + + } // end keepDiggingDeeper - + return bytesAtThisLevel; } bool VoxelTree::readFromSVOFile(const char* fileName) { std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); if(file.is_open()) { - printLog("loading file %s...\n", fileName); - - // get file length.... + printLog("loading file %s...\n", fileName); + + // get file length.... unsigned long fileLength = file.tellg(); file.seekg( 0, std::ios::beg ); - + // 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); delete[] entireFile; - + file.close(); return true; } @@ -1455,12 +1457,164 @@ bool VoxelTree::readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension return true; } +bool VoxelTree::readFromSchematicsFile(const char *fileName) { + std::stringstream ss; + int err = retrieveData(fileName, ss); + if (err && TAG_Compound != ss.get()) { + printLog("ERROR: Invalid schematics file.\n"); + return false; + } + + ss.get(); + TagCompound schematics(ss); + if (!schematics.blockId() || !schematics.blocksData()) { + printLog("ERROR: Invalid schematics file.\n"); + return false; + } + + int max = (schematics.width() > schematics.length()) ? schematics.width() : schematics.length(); + max = (max > schematics.height()) ? max : schematics.height(); + + int scale = 1; + while (max > scale) scale *= 2; + float size = 1.0f / scale; + bool create = false; + int red = 128, green = 128, blue = 128; + + for (int y = 0; y < schematics.height(); ++y) { + for (int z = 0; z < schematics.length(); ++z) { + for (int x = 0; x < schematics.width(); ++x) { + int pos = ((y * schematics.length()) + z) * schematics.width() + x; + int id = schematics.blockId()[pos]; + + create = true; + switch (id) { + case 1: + case 14: + case 15: + case 16: + case 21: + case 56: + case 73: + case 74: + case 97: + case 129: red = 128; green = 128; blue = 128; break; + case 2: red = 77; green = 117; blue = 66; break; + case 3: + case 60: red = 116; green = 83; blue = 56; break; + case 4: red = 71; green = 71; blue = 71; break; + case 5: + case 125: red = 133; green = 94; blue = 62; break; + case 7: red = 35; green = 35; blue = 35; break; + case 8: + case 9: red = 100; green = 109; blue = 185; break; + case 10: + case 11: red = 192; green = 64; blue = 8; break; + case 12: red = 209; green = 199; blue = 155; break; + case 13: red = 96; green = 94; blue = 93; break; + case 17: red = 71; green = 56; blue = 35; break; + case 18: red = 76; green = 104; blue = 64; break; + case 19: red = 119; green = 119; blue = 37; break; + case 22: red = 22; green = 44; blue = 86; break; + case 23: + case 29: + case 33: + case 61: + case 62: + case 158: red = 61; green = 61; blue = 61; break; + case 24: red = 209; green = 202; blue = 156; break; + case 25: + case 58: + case 84: + case 137: red = 57; green = 38; blue = 25; break; + case 35: + red = 255; + green = 255; + blue = 255; + break; + case 41: red = 239; green = 238; blue = 105; break; + case 42: red = 146; green = 146; blue = 146; break; + case 43: + case 98: red = 161; green = 161; blue = 161; break; + case 44: + red = 161; + green = 161; + blue = 161; + break; + case 45: red = 121; green = 67; blue = 53; break; + case 46: red = 118; green = 36; blue = 13; break; + case 47: red = 155; green = 127; blue = 76; break; + case 48: red = 61; green = 79; blue = 61; break; + case 49: red = 52; green = 41; blue = 74; break; + case 52: red = 12; green = 66; blue = 71; break; + case 53: + case 67: + case 108: + case 109: + case 114: + case 128: + case 134: + case 135: + case 136: + case 156: + red = 150; + green = 121; + blue = 74; + break; + case 54: + case 95: + case 146: red = 155; green = 105; blue = 32; break; + case 57: red = 145; green = 219; blue = 215; break; + case 79: red = 142; green = 162; blue = 195; break; + case 80: red = 255; green = 255; blue = 255; break; + case 81: red = 8; green = 64; blue = 15; break; + case 82: red = 150; green = 155; blue = 166; break; + case 86: + case 91: red = 179; green = 108; blue = 17; break; + case 87: + case 153: red = 91; green = 31; blue = 30; break; + case 88: red = 68; green = 49; blue = 38; break; + case 89: red = 180; green = 134; blue = 65; break; + case 103: red = 141; green = 143; blue = 36; break; + case 110: red = 103; green = 92; blue = 95; break; + case 112: red = 45; green = 22; blue = 26; break; + case 121: red = 183; green = 178; blue = 129; break; + case 123: red = 101; green = 59; blue = 31; break; + case 124: red = 213; green = 178; blue = 123; break; + case 130: red = 38; green = 54; blue = 56; break; + case 133: red = 53; green = 84; blue = 85; break; + case 152: red = 131; green = 22; blue = 7; break; + case 155: red = 195; green = 192; blue = 185; break; + case 159: red = 195; green = 165; blue = 150; break; + case 170: red = 168; green = 139; blue = 15; break; + case 172: red = 140; green = 86; blue = 61; break; + case 173: red = 9; green = 9; blue = 9; break; + + default: + if (0 < id && id < 160 && false) { + red = 128; blue = 128; green = 128; + } else { + create = false; + } + break; + } + + if (create) { + createVoxel(size * x, size * y, size * z, size, red, green, blue, true); + } + } + } + } + + return true; +} + void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const { std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { - printLog("saving to file %s...\n", fileName); + printLog("saving to file %s...\n", fileName); VoxelNodeBag nodeBag; // If we were given a specific node, start from there, otherwise start from root @@ -1472,13 +1626,13 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const { static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static int bytesWritten = 0; - + while (!nodeBag.isEmpty()) { VoxelNode* subTree = nodeBag.extract(); EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); - + file.write((const char*)&outputBuffer[0], bytesWritten); } } @@ -1506,10 +1660,10 @@ void VoxelTree::copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinat static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static int bytesWritten = 0; - + while (!nodeBag.isEmpty()) { VoxelNode* subTree = nodeBag.extract(); - + // ask our tree to write a bitsteam EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS, chopLevels); bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); @@ -1523,17 +1677,17 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin VoxelNodeBag nodeBag; // If we were given a specific node, start from there, otherwise start from root nodeBag.insert(sourceTree->rootNode); - + static unsigned char outputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static int bytesWritten = 0; - + while (!nodeBag.isEmpty()) { VoxelNode* subTree = nodeBag.extract(); - + // ask our tree to write a bitsteam EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); 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); } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 72ea9e2326..3591141549 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -135,6 +135,7 @@ public: bool readFromSVOFile(const char* filename); // reads voxels from square image with alpha as a Y-axis bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); + bool readFromSchematicsFile(const char* filename); unsigned long getVoxelCount(); From 981e4c61b5b80d4b2e01cdb3ec6012717d6c8ac5 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Mon, 8 Jul 2013 13:45:38 +0200 Subject: [PATCH 44/63] Tags files missing in last commit --- libraries/voxels/src/Tags.cpp | 218 ++++++++++++++++++++++++++++++++++ libraries/voxels/src/Tags.h | 152 ++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 libraries/voxels/src/Tags.cpp create mode 100644 libraries/voxels/src/Tags.h diff --git a/libraries/voxels/src/Tags.cpp b/libraries/voxels/src/Tags.cpp new file mode 100644 index 0000000000..81cf4dbe95 --- /dev/null +++ b/libraries/voxels/src/Tags.cpp @@ -0,0 +1,218 @@ +#include "Tags.h" +#include + +#include +#include + +#include + +Tag::Tag(int tagId, std::stringstream &ss) : _tagId(tagId) { + int size(ss.get() << 8 | ss.get()); + + _name.clear(); + for (int i(0); i < size; ++i) { + _name += ss.get(); + } +} + +Tag* Tag::readTag(int tagId, std::stringstream &ss) { + + switch (tagId) { + case TAG_Byte: + return new TagByte(ss); + case TAG_Short: + return new TagShort(ss); + case TAG_Int: + return new TagInt(ss); + case TAG_Long: + return new TagLong(ss); + case TAG_Byte_Array: + return new TagByteArray(ss); + case TAG_String: + return new TagString(ss); + case TAG_List: + return new TagList(ss); + case TAG_Compound: + return new TagCompound(ss); + case TAG_Int_Array: + return new TagIntArray(ss); + case TAG_Float: + case TAG_Double: + default: + //TODO + exit(EXIT_FAILURE); + break; + } +} + +TagByte::TagByte(std::stringstream &ss) : Tag(TAG_Byte, ss) { + _data = ss.get(); +} + +TagShort::TagShort(std::stringstream &ss) : Tag(TAG_Short, ss) { + _data = ss.get() << 8 | ss.get(); +} + +TagInt::TagInt(std::stringstream &ss) : Tag(TAG_Int, ss) { + _data = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); +} + +TagLong::TagLong(std::stringstream &ss) : Tag(TAG_Long, ss) { + _data = (((int64_t) ss.get()) << 56 | ((int64_t) ss.get()) << 48 + | ((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32 + | ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get()); +} + +TagByteArray::TagByteArray(std::stringstream &ss) : Tag(TAG_Byte_Array, ss) { + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + + _data = new char[_size]; + for (int i(0); i < _size; ++i) { + _data[i] = ss.get(); + } +} + +TagString::TagString(std::stringstream &ss) : Tag(TAG_String, ss) { + _size = ss.get() << 8 | ss.get(); + + for (int i(0); i < _size; ++i) { + _data += ss.get(); + } +} + +TagList::TagList(std::stringstream &ss) : Tag(TAG_List, ss) { + _tagId = ss.get(); + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + + for (int i(0); i < _size; ++i) { + ss.putback(0); + ss.putback(0); + _data.push_back(readTag(_tagId, ss)); + } +} + +TagCompound::TagCompound(std::stringstream &ss) : Tag(TAG_Compound, ss), + _size(0), + _width(0), + _length(0), + _height(0), + _blocksId(NULL), + _blocksData(NULL) { + int tagId; + + while (TAG_End != (tagId = ss.get())) { + _data.push_back(readTag(tagId, ss)); + ++_size; + + if (TAG_Short == tagId) { + if ("Width" == _data.back()->name()) { + _width = ((TagShort*) _data.back())->data(); + } else if ("Height" == _data.back()->name()) { + _height = ((TagShort*) _data.back())->data(); + } else if ("Length" == _data.back()->name()) { + _length = ((TagShort*) _data.back())->data(); + } + } else if (TAG_Byte_Array == tagId) { + if ("Blocks" == _data.back()->name()) { + _blocksId = ((TagByteArray*) _data.back())->data(); + } else if ("Data" == _data.back()->name()) { + _blocksData = ((TagByteArray*) _data.back())->data(); + } + } + } +} + +TagIntArray::TagIntArray(std::stringstream &ss) : Tag(TAG_Int_Array, ss) { + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + + _data = new int[_size]; + for (int i(0); i < _size; ++i) { + _data[i] = ss.get(); + } +} + +int retrieveData(std::string filename, std::stringstream &ss) { + std::ifstream file(filename.c_str(), std::ios::binary); + + int type = file.peek(); + if (0x0A == type) { + ss.flush(); + ss << file; + return 0; + } + if (0x1F == type) { + return ungzip(file, ss); + } + + return 1; +} + +int ungzip(std::ifstream &file, std::stringstream &ss) { + std::string gzipedBytes; + gzipedBytes.clear(); + ss.flush(); + + while (!file.eof()) { + gzipedBytes += (char) file.get(); + } + file.close(); + + if ( gzipedBytes.size() == 0 ) { + ss << gzipedBytes; + return 0; + } + + unsigned int full_length = gzipedBytes.size(); + unsigned int half_length = gzipedBytes.size()/2; + unsigned int uncompLength = full_length; + + char* uncomp = (char*) calloc(sizeof(char), uncompLength); + + z_stream strm; + strm.next_in = (Bytef *) gzipedBytes.c_str(); + strm.avail_in = gzipedBytes.size(); + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + + bool done = false; + + if (inflateInit2(&strm, (16+MAX_WBITS)) != Z_OK) { + free(uncomp); + return 1; + } + + while (!done) { + // If our output buffer is too small + if (strm.total_out >= uncompLength) { + // Increase size of output buffer + char* uncomp2 = (char*) calloc(sizeof(char), uncompLength + half_length); + memcpy( uncomp2, uncomp, uncompLength ); + uncompLength += half_length; + free(uncomp); + uncomp = uncomp2; + } + + strm.next_out = (Bytef *) (uncomp + strm.total_out); + strm.avail_out = uncompLength - strm.total_out; + + // Inflate another chunk. + int err = inflate (&strm, Z_SYNC_FLUSH); + if (err == Z_STREAM_END) done = true; + else if (err != Z_OK) break; + } + + if (inflateEnd (&strm) != Z_OK) { + free(uncomp); + return 1; + } + + for (size_t i=0; i +#include + +#include +#include +#include + +#define TAG_End 0 +#define TAG_Byte 1 +#define TAG_Short 2 +#define TAG_Int 3 +#define TAG_Long 4 +#define TAG_Float 5 +#define TAG_Double 6 +#define TAG_Byte_Array 7 +#define TAG_String 8 +#define TAG_List 9 +#define TAG_Compound 10 +#define TAG_Int_Array 11 + +int retrieveData(std::string filename, std::stringstream &ss); +int ungzip(std::ifstream &file, std::stringstream &ss); + +class Tag { + protected: + int _tagId; + std::string _name; + + public: + Tag(int tagId, std::stringstream &ss); + + int tagId() const {return _tagId;} + std::string name () const {return _name; } + + static Tag* readTag(int tagId, std::stringstream &ss); +}; + +class TagByte : public Tag { + private: + int8_t _data; + + public: + TagByte(std::stringstream &ss); + + int8_t data() const {return _data;} +}; + +class TagShort : public Tag { + private: + int16_t _data; + + public: + TagShort(std::stringstream &ss); + + int16_t data() const {return _data;} +}; + +class TagInt : public Tag { + private: + int32_t _data; + + public: + TagInt(std::stringstream &ss); + + int32_t data() const {return _data;} +}; + +class TagLong : public Tag { + private: + int64_t _data; + + public: + TagLong(std::stringstream &ss); + + int64_t data() const {return _data;} +}; + +class TagByteArray : public Tag { + private: + int _size; + char* _data; + + public: + TagByteArray(std::stringstream &ss); + + int size() const {return _size;} + char* data() const {return _data;} +}; + +class TagString : public Tag { + private: + int _size; + std::string _data; + + public: + TagString(std::stringstream &ss); + + int size() const {return _size;} + std::string data() const {return _data;} +}; + +class TagList : public Tag { + private: + int _tagId; + int _size; + std::list _data; + + public: + TagList(std::stringstream &ss); + + int tagId() const {return _tagId;} + int size () const {return _size; } + std::list data () const {return _data; } +}; + +class TagCompound : public Tag { + private: + int _size; + std::list _data; + + // Specific to schematics file + int _width; + int _length; + int _height; + char* _blocksData; + char* _blocksId; + + public: + TagCompound(std::stringstream &ss); + + int size () const {return _size; } + std::list data () const {return _data; } + + int width () const {return _width; } + int length () const {return _length; } + int height () const {return _height; } + char* blockId () const {return _blocksId; } + char* blocksData() const {return _blocksData;} +}; + +class TagIntArray : public Tag { + private: + int _size; + int* _data; + + public: + TagIntArray(std::stringstream &ss); + ~TagIntArray() {delete _data;} + + int size() const {return _size;} + int* data() const {return _data;} +}; From 4899f77a0cb9a782402681c6cd5583176afc52c6 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Mon, 8 Jul 2013 16:11:50 +0200 Subject: [PATCH 45/63] Complete color handling in minecraft import --- libraries/voxels/src/VoxelTree.cpp | 114 ++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 18 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 7ecba71f3c..e0cc6c985b 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1461,14 +1461,14 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { std::stringstream ss; int err = retrieveData(fileName, ss); if (err && TAG_Compound != ss.get()) { - printLog("ERROR: Invalid schematics file.\n"); + printLog("ERROR: Invalid schematic file.\n"); return false; } ss.get(); TagCompound schematics(ss); - if (!schematics.blockId() || !schematics.blocksData()) { - printLog("ERROR: Invalid schematics file.\n"); + if (!schematics.blockId()) { + printLog("ERROR: Can't read schematic data.\n"); return false; } @@ -1478,8 +1478,10 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { int scale = 1; while (max > scale) scale *= 2; float size = 1.0f / scale; + bool create = false; int red = 128, green = 128, blue = 128; + int count = 0; for (int y = 0; y < schematics.height(); ++y) { for (int z = 0; z < schematics.length(); ++z) { @@ -1528,18 +1530,55 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { case 84: case 137: red = 57; green = 38; blue = 25; break; case 35: - red = 255; - green = 255; - blue = 255; + switch (schematics.blocksData()[pos]) { + case 0: red = 234; green = 234; blue = 234; break; + case 1: red = 224; green = 140; blue = 84; break; + case 2: red = 185; green = 90; blue = 194; break; + case 3: red = 124; green = 152; blue = 208; break; + case 4: red = 165; green = 154; blue = 35; break; + case 5: red = 70; green = 187; blue = 61; break; + case 6: red = 206; green = 124; blue = 145; break; + case 7: red = 66; green = 66; blue = 66; break; + case 8: red = 170; green = 176; blue = 176; break; + case 9: red = 45; green = 108; blue = 35; break; + case 10: red = 130; green = 62; blue = 8; break; + case 11: red = 43; green = 51; blue = 29; break; + case 12: red = 73; green = 47; blue = 29; break; + case 13: red = 57; green = 76; blue = 36; break; + case 14: red = 165; green = 58; blue = 53; break; + case 15: red = 24; green = 24; blue = 24; break; + default: + create = false; + break; + } break; case 41: red = 239; green = 238; blue = 105; break; case 42: red = 146; green = 146; blue = 146; break; case 43: case 98: red = 161; green = 161; blue = 161; break; case 44: - red = 161; - green = 161; - blue = 161; + switch (schematics.blocksData()[pos]) { + case 0: red = 161; green = 161; blue = 161; break; + case 1: red = 209; green = 202; blue = 156; break; + case 2: red = 133; green = 94; blue = 62; break; + case 3: red = 71; green = 71; blue = 71; break; + case 4: red = 121; green = 67; blue = 53; break; + case 5: red = 161; green = 161; blue = 161; break; + case 6: red = 45; green = 22; blue = 26; break; + case 7: red = 195; green = 192; blue = 185; break; + default: + create = false; + break; + } + + if (create) { + createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); + count += 4; + create = false; + } break; case 45: red = 121; green = 67; blue = 53; break; case 46: red = 118; green = 36; blue = 13; break; @@ -1557,9 +1596,50 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { case 135: case 136: case 156: - red = 150; - green = 121; - blue = 74; + switch (id) { + case 53: + case 134: + case 135: + case 136: red = 133; green = 94; blue = 62; break; + case 67: red = 71; green = 71; blue = 71; break; + case 108: red = 121; green = 67; blue = 53; break; + case 109: red = 161; green = 161; blue = 161; break; + case 114: red = 45; green = 22; blue = 26; break; + case 128: red = 209; green = 202; blue = 156; break; + case 156: red = 195; green = 192; blue = 185; break; + default: + create = false; + break; + } + + if (create) { + createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); + + switch (schematics.blocksData()[pos]) { + case 0: + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 1: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 2: + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 3: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + break; + } + + count += 6; + create = false; + } break; case 54: case 95: @@ -1589,23 +1669,21 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { case 170: red = 168; green = 139; blue = 15; break; case 172: red = 140; green = 86; blue = 61; break; case 173: red = 9; green = 9; blue = 9; break; - default: - if (0 < id && id < 160 && false) { - red = 128; blue = 128; green = 128; - } else { - create = false; - } + create = false; break; } if (create) { createVoxel(size * x, size * y, size * z, size, red, green, blue, true); + ++count; } } } } + printLog("Created %d voxels frome minecraft import.\n", count); + return true; } From 4a22154737b991c7e6e268c3ee56caee164c3df5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 8 Jul 2013 11:35:42 -0700 Subject: [PATCH 46/63] Fix for leaning, smoothing. --- interface/src/Avatar.cpp | 5 +++++ interface/src/Webcam.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 174a0a4aa5..d471bd166b 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -306,6 +306,11 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA if (joints.size() > i && joints[i].isValid) { JointData data = { i, joints[i].orientation }; _joints.push_back(data); + + if (i == AVATAR_JOINT_CHEST) { + // if we have a chest rotation, don't apply lean based on head + estimatedPosition = glm::vec3(); + } } } } else { diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 28026697a9..2ca7260ffe 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -214,7 +214,7 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota if (!_joints[i].isValid) { continue; } - const float JOINT_SMOOTHING = 0.5f; + const float JOINT_SMOOTHING = 0.95f; _estimatedJoints[i].isValid = true; _estimatedJoints[i].position = glm::mix(_joints[i].position - origin, _estimatedJoints[i].position, JOINT_SMOOTHING); @@ -380,7 +380,9 @@ void FrameGrabber::grabFrame() { format = GL_RGB; Mat depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap()); - depth.convertTo(_grayDepthFrame, CV_8UC1, 256.0 / 2048.0); + const double EIGHT_BIT_MAX = 255; + const double ELEVEN_BIT_MAX = 2047; + depth.convertTo(_grayDepthFrame, CV_8UC1, EIGHT_BIT_MAX / ELEVEN_BIT_MAX); _userID = 0; XnUInt16 userCount = 1; @@ -493,6 +495,7 @@ bool FrameGrabber::init() { _userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback); _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER); + //_userGenerator.GetSkeletonCap().SetSmoothing(0.9f); _xnContext.StartGeneratingAll(); return true; From c43994fe03318ae097631a9cfd66129e8b40a151 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 8 Jul 2013 12:54:27 -0700 Subject: [PATCH 47/63] Orientation -> rotation. --- interface/src/Avatar.cpp | 2 +- interface/src/Webcam.cpp | 16 ++++++++-------- interface/src/Webcam.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index d471bd166b..ccec4cbdee 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -304,7 +304,7 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA _joints.clear(); for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { if (joints.size() > i && joints[i].isValid) { - JointData data = { i, joints[i].orientation }; + JointData data = { i, joints[i].rotation }; _joints.push_back(data); if (i == AVATAR_JOINT_CHEST) { diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 2ca7260ffe..6c7006c5f4 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -208,20 +208,21 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota origin = glm::mix(_joints[AVATAR_JOINT_LEFT_HIP].position, _joints[AVATAR_JOINT_RIGHT_HIP].position, 0.5f); } else if (_joints[AVATAR_JOINT_TORSO].isValid) { - origin = _joints[AVATAR_JOINT_TORSO].position + glm::vec3(0.0f, -0.09f, -0.01f); + const glm::vec3 TORSO_TO_PELVIS = glm::vec3(0.0f, -0.09f, -0.01f); + origin = _joints[AVATAR_JOINT_TORSO].position + TORSO_TO_PELVIS; } for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { if (!_joints[i].isValid) { continue; } - const float JOINT_SMOOTHING = 0.95f; + const float JOINT_SMOOTHING = 0.9f; _estimatedJoints[i].isValid = true; _estimatedJoints[i].position = glm::mix(_joints[i].position - origin, _estimatedJoints[i].position, JOINT_SMOOTHING); - _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, - _estimatedJoints[i].orientation, JOINT_SMOOTHING); + _estimatedJoints[i].rotation = safeMix(_joints[i].rotation, + _estimatedJoints[i].rotation, JOINT_SMOOTHING); } - _estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].orientation); + _estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].rotation); _estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position; } else { @@ -495,7 +496,6 @@ bool FrameGrabber::init() { _userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback); _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER); - //_userGenerator.GetSkeletonCap().SetSmoothing(0.9f); _xnContext.StartGeneratingAll(); return true; @@ -531,8 +531,8 @@ void FrameGrabber::updateHSVFrame(const Mat& frame, int format) { inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask); } -Joint::Joint(const glm::vec3& position, const glm::quat& orientation, const glm::vec3& projected) : - isValid(true), position(position), orientation(orientation), projected(projected) { +Joint::Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected) : + isValid(true), position(position), rotation(rotation), projected(projected) { } Joint::Joint() : isValid(false) { diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index be7fe8e90f..f0910c7bce 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -125,12 +125,12 @@ private: class Joint { public: - Joint(const glm::vec3& position, const glm::quat& orientation, const glm::vec3& projected); + Joint(const glm::vec3& position, const glm::quat& rotation, const glm::vec3& projected); Joint(); bool isValid; glm::vec3 position; - glm::quat orientation; + glm::quat rotation; glm::vec3 projected; }; From c5c9debed55794ca195a90541a6622afecafdcad Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 8 Jul 2013 13:03:53 -0700 Subject: [PATCH 48/63] Fix for combined gyro/depth camera. --- interface/src/Avatar.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index ccec4cbdee..c2c850c923 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -290,15 +290,17 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA Webcam* webcam = Application::getInstance()->getWebcam(); glm::vec3 estimatedPosition, estimatedRotation; if (gyros->isActive()) { - if (webcam->isActive()) { - estimatedPosition = webcam->getEstimatedPosition(); - } estimatedRotation = gyros->getEstimatedRotation(); - + } else if (webcam->isActive()) { - estimatedPosition = webcam->getEstimatedPosition(); estimatedRotation = webcam->getEstimatedRotation(); + } else { + return; + } + if (webcam->isActive()) { + estimatedPosition = webcam->getEstimatedPosition(); + // compute and store the joint rotations const JointVector& joints = webcam->getEstimatedJoints(); _joints.clear(); @@ -313,8 +315,6 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyA } } } - } else { - return; } _head.setPitch(estimatedRotation.x * amplifyAngle.x); _head.setYaw(estimatedRotation.y * amplifyAngle.y); From b1050c633bb721e026e9eb3d02df09a43ef8f899 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 8 Jul 2013 14:17:03 -0700 Subject: [PATCH 49/63] Suppress warnings for OpenNI. --- interface/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 54ff9a017d..cccb9b96b0 100755 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -101,6 +101,7 @@ find_package(OpenNI) if (OPENNI_FOUND) add_definitions(-DHAVE_OPENNI) include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS}) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENNI_INCLUDE_DIRS}") target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES}) endif (OPENNI_FOUND) From c298f78ae97659cb33b9dbd874d4e6f816c1d931 Mon Sep 17 00:00:00 2001 From: Eric Johnston Date: Mon, 8 Jul 2013 16:31:28 -0700 Subject: [PATCH 50/63] Fixed multi-touch event loop problems by selectively processing just the touch events. --- interface/src/Application.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 668ed4d7bc..20cf2d70ce 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -955,17 +955,16 @@ void Application::idle() { // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran if (diffclock(&_lastTimeIdle, &check) > IDLE_SIMULATE_MSECS) { - // We call processEvents() here because the idle timer takes priority over - // event handling in Qt, so when the framerate gets low events will pile up - // unless we handle them here. - // NOTE - this is commented out for now - causing event processing issues reported by Philip and Ryan - // birarda - July 3rd - // Added some safety catches which will enable us to test this. - // ej - July 3rd + // If we're using multi-touch look, immediately process any + // touch events, and no other events. + // This is necessary because id the idle() call takes longer than the + // interval between idle() calls, the event loop never gets to run, + // and touch events get delayed. if (_touchLook->isChecked()) { - int maxTimeMS = 1; - processEvents(QEventLoop::ExcludeSocketNotifiers, maxTimeMS); + sendPostedEvents(NULL, QEvent::TouchBegin); + sendPostedEvents(NULL, QEvent::TouchUpdate); + sendPostedEvents(NULL, QEvent::TouchEnd); } update(1.0f / _fps); From d38c1f081c56130dbb2bf486fc2b9d27b4e61708 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 10:25:46 +0200 Subject: [PATCH 51/63] Modified code according to review for UI job --- interface/src/Application.cpp | 51 +++++++------- interface/src/Application.h | 2 +- interface/src/Swatch.cpp | 119 ++++++++++++++------------------- interface/src/Swatch.h | 13 +++- interface/src/Tool.cpp | 32 +++++---- interface/src/Tool.h | 13 +++- interface/src/ToolsPalette.cpp | 39 +++++------ interface/src/ToolsPalette.h | 5 +- interface/src/Util.h | 4 -- 9 files changed, 132 insertions(+), 146 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 010db97c9c..2b81c5fcd8 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -200,7 +200,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), - _bytesCount(0) + _bytesCount(0), + _swatch(NULL) { _applicationStartupTime = startup_time; _window->setWindowTitle("Interface"); @@ -276,11 +277,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : FvUpdater::sharedUpdater()->SetFeedURL("https://s3-us-west-1.amazonaws.com/highfidelity/appcast.xml"); FvUpdater::sharedUpdater()->CheckForUpdatesSilent(); #endif - + initMenu(); - _swatch = new Swatch(_voxelPaintColor); - QRect available = desktop()->availableGeometry(); _window->resize(available.size()); _window->setVisible(true); @@ -727,7 +726,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: - _swatch->handleEvent(event->key(), _eyedropperMode->isChecked()); + _swatch.handleEvent(event->key(), _eyedropperMode->isChecked()); break; default: event->ignore(); @@ -1241,7 +1240,7 @@ void Application::increaseVoxelSize() { } void Application::resetSwatchColors() { - _swatch->reset(); + _swatch.reset(); } static QIcon createSwatchIcon(const QColor& color) { @@ -1547,6 +1546,8 @@ void Application::initMenu() { _voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this, SLOT(chooseVoxelPaintColor()), Qt::META | Qt::Key_C); + _swatch.setAction(_voxelPaintColor); + QColor paintColor(128, 128, 128); _voxelPaintColor->setData(paintColor); _voxelPaintColor->setIcon(createSwatchIcon(paintColor)); @@ -1679,7 +1680,7 @@ void Application::init() { _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(_addVoxelMode, 0, 0); _palette.addAction(_deleteVoxelMode, 0, 1); - _palette.addTool(_swatch); + _palette.addTool(&_swatch); _palette.addAction(_colorVoxelMode, 0, 2); _palette.addAction(_eyedropperMode, 0, 3); _palette.addAction(_selectVoxelMode, 0, 4); @@ -2434,28 +2435,28 @@ void Application::displayOverlay() { _palette.render(_glWidget->width(), _glWidget->height()); - if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value() != _swatch->getColor()) { - QColor color(_voxelPaintColor->data().value()); + if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value() != _swatch.getColor()) { + QColor color = _voxelPaintColor->data().value(); TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); - const char* line1("Assign this color to a swatch"); - const char* line2("by choosing a key from 1 to 8."); - double step(0.05f); - int left((_glWidget->width() - 300)/2); - int top(_glWidget->height()/40.0f); - double margin(10.0f); + const char line1[] = "Assign this color to a swatch"; + const char line2[] = "by choosing a key from 1 to 8."; + double step = 0.05f; + int left = (_glWidget->width() - 300) / 2; + int top = _glWidget->height() / 40.0f; + double margin = 10.0f; glBegin(GL_POLYGON); glColor3f(0.0f, 0.0f, 0.0f); - for (double a(M_PI); a < 1.5f*M_PI; a += step) { - glVertex2f(left + margin*cos(a), top + margin*sin(a)); + for (double a = M_PI; a < 1.5f * M_PI; a += step) { + glVertex2f(left + margin * cos(a), top + margin * sin(a)); } - for (double a(1.5f*M_PI); a < 2.0f*M_PI; a += step) { - glVertex2f(left + 280 + margin*cos(a), top + margin*sin(a)); + for (double a = 1.5f * M_PI; a < 2.0f*M_PI; a += step) { + glVertex2f(left + 280 + margin * cos(a), top + margin * sin(a)); } - for (double a(0.0f); a < 0.5f*M_PI; a += step) { - glVertex2f(left + 280 + margin*cos(a), top + 30 + margin*sin(a)); + for (double a = 0.0f; a < 0.5f * M_PI; a += step) { + glVertex2f(left + 280 + margin * cos(a), top + 30 + margin * sin(a)); } - for (double a(0.5f*M_PI); a < 1.0f*M_PI; a += step) { + for (double a = 0.5f*M_PI; a < 1.0f*M_PI; a += step) { glVertex2f(left + margin*cos(a), top + 30 + margin*sin(a)); } glEnd(); @@ -2475,7 +2476,7 @@ void Application::displayOverlay() { textRenderer.draw(left + 74, top + 28, line2); } else { - _swatch->checkColor(); + _swatch.checkColor(); } glPopMatrix(); @@ -3027,7 +3028,7 @@ void Application::loadSettings(QSettings* settings) { scanMenuBar(&Application::loadAction, settings); getAvatar()->loadData(settings); - _swatch->loadData(settings); + _swatch.loadData(settings); } @@ -3049,7 +3050,7 @@ void Application::saveSettings(QSettings* settings) { scanMenuBar(&Application::saveAction, settings); getAvatar()->saveData(settings); - _swatch->saveData(settings); + _swatch.saveData(settings); } void Application::importSettings() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 73ae597da4..8c6c26c7ff 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -361,7 +361,7 @@ private: int _bytesCount; ToolsPalette _palette; - Swatch* _swatch; + Swatch _swatch; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index 3b2e4ca4a4..ad0e3e420a 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -1,20 +1,18 @@ #include "Swatch.h" #include -Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), - _selected(1), - _textRenderer(MONO_FONT_FAMILY, 10, 100) { +Swatch::Swatch(QAction* action) : + Tool(action, 0, -1, -1), + _selected(1), + _textRenderer(MONO_FONT_FAMILY, 10, 100) { } void Swatch::reset() { - _colors[0].setRgb(237, 175, 0); - _colors[1].setRgb(161, 211, 72); - _colors[2].setRgb(51, 204, 204); - _colors[3].setRgb(63, 169, 245); - _colors[4].setRgb(193, 99, 122); - _colors[5].setRgb(255, 54, 69); - _colors[6].setRgb(124, 36, 36); - _colors[7].setRgb(63, 35, 19); + for (int i = 0; i < 8; ++i) { + _colors[i].setRgb(colorBase[i][0], + colorBase[i][1], + colorBase[i][2]); + } } QColor Swatch::getColor() { @@ -36,13 +34,13 @@ void Swatch::saveData(QSettings* settings) { settings->beginGroup("Swatch"); for (int i(0); i < SWATCH_SIZE; ++i) { - QString name("R1"); - name[1] = '1' + i; - settings->setValue(name, _colors[i].red()); - name[0] = 'G'; - settings->setValue(name, _colors[i].green()); - name[0] = 'B'; - settings->setValue(name, _colors[i].blue()); + QString rx("R1"), gx("G1"), bx("B1"); + rx[1] = '1' + i; + gx[1] = rx[1]; + bx[1] = rx[1]; + settings->setValue(rx, _colors[i].red()); + settings->setValue(gx, _colors[i].green()); + settings->setValue(bx, _colors[i].blue()); } settings->endGroup(); @@ -51,30 +49,15 @@ void Swatch::saveData(QSettings* settings) { void Swatch::loadData(QSettings* settings) { settings->beginGroup("Swatch"); - _colors[0].setRgb(settings->value("R1", 237).toInt(), - settings->value("G1", 175).toInt(), - settings->value("B1", 0).toInt()); - _colors[1].setRgb(settings->value("R2", 161).toInt(), - settings->value("G2", 211).toInt(), - settings->value("B2", 72).toInt()); - _colors[2].setRgb(settings->value("R3", 51).toInt(), - settings->value("G3", 204).toInt(), - settings->value("B3", 204).toInt()); - _colors[3].setRgb(settings->value("R4", 63).toInt(), - settings->value("G4", 169).toInt(), - settings->value("B4", 245).toInt()); - _colors[4].setRgb(settings->value("R5", 193).toInt(), - settings->value("G5", 99).toInt(), - settings->value("B5", 122).toInt()); - _colors[5].setRgb(settings->value("R6", 255).toInt(), - settings->value("G6", 54).toInt(), - settings->value("B6", 69).toInt()); - _colors[6].setRgb(settings->value("R7", 124).toInt(), - settings->value("G7", 36).toInt(), - settings->value("B7", 36).toInt()); - _colors[7].setRgb(settings->value("R8", 63).toInt(), - settings->value("G8", 35).toInt(), - settings->value("B8", 19).toInt()); + for (int i = 0; i < SWATCH_SIZE; ++i) { + QString rx("R1"), gx("G1"), bx("B1"); + rx[1] = '1' + i; + gx[1] = rx[1]; + bx[1] = rx[1]; + _colors[i].setRgb(settings->value(rx, colorBase[i][0]).toInt(), + settings->value(gx, colorBase[i][1]).toInt(), + settings->value(bx, colorBase[i][2]).toInt()); + } settings->endGroup(); @@ -118,8 +101,7 @@ void Swatch::handleEvent(int key, bool getColor) { _selected = next; _colors[_selected - 1] = _action->data().value(); } - } - else { + } else { _selected = next; QPixmap map(16, 16); map.fill(_colors[_selected - 1]); @@ -130,61 +112,58 @@ void Swatch::handleEvent(int key, bool getColor) { void Swatch::render(int width, int height) { char str[2]; - int margin = 0.10f*height; - height = 0.75f*height; + int margin = 0.10f * height; + height = 0.75f * height; glBegin(GL_QUADS); glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(0, 8*(height - margin) + margin); - glVertex2f(width, 8*(height - margin) + margin); + glVertex2f(0, 8 * (height - margin) + margin); + glVertex2f(width, 8 * (height - margin) + margin); glVertex2f(width, 0); glVertex2f(0, 0); glEnd(); - for (unsigned int i(0); i < SWATCH_SIZE; ++i) { + for (unsigned int i = 0; i < SWATCH_SIZE; ++i) { glBegin(GL_QUADS); glColor3f(_colors[i].redF(), _colors[i].greenF(), _colors[i].blueF()); - glVertex2f(margin, (i + 1)*(height - margin)); - glVertex2f(width - margin, (i + 1)*(height - margin)); - glVertex2f(width - margin, i*(height - margin) + margin); - glVertex2f(margin, i*(height - margin) + margin); + glVertex2f(margin , (i + 1) * (height - margin)); + glVertex2f(width - margin, (i + 1) * (height - margin)); + glVertex2f(width - margin, i * (height - margin) + margin); + glVertex2f(margin , i * (height - margin) + margin); glEnd(); if (_colors[i].lightness() < 50) { glBegin(GL_LINES); glColor3f(1.0f, 1.0f, 1.0f); - glVertex2f(margin, (i + 1)*(height - margin)); - glVertex2f(width - margin, (i + 1)*(height - margin)); + glVertex2f(margin , (i + 1) * (height - margin)); + glVertex2f(width - margin, (i + 1) * (height - margin)); - glVertex2f(width - margin, (i + 1)*(height - margin)); - glVertex2f(width - margin, i*(height - margin) + margin); + glVertex2f(width - margin, (i + 1) * (height - margin)); + glVertex2f(width - margin, i * (height - margin) + margin); - glVertex2f(width - margin, i*(height - margin) + margin); - glVertex2f(margin, i*(height - margin) + margin); + glVertex2f(width - margin, i * (height - margin) + margin); + glVertex2f(margin , i * (height - margin) + margin); - glVertex2f(margin, i*(height - margin) + margin); - glVertex2f(margin, (i + 1)*(height - margin)); + glVertex2f(margin , i * (height - margin) + margin); + glVertex2f(margin , (i + 1) * (height - margin)); glEnd(); - - - } - else { + } else { glColor3f(0.0f, 0.0f, 0.0f); } if (_selected == i + 1) { glBegin(GL_TRIANGLES); - glVertex2f(margin, (i + 1)*(height - margin) - margin); - glVertex2f(width/4 - margin, i*(height - margin) + height/2.0f); - glVertex2f(margin, i*(height - margin) + margin + margin); + glVertex2f(margin , (i + 1) * (height - margin) - margin); + glVertex2f(width/4 - margin, i * (height - margin) + height / 2.0f); + glVertex2f(margin , i * (height - margin) + margin + margin); glEnd(); } sprintf(str, "%d", i + 1); - _textRenderer.draw(3*width/4, (i + 1)*(height - margin) - 0.2f*height, str); + _textRenderer.draw(3 * width/4, (i + 1) * (height - margin) - 0.2f * height, str); } - glTranslated(0, 8*(height - margin) + margin + 0.075f*height, 0); + glTranslated(0, 8 * (height - margin) + margin + 0.075f * height, 0); } diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h index 6727f90086..e167cd05c4 100644 --- a/interface/src/Swatch.h +++ b/interface/src/Swatch.h @@ -8,16 +8,23 @@ #ifndef __interface__Swatch__ #define __interface__Swatch__ -#define SWATCH_SIZE 8 - #include "Tool.h" #include "ui/TextRenderer.h" +static const int SWATCH_SIZE = 8; +static const int colorBase[8][3] = {{237, 175, 0}, + {61, 211, 72}, + {51, 204, 204}, + {63, 169, 245}, + {193, 99, 122}, + {255, 54, 69}, + {124, 36, 36}, + {63, 35, 19}}; + class Swatch : public Tool { public: Swatch(QAction* action); - QColor getColor(); void checkColor(); void saveData(QSettings* settings); diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp index 48bcfe5af6..3ad5a648e5 100644 --- a/interface/src/Tool.cpp +++ b/interface/src/Tool.cpp @@ -4,10 +4,15 @@ #include #include -Tool::Tool(QAction *action, GLuint texture, int x, int y) : _texture(texture), - _action(action), - _x(x), - _y(y) { +Tool::Tool(QAction *action, GLuint texture, int x, int y) : + _texture(texture), + _action(action), + _x(x), + _y(y) { +} + +void Tool::setAction(QAction* action) { + _action = action; } bool Tool::isActive() { @@ -22,26 +27,25 @@ void Tool::render(int width, int height) { if (_action == 0 || _action->isChecked()) { glColor3f(1.0f, 1.0f, 1.0f); // reset gl color - } - else { + } else { glColor3f(0.3f, 0.3f, 0.3f); } glBegin(GL_QUADS); - glTexCoord2f(_x/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); - glVertex2f(0, height); + glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS); + glVertex2f(0 , height); - glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); + glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS); glVertex2f(width, height); - glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); - glVertex2f(width, 0); + glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS); + glVertex2f(width, 0); - glTexCoord2f(_x/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); - glVertex2f(0, 0); + glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS); + glVertex2f(0 , 0); glEnd(); glDisable(GL_TEXTURE_2D); - glTranslated(0, 1.10f*height, 0); + glTranslated(0, 1.10f * height, 0); } diff --git a/interface/src/Tool.h b/interface/src/Tool.h index 1ff854372e..9bea07a633 100644 --- a/interface/src/Tool.h +++ b/interface/src/Tool.h @@ -15,10 +15,21 @@ class QAction; + +// Number of rows and columns in the SVG file for the tool palette +static const int NUM_TOOLS_ROWS = 10; +static const int NUM_TOOLS_COLS = 2; +static const int SWATCHS_TOOLS_COUNT = 13; // 8 swatch + 5 tools + +static const int WIDTH_MIN = 47; // Minimal tools width +static const float TOOLS_RATIO = 40.0f / 60.0f; // ratio height/width of tools icons +static const float PAL_SCREEN_RATIO = 3.0f / 100.0f; // Percentage of the screeen width the palette is going to occupy + class Tool { public: - Tool(QAction *action, GLuint texture, int x, int y); + Tool(QAction* action, GLuint texture, int x, int y); + void setAction(QAction* action); bool isActive(); virtual void render(int width, int height); diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp index 2829ac3d58..341a128f8a 100644 --- a/interface/src/ToolsPalette.cpp +++ b/interface/src/ToolsPalette.cpp @@ -5,25 +5,19 @@ #include #include -ToolsPalette::ToolsPalette() { -} - void ToolsPalette::init(int screenWidth, int screenHeight) { - _width = 3*screenWidth/100; - if (_width < 47) { - _width = 47; - } - _height = 40*_width/62; + _width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth; + _height = TOOLS_RATIO * _width; - _left = screenWidth/100; - _top = (screenHeight - 12*_height)/2; + _left = screenWidth / 150; + _top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2; // Load SVG switchToResourcesParentIfRequired(); QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); // Prepare a QImage with desired characteritisc - QImage image(TOOLS_COLS*_width, TOOLS_ROWS*_height, QImage::Format_ARGB32); + QImage image(NUM_TOOLS_COLS * _width, NUM_TOOLS_ROWS * _height, QImage::Format_ARGB32); // Get QPainter that paints to the image QPainter painter(&image); @@ -36,11 +30,11 @@ void ToolsPalette::init(int screenWidth, int screenHeight) { glBindTexture(GL_TEXTURE_2D, _textureID); //generate the texture - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _textureImage.width(), _textureImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, - _textureImage.bits() ); + _textureImage.bits()); //texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -53,25 +47,22 @@ void ToolsPalette::addAction(QAction* action, int x, int y) { _tools.push_back(tmp); } -void ToolsPalette::addTool(Tool *tool) { +void ToolsPalette::addTool(Tool* tool) { _tools.push_back(tool); } void ToolsPalette::render(int screenWidth, int screenHeight) { - _width = 3*screenWidth/100; - if (_width < 47) { - _width = 47; - } - _height = 40*_width/62; + _width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth; + _height = TOOLS_RATIO * _width; - _left = screenWidth/150; - _top = (screenHeight - 13*_height)/2; + _left = screenWidth / 150; + _top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2; glPushMatrix(); glTranslated(_left, _top, 0); - bool show(false); - for (unsigned int i(0); i < _tools.size(); ++i) { + bool show = false; + for (unsigned int i = 0; i < _tools.size(); ++i) { if (_tools[i]->isActive()) { show = true; break; @@ -79,7 +70,7 @@ void ToolsPalette::render(int screenWidth, int screenHeight) { } if (show) { - for (unsigned int i(0); i < _tools.size(); ++i) { + for (unsigned int i = 0; i < _tools.size(); ++i) { _tools[i]->render(_width, _height); } } diff --git a/interface/src/ToolsPalette.h b/interface/src/ToolsPalette.h index ed1aea7e40..0b4ee95524 100644 --- a/interface/src/ToolsPalette.h +++ b/interface/src/ToolsPalette.h @@ -15,14 +15,11 @@ class ToolsPalette { public: - ToolsPalette(); - void init(int screenWidth, int screenHeight); void addAction(QAction* action, int x, int y); - void addTool(Tool *tool); + void addTool(Tool* tool); void render(int screenWidth, int screenHeight); - private: QImage _textureImage; GLuint _textureID; diff --git a/interface/src/Util.h b/interface/src/Util.h index 4a914eb565..2005b76438 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -25,10 +25,6 @@ // the standard mono font family #define MONO_FONT_FAMILY "Courier" -// Number of rows and columns in the SVG file for the tool palette -#define TOOLS_ROWS 10 -#define TOOLS_COLS 2 - void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos); From 4dda71e830dc8f6023f9c771749b220c2ef08f05 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 12:41:44 +0200 Subject: [PATCH 52/63] Final code for minecraft import to review --- libraries/voxels/src/Tags.cpp | 168 ++++++------ libraries/voxels/src/Tags.h | 60 +++-- libraries/voxels/src/VoxelTree.cpp | 400 +++++++++++++++-------------- libraries/voxels/src/VoxelTree.h | 1 + 4 files changed, 331 insertions(+), 298 deletions(-) diff --git a/libraries/voxels/src/Tags.cpp b/libraries/voxels/src/Tags.cpp index 81cf4dbe95..13e8795197 100644 --- a/libraries/voxels/src/Tags.cpp +++ b/libraries/voxels/src/Tags.cpp @@ -7,42 +7,42 @@ #include Tag::Tag(int tagId, std::stringstream &ss) : _tagId(tagId) { - int size(ss.get() << 8 | ss.get()); + int size = ss.get() << 8 | ss.get(); - _name.clear(); - for (int i(0); i < size; ++i) { - _name += ss.get(); - } + _name.clear(); + for (int i = 0; i < size; ++i) { + _name += ss.get(); + } } Tag* Tag::readTag(int tagId, std::stringstream &ss) { - - switch (tagId) { - case TAG_Byte: - return new TagByte(ss); - case TAG_Short: - return new TagShort(ss); - case TAG_Int: - return new TagInt(ss); - case TAG_Long: - return new TagLong(ss); - case TAG_Byte_Array: - return new TagByteArray(ss); - case TAG_String: - return new TagString(ss); - case TAG_List: - return new TagList(ss); - case TAG_Compound: - return new TagCompound(ss); - case TAG_Int_Array: - return new TagIntArray(ss); - case TAG_Float: - case TAG_Double: - default: - //TODO - exit(EXIT_FAILURE); - break; - } + + switch (tagId) { + case TAG_Byte: + return new TagByte(ss); + case TAG_Short: + return new TagShort(ss); + case TAG_Int: + return new TagInt(ss); + case TAG_Long: + return new TagLong(ss); + case TAG_Float: + return new TagFloat(ss); + case TAG_Double: + return new TagDouble(ss); + case TAG_Byte_Array: + return new TagByteArray(ss); + case TAG_String: + return new TagString(ss); + case TAG_List: + return new TagList(ss); + case TAG_Compound: + return new TagCompound(ss); + case TAG_Int_Array: + return new TagIntArray(ss); + default: + return NULL; + } } TagByte::TagByte(std::stringstream &ss) : Tag(TAG_Byte, ss) { @@ -59,15 +59,25 @@ TagInt::TagInt(std::stringstream &ss) : Tag(TAG_Int, ss) { TagLong::TagLong(std::stringstream &ss) : Tag(TAG_Long, ss) { _data = (((int64_t) ss.get()) << 56 | ((int64_t) ss.get()) << 48 - | ((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32 - | ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get()); + |((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32 + | ss.get() << 24 | ss.get() << 16 + | ss.get() << 8 | ss.get()); +} + +// We don't need Float and double, so we just ignore the bytes +TagFloat::TagFloat(std::stringstream &ss) : Tag(TAG_Float, ss) { + ss.seekg(4, ss.cur); +} + +TagDouble::TagDouble(std::stringstream &ss) : Tag(TAG_Double, ss) { + ss.seekg(8, ss.cur); } TagByteArray::TagByteArray(std::stringstream &ss) : Tag(TAG_Byte_Array, ss) { _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); _data = new char[_size]; - for (int i(0); i < _size; ++i) { + for (int i = 0; i < _size; ++i) { _data[i] = ss.get(); } } @@ -75,58 +85,64 @@ TagByteArray::TagByteArray(std::stringstream &ss) : Tag(TAG_Byte_Array, ss) { TagString::TagString(std::stringstream &ss) : Tag(TAG_String, ss) { _size = ss.get() << 8 | ss.get(); - for (int i(0); i < _size; ++i) { + for (int i = 0; i < _size; ++i) { _data += ss.get(); } } -TagList::TagList(std::stringstream &ss) : Tag(TAG_List, ss) { +TagList::TagList(std::stringstream &ss) : + Tag(TAG_List, ss) { _tagId = ss.get(); _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); - for (int i(0); i < _size; ++i) { - ss.putback(0); - ss.putback(0); + for (int i = 0; i < _size; ++i) { + ss.putback(0); ss.putback(0); _data.push_back(readTag(_tagId, ss)); } } -TagCompound::TagCompound(std::stringstream &ss) : Tag(TAG_Compound, ss), - _size(0), - _width(0), - _length(0), - _height(0), - _blocksId(NULL), - _blocksData(NULL) { - int tagId; +TagCompound::TagCompound(std::stringstream &ss) : + Tag(TAG_Compound, ss), + _size(0), + _width(0), + _length(0), + _height(0), + _blocksId(NULL), + _blocksData(NULL) +{ + int tagId; - while (TAG_End != (tagId = ss.get())) { - _data.push_back(readTag(tagId, ss)); - ++_size; + while (TAG_End != (tagId = ss.get())) { + _data.push_back(readTag(tagId, ss)); + ++_size; - if (TAG_Short == tagId) { - if ("Width" == _data.back()->name()) { - _width = ((TagShort*) _data.back())->data(); - } else if ("Height" == _data.back()->name()) { - _height = ((TagShort*) _data.back())->data(); - } else if ("Length" == _data.back()->name()) { - _length = ((TagShort*) _data.back())->data(); - } - } else if (TAG_Byte_Array == tagId) { - if ("Blocks" == _data.back()->name()) { - _blocksId = ((TagByteArray*) _data.back())->data(); - } else if ("Data" == _data.back()->name()) { - _blocksData = ((TagByteArray*) _data.back())->data(); + if (NULL == _data.back()) { + _blocksId = NULL; + _blocksData = NULL; + return; + } else if (TAG_Short == tagId) { + if ("Width" == _data.back()->getName()) { + _width = ((TagShort*) _data.back())->getData(); + } else if ("Height" == _data.back()->getName()) { + _height = ((TagShort*) _data.back())->getData(); + } else if ("Length" == _data.back()->getName()) { + _length = ((TagShort*) _data.back())->getData(); + } + } else if (TAG_Byte_Array == tagId) { + if ("Blocks" == _data.back()->getName()) { + _blocksId = ((TagByteArray*) _data.back())->getData(); + } else if ("Data" == _data.back()->getName()) { + _blocksData = ((TagByteArray*) _data.back())->getData(); + } } } - } } TagIntArray::TagIntArray(std::stringstream &ss) : Tag(TAG_Int_Array, ss) { _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); _data = new int[_size]; - for (int i(0); i < _size; ++i) { + for (int i = 0; i < _size; ++i) { _data[i] = ss.get(); } } @@ -162,22 +178,22 @@ int ungzip(std::ifstream &file, std::stringstream &ss) { return 0; } - unsigned int full_length = gzipedBytes.size(); - unsigned int half_length = gzipedBytes.size()/2; + unsigned int full_length = gzipedBytes.size(); + unsigned int half_length = gzipedBytes.size()/2; unsigned int uncompLength = full_length; char* uncomp = (char*) calloc(sizeof(char), uncompLength); z_stream strm; - strm.next_in = (Bytef *) gzipedBytes.c_str(); - strm.avail_in = gzipedBytes.size(); + strm.next_in = (Bytef *) gzipedBytes.c_str(); + strm.avail_in = full_length; strm.total_out = 0; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; bool done = false; - if (inflateInit2(&strm, (16+MAX_WBITS)) != Z_OK) { + if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { free(uncomp); return 1; } @@ -187,13 +203,13 @@ int ungzip(std::ifstream &file, std::stringstream &ss) { if (strm.total_out >= uncompLength) { // Increase size of output buffer char* uncomp2 = (char*) calloc(sizeof(char), uncompLength + half_length); - memcpy( uncomp2, uncomp, uncompLength ); + memcpy(uncomp2, uncomp, uncompLength); uncompLength += half_length; free(uncomp); uncomp = uncomp2; } - strm.next_out = (Bytef *) (uncomp + strm.total_out); + strm.next_out = (Bytef *) (uncomp + strm.total_out); strm.avail_out = uncompLength - strm.total_out; // Inflate another chunk. @@ -207,7 +223,7 @@ int ungzip(std::ifstream &file, std::stringstream &ss) { return 1; } - for (size_t i=0; i _data; public: TagList(std::stringstream &ss); - int tagId() const {return _tagId;} - int size () const {return _size; } - std::list data () const {return _data; } + int getTagId() const {return _tagId;} + int getSize () const {return _size; } + std::list getData () const {return _data; } }; class TagCompound : public Tag { private: - int _size; + int _size; std::list _data; // Specific to schematics file @@ -128,14 +138,14 @@ class TagCompound : public Tag { public: TagCompound(std::stringstream &ss); - int size () const {return _size; } - std::list data () const {return _data; } + int getSize () const {return _size; } + std::list getData () const {return _data; } - int width () const {return _width; } - int length () const {return _length; } - int height () const {return _height; } - char* blockId () const {return _blocksId; } - char* blocksData() const {return _blocksData;} + int getWidth () const {return _width; } + int getLength () const {return _length; } + int getHeight () const {return _height; } + char* getBlocksId () const {return _blocksId; } + char* getBlocksData() const {return _blocksData;} }; class TagIntArray : public Tag { @@ -147,6 +157,6 @@ class TagIntArray : public Tag { TagIntArray(std::stringstream &ss); ~TagIntArray() {delete _data;} - int size() const {return _size;} - int* data() const {return _data;} + int getSize() const {return _size;} + int* getData() const {return _data;} }; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index e0cc6c985b..6189c04cbb 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1461,228 +1461,77 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { std::stringstream ss; int err = retrieveData(fileName, ss); if (err && TAG_Compound != ss.get()) { - printLog("ERROR: Invalid schematic file.\n"); + printLog("[ERROR] Invalid schematic file.\n"); return false; } ss.get(); TagCompound schematics(ss); - if (!schematics.blockId()) { - printLog("ERROR: Can't read schematic data.\n"); + if (!schematics.getBlocksId() || !schematics.getBlocksData()) { + printLog("[ERROR] Invalid schematic file.\n"); return false; } - int max = (schematics.width() > schematics.length()) ? schematics.width() : schematics.length(); - max = (max > schematics.height()) ? max : schematics.height(); + int max = (schematics.getWidth() > schematics.getLength()) ? schematics.getWidth() : schematics.getLength(); + max = (max > schematics.getHeight()) ? max : schematics.getHeight(); int scale = 1; while (max > scale) scale *= 2; float size = 1.0f / scale; - bool create = false; + int create = 1; int red = 128, green = 128, blue = 128; int count = 0; - for (int y = 0; y < schematics.height(); ++y) { - for (int z = 0; z < schematics.length(); ++z) { - for (int x = 0; x < schematics.width(); ++x) { - int pos = ((y * schematics.length()) + z) * schematics.width() + x; - int id = schematics.blockId()[pos]; + for (int y = 0; y < schematics.getHeight(); ++y) { + for (int z = 0; z < schematics.getLength(); ++z) { + for (int x = 0; x < schematics.getWidth(); ++x) { + int pos = ((y * schematics.getLength()) + z) * schematics.getWidth() + x; + int id = schematics.getBlocksId()[pos]; + int data = schematics.getBlocksData()[pos]; - create = true; - switch (id) { - case 1: - case 14: - case 15: - case 16: - case 21: - case 56: - case 73: - case 74: - case 97: - case 129: red = 128; green = 128; blue = 128; break; - case 2: red = 77; green = 117; blue = 66; break; - case 3: - case 60: red = 116; green = 83; blue = 56; break; - case 4: red = 71; green = 71; blue = 71; break; - case 5: - case 125: red = 133; green = 94; blue = 62; break; - case 7: red = 35; green = 35; blue = 35; break; - case 8: - case 9: red = 100; green = 109; blue = 185; break; - case 10: - case 11: red = 192; green = 64; blue = 8; break; - case 12: red = 209; green = 199; blue = 155; break; - case 13: red = 96; green = 94; blue = 93; break; - case 17: red = 71; green = 56; blue = 35; break; - case 18: red = 76; green = 104; blue = 64; break; - case 19: red = 119; green = 119; blue = 37; break; - case 22: red = 22; green = 44; blue = 86; break; - case 23: - case 29: - case 33: - case 61: - case 62: - case 158: red = 61; green = 61; blue = 61; break; - case 24: red = 209; green = 202; blue = 156; break; - case 25: - case 58: - case 84: - case 137: red = 57; green = 38; blue = 25; break; - case 35: - switch (schematics.blocksData()[pos]) { - case 0: red = 234; green = 234; blue = 234; break; - case 1: red = 224; green = 140; blue = 84; break; - case 2: red = 185; green = 90; blue = 194; break; - case 3: red = 124; green = 152; blue = 208; break; - case 4: red = 165; green = 154; blue = 35; break; - case 5: red = 70; green = 187; blue = 61; break; - case 6: red = 206; green = 124; blue = 145; break; - case 7: red = 66; green = 66; blue = 66; break; - case 8: red = 170; green = 176; blue = 176; break; - case 9: red = 45; green = 108; blue = 35; break; - case 10: red = 130; green = 62; blue = 8; break; - case 11: red = 43; green = 51; blue = 29; break; - case 12: red = 73; green = 47; blue = 29; break; - case 13: red = 57; green = 76; blue = 36; break; - case 14: red = 165; green = 58; blue = 53; break; - case 15: red = 24; green = 24; blue = 24; break; - default: - create = false; - break; - } - break; - case 41: red = 239; green = 238; blue = 105; break; - case 42: red = 146; green = 146; blue = 146; break; - case 43: - case 98: red = 161; green = 161; blue = 161; break; - case 44: - switch (schematics.blocksData()[pos]) { - case 0: red = 161; green = 161; blue = 161; break; - case 1: red = 209; green = 202; blue = 156; break; - case 2: red = 133; green = 94; blue = 62; break; - case 3: red = 71; green = 71; blue = 71; break; - case 4: red = 121; green = 67; blue = 53; break; - case 5: red = 161; green = 161; blue = 161; break; - case 6: red = 45; green = 22; blue = 26; break; - case 7: red = 195; green = 192; blue = 185; break; - default: - create = false; - break; - } + create = 1; + computeBlockColor(id, data, &red, &green, &blue, &create); - if (create) { - createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); - count += 4; - create = false; - } - break; - case 45: red = 121; green = 67; blue = 53; break; - case 46: red = 118; green = 36; blue = 13; break; - case 47: red = 155; green = 127; blue = 76; break; - case 48: red = 61; green = 79; blue = 61; break; - case 49: red = 52; green = 41; blue = 74; break; - case 52: red = 12; green = 66; blue = 71; break; - case 53: - case 67: - case 108: - case 109: - case 114: - case 128: - case 134: - case 135: - case 136: - case 156: - switch (id) { - case 53: - case 134: - case 135: - case 136: red = 133; green = 94; blue = 62; break; - case 67: red = 71; green = 71; blue = 71; break; - case 108: red = 121; green = 67; blue = 53; break; - case 109: red = 161; green = 161; blue = 161; break; - case 114: red = 45; green = 22; blue = 26; break; - case 128: red = 209; green = 202; blue = 156; break; - case 156: red = 195; green = 192; blue = 185; break; - default: - create = false; - break; - } - - if (create) { - createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); - - switch (schematics.blocksData()[pos]) { - case 0: - createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - break; - case 1: - createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - break; - case 2: - createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - break; - case 3: - createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); - break; - } - - count += 6; - create = false; - } - break; - case 54: - case 95: - case 146: red = 155; green = 105; blue = 32; break; - case 57: red = 145; green = 219; blue = 215; break; - case 79: red = 142; green = 162; blue = 195; break; - case 80: red = 255; green = 255; blue = 255; break; - case 81: red = 8; green = 64; blue = 15; break; - case 82: red = 150; green = 155; blue = 166; break; - case 86: - case 91: red = 179; green = 108; blue = 17; break; - case 87: - case 153: red = 91; green = 31; blue = 30; break; - case 88: red = 68; green = 49; blue = 38; break; - case 89: red = 180; green = 134; blue = 65; break; - case 103: red = 141; green = 143; blue = 36; break; - case 110: red = 103; green = 92; blue = 95; break; - case 112: red = 45; green = 22; blue = 26; break; - case 121: red = 183; green = 178; blue = 129; break; - case 123: red = 101; green = 59; blue = 31; break; - case 124: red = 213; green = 178; blue = 123; break; - case 130: red = 38; green = 54; blue = 56; break; - case 133: red = 53; green = 84; blue = 85; break; - case 152: red = 131; green = 22; blue = 7; break; - case 155: red = 195; green = 192; blue = 185; break; - case 159: red = 195; green = 165; blue = 150; break; - case 170: red = 168; green = 139; blue = 15; break; - case 172: red = 140; green = 86; blue = 61; break; - case 173: red = 9; green = 9; blue = 9; break; - default: - create = false; - break; - } - - if (create) { + switch (create) { + case 1: createVoxel(size * x, size * y, size * z, size, red, green, blue, true); ++count; + break; + case 2: + switch (data) { + case 0: + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 1: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 2: + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 3: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + break; + } + count += 2; + // There's no break on purpose. + case 3: + createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); + count += 4; + break; } } } } - printLog("Created %d voxels frome minecraft import.\n", count); + printLog("Created %d voxels from minecraft import.\n", count); return true; } @@ -1771,3 +1620,160 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin } } +void VoxelTree::computeBlockColor(int id, int data, int* r, int* g, int* b, int* create) { + int red = *r, green = *g, blue = *b; + + switch (id) { + case 1: + case 14: + case 15: + case 16: + case 21: + case 56: + case 73: + case 74: + case 97: + case 129: red = 128; green = 128; blue = 128; break; + case 2: red = 77; green = 117; blue = 66; break; + case 3: + case 60: red = 116; green = 83; blue = 56; break; + case 4: red = 71; green = 71; blue = 71; break; + case 5: + case 125: red = 133; green = 94; blue = 62; break; + case 7: red = 35; green = 35; blue = 35; break; + case 8: + case 9: red = 100; green = 109; blue = 185; break; + case 10: + case 11: red = 192; green = 64; blue = 8; break; + case 12: red = 209; green = 199; blue = 155; break; + case 13: red = 96; green = 94; blue = 93; break; + case 17: red = 71; green = 56; blue = 35; break; + case 18: red = 76; green = 104; blue = 64; break; + case 19: red = 119; green = 119; blue = 37; break; + case 22: red = 22; green = 44; blue = 86; break; + case 23: + case 29: + case 33: + case 61: + case 62: + case 158: red = 61; green = 61; blue = 61; break; + case 24: red = 209; green = 202; blue = 156; break; + case 25: + case 58: + case 84: + case 137: red = 57; green = 38; blue = 25; break; + case 35: + switch (data) { + case 0: red = 234; green = 234; blue = 234; break; + case 1: red = 224; green = 140; blue = 84; break; + case 2: red = 185; green = 90; blue = 194; break; + case 3: red = 124; green = 152; blue = 208; break; + case 4: red = 165; green = 154; blue = 35; break; + case 5: red = 70; green = 187; blue = 61; break; + case 6: red = 206; green = 124; blue = 145; break; + case 7: red = 66; green = 66; blue = 66; break; + case 8: red = 170; green = 176; blue = 176; break; + case 9: red = 45; green = 108; blue = 35; break; + case 10: red = 130; green = 62; blue = 8; break; + case 11: red = 43; green = 51; blue = 29; break; + case 12: red = 73; green = 47; blue = 29; break; + case 13: red = 57; green = 76; blue = 36; break; + case 14: red = 165; green = 58; blue = 53; break; + case 15: red = 24; green = 24; blue = 24; break; + default: + *create = 0; + break; + } + break; + case 41: red = 239; green = 238; blue = 105; break; + case 42: red = 146; green = 146; blue = 146; break; + case 43: + case 98: red = 161; green = 161; blue = 161; break; + case 44: + *create = 3; + + switch (data) { + case 0: red = 161; green = 161; blue = 161; break; + case 1: red = 209; green = 202; blue = 156; break; + case 2: red = 133; green = 94; blue = 62; break; + case 3: red = 71; green = 71; blue = 71; break; + case 4: red = 121; green = 67; blue = 53; break; + case 5: red = 161; green = 161; blue = 161; break; + case 6: red = 45; green = 22; blue = 26; break; + case 7: red = 195; green = 192; blue = 185; break; + default: + *create = 0; + break; + } + break; + case 45: red = 121; green = 67; blue = 53; break; + case 46: red = 118; green = 36; blue = 13; break; + case 47: red = 155; green = 127; blue = 76; break; + case 48: red = 61; green = 79; blue = 61; break; + case 49: red = 52; green = 41; blue = 74; break; + case 52: red = 12; green = 66; blue = 71; break; + case 53: + case 67: + case 108: + case 109: + case 114: + case 128: + case 134: + case 135: + case 136: + case 156: + *create = 2; + + switch (id) { + case 53: + case 134: + case 135: + case 136: red = 133; green = 94; blue = 62; break; + case 67: red = 71; green = 71; blue = 71; break; + case 108: red = 121; green = 67; blue = 53; break; + case 109: red = 161; green = 161; blue = 161; break; + case 114: red = 45; green = 22; blue = 26; break; + case 128: red = 209; green = 202; blue = 156; break; + case 156: red = 195; green = 192; blue = 185; break; + default: + *create = 0; + break; + } + break; + case 54: + case 95: + case 146: red = 155; green = 105; blue = 32; break; + case 57: red = 145; green = 219; blue = 215; break; + case 79: red = 142; green = 162; blue = 195; break; + case 80: red = 255; green = 255; blue = 255; break; + case 81: red = 8; green = 64; blue = 15; break; + case 82: red = 150; green = 155; blue = 166; break; + case 86: + case 91: red = 179; green = 108; blue = 17; break; + case 87: + case 153: red = 91; green = 31; blue = 30; break; + case 88: red = 68; green = 49; blue = 38; break; + case 89: red = 180; green = 134; blue = 65; break; + case 103: red = 141; green = 143; blue = 36; break; + case 110: red = 103; green = 92; blue = 95; break; + case 112: red = 45; green = 22; blue = 26; break; + case 121: red = 183; green = 178; blue = 129; break; + case 123: red = 101; green = 59; blue = 31; break; + case 124: red = 213; green = 178; blue = 123; break; + case 130: red = 38; green = 54; blue = 56; break; + case 133: red = 53; green = 84; blue = 85; break; + case 152: red = 131; green = 22; blue = 7; break; + case 155: red = 195; green = 192; blue = 185; break; + case 159: red = 195; green = 165; blue = 150; break; + case 170: red = 168; green = 139; blue = 15; break; + case 172: red = 140; green = 86; blue = 61; break; + case 173: red = 9; green = 9; blue = 9; break; + default: + *create = 0; + break; + } + + *r = red; + *g = green; + *b = blue; +} diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 3591141549..56d0e1120e 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -136,6 +136,7 @@ public: // reads voxels from square image with alpha as a Y-axis bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); bool readFromSchematicsFile(const char* filename); + void computeBlockColor(int id, int data, int* r, int* g, int* b, int* create); unsigned long getVoxelCount(); From 3c4f8f5120bb7c99f33f39338570d107f08279dc Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 14:17:42 +0200 Subject: [PATCH 53/63] put hard coded numbers into variables for UI job --- interface/src/Application.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8b1b761c2d..603a0c1982 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2470,23 +2470,28 @@ void Application::displayOverlay() { const char line1[] = "Assign this color to a swatch"; const char line2[] = "by choosing a key from 1 to 8."; double step = 0.05f; - int left = (_glWidget->width() - 300) / 2; - int top = _glWidget->height() / 40.0f; + + int width = 280, height = 30; double margin = 10.0f; + int left = (_glWidget->width() - width - 2 * margin) / 2; + int top = _glWidget->height() / 40; + + int swatchWidth = 64, swatchHeight = 30; + int firstLineOffset = 12, secondLineOffset = 28; glBegin(GL_POLYGON); glColor3f(0.0f, 0.0f, 0.0f); for (double a = M_PI; a < 1.5f * M_PI; a += step) { - glVertex2f(left + margin * cos(a), top + margin * sin(a)); + glVertex2f(left + margin * cos(a) , top + margin * sin(a)); } for (double a = 1.5f * M_PI; a < 2.0f*M_PI; a += step) { - glVertex2f(left + 280 + margin * cos(a), top + margin * sin(a)); + glVertex2f(left + width + margin * cos(a), top + margin * sin(a)); } for (double a = 0.0f; a < 0.5f * M_PI; a += step) { - glVertex2f(left + 280 + margin * cos(a), top + 30 + margin * sin(a)); + glVertex2f(left + width + margin * cos(a), top + height + margin * sin(a)); } for (double a = 0.5f*M_PI; a < 1.0f*M_PI; a += step) { - glVertex2f(left + margin*cos(a), top + 30 + margin*sin(a)); + glVertex2f(left + margin*cos(a) , top + height + margin*sin(a)); } glEnd(); @@ -2494,15 +2499,15 @@ void Application::displayOverlay() { glColor3f(color.redF(), color.greenF(), color.blueF()); - glVertex2f(left, top); - glVertex2f(left + 64, top); - glVertex2f(left + 64, top + 30); - glVertex2f(left, top + 30); + glVertex2f(left , top); + glVertex2f(left + swatchWidth, top); + glVertex2f(left + swatchWidth, top + swatchHeight); + glVertex2f(left , top + swatchHeight); glEnd(); glColor3f(1.0f, 1.0f, 1.0f); - textRenderer.draw(left + 74, top + 12, line1); - textRenderer.draw(left + 74, top + 28, line2); + textRenderer.draw(left + swatchWidth + margin, top + firstLineOffset , line1); + textRenderer.draw(left + swatchWidth + margin, top + secondLineOffset, line2); } else { _swatch.checkColor(); From c3b0be5c6398039156e262b6d40f6eed9527f76d Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 14:49:01 +0200 Subject: [PATCH 54/63] More consts for UI job --- interface/src/Application.cpp | 36 +++++++++++++++-------------------- interface/src/Tool.h | 10 ++++++++++ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 603a0c1982..986b357edf 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2469,29 +2469,23 @@ void Application::displayOverlay() { TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); const char line1[] = "Assign this color to a swatch"; const char line2[] = "by choosing a key from 1 to 8."; - double step = 0.05f; - int width = 280, height = 30; - double margin = 10.0f; - int left = (_glWidget->width() - width - 2 * margin) / 2; + int left = (_glWidget->width() - WIDTH - 2 * MARGIN) / 2; int top = _glWidget->height() / 40; - int swatchWidth = 64, swatchHeight = 30; - int firstLineOffset = 12, secondLineOffset = 28; - glBegin(GL_POLYGON); glColor3f(0.0f, 0.0f, 0.0f); - for (double a = M_PI; a < 1.5f * M_PI; a += step) { - glVertex2f(left + margin * cos(a) , top + margin * sin(a)); + for (double a = M_PI; a < 1.5f * M_PI; a += STEP) { + glVertex2f(left + MARGIN * cos(a) , top + MARGIN * sin(a)); } - for (double a = 1.5f * M_PI; a < 2.0f*M_PI; a += step) { - glVertex2f(left + width + margin * cos(a), top + margin * sin(a)); + for (double a = 1.5f * M_PI; a < 2.0f*M_PI; a += STEP) { + glVertex2f(left + WIDTH + MARGIN * cos(a), top + MARGIN * sin(a)); } - for (double a = 0.0f; a < 0.5f * M_PI; a += step) { - glVertex2f(left + width + margin * cos(a), top + height + margin * sin(a)); + for (double a = 0.0f; a < 0.5f * M_PI; a += STEP) { + glVertex2f(left + WIDTH + MARGIN * cos(a), top + HEIGHT + MARGIN * sin(a)); } - for (double a = 0.5f*M_PI; a < 1.0f*M_PI; a += step) { - glVertex2f(left + margin*cos(a) , top + height + margin*sin(a)); + for (double a = 0.5f*M_PI; a < 1.0f*M_PI; a += STEP) { + glVertex2f(left + MARGIN*cos(a) , top + HEIGHT + MARGIN*sin(a)); } glEnd(); @@ -2499,15 +2493,15 @@ void Application::displayOverlay() { glColor3f(color.redF(), color.greenF(), color.blueF()); - glVertex2f(left , top); - glVertex2f(left + swatchWidth, top); - glVertex2f(left + swatchWidth, top + swatchHeight); - glVertex2f(left , top + swatchHeight); + glVertex2f(left , top); + glVertex2f(left + SWATCH_WIDTH, top); + glVertex2f(left + SWATCH_WIDTH, top + SWATCH_HEIGHT); + glVertex2f(left , top + SWATCH_HEIGHT); glEnd(); glColor3f(1.0f, 1.0f, 1.0f); - textRenderer.draw(left + swatchWidth + margin, top + firstLineOffset , line1); - textRenderer.draw(left + swatchWidth + margin, top + secondLineOffset, line2); + textRenderer.draw(left + SWATCH_WIDTH + MARGIN, top + FIRST_LINE_OFFSET , line1); + textRenderer.draw(left + SWATCH_WIDTH + MARGIN, top + SECOND_LINE_OFFSET, line2); } else { _swatch.checkColor(); diff --git a/interface/src/Tool.h b/interface/src/Tool.h index 9bea07a633..405c64bc5e 100644 --- a/interface/src/Tool.h +++ b/interface/src/Tool.h @@ -25,6 +25,16 @@ static const int WIDTH_MIN = 47; // Minimal tools width static const float TOOLS_RATIO = 40.0f / 60.0f; // ratio height/width of tools icons static const float PAL_SCREEN_RATIO = 3.0f / 100.0f; // Percentage of the screeen width the palette is going to occupy +// Swatch popup consts +static const float STEP = 0.05f; +static const float MARGIN = 10.0f; +static const int WIDTH = 280; +static const int HEIGHT = 30; +static const int SWATCH_WIDTH = 64; +static const int SWATCH_HEIGHT = 30; +static const int FIRST_LINE_OFFSET = 12; +static const int SECOND_LINE_OFFSET = 28; + class Tool { public: Tool(QAction* action, GLuint texture, int x, int y); From b3c0f3fedbcd00069350d763d627b7a0c59fd85b Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 15:29:12 +0200 Subject: [PATCH 55/63] More code review for UI job --- interface/src/Application.cpp | 22 +++++++++++----------- interface/src/Tool.h | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 986b357edf..d1028b55cb 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2470,22 +2470,22 @@ void Application::displayOverlay() { const char line1[] = "Assign this color to a swatch"; const char line2[] = "by choosing a key from 1 to 8."; - int left = (_glWidget->width() - WIDTH - 2 * MARGIN) / 2; + int left = (_glWidget->width() - POPUP_WIDTH - 2 * POPUP_MARGIN) / 2; int top = _glWidget->height() / 40; glBegin(GL_POLYGON); glColor3f(0.0f, 0.0f, 0.0f); - for (double a = M_PI; a < 1.5f * M_PI; a += STEP) { - glVertex2f(left + MARGIN * cos(a) , top + MARGIN * sin(a)); + for (double a = M_PI; a < 1.5f * M_PI; a += POPUP_STEP) { + glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_MARGIN * sin(a)); } - for (double a = 1.5f * M_PI; a < 2.0f*M_PI; a += STEP) { - glVertex2f(left + WIDTH + MARGIN * cos(a), top + MARGIN * sin(a)); + for (double a = 1.5f * M_PI; a < 2.0f * M_PI; a += POPUP_STEP) { + glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_MARGIN * sin(a)); } - for (double a = 0.0f; a < 0.5f * M_PI; a += STEP) { - glVertex2f(left + WIDTH + MARGIN * cos(a), top + HEIGHT + MARGIN * sin(a)); + for (double a = 0.0f; a < 0.5f * M_PI; a += POPUP_STEP) { + glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_HEIGHT + POPUP_MARGIN * sin(a)); } - for (double a = 0.5f*M_PI; a < 1.0f*M_PI; a += STEP) { - glVertex2f(left + MARGIN*cos(a) , top + HEIGHT + MARGIN*sin(a)); + for (double a = 0.5f * M_PI; a < 1.0f * M_PI; a += POPUP_STEP) { + glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_HEIGHT + POPUP_MARGIN * sin(a)); } glEnd(); @@ -2500,8 +2500,8 @@ void Application::displayOverlay() { glEnd(); glColor3f(1.0f, 1.0f, 1.0f); - textRenderer.draw(left + SWATCH_WIDTH + MARGIN, top + FIRST_LINE_OFFSET , line1); - textRenderer.draw(left + SWATCH_WIDTH + MARGIN, top + SECOND_LINE_OFFSET, line2); + textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + FIRST_LINE_OFFSET , line1); + textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + SECOND_LINE_OFFSET, line2); } else { _swatch.checkColor(); diff --git a/interface/src/Tool.h b/interface/src/Tool.h index 405c64bc5e..f0e1bae4b0 100644 --- a/interface/src/Tool.h +++ b/interface/src/Tool.h @@ -26,10 +26,10 @@ static const float TOOLS_RATIO = 40.0f / 60.0f; // ratio height/width of tools i static const float PAL_SCREEN_RATIO = 3.0f / 100.0f; // Percentage of the screeen width the palette is going to occupy // Swatch popup consts -static const float STEP = 0.05f; -static const float MARGIN = 10.0f; -static const int WIDTH = 280; -static const int HEIGHT = 30; +static const float POPUP_STEP = 0.05f; +static const float POPUP_MARGIN = 10.0f; +static const int POPUP_WIDTH = 280; +static const int POPUP_HEIGHT = 30; static const int SWATCH_WIDTH = 64; static const int SWATCH_HEIGHT = 30; static const int FIRST_LINE_OFFSET = 12; From 2c438e8af3a5752e80e39f39a34a6ece3e7e8890 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 16:20:55 +0200 Subject: [PATCH 56/63] Fixed broken shortcut (Turn on/off voxels is now Shift + V) --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d1028b55cb..5a0b45960e 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1512,7 +1512,7 @@ void Application::initMenu() { QMenu* renderMenu = menuBar->addMenu("Render"); (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); _renderVoxels->setChecked(true); - _renderVoxels->setShortcut(Qt::CTRL | Qt::Key_V); + _renderVoxels->setShortcut(Qt::SHIFT | Qt::Key_V); (_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true); (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); _renderStarsOn->setChecked(true); From dbdbd7a8c4161f7d28f8b0da96c4ab4fbb1ae8b0 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 9 Jul 2013 17:55:55 +0200 Subject: [PATCH 57/63] Incorporating comments for code review (minecraft import) --- interface/src/Application.cpp | 6 +- libraries/voxels/src/Tags.cpp | 211 +++++++++-------- libraries/voxels/src/Tags.h | 213 +++++++++-------- libraries/voxels/src/VoxelTree.cpp | 351 ++++++++++++++--------------- libraries/voxels/src/VoxelTree.h | 2 +- 5 files changed, 402 insertions(+), 381 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fc72aa63bd..95d40eec12 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1363,8 +1363,10 @@ void Application::exportVoxels() { void Application::importVoxels() { QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); - QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation, - tr("Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)")); + QString fileNameString = QFileDialog::getOpenFileName( + _glWidget, tr("Import Voxels"), desktopLocation, + tr("Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)")); + QByteArray fileNameAscii = fileNameString.toAscii(); const char* fileName = fileNameAscii.data(); diff --git a/libraries/voxels/src/Tags.cpp b/libraries/voxels/src/Tags.cpp index 13e8795197..0cbfa1a37c 100644 --- a/libraries/voxels/src/Tags.cpp +++ b/libraries/voxels/src/Tags.cpp @@ -1,3 +1,11 @@ +// +// Tags.h +// hifi +// +// Created by Clement Brisset on 7/3/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + #include "Tags.h" #include @@ -8,10 +16,9 @@ Tag::Tag(int tagId, std::stringstream &ss) : _tagId(tagId) { int size = ss.get() << 8 | ss.get(); - + _name.clear(); - for (int i = 0; i < size; ++i) { - _name += ss.get(); + for (int i = 0; i < size; ++i) { _name += ss.get(); } } @@ -46,22 +53,22 @@ Tag* Tag::readTag(int tagId, std::stringstream &ss) { } TagByte::TagByte(std::stringstream &ss) : Tag(TAG_Byte, ss) { - _data = ss.get(); + _data = ss.get(); } TagShort::TagShort(std::stringstream &ss) : Tag(TAG_Short, ss) { - _data = ss.get() << 8 | ss.get(); + _data = ss.get() << 8 | ss.get(); } TagInt::TagInt(std::stringstream &ss) : Tag(TAG_Int, ss) { - _data = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + _data = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); } TagLong::TagLong(std::stringstream &ss) : Tag(TAG_Long, ss) { - _data = (((int64_t) ss.get()) << 56 | ((int64_t) ss.get()) << 48 - |((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32 - | ss.get() << 24 | ss.get() << 16 - | ss.get() << 8 | ss.get()); + _data = (((int64_t) ss.get()) << 56 | ((int64_t) ss.get()) << 48 + |((int64_t) ss.get()) << 40 | ((int64_t) ss.get()) << 32 + | ss.get() << 24 | ss.get() << 16 + | ss.get() << 8 | ss.get()); } // We don't need Float and double, so we just ignore the bytes @@ -74,31 +81,32 @@ TagDouble::TagDouble(std::stringstream &ss) : Tag(TAG_Double, ss) { } TagByteArray::TagByteArray(std::stringstream &ss) : Tag(TAG_Byte_Array, ss) { - _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); - - _data = new char[_size]; - for (int i = 0; i < _size; ++i) { - _data[i] = ss.get(); - } + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + + _data = new char[_size]; + for (int i = 0; i < _size; ++i) { + _data[i] = ss.get(); + } } TagString::TagString(std::stringstream &ss) : Tag(TAG_String, ss) { - _size = ss.get() << 8 | ss.get(); - - for (int i = 0; i < _size; ++i) { - _data += ss.get(); - } + _size = ss.get() << 8 | ss.get(); + + for (int i = 0; i < _size; ++i) { + _data += ss.get(); + } } TagList::TagList(std::stringstream &ss) : Tag(TAG_List, ss) { - _tagId = ss.get(); - _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + _tagId = ss.get(); + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); - for (int i = 0; i < _size; ++i) { - ss.putback(0); ss.putback(0); - _data.push_back(readTag(_tagId, ss)); - } + for (int i = 0; i < _size; ++i) { + ss.putback(0); + ss.putback(0); + _data.push_back(readTag(_tagId, ss)); + } } TagCompound::TagCompound(std::stringstream &ss) : @@ -139,96 +147,99 @@ TagCompound::TagCompound(std::stringstream &ss) : } TagIntArray::TagIntArray(std::stringstream &ss) : Tag(TAG_Int_Array, ss) { - _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); - - _data = new int[_size]; - for (int i = 0; i < _size; ++i) { - _data[i] = ss.get(); - } + _size = ss.get() << 24 | ss.get() << 16 | ss.get() << 8 | ss.get(); + + _data = new int[_size]; + for (int i = 0; i < _size; ++i) { + _data[i] = ss.get(); + } } int retrieveData(std::string filename, std::stringstream &ss) { std::ifstream file(filename.c_str(), std::ios::binary); - int type = file.peek(); - if (0x0A == type) { - ss.flush(); - ss << file; - return 0; - } - if (0x1F == type) { - return ungzip(file, ss); - } + int type = file.peek(); + if (type == 0x0A) { + ss.flush(); + ss << file; + return 0; + } + if (type == 0x1F) { + return ungzip(file, ss); + } - return 1; + return 1; } int ungzip(std::ifstream &file, std::stringstream &ss) { - std::string gzipedBytes; - gzipedBytes.clear(); - ss.flush(); + std::string gzipedBytes; + gzipedBytes.clear(); + ss.flush(); - while (!file.eof()) { - gzipedBytes += (char) file.get(); - } - file.close(); + while (!file.eof()) { + gzipedBytes += (char) file.get(); + } + file.close(); - if ( gzipedBytes.size() == 0 ) { - ss << gzipedBytes; - return 0; - } - - unsigned int full_length = gzipedBytes.size(); - unsigned int half_length = gzipedBytes.size()/2; - unsigned int uncompLength = full_length; - - char* uncomp = (char*) calloc(sizeof(char), uncompLength); - - z_stream strm; - strm.next_in = (Bytef *) gzipedBytes.c_str(); - strm.avail_in = full_length; - strm.total_out = 0; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - - bool done = false; - - if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { - free(uncomp); - return 1; - } - - while (!done) { - // If our output buffer is too small - if (strm.total_out >= uncompLength) { - // Increase size of output buffer - char* uncomp2 = (char*) calloc(sizeof(char), uncompLength + half_length); - memcpy(uncomp2, uncomp, uncompLength); - uncompLength += half_length; - free(uncomp); - uncomp = uncomp2; + if (gzipedBytes.size() == 0) { + ss << gzipedBytes; + return 0; } - strm.next_out = (Bytef *) (uncomp + strm.total_out); - strm.avail_out = uncompLength - strm.total_out; + unsigned int full_length = gzipedBytes.size(); + unsigned int half_length = gzipedBytes.size()/2; + unsigned int uncompLength = full_length; - // Inflate another chunk. - int err = inflate (&strm, Z_SYNC_FLUSH); - if (err == Z_STREAM_END) done = true; - else if (err != Z_OK) break; - } + char* uncomp = (char*) calloc(sizeof(char), uncompLength); - if (inflateEnd (&strm) != Z_OK) { + z_stream strm; + strm.next_in = (Bytef *) gzipedBytes.c_str(); + strm.avail_in = full_length; + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + + bool done = false; + + if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) { + free(uncomp); + return 1; + } + + while (!done) { + // If our output buffer is too small + if (strm.total_out >= uncompLength) { + // Increase size of output buffer + char* uncomp2 = (char*) calloc(sizeof(char), uncompLength + half_length); + memcpy(uncomp2, uncomp, uncompLength); + uncompLength += half_length; + free(uncomp); + uncomp = uncomp2; + } + + strm.next_out = (Bytef *) (uncomp + strm.total_out); + strm.avail_out = uncompLength - strm.total_out; + + // Inflate another chunk. + int err = inflate (&strm, Z_SYNC_FLUSH); + if (err == Z_STREAM_END) { + done = true; + } else if (err != Z_OK) { + break; + } + } + + if (inflateEnd (&strm) != Z_OK) { + free(uncomp); + return 1; + } + + for (size_t i = 0; i < strm.total_out; ++i) { + ss << uncomp[i]; + } free(uncomp); - return 1; - } - for (size_t i = 0; i < strm.total_out; ++i) { - ss << uncomp[i]; - } - free(uncomp); - - return 0; + return 0; } diff --git a/libraries/voxels/src/Tags.h b/libraries/voxels/src/Tags.h index be45e2dc86..a005095142 100644 --- a/libraries/voxels/src/Tags.h +++ b/libraries/voxels/src/Tags.h @@ -1,3 +1,14 @@ +// +// Tags.h +// hifi +// +// Created by Clement Brisset on 7/3/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__Tags__ +#define __hifi__Tags__ + #include #include @@ -22,141 +33,143 @@ int retrieveData(std::string filename, std::stringstream &ss); int ungzip(std::ifstream &file, std::stringstream &ss); class Tag { - protected: - int _tagId; - std::string _name; - - public: - Tag(int tagId, std::stringstream &ss); - - int getTagId() const {return _tagId;} - std::string getName () const {return _name; } - - static Tag* readTag(int tagId, std::stringstream &ss); +public: + Tag(int tagId, std::stringstream &ss); + + int getTagId() const {return _tagId;} + std::string getName () const {return _name; } + + static Tag* readTag(int tagId, std::stringstream &ss); + +protected: + int _tagId; + std::string _name; }; class TagByte : public Tag { - private: - int8_t _data; - - public: - TagByte(std::stringstream &ss); - - int8_t getData() const {return _data;} +public: + TagByte(std::stringstream &ss); + + int8_t getData() const {return _data;} + +private: + int8_t _data; }; class TagShort : public Tag { - private: - int16_t _data; - - public: - TagShort(std::stringstream &ss); - - int16_t getData() const {return _data;} +public: + TagShort(std::stringstream &ss); + + int16_t getData() const {return _data;} + +private: + int16_t _data; }; class TagInt : public Tag { - private: - int32_t _data; - - public: - TagInt(std::stringstream &ss); - - int32_t getData() const {return _data;} +public: + TagInt(std::stringstream &ss); + + int32_t getData() const {return _data;} + +private: + int32_t _data; }; class TagLong : public Tag { - private: - int64_t _data; - - public: - TagLong(std::stringstream &ss); - - int64_t getData() const {return _data;} +public: + TagLong(std::stringstream &ss); + + int64_t getData() const {return _data;} + +private: + int64_t _data; }; class TagFloat : public Tag { - public: - TagFloat(std::stringstream &ss); +public: + TagFloat(std::stringstream &ss); }; class TagDouble : public Tag { - public: - TagDouble(std::stringstream &ss); +public: + TagDouble(std::stringstream &ss); }; class TagByteArray : public Tag { - private: - int _size; - char* _data; - - public: - TagByteArray(std::stringstream &ss); - - int getSize() const {return _size;} - char* getData() const {return _data;} +public: + TagByteArray(std::stringstream &ss); + + int getSize() const {return _size;} + char* getData() const {return _data;} + +private: + int _size; + char* _data; }; class TagString : public Tag { - private: - int _size; - std::string _data; +public: + TagString(std::stringstream &ss); - public: - TagString(std::stringstream &ss); - - int getSize() const {return _size;} - std::string getData() const {return _data;} + int getSize() const {return _size;} + std::string getData() const {return _data;} + +private: + int _size; + std::string _data; }; class TagList : public Tag { - private: - int _tagId; - int _size; - std::list _data; - - public: - TagList(std::stringstream &ss); - - int getTagId() const {return _tagId;} - int getSize () const {return _size; } - std::list getData () const {return _data; } +public: + TagList(std::stringstream &ss); + + int getTagId() const {return _tagId;} + int getSize () const {return _size; } + std::list getData () const {return _data; } + +private: + int _tagId; + int _size; + std::list _data; }; class TagCompound : public Tag { - private: - int _size; - std::list _data; - - // Specific to schematics file - int _width; - int _length; - int _height; - char* _blocksData; - char* _blocksId; +public: + TagCompound(std::stringstream &ss); - public: - TagCompound(std::stringstream &ss); - - int getSize () const {return _size; } - std::list getData () const {return _data; } + int getSize () const {return _size; } + std::list getData () const {return _data; } - int getWidth () const {return _width; } - int getLength () const {return _length; } - int getHeight () const {return _height; } - char* getBlocksId () const {return _blocksId; } - char* getBlocksData() const {return _blocksData;} + int getWidth () const {return _width; } + int getLength () const {return _length; } + int getHeight () const {return _height; } + char* getBlocksId () const {return _blocksId; } + char* getBlocksData() const {return _blocksData;} + +private: + int _size; + std::list _data; + + // Specific to schematics file + int _width; + int _length; + int _height; + char* _blocksData; + char* _blocksId; }; class TagIntArray : public Tag { - private: - int _size; - int* _data; +public: + TagIntArray(std::stringstream &ss); + ~TagIntArray() {delete _data;} - public: - TagIntArray(std::stringstream &ss); - ~TagIntArray() {delete _data;} - - int getSize() const {return _size;} - int* getData() const {return _data;} + int getSize() const {return _size;} + int* getData() const {return _data;} + +private: + int _size; + int* _data; }; + +#endif /* defined(__hifi__Tags__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 6189c04cbb..d4b7ec831e 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -802,7 +802,7 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v if (debug) { printLog("perlin=%f gradient=%f color=(%d,%d,%d)\n",perlin, gradient, red, green, blue); } - } break; + } break; } if (wantNaturalSurface) { // for natural surfaces, we will render up to 16 voxel's above the surface of the sphere @@ -1460,7 +1460,7 @@ bool VoxelTree::readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension bool VoxelTree::readFromSchematicsFile(const char *fileName) { std::stringstream ss; int err = retrieveData(fileName, ss); - if (err && TAG_Compound != ss.get()) { + if (err && ss.get() != TAG_Compound) { printLog("[ERROR] Invalid schematic file.\n"); return false; } @@ -1476,7 +1476,7 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { max = (max > schematics.getHeight()) ? max : schematics.getHeight(); int scale = 1; - while (max > scale) scale *= 2; + while (max > scale) {scale *= 2;} float size = 1.0f / scale; int create = 1; @@ -1491,41 +1491,41 @@ bool VoxelTree::readFromSchematicsFile(const char *fileName) { int data = schematics.getBlocksData()[pos]; create = 1; - computeBlockColor(id, data, &red, &green, &blue, &create); + computeBlockColor(id, data, red, green, blue, create); switch (create) { - case 1: - createVoxel(size * x, size * y, size * z, size, red, green, blue, true); - ++count; - break; - case 2: - switch (data) { - case 0: - createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - break; case 1: - createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x, size * y, size * z, size, red, green, blue, true); + ++count; break; case 2: - createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); - break; + switch (data) { + case 0: + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 1: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 2: + createVoxel(size * x , size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z + size / 2, size / 2, red, green, blue, true); + break; + case 3: + createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + break; + } + count += 2; + // There's no break on purpose. case 3: - createVoxel(size * x , size * y + size / 2, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y + size / 2, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); + createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); + createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); + count += 4; break; - } - count += 2; - // There's no break on purpose. - case 3: - createVoxel(size * x , size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z , size / 2, red, green, blue, true); - createVoxel(size * x , size * y, size * z + size / 2, size / 2, red, green, blue, true); - createVoxel(size * x + size / 2, size * y, size * z + size / 2, size / 2, red, green, blue, true); - count += 4; - break; } } } @@ -1620,160 +1620,155 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin } } -void VoxelTree::computeBlockColor(int id, int data, int* r, int* g, int* b, int* create) { - int red = *r, green = *g, blue = *b; +void VoxelTree::computeBlockColor(int id, int data, int& red, int& green, int& blue, int& create) { switch (id) { - case 1: - case 14: - case 15: - case 16: - case 21: - case 56: - case 73: - case 74: - case 97: - case 129: red = 128; green = 128; blue = 128; break; - case 2: red = 77; green = 117; blue = 66; break; - case 3: - case 60: red = 116; green = 83; blue = 56; break; - case 4: red = 71; green = 71; blue = 71; break; - case 5: - case 125: red = 133; green = 94; blue = 62; break; - case 7: red = 35; green = 35; blue = 35; break; - case 8: - case 9: red = 100; green = 109; blue = 185; break; - case 10: - case 11: red = 192; green = 64; blue = 8; break; - case 12: red = 209; green = 199; blue = 155; break; - case 13: red = 96; green = 94; blue = 93; break; - case 17: red = 71; green = 56; blue = 35; break; - case 18: red = 76; green = 104; blue = 64; break; - case 19: red = 119; green = 119; blue = 37; break; - case 22: red = 22; green = 44; blue = 86; break; - case 23: - case 29: - case 33: - case 61: - case 62: - case 158: red = 61; green = 61; blue = 61; break; - case 24: red = 209; green = 202; blue = 156; break; - case 25: - case 58: - case 84: - case 137: red = 57; green = 38; blue = 25; break; - case 35: - switch (data) { - case 0: red = 234; green = 234; blue = 234; break; - case 1: red = 224; green = 140; blue = 84; break; - case 2: red = 185; green = 90; blue = 194; break; - case 3: red = 124; green = 152; blue = 208; break; - case 4: red = 165; green = 154; blue = 35; break; - case 5: red = 70; green = 187; blue = 61; break; - case 6: red = 206; green = 124; blue = 145; break; - case 7: red = 66; green = 66; blue = 66; break; - case 8: red = 170; green = 176; blue = 176; break; - case 9: red = 45; green = 108; blue = 35; break; - case 10: red = 130; green = 62; blue = 8; break; - case 11: red = 43; green = 51; blue = 29; break; - case 12: red = 73; green = 47; blue = 29; break; - case 13: red = 57; green = 76; blue = 36; break; - case 14: red = 165; green = 58; blue = 53; break; - case 15: red = 24; green = 24; blue = 24; break; - default: - *create = 0; + case 1: + case 14: + case 15: + case 16: + case 21: + case 56: + case 73: + case 74: + case 97: + case 129: red = 128; green = 128; blue = 128; break; + case 2: red = 77; green = 117; blue = 66; break; + case 3: + case 60: red = 116; green = 83; blue = 56; break; + case 4: red = 71; green = 71; blue = 71; break; + case 5: + case 125: red = 133; green = 94; blue = 62; break; + case 7: red = 35; green = 35; blue = 35; break; + case 8: + case 9: red = 100; green = 109; blue = 185; break; + case 10: + case 11: red = 192; green = 64; blue = 8; break; + case 12: red = 209; green = 199; blue = 155; break; + case 13: red = 96; green = 94; blue = 93; break; + case 17: red = 71; green = 56; blue = 35; break; + case 18: red = 76; green = 104; blue = 64; break; + case 19: red = 119; green = 119; blue = 37; break; + case 22: red = 22; green = 44; blue = 86; break; + case 23: + case 29: + case 33: + case 61: + case 62: + case 158: red = 61; green = 61; blue = 61; break; + case 24: red = 209; green = 202; blue = 156; break; + case 25: + case 58: + case 84: + case 137: red = 57; green = 38; blue = 25; break; + case 35: + switch (data) { + case 0: red = 234; green = 234; blue = 234; break; + case 1: red = 224; green = 140; blue = 84; break; + case 2: red = 185; green = 90; blue = 194; break; + case 3: red = 124; green = 152; blue = 208; break; + case 4: red = 165; green = 154; blue = 35; break; + case 5: red = 70; green = 187; blue = 61; break; + case 6: red = 206; green = 124; blue = 145; break; + case 7: red = 66; green = 66; blue = 66; break; + case 8: red = 170; green = 176; blue = 176; break; + case 9: red = 45; green = 108; blue = 35; break; + case 10: red = 130; green = 62; blue = 8; break; + case 11: red = 43; green = 51; blue = 29; break; + case 12: red = 73; green = 47; blue = 29; break; + case 13: red = 57; green = 76; blue = 36; break; + case 14: red = 165; green = 58; blue = 53; break; + case 15: red = 24; green = 24; blue = 24; break; + default: + create = 0; + break; + } break; - } - break; - case 41: red = 239; green = 238; blue = 105; break; - case 42: red = 146; green = 146; blue = 146; break; - case 43: - case 98: red = 161; green = 161; blue = 161; break; - case 44: - *create = 3; + case 41: red = 239; green = 238; blue = 105; break; + case 42: red = 146; green = 146; blue = 146; break; + case 43: + case 98: red = 161; green = 161; blue = 161; break; + case 44: + create = 3; - switch (data) { - case 0: red = 161; green = 161; blue = 161; break; - case 1: red = 209; green = 202; blue = 156; break; - case 2: red = 133; green = 94; blue = 62; break; - case 3: red = 71; green = 71; blue = 71; break; - case 4: red = 121; green = 67; blue = 53; break; - case 5: red = 161; green = 161; blue = 161; break; - case 6: red = 45; green = 22; blue = 26; break; - case 7: red = 195; green = 192; blue = 185; break; - default: - *create = 0; + switch (data) { + case 0: red = 161; green = 161; blue = 161; break; + case 1: red = 209; green = 202; blue = 156; break; + case 2: red = 133; green = 94; blue = 62; break; + case 3: red = 71; green = 71; blue = 71; break; + case 4: red = 121; green = 67; blue = 53; break; + case 5: red = 161; green = 161; blue = 161; break; + case 6: red = 45; green = 22; blue = 26; break; + case 7: red = 195; green = 192; blue = 185; break; + default: + create = 0; + break; + } break; - } - break; - case 45: red = 121; green = 67; blue = 53; break; - case 46: red = 118; green = 36; blue = 13; break; - case 47: red = 155; green = 127; blue = 76; break; - case 48: red = 61; green = 79; blue = 61; break; - case 49: red = 52; green = 41; blue = 74; break; - case 52: red = 12; green = 66; blue = 71; break; - case 53: - case 67: - case 108: - case 109: - case 114: - case 128: - case 134: - case 135: - case 136: - case 156: - *create = 2; - - switch (id) { + case 45: red = 121; green = 67; blue = 53; break; + case 46: red = 118; green = 36; blue = 13; break; + case 47: red = 155; green = 127; blue = 76; break; + case 48: red = 61; green = 79; blue = 61; break; + case 49: red = 52; green = 41; blue = 74; break; + case 52: red = 12; green = 66; blue = 71; break; case 53: + case 67: + case 108: + case 109: + case 114: + case 128: case 134: case 135: - case 136: red = 133; green = 94; blue = 62; break; - case 67: red = 71; green = 71; blue = 71; break; - case 108: red = 121; green = 67; blue = 53; break; - case 109: red = 161; green = 161; blue = 161; break; - case 114: red = 45; green = 22; blue = 26; break; - case 128: red = 209; green = 202; blue = 156; break; - case 156: red = 195; green = 192; blue = 185; break; - default: - *create = 0; - break; - } - break; - case 54: - case 95: - case 146: red = 155; green = 105; blue = 32; break; - case 57: red = 145; green = 219; blue = 215; break; - case 79: red = 142; green = 162; blue = 195; break; - case 80: red = 255; green = 255; blue = 255; break; - case 81: red = 8; green = 64; blue = 15; break; - case 82: red = 150; green = 155; blue = 166; break; - case 86: - case 91: red = 179; green = 108; blue = 17; break; - case 87: - case 153: red = 91; green = 31; blue = 30; break; - case 88: red = 68; green = 49; blue = 38; break; - case 89: red = 180; green = 134; blue = 65; break; - case 103: red = 141; green = 143; blue = 36; break; - case 110: red = 103; green = 92; blue = 95; break; - case 112: red = 45; green = 22; blue = 26; break; - case 121: red = 183; green = 178; blue = 129; break; - case 123: red = 101; green = 59; blue = 31; break; - case 124: red = 213; green = 178; blue = 123; break; - case 130: red = 38; green = 54; blue = 56; break; - case 133: red = 53; green = 84; blue = 85; break; - case 152: red = 131; green = 22; blue = 7; break; - case 155: red = 195; green = 192; blue = 185; break; - case 159: red = 195; green = 165; blue = 150; break; - case 170: red = 168; green = 139; blue = 15; break; - case 172: red = 140; green = 86; blue = 61; break; - case 173: red = 9; green = 9; blue = 9; break; - default: - *create = 0; - break; - } + case 136: + case 156: + create = 2; - *r = red; - *g = green; - *b = blue; + switch (id) { + case 53: + case 134: + case 135: + case 136: red = 133; green = 94; blue = 62; break; + case 67: red = 71; green = 71; blue = 71; break; + case 108: red = 121; green = 67; blue = 53; break; + case 109: red = 161; green = 161; blue = 161; break; + case 114: red = 45; green = 22; blue = 26; break; + case 128: red = 209; green = 202; blue = 156; break; + case 156: red = 195; green = 192; blue = 185; break; + default: + create = 0; + break; + } + break; + case 54: + case 95: + case 146: red = 155; green = 105; blue = 32; break; + case 57: red = 145; green = 219; blue = 215; break; + case 79: red = 142; green = 162; blue = 195; break; + case 80: red = 255; green = 255; blue = 255; break; + case 81: red = 8; green = 64; blue = 15; break; + case 82: red = 150; green = 155; blue = 166; break; + case 86: + case 91: red = 179; green = 108; blue = 17; break; + case 87: + case 153: red = 91; green = 31; blue = 30; break; + case 88: red = 68; green = 49; blue = 38; break; + case 89: red = 180; green = 134; blue = 65; break; + case 103: red = 141; green = 143; blue = 36; break; + case 110: red = 103; green = 92; blue = 95; break; + case 112: red = 45; green = 22; blue = 26; break; + case 121: red = 183; green = 178; blue = 129; break; + case 123: red = 101; green = 59; blue = 31; break; + case 124: red = 213; green = 178; blue = 123; break; + case 130: red = 38; green = 54; blue = 56; break; + case 133: red = 53; green = 84; blue = 85; break; + case 152: red = 131; green = 22; blue = 7; break; + case 155: red = 195; green = 192; blue = 185; break; + case 159: red = 195; green = 165; blue = 150; break; + case 170: red = 168; green = 139; blue = 15; break; + case 172: red = 140; green = 86; blue = 61; break; + case 173: red = 9; green = 9; blue = 9; break; + default: + create = 0; + break; + } } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 56d0e1120e..d4fe888535 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -136,7 +136,7 @@ public: // reads voxels from square image with alpha as a Y-axis bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); bool readFromSchematicsFile(const char* filename); - void computeBlockColor(int id, int data, int* r, int* g, int* b, int* create); + void computeBlockColor(int id, int data, int& r, int& g, int& b, int& create); unsigned long getVoxelCount(); From e85d5f3b1432dd34dd7918334bbfe101ca0a6a9f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 9 Jul 2013 13:04:20 -0700 Subject: [PATCH 58/63] make keyhole radius larger --- libraries/voxels/src/ViewFrustum.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 6833eb6134..7308d5bb64 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -17,7 +17,7 @@ #include "AABox.h" #include "VoxelProjectedPolygon.h" -const float DEFAULT_KEYHOLE_RADIUS = 2.0f; +const float DEFAULT_KEYHOLE_RADIUS = 3.0f; class ViewFrustum { public: From 488a4affd3a0e9a396cf7e9295795a43ef567e5c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 10 Jul 2013 09:59:14 -0700 Subject: [PATCH 59/63] fix member variable initialization order to remove warnings --- interface/src/Swatch.cpp | 4 ++-- interface/src/Tool.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index ad0e3e420a..ea8516e9a8 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -3,8 +3,8 @@ Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), - _selected(1), - _textRenderer(MONO_FONT_FAMILY, 10, 100) { + _textRenderer(MONO_FONT_FAMILY, 10, 100), + _selected(1) { } void Swatch::reset() { diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp index 3ad5a648e5..ccb42381dd 100644 --- a/interface/src/Tool.cpp +++ b/interface/src/Tool.cpp @@ -5,8 +5,8 @@ #include Tool::Tool(QAction *action, GLuint texture, int x, int y) : - _texture(texture), _action(action), + _texture(texture), _x(x), _y(y) { } From c5df53ed525db899f8235681e9c1678646208b67 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 10 Jul 2013 11:26:39 -0700 Subject: [PATCH 60/63] fix cast to 0 for long long in SimpleMovingAverage --- libraries/shared/src/SimpleMovingAverage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SimpleMovingAverage.cpp b/libraries/shared/src/SimpleMovingAverage.cpp index df41433618..5a55486216 100644 --- a/libraries/shared/src/SimpleMovingAverage.cpp +++ b/libraries/shared/src/SimpleMovingAverage.cpp @@ -22,7 +22,7 @@ int SimpleMovingAverage::updateAverage(float sample) { if (_numSamples > 0) { _average = (ONE_MINUS_WEIGHTING * _average) + (WEIGHTING * sample); - float eventDelta = (usecTimestampNow() - _lastEventTimestamp) / 1000000; + float eventDelta = (usecTimestampNow() - _lastEventTimestamp) / 1000000.0f; if (_numSamples > 1) { _eventDeltaAverage = (ONE_MINUS_WEIGHTING * _eventDeltaAverage) + @@ -46,7 +46,7 @@ void SimpleMovingAverage::reset() { float SimpleMovingAverage::getEventDeltaAverage() { return (ONE_MINUS_WEIGHTING * _eventDeltaAverage) + - (WEIGHTING * ((usecTimestampNow() - _lastEventTimestamp) / 1000000)); + (WEIGHTING * ((usecTimestampNow() - _lastEventTimestamp) / 1000000.0f)); } float SimpleMovingAverage::getAverageSampleValuePerSecond() { From 70ccef0733d93da8e548a3e29ae481ead7514f65 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 10 Jul 2013 12:37:23 -0700 Subject: [PATCH 61/63] remove avatar data read and write to file --- interface/src/Avatar.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index 855d49e51c..654cf4b8fe 100755 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -151,10 +151,6 @@ public: // Get the position/rotation of a single body ball void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const; - - //read/write avatar data - void writeAvatarDataToFile(); - void readAvatarDataFromFile(); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); From 7d94f65448658bc6120ce3dc2a38fd9a53dee504 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 10 Jul 2013 13:00:16 -0700 Subject: [PATCH 62/63] Linked ZLIB to voxels library --- interface/src/Application.cpp | 2 +- libraries/voxels/CMakeLists.txt | 7 ++++++- libraries/voxels/src/VoxelTree.cpp | 2 +- libraries/voxels/src/VoxelTree.h | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9c72a94f5d..6480a39539 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1390,7 +1390,7 @@ void Application::importVoxels() { } else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) { importVoxels.readFromSVOFile(fileName); } else { - importVoxels.readFromSchematicsFile(fileName); + importVoxels.readFromSchematicFile(fileName); } VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); diff --git a/libraries/voxels/CMakeLists.txt b/libraries/voxels/CMakeLists.txt index d873320064..861001ed35 100644 --- a/libraries/voxels/CMakeLists.txt +++ b/libraries/voxels/CMakeLists.txt @@ -15,4 +15,9 @@ include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} ${ROOT_DIR}) include(${MACRO_DIR}/LinkHifiLibrary.cmake) -link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# link ZLIB +find_package(ZLIB) +include_directories(${ZLIB_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index d4b7ec831e..f17919bce9 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1457,7 +1457,7 @@ bool VoxelTree::readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension return true; } -bool VoxelTree::readFromSchematicsFile(const char *fileName) { +bool VoxelTree::readFromSchematicFile(const char *fileName) { std::stringstream ss; int err = retrieveData(fileName, ss); if (err && ss.get() != TAG_Compound) { diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index d4fe888535..e46ac4c2cb 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -135,7 +135,7 @@ public: bool readFromSVOFile(const char* filename); // reads voxels from square image with alpha as a Y-axis bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); - bool readFromSchematicsFile(const char* filename); + bool readFromSchematicFile(const char* filename); void computeBlockColor(int id, int data, int& r, int& g, int& b, int& create); unsigned long getVoxelCount(); From 8bccfff5b562201f15c1600a6e4ce225ca78c0ec Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jul 2013 13:14:23 -0700 Subject: [PATCH 63/63] fix random big average voxels slightly in view bug --- libraries/voxels/src/VoxelTree.cpp | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b16425370e..c745f4616d 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1270,9 +1270,39 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool childWasInView = (childNode && params.deltaViewFrustum && (params.lastViewFrustum && ViewFrustum::INSIDE == childNode->inFrustum(*params.lastViewFrustum))); - + + // There are two types of nodes for which we want to send colors: + // 1) Leaves - obviously + // 2) Non-leaves who's children would be visible and beyond our LOD. + // NOTE: This code works, but it's pretty expensive, because we're calculating distances for all the grand + // children, which we'll end up doing again later in the next level of recursion. We need to optimize this + // in the future. + bool isLeafOrLOD = childNode->isLeaf(); + if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) { + int grandChildrenInView = 0; + int grandChildrenInLOD = 0; + for (int grandChildIndex = 0; grandChildIndex < NUMBER_OF_CHILDREN; grandChildIndex++) { + VoxelNode* grandChild = childNode->getChildAtIndex(grandChildIndex); + + if (grandChild && grandChild->isColored() && grandChild->isInView(*params.viewFrustum)) { + grandChildrenInView++; + + float grandChildDistance = grandChild->distanceToCamera(*params.viewFrustum); + float grandChildBoundaryDistance = boundaryDistanceForRenderLevel(grandChild->getLevel() + 1); + if (grandChildDistance < grandChildBoundaryDistance) { + grandChildrenInLOD++; + } + } + } + // if any of our grandchildren ARE in view, then we don't want to include our color. If none are, then + // we do want to include our color + if (grandChildrenInView > 0 && grandChildrenInLOD==0) { + isLeafOrLOD = true; + } + } + // track children with actual color, only if the child wasn't previously in view! - if (childNode && childNode->isColored() && !childWasInView && !childIsOccluded) { + if (childNode && isLeafOrLOD && childNode->isColored() && !childWasInView && !childIsOccluded) { childrenColoredBits += (1 << (7 - originalIndex)); inViewWithColorCount++; }

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 literal 0 HcmV?d00001 From c683204df73cc1eb786869bc043a8023818db222 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Jun 2013 14:58:07 -0700 Subject: [PATCH 05/63] More Mac libraries. --- .../external/skeltrack/lib/MacOS/libskeltrack.a | Bin 0 -> 52416 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/external/skeltrack/lib/MacOS/libskeltrack.a diff --git a/interface/external/skeltrack/lib/MacOS/libskeltrack.a b/interface/external/skeltrack/lib/MacOS/libskeltrack.a new file mode 100644 index 0000000000000000000000000000000000000000..5a7a393c49a17978cc1ed5a750b1e3271c883b07 GIT binary patch literal 52416 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 IL5LyxKLPpTk^lez literal 0 HcmV?d00001 From 3d214c2655cc38607ab8ea2f94b161a841dc98eb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 25 Jun 2013 15:26:07 -0700 Subject: [PATCH 06/63] More work on Kinect integration. --- interface/src/Webcam.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index f01b9a5804..ce12ee4384 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -181,6 +181,8 @@ FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0), _freenect FrameGrabber::~FrameGrabber() { if (_freenectContext != 0) { + freenect_stop_depth(_freenectDevice); + freenect_stop_video(_freenectDevice); freenect_close_device(_freenectDevice); freenect_shutdown(_freenectContext); @@ -198,7 +200,7 @@ void FrameGrabber::grabFrame() { return; } if (_freenectContext != 0) { - + freenect_process_events(_freenectContext); return; } @@ -250,11 +252,21 @@ void FrameGrabber::grabFrame() { Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect)); } +static void depthCallback(freenect_device* freenectDevice, void* depth, uint32_t timestamp) { +} + +static void videoCallback(freenect_device* freenectDevice, void* video, uint32_t timestamp) { +} + bool FrameGrabber::init() { // first try for a Kinect if (freenect_init(&_freenectContext, 0) >= 0) { if (freenect_num_devices(_freenectContext) > 0) { if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { + freenect_set_depth_callback(_freenectDevice, depthCallback); + freenect_set_video_callback(_freenectDevice, videoCallback); + freenect_start_depth(_freenectDevice); + freenect_start_video(_freenectDevice); return true; } } From 5e989b6ee5a8596dbf3b00b22f1c0b650ed28b2d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Jun 2013 12:19:10 -0700 Subject: [PATCH 07/63] More work on obtaining Kinect data. --- interface/src/Webcam.cpp | 21 +++++++++++++++++++-- interface/src/Webcam.h | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index ce12ee4384..5c63ed488b 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -252,21 +252,38 @@ void FrameGrabber::grabFrame() { Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect)); } +const char* FREENECT_LOG_LEVEL_NAMES[] = { "fatal", "error", "warning", "notice", "info", "debug", "spew", "flood" }; + +static void logCallback(freenect_context* freenectDevice, freenect_loglevel level, const char *msg) { + printLog("Freenect %s: %s\n", FREENECT_LOG_LEVEL_NAMES[level], msg); +} + static void depthCallback(freenect_device* freenectDevice, void* depth, uint32_t timestamp) { + qDebug() << "Got depth " << depth << timestamp; } static void videoCallback(freenect_device* freenectDevice, void* video, uint32_t timestamp) { + qDebug() << "Got video " << video << timestamp; } bool FrameGrabber::init() { // first try for a Kinect if (freenect_init(&_freenectContext, 0) >= 0) { + freenect_set_log_level(_freenectContext, FREENECT_LOG_DEBUG); + freenect_set_log_callback(_freenectContext, logCallback); + freenect_select_subdevices(_freenectContext, FREENECT_DEVICE_CAMERA); + if (freenect_num_devices(_freenectContext) > 0) { if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { freenect_set_depth_callback(_freenectDevice, depthCallback); - freenect_set_video_callback(_freenectDevice, videoCallback); + freenect_set_video_callback(_freenectDevice, videoCallback); + _freenectVideoMode = freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB); + freenect_set_video_mode(_freenectDevice, _freenectVideoMode); + _freenectDepthMode = freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT); + freenect_set_depth_mode(_freenectDevice, _freenectDepthMode); + freenect_start_depth(_freenectDevice); - freenect_start_video(_freenectDevice); + freenect_start_video(_freenectDevice); return true; } } diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 59110d35a2..5360b94764 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -97,6 +97,8 @@ private: freenect_context* _freenectContext; freenect_device* _freenectDevice; + freenect_frame_mode _freenectVideoMode; + freenect_frame_mode _freenectDepthMode; }; Q_DECLARE_METATYPE(cv::Mat) From afab7d32ef37b6047b7dde896cbc6b44fbf6cb45 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Jun 2013 12:57:57 -0700 Subject: [PATCH 08/63] Version of freenect patched for Kinect for Windows support. --- .../freenect/include/libfreenect-audio.h | 7 ++++++- .../include/libfreenect-registration.h | 5 ++++- .../external/freenect/include/libfreenect.h | 15 ++++++++++++++- .../external/freenect/lib/MacOS/libfreenect.a | Bin 67216 -> 68160 bytes .../freenect/lib/MacOS/libfreenect_sync.a | Bin 10496 -> 10496 bytes .../external/freenect/lib/UNIX/libfreenect.a | Bin 65072 -> 65880 bytes .../freenect/lib/UNIX/libfreenect_sync.a | Bin 12952 -> 12952 bytes 7 files changed, 24 insertions(+), 3 deletions(-) diff --git a/interface/external/freenect/include/libfreenect-audio.h b/interface/external/freenect/include/libfreenect-audio.h index 108cf38178..3f2febabba 100644 --- a/interface/external/freenect/include/libfreenect-audio.h +++ b/interface/external/freenect/include/libfreenect-audio.h @@ -24,7 +24,8 @@ * either License. */ -#pragma once +#ifndef LIBFREENECT_AUDIO_H +#define LIBFREENECT_AUDIO_H #include #include @@ -113,3 +114,7 @@ FREENECTAPI int freenect_stop_audio(freenect_device* dev); #ifdef __cplusplus } #endif + +#endif // + + diff --git a/interface/external/freenect/include/libfreenect-registration.h b/interface/external/freenect/include/libfreenect-registration.h index 7ad2b5bb80..2ef5d60981 100644 --- a/interface/external/freenect/include/libfreenect-registration.h +++ b/interface/external/freenect/include/libfreenect-registration.h @@ -24,7 +24,8 @@ * either License. */ -#pragma once +#ifndef LIBFREENECT_REGISTRATION_H +#define LIBFREENECT_REGISTRATION_H #include #include @@ -124,3 +125,5 @@ FREENECTAPI void freenect_camera_to_world(freenect_device* dev, #ifdef __cplusplus } #endif + +#endif // LIBFREENECT_REGISTRATION_H diff --git a/interface/external/freenect/include/libfreenect.h b/interface/external/freenect/include/libfreenect.h index 13c7c4c538..470558c33a 100644 --- a/interface/external/freenect/include/libfreenect.h +++ b/interface/external/freenect/include/libfreenect.h @@ -24,7 +24,8 @@ * either License. */ -#pragma once +#ifndef LIBFREENECT_H +#define LIBFREENECT_H #include @@ -302,6 +303,15 @@ FREENECTAPI int freenect_supported_subdevices(void); */ FREENECTAPI void freenect_select_subdevices(freenect_context *ctx, freenect_device_flags subdevs); +/** + * Returns the devices that are enabled after calls to freenect_open_device() + * On newer kinects the motor and audio are automatically disabled for now + * + * @param ctx Context to set future subdevice selection for + * @return Flags representing the subdevices that were actually opened (see freenect_device_flags) + */ +FREENECTAPI freenect_device_flags freenect_enabled_subdevices(freenect_context *ctx); + /** * Opens a kinect device via a context. Index specifies the index of * the device on the current state of the bus. Bus resets may cause @@ -617,3 +627,6 @@ FREENECTAPI int freenect_set_depth_mode(freenect_device* dev, const freenect_fra #ifdef __cplusplus } #endif + +#endif // + diff --git a/interface/external/freenect/lib/MacOS/libfreenect.a b/interface/external/freenect/lib/MacOS/libfreenect.a index 81c2b3a28331b96601458a05debc06e9f2c5413a..19a9fc1806dd4b8736eda1b53a85d47e41ed31e3 100644 GIT binary patch delta 7376 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 OPhCwO#sa5n1OE#Gtey@4 delta 6672 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 XQ8{9WsO+i~Yl=LHd)~gT=K}u+*1y^v diff --git a/interface/external/freenect/lib/MacOS/libfreenect_sync.a b/interface/external/freenect/lib/MacOS/libfreenect_sync.a index 6a2938d5859eb25159d8e597d0be57aa254a5166..9f739a45007c621fafe7b9ff237b2c4a24ecb359 100644 GIT binary patch delta 133 zcmZn&Y6zMj!)j!1W^6Lqkx_i31q&mDH&Ic1a|9y?Bct@@L?%H2CIzF(MM7IxmakrA zG}%zte6j*F>*fsM3U0-1d<LmDjr delta 132 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 delta 6480 zcmb_heOOdg8b9wDzP|<(=5Pm;0YOoaVL$;Z`LL$OEe$HmTrUR`D6B1=YEdUA%zlx*TZ*V1INq>V^Z#HM^dLdZx$yzl$puh=GX^IGKk z(nLtg-2d;@u$cVUE5?=35le1!$=MJ9xTb%gTtao8UP^?JGkSs_0T%vM}|6WhIBls(d$NPrEM7+>93g%;AXcWAU zLqnrr4~_|qf>SsVeh25VDlDCDXZU$oCQbB3OPB#ohIs8U-w+SUSYgPb z%l+_TVkX_?r&3tFZwvoogpdr(W9hzYBGk9{u!^PAs{wd%Xr}L)V2u(Ip6=@iRo@(P zUie!8#n>e=dfijLCOr<|kQxsadt7j$u|0x8yi(@ya`Jep zGQ+%8rcu3-0wFgGc^@I4FXTBwZV+-5@+aKv)G59im2$6A9^$3k68QZe^gX}-=bXP%mbHO3{Zuo(M)P}A@TS%<mtxKqQmeo_@#A^7B{&Wdt#ZSbq0khG`Q9i7#+u;81v#H)ZEO#h293w z)M3NZl;ngN*c|tmJa}LV;KD)cb$09bcw$f-7RH~Wgy5rte|Y#0++j{o@So_tfnGW* z1bF+9NJ7X&wT`5Q-!+_mz+nZSFYqz9;5Pa7Awz-2>d?=g2G8S#ge*E$cO!SjBhTF# zSeKYZ9Xcg`X^CKP z1^(f*uKErxvoJayFv($nIoNDr#`j!49H%}cRDT7WWHHeDDZjyN?3cofTSNewS*@*` zD2`1r7*E^*{y~8yFOkab+I_GtD$kA6&NV(sifPwrph!zuC*1t~;GMyG8*aqbok$ z#fR6uynEX!4xKIPb~-D{yRSQ)rOmwLq`4{fmHgReFL&UY$71D%{16>HjGs)I4rei8 zYA(Es^QX>&m3VHdB_isA!pv|wox6Ff?d3_xO?zFy;nVJe7JOxzF=BnU(^+Y=t2Ex& zd;1ES9k)#jH@?t)(+aQY4J%J#*R*iRmak1a4p4|kpA9!Y@{_`i=N@n2jzE|Z4kp<+ zqYWS)&(BQo>TPp6&!ym+nGy~!uy}9bucryFoyl-XK_WbZ^#uu#hTjz2@B5FVPUrb} z`^tOp=u86!6!^hG98)-)k`7!_m~1RR<8P zj0`*Dbe>NYu-Y7+pf<-f%$^k<+4Y0dS*dRvbJ3Nf+Bt}GXDxvo{B_n;_!mx^{Up4G zO|z#$n%u8wGldxWv*%Y+j|rbSomkz(uyjs148m1&Vxby8o-=}yZfu#8hy&-o0Y|WY z?o~_;Gs?9k@&3Bhlob4O@m#Feg~}64ZC?6X-fG&Yp!-mkUmvG~^|G(M+LP8&3~G0a(5Ep3nN%Rq5`m&XTQLWj)P9dD<})DweJ0RTSd2`{8>vb&)Efa0 z)o(VgMJA@Tyb+X+z zRF(5a<PRccNtzgAlCR#9#UnVqQwuM8;{B-xQVuFmE%HvPE9F+bP0Q#X{a@ z27y?=nMsR8j_~+h*FIlx6fP^}EDYJgq=lKcZ@C7{*}_J53RH_{Sb)mDdQ4p#QJSa8ZUIS-O!10*S13-trN-lCb#FZNQ}UJ^-NkRMm0Xvx}wvur=Cf1 zq9X;ntSvZk6h=2N>1l!Hx}gP_*TAF>fmXVq3`-lB)G9h&n;Y7W^$koqB|2U({~Rtc z=V4a^lPW}K%XLGuF+{#OAXmDf1(+k>9FW`G(C@KWzGbm&v81MDC_CBhO#W8x15k=R zA4%{a`tL|cFzOqvpH3e+$)hAh-)Q}O`pB)c#JkLT1$&3Cz~T3Gh%+iN{=8?AsGAf(KC36v;{_N2fS zT)W2zN{#xfH40W^S)9#-E^jGGyVXuokV2c6d#l(J3!9^*D{$VUQt1@kV7GQFouX6! z=9OMt-#h>u_*rv5aL7M4|Jl=fSC7+a9~X?-`@^w%zZdO$S+3b1r^CYsU%`ck`s4Y7 z{_=)H6Le-}`KPV2=#Kx`d)au3YPjxRG#{=CF6RCHhHRFH+;(^_P3vO=1YI%>qrX3 zEEW>m6_13xpd5@W=cTk>aE^&NB2Em5I~4S=Kz|8RRZ|6eQnAp(OAo#Q!{VPqBmMdE zkpdYanrgCeC#i%m`K#kLikW}EhzSl0yn+@JqwVUZF$s z$^d%2;9$mw#Bj-*kskqRGy7&8PI4HG2V7w00{^7Lu_p}D6w&(p=6YmOi3?6JW0MQ& zx(to=KstsjAL9TXdd7m;rwYo#kr?HZarl%#)u`D7UK7UHO!y0j7#0%VIntHsc@GY{H%cg zslq{`cWcI%2-p;$7$5Hj9~W?e0@D`*lnCE)V_gxfwJPiWK)mQM&}WpJv0kisG>`Z2 z2SUjHKur3YNdo2V=ellO8l-rt5$UEWPZY|#f|PJ0`MIK49`FWzL%>OT4yQaLU~Zgm zv9vzAz)kH#q4t)dX58im9~baGJr;e#K#vPg7$5)_bTH!}aa_z9Jb+$79TCg}preB^ zsMA27RBpzpZsb^ztS>5LW1tu~euS_!bbWVXXgjmK$!!Aa1JaBBP z0p{WAE+)OhIfM|C_%7TczxLvhE@spQK|Z7NpN4k$@{>rTHvWC+2LBR-Nv91l+L&gx UWT=0Gh~F)zMND3B`s@Dx2I1+tZvX%Q diff --git a/interface/external/freenect/lib/UNIX/libfreenect_sync.a b/interface/external/freenect/lib/UNIX/libfreenect_sync.a index 7e509c8b3875a94015b38ebb4532281ff8fde526..85b7479a75d0e853c60e539d3a1f409cf3f3ca39 100644 GIT binary patch delta 49 zcmbP{IwN(044aX;se!TKMkO;wD06chqoWL?>Es2nnoO1klTXWrFm2 AKL7v# From 5ef400b19392d975c99d0c0eb6b1294a2b25dc8e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Jun 2013 15:35:44 -0700 Subject: [PATCH 09/63] Got the depth information displaying. --- interface/src/Webcam.cpp | 120 ++++++++++++++++++++++++++++----------- interface/src/Webcam.h | 10 ++-- 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 5c63ed488b..9438a18c3d 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -25,7 +25,7 @@ using namespace std; int matMetaType = qRegisterMetaType("cv::Mat"); int rotatedRectMetaType = qRegisterMetaType("cv::RotatedRect"); -Webcam::Webcam() : _enabled(false), _active(false), _frameTextureID(0) { +Webcam::Webcam() : _enabled(false), _active(false), _frameTextureID(0), _depthTextureID(0) { // the grabber simply runs as fast as possible _grabber = new FrameGrabber(); _grabber->moveToThread(&_grabberThread); @@ -79,6 +79,22 @@ 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); + int depthPreviewWidth = _depthWidth * PREVIEW_HEIGHT / _depthHeight; + int depthLeft = screenWidth - depthPreviewWidth - 10; + glTexCoord2f(0, 0); + glVertex2f(depthLeft, top - PREVIEW_HEIGHT); + glTexCoord2f(1, 0); + glVertex2f(depthLeft + depthPreviewWidth, top - PREVIEW_HEIGHT); + glTexCoord2f(1, 1); + glVertex2f(depthLeft + depthPreviewWidth, top); + glTexCoord2f(0, 1); + glVertex2f(depthLeft, top); + glEnd(); + } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); @@ -107,20 +123,38 @@ Webcam::~Webcam() { delete _grabber; } -void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) { +void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const RotatedRect& faceRect) { IplImage image = frame; glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3); if (_frameTextureID == 0) { glGenTextures(1, &_frameTextureID); glBindTexture(GL_TEXTURE_2D, _frameTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, GL_BGR, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, format, GL_UNSIGNED_BYTE, image.imageData); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - printLog("Capturing webcam at %dx%d.\n", _frameWidth, _frameHeight); + printLog("Capturing video at %dx%d.\n", _frameWidth, _frameHeight); } else { glBindTexture(GL_TEXTURE_2D, _frameTextureID); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_BGR, GL_UNSIGNED_BYTE, image.imageData); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, format, GL_UNSIGNED_BYTE, image.imageData); + } + + if (!depth.empty()) { + IplImage depthImage = depth; + glPixelStorei(GL_UNPACK_ROW_LENGTH, depthImage.widthStep); + if (_depthTextureID == 0) { + glGenTextures(1, &_depthTextureID); + glBindTexture(GL_TEXTURE_2D, _depthTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _depthWidth = depthImage.width, _depthHeight = depthImage.height, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, depthImage.imageData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + printLog("Capturing depth at %dx%d.\n", _depthWidth, _depthHeight); + + } else { + glBindTexture(GL_TEXTURE_2D, _depthTextureID); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _depthWidth, _depthHeight, GL_LUMINANCE, + GL_UNSIGNED_BYTE, depthImage.imageData); + } } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glBindTexture(GL_TEXTURE_2D, 0); @@ -195,39 +229,51 @@ void FrameGrabber::reset() { _searchWindow = Rect(0, 0, 0, 0); } +static Mat videoFrame, depthFrame; +static bool gotVideoFrame, gotDepthFrame; + void FrameGrabber::grabFrame() { if (_capture == 0 && _freenectContext == 0 && !init()) { return; } + int format = GL_BGR; + ::gotVideoFrame = ::gotDepthFrame = false; if (_freenectContext != 0) { freenect_process_events(_freenectContext); - return; + if (::gotDepthFrame) { + ::depthFrame.convertTo(_grayDepth, CV_8UC1, 255.0 / 2047.0); + } + format = GL_RGB; + + } else { + IplImage* image = cvQueryFrame(_capture); + if (image != 0) { + // make sure it's in the format we expect + if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || + image->origin != 0) { + printLog("Invalid webcam image format.\n"); + return; + } + ::videoFrame = image; + ::gotVideoFrame = true; + } } - - IplImage* image = cvQueryFrame(_capture); - if (image == 0) { + if (!::gotVideoFrame) { // try again later QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection); return; - } - // make sure it's in the format we expect - if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || - image->origin != 0) { - printLog("Invalid webcam image format.\n"); - return; } // if we don't have a search window (yet), try using the face cascade - Mat frame = image; int channels = 0; float ranges[] = { 0, 180 }; const float* range = ranges; if (_searchWindow.area() == 0) { vector faces; - _faceCascade.detectMultiScale(frame, faces, 1.1, 6); + _faceCascade.detectMultiScale(::videoFrame, faces, 1.1, 6); if (!faces.empty()) { _searchWindow = faces.front(); - updateHSVFrame(frame); + updateHSVFrame(::videoFrame, format); Mat faceHsv(_hsvFrame, _searchWindow); Mat faceMask(_mask, _searchWindow); @@ -240,7 +286,7 @@ void FrameGrabber::grabFrame() { } RotatedRect faceRect; if (_searchWindow.area() > 0) { - updateHSVFrame(frame); + updateHSVFrame(::videoFrame, format); calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range); bitwise_and(_backProject, _mask, _backProject); @@ -249,7 +295,7 @@ void FrameGrabber::grabFrame() { _searchWindow = faceRect.boundingRect(); } QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", - Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect)); + Q_ARG(cv::Mat, ::videoFrame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepth), Q_ARG(cv::RotatedRect, faceRect)); } const char* FREENECT_LOG_LEVEL_NAMES[] = { "fatal", "error", "warning", "notice", "info", "debug", "spew", "flood" }; @@ -258,18 +304,29 @@ static void logCallback(freenect_context* freenectDevice, freenect_loglevel leve printLog("Freenect %s: %s\n", FREENECT_LOG_LEVEL_NAMES[level], msg); } +static freenect_frame_mode freenectVideoMode, freenectDepthMode; + static void depthCallback(freenect_device* freenectDevice, void* depth, uint32_t timestamp) { - qDebug() << "Got depth " << depth << timestamp; + ::depthFrame = Mat(::freenectDepthMode.height, ::freenectDepthMode.width, CV_16UC1, depth); + ::gotDepthFrame = true; } static void videoCallback(freenect_device* freenectDevice, void* video, uint32_t timestamp) { - qDebug() << "Got video " << video << timestamp; + ::videoFrame = Mat(::freenectVideoMode.height, ::freenectVideoMode.width, CV_8UC3, video); + ::gotVideoFrame = true; } bool FrameGrabber::init() { + // load our face cascade + switchToResourcesParentIfRequired(); + if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { + printLog("Failed to load Haar cascade for face tracking.\n"); + return false; + } + // first try for a Kinect if (freenect_init(&_freenectContext, 0) >= 0) { - freenect_set_log_level(_freenectContext, FREENECT_LOG_DEBUG); + freenect_set_log_level(_freenectContext, FREENECT_LOG_WARNING); freenect_set_log_callback(_freenectContext, logCallback); freenect_select_subdevices(_freenectContext, FREENECT_DEVICE_CAMERA); @@ -277,10 +334,10 @@ bool FrameGrabber::init() { if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { freenect_set_depth_callback(_freenectDevice, depthCallback); freenect_set_video_callback(_freenectDevice, videoCallback); - _freenectVideoMode = freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB); - freenect_set_video_mode(_freenectDevice, _freenectVideoMode); - _freenectDepthMode = freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT); - freenect_set_depth_mode(_freenectDevice, _freenectDepthMode); + ::freenectVideoMode = freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB); + freenect_set_video_mode(_freenectDevice, ::freenectVideoMode); + ::freenectDepthMode = freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT); + freenect_set_depth_mode(_freenectDevice, ::freenectDepthMode); freenect_start_depth(_freenectDevice); freenect_start_video(_freenectDevice); @@ -312,15 +369,10 @@ bool FrameGrabber::init() { cvSetCaptureProperty(_capture, CV_CAP_PROP_GAIN, 0.5); #endif - switchToResourcesParentIfRequired(); - if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { - printLog("Failed to load Haar cascade for face tracking.\n"); - return false; - } return true; } -void FrameGrabber::updateHSVFrame(const Mat& frame) { - cvtColor(frame, _hsvFrame, CV_BGR2HSV); +void FrameGrabber::updateHSVFrame(const Mat& frame, int format) { + cvtColor(frame, _hsvFrame, format == GL_RGB ? CV_RGB2HSV : CV_BGR2HSV); inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask); } diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 5360b94764..27e54caada 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -45,7 +45,7 @@ public: public slots: void setEnabled(bool enabled); - void setFrame(const cv::Mat& image, const cv::RotatedRect& faceRect); + void setFrame(const cv::Mat& video, int format, const cv::Mat& depth, const cv::RotatedRect& faceRect); private: @@ -56,7 +56,10 @@ private: bool _active; int _frameWidth; int _frameHeight; + int _depthWidth; + int _depthHeight; GLuint _frameTextureID; + GLuint _depthTextureID; cv::RotatedRect _faceRect; cv::RotatedRect _initialFaceRect; @@ -85,7 +88,7 @@ public slots: private: bool init(); - void updateHSVFrame(const cv::Mat& frame); + void updateHSVFrame(const cv::Mat& frame, int format); CvCapture* _capture; cv::CascadeClassifier _faceCascade; @@ -94,11 +97,10 @@ private: cv::SparseMat _histogram; cv::Mat _backProject; cv::Rect _searchWindow; + cv::Mat _grayDepth; freenect_context* _freenectContext; freenect_device* _freenectDevice; - freenect_frame_mode _freenectVideoMode; - freenect_frame_mode _freenectDepthMode; }; Q_DECLARE_METATYPE(cv::Mat) From f83c16e578267bc769cec80efcd99a6c7d9af255 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Jun 2013 12:27:50 -0700 Subject: [PATCH 10/63] Goodbye, skeltrack. You won't be missed. --- cmake/modules/FindSkeltrack.cmake | 44 - interface/external/skeltrack/AUTHORS | 1 - interface/external/skeltrack/CMakeLists.txt | 14 - interface/external/skeltrack/COPYING | 165 -- .../skeltrack/include/skeltrack-joint.h | 90 - .../skeltrack/include/skeltrack-skeleton.h | 95 - .../skeltrack/include/skeltrack-smooth.h | 36 - .../skeltrack/include/skeltrack-util.h | 127 -- .../external/skeltrack/include/skeltrack.h | 28 - .../skeltrack/lib/MacOS/libskeltrack.a | Bin 52416 -> 0 bytes .../skeltrack/lib/UNIX/libskeltrack.a | Bin 53760 -> 0 bytes .../external/skeltrack/src/skeltrack-joint.c | 159 -- .../skeltrack/src/skeltrack-skeleton.c | 2027 ----------------- .../external/skeltrack/src/skeltrack-smooth.c | 226 -- .../external/skeltrack/src/skeltrack-util.c | 626 ----- 15 files changed, 3638 deletions(-) delete mode 100644 cmake/modules/FindSkeltrack.cmake delete mode 100644 interface/external/skeltrack/AUTHORS delete mode 100644 interface/external/skeltrack/CMakeLists.txt delete mode 100644 interface/external/skeltrack/COPYING delete mode 100644 interface/external/skeltrack/include/skeltrack-joint.h delete mode 100644 interface/external/skeltrack/include/skeltrack-skeleton.h delete mode 100644 interface/external/skeltrack/include/skeltrack-smooth.h delete mode 100644 interface/external/skeltrack/include/skeltrack-util.h delete mode 100644 interface/external/skeltrack/include/skeltrack.h delete mode 100644 interface/external/skeltrack/lib/MacOS/libskeltrack.a delete mode 100644 interface/external/skeltrack/lib/UNIX/libskeltrack.a delete mode 100644 interface/external/skeltrack/src/skeltrack-joint.c delete mode 100644 interface/external/skeltrack/src/skeltrack-skeleton.c delete mode 100644 interface/external/skeltrack/src/skeltrack-smooth.c delete mode 100644 interface/external/skeltrack/src/skeltrack-util.c diff --git a/cmake/modules/FindSkeltrack.cmake b/cmake/modules/FindSkeltrack.cmake deleted file mode 100644 index a05e0c2990..0000000000 --- a/cmake/modules/FindSkeltrack.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Try to find the Skeltrack library to perform skeleton tracking via depth camera -# -# You must provide a SKELTRACK_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# SKELTRACK_FOUND - system found Skeltrack -# SKELTRACK_INCLUDE_DIRS - the Skeltrack include directory -# SKELTRACK_LIBRARIES - Link this to use Skeltrack -# -# Created on 6/25/2013 by Andrzej Kapolka -# Copyright (c) 2013 High Fidelity -# - -if (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) - # in cache already - set(SKELTRACK_FOUND TRUE) -else (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) - find_path(SKELTRACK_INCLUDE_DIRS skeltrack.h ${SKELTRACK_ROOT_DIR}/include) - - if (APPLE) - find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/MacOS/) - elseif (UNIX) - find_library(SKELTRACK_LIBRARIES libskeltrack.a ${SKELTRACK_ROOT_DIR}/lib/UNIX/) - endif () - - if (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) - set(SKELTRACK_FOUND TRUE) - endif (SKELTRACK_INCLUDE_DIRS AND SKELTRACK_LIBRARIES) - - if (SKELTRACK_FOUND) - if (NOT SKELTRACK_FIND_QUIETLY) - message(STATUS "Found Skeltrack: ${SKELTRACK_LIBRARIES}") - endif (NOT SKELTRACK_FIND_QUIETLY) - else (SKELTRACK_FOUND) - if (SKELTRACK_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Skeltrack") - endif (SKELTRACK_FIND_REQUIRED) - endif (SKELTRACK_FOUND) - - # show the SKELTRACK_INCLUDE_DIRS and SKELTRACK_LIBRARIES variables only in the advanced view - mark_as_advanced(SKELTRACK_INCLUDE_DIRS SKELTRACK_LIBRARIES) - -endif (SKELTRACK_LIBRARIES AND SKELTRACK_INCLUDE_DIRS) diff --git a/interface/external/skeltrack/AUTHORS b/interface/external/skeltrack/AUTHORS deleted file mode 100644 index 751f7ed02c..0000000000 --- a/interface/external/skeltrack/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Joaquim Rocha - Creator diff --git a/interface/external/skeltrack/CMakeLists.txt b/interface/external/skeltrack/CMakeLists.txt deleted file mode 100644 index 21ef04931b..0000000000 --- a/interface/external/skeltrack/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -include_directories(include) - -# grab the implementation and header files from src dirs -file(GLOB SKELTRACK_SRCS src/*.c include/*.h) - -find_package(PkgConfig REQUIRED) -pkg_check_modules(GLIB2 glib-2.0) - -string(REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GLIB2_STATIC_CFLAGS} ${GLIB2_STATIC_LDFLAGS}") -message("${CMAKE_C_FLAGS}") - -add_library(skeltrack ${SKELTRACK_SRCS}) - diff --git a/interface/external/skeltrack/COPYING b/interface/external/skeltrack/COPYING deleted file mode 100644 index 65c5ca88a6..0000000000 --- a/interface/external/skeltrack/COPYING +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/interface/external/skeltrack/include/skeltrack-joint.h b/interface/external/skeltrack/include/skeltrack-joint.h deleted file mode 100644 index 4a9e0ba28c..0000000000 --- a/interface/external/skeltrack/include/skeltrack-joint.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * skeltrak-joint.h - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 3, or (at your option) any later version as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#ifndef __SKELTRACK_JOINT_H__ -#define __SKELTRACK_JOINT_H__ - -#include -#include - -G_BEGIN_DECLS - -#define SKELTRACK_TYPE_JOINT (skeltrack_joint_get_type ()) -#define SKELTRACK_JOINT_MAX_JOINTS 7 - -typedef struct _SkeltrackJoint SkeltrackJoint; -typedef SkeltrackJoint **SkeltrackJointList; - -/** - * SkeltrackJointId: - * @SKELTRACK_JOINT_ID_HEAD: The head - * @SKELTRACK_JOINT_ID_LEFT_SHOULDER: The left shoulder - * @SKELTRACK_JOINT_ID_RIGHT_SHOULDER: The right shoulder - * @SKELTRACK_JOINT_ID_LEFT_ELBOW: The left elbow - * @SKELTRACK_JOINT_ID_RIGHT_ELBOW: The right elbow - * @SKELTRACK_JOINT_ID_LEFT_HAND: The left hand - * @SKELTRACK_JOINT_ID_RIGHT_HAND: The right hand - * - * Available joint ids. - **/ -typedef enum { - SKELTRACK_JOINT_ID_HEAD, - SKELTRACK_JOINT_ID_LEFT_SHOULDER, - SKELTRACK_JOINT_ID_RIGHT_SHOULDER, - SKELTRACK_JOINT_ID_LEFT_ELBOW, - SKELTRACK_JOINT_ID_RIGHT_ELBOW, - SKELTRACK_JOINT_ID_LEFT_HAND, - SKELTRACK_JOINT_ID_RIGHT_HAND -} SkeltrackJointId; - -/** - * SkeltrackJoint: - * @id: The id of the joint - * @x: The x coordinate of the joint in the space (in mm) - * @y: The y coordinate of the joint in the space (in mm) - * @z: The z coordinate of the joint in the space (in mm) - * @screen_x: The x coordinate of the joint in the screen (in pixels) - * @screen_y: The y coordinate of the joint in the screen (in pixels) - **/ -struct _SkeltrackJoint -{ - SkeltrackJointId id; - - gint x; - gint y; - gint z; - - gint screen_x; - gint screen_y; -}; - -GType skeltrack_joint_get_type (void); -gpointer skeltrack_joint_copy (SkeltrackJoint *joint); -void skeltrack_joint_free (SkeltrackJoint *joint); -void skeltrack_joint_list_free (SkeltrackJointList list); -SkeltrackJointList skeltrack_joint_list_new (void); -SkeltrackJoint * skeltrack_joint_list_get_joint (SkeltrackJointList list, - SkeltrackJointId id); - -G_END_DECLS - -#endif /* __SKELTRACK_JOINT_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-skeleton.h b/interface/external/skeltrack/include/skeltrack-skeleton.h deleted file mode 100644 index 11e4344025..0000000000 --- a/interface/external/skeltrack/include/skeltrack-skeleton.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * skeltrack.h - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 3, or (at your option) any later version as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#ifndef __SKELTRACK_SKELETON_H__ -#define __SKELTRACK_SKELETON_H__ - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define SKELTRACK_TYPE_SKELETON (skeltrack_skeleton_get_type ()) -#define SKELTRACK_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeleton)) -#define SKELTRACK_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) -#define SKELTRACK_IS_SKELETON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SKELTRACK_TYPE_SKELETON)) -#define SKELTRACK_IS_SKELETON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SKELTRACK_TYPE_SKELETON)) -#define SKELTRACK_SKELETON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SKELTRACK_TYPE_SKELETON, SkeltrackSkeletonClass)) - -typedef struct _SkeltrackSkeleton SkeltrackSkeleton; -typedef struct _SkeltrackSkeletonClass SkeltrackSkeletonClass; -typedef struct _SkeltrackSkeletonPrivate SkeltrackSkeletonPrivate; - -struct _SkeltrackSkeleton -{ - GObject parent; - - /*< private >*/ - SkeltrackSkeletonPrivate *priv; -}; - -/** - * SkeltrackSkeletonClass: - **/ -struct _SkeltrackSkeletonClass -{ - GObjectClass parent_class; -}; - -GType skeltrack_skeleton_get_type (void) G_GNUC_CONST; - -SkeltrackSkeleton * skeltrack_skeleton_new (void); - -void skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, - guint16 *buffer, - guint width, - guint height, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -SkeltrackJointList skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, - GAsyncResult *result, - GError **error); - -SkeltrackJointList skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, - guint16 *buffer, - guint width, - guint height, - GCancellable *cancellable, - GError **error); - -void skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, - gint *x, - gint *y, - gint *z); - -void skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, - gint x, - gint y, - gint z); - -G_END_DECLS - -#endif /* __SKELTRACK_SKELETON_H__ */ diff --git a/interface/external/skeltrack/include/skeltrack-smooth.h b/interface/external/skeltrack/include/skeltrack-smooth.h deleted file mode 100644 index ea99bfc7ae..0000000000 --- a/interface/external/skeltrack/include/skeltrack-smooth.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * skeltrack-smooth.h - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#include "skeltrack-joint.h" - -typedef struct { - SkeltrackJointList smoothed_joints; - SkeltrackJointList trend_joints; - guint joints_persistency; - gfloat smoothing_factor; - guint joints_persistency_counter[SKELTRACK_JOINT_MAX_JOINTS]; -} SmoothData; - -void reset_joints_persistency_counter (SmoothData *smooth_data); - -void smooth_joints (SmoothData *data, - SkeltrackJointList new_joints); diff --git a/interface/external/skeltrack/include/skeltrack-util.h b/interface/external/skeltrack/include/skeltrack-util.h deleted file mode 100644 index 540aee7f33..0000000000 --- a/interface/external/skeltrack/include/skeltrack-util.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * skeltrack-util.h - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#include -#include "skeltrack-joint.h" - -typedef struct _Label Label; -typedef struct _Node Node; - -struct _Label { - gint index; - Label *parent; - GList *nodes; - Node *bridge_node; - Node *to_node; - gint lower_screen_y; - gint higher_z; - gint lower_z; - gdouble normalized_num_nodes; -}; - -struct _Node { - gint i; - gint j; - gint x; - gint y; - gint z; - GList *neighbors; - GList *linked_nodes; - Label *label; -}; - -Node * get_closest_node_to_joint (GList *extremas, - SkeltrackJoint *joint, - gint *distance); - -Node * get_closest_node (GList *node_list, Node *from); - -Node * get_closest_torso_node (GList *node_list, - Node *from, - Node *head); - -Label * get_main_component (GList *node_list, - Node *from, - gdouble min_normalized_nr_nodes); - -Label * label_find (Label *label); - -void label_union (Label *a, Label *b); - -gint get_distance (Node *a, Node *b); - -void free_label (Label *label); - -void clean_labels (GList *labels); - -void free_node (Node *node, - gboolean unlink_node_first); - -void clean_nodes (GList *nodes); - -GList * remove_nodes_with_label (GList *nodes, - Node **node_matrix, - gint width, - Label *label); - -Label * get_lowest_index_label (Label **neighbor_labels); - -Label * new_label (gint index); - -void join_components_to_main (GList *nodes, - Label *lowest_component_label, - guint horizontal_max_distance, - guint depth_max_distance, - guint graph_distance_threshold); - -void set_joint_from_node (SkeltrackJointList *joints, - Node *node, - SkeltrackJointId id, - gint dimension_reduction); - -gint * create_new_dist_matrix (gint matrix_size); - -gboolean dijkstra_to (GList *nodes, - Node *source, - Node *target, - gint width, - gint height, - gint *distances, - Node **previous); - -void convert_screen_coords_to_mm (guint width, - guint height, - guint dimension_reduction, - guint i, - guint j, - gint z, - gint *x, - gint *y); - -void convert_mm_to_screen_coords (guint width, - guint height, - guint dimension_reduction, - gint x, - gint y, - gint z, - guint *i, - guint *j); diff --git a/interface/external/skeltrack/include/skeltrack.h b/interface/external/skeltrack/include/skeltrack.h deleted file mode 100644 index 8e1c572303..0000000000 --- a/interface/external/skeltrack/include/skeltrack.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * skeltrack.h - * - * skeltrack - A GObject wrapper of the libfreenect library - * Copyright (C) 2011 Igalia S.L. - * - * Authors: - * Joaquim Manuel Pereira Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * version 3, or (at your option) any later version as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#ifndef __SKELTRACK_H__ -#define __SKELTRACK_H__ - -#include - -#endif /* __SKELTRACK_H__ */ diff --git a/interface/external/skeltrack/lib/MacOS/libskeltrack.a b/interface/external/skeltrack/lib/MacOS/libskeltrack.a deleted file mode 100644 index 5a7a393c49a17978cc1ed5a750b1e3271c883b07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52416 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 IL5LyxKLPpTk^lez diff --git a/interface/external/skeltrack/lib/UNIX/libskeltrack.a b/interface/external/skeltrack/lib/UNIX/libskeltrack.a deleted file mode 100644 index f34c83224bfaeed07d09a6a02e47e802ba075ea5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53760 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 diff --git a/interface/external/skeltrack/src/skeltrack-joint.c b/interface/external/skeltrack/src/skeltrack-joint.c deleted file mode 100644 index 6310c6a789..0000000000 --- a/interface/external/skeltrack/src/skeltrack-joint.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * skeltrack-joint.c - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -/** - * SECTION:skeltrack-joint - * @short_description: Data structure that holds information about - * a skeleton joint. - * - * A #SkeltrackJoint is built automatically by #SkeltrackSkeleton when - * it finds a skeleton joint and can be used to get information about it. - * Each #SkeltrackJoint holds an id, given by #SkeltrackJointId that indicates - * which of the human skeleton joints it represents. - * - * Spacial information about a joint is given by the @x, @y and @z coordinates. - * To represent the joint in a 2D, the variables @screen_x and - * @screen_y will indicate the joint's position in the screen and are calculated - * taking into account the #SkeltrackSkeleton:dimension-reduction (it will - * be multiplied by this value). - * - * The tracked list of joints is represented by #SkeltrackJointList and given - * by skeltrack_skeleton_track_joints_finish(). - * To get a #SkeltrackJoint from a #SkeltrackJointList object, use the - * skeltrack_joint_list_get_joint() indicating the needed #SkeltrackJointId. - * - * A #SkeltrackJointList can be freed by using skeltrack_joint_list_free(). - * A #SkeltrackJoint can be copied by skeltrack_joint_copy() and freed by - * skeltrack_joint_free(). - **/ - -#include -#include "skeltrack-joint.h" - -/** - * skeltrack_joint_get_type: - * - * Returns: The registered #GType for #SkeltrackJoint boxed type - **/ -GType -skeltrack_joint_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) - type = g_boxed_type_register_static ("SkeltrackJoint", - (GBoxedCopyFunc) skeltrack_joint_copy, - (GBoxedFreeFunc) skeltrack_joint_free); - return type; -} - -/** - * skeltrack_joint_copy: - * @joint: The #SkeltrackJoint to copy - * - * Makes an exact copy of a #SkeltrackJoint object. - * - * Returns: (transfer full): A newly created #SkeltrackJoint. Use - * skeltrack_joint_free() to free it. - **/ -gpointer -skeltrack_joint_copy (SkeltrackJoint *joint) -{ - SkeltrackJoint *new_joint; - - if (joint == NULL) - return NULL; - - new_joint = g_slice_new0 (SkeltrackJoint); - memcpy (new_joint, joint, sizeof (SkeltrackJoint)); - - return new_joint; -} - -/** - * skeltrack_joint_free: - * @joint: The #SkeltrackJoint to free - * - * Frees a #SkeltrackJoint object. - **/ -void -skeltrack_joint_free (SkeltrackJoint *joint) -{ - g_slice_free (SkeltrackJoint, joint); -} - - -/** - * skeltrack_joint_list_free: - * @list: The #SkeltrackJointList to free - * - * Frees a #SkeltrackJointList object and each #SkeltrackJoint - * in it. - **/ -void -skeltrack_joint_list_free (SkeltrackJointList list) -{ - gint i; - - if (list == NULL) - return; - - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - g_slice_free (SkeltrackJoint, list[i]); - } - g_slice_free1 (SKELTRACK_JOINT_MAX_JOINTS * sizeof (SkeltrackJoint *), list); -} - -/** - * skeltrack_joint_list_get_joint: - * @list: The #SkeltrackJointList - * @id: The #SkeltrackJointId of the joint to get - * - * Gets a joint from a list of skeleton joints. The joint - * returned needs to be freed by using skeltrack_joint_free() or, - * alternatively, the whole list and its joints can be freed by using - * skeltrack_joint_list_free(). - * - * Returns: (transfer full): The #SkeltrackJoint that corresponds to - * the given @id or %NULL if that joint wasn't found. - **/ -SkeltrackJoint * -skeltrack_joint_list_get_joint (SkeltrackJointList list, SkeltrackJointId id) -{ - return list[id]; -} - -/** - * skeltrack_joint_list_new: - * - * Created a new list of #SkeltrackJointsList with its joints as #NULL. - * When it is no longer needed, free it with skeltrack_joint_list_free(). - * - * Returns: (transfer full): A newly allocated #SkeltrackJointList - **/ -SkeltrackJointList -skeltrack_joint_list_new (void) -{ - return (SkeltrackJointList) g_slice_alloc0 (SKELTRACK_JOINT_MAX_JOINTS * - sizeof (SkeltrackJoint *)); -} diff --git a/interface/external/skeltrack/src/skeltrack-skeleton.c b/interface/external/skeltrack/src/skeltrack-skeleton.c deleted file mode 100644 index 9373af56a8..0000000000 --- a/interface/external/skeltrack/src/skeltrack-skeleton.c +++ /dev/null @@ -1,2027 +0,0 @@ -/* - * skeltrack-skeleton.c - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -/** - * SECTION:skeltrack-skeleton - * @short_description: Object that tracks the joints in a human skeleton - * - * This object tries to detect joints of the human skeleton. - * - * To track the joints, first create an instance of #SkeltrackSkeleton using - * skeltrack_skeleton_new() and then set a buffer from where the joints will - * be retrieved using the asynchronous function - * skeltrack_skeleton_track_joints() and get the list of joints using - * skeltrack_skeleton_track_joints_finish(). - * - * A common use case is to use this library together with a Kinect device so - * an easy way to retrieve the needed buffer is to use the GFreenect library. - * - * It currently tracks the joints identified by #SkeltrackJointId . - * - * Tracking the skeleton joints can be computational heavy so it is advised that - * the given buffer's dimension is reduced before setting it. To do it, - * simply choose the reduction factor and loop through the original buffer - * (using this factor as a step) and set the reduced buffer's values accordingly. - * The #SkeltrackSkeleton:dimension-reduction property holds this reduction - * value and should be changed to the reduction factor used (alternatively you - * can retrieve its default value and use it in the reduction, if it fits your - * needs). - * - * The skeleton tracking uses a few heuristics that proved to work well for - * tested cases but they can be tweaked by changing the following properties: - * #SkeltrackSkeleton:graph-distance-threshold , - * #SkeltrackSkeleton:graph-minimum-number-nodes , - * #SkeltrackSkeleton:hands-minimum-distance , - * #SkeltrackSkeleton:shoulders-arc-start-point , - * #SkeltrackSkeleton:shoulders-arc-length , - * #SkeltrackSkeleton:shoulders-circumference-radius , - * #SkeltrackSkeleton:shoulders-search-step . - **/ -#include -#include -#include - -#include "skeltrack-skeleton.h" -#include "skeltrack-smooth.h" -#include "skeltrack-util.h" - -#define SKELTRACK_SKELETON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ - SKELTRACK_TYPE_SKELETON, \ - SkeltrackSkeletonPrivate)) - -#define DIMENSION_REDUCTION 16 -#define GRAPH_DISTANCE_THRESHOLD 150 -#define GRAPH_MINIMUM_NUMBER_OF_NODES 5 -#define HANDS_MINIMUM_DISTANCE 550 -#define SHOULDERS_CIRCUMFERENCE_RADIUS 300 -#define SHOULDERS_ARC_START_POINT 100 -#define SHOULDERS_ARC_LENGTH 250 -#define SHOULDERS_SEARCH_STEP 0.05 -#define JOINTS_PERSISTENCY_DEFAULT 3 -#define SMOOTHING_FACTOR_DEFAULT .5 -#define ENABLE_SMOOTHING_DEFAULT TRUE -#define DEFAULT_FOCUS_POINT_Z 1000 -#define TORSO_MINIMUM_NUMBER_NODES_DEFAULT 16.0 -#define EXTREMA_SPHERE_RADIUS 300 - -/* private data */ -struct _SkeltrackSkeletonPrivate -{ - guint16 *buffer; - guint buffer_width; - guint buffer_height; - - GAsyncResult *track_joints_result; - GMutex track_joints_mutex; - - GList *graph; - GList *labels; - Node **node_matrix; - gint *distances_matrix; - GList *main_component; - - guint16 dimension_reduction; - guint16 distance_threshold; - guint16 min_nr_nodes; - - guint16 hands_minimum_distance; - - guint16 shoulders_circumference_radius; - guint16 shoulders_arc_start_point; - guint16 shoulders_arc_length; - gfloat shoulders_search_step; - - guint16 extrema_sphere_radius; - - Node *focus_node; - - gboolean enable_smoothing; - SmoothData smooth_data; - - gfloat torso_minimum_number_nodes; - - SkeltrackJoint *previous_head; -}; - -/* Currently searches for head and hands */ -static const guint NR_EXTREMAS_TO_SEARCH = 3; - -/* properties */ -enum - { - PROP_0, - PROP_DIMENSION_REDUCTION, - PROP_GRAPH_DISTANCE_THRESHOLD, - PROP_GRAPH_MIN_NR_NODES, - PROP_HANDS_MINIMUM_DISTANCE, - PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, - PROP_SHOULDERS_ARC_START_POINT, - PROP_SHOULDERS_ARC_LENGTH, - PROP_SHOULDERS_SEARCH_STEP, - PROP_EXTREMA_SPHERE_RADIUS, - PROP_SMOOTHING_FACTOR, - PROP_JOINTS_PERSISTENCY, - PROP_ENABLE_SMOOTHING, - PROP_TORSO_MINIMUM_NUMBER_NODES - }; - - -static void skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class); -static void skeltrack_skeleton_init (SkeltrackSkeleton *self); -static void skeltrack_skeleton_finalize (GObject *obj); -static void skeltrack_skeleton_dispose (GObject *obj); - -static void skeltrack_skeleton_set_property (GObject *obj, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void skeltrack_skeleton_get_property (GObject *obj, - guint prop_id, - GValue *value, - GParamSpec *pspec); - - -static void clean_tracking_resources (SkeltrackSkeleton *self); - -G_DEFINE_TYPE (SkeltrackSkeleton, skeltrack_skeleton, G_TYPE_OBJECT) - -static void -skeltrack_skeleton_class_init (SkeltrackSkeletonClass *class) -{ - GObjectClass *obj_class; - - obj_class = G_OBJECT_CLASS (class); - - obj_class->dispose = skeltrack_skeleton_dispose; - obj_class->finalize = skeltrack_skeleton_finalize; - obj_class->get_property = skeltrack_skeleton_get_property; - obj_class->set_property = skeltrack_skeleton_set_property; - - /* install properties */ - - /** - * SkeltrackSkeleton:dimension-reduction - * - * The value by which the dimension of the buffer was reduced - * (in case it was). - **/ - g_object_class_install_property (obj_class, - PROP_DIMENSION_REDUCTION, - g_param_spec_uint ("dimension-reduction", - "Dimension reduction", - "The dimension reduction value", - 1, - 1024, - DIMENSION_REDUCTION, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:graph-distance-threshold - * - * The value (in mm) for the distance threshold between each node and its - * neighbors. This means that a node in the graph will only be connected - * to another if they aren't farther apart then this value. - **/ - g_object_class_install_property (obj_class, - PROP_GRAPH_DISTANCE_THRESHOLD, - g_param_spec_uint ("graph-distance-threshold", - "Graph's distance threshold", - "The distance threshold between " - "each node.", - 1, - G_MAXUINT16, - GRAPH_DISTANCE_THRESHOLD, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:graph-minimum-number-nodes - * - * The minimum number of nodes each of the graph's components - * should have (when it is not fully connected). - **/ - g_object_class_install_property (obj_class, - PROP_GRAPH_MIN_NR_NODES, - g_param_spec_uint ("graph-minimum-number-nodes", - "Graph's minimum number of nodes", - "The minimum number of nodes " - "of the graph's components ", - 1, - G_MAXUINT16, - GRAPH_MINIMUM_NUMBER_OF_NODES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:hands-minimum-distance - * - * The minimum distance (in mm) that each hand should be from its - * respective shoulder. - **/ - g_object_class_install_property (obj_class, - PROP_HANDS_MINIMUM_DISTANCE, - g_param_spec_uint ("hands-minimum-distance", - "Hands' minimum distance from the " - "shoulders", - "The minimum distance (in mm) that " - "each hand should be from its " - "respective shoulder.", - 300, - G_MAXUINT, - HANDS_MINIMUM_DISTANCE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:shoulders-circumference-radius - * - * The radius of the circumference (in mm) from the head with which - * to look for the shoulders. - **/ - g_object_class_install_property (obj_class, - PROP_SHOULDERS_CIRCUMFERENCE_RADIUS, - g_param_spec_uint ("shoulders-circumference-radius", - "Shoulders' circumference radius", - "The radius of the circumference " - "(in mm) from the head with which " - "to look for the shoulders.", - 1, - G_MAXUINT16, - SHOULDERS_CIRCUMFERENCE_RADIUS, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:shoulders-arc-start-point - * - * The starting point (in mm) of the arc (from the bottom of the - * shoulders' circumference) where the shoulders will be searched for. - * This point is used together with the - * SkeltrackSkeleton::shoulders-arc-length to determine the arc - * where the shoulders' points will be looked for. - **/ - g_object_class_install_property (obj_class, - PROP_SHOULDERS_ARC_START_POINT, - g_param_spec_uint ("shoulders-arc-start-point", - "Shoulders' arc start point", - "The starting point (in mm) of the " - "arc from the bottom of the " - "shoulders' circumference where " - "the shoulders will be searched for.", - 1, - G_MAXUINT16, - SHOULDERS_ARC_START_POINT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:shoulders-arc-length - * - * The length (in mm) of the arc where the shoulders will be searched. - * This length is used together with the - * SkeltrackSkeleton::shoulders-arc-start-point to determine the arc - * where the shoulders' points will be looked for. - **/ - g_object_class_install_property (obj_class, - PROP_SHOULDERS_ARC_LENGTH, - g_param_spec_uint ("shoulders-arc-length", - "Shoulders' arc length", - "The length (in mm) of the arc " - "where the shoulders will be " - "searched.", - 1, - G_MAXUINT16, - SHOULDERS_ARC_LENGTH, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - - /** - * SkeltrackSkeleton:shoulders-search-step - * - * The step considered for sampling the shoulders' circumference - * when searching for the shoulders. - **/ - g_object_class_install_property (obj_class, - PROP_SHOULDERS_SEARCH_STEP, - g_param_spec_float ("shoulders-search-step", - "Shoulders' search step", - "The step considered for sampling " - "the shoulders' circumference " - "when searching for the shoulders.", - .01, - M_PI, - .01, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:smoothing-factor - * - * The factor by which the joints should be smoothed. This refers to - * Holt's Double Exponential Smoothing and determines how the current and - * previous data and trend will be used. A value closer to 0 will produce smoother - * results but increases latency. - **/ - g_object_class_install_property (obj_class, - PROP_SMOOTHING_FACTOR, - g_param_spec_float ("smoothing-factor", - "Smoothing factor", - "The factor by which the joints values" - "should be smoothed.", - .0, - 1.0, - .5, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:joints-persistency - * - * The number of times that a joint can be null until its previous - * value is discarded. For example, if this property is 3, the last value for - * a joint will keep being used until the new value for this joint is null for - * 3 consecutive times. - * - **/ - g_object_class_install_property (obj_class, - PROP_JOINTS_PERSISTENCY, - g_param_spec_uint ("joints-persistency", - "Joints persistency", - "The number of times that a joint " - "can be null until its previous " - "value is discarded", - 0, - G_MAXUINT16, - JOINTS_PERSISTENCY_DEFAULT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:enable-smoothing - * - * Whether smoothing the joints should be applied or not. - * - **/ - g_object_class_install_property (obj_class, - PROP_ENABLE_SMOOTHING, - g_param_spec_boolean ("enable-smoothing", - "Enable smoothing", - "Whether smoothing should be " - "applied or not", - ENABLE_SMOOTHING_DEFAULT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:torso-minimum-number-nodes - * - * Minimum number of nodes for a component to be considered torso. - * - **/ - g_object_class_install_property (obj_class, - PROP_TORSO_MINIMUM_NUMBER_NODES, - g_param_spec_float ("torso-minimum-number-nodes", - "Torso minimum number of nodes", - "Minimum number of nodes for a " - "component to be considered " - "torso", - 0, - G_MAXUINT16, - TORSO_MINIMUM_NUMBER_NODES_DEFAULT, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - /** - * SkeltrackSkeleton:extrema-sphere-radius - * - * The radius of the sphere around the extremas (in mm). - * - * Points inside this sphere are considered for calculating the average position - * of the extrema. If the value is 0, no averaging is done. - **/ - g_object_class_install_property (obj_class, - PROP_EXTREMA_SPHERE_RADIUS, - g_param_spec_uint ("extrema-sphere-radius", - "Extrema sphere radius", - "The radius of the sphere around " - "the extremas (in mm).", - 0, - G_MAXUINT16, - EXTREMA_SPHERE_RADIUS, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - - /* add private structure */ - g_type_class_add_private (obj_class, sizeof (SkeltrackSkeletonPrivate)); -} - -static void -skeltrack_skeleton_init (SkeltrackSkeleton *self) -{ - guint i; - SkeltrackSkeletonPrivate *priv; - - priv = SKELTRACK_SKELETON_GET_PRIVATE (self); - self->priv = priv; - - priv->buffer = NULL; - priv->buffer_width = 0; - priv->buffer_height = 0; - - priv->graph = NULL; - priv->labels = NULL; - priv->main_component = NULL; - priv->node_matrix = NULL; - priv->distances_matrix = NULL; - - priv->dimension_reduction = DIMENSION_REDUCTION; - priv->distance_threshold = GRAPH_DISTANCE_THRESHOLD; - - priv->min_nr_nodes = GRAPH_MINIMUM_NUMBER_OF_NODES; - - priv->hands_minimum_distance = HANDS_MINIMUM_DISTANCE; - - priv->shoulders_circumference_radius = SHOULDERS_CIRCUMFERENCE_RADIUS; - priv->shoulders_arc_start_point = SHOULDERS_ARC_START_POINT; - priv->shoulders_arc_length = SHOULDERS_ARC_LENGTH; - priv->shoulders_search_step = SHOULDERS_SEARCH_STEP; - - priv->extrema_sphere_radius = EXTREMA_SPHERE_RADIUS; - - priv->focus_node = g_slice_new0 (Node); - priv->focus_node->x = 0; - priv->focus_node->y = 0; - priv->focus_node->z = DEFAULT_FOCUS_POINT_Z; - - priv->track_joints_result = NULL; - - g_mutex_init (&priv->track_joints_mutex); - - priv->enable_smoothing = ENABLE_SMOOTHING_DEFAULT; - priv->smooth_data.smoothing_factor = SMOOTHING_FACTOR_DEFAULT; - priv->smooth_data.smoothed_joints = NULL; - priv->smooth_data.trend_joints = NULL; - priv->smooth_data.joints_persistency = JOINTS_PERSISTENCY_DEFAULT; - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - priv->smooth_data.joints_persistency_counter[i] = JOINTS_PERSISTENCY_DEFAULT; - - priv->torso_minimum_number_nodes = TORSO_MINIMUM_NUMBER_NODES_DEFAULT; - - priv->previous_head = NULL; -} - -static void -skeltrack_skeleton_dispose (GObject *obj) -{ - /* TODO: cancel any cancellable to interrupt joints tracking operation */ - - G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->dispose (obj); -} - -static void -skeltrack_skeleton_finalize (GObject *obj) -{ - SkeltrackSkeleton *self = SKELTRACK_SKELETON (obj); - - g_mutex_clear (&self->priv->track_joints_mutex); - - skeltrack_joint_list_free (self->priv->smooth_data.smoothed_joints); - skeltrack_joint_list_free (self->priv->smooth_data.trend_joints); - - skeltrack_joint_free (self->priv->previous_head); - - clean_tracking_resources (self); - - g_slice_free (Node, self->priv->focus_node); - - G_OBJECT_CLASS (skeltrack_skeleton_parent_class)->finalize (obj); -} - -static void -skeltrack_skeleton_set_property (GObject *obj, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SkeltrackSkeleton *self; - - self = SKELTRACK_SKELETON (obj); - - switch (prop_id) - { - case PROP_DIMENSION_REDUCTION: - self->priv->dimension_reduction = g_value_get_uint (value); - break; - - case PROP_GRAPH_DISTANCE_THRESHOLD: - self->priv->distance_threshold = g_value_get_uint (value); - break; - - case PROP_GRAPH_MIN_NR_NODES: - self->priv->min_nr_nodes = g_value_get_uint (value); - break; - - case PROP_HANDS_MINIMUM_DISTANCE: - self->priv->hands_minimum_distance = g_value_get_uint (value); - break; - - case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: - self->priv->shoulders_circumference_radius = g_value_get_uint (value); - break; - - case PROP_SHOULDERS_ARC_START_POINT: - self->priv->shoulders_arc_start_point = g_value_get_uint (value); - break; - - case PROP_SHOULDERS_ARC_LENGTH: - self->priv->shoulders_arc_length = g_value_get_uint (value); - break; - - case PROP_SHOULDERS_SEARCH_STEP: - self->priv->shoulders_circumference_radius = g_value_get_float (value); - break; - - case PROP_EXTREMA_SPHERE_RADIUS: - self->priv->extrema_sphere_radius = g_value_get_uint (value); - break; - - case PROP_SMOOTHING_FACTOR: - self->priv->smooth_data.smoothing_factor = g_value_get_float (value); - break; - - case PROP_JOINTS_PERSISTENCY: - self->priv->smooth_data.joints_persistency = g_value_get_uint (value); - reset_joints_persistency_counter (&self->priv->smooth_data); - break; - - case PROP_ENABLE_SMOOTHING: - self->priv->enable_smoothing = g_value_get_boolean (value); - break; - - case PROP_TORSO_MINIMUM_NUMBER_NODES: - self->priv->torso_minimum_number_nodes = g_value_get_float (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); - break; - } -} - -static void -skeltrack_skeleton_get_property (GObject *obj, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SkeltrackSkeleton *self; - - self = SKELTRACK_SKELETON (obj); - - switch (prop_id) - { - case PROP_DIMENSION_REDUCTION: - g_value_set_uint (value, self->priv->dimension_reduction); - break; - - case PROP_GRAPH_DISTANCE_THRESHOLD: - g_value_set_uint (value, self->priv->distance_threshold); - break; - - case PROP_GRAPH_MIN_NR_NODES: - g_value_set_uint (value, self->priv->min_nr_nodes); - break; - - case PROP_HANDS_MINIMUM_DISTANCE: - g_value_set_uint (value, self->priv->hands_minimum_distance); - break; - - case PROP_SHOULDERS_CIRCUMFERENCE_RADIUS: - g_value_set_uint (value, self->priv->shoulders_circumference_radius); - break; - - case PROP_SHOULDERS_ARC_START_POINT: - g_value_set_uint (value, self->priv->shoulders_arc_start_point); - break; - - case PROP_SHOULDERS_ARC_LENGTH: - g_value_set_uint (value, self->priv->shoulders_arc_length); - break; - - case PROP_SHOULDERS_SEARCH_STEP: - g_value_set_float (value, self->priv->shoulders_search_step); - break; - - case PROP_EXTREMA_SPHERE_RADIUS: - g_value_set_uint (value, self->priv->extrema_sphere_radius); - break; - - case PROP_SMOOTHING_FACTOR: - g_value_set_float (value, self->priv->smooth_data.smoothing_factor); - break; - - case PROP_JOINTS_PERSISTENCY: - g_value_set_uint (value, self->priv->smooth_data.joints_persistency); - break; - - case PROP_ENABLE_SMOOTHING: - g_value_set_boolean (value, self->priv->enable_smoothing); - break; - - case PROP_TORSO_MINIMUM_NUMBER_NODES: - g_value_set_float (value, self->priv->torso_minimum_number_nodes); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); - break; - } -} - -static gint -join_neighbor (SkeltrackSkeleton *self, - Node *node, - Label **neighbor_labels, - gint index, - gint i, - gint j) -{ - Node *neighbor; - if (i < 0 || i >= self->priv->buffer_width || - j < 0 || j >= self->priv->buffer_height) - { - return index; - } - - neighbor = self->priv->node_matrix[self->priv->buffer_width * j + i]; - if (neighbor != NULL) - { - gint distance; - distance = get_distance (neighbor, node); - if (distance < self->priv->distance_threshold) - { - neighbor->neighbors = g_list_append (neighbor->neighbors, - node); - node->neighbors = g_list_append (node->neighbors, - neighbor); - neighbor_labels[index] = neighbor->label; - index++; - } - } - return index; -} - -GList * -make_graph (SkeltrackSkeleton *self, GList **label_list) -{ - SkeltrackSkeletonPrivate *priv; - gint i, j, n; - Node *node; - GList *nodes = NULL; - GList *labels = NULL; - GList *current_label; - Label *main_component_label = NULL; - gint index = 0; - gint next_label = -1; - guint16 value; - guint16 *buffer; - gint width, height; - - buffer = self->priv->buffer; - width = self->priv->buffer_width; - height = self->priv->buffer_height; - - priv = self->priv; - - - if (priv->node_matrix == NULL) - { - priv->node_matrix = g_slice_alloc0 (width * height * sizeof (Node *)); - } - else - { - memset (self->priv->node_matrix, - 0, - width * height * sizeof (Node *)); - } - - for (i = 0; i < width; i++) - { - for (j = 0; j < height; j++) - { - gint south, north, west; - Label *lowest_index_label = NULL; - Label *neighbor_labels[4] = {NULL, NULL, NULL, NULL}; - - value = buffer[j * width + i]; - if (value == 0) - continue; - - node = g_slice_new0 (Node); - node->i = i; - node->j = j; - node->z = value; - convert_screen_coords_to_mm (self->priv->buffer_width, - self->priv->buffer_height, - self->priv->dimension_reduction, - i, j, - node->z, - &(node->x), - &(node->y)); - node->neighbors = NULL; - node->linked_nodes = NULL; - - index = 0; - - south = j + 1; - north = j - 1; - west = i - 1; - - /* West */ - index = join_neighbor (self, - node, - neighbor_labels, - index, - west, j); - /* South West*/ - index = join_neighbor (self, - node, - neighbor_labels, - index, - west, south); - /* North */ - index = join_neighbor (self, - node, - neighbor_labels, - index, - i, north); - - /* North West */ - index = join_neighbor (self, - node, - neighbor_labels, - index, - west, north); - - lowest_index_label = get_lowest_index_label (neighbor_labels); - - /* No neighbors */ - if (lowest_index_label == NULL) - { - Label *label; - next_label++; - label = new_label (next_label); - labels = g_list_append (labels, label); - lowest_index_label = label; - } - else - { - for (index = 0; index < 4; index++) - { - if (neighbor_labels[index] != NULL) - { - label_union (neighbor_labels[index], lowest_index_label); - } - } - } - - node->label = lowest_index_label; - nodes = g_list_append(nodes, node); - priv->node_matrix[width * node->j + node->i] = node; - } - } - - for (n = 0; n < g_list_length (nodes); n++) - { - Node *node = (Node *) g_list_nth_data (nodes, n); - node->label = label_find (node->label); - node->label->nodes = g_list_append (node->label->nodes, - node); - - /* Assign lower node so we can extract the - lower graph's component */ - if (node->label->lower_screen_y == -1 || - node->j > node->label->lower_screen_y) - { - node->label->lower_screen_y = node->j; - } - - /* Assign farther to the camera node so we - can extract the main graph component */ - if (node->label->higher_z == -1 || - node->z > node->label->higher_z) - { - node->label->higher_z = node->z; - } - - /* Assign closer to the camera node so we - can extract the main graph component */ - if (node->label->lower_z == -1 || - node->z < node->label->lower_z) - { - node->label->lower_z = node->z; - } - } - - for (current_label = g_list_first (labels); - current_label != NULL; - current_label = g_list_next (current_label)) - { - Label *label; - GList *current_nodes; - - label = (Label *) current_label->data; - current_nodes = label->nodes; - - label->normalized_num_nodes = g_list_length (current_nodes) * - ((label->higher_z - label->lower_z)/2 + - label->lower_z) * - (pow (DIMENSION_REDUCTION, 2)/2) / - 1000000; - } - - main_component_label = get_main_component (nodes, - priv->focus_node, - priv->torso_minimum_number_nodes); - - current_label = g_list_first (labels); - while (current_label != NULL) - { - Label *label; - label = (Label *) current_label->data; - - /* Remove label if number of nodes is less than - the minimum required */ - if (g_list_length (label->nodes) < priv->min_nr_nodes) - { - nodes = remove_nodes_with_label (nodes, - priv->node_matrix, - priv->buffer_width, - label); - - GList *link = current_label; - current_label = g_list_next (current_label); - labels = g_list_delete_link (labels, link); - free_label (label); - continue; - } - - current_label = g_list_next (current_label); - } - - if (main_component_label) - { - join_components_to_main (labels, - main_component_label, - priv->distance_threshold, - priv->hands_minimum_distance, - priv->distance_threshold); - - current_label = g_list_first (labels); - while (current_label != NULL) - { - Label *label; - label = (Label *) current_label->data; - if (label == main_component_label) - { - current_label = g_list_next (current_label); - continue; - } - - if (label->bridge_node == NULL) - { - nodes = remove_nodes_with_label (nodes, - priv->node_matrix, - priv->buffer_width, - label); - - GList *link = current_label; - current_label = g_list_next (current_label); - labels = g_list_delete_link (labels, link); - free_label (label); - continue; - } - - label->bridge_node->neighbors = - g_list_append (label->bridge_node->neighbors, label->to_node); - label->to_node->neighbors = g_list_append (label->to_node->neighbors, - label->bridge_node); - - current_label = g_list_next (current_label); - } - - priv->main_component = main_component_label->nodes; - } - - *label_list = labels; - - return nodes; -} - -Node * -get_centroid (SkeltrackSkeleton *self) -{ - gint avg_x = 0; - gint avg_y = 0; - gint avg_z = 0; - gint length; - GList *node_list; - Node *cent = NULL; - Node *centroid = NULL; - - if (self->priv->main_component == NULL) - return NULL; - - for (node_list = g_list_first (self->priv->main_component); - node_list != NULL; - node_list = g_list_next (node_list)) - { - Node *node; - node = (Node *) node_list->data; - avg_x += node->x; - avg_y += node->y; - avg_z += node->z; - } - - length = g_list_length (self->priv->main_component); - cent = g_slice_new0 (Node); - cent->x = avg_x / length; - cent->y = avg_y / length; - cent->z = avg_z / length; - cent->linked_nodes = NULL; - - centroid = get_closest_node (self->priv->graph, cent); - - g_slice_free (Node, cent); - - return centroid; -} - -static Node * -get_lowest (SkeltrackSkeleton *self, Node *centroid) -{ - Node *lowest = NULL; - /* @TODO: Use the node_matrix instead of the lowest - component to look for the lowest node as it's faster. */ - if (self->priv->main_component != NULL) - { - GList *node_list; - for (node_list = g_list_first (self->priv->main_component); - node_list != NULL; - node_list = g_list_next (node_list)) - { - Node *node; - node = (Node *) node_list->data; - if (node->i != centroid->i) - continue; - if (lowest == NULL || - lowest->j < node->j) - { - lowest = node; - } - } - } - return lowest; -} - -static Node * -get_longer_distance (SkeltrackSkeleton *self, gint *distances) -{ - GList *current; - Node *farthest_node; - - current = g_list_first (self->priv->graph); - farthest_node = (Node *) current->data; - current = g_list_next (current); - - while (current != NULL) - { - Node *node; - node = (Node *) current->data; - if (node != NULL && - (distances[farthest_node->j * - self->priv->buffer_width + - farthest_node->i] != -1 && - distances[farthest_node->j * - self->priv->buffer_width + - farthest_node->i] < distances[node->j * - self->priv->buffer_width + - node->i])) - { - farthest_node = node; - } - current = g_list_next (current); - } - return farthest_node; -} - -static void -set_average_extremas (SkeltrackSkeletonPrivate *priv, GList *extremas) -{ - GList *current_extrema, *averaged_extremas = NULL; - - for (current_extrema = g_list_first (extremas); - current_extrema != NULL; - current_extrema = g_list_next (current_extrema)) - { - GList *current_node; - Node *extrema, *node = NULL, *cent = NULL, *node_centroid = NULL; - gint avg_x = 0, avg_y = 0, avg_z = 0, length = 0; - - extrema = (Node *) current_extrema->data; - - for (current_node = g_list_first (priv->graph); - current_node != NULL; - current_node = g_list_next (current_node)) - { - node = (Node *) current_node->data; - - if ((get_distance (extrema, node) < - priv->extrema_sphere_radius)) - { - avg_x += node->x; - avg_y += node->y; - avg_z += node->z; - - length++; - } - } - - /* if the length is 1 then it is because no other - nodes were considered for the average */ - if (length > 1) - { - cent = g_slice_new0 (Node); - cent->x = avg_x / length; - cent->y = avg_y / length; - cent->z = avg_z / length; - cent->linked_nodes = NULL; - - node_centroid = get_closest_node (priv->graph, cent); - - /* If the new averaged extrema is not already an extrema - set it for addition */ - if (g_list_find (averaged_extremas, node_centroid) == NULL && - g_list_find (extremas, node_centroid) == NULL) - { - current_extrema->data = node_centroid; - } - - g_slice_free (Node, cent); - } - } -} - -static GList * -get_extremas (SkeltrackSkeleton *self, Node *centroid) -{ - SkeltrackSkeletonPrivate *priv; - gint i, nr_nodes, matrix_size; - Node *lowest, *source, *node; - GList *extremas = NULL; - - priv = self->priv; - lowest = get_lowest (self, centroid); - source = lowest; - - matrix_size = priv->buffer_width * priv->buffer_height; - if (priv->distances_matrix == NULL) - { - priv->distances_matrix = g_slice_alloc0 (matrix_size * sizeof (gint)); - } - - for (i = 0; i < matrix_size; i++) - { - priv->distances_matrix[i] = -1; - } - - for (nr_nodes = NR_EXTREMAS_TO_SEARCH; - source != NULL && nr_nodes > 0; - nr_nodes--) - { - dijkstra_to (priv->graph, - source, - NULL, - priv->buffer_width, - priv->buffer_height, - priv->distances_matrix, - NULL); - - node = get_longer_distance (self, priv->distances_matrix); - - if (node == NULL) - continue; - - if (node != source) - { - priv->distances_matrix[node->j * priv->buffer_width + node->i] = 0; - source->linked_nodes = g_list_append (source->linked_nodes, node); - node->linked_nodes = g_list_append (node->linked_nodes, source); - source = node; - extremas = g_list_append (extremas, node); - } - } - - if (self->priv->extrema_sphere_radius != 0) - { - set_average_extremas (priv, extremas); - } - - return extremas; -} - -static Node * -get_shoulder_node (SkeltrackSkeletonPrivate *priv, - gfloat alpha, - gfloat step, - gint x_node, - gint y_node, - gint z_centroid) -{ - guint radius, arc_start_point, arc_length, current_i, current_j; - gfloat start_angle, last_node_arc, current_arc, angle, current_x, current_y; - Node *current_node = NULL; - Node *last_node = NULL; - - radius = priv->shoulders_circumference_radius; - arc_start_point = priv->shoulders_arc_start_point; - arc_length = priv->shoulders_arc_length; - - start_angle = M_PI_2; - - angle = start_angle + alpha; - current_x = x_node + radius * cos (angle); - current_y = y_node + radius * sin (angle); - current_arc = 0; - last_node_arc = 0; - current_node = NULL; - last_node = NULL; - - while (current_arc <= (arc_start_point + arc_length)) - { - convert_mm_to_screen_coords (priv->buffer_width, - priv->buffer_height, - priv->dimension_reduction, - current_x, - current_y, - z_centroid, - ¤t_i, - ¤t_j); - - if (current_i >= priv->buffer_width || current_j >= priv->buffer_height) - break; - - current_node = priv->node_matrix[current_j * priv->buffer_width + - current_i]; - - if (current_node != NULL) - { - last_node = current_node; - last_node_arc = current_arc; - } - - angle += step; - current_x = x_node + radius * cos (angle); - current_y = y_node + radius * sin (angle); - current_arc = ABS (angle - start_angle) * radius; - } - - if (last_node_arc < arc_start_point) - return NULL; - - return last_node; -} - -static gboolean -check_if_node_can_be_head (SkeltrackSkeleton *self, - Node *node, - Node *centroid, - Node **left_shoulder, - Node **right_shoulder) -{ - gfloat alpha; - Node *found_right_shoulder = NULL, *found_left_shoulder = NULL; - - SkeltrackSkeletonPrivate *priv; - - *left_shoulder = NULL; - *right_shoulder = NULL; - - priv = self->priv; - - if (node->j > centroid->j) - return FALSE; - - if ((node->y - centroid->y) != 0) - alpha = atan( ABS (node->x - centroid->x) / ABS (node->y - centroid->y)); - else - return FALSE; - - /* too much tilt, cannot be the head */ - if (alpha >= M_PI_4) - return FALSE; - - if (node->x < centroid->x) - alpha = -alpha; - - found_right_shoulder = get_shoulder_node (priv, - alpha, - priv->shoulders_search_step, - node->x, - node->y, - centroid->z); - if (found_right_shoulder == NULL) - return FALSE; - - found_left_shoulder = get_shoulder_node (priv, - alpha, - -priv->shoulders_search_step, - node->x, - node->y, - centroid->z); - - if (found_left_shoulder == NULL) - return FALSE; - - *right_shoulder = found_right_shoulder; - *left_shoulder = found_left_shoulder; - - return TRUE; -} - -static gboolean -get_head_and_shoulders (SkeltrackSkeleton *self, - GList *extremas, - Node *centroid, - Node **head, - Node **left_shoulder, - Node **right_shoulder) -{ - Node *node; - GList *current_extrema; - - for (current_extrema = g_list_first (extremas); - current_extrema != NULL; - current_extrema = g_list_next (current_extrema)) - { - node = (Node *) current_extrema->data; - - if (check_if_node_can_be_head (self, - node, - centroid, - left_shoulder, - right_shoulder)) - { - *head = node; - return TRUE; - } - } - return FALSE; -} - -static void -identify_arm_extrema (gint *distances, - Node **previous_nodes, - gint width, - gint hand_distance, - Node *extrema, - Node **elbow_extrema, - Node **hand_extrema) -{ - gint total_dist; - - if (extrema == NULL) - return; - - total_dist = distances[width * extrema->j + extrema->i]; - if (total_dist < hand_distance) - { - *elbow_extrema = extrema; - *hand_extrema = NULL; - } - else - { - Node *previous; - gint elbow_dist; - - previous = previous_nodes[extrema->j * width + extrema->i]; - elbow_dist = total_dist / 2; - while (previous && - distances[previous->j * width + previous->i] > elbow_dist) - { - previous = previous_nodes[previous->j * width + previous->i]; - } - *elbow_extrema = previous; - *hand_extrema = extrema; - } -} - -static void -set_left_and_right_from_extremas (SkeltrackSkeleton *self, - GList *extremas, - Node *head, - Node *left_shoulder, - Node *right_shoulder, - SkeltrackJointList *joints) -{ - gint *dist_left_a = NULL; - gint *dist_left_b = NULL; - gint *dist_right_a = NULL; - gint *dist_right_b = NULL; - gint total_dist_left_a = -1; - gint total_dist_right_a = -1; - gint total_dist_left_b = -1; - gint total_dist_right_b = -1; - gint *distances_left[2] = {NULL, NULL}; - gint *distances_right[2] = {NULL, NULL}; - gint index_left = -1; - gint index_right = -1; - Node *elbow_extrema, *hand_extrema; - Node **previous_left_a = NULL; - Node **previous_left_b = NULL; - Node **previous_right_a = NULL; - Node **previous_right_b = NULL; - Node **previous_left[2] = {NULL, NULL}; - Node **previous_right[2] = {NULL, NULL}; - Node *ext_a = NULL; - Node *ext_b = NULL; - Node *left_extrema[2] = {NULL, NULL}; - Node *right_extrema[2] = {NULL, NULL}; - GList *current_extrema; - gint width, height, matrix_size; - - for (current_extrema = g_list_first (extremas); - current_extrema != NULL; - current_extrema = g_list_next (current_extrema)) - { - Node *node; - node = (Node *) current_extrema->data; - if (node != head) - { - if (ext_a == NULL) - ext_a = node; - else - ext_b = node; - } - } - - if (head == NULL) - return; - - width = self->priv->buffer_width; - height = self->priv->buffer_height; - matrix_size = width * height; - - previous_left_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); - previous_left_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); - previous_right_a = g_slice_alloc0 (matrix_size * sizeof (Node *)); - previous_right_b = g_slice_alloc0 (matrix_size * sizeof (Node *)); - - dist_left_a = create_new_dist_matrix(matrix_size); - dijkstra_to (self->priv->graph, - left_shoulder, - ext_a, - width, - height, - dist_left_a, - previous_left_a); - - dist_left_b = create_new_dist_matrix(matrix_size); - dijkstra_to (self->priv->graph, - left_shoulder, - ext_b, - width, - height, - dist_left_b, - previous_left_b); - - dist_right_a = create_new_dist_matrix(matrix_size); - dijkstra_to (self->priv->graph, - right_shoulder, - ext_a, - width, - height, - dist_right_a, previous_right_a); - - dist_right_b = create_new_dist_matrix(matrix_size); - dijkstra_to (self->priv->graph, - right_shoulder, - ext_b, - width, - height, - dist_right_b, - previous_right_b); - - total_dist_left_a = dist_left_a[ext_a->j * width + ext_a->i]; - total_dist_right_a = dist_right_a[ext_a->j * width + ext_a->i]; - total_dist_left_b = dist_left_b[ext_b->j * width + ext_b->i]; - total_dist_right_b = dist_right_b[ext_b->j * width + ext_b->i]; - - if (total_dist_left_a < total_dist_right_a) - { - index_left++; - left_extrema[index_left] = ext_a; - distances_left[index_left] = dist_left_a; - previous_left[index_left] = previous_left_a; - } - else - { - index_right++; - right_extrema[index_right] = ext_a; - distances_right[index_right] = dist_right_a; - previous_right[index_right] = previous_right_a; - } - - if (total_dist_left_b < total_dist_right_b) - { - index_left++; - left_extrema[index_left] = ext_b; - distances_left[index_left] = dist_left_b; - previous_left[index_left] = previous_left_b; - } - else - { - index_right++; - right_extrema[index_right] = ext_b; - distances_right[index_right] = dist_right_b; - previous_right[index_right] = previous_right_b; - } - - elbow_extrema = NULL; - hand_extrema = NULL; - identify_arm_extrema (distances_left[0], - previous_left[0], - width, - self->priv->hands_minimum_distance, - left_extrema[0], - &elbow_extrema, - &hand_extrema); - - /* Two left extremas */ - if (index_left == 1) - { - if (hand_extrema == NULL) - { - hand_extrema = left_extrema[1]; - elbow_extrema = left_extrema[0]; - } - else - { - hand_extrema = left_extrema[0]; - elbow_extrema = left_extrema[1]; - } - } - - set_joint_from_node (joints, - elbow_extrema, - SKELTRACK_JOINT_ID_LEFT_ELBOW, - self->priv->dimension_reduction); - set_joint_from_node (joints, - hand_extrema, - SKELTRACK_JOINT_ID_LEFT_HAND, - self->priv->dimension_reduction); - - - elbow_extrema = NULL; - hand_extrema = NULL; - identify_arm_extrema (distances_right[0], - previous_right[0], - width, - self->priv->hands_minimum_distance, - right_extrema[0], - &elbow_extrema, - &hand_extrema); - - /* Two right extremas */ - if (index_right == 1) - { - if (hand_extrema == NULL) - { - hand_extrema = right_extrema[1]; - elbow_extrema = right_extrema[0]; - } - else - { - hand_extrema = right_extrema[0]; - elbow_extrema = right_extrema[1]; - } - } - - set_joint_from_node (joints, - elbow_extrema, - SKELTRACK_JOINT_ID_RIGHT_ELBOW, - self->priv->dimension_reduction); - set_joint_from_node (joints, - hand_extrema, - SKELTRACK_JOINT_ID_RIGHT_HAND, - self->priv->dimension_reduction); - - g_slice_free1 (matrix_size * sizeof (Node *), previous_left_a); - g_slice_free1 (matrix_size * sizeof (Node *), previous_left_b); - g_slice_free1 (matrix_size * sizeof (Node *), previous_right_a); - g_slice_free1 (matrix_size * sizeof (Node *), previous_right_b); - - g_slice_free1 (matrix_size * sizeof (gint), dist_left_a); - g_slice_free1 (matrix_size * sizeof (gint), dist_left_b); - g_slice_free1 (matrix_size * sizeof (gint), dist_right_a); - g_slice_free1 (matrix_size * sizeof (gint), dist_right_b); -} - -static Node * -get_adjusted_shoulder (guint buffer_width, - guint buffer_height, - guint dimension_reduction, - GList *graph, - Node *centroid, - Node *head, - Node *shoulder) -{ - Node *virtual_shoulder, *adjusted_shoulder = NULL; - virtual_shoulder = g_slice_new (Node); - virtual_shoulder->x = shoulder->x; - virtual_shoulder->y = shoulder->y; - virtual_shoulder->z = centroid->z; - - convert_mm_to_screen_coords (buffer_width, - buffer_height, - dimension_reduction, - virtual_shoulder->x, - virtual_shoulder->y, - virtual_shoulder->z, - (guint *) &virtual_shoulder->i, - (guint *) &virtual_shoulder->j); - - adjusted_shoulder = get_closest_torso_node (graph, - virtual_shoulder, - head); - g_slice_free (Node, virtual_shoulder); - - return adjusted_shoulder; -} - -static SkeltrackJoint ** -track_joints (SkeltrackSkeleton *self) -{ - Node * centroid; - Node *head = NULL; - Node *right_shoulder = NULL; - Node *left_shoulder = NULL; - GList *extremas; - SkeltrackJointList joints = NULL; - SkeltrackJointList smoothed = NULL; - - self->priv->graph = make_graph (self, &self->priv->labels); - centroid = get_centroid (self); - extremas = get_extremas (self, centroid); - - if (g_list_length (extremas) > 2) - { - if (self->priv->previous_head) - { - gint distance; - gboolean can_be_head = FALSE; - head = get_closest_node_to_joint (extremas, - self->priv->previous_head, - &distance); - if (head != NULL && - distance < GRAPH_DISTANCE_THRESHOLD) - { - can_be_head = check_if_node_can_be_head (self, - head, - centroid, - &left_shoulder, - &right_shoulder); - } - - if (!can_be_head) - head = NULL; - } - - if (head == NULL) - { - get_head_and_shoulders (self, - extremas, - centroid, - &head, - &left_shoulder, - &right_shoulder); - } - - if (joints == NULL) - joints = skeltrack_joint_list_new (); - - set_joint_from_node (&joints, - head, - SKELTRACK_JOINT_ID_HEAD, - self->priv->dimension_reduction); - - if (left_shoulder && head && head->z > left_shoulder->z) - { - Node *adjusted_shoulder; - adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, - self->priv->buffer_height, - self->priv->dimension_reduction, - self->priv->graph, - centroid, - head, - left_shoulder); - - if (adjusted_shoulder) - left_shoulder = adjusted_shoulder; - } - - set_joint_from_node (&joints, - left_shoulder, - SKELTRACK_JOINT_ID_LEFT_SHOULDER, - self->priv->dimension_reduction); - - if (right_shoulder && head && head->z > right_shoulder->z) - { - Node *adjusted_shoulder; - adjusted_shoulder = get_adjusted_shoulder (self->priv->buffer_width, - self->priv->buffer_height, - self->priv->dimension_reduction, - self->priv->graph, - centroid, - head, - right_shoulder); - - if (adjusted_shoulder) - right_shoulder = adjusted_shoulder; - } - set_joint_from_node (&joints, - right_shoulder, - SKELTRACK_JOINT_ID_RIGHT_SHOULDER, - self->priv->dimension_reduction); - - set_left_and_right_from_extremas (self, - extremas, - head, - left_shoulder, - right_shoulder, - &joints); - } - - self->priv->buffer = NULL; - - self->priv->main_component = NULL; - - clean_nodes (self->priv->graph); - g_list_free (self->priv->graph); - self->priv->graph = NULL; - - clean_labels (self->priv->labels); - g_list_free (self->priv->labels); - self->priv->labels = NULL; - - if (self->priv->enable_smoothing) - { - smooth_joints (&self->priv->smooth_data, joints); - - if (self->priv->smooth_data.smoothed_joints != NULL) - { - guint i; - smoothed = skeltrack_joint_list_new (); - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - SkeltrackJoint *smoothed_joint, *smooth, *trend; - smoothed_joint = NULL; - smooth = self->priv->smooth_data.smoothed_joints[i]; - if (smooth != NULL) - { - if (self->priv->smooth_data.trend_joints != NULL) - { - trend = self->priv->smooth_data.trend_joints[i]; - if (trend != NULL) - { - smoothed_joint = g_slice_new0 (SkeltrackJoint); - smoothed_joint->x = smooth->x + trend->x; - smoothed_joint->y = smooth->y + trend->y; - smoothed_joint->z = smooth->z + trend->z; - smoothed_joint->screen_x = smooth->screen_x + trend->screen_x; - smoothed_joint->screen_y = smooth->screen_y + trend->screen_y; - } - else - smoothed_joint = skeltrack_joint_copy (smooth); - } - else - smoothed_joint = skeltrack_joint_copy (smooth); - } - smoothed[i] = smoothed_joint; - } - } - skeltrack_joint_list_free (joints); - - joints = smoothed; - } - - if (joints) - { - SkeltrackJoint *joint = skeltrack_joint_list_get_joint (joints, - SKELTRACK_JOINT_ID_HEAD); - if (joint != NULL) - { - skeltrack_joint_free (self->priv->previous_head); - self->priv->previous_head = skeltrack_joint_copy (joint); - } - } - - g_list_free (extremas); - - return joints; -} - -static void -clean_tracking_resources (SkeltrackSkeleton *self) -{ - g_slice_free1 (self->priv->buffer_width * - self->priv->buffer_height * sizeof (gint), - self->priv->distances_matrix); - self->priv->distances_matrix = NULL; - - g_slice_free1 (self->priv->buffer_width * - self->priv->buffer_height * sizeof (Node *), - self->priv->node_matrix); - self->priv->node_matrix = NULL; -} - -static void -track_joints_in_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - SkeltrackSkeleton *self = SKELTRACK_SKELETON (object); - SkeltrackJointList joints; - - joints = track_joints (self); - - g_mutex_lock (&self->priv->track_joints_mutex); - self->priv->track_joints_result = NULL; - g_mutex_unlock (&self->priv->track_joints_mutex); - - g_simple_async_result_set_op_res_gpointer (res, - joints, - NULL); - - g_object_unref (res); -} - -/* public methods */ - -/** - * skeltrack_skeleton_new: - * - * Constructs and returns a new #SkeltrackSkeleton instance. - * - * Returns: (transfer full): The newly created #SkeltrackSkeleton. - */ -SkeltrackSkeleton * -skeltrack_skeleton_new (void) -{ - return g_object_new (SKELTRACK_TYPE_SKELETON, NULL); -} - -/** - * skeltrack_skeleton_set_focus_point: - * @self: The #SkeltrackSkeleton - * @x: The x coordinate of the focus point. - * @y: The y coordinate of the focus point. - * @z: The z coordinate of the focus point. - * - * Gets the focus point which is the origin from where the tracking will - * start. The coordinates will be in mm. - * - **/ -void -skeltrack_skeleton_get_focus_point (SkeltrackSkeleton *self, - gint *x, - gint *y, - gint *z) -{ - *x = self->priv->focus_node->x; - *y = self->priv->focus_node->y; - *z = self->priv->focus_node->z; -} - -/** - * skeltrack_skeleton_set_focus_point: - * @self: The #SkeltrackSkeleton - * @x: The x coordinate of the focus point. - * @y: The y coordinate of the focus point. - * @z: The z coordinate of the focus point. - * - * Sets the focus point which is the origin from where the tracking will - * start. The coordinates should be in mm. - * - * If this method is not called the default values are @x = 0, @y = 0, - * @z = 1000, that is, in the center of the screen and at 1m from the - * camera. - * - **/ -void -skeltrack_skeleton_set_focus_point (SkeltrackSkeleton *self, - gint x, - gint y, - gint z) -{ - self->priv->focus_node->x = x; - self->priv->focus_node->y = y; - self->priv->focus_node->z = z; -} - -/** - * skeltrack_skeleton_track_joints: - * @self: The #SkeltrackSkeleton - * @buffer: The buffer containing the depth information, from which - * all the information will be retrieved. - * @width: The width of the @buffer - * @height: The height of the @buffer - * @cancellable: (allow-none): A cancellable object, or %NULL (currently - * unused) - * @callback: (scope async): The function to call when the it is finished - * tracking the joints - * @user_data: (allow-none): An arbitrary user data to pass in @callback, - * or %NULL - * - * Tracks the skeleton's joints. - * - * It uses the depth information contained in the given @buffer and tries to - * track the skeleton joints. The @buffer's depth values should be in mm. - * Use skeltrack_skeleton_track_joints_finish() to get a list of the joints - * found. - * - * If this method is called while a previous attempt of tracking the joints - * is still running, a %G_IO_ERROR_PENDING error occurs. - * - **/ -void -skeltrack_skeleton_track_joints (SkeltrackSkeleton *self, - guint16 *buffer, - guint width, - guint height, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result = NULL; - - g_return_if_fail (SKELTRACK_IS_SKELETON (self) && - callback != NULL && - buffer != NULL); - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - skeltrack_skeleton_track_joints); - - if (self->priv->track_joints_result != NULL) - { - g_simple_async_result_set_error (result, - G_IO_ERROR, - G_IO_ERROR_PENDING, - "Currently tracking joints"); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - g_mutex_lock (&self->priv->track_joints_mutex); - - self->priv->track_joints_result = G_ASYNC_RESULT (result); - - /* @TODO: Set the cancellable */ - - self->priv->buffer = buffer; - - if (self->priv->buffer_width != width || - self->priv->buffer_height != height) - { - clean_tracking_resources (self); - - self->priv->buffer_width = width; - self->priv->buffer_height = height; - } - - g_simple_async_result_run_in_thread (result, - track_joints_in_thread, - G_PRIORITY_DEFAULT, - cancellable); - - g_mutex_unlock (&self->priv->track_joints_mutex); -} - -/** - * skeltrack_skeleton_track_joints_finish: - * @self: The #SkeltrackSkeleton - * @result: The #GAsyncResult provided in the callback - * @error: (allow-none): A pointer to a #GError, or %NULL - * - * Gets the list of joints that were retrieved by a - * skeltrack_skeleton_track_joints() operation. - * - * Use skeltrack_joint_list_get_joint() with #SkeltrackJointId - * to get the respective joints from the list. - * Joints that could not be found will appear as %NULL in the list. - * - * The list should be freed using skeltrack_joint_list_free(). - * - * Returns: (transfer full): The #SkeltrackJointList with the joints found. - */ -SkeltrackJointList -skeltrack_skeleton_track_joints_finish (SkeltrackSkeleton *self, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *res; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - res = G_SIMPLE_ASYNC_RESULT (result); - - if (! g_simple_async_result_propagate_error (res, error)) - { - SkeltrackJointList joints = NULL; - joints = g_simple_async_result_get_op_res_gpointer (res); - return joints; - } - else - return NULL; -} - -/** - * skeltrack_skeleton_track_joints_sync: - * @self: The #SkeltrackSkeleton - * @buffer: The buffer containing the depth information, from which - * all the information will be retrieved. - * @width: The width of the @buffer - * @height: The height of the @buffer - * @cancellable: (allow-none): A cancellable object, or %NULL (currently - * unused) - * @error: (allow-none): A pointer to a #GError, or %NULL - * - * Tracks the skeleton's joints synchronously. - * - * Does the same as skeltrack_skeleton_track_joints() but synchronously - * and returns the list of joints found. - * Ideal for off-line skeleton tracking. - * - * If this method is called while a previous attempt of asynchronously - * tracking the joints is still running, a %G_IO_ERROR_PENDING error occurs. - * - * The joints list should be freed using skeltrack_joint_list_free(). - * - * Returns: (transfer full): The #SkeltrackJointList with the joints found. - **/ -SkeltrackJointList -skeltrack_skeleton_track_joints_sync (SkeltrackSkeleton *self, - guint16 *buffer, - guint width, - guint height, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (SKELTRACK_IS_SKELETON (self), NULL); - - if (self->priv->track_joints_result != NULL && error != NULL) - { - *error = g_error_new (G_IO_ERROR, - G_IO_ERROR_PENDING, - "Currently tracking joints"); - return NULL; - } - - self->priv->buffer = buffer; - - if (self->priv->buffer_width != width || - self->priv->buffer_height != height) - { - clean_tracking_resources (self); - - self->priv->buffer_width = width; - self->priv->buffer_height = height; - } - - return track_joints (self); -} diff --git a/interface/external/skeltrack/src/skeltrack-smooth.c b/interface/external/skeltrack/src/skeltrack-smooth.c deleted file mode 100644 index 1e6658f578..0000000000 --- a/interface/external/skeltrack/src/skeltrack-smooth.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * skeltrack-smooth.c - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#include - -#include "skeltrack-smooth.h" - -static gfloat -holt_double_exp_formula_st (gfloat alpha, - gfloat previous_trend, - gfloat current_value, - gfloat previous_smoothed_value) -{ - return alpha * current_value + - (1.0 - alpha) * (previous_smoothed_value + previous_trend); -} - -static gfloat -holt_double_exp_formula_bt (gfloat beta, - gfloat previous_trend, - gfloat current_smoothed_value, - gfloat previous_smoothed_value) -{ - return beta * (current_smoothed_value - previous_smoothed_value) + - (1.0 - beta) * previous_trend; -} - -static void -holt_double_exp_joint (gfloat alpha, - gfloat beta, - SkeltrackJoint *smoothed_joint, - SkeltrackJoint *current_joint, - SkeltrackJoint *trend_joint) -{ - gfloat new_x, new_y, new_z, new_screen_x, new_screen_y; - new_x = holt_double_exp_formula_st (alpha, - trend_joint->x, - current_joint->x, - smoothed_joint->x); - new_y = holt_double_exp_formula_st (alpha, - trend_joint->y, - current_joint->y, - smoothed_joint->y); - new_z = holt_double_exp_formula_st (alpha, - trend_joint->z, - current_joint->z, - smoothed_joint->z); - new_screen_x = holt_double_exp_formula_st (alpha, - trend_joint->screen_x, - current_joint->screen_x, - smoothed_joint->screen_x); - new_screen_y = holt_double_exp_formula_st (alpha, - trend_joint->screen_y, - current_joint->screen_y, - smoothed_joint->screen_y); - trend_joint->x = holt_double_exp_formula_bt (beta, - trend_joint->x, - new_x, - smoothed_joint->x); - trend_joint->y = holt_double_exp_formula_bt (beta, - trend_joint->y, - new_y, - smoothed_joint->y); - trend_joint->z = holt_double_exp_formula_bt (beta, - trend_joint->z, - new_z, - smoothed_joint->z); - trend_joint->screen_x = holt_double_exp_formula_bt (beta, - trend_joint->screen_x, - new_screen_x, - smoothed_joint->screen_x); - trend_joint->screen_y = holt_double_exp_formula_bt (beta, - trend_joint->screen_y, - new_screen_y, - smoothed_joint->screen_y); - smoothed_joint->x = new_x; - smoothed_joint->y = new_y; - smoothed_joint->z = new_z; - smoothed_joint->screen_x = new_screen_x; - smoothed_joint->screen_y = new_screen_y; -} - -void -reset_joints_persistency_counter (SmoothData *smooth_data) -{ - guint i; - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - smooth_data->joints_persistency_counter[i] = - smooth_data->joints_persistency; - } -} - -static void -decrease_joints_persistency (SmoothData *smooth_data) -{ - guint i; - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - SkeltrackJoint *smoothed_joint = NULL; - SkeltrackJoint *trend_joint = NULL; - - if (smooth_data->smoothed_joints) - smoothed_joint = smooth_data->smoothed_joints[i]; - if (smooth_data->trend_joints) - trend_joint = smooth_data->trend_joints[i]; - - if (smoothed_joint != NULL || trend_joint != NULL) - { - if (smooth_data->joints_persistency_counter[i] > 0) - smooth_data->joints_persistency_counter[i]--; - else - { - skeltrack_joint_free (smoothed_joint); - skeltrack_joint_free (trend_joint); - if (smoothed_joint) - smooth_data->smoothed_joints[i] = NULL; - if (trend_joint) - smooth_data->trend_joints[i] = NULL; - smooth_data->joints_persistency_counter[i] = smooth_data->joints_persistency; - } - } - } -} - -void -smooth_joints (SmoothData *data, - SkeltrackJointList new_joints) -{ - guint i; - - if (new_joints == NULL) - { - decrease_joints_persistency (data); - return; - } - - if (data->smoothed_joints == NULL) - { - data->smoothed_joints = skeltrack_joint_list_new (); - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - data->smoothed_joints[i] = skeltrack_joint_copy (new_joints[i]); - } - return; - } - if (data->trend_joints == NULL) - { - data->trend_joints = skeltrack_joint_list_new (); - } - - for (i = 0; i < SKELTRACK_JOINT_MAX_JOINTS; i++) - { - SkeltrackJoint *joint, *smoothed_joint, *trend_joint; - - smoothed_joint = data->smoothed_joints[i]; - trend_joint = data->trend_joints[i]; - joint = new_joints[i]; - if (joint == NULL) - { - if (smoothed_joint != NULL) - { - if (data->joints_persistency_counter[i] > 0) - data->joints_persistency_counter[i]--; - else - { - skeltrack_joint_free (smoothed_joint); - skeltrack_joint_free (trend_joint); - data->smoothed_joints[i] = NULL; - data->trend_joints[i] = NULL; - data->joints_persistency_counter[i] = data->joints_persistency; - } - } - continue; - } - data->joints_persistency_counter[i] = data->joints_persistency; - - if (smoothed_joint == NULL) - { - data->smoothed_joints[i] = skeltrack_joint_copy (joint); - continue; - } - - if (trend_joint == NULL) - { - /* First case (when there are only initial values) */ - trend_joint = g_slice_new0 (SkeltrackJoint); - trend_joint->x = joint->x - smoothed_joint->x; - trend_joint->y = joint->y - smoothed_joint->y; - trend_joint->z = joint->z - smoothed_joint->z; - trend_joint->screen_x = joint->screen_x - smoothed_joint->screen_x; - trend_joint->screen_y = joint->screen_y - smoothed_joint->screen_y; - data->trend_joints[i] = trend_joint; - } - else - { - /* @TODO: Check if we should give the control of each factor - independently (data-smoothing-factor and trend-smoothing-factor). - */ - holt_double_exp_joint (data->smoothing_factor, - data->smoothing_factor, - smoothed_joint, - joint, - trend_joint); - } - } -} diff --git a/interface/external/skeltrack/src/skeltrack-util.c b/interface/external/skeltrack/src/skeltrack-util.c deleted file mode 100644 index 482f26069a..0000000000 --- a/interface/external/skeltrack/src/skeltrack-util.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * skeltrack-util.c - * - * Skeltrack - A Free Software skeleton tracking library - * Copyright (C) 2012 Igalia S.L. - * - * Authors: - * Joaquim Rocha - * Eduardo Lima Mitev - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License at http://www.gnu.org/licenses/lgpl-3.0.txt - * for more details. - */ - -#include - -#include "skeltrack-util.h" - -/* @TODO: Expose these to the user */ -static const gfloat SCALE_FACTOR = .0021; -static const gint MIN_DISTANCE = -10.0; - -static SkeltrackJoint * -node_to_joint (Node *node, SkeltrackJointId id, gint dimension_reduction) -{ - SkeltrackJoint *joint; - - if (node == NULL) - return NULL; - - joint = g_slice_new0 (SkeltrackJoint); - joint->id = id; - joint->x = node->x; - joint->y = node->y; - joint->z = node->z; - joint->screen_x = node->i * dimension_reduction; - joint->screen_y = node->j * dimension_reduction; - - return joint; -} - -static guint -get_distance_from_joint (Node *node, SkeltrackJoint *joint) -{ - guint dx, dy, dz; - dx = ABS (node->x - joint->x); - dy = ABS (node->y - joint->y); - dz = ABS (node->z - joint->z); - return sqrt (dx * dx + dy * dy + dz * dz); -} - -static void -unlink_node (Node *node) -{ - Node *neighbor; - GList *current_neighbor; - - for (current_neighbor = g_list_first (node->neighbors); - current_neighbor != NULL; - current_neighbor = g_list_next (current_neighbor)) - { - neighbor = (Node *) current_neighbor->data; - neighbor->neighbors = g_list_remove (neighbor->neighbors, node); - } - - for (current_neighbor = g_list_first (node->linked_nodes); - current_neighbor != NULL; - current_neighbor = g_list_next (current_neighbor)) - { - neighbor = (Node *) current_neighbor->data; - neighbor->linked_nodes = g_list_remove (neighbor->linked_nodes, node); - } - - g_list_free (node->neighbors); - g_list_free (node->linked_nodes); - node->neighbors = NULL; - node->linked_nodes = NULL; -} - -static Node * -get_closest_node_with_distances (GList *node_list, - Node *from, - guint x_dist, - guint y_dist, - guint z_dist, - gint *closest_node_dist) -{ - Node *closest = NULL; - gint distance = -1; - GList *current_node; - - /* @TODO: Replace this and use closest pair of points - algorithm and ensure O(n log n) instead of brute-force */ - - for (current_node = g_list_first (node_list); - current_node != NULL; - current_node = g_list_next (current_node)) - { - guint dx, dy, dz; - Node *node; - gint current_distance; - node = (Node *) current_node->data; - - dx = ABS (from->x - node->x); - dy = ABS (from->y - node->y); - dz = ABS (from->z - node->z); - - if (dx > x_dist || dy > y_dist || dz > z_dist) - continue; - - current_distance = sqrt (dx * dx + dy * dy + dz * dz); - if (closest == NULL || distance > current_distance) - { - closest = node; - distance = current_distance; - } - } - - *closest_node_dist = distance; - return closest; -} - -Node * -get_closest_node_to_joint (GList *extremas, - SkeltrackJoint *joint, - gint *distance) -{ - GList *current_node; - gint dist = -1; - Node *closest_node = NULL; - - for (current_node = g_list_first (extremas); - current_node != NULL; - current_node = g_list_next (current_node)) - { - guint current_dist; - Node *node = (Node *) current_node->data; - if (node == NULL) - continue; - - current_dist = get_distance_from_joint (node, joint); - if (dist == -1 || current_dist < dist) - { - closest_node = node; - dist = current_dist; - } - } - *distance = dist; - return closest_node; -} - -gint -get_distance (Node *a, Node *b) -{ - guint dx, dy, dz; - dx = ABS (a->x - b->x); - dy = ABS (a->y - b->y); - dz = ABS (a->z - b->z); - return sqrt (dx * dx + dy * dy + dz * dz); -} - -Node * -get_closest_torso_node (GList *node_list, Node *from, Node *head) -{ - Node *closest = NULL; - gint distance = -1; - GList *current_node; - - /* @TODO: Replace this and use closest pair of points - algorithm and ensure O(n log n) instead of brute-force */ - - for (current_node = g_list_first (node_list); - current_node != NULL; - current_node = g_list_next (current_node)) - { - Node *node; - gint current_distance; - node = (Node *) current_node->data; - if (node->z >= head->z && - node->y >= from->y) - { - current_distance = get_distance (node, from); - if (closest == NULL || current_distance < distance) - { - closest = node; - distance = current_distance; - } - } - } - return closest; -} - -Node * -get_closest_node (GList *node_list, Node *from) -{ - Node *closest = NULL; - gint distance = -1; - GList *current_node; - - /* @TODO: Replace this and use closest pair of points - algorithm and ensure O(n log n) instead of brute-force */ - - for (current_node = g_list_first (node_list); - current_node != NULL; - current_node = g_list_next (current_node)) - { - Node *node; - gint current_distance; - node = (Node *) current_node->data; - if (closest == NULL) - { - closest = node; - distance = get_distance (node, from); - continue; - } - current_distance = get_distance (node, from); - if (current_distance < distance) - { - closest = node; - distance = current_distance; - } - } - return closest; -} - -Label * -get_main_component (GList *node_list, Node *from, gdouble min_normalized_nr_nodes) -{ - Label *main_component = NULL; - gint distance = -1; - GList *current_node; - - for (current_node = g_list_first (node_list); - current_node != NULL; - current_node = g_list_next (current_node)) - { - Node *node; - Label *label; - gint current_distance; - node = (Node *) current_node->data; - label = node->label; - - if (main_component == NULL && - label->normalized_num_nodes > min_normalized_nr_nodes) - { - main_component = label; - distance = get_distance (node, from); - continue; - } - - current_distance = get_distance (node, from); - if (current_distance < distance && - label->normalized_num_nodes > min_normalized_nr_nodes) - { - main_component = label; - distance = current_distance; - } - } - - return main_component; -} - -Label * -label_find (Label *label) -{ - Label *parent; - - g_return_val_if_fail (label != NULL, NULL); - - parent = label->parent; - if (parent == label) - return parent; - else - return label_find (parent); -} - -void -label_union (Label *a, Label *b) -{ - Label *root_a, *root_b; - root_a = label_find (a); - root_b = label_find (b); - if (root_a->index < root_b->index) - { - b->parent = root_a; - } - else - { - a->parent = root_b; - } -} - -void -free_label (Label *label) -{ - g_list_free (label->nodes); - label->nodes = NULL; - g_slice_free (Label, label); -} - -void -clean_labels (GList *labels) -{ - GList *current = g_list_first (labels); - while (current != NULL) - { - Label *label; - label = (Label *) current->data; - free_label (label); - current = g_list_next (current); - } -} - -void -free_node (Node *node, gboolean unlink_node_first) -{ - if (unlink_node_first) - { - unlink_node (node); - } - else - { - g_list_free (node->neighbors); - g_list_free (node->linked_nodes); - node->neighbors = NULL; - node->linked_nodes = NULL; - } - g_slice_free (Node, node); -} - -void -clean_nodes (GList *nodes) -{ - GList *current = g_list_first (nodes); - while (current != NULL) - { - Node *node; - node = (Node *) current->data; - free_node (node, FALSE); - current = g_list_next (current); - } -} - -GList * -remove_nodes_with_label (GList *nodes, - Node **node_matrix, - gint width, - Label *label) -{ - Node *node; - GList *link_to_delete, *current_node; - - current_node = g_list_first (nodes); - while (current_node != NULL) - { - node = (Node *) current_node->data; - if (node->label == label) - { - link_to_delete = current_node; - current_node = g_list_next (current_node); - nodes = g_list_delete_link (nodes, link_to_delete); - node_matrix[width * node->j + node->i] = NULL; - free_node (node, TRUE); - continue; - } - current_node = g_list_next (current_node); - } - return nodes; -} - -Label * -get_lowest_index_label (Label **neighbor_labels) -{ - guint index; - Label *lowest_index_label = NULL; - - lowest_index_label = neighbor_labels[0]; - for (index = 1; index < 4; index++) - { - if (neighbor_labels[index] == NULL) - continue; - - if (lowest_index_label == NULL || - lowest_index_label->index < neighbor_labels[index]->index) - { - lowest_index_label = neighbor_labels[index]; - } - } - - return lowest_index_label; -} - -Label * -new_label (gint index) -{ - Label *label = g_slice_new (Label); - label->index = index; - label->parent = label; - label->nodes = NULL; - label->bridge_node = NULL; - label->to_node = NULL; - label->lower_screen_y = -1; - label->higher_z = -1; - label->lower_z = -1; - label->normalized_num_nodes = -1; - - return label; -} - -void -join_components_to_main (GList *labels, - Label *main_component_label, - guint horizontal_max_distance, - guint depth_max_distance, - guint graph_distance_threshold) -{ - GList *current_label; - - for (current_label = g_list_first (labels); - current_label != NULL; - current_label = g_list_next (current_label)) - { - gint closer_distance = -1; - Label *label; - GList *current_node, *nodes; - - label = (Label *) current_label->data; - if (label == main_component_label) - continue; - - /* Skip nodes behind main component */ - if (label->higher_z > main_component_label->higher_z + - graph_distance_threshold) - continue; - - nodes = label->nodes; - for (current_node = g_list_first (nodes); - current_node != NULL; - current_node = g_list_next (current_node)) - { - Node *node; - gint current_distance; - node = (Node *) current_node->data; - /* Skip nodes that belong to the same component or - that a not in the edge of their component */ - if (g_list_length (node->neighbors) == 8) - continue; - - Node *closest_node = - get_closest_node_with_distances (main_component_label->nodes, - node, - horizontal_max_distance, - horizontal_max_distance, - depth_max_distance, - ¤t_distance); - if (closest_node && - (current_distance < closer_distance || - closer_distance == -1)) - { - node->label->bridge_node = node; - node->label->to_node = closest_node; - closer_distance = current_distance; - } - } - } -} - -void -set_joint_from_node (SkeltrackJointList *joints, - Node *node, - SkeltrackJointId id, - gint dimension_reduction) -{ - (*joints)[id] = node_to_joint (node, id, dimension_reduction); -} - -gint * -create_new_dist_matrix (gint matrix_size) -{ - guint i; - gint *distances; - - distances = g_slice_alloc0 (matrix_size * sizeof (gint)); - for (i = 0; i < matrix_size; i++) - { - distances[i] = -1; - } - - return distances; -} - -gboolean -dijkstra_to (GList *nodes, Node *source, Node *target, - gint width, gint height, - gint *distances, Node **previous) -{ - gint nr; - GList *unvisited_nodes, *current; - - for (current = g_list_first (nodes); - previous != NULL && current != NULL; - current = g_list_next (current)) - { - Node *node; - node = (Node *) current->data; - previous[node->j * width + node->i] = NULL; - } - distances[source->j * width + source->i] = 0; - - unvisited_nodes = g_list_copy (nodes); - nr = 0; - while (unvisited_nodes != NULL) - { - Node *node; - GList *current_neighbor, *shorter_dist_node, *cur_node; - - shorter_dist_node = g_list_first (unvisited_nodes); - cur_node = g_list_next (shorter_dist_node); - while (cur_node != NULL) - { - Node *value, *shorter_dist; - value = (Node *) cur_node->data; - shorter_dist = (Node *) shorter_dist_node->data; - if (distances[shorter_dist->j * width + shorter_dist->i] == -1 || - (distances[value->j * width + value->i] != -1 && - distances[value->j * width + - value->i] < distances[shorter_dist->j * width + - shorter_dist->i])) - { - shorter_dist_node = cur_node; - } - cur_node = g_list_next (cur_node); - } - - node = (Node *) shorter_dist_node->data; - if (distances[node->j * width + node->i] == -1) - { - break; - } - - current_neighbor = g_list_first (node->neighbors); - while (current_neighbor) - { - gint dist; - Node *neighbor; - - neighbor = (Node *) current_neighbor->data; - dist = get_distance (node, neighbor) + - distances[node->j * width + node->i]; - - if (distances[neighbor->j * width + neighbor->i] == -1 || - dist < distances[neighbor->j * width + neighbor->i]) - { - distances[neighbor->j * width + neighbor->i] = dist; - if (previous != NULL) - { - previous[neighbor->j * width + neighbor->i] = node; - } - nr++; - } - if (target != NULL && neighbor == target) - { - g_list_free (unvisited_nodes); - return TRUE; - } - - current_neighbor = g_list_next (current_neighbor); - } - unvisited_nodes = g_list_delete_link (unvisited_nodes, shorter_dist_node); - } - g_list_free (unvisited_nodes); - return FALSE; -} - -void -convert_screen_coords_to_mm (guint width, - guint height, - guint dimension_reduction, - guint i, - guint j, - gint z, - gint *x, - gint *y) -{ - gfloat width_height_relation = - width > height ? (gfloat) width / height : (gfloat) height / width; - /* Formula from http://openkinect.org/wiki/Imaging_Information */ - *x = round((i * dimension_reduction - width * dimension_reduction / 2.0) * - (z + MIN_DISTANCE) * SCALE_FACTOR * width_height_relation); - *y = round((j * dimension_reduction - height * dimension_reduction / 2.0) * - (z + MIN_DISTANCE) * SCALE_FACTOR); -} - -void -convert_mm_to_screen_coords (guint width, - guint height, - guint dimension_reduction, - gint x, - gint y, - gint z, - guint *i, - guint *j) -{ - gfloat width_height_relation = - width > height ? (gfloat) width / height : (gfloat) height / width; - - if (z + MIN_DISTANCE == 0) - { - *i = 0; - *j = 0; - return; - } - - *i = round (width / 2.0 + x / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * - dimension_reduction * width_height_relation)); - *j = round (height / 2.0 + y / ((gfloat) (z + MIN_DISTANCE) * SCALE_FACTOR * - dimension_reduction)); -} From c4d7cea9685adb0d2ce38ee11b5bc1982ff411fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Jun 2013 15:57:27 -0700 Subject: [PATCH 11/63] Remove skeltrack from CMakeLists.txt. --- interface/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 234f365687..d45c17e5de 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -15,7 +15,6 @@ set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV) set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl) set(LIBUSB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibUSB) set(FREENECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/freenect) -set(SKELTRACK_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/skeltrack) if (APPLE) set(GL_HEADERS "#include \n#include ") @@ -96,7 +95,6 @@ find_package(ZLIB) find_package(UVCCameraControl) find_package(LibUSB) find_package(Freenect) -find_package(Skeltrack) # include headers for interface and InterfaceConfig. include_directories( @@ -113,11 +111,10 @@ include_directories( ${OPENCV_INCLUDE_DIRS} ${SPEEXDSP_INCLUDE_DIRS} ${FREENECT_INCLUDE_DIRS} - ${SKELTRACK_INCLUDE_DIRS} ) target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES} - ${FREENECT_LIBRARIES} ${SKELTRACK_LIBRARIES} ${LIBUSB_LIBRARIES}) + ${FREENECT_LIBRARIES} ${LIBUSB_LIBRARIES}) if (APPLE) # link in required OS X frameworks and include the right GL headers From f602e1643bb9f0f2fc7d50b84374a47d2e14d3ca Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Jun 2013 16:06:42 -0700 Subject: [PATCH 12/63] Remove references to LibUSB/Freenect. --- interface/CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d45c17e5de..148e6819be 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -13,8 +13,6 @@ set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio) set(SPEEX_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Speex) set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV) set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl) -set(LIBUSB_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibUSB) -set(FREENECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/freenect) if (APPLE) set(GL_HEADERS "#include \n#include ") @@ -93,8 +91,6 @@ find_package(SpeexDSP REQUIRED) find_package(OpenCV) find_package(ZLIB) find_package(UVCCameraControl) -find_package(LibUSB) -find_package(Freenect) # include headers for interface and InterfaceConfig. include_directories( @@ -110,11 +106,9 @@ include_directories( ${LIBOVR_INCLUDE_DIRS} ${OPENCV_INCLUDE_DIRS} ${SPEEXDSP_INCLUDE_DIRS} - ${FREENECT_INCLUDE_DIRS} ) -target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES} - ${FREENECT_LIBRARIES} ${LIBUSB_LIBRARIES}) +target_link_libraries(${TARGET_NAME} ${QT_LIBRARIES} ${OPENCV_LIBRARIES} ${ZLIB_LIBRARIES} ${SPEEXDSP_LIBRARIES}) if (APPLE) # link in required OS X frameworks and include the right GL headers From ea9566596acc711d137e8f38976cd21ab6c5859f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Jun 2013 16:47:08 -0700 Subject: [PATCH 13/63] Build OpenNI/NITE into interface if available. --- cmake/modules/FindNITE.cmake | 44 +++++++++++++++ cmake/modules/FindOpenNI.cmake | 44 +++++++++++++++ interface/CMakeLists.txt | 9 ++++ interface/src/Webcam.cpp | 99 ++++++++-------------------------- interface/src/Webcam.h | 14 ++--- 5 files changed, 126 insertions(+), 84 deletions(-) create mode 100644 cmake/modules/FindNITE.cmake create mode 100644 cmake/modules/FindOpenNI.cmake diff --git a/cmake/modules/FindNITE.cmake b/cmake/modules/FindNITE.cmake new file mode 100644 index 0000000000..301ed651ee --- /dev/null +++ b/cmake/modules/FindNITE.cmake @@ -0,0 +1,44 @@ +# Find the NITE library +# +# You must provide an NITE_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# NITE_FOUND - system found NITE +# NITE_INCLUDE_DIRS - the NITE include directory +# NITE_LIBRARIES - Link this to use NITE +# +# Created on 6/28/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) + # in cache already + set(NITE_FOUND TRUE) +else (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) + find_path(NITE_INCLUDE_DIRS XnVNite.h /usr/include/nite) + + if (APPLE) + find_library(NITE_LIBRARIES libXnVNite_1_5_2.dylib /usr/lib) + elseif (UNIX) + find_library(NITE_LIBRARIES libXnVNite_1_5_2.so /usr/lib) + endif () + + if (NITE_INCLUDE_DIRS AND NITE_LIBRARIES) + set(NITE_FOUND TRUE) + endif (NITE_INCLUDE_DIRS AND NITE_LIBRARIES) + + if (NITE_FOUND) + if (NOT NITE_FIND_QUIETLY) + message(STATUS "Found NITE: ${NITE_LIBRARIES}") + endif (NOT NITE_FIND_QUIETLY) + else (NITE_FOUND) + if (NITE_FIND_REQUIRED) + message(FATAL_ERROR "Could not find NITE") + endif (NITE_FIND_REQUIRED) + endif (NITE_FOUND) + + # show the NITE_INCLUDE_DIRS and NITE_LIBRARIES variables only in the advanced view + mark_as_advanced(NITE_INCLUDE_DIRS NITE_LIBRARIES) + +endif (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) diff --git a/cmake/modules/FindOpenNI.cmake b/cmake/modules/FindOpenNI.cmake new file mode 100644 index 0000000000..111a33dfbe --- /dev/null +++ b/cmake/modules/FindOpenNI.cmake @@ -0,0 +1,44 @@ +# Find the OpenNI library +# +# You must provide an OPENNI_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# OPENNI_FOUND - system found OpenNI +# OPENNI_INCLUDE_DIRS - the OpenNI include directory +# OPENNI_LIBRARIES - Link this to use OpenNI +# +# Created on 6/28/2013 by Andrzej Kapolka +# Copyright (c) 2013 High Fidelity +# + +if (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS) + # in cache already + set(OPENNI_FOUND TRUE) +else (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS) + find_path(OPENNI_INCLUDE_DIRS XnOpenNI.h /usr/include/ni) + + if (APPLE) + find_library(OPENNI_LIBRARIES libOpenNI.dylib /usr/lib) + elseif (UNIX) + find_library(OPENNI_LIBRARIES libOpenNI.so /usr/lib) + endif () + + if (OPENNI_INCLUDE_DIRS AND OPENNI_LIBRARIES) + set(OPENNI_FOUND TRUE) + endif (OPENNI_INCLUDE_DIRS AND OPENNI_LIBRARIES) + + if (OPENNI_FOUND) + if (NOT OPENNI_FIND_QUIETLY) + message(STATUS "Found OpenNI: ${OPENNI_LIBRARIES}") + endif (NOT OPENNI_FIND_QUIETLY) + else (OPENNI_FOUND) + if (OPENNI_FIND_REQUIRED) + message(FATAL_ERROR "Could not find OpenNI") + endif (OPENNI_FIND_REQUIRED) + endif (OPENNI_FOUND) + + # show the OPENNI_INCLUDE_DIRS and OPENNI_LIBRARIES variables only in the advanced view + mark_as_advanced(OPENNI_INCLUDE_DIRS OPENNI_LIBRARIES) + +endif (OPENNI_LIBRARIES AND OPENNI_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c840bd980d..941a321c22 100755 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -91,6 +91,15 @@ find_package(Leap) find_package(OpenCV) find_package(ZLIB) find_package(UVCCameraControl) +find_package(OpenNI) +find_package(NITE) + +# let the source know that we have OpenNI/NITE for Kinect +if (OPENNI_FOUND AND NITE_FOUND) + add_definitions(-DHAVE_NITE) + include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS} ${NITE_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES} ${NITE_LIBRARIES}) +endif (OPENNI_FOUND AND NITE_FOUND) # include headers for interface and InterfaceConfig. include_directories( diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 9438a18c3d..e18c2c5aa8 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -210,17 +210,11 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame())); } -FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0), _freenectContext(0) { +FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) { } FrameGrabber::~FrameGrabber() { - if (_freenectContext != 0) { - freenect_stop_depth(_freenectDevice); - freenect_stop_video(_freenectDevice); - freenect_close_device(_freenectDevice); - freenect_shutdown(_freenectContext); - - } else if (_capture != 0) { + if (_capture != 0) { cvReleaseCapture(&_capture); } } @@ -229,51 +223,36 @@ void FrameGrabber::reset() { _searchWindow = Rect(0, 0, 0, 0); } -static Mat videoFrame, depthFrame; -static bool gotVideoFrame, gotDepthFrame; - void FrameGrabber::grabFrame() { - if (_capture == 0 && _freenectContext == 0 && !init()) { + if (_capture == 0 && !init()) { return; } int format = GL_BGR; - ::gotVideoFrame = ::gotDepthFrame = false; - if (_freenectContext != 0) { - freenect_process_events(_freenectContext); - if (::gotDepthFrame) { - ::depthFrame.convertTo(_grayDepth, CV_8UC1, 255.0 / 2047.0); - } - format = GL_RGB; - - } else { - IplImage* image = cvQueryFrame(_capture); - if (image != 0) { - // make sure it's in the format we expect - if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || - image->origin != 0) { - printLog("Invalid webcam image format.\n"); - return; - } - ::videoFrame = image; - ::gotVideoFrame = true; - } - } - if (!::gotVideoFrame) { + IplImage* image = cvQueryFrame(_capture); + if (image == 0) { // try again later QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection); return; } + // make sure it's in the format we expect + if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || + image->origin != 0) { + printLog("Invalid webcam image format.\n"); + return; + } + Mat frame = 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 faces; - _faceCascade.detectMultiScale(::videoFrame, faces, 1.1, 6); + _faceCascade.detectMultiScale(frame, faces, 1.1, 6); if (!faces.empty()) { _searchWindow = faces.front(); - updateHSVFrame(::videoFrame, format); + updateHSVFrame(frame, format); Mat faceHsv(_hsvFrame, _searchWindow); Mat faceMask(_mask, _searchWindow); @@ -286,7 +265,7 @@ void FrameGrabber::grabFrame() { } RotatedRect faceRect; if (_searchWindow.area() > 0) { - updateHSVFrame(::videoFrame, format); + updateHSVFrame(frame, format); calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range); bitwise_and(_backProject, _mask, _backProject); @@ -295,25 +274,7 @@ void FrameGrabber::grabFrame() { _searchWindow = faceRect.boundingRect(); } QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", - Q_ARG(cv::Mat, ::videoFrame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepth), Q_ARG(cv::RotatedRect, faceRect)); -} - -const char* FREENECT_LOG_LEVEL_NAMES[] = { "fatal", "error", "warning", "notice", "info", "debug", "spew", "flood" }; - -static void logCallback(freenect_context* freenectDevice, freenect_loglevel level, const char *msg) { - printLog("Freenect %s: %s\n", FREENECT_LOG_LEVEL_NAMES[level], msg); -} - -static freenect_frame_mode freenectVideoMode, freenectDepthMode; - -static void depthCallback(freenect_device* freenectDevice, void* depth, uint32_t timestamp) { - ::depthFrame = Mat(::freenectDepthMode.height, ::freenectDepthMode.width, CV_16UC1, depth); - ::gotDepthFrame = true; -} - -static void videoCallback(freenect_device* freenectDevice, void* video, uint32_t timestamp) { - ::videoFrame = Mat(::freenectVideoMode.height, ::freenectVideoMode.width, CV_8UC3, video); - ::gotVideoFrame = true; + Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, Mat()), Q_ARG(cv::RotatedRect, faceRect)); } bool FrameGrabber::init() { @@ -325,28 +286,10 @@ bool FrameGrabber::init() { } // first try for a Kinect - if (freenect_init(&_freenectContext, 0) >= 0) { - freenect_set_log_level(_freenectContext, FREENECT_LOG_WARNING); - freenect_set_log_callback(_freenectContext, logCallback); - freenect_select_subdevices(_freenectContext, FREENECT_DEVICE_CAMERA); - - if (freenect_num_devices(_freenectContext) > 0) { - if (freenect_open_device(_freenectContext, &_freenectDevice, 0) >= 0) { - freenect_set_depth_callback(_freenectDevice, depthCallback); - freenect_set_video_callback(_freenectDevice, videoCallback); - ::freenectVideoMode = freenect_find_video_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_VIDEO_RGB); - freenect_set_video_mode(_freenectDevice, ::freenectVideoMode); - ::freenectDepthMode = freenect_find_depth_mode(FREENECT_RESOLUTION_MEDIUM, FREENECT_DEPTH_11BIT); - freenect_set_depth_mode(_freenectDevice, ::freenectDepthMode); - - freenect_start_depth(_freenectDevice); - freenect_start_video(_freenectDevice); - return true; - } - } - freenect_shutdown(_freenectContext); - _freenectContext = 0; - } +#ifdef HAVE_NITE + _xnContext.Init(); + +#endif // next, an ordinary webcam if ((_capture = cvCaptureFromCAM(-1)) == 0) { diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 27e54caada..b9a2f0e6fe 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -15,10 +15,12 @@ #include -#include - #include +#ifdef HAVE_NITE + #include +#endif + #include "InterfaceConfig.h" class QImage; @@ -97,10 +99,10 @@ private: cv::SparseMat _histogram; cv::Mat _backProject; cv::Rect _searchWindow; - cv::Mat _grayDepth; - - freenect_context* _freenectContext; - freenect_device* _freenectDevice; + +#ifdef HAVE_NITE + xn::Context _xnContext; +#endif }; Q_DECLARE_METATYPE(cv::Mat) From b4b09b007073dc612d0b17f12951d2e4da5ddef9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Jun 2013 17:31:49 -0700 Subject: [PATCH 14/63] Back up and running, now with OpenNI rather than libfreenect. --- interface/CMakeLists.txt | 2 +- interface/src/Webcam.cpp | 56 ++++++++++++++++++++++++++++------------ interface/src/Webcam.h | 10 +++++-- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 941a321c22..827a67cbff 100755 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -96,7 +96,7 @@ find_package(NITE) # let the source know that we have OpenNI/NITE for Kinect if (OPENNI_FOUND AND NITE_FOUND) - add_definitions(-DHAVE_NITE) + add_definitions(-DHAVE_OPENNI) include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS} ${NITE_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES} ${NITE_LIBRARIES}) endif (OPENNI_FOUND AND NITE_FOUND) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index e18c2c5aa8..805007ddc6 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -210,7 +210,7 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame())); } -FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) { +FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0) { } FrameGrabber::~FrameGrabber() { @@ -224,24 +224,38 @@ void FrameGrabber::reset() { } void FrameGrabber::grabFrame() { - if (_capture == 0 && !init()) { + if (!(_initialized || init())) { return; } int format = GL_BGR; - IplImage* image = cvQueryFrame(_capture); - if (image == 0) { - // try again later - QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection); - return; - } + Mat frame; - // make sure it's in the format we expect - if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || - image->origin != 0) { - printLog("Invalid webcam image format.\n"); - return; +#ifdef HAVE_OPENNI + if (_depthGenerator.IsValid()) { + _xnContext.WaitAnyUpdateAll(); + frame = Mat(_imageMetaData.YRes(), _imageMetaData.XRes(), CV_8UC3, (void*)_imageGenerator.GetImageMap()); + format = GL_RGB; + + Mat depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap()); + depth.convertTo(_grayDepthFrame, CV_8UC1, 256.0 / 2048.0); + } +#endif + + if (frame.empty()) { + IplImage* image = cvQueryFrame(_capture); + if (image == 0) { + // try again later + QMetaObject::invokeMethod(this, "grabFrame", Qt::QueuedConnection); + return; + } + // make sure it's in the format we expect + if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U || image->dataOrder != IPL_DATA_ORDER_PIXEL || + image->origin != 0) { + printLog("Invalid webcam image format.\n"); + return; + } + frame = image; } - Mat frame = image; // if we don't have a search window (yet), try using the face cascade int channels = 0; @@ -274,10 +288,12 @@ void FrameGrabber::grabFrame() { _searchWindow = faceRect.boundingRect(); } QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", - Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, Mat()), Q_ARG(cv::RotatedRect, faceRect)); + Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(cv::RotatedRect, faceRect)); } bool FrameGrabber::init() { + _initialized = true; + // load our face cascade switchToResourcesParentIfRequired(); if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) { @@ -286,9 +302,15 @@ bool FrameGrabber::init() { } // first try for a Kinect -#ifdef HAVE_NITE +#ifdef HAVE_OPENNI _xnContext.Init(); - + if (_depthGenerator.Create(_xnContext) == XN_STATUS_OK && _imageGenerator.Create(_xnContext) == XN_STATUS_OK) { + _depthGenerator.GetMetaData(_depthMetaData); + _imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24); + _imageGenerator.GetMetaData(_imageMetaData); + _xnContext.StartGeneratingAll(); + return true; + } #endif // next, an ordinary webcam diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index b9a2f0e6fe..660a79fc22 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -17,7 +17,7 @@ #include -#ifdef HAVE_NITE +#ifdef HAVE_OPENNI #include #endif @@ -92,6 +92,7 @@ private: bool init(); void updateHSVFrame(const cv::Mat& frame, int format); + bool _initialized; CvCapture* _capture; cv::CascadeClassifier _faceCascade; cv::Mat _hsvFrame; @@ -99,9 +100,14 @@ private: cv::SparseMat _histogram; cv::Mat _backProject; cv::Rect _searchWindow; + cv::Mat _grayDepthFrame; -#ifdef HAVE_NITE +#ifdef HAVE_OPENNI xn::Context _xnContext; + xn::DepthGenerator _depthGenerator; + xn::ImageGenerator _imageGenerator; + xn::DepthMetaData _depthMetaData; + xn::ImageMetaData _imageMetaData; #endif }; From 60bea894d83c3b44e9ec91b8a2c87a8aa060eaf9 Mon Sep 17 00:00:00 2001 From: Grayson Stebbins Date: Fri, 28 Jun 2013 18:12:04 -0700 Subject: [PATCH 15/63] UI improvements --- interface/src/Application.cpp | 16 ++++--- interface/src/Swatch.cpp | 78 +++++++++++++++++++++++++++++----- interface/src/Swatch.h | 4 ++ interface/src/ToolsPalette.cpp | 2 +- 4 files changed, 82 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 970344e41f..0293611f97 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -255,6 +255,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : initMenu(); + _swatch = new Swatch(_voxelPaintColor); + QRect available = desktop()->availableGeometry(); _window->resize(available.size()); _window->setVisible(true); @@ -1544,8 +1546,6 @@ void Application::init() { sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); - - _swatch = new Swatch(_voxelPaintColor); _palette.init(); _palette.addAction(_addVoxelMode, 0, 0); _palette.addAction(_deleteVoxelMode, 0, 1); @@ -2292,7 +2292,7 @@ void Application::displayOverlay() { _palette.render(_glWidget->width(), _glWidget->height()); - if (_eyedropperMode->isChecked()) { + if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value() != _swatch->getColor()) { QColor color(_voxelPaintColor->data().value()); TextRenderer textRenderer(SANS_FONT_FAMILY, -1, 100); const char* line1("Assign this color to a swatch"); @@ -2329,8 +2329,8 @@ void Application::displayOverlay() { glEnd(); glColor3f(1.0f, 1.0f, 1.0f); - textRenderer.draw(left + 74, top + 10, line1); - textRenderer.draw(left + 74, top + 30, line2); + textRenderer.draw(left + 74, top + 12, line1); + textRenderer.draw(left + 74, top + 27, line2); } glPopMatrix(); @@ -2863,7 +2863,8 @@ void Application::loadSettings(QSettings* settings) { settings->endGroup(); scanMenuBar(&Application::loadAction, settings); - getAvatar()->loadData(settings); + getAvatar()->loadData(settings); + _swatch->loadData(settings); } @@ -2885,8 +2886,11 @@ void Application::saveSettings(QSettings* settings) { settings->setValue("echoCancellation", _audio.isCancellingEcho()); settings->endGroup(); + + scanMenuBar(&Application::saveAction, settings); getAvatar()->saveData(settings); + _swatch->saveData(settings); } void Application::importSettings() { diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index 5989a2e512..ea3afa6c96 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -18,45 +18,102 @@ Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), _colors[7].setRgb(0, 0, 0); QPixmap map(16, 16); - map.fill(_colors[0]); + map.fill(_colors[_selected - 1]); _action->setData(_colors[_selected - 1]) ; _action->setIcon(map); } +QColor Swatch::getColor() { + return _colors[_selected - 1]; +} + +void Swatch::saveData(QSettings* settings) { + settings->beginGroup("Swatch"); + + for (int i(0); i < SWATCH_SIZE; ++i) { + QString name("R0"); + name[1] = '1' + i; + settings->setValue(name, _colors[i].red()); + name[0] = 'G'; + settings->setValue(name, _colors[i].green()); + name[0] = 'B'; + settings->setValue(name, _colors[i].blue()); + } + + settings->endGroup(); +} + +void Swatch::loadData(QSettings* settings) { + settings->beginGroup("Swatch"); + + _colors[0].setRgb(settings->value("R1", 128).toInt(), + settings->value("G1", 128).toInt(), + settings->value("B1", 128).toInt()); + _colors[1].setRgb(settings->value("R2", 128).toInt(), + settings->value("G2", 128).toInt(), + settings->value("B2", 128).toInt()); + _colors[2].setRgb(settings->value("R3", 128).toInt(), + settings->value("G3", 128).toInt(), + settings->value("B3", 128).toInt()); + _colors[3].setRgb(settings->value("R4", 128).toInt(), + settings->value("G4", 128).toInt(), + settings->value("B4", 128).toInt()); + _colors[4].setRgb(settings->value("R5", 128).toInt(), + settings->value("G5", 128).toInt(), + settings->value("B5", 128).toInt()); + _colors[5].setRgb(settings->value("R6", 128).toInt(), + settings->value("G6", 128).toInt(), + settings->value("B6", 128).toInt()); + _colors[6].setRgb(settings->value("R7", 128).toInt(), + settings->value("G7", 128).toInt(), + settings->value("B7", 128).toInt()); + _colors[7].setRgb(settings->value("R8", 128).toInt(), + settings->value("G8", 128).toInt(), + settings->value("B8", 128).toInt()); + + settings->endGroup(); +} + void Swatch::handleEvent(int key, bool getColor) { + int next(0); + switch (key) { case Qt::Key_1: - _selected = 1; + next = 1; break; case Qt::Key_2: - _selected = 2; + next = 2; break; case Qt::Key_3: - _selected = 3; + next = 3; break; case Qt::Key_4: - _selected = 4; + next = 4; break; case Qt::Key_5: - _selected = 5; + next = 5; break; case Qt::Key_6: - _selected = 6; + next = 6; break; case Qt::Key_7: - _selected = 7; + next = 7; break; case Qt::Key_8: - _selected = 8; + next = 8; break; default: break; } if (getColor) { - _colors[_selected - 1] = _action->data().value(); + if (_action->data().value() != _colors[_selected - 1]) { + _selected = next; + _colors[_selected - 1] = _action->data().value(); + } } else { + _selected = next; QPixmap map(16, 16); map.fill(_colors[_selected - 1]); _action->setData(_colors[_selected - 1]) ; @@ -121,5 +178,4 @@ void Swatch::render(int screenWidth, int screenHeight) { } glTranslated(0, 8*(_height - _margin) + _margin + 5, 0); - } diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h index 292b011882..bf40791a9d 100644 --- a/interface/src/Swatch.h +++ b/interface/src/Swatch.h @@ -16,6 +16,10 @@ class Swatch : public Tool { public: Swatch(QAction* action); + QColor getColor(); + void saveData(QSettings* settings); + void loadData(QSettings* settings); + void render(int screenWidth, int screenHeight); void handleEvent(int key, bool getColor); diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp index 570d9e3f2c..5c8ee22056 100644 --- a/interface/src/ToolsPalette.cpp +++ b/interface/src/ToolsPalette.cpp @@ -6,7 +6,7 @@ ToolsPalette::ToolsPalette() { // Load SVG - QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); + QSvgRenderer renderer(QString("/Users/graysonstebbins/Documents/hifi/interface/resources/images/hifi-interface-tools.svg")); // Prepare a QImage with desired characteritisc QImage image(124, 400, QImage::Format_ARGB32); From 45e13fda152c1fa682530c3b873b7e99cd09d778 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Mon, 1 Jul 2013 16:34:43 +0200 Subject: [PATCH 16/63] More UI improvements --- interface/src/Application.cpp | 106 +++++++++++++++++++++++++-------- interface/src/Application.h | 8 ++- interface/src/Swatch.cpp | 90 +++++++++++++++------------- interface/src/Swatch.h | 6 +- interface/src/Tool.cpp | 4 ++ interface/src/Tool.h | 1 + interface/src/ToolsPalette.cpp | 16 ++++- interface/src/ToolsPalette.h | 3 +- 8 files changed, 165 insertions(+), 69 deletions(-) mode change 100644 => 100755 interface/src/Application.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp old mode 100644 new mode 100755 index 0293611f97..fa1326f7e5 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -58,11 +58,13 @@ #include "Application.h" #include "InterfaceConfig.h" #include "LogDisplay.h" +#include "LeapManager.h" #include "OculusManager.h" #include "Util.h" #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" #include "Swatch.h" +#include "fvupdater.h" using namespace std; @@ -248,10 +250,28 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window->setCentralWidget(_glWidget); - // these are used, for example, to identify the application settings - setApplicationName("Interface"); - setOrganizationDomain("highfidelity.io"); - setOrganizationName("High Fidelity"); +#ifdef Q_WS_MAC + QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources"; +#else + QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources"; +#endif + + // read the ApplicationInfo.ini file for Name/Version/Domain information + QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat); + + // set the associated application properties + applicationInfo.beginGroup("INFO"); + + setApplicationName(applicationInfo.value("name").toString()); + setApplicationVersion(applicationInfo.value("version").toString()); + setOrganizationName(applicationInfo.value("organizationName").toString()); + setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + +#if defined(Q_WS_MAC) && defined(QT_NO_DEBUG) + // if this is a release OS X build use fervor to check for an update + FvUpdater::sharedUpdater()->SetFeedURL("https://s3-us-west-1.amazonaws.com/highfidelity/appcast.xml"); + FvUpdater::sharedUpdater()->CheckForUpdatesSilent(); +#endif initMenu(); @@ -845,9 +865,26 @@ void Application::wheelEvent(QWheelEvent* event) { } } +void sendPingPackets() { + + char agentTypesOfInterest[] = {AGENT_TYPE_VOXEL_SERVER, AGENT_TYPE_AUDIO_MIXER, AGENT_TYPE_AVATAR_MIXER}; + long long currentTime = usecTimestampNow(); + char pingPacket[1 + sizeof(currentTime)]; + pingPacket[0] = PACKET_HEADER_PING; + + memcpy(&pingPacket[1], ¤tTime, sizeof(currentTime)); + AgentList::getInstance()->broadcastToAgents((unsigned char*)pingPacket, 1 + sizeof(currentTime), agentTypesOfInterest, 3); + +} + // Every second, check the frame rates and other stuff void Application::timer() { gettimeofday(&_timerEnd, NULL); + + if (_testPing->isChecked()) { + sendPingPackets(); + } + _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); @@ -972,10 +1009,6 @@ void Application::editPreferences() { headCameraPitchYawScale->setValue(_headCameraPitchYawScale); form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); - QCheckBox* audioEchoCancellation = new QCheckBox(); - audioEchoCancellation->setChecked(_audio.isCancellingEcho()); - form->addRow("Audio Echo Cancellation", audioEchoCancellation); - QDoubleSpinBox* leanScale = new QDoubleSpinBox(); leanScale->setValue(_myAvatar.getLeanScale()); form->addRow("Lean Scale:", leanScale); @@ -997,7 +1030,6 @@ void Application::editPreferences() { QUrl url(avatarURL->text()); _myAvatar.getVoxels()->setVoxelURL(url); sendAvatarVoxelURLMessage(url); - _audio.setIsCancellingEcho( audioEchoCancellation->isChecked() ); _headCameraPitchYawScale = headCameraPitchYawScale->value(); _myAvatar.setLeanScale(leanScale->value()); _audioJitterBufferSamples = audioJitterBufferSamples->value(); @@ -1143,7 +1175,11 @@ void Application::decreaseVoxelSize() { void Application::increaseVoxelSize() { _mouseVoxelScale *= 2; } - + +void Application::resetSwatchColors() { + _swatch->reset(); +} + static QIcon createSwatchIcon(const QColor& color) { QPixmap map(16, 16); map.fill(color); @@ -1355,6 +1391,8 @@ void Application::initMenu() { (_gravityUse = optionsMenu->addAction("Use Gravity"))->setCheckable(true); _gravityUse->setChecked(true); _gravityUse->setShortcut(Qt::SHIFT | Qt::Key_G); + (_testPing = optionsMenu->addAction("Test Ping"))->setCheckable(true); + _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); @@ -1414,7 +1452,8 @@ void Application::initMenu() { 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, SLOT(chooseVoxelPaintColor()), Qt::META | Qt::Key_C); QColor paintColor(128, 128, 128); @@ -1697,7 +1736,11 @@ void Application::update(float deltaTime) { _touchAvgY - _touchDragStartedAvgY); } - // Read serial port interface devices + // Leap finger-sensing device + LeapManager::nextFrame(); + _myAvatar.getHand().setLeapFingers(LeapManager::getFingerPositions()); + + // Read serial port interface devices if (_serialHeadSensor.isActive()) { _serialHeadSensor.readData(deltaTime); } @@ -2294,11 +2337,11 @@ void Application::displayOverlay() { if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value() != _swatch->getColor()) { QColor color(_voxelPaintColor->data().value()); - TextRenderer textRenderer(SANS_FONT_FAMILY, -1, 100); + TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); const char* line1("Assign this color to a swatch"); const char* line2("by choosing a key from 1 to 8."); double step(0.05f); - int left((_glWidget->width() - 320)/2); + int left((_glWidget->width() - 300)/2); int top(_glWidget->height()/40.0f); double margin(10.0f); @@ -2308,10 +2351,10 @@ void Application::displayOverlay() { glVertex2f(left + margin*cos(a), top + margin*sin(a)); } for (double a(1.5f*M_PI); a < 2.0f*M_PI; a += step) { - glVertex2f(left + 300 + margin*cos(a), top + margin*sin(a)); + glVertex2f(left + 280 + margin*cos(a), top + margin*sin(a)); } for (double a(0.0f); a < 0.5f*M_PI; a += step) { - glVertex2f(left + 300 + margin*cos(a), top + 30 + margin*sin(a)); + glVertex2f(left + 280 + margin*cos(a), top + 30 + margin*sin(a)); } for (double a(0.5f*M_PI); a < 1.0f*M_PI; a += step) { glVertex2f(left + margin*cos(a), top + 30 + margin*sin(a)); @@ -2330,7 +2373,10 @@ void Application::displayOverlay() { glColor3f(1.0f, 1.0f, 1.0f); textRenderer.draw(left + 74, top + 12, line1); - textRenderer.draw(left + 74, top + 27, line2); + textRenderer.draw(left + 74, top + 28, line2); + } + else { + _swatch->checkColor(); } glPopMatrix(); @@ -2343,7 +2389,24 @@ void Application::displayStats() { sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps", _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats); - + + if (_testPing->isChecked()) { + int pingAudio = 0, pingAvatar = 0, pingVoxel = 0; + + AgentList *agentList = AgentList::getInstance(); + Agent *audioMixerAgent = agentList->soloAgentOfType(AGENT_TYPE_AUDIO_MIXER); + Agent *avatarMixerAgent = agentList->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER); + Agent *voxelServerAgent = agentList->soloAgentOfType(AGENT_TYPE_VOXEL_SERVER); + + pingAudio = audioMixerAgent ? audioMixerAgent->getPingMs() : 0; + pingAvatar = avatarMixerAgent ? avatarMixerAgent->getPingMs() : 0; + pingVoxel = voxelServerAgent ? voxelServerAgent->getPingMs() : 0; + + char pingStats[200]; + sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d ", pingAudio, pingAvatar, pingVoxel); + drawtext(10, statsVerticalOffset + 35, 0.10f, 0, 1.0, 0, pingStats); + } + std::stringstream voxelStats; voxelStats.precision(4); voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K"; @@ -2384,6 +2447,7 @@ void Application::displayStats() { } drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, avatarMixerStats); + drawtext(10, statsVerticalOffset + 450, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str()); if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups @@ -2858,9 +2922,6 @@ void Application::loadSettings(QSettings* settings) { _viewFrustumOffsetDistance = loadSetting(settings, "viewFrustumOffsetDistance", 0.0f); _viewFrustumOffsetUp = loadSetting(settings, "viewFrustumOffsetUp" , 0.0f); settings->endGroup(); - settings->beginGroup("Audio Echo Cancellation"); - _audio.setIsCancellingEcho(settings->value("enabled", false).toBool()); - settings->endGroup(); scanMenuBar(&Application::loadAction, settings); getAvatar()->loadData(settings); @@ -2882,9 +2943,6 @@ void Application::saveSettings(QSettings* settings) { settings->setValue("viewFrustumOffsetDistance", _viewFrustumOffsetDistance); settings->setValue("viewFrustumOffsetUp", _viewFrustumOffsetUp); settings->endGroup(); - settings->beginGroup("Audio"); - settings->setValue("echoCancellation", _audio.isCancellingEcho()); - settings->endGroup(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 8665bfd325..ca60af97c9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -9,9 +9,9 @@ #ifndef __interface__Application__ #define __interface__Application__ +#include #include #include -#include #include #include @@ -33,6 +33,7 @@ #include "ViewFrustum.h" #include "VoxelSystem.h" #include "Webcam.h" +#include "renderer/GeometryCache.h" #include "ui/ChatEntry.h" #include "ToolsPalette.h" #include "Swatch.h" @@ -91,6 +92,7 @@ public: bool shouldDynamicallySetJitterBuffer() { return _audioJitterBufferSamples == 0; } QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } + GeometryCache* getGeometryCache() { return &_geometryCache; } private slots: @@ -132,6 +134,7 @@ private slots: void updateVoxelModeActions(); void decreaseVoxelSize(); void increaseVoxelSize(); + void resetSwatchColors(); void chooseVoxelPaintColor(); void loadSettings(QSettings* set = NULL); void saveSettings(QSettings* set = NULL); @@ -199,6 +202,7 @@ private: QAction* _showHeadMouse; // Whether the have the mouse near edge of screen move your view QAction* _transmitterDrives; // Whether to have Transmitter data move/steer the Avatar QAction* _gravityUse; // Whether gravity is on or not + QAction* _testPing; // Whether to display ping or not QAction* _renderVoxels; // Whether to render voxels QAction* _renderVoxelTextures; // Whether to render noise textures on voxels QAction* _renderStarsOn; // Whether to display the stars @@ -320,6 +324,8 @@ private: int _scaleInLocation; int _hmdWarpParamLocation; + GeometryCache _geometryCache; + #ifndef _WIN32 Audio _audio; #endif diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index ea3afa6c96..cdfde34212 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -4,18 +4,30 @@ Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), _selected(1), _margin(4), - _textRenderer(SANS_FONT_FAMILY, -1, 100) { + _textRenderer(MONO_FONT_FAMILY, 10, 100) { _width = 62; _height = 30; +} - _colors[0].setRgb(128, 128, 128); - _colors[1].setRgb(255, 0, 0); - _colors[2].setRgb(0, 255, 0); - _colors[3].setRgb(0, 0, 255); - _colors[4].setRgb(255, 0, 255); - _colors[5].setRgb(255, 255, 0); - _colors[6].setRgb(0, 255, 255); - _colors[7].setRgb(0, 0, 0); +void Swatch::reset() { + _colors[0].setRgb(237, 175, 0); + _colors[1].setRgb(161, 211, 72); + _colors[2].setRgb(51, 204, 204); + _colors[3].setRgb(63, 169, 245); + _colors[4].setRgb(193, 99, 122); + _colors[5].setRgb(255, 54, 69); + _colors[6].setRgb(124, 36, 36); + _colors[7].setRgb(63, 35, 19); +} + +QColor Swatch::getColor() { + return _colors[_selected - 1]; +} + +void Swatch::checkColor() { + if (_action->data().value() == _colors[_selected - 1]) { + return; + } QPixmap map(16, 16); map.fill(_colors[_selected - 1]); @@ -23,15 +35,11 @@ Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), _action->setIcon(map); } -QColor Swatch::getColor() { - return _colors[_selected - 1]; -} - void Swatch::saveData(QSettings* settings) { settings->beginGroup("Swatch"); for (int i(0); i < SWATCH_SIZE; ++i) { - QString name("R0"); + QString name("R1"); name[1] = '1' + i; settings->setValue(name, _colors[i].red()); name[0] = 'G'; @@ -45,33 +53,35 @@ void Swatch::saveData(QSettings* settings) { void Swatch::loadData(QSettings* settings) { settings->beginGroup("Swatch"); - - _colors[0].setRgb(settings->value("R1", 128).toInt(), - settings->value("G1", 128).toInt(), - settings->value("B1", 128).toInt()); - _colors[1].setRgb(settings->value("R2", 128).toInt(), - settings->value("G2", 128).toInt(), - settings->value("B2", 128).toInt()); - _colors[2].setRgb(settings->value("R3", 128).toInt(), - settings->value("G3", 128).toInt(), - settings->value("B3", 128).toInt()); - _colors[3].setRgb(settings->value("R4", 128).toInt(), - settings->value("G4", 128).toInt(), - settings->value("B4", 128).toInt()); - _colors[4].setRgb(settings->value("R5", 128).toInt(), - settings->value("G5", 128).toInt(), - settings->value("B5", 128).toInt()); - _colors[5].setRgb(settings->value("R6", 128).toInt(), - settings->value("G6", 128).toInt(), - settings->value("B6", 128).toInt()); - _colors[6].setRgb(settings->value("R7", 128).toInt(), - settings->value("G7", 128).toInt(), - settings->value("B7", 128).toInt()); - _colors[7].setRgb(settings->value("R8", 128).toInt(), - settings->value("G8", 128).toInt(), - settings->value("B8", 128).toInt()); - + + _colors[0].setRgb(settings->value("R1", 237).toInt(), + settings->value("G1", 175).toInt(), + settings->value("B1", 0).toInt()); + _colors[1].setRgb(settings->value("R2", 161).toInt(), + settings->value("G2", 211).toInt(), + settings->value("B2", 72).toInt()); + _colors[2].setRgb(settings->value("R3", 51).toInt(), + settings->value("G3", 204).toInt(), + settings->value("B3", 204).toInt()); + _colors[3].setRgb(settings->value("R4", 63).toInt(), + settings->value("G4", 169).toInt(), + settings->value("B4", 245).toInt()); + _colors[4].setRgb(settings->value("R5", 193).toInt(), + settings->value("G5", 99).toInt(), + settings->value("B5", 122).toInt()); + _colors[5].setRgb(settings->value("R6", 255).toInt(), + settings->value("G6", 54).toInt(), + settings->value("B6", 69).toInt()); + _colors[6].setRgb(settings->value("R7", 124).toInt(), + settings->value("G7", 36).toInt(), + settings->value("B7", 36).toInt()); + _colors[7].setRgb(settings->value("R8", 63).toInt(), + settings->value("G8", 35).toInt(), + settings->value("B8", 19).toInt()); + settings->endGroup(); + + checkColor(); } void Swatch::handleEvent(int key, bool getColor) { diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h index bf40791a9d..35da266078 100644 --- a/interface/src/Swatch.h +++ b/interface/src/Swatch.h @@ -16,10 +16,14 @@ class Swatch : public Tool { public: Swatch(QAction* action); + + QColor getColor(); + void checkColor(); void saveData(QSettings* settings); void loadData(QSettings* settings); - + void reset(); + void render(int screenWidth, int screenHeight); void handleEvent(int key, bool getColor); diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp index 67b0262f7d..c8fc4430f6 100644 --- a/interface/src/Tool.cpp +++ b/interface/src/Tool.cpp @@ -12,6 +12,10 @@ Tool::Tool(QAction *action, GLuint texture, int x, int y) : _texture(texture), _height(40) { } +bool Tool::isActive() { + return _action->isChecked(); +} + void Tool::render(int screenWidth, int screenHeight) { glEnable(GL_TEXTURE_2D); diff --git a/interface/src/Tool.h b/interface/src/Tool.h index 8fdc723fd9..4b564e8578 100644 --- a/interface/src/Tool.h +++ b/interface/src/Tool.h @@ -23,6 +23,7 @@ class Tool { public: Tool(QAction *action, GLuint texture, int x, int y); + bool isActive(); virtual void render(int screenWidth, int screenHeight); protected: diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp index 5c8ee22056..5b0f550f15 100644 --- a/interface/src/ToolsPalette.cpp +++ b/interface/src/ToolsPalette.cpp @@ -3,10 +3,12 @@ #include #include #include +#include ToolsPalette::ToolsPalette() { // Load SVG - QSvgRenderer renderer(QString("/Users/graysonstebbins/Documents/hifi/interface/resources/images/hifi-interface-tools.svg")); + switchToResourcesParentIfRequired(); + QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); // Prepare a QImage with desired characteritisc QImage image(124, 400, QImage::Format_ARGB32); @@ -52,8 +54,18 @@ void ToolsPalette::render(int screenWidth, int screenHeight) { glPushMatrix(); glTranslated(_left, _top, 0); + bool show(false); for (unsigned int i(0); i < _tools.size(); ++i) { - _tools[i]->render(screenWidth, screenHeight); + if (_tools[i]->isActive()) { + show = true; + break; + } + } + + if (show) { + for (unsigned int i(0); i < _tools.size(); ++i) { + _tools[i]->render(screenWidth, screenHeight); + } } glPopMatrix(); diff --git a/interface/src/ToolsPalette.h b/interface/src/ToolsPalette.h index 4856048a01..a1c99d63f6 100644 --- a/interface/src/ToolsPalette.h +++ b/interface/src/ToolsPalette.h @@ -8,7 +8,8 @@ #ifndef __interface__ToolsPalette__ #define __interface__ToolsPalette__ -#include +#include "Tool.h" +#include "Swatch.h" #include From a1cab3ddc6e7a682806d05597610afc824134490 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Jul 2013 09:48:36 -0700 Subject: [PATCH 17/63] Working on getting skeleton data. --- interface/src/Webcam.cpp | 18 +++++++++++++++++- interface/src/Webcam.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 805007ddc6..8b7b0b7982 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -20,6 +20,7 @@ using namespace cv; using namespace std; +using namespace xn; // register OpenCV matrix type with Qt metatype system int matMetaType = qRegisterMetaType("cv::Mat"); @@ -291,6 +292,15 @@ void FrameGrabber::grabFrame() { Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(cv::RotatedRect, faceRect)); } +#ifdef HAVE_OPENNI +static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { + printLog("Found user %d.\n", id); +} +static void XN_CALLBACK_TYPE lostUser(UserGenerator& generator, XnUserID id, void* cookie) { + printLog("Lost user %d.\n", id); +} +#endif + bool FrameGrabber::init() { _initialized = true; @@ -304,10 +314,16 @@ bool FrameGrabber::init() { // first try for a Kinect #ifdef HAVE_OPENNI _xnContext.Init(); - if (_depthGenerator.Create(_xnContext) == XN_STATUS_OK && _imageGenerator.Create(_xnContext) == XN_STATUS_OK) { + if (_depthGenerator.Create(_xnContext) == XN_STATUS_OK && _imageGenerator.Create(_xnContext) == XN_STATUS_OK && + _userGenerator.Create(_xnContext) == XN_STATUS_OK && + _userGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) { _depthGenerator.GetMetaData(_depthMetaData); _imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24); _imageGenerator.GetMetaData(_imageMetaData); + + XnCallbackHandle userCallbacks; + _userGenerator.RegisterUserCallbacks(newUser, lostUser, 0, userCallbacks); + _xnContext.StartGeneratingAll(); return true; } diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 660a79fc22..89ece788c7 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -106,6 +106,7 @@ private: xn::Context _xnContext; xn::DepthGenerator _depthGenerator; xn::ImageGenerator _imageGenerator; + xn::UserGenerator _userGenerator; xn::DepthMetaData _depthMetaData; xn::ImageMetaData _imageMetaData; #endif From 182f4e70af265c5b132637f90bd2201f1ac0f73f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Jul 2013 15:01:36 -0700 Subject: [PATCH 18/63] More work on skeleton tracking. --- cmake/modules/FindNITE.cmake | 44 ----------- interface/CMakeLists.txt | 9 +-- interface/src/Webcam.cpp | 142 +++++++++++++++++++++++++++++++---- interface/src/Webcam.h | 19 ++++- 4 files changed, 148 insertions(+), 66 deletions(-) delete mode 100644 cmake/modules/FindNITE.cmake diff --git a/cmake/modules/FindNITE.cmake b/cmake/modules/FindNITE.cmake deleted file mode 100644 index 301ed651ee..0000000000 --- a/cmake/modules/FindNITE.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Find the NITE library -# -# You must provide an NITE_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# NITE_FOUND - system found NITE -# NITE_INCLUDE_DIRS - the NITE include directory -# NITE_LIBRARIES - Link this to use NITE -# -# Created on 6/28/2013 by Andrzej Kapolka -# Copyright (c) 2013 High Fidelity -# - -if (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) - # in cache already - set(NITE_FOUND TRUE) -else (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) - find_path(NITE_INCLUDE_DIRS XnVNite.h /usr/include/nite) - - if (APPLE) - find_library(NITE_LIBRARIES libXnVNite_1_5_2.dylib /usr/lib) - elseif (UNIX) - find_library(NITE_LIBRARIES libXnVNite_1_5_2.so /usr/lib) - endif () - - if (NITE_INCLUDE_DIRS AND NITE_LIBRARIES) - set(NITE_FOUND TRUE) - endif (NITE_INCLUDE_DIRS AND NITE_LIBRARIES) - - if (NITE_FOUND) - if (NOT NITE_FIND_QUIETLY) - message(STATUS "Found NITE: ${NITE_LIBRARIES}") - endif (NOT NITE_FIND_QUIETLY) - else (NITE_FOUND) - if (NITE_FIND_REQUIRED) - message(FATAL_ERROR "Could not find NITE") - endif (NITE_FIND_REQUIRED) - endif (NITE_FOUND) - - # show the NITE_INCLUDE_DIRS and NITE_LIBRARIES variables only in the advanced view - mark_as_advanced(NITE_INCLUDE_DIRS NITE_LIBRARIES) - -endif (NITE_LIBRARIES AND NITE_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 98045fb63c..0b8d28f266 100755 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -96,14 +96,13 @@ find_package(OpenCV) find_package(ZLIB) find_package(UVCCameraControl) find_package(OpenNI) -find_package(NITE) # let the source know that we have OpenNI/NITE for Kinect -if (OPENNI_FOUND AND NITE_FOUND) +if (OPENNI_FOUND) add_definitions(-DHAVE_OPENNI) - include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS} ${NITE_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES} ${NITE_LIBRARIES}) -endif (OPENNI_FOUND AND NITE_FOUND) + include_directories(SYSTEM ${OPENNI_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES}) +endif (OPENNI_FOUND) # include headers for interface and InterfaceConfig. include_directories( diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index ac219de06b..9b554b6044 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -22,7 +22,8 @@ using namespace cv; using namespace std; using namespace xn; -// register OpenCV matrix type with Qt metatype system +// register types with Qt metatype system +int jointVectorMetaType = qRegisterMetaType("JointVector"); int matMetaType = qRegisterMetaType("cv::Mat"); int rotatedRectMetaType = qRegisterMetaType("cv::RotatedRect"); @@ -95,9 +96,28 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) { glTexCoord2f(0, 1); glVertex2f(depthLeft, top); glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + if (!_joints.isEmpty()) { + glColor3f(1.0f, 0.0f, 0.0f); + glPointSize(4.0f); + glBegin(GL_POINTS); + float projectedScale = PREVIEW_HEIGHT / (float)_depthHeight; + foreach (const Joint& joint, _joints) { + if (joint.isValid) { + glVertex2f(depthLeft + joint.projected.x * projectedScale, + top - PREVIEW_HEIGHT + joint.projected.y * projectedScale); + } + } + glEnd(); + glPointSize(1.0f); + } + } else { + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); glBegin(GL_LINE_LOOP); Point2f facePoints[4]; @@ -124,7 +144,7 @@ Webcam::~Webcam() { delete _grabber; } -void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const RotatedRect& faceRect) { +void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const RotatedRect& faceRect, const JointVector& joints) { IplImage image = frame; glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3); if (_frameTextureID == 0) { @@ -160,8 +180,9 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glBindTexture(GL_TEXTURE_2D, 0); - // store our face rect, update our frame count for fps computation + // store our face rect and joints, update our frame count for fps computation _faceRect = faceRect; + _joints = joints; _frameCount++; const int MAX_FPS = 60; @@ -224,12 +245,80 @@ void FrameGrabber::reset() { _searchWindow = cv::Rect(0, 0, 0, 0); } +#ifdef HAVE_OPENNI +static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { + switch (joint) { + case XN_SKEL_HEAD: return AVATAR_JOINT_HEAD_BASE; + case XN_SKEL_NECK: return AVATAR_JOINT_NECK_BASE; + case XN_SKEL_TORSO: return AVATAR_JOINT_TORSO; + case XN_SKEL_WAIST: return AVATAR_JOINT_PELVIS; + case XN_SKEL_LEFT_COLLAR: return AVATAR_JOINT_LEFT_COLLAR; + case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_LEFT_SHOULDER; + case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_LEFT_ELBOW; + case XN_SKEL_LEFT_WRIST: return AVATAR_JOINT_LEFT_WRIST; + case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_NULL; + case XN_SKEL_LEFT_FINGERTIP: return AVATAR_JOINT_LEFT_FINGERTIPS; + case XN_SKEL_RIGHT_COLLAR: return AVATAR_JOINT_RIGHT_COLLAR; + case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_RIGHT_SHOULDER; + case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_RIGHT_ELBOW; + case XN_SKEL_RIGHT_WRIST: return AVATAR_JOINT_RIGHT_WRIST; + case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_NULL; + case XN_SKEL_RIGHT_FINGERTIP: return AVATAR_JOINT_RIGHT_FINGERTIPS; + case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_LEFT_HIP; + case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_LEFT_KNEE; + case XN_SKEL_LEFT_ANKLE: return AVATAR_JOINT_LEFT_HEEL; + case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_LEFT_TOES; + case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_RIGHT_HIP; + case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; + case XN_SKEL_RIGHT_ANKLE: return AVATAR_JOINT_RIGHT_HEEL; + case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_RIGHT_TOES; + } +} + +static glm::vec3 xnToGLM(const XnVector3D& vector) { + return glm::vec3(vector.X, vector.Y, vector.Z); +} + +static glm::mat3 xnToGLM(const XnMatrix3X3& matrix) { + return glm::mat3( + matrix.elements[0], matrix.elements[1], matrix.elements[2], + matrix.elements[3], matrix.elements[4], matrix.elements[5], + matrix.elements[6], matrix.elements[7], matrix.elements[8]); +} + +static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { + printLog("Found user %d.\n", id); + generator.GetSkeletonCap().RequestCalibration(id, true); +} + +static void XN_CALLBACK_TYPE lostUser(UserGenerator& generator, XnUserID id, void* cookie) { + printLog("Lost user %d.\n", id); +} + +static void XN_CALLBACK_TYPE calibrationStarted(SkeletonCapability& capability, XnUserID id, void* cookie) { + printLog("Calibration started for user %d.\n", id); +} + +static void XN_CALLBACK_TYPE calibrationCompleted(SkeletonCapability& capability, + XnUserID id, XnCalibrationStatus status, void* cookie) { + if (status == XN_CALIBRATION_STATUS_OK) { + printLog("Calibration completed for user %d.\n", id); + capability.StartTracking(id); + + } else { + printLog("Calibration failed to user %d.\n", id); + capability.RequestCalibration(id, true); + } +} +#endif + void FrameGrabber::grabFrame() { if (!(_initialized || init())) { return; } int format = GL_BGR; Mat frame; + JointVector joints; #ifdef HAVE_OPENNI if (_depthGenerator.IsValid()) { @@ -239,6 +328,31 @@ void FrameGrabber::grabFrame() { Mat depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap()); depth.convertTo(_grayDepthFrame, CV_8UC1, 256.0 / 2048.0); + + XnUserID userID; + XnUInt16 userCount = 1; + _userGenerator.GetUsers(&userID, userCount); + if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(userID)) { + joints.resize(NUM_AVATAR_JOINTS); + const int MAX_ACTIVE_JOINTS = 16; + XnSkeletonJoint activeJoints[MAX_ACTIVE_JOINTS]; + XnUInt16 activeJointCount = MAX_ACTIVE_JOINTS; + _userGenerator.GetSkeletonCap().EnumerateActiveJoints(activeJoints, activeJointCount); + XnSkeletonJointTransformation transform; + for (int i = 0; i < activeJointCount; i++) { + AvatarJointID avatarJoint = xnToAvatarJoint(activeJoints[i]); + if (avatarJoint == AVATAR_JOINT_NULL) { + continue; + } + _userGenerator.GetSkeletonCap().GetSkeletonJoint(userID, activeJoints[i], transform); + XnVector3D projected; + _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); + Joint joint = { true, xnToGLM(transform.position.position), + glm::quat_cast(xnToGLM(transform.orientation.orientation)), + xnToGLM(projected) }; + joints[avatarJoint] = joint; + } + } } #endif @@ -289,18 +403,10 @@ void FrameGrabber::grabFrame() { _searchWindow = faceRect.boundingRect(); } QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", - Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(cv::RotatedRect, faceRect)); + Q_ARG(cv::Mat, frame), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), + Q_ARG(cv::RotatedRect, faceRect), Q_ARG(JointVector, joints)); } -#ifdef HAVE_OPENNI -static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { - printLog("Found user %d.\n", id); -} -static void XN_CALLBACK_TYPE lostUser(UserGenerator& generator, XnUserID id, void* cookie) { - printLog("Lost user %d.\n", id); -} -#endif - bool FrameGrabber::init() { _initialized = true; @@ -321,8 +427,12 @@ bool FrameGrabber::init() { _imageGenerator.SetPixelFormat(XN_PIXEL_FORMAT_RGB24); _imageGenerator.GetMetaData(_imageMetaData); - XnCallbackHandle userCallbacks; + 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); _xnContext.StartGeneratingAll(); return true; diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 89ece788c7..99ec424853 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -12,8 +12,10 @@ #include #include #include +#include #include +#include #include @@ -28,6 +30,9 @@ class QImage; struct CvCapture; class FrameGrabber; +class Joint; + +typedef QVector JointVector; class Webcam : public QObject { Q_OBJECT @@ -47,7 +52,8 @@ public: public slots: void setEnabled(bool enabled); - void setFrame(const cv::Mat& video, int format, const cv::Mat& depth, const cv::RotatedRect& faceRect); + void setFrame(const cv::Mat& video, int format, const cv::Mat& depth, + const cv::RotatedRect& faceRect, const JointVector& joints); private: @@ -64,6 +70,7 @@ private: GLuint _depthTextureID; cv::RotatedRect _faceRect; cv::RotatedRect _initialFaceRect; + JointVector _joints; long long _startTimestamp; int _frameCount; @@ -112,6 +119,16 @@ private: #endif }; +class Joint { +public: + + bool isValid; + glm::vec3 position; + glm::quat orientation; + glm::vec3 projected; +}; + +Q_DECLARE_METATYPE(JointVector) Q_DECLARE_METATYPE(cv::Mat) Q_DECLARE_METATYPE(cv::RotatedRect) From da6a9a342ac3082c1bd5221d570a35aca23c2f08 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Jul 2013 15:07:10 -0700 Subject: [PATCH 19/63] Need to wrap the using statement in ifdef, too. --- interface/src/Webcam.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 9b554b6044..75224c183d 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -20,7 +20,10 @@ using namespace cv; using namespace std; + +#ifdef HAVE_OPENNI using namespace xn; +#endif // register types with Qt metatype system int jointVectorMetaType = qRegisterMetaType("JointVector"); From 095782af450f2293b5b1dc8ff968250a6473bdf7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Jul 2013 17:46:05 -0700 Subject: [PATCH 20/63] Smooth the joints, get the head position/orientation from there. --- interface/src/Application.cpp | 4 +- interface/src/Avatar.cpp | 8 ++-- interface/src/Avatar.h | 2 +- interface/src/Webcam.cpp | 79 +++++++++++++++++++++-------------- interface/src/Webcam.h | 1 + 5 files changed, 55 insertions(+), 39 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8373f8f440..47182634da 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1803,8 +1803,8 @@ void Application::update(float deltaTime) { void Application::updateAvatar(float deltaTime) { - // Update my avatar's head position from gyros and/or webcam - _myAvatar.updateHeadFromGyrosAndOrWebcam(); + // Update my avatar's state position from gyros and/or webcam + _myAvatar.updateFromGyrosAndOrWebcam(); if (_serialHeadSensor.isActive()) { diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 277cca906e..52f3e98a70 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -285,10 +285,10 @@ void Avatar::reset() { } // Update avatar head rotation with sensor data -void Avatar::updateHeadFromGyrosAndOrWebcam() { - const float AMPLIFY_PITCH = 1.f; - const float AMPLIFY_YAW = 1.f; - const float AMPLIFY_ROLL = 1.f; +void Avatar::updateFromGyrosAndOrWebcam() { + const float AMPLIFY_PITCH = 2.f; + const float AMPLIFY_YAW = 2.f; + const float AMPLIFY_ROLL = 2.f; SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor(); Webcam* webcam = Application::getInstance()->getWebcam(); diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index 86c333dad4..19b3077369 100755 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -87,7 +87,7 @@ public: void reset(); void simulate(float deltaTime, Transmitter* transmitter); void updateThrust(float deltaTime, Transmitter * transmitter); - void updateHeadFromGyrosAndOrWebcam(); + void updateFromGyrosAndOrWebcam(); void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight); void updateFromTouch(float touchAvgDistX, float touchAvgDistY); void addBodyYaw(float y) {_bodyYaw += y;}; diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 75224c183d..ca23c940c9 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -199,33 +199,46 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota } _lastFrameTimestamp = now; - // roll is just the angle of the face rect (correcting for 180 degree rotations) - float roll = faceRect.angle; - if (roll < -90.0f) { - roll += 180.0f; + // see if we have joint data + if (!_joints.isEmpty()) { + for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { + const float JOINT_SMOOTHING = 0.95f; + _estimatedJoints[i].position = glm::mix(_joints[i].position - _joints[AVATAR_JOINT_TORSO].position, + _estimatedJoints[i].position, JOINT_SMOOTHING); + _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, _estimatedJoints[i].orientation, JOINT_SMOOTHING); + } + _estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].orientation); + _estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position; - } else if (roll > 90.0f) { - roll -= 180.0f; - } - const float ROTATION_SMOOTHING = 0.95f; - _estimatedRotation.z = glm::mix(roll, _estimatedRotation.z, ROTATION_SMOOTHING); - - // determine position based on translation and scaling of the face rect - if (_initialFaceRect.size.area() == 0) { - _initialFaceRect = faceRect; - _estimatedPosition = glm::vec3(); - } else { - float proportion = sqrtf(_initialFaceRect.size.area() / (float)faceRect.size.area()); - const float DISTANCE_TO_CAMERA = 0.333f; - const float POSITION_SCALE = 0.5f; - float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA; - glm::vec3 position = glm::vec3( - (faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _frameWidth, - (faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _frameWidth, - z); - const float POSITION_SMOOTHING = 0.95f; - _estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING); + // roll is just the angle of the face rect (correcting for 180 degree rotations) + float roll = faceRect.angle; + if (roll < -90.0f) { + roll += 180.0f; + + } else if (roll > 90.0f) { + roll -= 180.0f; + } + const float ROTATION_SMOOTHING = 0.95f; + _estimatedRotation.z = glm::mix(roll, _estimatedRotation.z, ROTATION_SMOOTHING); + + // determine position based on translation and scaling of the face rect + if (_initialFaceRect.size.area() == 0) { + _initialFaceRect = faceRect; + _estimatedPosition = glm::vec3(); + + } else { + float proportion = sqrtf(_initialFaceRect.size.area() / (float)faceRect.size.area()); + const float DISTANCE_TO_CAMERA = 0.333f; + const float POSITION_SCALE = 0.5f; + float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA; + glm::vec3 position = glm::vec3( + (faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _frameWidth, + (faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _frameWidth, + z); + const float POSITION_SMOOTHING = 0.95f; + _estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING); + } } // note that we have data @@ -279,14 +292,16 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { } static glm::vec3 xnToGLM(const XnVector3D& vector) { - return glm::vec3(vector.X, vector.Y, vector.Z); + const float METERS_PER_MM = 1.0f / 1000.0f; + return glm::vec3(vector.X, vector.Y, -vector.Z) * METERS_PER_MM; } -static glm::mat3 xnToGLM(const XnMatrix3X3& matrix) { - return glm::mat3( - matrix.elements[0], matrix.elements[1], matrix.elements[2], - matrix.elements[3], matrix.elements[4], matrix.elements[5], - matrix.elements[6], matrix.elements[7], matrix.elements[8]); +static glm::quat xnToGLM(const XnMatrix3X3& matrix) { + glm::quat rotation = glm::quat_cast(glm::mat3( + matrix.elements[0], matrix.elements[3], matrix.elements[6], + matrix.elements[1], matrix.elements[4], matrix.elements[7], + matrix.elements[2], matrix.elements[5], matrix.elements[8])); + return glm::quat(rotation.w, rotation.x, rotation.y, -rotation.z); } static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { @@ -351,7 +366,7 @@ void FrameGrabber::grabFrame() { XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); Joint joint = { true, xnToGLM(transform.position.position), - glm::quat_cast(xnToGLM(transform.orientation.orientation)), + xnToGLM(transform.orientation.orientation), xnToGLM(projected) }; joints[avatarJoint] = joint; } diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 99ec424853..b5d52dc3a2 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -79,6 +79,7 @@ private: glm::vec3 _estimatedPosition; glm::vec3 _estimatedRotation; + JointVector _estimatedJoints; }; class FrameGrabber : public QObject { From d2e9686c816ef902f22426b62133566d367d5b53 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 1 Jul 2013 17:52:17 -0700 Subject: [PATCH 21/63] Need to resize this. --- interface/src/Webcam.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index ca23c940c9..4788472555 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -201,6 +201,7 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota // see if we have joint data if (!_joints.isEmpty()) { + _estimatedJoints.resize(NUM_AVATAR_JOINTS); for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { const float JOINT_SMOOTHING = 0.95f; _estimatedJoints[i].position = glm::mix(_joints[i].position - _joints[AVATAR_JOINT_TORSO].position, From 05a77bf373235cd514ee8ea731a75b43a0a96007 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 2 Jul 2013 09:29:11 +0200 Subject: [PATCH 22/63] Forgot to add .svg file --- .../resources/images/hifi-interface-tools.svg | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 interface/resources/images/hifi-interface-tools.svg diff --git a/interface/resources/images/hifi-interface-tools.svg b/interface/resources/images/hifi-interface-tools.svg new file mode 100644 index 0000000000..60b5a02fba --- /dev/null +++ b/interface/resources/images/hifi-interface-tools.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f3ab3b6c8388a9d683213b3373caa285ebe6bded Mon Sep 17 00:00:00 2001 From: tosh Date: Tue, 2 Jul 2013 12:41:34 +0200 Subject: [PATCH 23/63] prevents voxel data from being sent or received (read: requested) when voxel rendering is disabled also adds a menu option under "Tools" to enable/disable the Oscilloscope --- interface/src/Application.cpp | 26 +++++++++++++++++--------- interface/src/Application.h | 4 +++- interface/src/BandwidthMeter.cpp | 7 +++++++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fb63fe2daf..4c57c67c3c 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -483,8 +483,7 @@ void Application::resizeGL(int width, int height) { void Application::broadcastToAgents(unsigned char* data, size_t bytes, const char type) { - int n = AgentList::getInstance()->broadcastToAgents(data, bytes, &type, 1); - + Application* self = getInstance(); BandwidthMeter::ChannelIndex channel; switch (type) { case AGENT_TYPE_AVATAR: @@ -493,11 +492,15 @@ void Application::broadcastToAgents(unsigned char* data, size_t bytes, const cha break; case AGENT_TYPE_VOXEL_SERVER: channel = BandwidthMeter::VOXELS; - break; + if (self->_renderVoxels->isChecked()) { + break; + } default: return; } - getInstance()->_bandwidthMeter.outputStream(channel).updateValue(n * bytes); + int n = AgentList::getInstance()->broadcastToAgents(data, bytes, & type, 1); +broadcastDone: + self->_bandwidthMeter.outputStream(channel).updateValue(n * bytes); } void Application::sendVoxelServerAddScene() { @@ -875,15 +878,17 @@ void Application::wheelEvent(QWheelEvent* event) { } } -void sendPingPackets() { +void Application::sendPingPackets() { char agentTypesOfInterest[] = {AGENT_TYPE_VOXEL_SERVER, AGENT_TYPE_AUDIO_MIXER, AGENT_TYPE_AVATAR_MIXER}; long long currentTime = usecTimestampNow(); - char pingPacket[1 + sizeof(currentTime)]; + unsigned char pingPacket[1 + sizeof(currentTime)]; pingPacket[0] = PACKET_HEADER_PING; memcpy(&pingPacket[1], ¤tTime, sizeof(currentTime)); - AgentList::getInstance()->broadcastToAgents((unsigned char*)pingPacket, 1 + sizeof(currentTime), agentTypesOfInterest, 3); + for (int i = 0; i < sizeof(agentTypesOfInterest) / sizeof(char); ++i) { + getInstance()->broadcastToAgents(pingPacket, 1 + sizeof(currentTime), agentTypesOfInterest[i]); + } } @@ -1466,6 +1471,8 @@ void Application::initMenu() { (_logOn = toolsMenu->addAction("Log"))->setCheckable(true); _logOn->setChecked(false); _logOn->setShortcut(Qt::CTRL | Qt::Key_L); + (_oscilloscopeOn = toolsMenu->addAction("Audio Oscilloscope"))->setCheckable(true); + _oscilloscopeOn->setChecked(true); (_bandwidthDisplayOn = toolsMenu->addAction("Bandwidth Display"))->setCheckable(true); _bandwidthDisplayOn->setChecked(true); toolsMenu->addAction("Bandwidth Details", this, SLOT(bandwidthDetails())); @@ -2294,8 +2301,9 @@ void Application::displayOverlay() { #ifndef _WIN32 _audio.render(_glWidget->width(), _glWidget->height()); - _audioScope.render(20, _glWidget->height() - 200); - //_audio.renderEchoCompare(); // PER: Will turn back on to further test echo + if (_oscilloscopeOn->isChecked()) { + _audioScope.render(20, _glWidget->height() - 200); + } #endif //noiseTest(_glWidget->width(), _glWidget->height()); diff --git a/interface/src/Application.h b/interface/src/Application.h index e2b0cd21f4..4fb755be79 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -155,9 +155,10 @@ private: static void broadcastToAgents(unsigned char* data, size_t bytes, const char type); static void sendVoxelServerAddScene(); static bool sendVoxelsOperation(VoxelNode* node, void* extraData); + static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); static void sendAvatarVoxelURLMessage(const QUrl& url); static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes); - static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); + static void sendPingPackets(); void initMenu(); void updateFrustumRenderModeAction(); @@ -226,6 +227,7 @@ private: QAction* _manualFirstPerson; // Whether to force first-person mode QAction* _manualThirdPerson; // Whether to force third-person mode QAction* _logOn; // Whether to show on-screen log + QAction* _oscilloscopeOn; // Whether to show the oscilloscope QAction* _bandwidthDisplayOn; // Whether to show on-screen bandwidth bars QActionGroup* _voxelModeActions; // The group of voxel edit mode actions QAction* _addVoxelMode; // Whether add voxel mode is enabled diff --git a/interface/src/BandwidthMeter.cpp b/interface/src/BandwidthMeter.cpp index ba89864807..84d92a3020 100644 --- a/interface/src/BandwidthMeter.cpp +++ b/interface/src/BandwidthMeter.cpp @@ -225,6 +225,13 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) { textYlowerLine, fmtBuf); glPopMatrix(); + + // After rendering, indicate that no data has been sent/received since the last feed. + // This way, the meters fall when not continuously fed. + for (int i = 0; i < N_CHANNELS; ++i) { + inputStream(ChannelIndex(i)).updateValue(0); + outputStream(ChannelIndex(i)).updateValue(0); + } } From 78aa3ac9f902d28159eaa04f5358252f479a625c Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 2 Jul 2013 16:17:25 +0200 Subject: [PATCH 24/63] Added UI responsivness --- .../resources/images/hifi-interface-tools.svg | 259 +++++++++--------- interface/src/Application.cpp | 2 +- interface/src/Swatch.cpp | 47 ++-- interface/src/Swatch.h | 4 +- interface/src/Tool.cpp | 14 +- interface/src/Tool.h | 10 +- interface/src/ToolsPalette.cpp | 30 +- interface/src/ToolsPalette.h | 5 +- 8 files changed, 194 insertions(+), 177 deletions(-) diff --git a/interface/resources/images/hifi-interface-tools.svg b/interface/resources/images/hifi-interface-tools.svg index 60b5a02fba..311514581f 100644 --- a/interface/resources/images/hifi-interface-tools.svg +++ b/interface/resources/images/hifi-interface-tools.svg @@ -1,166 +1,177 @@ - + - - - - - - - - - + width="124px" height="400px" viewBox="-0.5 0.5 124 400" enable-background="new -0.5 0.5 124 400" xml:space="preserve"> - - - - - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 33482aeb50..33289e4e61 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1585,7 +1585,7 @@ void Application::init() { sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); - _palette.init(); + _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(_addVoxelMode, 0, 0); _palette.addAction(_deleteVoxelMode, 0, 1); _palette.addTool(_swatch); diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index cdfde34212..3b2e4ca4a4 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -3,10 +3,7 @@ Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), _selected(1), - _margin(4), _textRenderer(MONO_FONT_FAMILY, 10, 100) { - _width = 62; - _height = 30; } void Swatch::reset() { @@ -131,14 +128,16 @@ void Swatch::handleEvent(int key, bool getColor) { } } -void Swatch::render(int screenWidth, int screenHeight) { +void Swatch::render(int width, int height) { char str[2]; + int margin = 0.10f*height; + height = 0.75f*height; glBegin(GL_QUADS); glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(0, 8*(_height - _margin) + _margin); - glVertex2f(_width, 8*(_height - _margin) + _margin); - glVertex2f(_width, 0); + glVertex2f(0, 8*(height - margin) + margin); + glVertex2f(width, 8*(height - margin) + margin); + glVertex2f(width, 0); glVertex2f(0, 0); glEnd(); @@ -147,26 +146,26 @@ void Swatch::render(int screenWidth, int screenHeight) { glColor3f(_colors[i].redF(), _colors[i].greenF(), _colors[i].blueF()); - glVertex2f(_margin, (i + 1)*(_height - _margin)); - glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); - glVertex2f(_width - _margin, i*(_height - _margin) + _margin); - glVertex2f(_margin, i*(_height - _margin) + _margin); + glVertex2f(margin, (i + 1)*(height - margin)); + glVertex2f(width - margin, (i + 1)*(height - margin)); + glVertex2f(width - margin, i*(height - margin) + margin); + glVertex2f(margin, i*(height - margin) + margin); glEnd(); if (_colors[i].lightness() < 50) { glBegin(GL_LINES); glColor3f(1.0f, 1.0f, 1.0f); - glVertex2f(_margin, (i + 1)*(_height - _margin)); - glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); + glVertex2f(margin, (i + 1)*(height - margin)); + glVertex2f(width - margin, (i + 1)*(height - margin)); - glVertex2f(_width - _margin, (i + 1)*(_height - _margin)); - glVertex2f(_width - _margin, i*(_height - _margin) + _margin); + glVertex2f(width - margin, (i + 1)*(height - margin)); + glVertex2f(width - margin, i*(height - margin) + margin); - glVertex2f(_width - _margin, i*(_height - _margin) + _margin); - glVertex2f(_margin, i*(_height - _margin) + _margin); + glVertex2f(width - margin, i*(height - margin) + margin); + glVertex2f(margin, i*(height - margin) + margin); - glVertex2f(_margin, i*(_height - _margin) + _margin); - glVertex2f(_margin, (i + 1)*(_height - _margin)); + glVertex2f(margin, i*(height - margin) + margin); + glVertex2f(margin, (i + 1)*(height - margin)); glEnd(); @@ -177,15 +176,15 @@ void Swatch::render(int screenWidth, int screenHeight) { if (_selected == i + 1) { glBegin(GL_TRIANGLES); - glVertex2f(_margin, (i + 1)*(_height - _margin) - _margin); - glVertex2f(_width/4 - _margin, i*(_height - _margin) + _height/2.0f); - glVertex2f(_margin, i*(_height - _margin) + _margin + _margin); + glVertex2f(margin, (i + 1)*(height - margin) - margin); + glVertex2f(width/4 - margin, i*(height - margin) + height/2.0f); + glVertex2f(margin, i*(height - margin) + margin + margin); glEnd(); } sprintf(str, "%d", i + 1); - _textRenderer.draw(3*_width/4, (i + 1)*(_height - _margin) - 6, str); + _textRenderer.draw(3*width/4, (i + 1)*(height - margin) - 0.2f*height, str); } - glTranslated(0, 8*(_height - _margin) + _margin + 5, 0); + glTranslated(0, 8*(height - margin) + margin + 0.075f*height, 0); } diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h index 35da266078..6727f90086 100644 --- a/interface/src/Swatch.h +++ b/interface/src/Swatch.h @@ -24,15 +24,13 @@ public: void loadData(QSettings* settings); void reset(); - void render(int screenWidth, int screenHeight); + void render(int width, int height); void handleEvent(int key, bool getColor); private: TextRenderer _textRenderer; QColor _colors[SWATCH_SIZE]; int _selected; - - int _margin; }; #endif /* defined(__interface__Swatch__) */ diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp index c8fc4430f6..48bcfe5af6 100644 --- a/interface/src/Tool.cpp +++ b/interface/src/Tool.cpp @@ -7,16 +7,14 @@ Tool::Tool(QAction *action, GLuint texture, int x, int y) : _texture(texture), _action(action), _x(x), - _y(y), - _width(62), - _height(40) { + _y(y) { } bool Tool::isActive() { return _action->isChecked(); } -void Tool::render(int screenWidth, int screenHeight) { +void Tool::render(int width, int height) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, _texture); @@ -31,13 +29,13 @@ void Tool::render(int screenWidth, int screenHeight) { glBegin(GL_QUADS); glTexCoord2f(_x/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); - glVertex2f(0, _height); + glVertex2f(0, height); glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - (_y + 1)/TOOLS_ROWS); - glVertex2f(_width, _height); + glVertex2f(width, height); glTexCoord2f((_x + 1)/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); - glVertex2f(_width, 0); + glVertex2f(width, 0); glTexCoord2f(_x/TOOLS_COLS, 1.0f - _y/TOOLS_ROWS); glVertex2f(0, 0); @@ -45,5 +43,5 @@ void Tool::render(int screenWidth, int screenHeight) { glDisable(GL_TEXTURE_2D); - glTranslated(0, _height + 5, 0); + glTranslated(0, 1.10f*height, 0); } diff --git a/interface/src/Tool.h b/interface/src/Tool.h index 4b564e8578..1ff854372e 100644 --- a/interface/src/Tool.h +++ b/interface/src/Tool.h @@ -15,16 +15,12 @@ class QAction; -// tool size -static double _width; -static double _height; - class Tool { public: Tool(QAction *action, GLuint texture, int x, int y); bool isActive(); - virtual void render(int screenWidth, int screenHeight); + virtual void render(int width, int height); protected: QAction* _action; @@ -33,10 +29,6 @@ protected: // position in the SVG grid double _x; double _y; - - // tool size - double _width; - double _height; }; #endif /* defined(__interface__Tool__) */ diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp index 5b0f550f15..2829ac3d58 100644 --- a/interface/src/ToolsPalette.cpp +++ b/interface/src/ToolsPalette.cpp @@ -6,12 +6,24 @@ #include ToolsPalette::ToolsPalette() { +} + +void ToolsPalette::init(int screenWidth, int screenHeight) { + _width = 3*screenWidth/100; + if (_width < 47) { + _width = 47; + } + _height = 40*_width/62; + + _left = screenWidth/100; + _top = (screenHeight - 12*_height)/2; + // Load SVG switchToResourcesParentIfRequired(); QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); // Prepare a QImage with desired characteritisc - QImage image(124, 400, QImage::Format_ARGB32); + QImage image(TOOLS_COLS*_width, TOOLS_ROWS*_height, QImage::Format_ARGB32); // Get QPainter that paints to the image QPainter painter(&image); @@ -19,11 +31,6 @@ ToolsPalette::ToolsPalette() { //get the OpenGL-friendly image _textureImage = QGLWidget::convertToGLFormat(image); -} - -void ToolsPalette::init(int top, int left) { - _top = top; - _left = left; glGenTextures(1, &_textureID); glBindTexture(GL_TEXTURE_2D, _textureID); @@ -51,6 +58,15 @@ void ToolsPalette::addTool(Tool *tool) { } void ToolsPalette::render(int screenWidth, int screenHeight) { + _width = 3*screenWidth/100; + if (_width < 47) { + _width = 47; + } + _height = 40*_width/62; + + _left = screenWidth/150; + _top = (screenHeight - 13*_height)/2; + glPushMatrix(); glTranslated(_left, _top, 0); @@ -64,7 +80,7 @@ void ToolsPalette::render(int screenWidth, int screenHeight) { if (show) { for (unsigned int i(0); i < _tools.size(); ++i) { - _tools[i]->render(screenWidth, screenHeight); + _tools[i]->render(_width, _height); } } diff --git a/interface/src/ToolsPalette.h b/interface/src/ToolsPalette.h index a1c99d63f6..ed1aea7e40 100644 --- a/interface/src/ToolsPalette.h +++ b/interface/src/ToolsPalette.h @@ -17,7 +17,7 @@ class ToolsPalette { public: ToolsPalette(); - void init(int top = 200, int left = 10); + void init(int screenWidth, int screenHeight); void addAction(QAction* action, int x, int y); void addTool(Tool *tool); void render(int screenWidth, int screenHeight); @@ -30,6 +30,9 @@ private: int _top; int _left; + + int _width; + int _height; }; #endif /* defined(__interface__ToolsPalette__) */ From 52e7ff9a68439a4ccc2ae9b64b7c61d0c2535a62 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Jul 2013 14:51:27 -0700 Subject: [PATCH 25/63] Working on filling in missing joint data. --- interface/src/Application.cpp | 2 +- interface/src/Avatar.cpp | 74 ++++++++++++++++++++++++++++-- interface/src/Webcam.cpp | 65 ++++++++++++++------------ interface/src/Webcam.h | 2 + libraries/avatars/src/AvatarData.h | 13 ++++++ 5 files changed, 122 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cf3ef70090..2deec90108 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -362,7 +362,7 @@ void Application::paintGL() { glEnable(GL_LINE_SMOOTH); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - float headCameraScale = _serialHeadSensor.isActive() ? _headCameraPitchYawScale : 1.0f; + float headCameraScale = (_serialHeadSensor.isActive() || _webcam.isActive()) ? _headCameraPitchYawScale : 1.0f; if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness (100.0f); diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 52f3e98a70..230c1f3ec4 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -284,11 +284,11 @@ void Avatar::reset() { _hand.reset(); } -// Update avatar head rotation with sensor data +// Update avatar state with sensor data void Avatar::updateFromGyrosAndOrWebcam() { - const float AMPLIFY_PITCH = 2.f; - const float AMPLIFY_YAW = 2.f; - const float AMPLIFY_ROLL = 2.f; + const float AMPLIFY_PITCH = 1.f; + const float AMPLIFY_YAW = 1.f; + const float AMPLIFY_ROLL = 1.f; SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor(); Webcam* webcam = Application::getInstance()->getWebcam(); @@ -301,6 +301,15 @@ void Avatar::updateFromGyrosAndOrWebcam() { estimatedPosition = webcam->getEstimatedPosition(); estimatedRotation = webcam->getEstimatedRotation(); + // compute and store the joint rotations + const JointVector& joints = webcam->getEstimatedJoints(); + _joints.clear(); + for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { + if (joints[i].isValid) { + JointData data = { i, joints[i].position, joints[i].orientation }; + _joints.push_back(data); + } + } } else { return; } @@ -487,6 +496,63 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // update avatar skeleton _skeleton.update(deltaTime, getOrientation(), _position); + + // apply joint data (if any) to skeleton + for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { + _skeleton.joint[it->jointID].absoluteRotation = orientation * it->rotation; + _skeleton.joint[it->jointID].position = _position + orientation * it->position; + + AvatarJointID derivedJointID = AVATAR_JOINT_NULL; + AvatarJointID secondJointID = (AvatarJointID)it->jointID; + float proportion = 0.5f; + switch (it->jointID) { + case AVATAR_JOINT_NECK_BASE: + secondJointID = AVATAR_JOINT_TORSO; + derivedJointID = AVATAR_JOINT_CHEST; + break; + + case AVATAR_JOINT_HEAD_BASE: + derivedJointID = AVATAR_JOINT_HEAD_TOP; + break; + + case AVATAR_JOINT_LEFT_SHOULDER: + secondJointID = AVATAR_JOINT_CHEST; + derivedJointID = AVATAR_JOINT_LEFT_COLLAR; + break; + + case AVATAR_JOINT_LEFT_WRIST: + derivedJointID = AVATAR_JOINT_LEFT_FINGERTIPS; + break; + + case AVATAR_JOINT_RIGHT_SHOULDER: + secondJointID = AVATAR_JOINT_CHEST; + derivedJointID = AVATAR_JOINT_RIGHT_COLLAR; + break; + + case AVATAR_JOINT_RIGHT_WRIST: + derivedJointID = AVATAR_JOINT_RIGHT_FINGERTIPS; + break; + + case AVATAR_JOINT_LEFT_HEEL: + derivedJointID = AVATAR_JOINT_LEFT_TOES; + break; + + case AVATAR_JOINT_RIGHT_HIP: + secondJointID = AVATAR_JOINT_LEFT_HIP; + derivedJointID = AVATAR_JOINT_PELVIS; + break; + + case AVATAR_JOINT_RIGHT_HEEL: + derivedJointID = AVATAR_JOINT_RIGHT_TOES; + break; + } + if (derivedJointID != AVATAR_JOINT_NULL) { + _skeleton.joint[derivedJointID].position = glm::mix(_skeleton.joint[it->jointID].position, + _skeleton.joint[secondJointID].position, proportion); + _skeleton.joint[derivedJointID].absoluteRotation = safeMix(_skeleton.joint[it->jointID].absoluteRotation, + _skeleton.joint[secondJointID].absoluteRotation, proportion); + } + } //determine the lengths of the body springs now that we have updated the skeleton at least once if (!_ballSpringsInitialized) { diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 4788472555..885ffa0514 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -122,6 +122,7 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) { glDisable(GL_TEXTURE_2D); } + glColor3f(1.0f, 1.0f, 1.0f); glBegin(GL_LINE_LOOP); Point2f facePoints[4]; _faceRect.points(facePoints); @@ -203,9 +204,12 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota if (!_joints.isEmpty()) { _estimatedJoints.resize(NUM_AVATAR_JOINTS); for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { + if (!_joints[i].isValid) { + continue; + } const float JOINT_SMOOTHING = 0.95f; - _estimatedJoints[i].position = glm::mix(_joints[i].position - _joints[AVATAR_JOINT_TORSO].position, - _estimatedJoints[i].position, JOINT_SMOOTHING); + _estimatedJoints[i].isValid = true; + _estimatedJoints[i].position = glm::mix(_joints[i].position, _estimatedJoints[i].position, JOINT_SMOOTHING); _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, _estimatedJoints[i].orientation, JOINT_SMOOTHING); } _estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].orientation); @@ -258,43 +262,35 @@ FrameGrabber::~FrameGrabber() { } } -void FrameGrabber::reset() { - _searchWindow = cv::Rect(0, 0, 0, 0); -} - #ifdef HAVE_OPENNI static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { switch (joint) { case XN_SKEL_HEAD: return AVATAR_JOINT_HEAD_BASE; case XN_SKEL_NECK: return AVATAR_JOINT_NECK_BASE; case XN_SKEL_TORSO: return AVATAR_JOINT_TORSO; - case XN_SKEL_WAIST: return AVATAR_JOINT_PELVIS; - case XN_SKEL_LEFT_COLLAR: return AVATAR_JOINT_LEFT_COLLAR; + case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_LEFT_SHOULDER; case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_LEFT_ELBOW; - case XN_SKEL_LEFT_WRIST: return AVATAR_JOINT_LEFT_WRIST; - case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_NULL; - case XN_SKEL_LEFT_FINGERTIP: return AVATAR_JOINT_LEFT_FINGERTIPS; - case XN_SKEL_RIGHT_COLLAR: return AVATAR_JOINT_RIGHT_COLLAR; + case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_LEFT_WRIST; + case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_RIGHT_SHOULDER; case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_RIGHT_ELBOW; - case XN_SKEL_RIGHT_WRIST: return AVATAR_JOINT_RIGHT_WRIST; - case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_NULL; - case XN_SKEL_RIGHT_FINGERTIP: return AVATAR_JOINT_RIGHT_FINGERTIPS; + case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_RIGHT_WRIST; + case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_LEFT_HIP; case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_LEFT_KNEE; - case XN_SKEL_LEFT_ANKLE: return AVATAR_JOINT_LEFT_HEEL; - case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_LEFT_TOES; + case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_LEFT_HEEL; + case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_RIGHT_HIP; case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; - case XN_SKEL_RIGHT_ANKLE: return AVATAR_JOINT_RIGHT_HEEL; - case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_RIGHT_TOES; + case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_RIGHT_HEEL; + + default: return AVATAR_JOINT_NULL; } } static glm::vec3 xnToGLM(const XnVector3D& vector) { - const float METERS_PER_MM = 1.0f / 1000.0f; - return glm::vec3(vector.X, vector.Y, -vector.Z) * METERS_PER_MM; + return glm::vec3(vector.X, vector.Y, vector.Z); } static glm::quat xnToGLM(const XnMatrix3X3& matrix) { @@ -302,12 +298,12 @@ static glm::quat xnToGLM(const XnMatrix3X3& matrix) { matrix.elements[0], matrix.elements[3], matrix.elements[6], matrix.elements[1], matrix.elements[4], matrix.elements[7], matrix.elements[2], matrix.elements[5], matrix.elements[8])); - return glm::quat(rotation.w, rotation.x, rotation.y, -rotation.z); + return glm::quat(rotation.w, rotation.x, rotation.y, rotation.z); } static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { printLog("Found user %d.\n", id); - generator.GetSkeletonCap().RequestCalibration(id, true); + generator.GetSkeletonCap().RequestCalibration(id, false); } static void XN_CALLBACK_TYPE lostUser(UserGenerator& generator, XnUserID id, void* cookie) { @@ -331,6 +327,16 @@ static void XN_CALLBACK_TYPE calibrationCompleted(SkeletonCapability& capability } #endif +void FrameGrabber::reset() { + _searchWindow = cv::Rect(0, 0, 0, 0); + +#ifdef HAVE_OPENNI + if (_userGenerator.IsValid() && _userGenerator.GetSkeletonCap().IsTracking(_userID)) { + _userGenerator.GetSkeletonCap().RequestCalibration(_userID, true); + } +#endif +} + void FrameGrabber::grabFrame() { if (!(_initialized || init())) { return; @@ -348,10 +354,10 @@ void FrameGrabber::grabFrame() { Mat depth = Mat(_depthMetaData.YRes(), _depthMetaData.XRes(), CV_16UC1, (void*)_depthGenerator.GetDepthMap()); depth.convertTo(_grayDepthFrame, CV_8UC1, 256.0 / 2048.0); - XnUserID userID; + _userID = 0; XnUInt16 userCount = 1; - _userGenerator.GetUsers(&userID, userCount); - if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(userID)) { + _userGenerator.GetUsers(&_userID, userCount); + if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(_userID)) { joints.resize(NUM_AVATAR_JOINTS); const int MAX_ACTIVE_JOINTS = 16; XnSkeletonJoint activeJoints[MAX_ACTIVE_JOINTS]; @@ -363,10 +369,11 @@ void FrameGrabber::grabFrame() { if (avatarJoint == AVATAR_JOINT_NULL) { continue; } - _userGenerator.GetSkeletonCap().GetSkeletonJoint(userID, activeJoints[i], transform); + _userGenerator.GetSkeletonCap().GetSkeletonJoint(_userID, activeJoints[i], transform); XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); - Joint joint = { true, xnToGLM(transform.position.position), + const float METERS_PER_MM = 1.0f / 1000.0f; + Joint joint = { true, xnToGLM(transform.position.position) * METERS_PER_MM, xnToGLM(transform.orientation.orientation), xnToGLM(projected) }; joints[avatarJoint] = joint; @@ -451,7 +458,7 @@ bool FrameGrabber::init() { _userGenerator.GetSkeletonCap().RegisterToCalibrationStart(calibrationStarted, 0, calibrationStartCallback); _userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback); - _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER); + _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL); _xnContext.StartGeneratingAll(); return true; diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index b5d52dc3a2..424728faa3 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -45,6 +45,7 @@ public: const bool isActive() const { return _active; } const glm::vec3& getEstimatedPosition() const { return _estimatedPosition; } const glm::vec3& getEstimatedRotation() const { return _estimatedRotation; } + const JointVector& getEstimatedJoints() const { return _estimatedJoints; } void reset(); void renderPreview(int screenWidth, int screenHeight); @@ -117,6 +118,7 @@ private: xn::UserGenerator _userGenerator; xn::DepthMetaData _depthMetaData; xn::ImageMetaData _imageMetaData; + XnUserID _userID; #endif }; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d3e6a40a7e..90d437f253 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -36,6 +37,8 @@ enum KeyState DELETE_KEY_DOWN }; +class JointData; + class AvatarData : public AgentData { public: AvatarData(Agent* owningAgent = NULL); @@ -132,14 +135,24 @@ protected: bool _wantDelta; bool _wantOcclusionCulling; + std::vector _joints; + HeadData* _headData; HandData* _handData; + private: // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&); AvatarData& operator= (const AvatarData&); }; +class JointData { +public: + + int jointID; + glm::vec3 position; + glm::quat rotation; +}; // These pack/unpack functions are designed to start specific known types in as efficient a manner // as possible. Taking advantage of the known characteristics of the semantic types. From 1fdfca727df82ed47fdb175555b0fa1172ab73bc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 2 Jul 2013 16:49:07 -0700 Subject: [PATCH 26/63] More work on Kinect-driven joints. --- interface/src/Avatar.cpp | 19 +++++++++++++------ interface/src/Webcam.cpp | 11 ++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 230c1f3ec4..539eba7434 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -305,7 +305,7 @@ void Avatar::updateFromGyrosAndOrWebcam() { const JointVector& joints = webcam->getEstimatedJoints(); _joints.clear(); for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { - if (joints[i].isValid) { + if (joints.size() > i && joints[i].isValid) { JointData data = { i, joints[i].position, joints[i].orientation }; _joints.push_back(data); } @@ -503,7 +503,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _skeleton.joint[it->jointID].position = _position + orientation * it->position; AvatarJointID derivedJointID = AVATAR_JOINT_NULL; - AvatarJointID secondJointID = (AvatarJointID)it->jointID; + AvatarJointID secondJointID = AVATAR_JOINT_NULL; float proportion = 0.5f; switch (it->jointID) { case AVATAR_JOINT_NECK_BASE: @@ -547,10 +547,17 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { break; } if (derivedJointID != AVATAR_JOINT_NULL) { - _skeleton.joint[derivedJointID].position = glm::mix(_skeleton.joint[it->jointID].position, - _skeleton.joint[secondJointID].position, proportion); - _skeleton.joint[derivedJointID].absoluteRotation = safeMix(_skeleton.joint[it->jointID].absoluteRotation, - _skeleton.joint[secondJointID].absoluteRotation, proportion); + if (secondJointID == AVATAR_JOINT_NULL) { + _skeleton.joint[derivedJointID].position = _skeleton.joint[it->jointID].position + + _skeleton.joint[it->jointID].absoluteRotation * JOINT_DIRECTION * _skeleton.joint[derivedJointID].length; + _skeleton.joint[derivedJointID].absoluteRotation = _skeleton.joint[it->jointID].absoluteRotation; + + } else { + _skeleton.joint[derivedJointID].position = glm::mix(_skeleton.joint[it->jointID].position, + _skeleton.joint[secondJointID].position, proportion); + _skeleton.joint[derivedJointID].absoluteRotation = safeMix(_skeleton.joint[it->jointID].absoluteRotation, + _skeleton.joint[secondJointID].absoluteRotation, proportion); + } } } diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 885ffa0514..e5c7c28e5c 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -108,11 +108,16 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) { glPointSize(4.0f); glBegin(GL_POINTS); float projectedScale = PREVIEW_HEIGHT / (float)_depthHeight; + int idx = 0; foreach (const Joint& joint, _joints) { if (joint.isValid) { + if (joint.projected.x == 0.0f && joint.projected.y == 0.0f) { + printLog("%d\n", idx); + } glVertex2f(depthLeft + joint.projected.x * projectedScale, top - PREVIEW_HEIGHT + joint.projected.y * projectedScale); } + idx++; } glEnd(); glPointSize(1.0f); @@ -210,7 +215,8 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota const float JOINT_SMOOTHING = 0.95f; _estimatedJoints[i].isValid = true; _estimatedJoints[i].position = glm::mix(_joints[i].position, _estimatedJoints[i].position, JOINT_SMOOTHING); - _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, _estimatedJoints[i].orientation, JOINT_SMOOTHING); + _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, + _estimatedJoints[i].orientation, JOINT_SMOOTHING); } _estimatedRotation = safeEulerAngles(_estimatedJoints[AVATAR_JOINT_HEAD_BASE].orientation); _estimatedPosition = _estimatedJoints[AVATAR_JOINT_HEAD_BASE].position; @@ -359,6 +365,9 @@ void FrameGrabber::grabFrame() { _userGenerator.GetUsers(&_userID, userCount); if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(_userID)) { joints.resize(NUM_AVATAR_JOINTS); + for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { + joints[i].isValid = false; + } const int MAX_ACTIVE_JOINTS = 16; XnSkeletonJoint activeJoints[MAX_ACTIVE_JOINTS]; XnUInt16 activeJointCount = MAX_ACTIVE_JOINTS; From a542ebc85143aa718a10b2dfd1838d02d93bf518 Mon Sep 17 00:00:00 2001 From: tosh Date: Wed, 3 Jul 2013 14:01:09 +0200 Subject: [PATCH 27/63] bandwidth meter - avoid division by zero --- interface/src/BandwidthMeter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/src/BandwidthMeter.cpp b/interface/src/BandwidthMeter.cpp index 84d92a3020..5e0d63d6c5 100644 --- a/interface/src/BandwidthMeter.cpp +++ b/interface/src/BandwidthMeter.cpp @@ -68,6 +68,12 @@ void BandwidthMeter::Stream::updateValue(double amount) { timeval now; gettimeofday(& now, NULL); double dt = diffclock(& _prevTime, & now); + + // Ignore this value when timer imprecision yields dt = 0 + if (dt == 0.0) { + return; + } + memcpy(& _prevTime, & now, sizeof(timeval)); // Compute approximate average From ebab09e5352953d1c914f951ece4344eb5dfac4e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Jul 2013 10:13:10 -0700 Subject: [PATCH 28/63] More work on joints. --- interface/src/Avatar.cpp | 3 +-- interface/src/Webcam.cpp | 47 +++++++++++++++++++++------------------- interface/src/Webcam.h | 3 +++ 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 539eba7434..c2c0e98c70 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -548,8 +548,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { } if (derivedJointID != AVATAR_JOINT_NULL) { if (secondJointID == AVATAR_JOINT_NULL) { - _skeleton.joint[derivedJointID].position = _skeleton.joint[it->jointID].position + - _skeleton.joint[it->jointID].absoluteRotation * JOINT_DIRECTION * _skeleton.joint[derivedJointID].length; + _skeleton.joint[derivedJointID].position = _skeleton.joint[it->jointID].position; _skeleton.joint[derivedJointID].absoluteRotation = _skeleton.joint[it->jointID].absoluteRotation; } else { diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index e5c7c28e5c..7079f0af4d 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -214,7 +214,8 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota } const float JOINT_SMOOTHING = 0.95f; _estimatedJoints[i].isValid = true; - _estimatedJoints[i].position = glm::mix(_joints[i].position, _estimatedJoints[i].position, JOINT_SMOOTHING); + _estimatedJoints[i].position = glm::mix(_joints[i].position - joints[AVATAR_JOINT_TORSO].position, + _estimatedJoints[i].position, JOINT_SMOOTHING); _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, _estimatedJoints[i].orientation, JOINT_SMOOTHING); } @@ -275,28 +276,28 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { case XN_SKEL_NECK: return AVATAR_JOINT_NECK_BASE; case XN_SKEL_TORSO: return AVATAR_JOINT_TORSO; - case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_LEFT_SHOULDER; - case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_LEFT_ELBOW; - case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_LEFT_WRIST; + case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_RIGHT_SHOULDER; + case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_ELBOW; + case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_RIGHT_WRIST; - case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_RIGHT_SHOULDER; - case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_RIGHT_ELBOW; - case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_RIGHT_WRIST; + case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_LEFT_SHOULDER; + case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_ELBOW; + case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_LEFT_WRIST; - case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_LEFT_HIP; - case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_LEFT_KNEE; - case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_LEFT_HEEL; + case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_RIGHT_HIP; + case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; + case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_RIGHT_HEEL; - case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_RIGHT_HIP; - case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; - case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_RIGHT_HEEL; + case XN_SKEL_RIGHT_HIP: return AVATAR_JOINT_LEFT_HIP; + case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_LEFT_KNEE; + case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_LEFT_HEEL; default: return AVATAR_JOINT_NULL; } } -static glm::vec3 xnToGLM(const XnVector3D& vector) { - return glm::vec3(vector.X, vector.Y, vector.Z); +static glm::vec3 xnToGLM(const XnVector3D& vector, bool flip = false) { + return glm::vec3(vector.X * (flip ? -1 : 1), vector.Y, vector.Z); } static glm::quat xnToGLM(const XnMatrix3X3& matrix) { @@ -365,9 +366,6 @@ void FrameGrabber::grabFrame() { _userGenerator.GetUsers(&_userID, userCount); if (userCount > 0 && _userGenerator.GetSkeletonCap().IsTracking(_userID)) { joints.resize(NUM_AVATAR_JOINTS); - for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { - joints[i].isValid = false; - } const int MAX_ACTIVE_JOINTS = 16; XnSkeletonJoint activeJoints[MAX_ACTIVE_JOINTS]; XnUInt16 activeJointCount = MAX_ACTIVE_JOINTS; @@ -382,10 +380,8 @@ void FrameGrabber::grabFrame() { XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); const float METERS_PER_MM = 1.0f / 1000.0f; - Joint joint = { true, xnToGLM(transform.position.position) * METERS_PER_MM, - xnToGLM(transform.orientation.orientation), - xnToGLM(projected) }; - joints[avatarJoint] = joint; + joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM, + xnToGLM(transform.orientation.orientation), xnToGLM(projected)); } } } @@ -502,3 +498,10 @@ void FrameGrabber::updateHSVFrame(const Mat& frame, int format) { cvtColor(frame, _hsvFrame, format == GL_RGB ? CV_RGB2HSV : CV_BGR2HSV); inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask); } + +Joint::Joint(const glm::vec3& position, const glm::quat& orientation, const glm::vec3& projected) : + isValid(true), position(position), orientation(orientation), projected(projected) { +} + +Joint::Joint() : isValid(false) { +} diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 424728faa3..be7fe8e90f 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -125,6 +125,9 @@ private: class Joint { public: + Joint(const glm::vec3& position, const glm::quat& orientation, const glm::vec3& projected); + Joint(); + bool isValid; glm::vec3 position; glm::quat orientation; From 3ea100e595e1483c7e1ea7b8fd8e8b77e9530fe3 Mon Sep 17 00:00:00 2001 From: Eric Johnston Date: Wed, 3 Jul 2013 15:33:18 -0700 Subject: [PATCH 29/63] Added safety checks to processEvents(), which was causing problems. Also narrowed the scope of the events processed. It can now be disabled in the interface by un-checking touch-look, which most people don't use yet. Also, the event processing time should be limited to 1 ms. --- interface/src/Application.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 088dd03e03..aa7019a0b0 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -950,8 +950,12 @@ void Application::idle() { // NOTE - this is commented out for now - causing event processing issues reported by Philip and Ryan // birarda - July 3rd - - // processEvents(); + // Added some safety catches which will enable us to test this. + // ej - July 3rd + if (_touchLook->isChecked()) { + int maxTimeMS = 1; + processEvents(QEventLoop::ExcludeSocketNotifiers, maxTimeMS); + } update(1.0f / _fps); From 30f77975cf93e96dc7005aa5443af17b55942ab8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Jul 2013 17:07:22 -0700 Subject: [PATCH 30/63] Trying to get the right rotations, send them on the wire. --- interface/src/Avatar.cpp | 208 ++++++++++----------------- interface/src/Avatar.h | 2 +- interface/src/Head.cpp | 7 +- interface/src/Webcam.cpp | 54 ++++++- libraries/avatars/src/AvatarData.cpp | 17 +++ libraries/avatars/src/AvatarData.h | 1 - 6 files changed, 151 insertions(+), 138 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 5f7725c2de..27ccf0db4b 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -148,120 +148,120 @@ void Avatar::initializeBodyBalls() { _bodyBall[ BODY_BALL_HEAD_BASE ].radius = 0.07; _bodyBall[ BODY_BALL_LEFT_COLLAR ].radius = 0.04; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].radius = 0.03; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].radius = 0.02; _bodyBall[ BODY_BALL_LEFT_WRIST ].radius = 0.02; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].radius = 0.01; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].radius = 0.04; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].radius = 0.03; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].radius = 0.02; _bodyBall[ BODY_BALL_RIGHT_WRIST ].radius = 0.02; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].radius = 0.01; - _bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04; + _bodyBall[ BODY_BALL_LEFT_HIP ].radius = 0.04; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].radius = 0.03; - _bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025; - _bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025; - _bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025; - _bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_KNEE ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_HEEL ].radius = 0.025; + _bodyBall[ BODY_BALL_LEFT_TOES ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_HIP ].radius = 0.04; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].radius = 0.025; + _bodyBall[ BODY_BALL_RIGHT_TOES ].radius = 0.025; // specify the parent joint for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS; + _bodyBall[ BODY_BALL_PELVIS ].parentJoint = AVATAR_JOINT_PELVIS; _bodyBall[ BODY_BALL_TORSO ].parentJoint = AVATAR_JOINT_TORSO; - _bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST; - _bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE; + _bodyBall[ BODY_BALL_CHEST ].parentJoint = AVATAR_JOINT_CHEST; + _bodyBall[ BODY_BALL_NECK_BASE ].parentJoint = AVATAR_JOINT_NECK_BASE; _bodyBall[ BODY_BALL_HEAD_BASE ].parentJoint = AVATAR_JOINT_HEAD_BASE; _bodyBall[ BODY_BALL_HEAD_TOP ].parentJoint = AVATAR_JOINT_HEAD_TOP; _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentJoint = AVATAR_JOINT_LEFT_COLLAR; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentJoint = AVATAR_JOINT_LEFT_SHOULDER; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW; - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentJoint = AVATAR_JOINT_LEFT_ELBOW; + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentJoint = AVATAR_JOINT_LEFT_WRIST; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentJoint = AVATAR_JOINT_LEFT_FINGERTIPS; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentJoint = AVATAR_JOINT_RIGHT_COLLAR; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentJoint = AVATAR_JOINT_RIGHT_SHOULDER; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentJoint = AVATAR_JOINT_RIGHT_ELBOW; _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentJoint = AVATAR_JOINT_RIGHT_WRIST; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentJoint = AVATAR_JOINT_RIGHT_FINGERTIPS; - _bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP; - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE; - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL; - _bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES; - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL; - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES; + _bodyBall[ BODY_BALL_LEFT_HIP ].parentJoint = AVATAR_JOINT_LEFT_HIP; + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentJoint = AVATAR_JOINT_LEFT_KNEE; + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentJoint = AVATAR_JOINT_LEFT_HEEL; + _bodyBall[ BODY_BALL_LEFT_TOES ].parentJoint = AVATAR_JOINT_LEFT_TOES; + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentJoint = AVATAR_JOINT_RIGHT_HIP; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentJoint = AVATAR_JOINT_RIGHT_KNEE; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentJoint = AVATAR_JOINT_RIGHT_HEEL; + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentJoint = AVATAR_JOINT_RIGHT_TOES; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentJoint = AVATAR_JOINT_LEFT_HIP; // specify the parent offset for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_PELVIS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_TORSO ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_CHEST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_NECK_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_HEAD_BASE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_HEAD_TOP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentOffset = glm::vec3(0.0, 0.0, 0.0); _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_LEFT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentOffset = glm::vec3(0.0, 0.0, 0.0); + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentOffset = glm::vec3(0.0, 0.0, 0.0); //_bodyBall[ BODY_BALL_LEFT_MID_THIGH].parentOffset = glm::vec3(-0.1, -0.1, 0.0); // specify the parent BALL for each ball - _bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL; + _bodyBall[ BODY_BALL_PELVIS ].parentBall = BODY_BALL_NULL; _bodyBall[ BODY_BALL_TORSO ].parentBall = BODY_BALL_PELVIS; - _bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO; - _bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST; + _bodyBall[ BODY_BALL_CHEST ].parentBall = BODY_BALL_TORSO; + _bodyBall[ BODY_BALL_NECK_BASE ].parentBall = BODY_BALL_CHEST; _bodyBall[ BODY_BALL_HEAD_BASE ].parentBall = BODY_BALL_NECK_BASE; _bodyBall[ BODY_BALL_HEAD_TOP ].parentBall = BODY_BALL_HEAD_BASE; _bodyBall[ BODY_BALL_LEFT_COLLAR ].parentBall = BODY_BALL_CHEST; _bodyBall[ BODY_BALL_LEFT_SHOULDER ].parentBall = BODY_BALL_LEFT_COLLAR; - _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER; - _bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW; + _bodyBall[ BODY_BALL_LEFT_ELBOW ].parentBall = BODY_BALL_LEFT_SHOULDER; + _bodyBall[ BODY_BALL_LEFT_WRIST ].parentBall = BODY_BALL_LEFT_ELBOW; _bodyBall[ BODY_BALL_LEFT_FINGERTIPS ].parentBall = BODY_BALL_LEFT_WRIST; _bodyBall[ BODY_BALL_RIGHT_COLLAR ].parentBall = BODY_BALL_CHEST; - _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR; - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER; + _bodyBall[ BODY_BALL_RIGHT_SHOULDER ].parentBall = BODY_BALL_RIGHT_COLLAR; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].parentBall = BODY_BALL_RIGHT_SHOULDER; _bodyBall[ BODY_BALL_RIGHT_WRIST ].parentBall = BODY_BALL_RIGHT_ELBOW; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS ].parentBall = BODY_BALL_RIGHT_WRIST; - _bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS; + _bodyBall[ BODY_BALL_LEFT_HIP ].parentBall = BODY_BALL_PELVIS; //_bodyBall[ BODY_BALL_LEFT_MID_THIGH ].parentBall = BODY_BALL_LEFT_HIP; - // _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH; - _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP; + // _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_MID_THIGH; + _bodyBall[ BODY_BALL_LEFT_KNEE ].parentBall = BODY_BALL_LEFT_HIP; - _bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE; - _bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL; - _bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS; - _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP; - _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE; - _bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL; + _bodyBall[ BODY_BALL_LEFT_HEEL ].parentBall = BODY_BALL_LEFT_KNEE; + _bodyBall[ BODY_BALL_LEFT_TOES ].parentBall = BODY_BALL_LEFT_HEEL; + _bodyBall[ BODY_BALL_RIGHT_HIP ].parentBall = BODY_BALL_PELVIS; + _bodyBall[ BODY_BALL_RIGHT_KNEE ].parentBall = BODY_BALL_RIGHT_HIP; + _bodyBall[ BODY_BALL_RIGHT_HEEL ].parentBall = BODY_BALL_RIGHT_KNEE; + _bodyBall[ BODY_BALL_RIGHT_TOES ].parentBall = BODY_BALL_RIGHT_HEEL; /* // to aid in hand-shaking and hand-holding, the right hand is not collidable - _bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false; - _bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false; + _bodyBall[ BODY_BALL_RIGHT_ELBOW ].isCollidable = false; + _bodyBall[ BODY_BALL_RIGHT_WRIST ].isCollidable = false; _bodyBall[ BODY_BALL_RIGHT_FINGERTIPS].isCollidable = false; */ } @@ -308,7 +308,7 @@ void Avatar::updateFromGyrosAndOrWebcam() { _joints.clear(); for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { if (joints.size() > i && joints[i].isValid) { - JointData data = { i, joints[i].position, joints[i].orientation }; + JointData data = { i, joints[i].orientation }; _joints.push_back(data); } } @@ -496,72 +496,18 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _skeleton.joint[AVATAR_JOINT_TORSO].rotation = glm::quat(glm::radians(glm::vec3( _head.getLeanForward(), 0.0f, _head.getLeanSideways()))); - // update avatar skeleton - _skeleton.update(deltaTime, getOrientation(), _position); - // apply joint data (if any) to skeleton + bool enableHandMovement = true; for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { - _skeleton.joint[it->jointID].absoluteRotation = orientation * it->rotation; - _skeleton.joint[it->jointID].position = _position + orientation * it->position; + _skeleton.joint[it->jointID].rotation = it->rotation; - AvatarJointID derivedJointID = AVATAR_JOINT_NULL; - AvatarJointID secondJointID = AVATAR_JOINT_NULL; - float proportion = 0.5f; - switch (it->jointID) { - case AVATAR_JOINT_NECK_BASE: - secondJointID = AVATAR_JOINT_TORSO; - derivedJointID = AVATAR_JOINT_CHEST; - break; - - case AVATAR_JOINT_HEAD_BASE: - derivedJointID = AVATAR_JOINT_HEAD_TOP; - break; - - case AVATAR_JOINT_LEFT_SHOULDER: - secondJointID = AVATAR_JOINT_CHEST; - derivedJointID = AVATAR_JOINT_LEFT_COLLAR; - break; - - case AVATAR_JOINT_LEFT_WRIST: - derivedJointID = AVATAR_JOINT_LEFT_FINGERTIPS; - break; - - case AVATAR_JOINT_RIGHT_SHOULDER: - secondJointID = AVATAR_JOINT_CHEST; - derivedJointID = AVATAR_JOINT_RIGHT_COLLAR; - break; - - case AVATAR_JOINT_RIGHT_WRIST: - derivedJointID = AVATAR_JOINT_RIGHT_FINGERTIPS; - break; - - case AVATAR_JOINT_LEFT_HEEL: - derivedJointID = AVATAR_JOINT_LEFT_TOES; - break; - - case AVATAR_JOINT_RIGHT_HIP: - secondJointID = AVATAR_JOINT_LEFT_HIP; - derivedJointID = AVATAR_JOINT_PELVIS; - break; - - case AVATAR_JOINT_RIGHT_HEEL: - derivedJointID = AVATAR_JOINT_RIGHT_TOES; - break; - } - if (derivedJointID != AVATAR_JOINT_NULL) { - if (secondJointID == AVATAR_JOINT_NULL) { - _skeleton.joint[derivedJointID].position = _skeleton.joint[it->jointID].position; - _skeleton.joint[derivedJointID].absoluteRotation = _skeleton.joint[it->jointID].absoluteRotation; - - } else { - _skeleton.joint[derivedJointID].position = glm::mix(_skeleton.joint[it->jointID].position, - _skeleton.joint[secondJointID].position, proportion); - _skeleton.joint[derivedJointID].absoluteRotation = safeMix(_skeleton.joint[it->jointID].absoluteRotation, - _skeleton.joint[secondJointID].absoluteRotation, proportion); - } - } + // disable hand movement if we have joint info for the right wrist + enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST); } + // update avatar skeleton + _skeleton.update(deltaTime, getOrientation(), _position); + //determine the lengths of the body springs now that we have updated the skeleton at least once if (!_ballSpringsInitialized) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) { @@ -592,7 +538,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { } //update the movement of the hand and process handshaking with other avatars... - updateHandMovementAndTouching(deltaTime); + updateHandMovementAndTouching(deltaTime, enableHandMovement); _avatarTouch.simulate(deltaTime); // apply gravity and collision with the ground/floor @@ -772,7 +718,7 @@ void Avatar::setOrientation(const glm::quat& orientation) { _bodyRoll = eulerAngles.z; } -void Avatar::updateHandMovementAndTouching(float deltaTime) { +void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovement) { glm::quat orientation = getOrientation(); @@ -781,12 +727,14 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { glm::vec3 up = orientation * IDENTITY_UP; glm::vec3 front = orientation * IDENTITY_FRONT; - glm::vec3 transformedHandMovement - = right * _movedHandOffset.x * 2.0f - + up * -_movedHandOffset.y * 2.0f - + front * -_movedHandOffset.y * 2.0f; + if (enableHandMovement) { + glm::vec3 transformedHandMovement = + right * _movedHandOffset.x * 2.0f + + up * -_movedHandOffset.y * 2.0f + + front * -_movedHandOffset.y * 2.0f; - _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement; + _skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position += transformedHandMovement; + } if (isMyAvatar()) { _avatarTouch.setMyBodyPosition(_position); @@ -877,7 +825,9 @@ void Avatar::updateHandMovementAndTouching(float deltaTime) { //constrain right arm length and re-adjust elbow position as it bends // NOTE - the following must be called on all avatars - not just _isMine - updateArmIKAndConstraints(deltaTime); + if (enableHandMovement) { + updateArmIKAndConstraints(deltaTime); + } //Set right hand position and state to be transmitted, and also tell AvatarTouch about it if (isMyAvatar()) { diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index fe8f867f18..cb26500e71 100755 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -229,7 +229,7 @@ private: void updateBodyBalls( float deltaTime ); void calculateBoneLengths(); void readSensors(); - void updateHandMovementAndTouching(float deltaTime); + void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement); void updateAvatarCollisions(float deltaTime); void updateArmIKAndConstraints( float deltaTime ); void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime ); diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 44eddeeb93..934cafae91 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -184,9 +184,10 @@ void Head::simulate(float deltaTime, bool isMine) { const float FULLY_CLOSED = 1.0f; if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { // no blinking when brows are raised; blink less with increasing loudness - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.75f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo( - sqrtf(_averageLoudness) * ROOT_LOUDNESS_TO_BLINK_INTERVAL, deltaTime))) { + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(_averageLoudness) * + ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { _leftEyeBlinkVelocity = BLINK_SPEED; _rightEyeBlinkVelocity = BLINK_SPEED; } diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 7079f0af4d..d4fde500bd 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -208,13 +208,20 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota // 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) { + origin = _joints[AVATAR_JOINT_TORSO].position + glm::vec3(0.0f, -0.09f, -0.01f); + } for (int i = 0; i < NUM_AVATAR_JOINTS; i++) { if (!_joints[i].isValid) { continue; } const float JOINT_SMOOTHING = 0.95f; _estimatedJoints[i].isValid = true; - _estimatedJoints[i].position = glm::mix(_joints[i].position - joints[AVATAR_JOINT_TORSO].position, + _estimatedJoints[i].position = glm::mix(_joints[i].position - origin, _estimatedJoints[i].position, JOINT_SMOOTHING); _estimatedJoints[i].orientation = safeMix(_joints[i].orientation, _estimatedJoints[i].orientation, JOINT_SMOOTHING); @@ -296,6 +303,27 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { } } +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; + } +} + static glm::vec3 xnToGLM(const XnVector3D& vector, bool flip = false) { return glm::vec3(vector.X * (flip ? -1 : 1), vector.Y, vector.Z); } @@ -305,7 +333,7 @@ static glm::quat xnToGLM(const XnMatrix3X3& matrix) { matrix.elements[0], matrix.elements[3], matrix.elements[6], matrix.elements[1], matrix.elements[4], matrix.elements[7], matrix.elements[2], matrix.elements[5], matrix.elements[8])); - return glm::quat(rotation.w, rotation.x, rotation.y, rotation.z); + return glm::quat(rotation.w, rotation.x, rotation.y, -rotation.z); } static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { @@ -344,6 +372,8 @@ void FrameGrabber::reset() { #endif } + + void FrameGrabber::grabFrame() { if (!(_initialized || init())) { return; @@ -377,11 +407,27 @@ void FrameGrabber::grabFrame() { continue; } _userGenerator.GetSkeletonCap().GetSkeletonJoint(_userID, activeJoints[i], transform); + glm::quat rotation = xnToGLM(transform.orientation.orientation); + int parent = getParentJoint(activeJoints[i]); + if (parent != -1) { + XnSkeletonJointOrientation parentOrientation; + _userGenerator.GetSkeletonCap().GetSkeletonJointOrientation( + _userID, (XnSkeletonJoint)parent, parentOrientation); + if (i == XN_SKEL_TORSO) { + glm::vec3 eulers = safeEulerAngles(rotation); + printLog("a: %g %g %g\n", eulers.x, eulers.y, eulers.z); + } + rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation; + if (i == XN_SKEL_TORSO) { + glm::vec3 eulers = safeEulerAngles(rotation); + printLog("r: %g %g %g\n", eulers.x, eulers.y, eulers.z); + } + } XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); const float METERS_PER_MM = 1.0f / 1000.0f; joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM, - xnToGLM(transform.orientation.orientation), xnToGLM(projected)); + rotation, xnToGLM(projected)); } } } @@ -463,7 +509,7 @@ bool FrameGrabber::init() { _userGenerator.GetSkeletonCap().RegisterToCalibrationStart(calibrationStarted, 0, calibrationStartCallback); _userGenerator.GetSkeletonCap().RegisterToCalibrationComplete(calibrationCompleted, 0, calibrationCompleteCallback); - _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_ALL); + _userGenerator.GetSkeletonCap().SetSkeletonProfile(XN_SKEL_PROFILE_UPPER); _xnContext.StartGeneratingAll(); return true; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bd3b6a357a..20fc780a9b 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -152,6 +152,13 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { } } + // skeleton joints + *destinationBuffer++ = (unsigned char)_joints.size(); + for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { + *destinationBuffer++ = (unsigned char)it->jointID; + destinationBuffer += packOrientationQuatToBytes(destinationBuffer, it->rotation); + } + return destinationBuffer - bufferStart; } @@ -263,6 +270,16 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { _handData->setFingerRoots(fingerRoots); } + // skeleton joints + if (sourceBuffer - startPosition < numBytes) // safety check + { + _joints.resize(*sourceBuffer++); + for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { + it->jointID = *sourceBuffer++; + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, it->rotation); + } + } + return sourceBuffer - startPosition; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 90d437f253..012f0c97a0 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -150,7 +150,6 @@ class JointData { public: int jointID; - glm::vec3 position; glm::quat rotation; }; From 43258f9dcdf1e8dd12ad5117a25699f157f1f51c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 3 Jul 2013 17:12:43 -0700 Subject: [PATCH 31/63] Use 15 blinks per minute as a base/maximum frequency. --- interface/src/Head.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 6a6643352a..6ad44e1078 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -189,12 +189,13 @@ void Head::simulate(float deltaTime, bool isMine) { const float FULLY_CLOSED = 1.0f; if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { // no blinking when brows are raised; blink less with increasing loudness - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.75f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo( - sqrtf(_averageLoudness) * ROOT_LOUDNESS_TO_BLINK_INTERVAL, deltaTime))) { + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(_averageLoudness) * + ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { _leftEyeBlinkVelocity = BLINK_SPEED; _rightEyeBlinkVelocity = BLINK_SPEED; - } + } } else { _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); From 2de6d3eb6a0b1a6119e7e237fee1f2edc45550a2 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 22:55:01 -0700 Subject: [PATCH 32/63] Make 'Go Home' an option from the options because it conflicts with 'G' for gravity on/off --- interface/src/Application.cpp | 3 ++- interface/src/Head.cpp | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 56469938ff..e6433d845a 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1472,7 +1472,8 @@ void Application::initMenu() { (_testPing = optionsMenu->addAction("Test Ping"))->setCheckable(true); _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("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true); + optionsMenu->addAction("Go Home", this, SLOT(goHome())); QMenu* renderMenu = menuBar->addMenu("Render"); (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 75c62ce87e..66f2123e2b 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -228,6 +228,9 @@ void Head::simulate(float deltaTime, bool isMine) { const float CAMERA_STOP_TOLERANCE_DEGREES = 0.25f; const float CAMERA_START_TOLERANCE_DEGREES = 15.0f; float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw)); + //if (cameraHeadAngleDifference > 0.1f) { + // printLog("angleDiff = %0.2f\n", cameraHeadAngleDifference); + //} if (_isCameraMoving) { _cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE, 0.f, From 0f312d4c9b432f2b3b249aac1e85650d2ae0399c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 23:17:43 -0700 Subject: [PATCH 33/63] Tuned smooth gyro look values to feel better. --- interface/src/Head.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 429b78cd2e..9812d80acf 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -223,11 +223,11 @@ void Head::simulate(float deltaTime, bool isMine) { if (isMine && _cameraFollowsHead) { // If we are using gyros and using gyroLook, have the camera follow head but with a null region // to create stable rendering view with small head movements. - const float CAMERA_FOLLOW_HEAD_RATE_START = 0.05f; - const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.25f; - const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.5f; - const float CAMERA_STOP_TOLERANCE_DEGREES = 0.25f; - const float CAMERA_START_TOLERANCE_DEGREES = 15.0f; + const float CAMERA_FOLLOW_HEAD_RATE_START = 0.01f; + const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f; + const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f; + const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f; + const float CAMERA_START_TOLERANCE_DEGREES = 2.0f; float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw)); //if (cameraHeadAngleDifference > 0.1f) { // printLog("angleDiff = %0.2f\n", cameraHeadAngleDifference); From 5df09b293248d7c85d9cdb7353a64f0f7de10093 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 23:20:55 -0700 Subject: [PATCH 34/63] remove debugging printf --- interface/src/Head.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 9812d80acf..6bf6c65341 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -229,9 +229,6 @@ void Head::simulate(float deltaTime, bool isMine) { const float CAMERA_STOP_TOLERANCE_DEGREES = 0.1f; const float CAMERA_START_TOLERANCE_DEGREES = 2.0f; float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw)); - //if (cameraHeadAngleDifference > 0.1f) { - // printLog("angleDiff = %0.2f\n", cameraHeadAngleDifference); - //} if (_isCameraMoving) { _cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE, 0.f, From d70eb3854221af3f9a232557154009811a72baa2 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 23:43:53 -0700 Subject: [PATCH 35/63] Properly make the goHome() function in slot group --- interface/src/Application.cpp | 1 + interface/src/Application.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f0ec794ada..a8e96bfbd7 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2791,6 +2791,7 @@ void Application::eyedropperVoxelUnderCursor() { } void Application::goHome() { + printLog("Going Home!\n"); _myAvatar.setPosition(START_LOCATION); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 41ed32ad68..01f85812d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -150,6 +150,8 @@ private slots: void copyVoxels(); void pasteVoxels(); void runTests(); + void goHome(); + private: static void broadcastToNodes(unsigned char* data, size_t bytes, const char type); @@ -181,7 +183,6 @@ private: void maybeEditVoxelUnderCursor(); void deleteVoxelUnderCursor(); void eyedropperVoxelUnderCursor(); - void goHome(); void resetSensors(); void setMenuShortcutsEnabled(bool enabled); From 2da132be65dae8192ca0dafad5179096d6a34187 Mon Sep 17 00:00:00 2001 From: tosh Date: Thu, 4 Jul 2013 21:21:11 +0200 Subject: [PATCH 36/63] upstream merge --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c8699cde24..ffa13c6364 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -500,7 +500,7 @@ void Application::broadcastToNodes(unsigned char* data, size_t bytes, const char default: return; } - int n = AgentList::getInstance()->broadcastToAgents(data, bytes, & type, 1); + int n = NodeList::getInstance()->broadcastToNodes(data, bytes, & type, 1); self->_bandwidthMeter.outputStream(channel).updateValue(n * bytes); } @@ -887,8 +887,8 @@ void Application::sendPingPackets() { pingPacket[0] = PACKET_HEADER_PING; memcpy(&pingPacket[1], ¤tTime, sizeof(currentTime)); - for (int i = 0; i < sizeof(agentTypesOfInterest) / sizeof(char); ++i) { - getInstance()->broadcastToAgents(pingPacket, 1 + sizeof(currentTime), agentTypesOfInterest[i]); + for (int i = 0; i < sizeof(nodeTypesOfInterest) / sizeof(char); ++i) { + getInstance()->broadcastToNodes(pingPacket, 1 + sizeof(currentTime), nodeTypesOfInterest[i]); } } From c2e2ff0ffecad4d9e35b308dcb051539d9a2865d Mon Sep 17 00:00:00 2001 From: tosh Date: Fri, 5 Jul 2013 14:16:50 +0200 Subject: [PATCH 37/63] changes broadcast function in Application to use a different name but the same signature as in NodeList --- interface/src/Application.cpp | 66 ++++++++++++++++++++--------------- interface/src/Application.h | 4 ++- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ffa13c6364..d9973bee02 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -483,32 +483,41 @@ void Application::resizeGL(int width, int height) { glLoadIdentity(); } -void Application::broadcastToNodes(unsigned char* data, size_t bytes, const char type) { - +void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const char* nodeTypes, int numNodeTypes) { Application* self = getInstance(); - BandwidthMeter::ChannelIndex channel; - switch (type) { - case NODE_TYPE_AGENT: - case NODE_TYPE_AVATAR_MIXER: - channel = BandwidthMeter::AVATARS; - break; - case NODE_TYPE_VOXEL_SERVER: - channel = BandwidthMeter::VOXELS; - if (self->_renderVoxels->isChecked()) { - break; + for (int i = 0; i < numNodeTypes; ++i) { + + // Intercept data to voxel server when voxels are disabled + if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && ! self->_renderVoxels->isChecked()) { + continue; } - default: - return; + + // Perform the broadcast for one type + int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); + + // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) + BandwidthMeter::ChannelIndex channel; + switch (nodeTypes[i]) { + case NODE_TYPE_AGENT: + case NODE_TYPE_AVATAR_MIXER: + channel = BandwidthMeter::AVATARS; + break; + case NODE_TYPE_VOXEL_SERVER: + channel = BandwidthMeter::VOXELS; + break; + default: + continue; + } + self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } - int n = NodeList::getInstance()->broadcastToNodes(data, bytes, & type, 1); - self->_bandwidthMeter.outputStream(channel).updateValue(n * bytes); } void Application::sendVoxelServerAddScene() { char message[100]; sprintf(message,"%c%s",'Z',"add scene"); int messageSize = strlen(message) + 1; - broadcastToNodes((unsigned char*)message, messageSize, NODE_TYPE_VOXEL_SERVER); + controlledBroadcastToNodes((unsigned char*)message, messageSize, & NODE_TYPE_VOXEL_SERVER, 1); } void Application::keyPressEvent(QKeyEvent* event) { @@ -887,10 +896,8 @@ void Application::sendPingPackets() { pingPacket[0] = PACKET_HEADER_PING; memcpy(&pingPacket[1], ¤tTime, sizeof(currentTime)); - for (int i = 0; i < sizeof(nodeTypesOfInterest) / sizeof(char); ++i) { - getInstance()->broadcastToNodes(pingPacket, 1 + sizeof(currentTime), nodeTypesOfInterest[i]); - } - + getInstance()->controlledBroadcastToNodes(pingPacket, 1 + sizeof(currentTime), + nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); } // Every second, check the frame rates and other stuff @@ -991,7 +998,7 @@ void Application::sendAvatarVoxelURLMessage(const QUrl& url) { message.append((const char*)&ownerID, sizeof(ownerID)); message.append(url.toEncoded()); - broadcastToNodes((unsigned char*)message.data(), message.size(), NODE_TYPE_AVATAR_MIXER); + controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), & NODE_TYPE_AVATAR_MIXER, 1); } void Application::processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes) { @@ -1224,7 +1231,7 @@ void Application::sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail int sizeOut; if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){ - Application::broadcastToNodes(bufferOut, sizeOut, NODE_TYPE_VOXEL_SERVER); + Application::controlledBroadcastToNodes(bufferOut, sizeOut, & NODE_TYPE_VOXEL_SERVER, 1); delete[] bufferOut; } } @@ -1305,7 +1312,7 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { // if we have room don't have room in the buffer, then send the previously generated message first if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { - broadcastToNodes(args->messageBuffer, args->bufferInUse, NODE_TYPE_VOXEL_SERVER); + controlledBroadcastToNodes(args->messageBuffer, args->bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset } @@ -1386,7 +1393,7 @@ void Application::importVoxels() { // If we have voxels left in the packet, then send the packet if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { - broadcastToNodes(args.messageBuffer, args.bufferInUse, NODE_TYPE_VOXEL_SERVER); + controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); } if (calculatedOctCode) { @@ -1438,7 +1445,7 @@ void Application::pasteVoxels() { // If we have voxels left in the packet, then send the packet if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { - broadcastToNodes(args.messageBuffer, args.bufferInUse, NODE_TYPE_VOXEL_SERVER); + controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); } if (calculatedOctCode) { @@ -1986,9 +1993,10 @@ void Application::updateAvatar(float deltaTime) { endOfBroadcastStringWrite += packNodeId(endOfBroadcastStringWrite, nodeList->getOwnerID()); endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - - broadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, NODE_TYPE_VOXEL_SERVER); - broadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, NODE_TYPE_AVATAR_MIXER); + + const char nodeTypesOfInterest[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AVATAR_MIXER }; + controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, + nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); // once in a while, send my voxel url const float AVATAR_VOXEL_URL_SEND_INTERVAL = 1.0f; // seconds diff --git a/interface/src/Application.h b/interface/src/Application.h index 47ee159f74..2b4bd27a3d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -152,7 +152,9 @@ private slots: void runTests(); private: - static void broadcastToNodes(unsigned char* data, size_t bytes, const char type); + static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const char* nodeTypes, int numNodeTypes); + static void sendVoxelServerAddScene(); static bool sendVoxelsOperation(VoxelNode* node, void* extraData); static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); From ff5d1455aa20c808cb6d1f73bf74d3c67157bc0a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Jul 2013 17:45:29 -0700 Subject: [PATCH 38/63] More work on joint tracking. --- interface/src/Avatar.cpp | 7 ++++- interface/src/Skeleton.cpp | 3 +- interface/src/Webcam.cpp | 61 +++++++------------------------------- 3 files changed, 18 insertions(+), 53 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 27ccf0db4b..6323820f65 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -499,7 +499,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // apply joint data (if any) to skeleton bool enableHandMovement = true; for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { - _skeleton.joint[it->jointID].rotation = it->rotation; + _skeleton.joint[it->jointID].absoluteRotation = orientation * it->rotation; + + AvatarJointID parent = _skeleton.joint[it->jointID].parent; + const glm::quat& parentRotation = (parent == AVATAR_JOINT_NULL) ? + orientation : _skeleton.joint[parent].absoluteRotation; + _skeleton.joint[it->jointID].rotation = glm::inverse(parentRotation) * _skeleton.joint[it->jointID].absoluteRotation; // disable hand movement if we have joint info for the right wrist enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST); diff --git a/interface/src/Skeleton.cpp b/interface/src/Skeleton.cpp index 1167194534..5bf8538109 100644 --- a/interface/src/Skeleton.cpp +++ b/interface/src/Skeleton.cpp @@ -121,8 +121,7 @@ void Skeleton::initialize() { joint[b].absoluteBindPosePosition = joint[ joint[b].parent ].absoluteBindPosePosition + joint[b].bindPosePosition; glm::vec3 parentDirection = joint[ joint[b].parent ].absoluteBindPoseRotation * JOINT_DIRECTION; - joint[b].absoluteBindPoseRotation = rotationBetween(parentDirection, joint[b].bindPosePosition) * - joint[ joint[b].parent ].absoluteBindPoseRotation; + joint[b].absoluteBindPoseRotation = joint[ joint[b].parent ].absoluteBindPoseRotation; } } } diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index d4fde500bd..fd44b54c4a 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -283,13 +283,13 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { case XN_SKEL_NECK: return AVATAR_JOINT_NECK_BASE; case XN_SKEL_TORSO: return AVATAR_JOINT_TORSO; - case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_RIGHT_SHOULDER; - case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_ELBOW; - case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_RIGHT_WRIST; + case XN_SKEL_LEFT_SHOULDER: return AVATAR_JOINT_RIGHT_COLLAR; + case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_SHOULDER; + case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_RIGHT_ELBOW; - case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_LEFT_SHOULDER; - case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_ELBOW; - case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_LEFT_WRIST; + case XN_SKEL_RIGHT_SHOULDER: return AVATAR_JOINT_LEFT_COLLAR; + case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_SHOULDER; + case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_LEFT_ELBOW; case XN_SKEL_LEFT_HIP: return AVATAR_JOINT_RIGHT_HIP; case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; @@ -303,37 +303,16 @@ static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { } } -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; - } -} - static glm::vec3 xnToGLM(const XnVector3D& vector, bool flip = false) { return glm::vec3(vector.X * (flip ? -1 : 1), vector.Y, vector.Z); } static glm::quat xnToGLM(const XnMatrix3X3& matrix) { glm::quat rotation = glm::quat_cast(glm::mat3( - matrix.elements[0], matrix.elements[3], matrix.elements[6], - matrix.elements[1], matrix.elements[4], matrix.elements[7], - matrix.elements[2], matrix.elements[5], matrix.elements[8])); - return glm::quat(rotation.w, rotation.x, rotation.y, -rotation.z); + matrix.elements[0], matrix.elements[1], matrix.elements[2], + matrix.elements[3], matrix.elements[4], matrix.elements[5], + matrix.elements[6], matrix.elements[7], matrix.elements[8])); + return glm::quat(rotation.w, -rotation.x, rotation.y, rotation.z); } static void XN_CALLBACK_TYPE newUser(UserGenerator& generator, XnUserID id, void* cookie) { @@ -372,8 +351,6 @@ void FrameGrabber::reset() { #endif } - - void FrameGrabber::grabFrame() { if (!(_initialized || init())) { return; @@ -407,27 +384,11 @@ void FrameGrabber::grabFrame() { continue; } _userGenerator.GetSkeletonCap().GetSkeletonJoint(_userID, activeJoints[i], transform); - glm::quat rotation = xnToGLM(transform.orientation.orientation); - int parent = getParentJoint(activeJoints[i]); - if (parent != -1) { - XnSkeletonJointOrientation parentOrientation; - _userGenerator.GetSkeletonCap().GetSkeletonJointOrientation( - _userID, (XnSkeletonJoint)parent, parentOrientation); - if (i == XN_SKEL_TORSO) { - glm::vec3 eulers = safeEulerAngles(rotation); - printLog("a: %g %g %g\n", eulers.x, eulers.y, eulers.z); - } - rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation; - if (i == XN_SKEL_TORSO) { - glm::vec3 eulers = safeEulerAngles(rotation); - printLog("r: %g %g %g\n", eulers.x, eulers.y, eulers.z); - } - } XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); const float METERS_PER_MM = 1.0f / 1000.0f; joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM, - rotation, xnToGLM(projected)); + xnToGLM(transform.orientation.orientation), xnToGLM(projected)); } } } From 9c977450e36e0f2b1e9e0a478aff48f4fc13a1dd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Jul 2013 22:14:32 -0700 Subject: [PATCH 39/63] Finally got the rotations figured out; I had forgotten the default pose/bind pose distinction. --- interface/src/Avatar.cpp | 7 +---- interface/src/Skeleton.cpp | 49 ++++++++++-------------------- interface/src/Skeleton.h | 1 - interface/src/Webcam.cpp | 61 +++++++++++++++++++++++++++----------- 4 files changed, 60 insertions(+), 58 deletions(-) diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 6323820f65..27ccf0db4b 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -499,12 +499,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // apply joint data (if any) to skeleton bool enableHandMovement = true; for (vector::iterator it = _joints.begin(); it != _joints.end(); it++) { - _skeleton.joint[it->jointID].absoluteRotation = orientation * it->rotation; - - AvatarJointID parent = _skeleton.joint[it->jointID].parent; - const glm::quat& parentRotation = (parent == AVATAR_JOINT_NULL) ? - orientation : _skeleton.joint[parent].absoluteRotation; - _skeleton.joint[it->jointID].rotation = glm::inverse(parentRotation) * _skeleton.joint[it->jointID].absoluteRotation; + _skeleton.joint[it->jointID].rotation = it->rotation; // disable hand movement if we have joint info for the right wrist enableHandMovement &= (it->jointID != AVATAR_JOINT_RIGHT_WRIST); diff --git a/interface/src/Skeleton.cpp b/interface/src/Skeleton.cpp index 5bf8538109..0d166abfa1 100644 --- a/interface/src/Skeleton.cpp +++ b/interface/src/Skeleton.cpp @@ -6,6 +6,7 @@ #include "Skeleton.h" #include "Util.h" +#include "world.h" const float BODY_SPRING_DEFAULT_TIGHTNESS = 1000.0f; const float FLOATING_HEIGHT = 0.13f; @@ -18,12 +19,21 @@ void Skeleton::initialize() { for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { joint[b].parent = AVATAR_JOINT_NULL; joint[b].position = glm::vec3(0.0, 0.0, 0.0); - joint[b].defaultPosePosition = glm::vec3(0.0, 0.0, 0.0); joint[b].rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); joint[b].length = 0.0; joint[b].bindRadius = 1.0f / 8; } + // put the arms at the side + joint[AVATAR_JOINT_LEFT_ELBOW].rotation = glm::quat(glm::vec3(0.0f, 0.0f, PIf * 0.5f)); + joint[AVATAR_JOINT_RIGHT_ELBOW].rotation = glm::quat(glm::vec3(0.0f, 0.0f, -PIf * 0.5f)); + + // bend the knees + joint[AVATAR_JOINT_LEFT_KNEE].rotation = joint[AVATAR_JOINT_RIGHT_KNEE].rotation = + glm::quat(glm::vec3(PIf / 8.0f, 0.0f, 0.0f)); + joint[AVATAR_JOINT_LEFT_HEEL].rotation = joint[AVATAR_JOINT_RIGHT_HEEL].rotation = + glm::quat(glm::vec3(-PIf / 4.0f, 0.0f, 0.0f)); + // specify the parental hierarchy joint[ AVATAR_JOINT_PELVIS ].parent = AVATAR_JOINT_NULL; joint[ AVATAR_JOINT_TORSO ].parent = AVATAR_JOINT_PELVIS; @@ -80,39 +90,9 @@ void Skeleton::initialize() { joint[ AVATAR_JOINT_RIGHT_HEEL ].bindPosePosition = glm::vec3( 0.00, -0.23, 0.00 ); joint[ AVATAR_JOINT_RIGHT_TOES ].bindPosePosition = glm::vec3( 0.00, 0.00, -0.06 ); - // specify the default pose position - joint[ AVATAR_JOINT_PELVIS ].defaultPosePosition = glm::vec3( 0.0, 0.0, 0.0 ); - joint[ AVATAR_JOINT_TORSO ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); - joint[ AVATAR_JOINT_CHEST ].defaultPosePosition = glm::vec3( 0.0, 0.09, -0.01 ); - joint[ AVATAR_JOINT_NECK_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.14, 0.01 ); - joint[ AVATAR_JOINT_HEAD_BASE ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 ); - joint[ AVATAR_JOINT_HEAD_TOP ].defaultPosePosition = glm::vec3( 0.0, 0.04, 0.00 ); - - joint[ AVATAR_JOINT_LEFT_COLLAR ].defaultPosePosition = glm::vec3( -0.06, 0.04, 0.01 ); - joint[ AVATAR_JOINT_LEFT_SHOULDER ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.01 ); - joint[ AVATAR_JOINT_LEFT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); - joint[ AVATAR_JOINT_LEFT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); - joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); - - joint[ AVATAR_JOINT_RIGHT_COLLAR ].defaultPosePosition = glm::vec3( 0.06, 0.04, 0.01 ); - joint[ AVATAR_JOINT_RIGHT_SHOULDER ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.01 ); - joint[ AVATAR_JOINT_RIGHT_ELBOW ].defaultPosePosition = glm::vec3( 0.0, -0.16, 0.0 ); - joint[ AVATAR_JOINT_RIGHT_WRIST ].defaultPosePosition = glm::vec3( 0.0, -0.117, 0.0 ); - joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].defaultPosePosition = glm::vec3( 0.0, -0.1, 0.0 ); - - joint[ AVATAR_JOINT_LEFT_HIP ].defaultPosePosition = glm::vec3( -0.05, 0.0, 0.02 ); - joint[ AVATAR_JOINT_LEFT_KNEE ].defaultPosePosition = glm::vec3( 0.01, -0.25, -0.03 ); - joint[ AVATAR_JOINT_LEFT_HEEL ].defaultPosePosition = glm::vec3( 0.01, -0.22, 0.08 ); - joint[ AVATAR_JOINT_LEFT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); - - joint[ AVATAR_JOINT_RIGHT_HIP ].defaultPosePosition = glm::vec3( 0.05, 0.0, 0.02 ); - joint[ AVATAR_JOINT_RIGHT_KNEE ].defaultPosePosition = glm::vec3( -0.01, -0.25, -0.03 ); - joint[ AVATAR_JOINT_RIGHT_HEEL ].defaultPosePosition = glm::vec3( -0.01, -0.22, 0.08 ); - joint[ AVATAR_JOINT_RIGHT_TOES ].defaultPosePosition = glm::vec3( 0.00, -0.03, -0.05 ); - // calculate bone length, absolute bind positions/rotations for (int b = 0; b < NUM_AVATAR_JOINTS; b++) { - joint[b].length = glm::length(joint[b].defaultPosePosition); + joint[b].length = glm::length(joint[b].bindPosePosition); if (joint[b].parent == AVATAR_JOINT_NULL) { joint[b].absoluteBindPosePosition = joint[b].bindPosePosition; @@ -121,7 +101,8 @@ void Skeleton::initialize() { joint[b].absoluteBindPosePosition = joint[ joint[b].parent ].absoluteBindPosePosition + joint[b].bindPosePosition; glm::vec3 parentDirection = joint[ joint[b].parent ].absoluteBindPoseRotation * JOINT_DIRECTION; - joint[b].absoluteBindPoseRotation = joint[ joint[b].parent ].absoluteBindPoseRotation; + joint[b].absoluteBindPoseRotation = rotationBetween(parentDirection, joint[b].bindPosePosition) * + joint[ joint[b].parent ].absoluteBindPoseRotation; } } } @@ -139,7 +120,7 @@ void Skeleton::update(float deltaTime, const glm::quat& orientation, glm::vec3 p joint[b].position = joint[ joint[b].parent ].position; } - glm::vec3 rotatedJointVector = joint[b].absoluteRotation * joint[b].defaultPosePosition; + glm::vec3 rotatedJointVector = joint[b].absoluteRotation * joint[b].bindPosePosition; joint[b].position += rotatedJointVector; } } diff --git a/interface/src/Skeleton.h b/interface/src/Skeleton.h index bb953fe947..8d99a80fa6 100644 --- a/interface/src/Skeleton.h +++ b/interface/src/Skeleton.h @@ -63,7 +63,6 @@ public: { AvatarJointID parent; // which joint is this joint connected to? glm::vec3 position; // the position at the "end" of the joint - in global space - glm::vec3 defaultPosePosition; // the parent relative position when the avatar is in the default pose glm::vec3 bindPosePosition; // the parent relative position when the avatar is in the "T-pose" glm::vec3 absoluteBindPosePosition; // the absolute position when the avatar is in the "T-pose" glm::quat absoluteBindPoseRotation; // the absolute rotation when the avatar is in the "T-pose" diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index fd44b54c4a..a81bec8ddd 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -219,7 +219,7 @@ void Webcam::setFrame(const Mat& frame, int format, const Mat& depth, const Rota if (!_joints[i].isValid) { continue; } - const float JOINT_SMOOTHING = 0.95f; + const float JOINT_SMOOTHING = 0.5f; _estimatedJoints[i].isValid = true; _estimatedJoints[i].position = glm::mix(_joints[i].position - origin, _estimatedJoints[i].position, JOINT_SMOOTHING); @@ -279,30 +279,49 @@ FrameGrabber::~FrameGrabber() { #ifdef HAVE_OPENNI static AvatarJointID xnToAvatarJoint(XnSkeletonJoint joint) { switch (joint) { - case XN_SKEL_HEAD: return AVATAR_JOINT_HEAD_BASE; - case XN_SKEL_NECK: return AVATAR_JOINT_NECK_BASE; - case XN_SKEL_TORSO: return AVATAR_JOINT_TORSO; + 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_COLLAR; - case XN_SKEL_LEFT_ELBOW: return AVATAR_JOINT_RIGHT_SHOULDER; - case XN_SKEL_LEFT_HAND: return AVATAR_JOINT_RIGHT_ELBOW; + 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_COLLAR; - case XN_SKEL_RIGHT_ELBOW: return AVATAR_JOINT_LEFT_SHOULDER; - case XN_SKEL_RIGHT_HAND: return AVATAR_JOINT_LEFT_ELBOW; + 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_HIP; - case XN_SKEL_LEFT_KNEE: return AVATAR_JOINT_RIGHT_KNEE; - case XN_SKEL_LEFT_FOOT: return AVATAR_JOINT_RIGHT_HEEL; + 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_HIP; - case XN_SKEL_RIGHT_KNEE: return AVATAR_JOINT_LEFT_KNEE; - case XN_SKEL_RIGHT_FOOT: return AVATAR_JOINT_LEFT_HEEL; + 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; } } +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; + } +} + static glm::vec3 xnToGLM(const XnVector3D& vector, bool flip = false) { return glm::vec3(vector.X * (flip ? -1 : 1), vector.Y, vector.Z); } @@ -386,9 +405,17 @@ void FrameGrabber::grabFrame() { _userGenerator.GetSkeletonCap().GetSkeletonJoint(_userID, activeJoints[i], transform); XnVector3D projected; _depthGenerator.ConvertRealWorldToProjective(1, &transform.position.position, &projected); + glm::quat rotation = xnToGLM(transform.orientation.orientation); + int parentJoint = getParentJoint(activeJoints[i]); + if (parentJoint != -1) { + XnSkeletonJointOrientation parentOrientation; + _userGenerator.GetSkeletonCap().GetSkeletonJointOrientation( + _userID, (XnSkeletonJoint)parentJoint, parentOrientation); + rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation; + } const float METERS_PER_MM = 1.0f / 1000.0f; joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM, - xnToGLM(transform.orientation.orientation), xnToGLM(projected)); + rotation, xnToGLM(projected)); } } } From 7a65ebbba37d6ea81202460d14a5f81215f0db13 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 5 Jul 2013 22:17:59 -0700 Subject: [PATCH 40/63] Remove Freenect/LibUSB bits. --- cmake/modules/FindFreenect.cmake | 44 - cmake/modules/FindLibUSB.cmake | 44 - interface/external/LibUSB/include/libusb.h | 1443 ----------------- interface/external/LibUSB/include/libusbi.h | 935 ----------- .../external/LibUSB/include/os/darwin_usb.h | 169 -- .../external/LibUSB/include/os/linux_usbfs.h | 139 -- .../external/LibUSB/include/os/poll_posix.h | 10 - .../external/LibUSB/include/os/poll_windows.h | 115 -- .../LibUSB/include/os/threads_posix.h | 48 - .../LibUSB/include/os/threads_windows.h | 86 - .../external/LibUSB/include/os/windows_usb.h | 608 ------- interface/external/LibUSB/include/version.h | 18 - .../external/LibUSB/lib/MacOS/libusb-1.0.a | Bin 264096 -> 0 bytes .../external/LibUSB/lib/UNIX/libusb-1.0.a | Bin 372942 -> 0 bytes .../external/LibUSB/lib/UNIX/libusb-1.0.la | 41 - .../freenect/include/libfreenect-audio.h | 120 -- .../include/libfreenect-registration.h | 129 -- .../external/freenect/include/libfreenect.h | 632 -------- .../external/freenect/lib/MacOS/libfreenect.a | Bin 68160 -> 0 bytes .../freenect/lib/MacOS/libfreenect_sync.a | Bin 10496 -> 0 bytes .../external/freenect/lib/UNIX/libfreenect.a | Bin 65880 -> 0 bytes .../freenect/lib/UNIX/libfreenect_sync.a | Bin 12952 -> 0 bytes 22 files changed, 4581 deletions(-) delete mode 100644 cmake/modules/FindFreenect.cmake delete mode 100644 cmake/modules/FindLibUSB.cmake delete mode 100644 interface/external/LibUSB/include/libusb.h delete mode 100644 interface/external/LibUSB/include/libusbi.h delete mode 100644 interface/external/LibUSB/include/os/darwin_usb.h delete mode 100644 interface/external/LibUSB/include/os/linux_usbfs.h delete mode 100644 interface/external/LibUSB/include/os/poll_posix.h delete mode 100644 interface/external/LibUSB/include/os/poll_windows.h delete mode 100644 interface/external/LibUSB/include/os/threads_posix.h delete mode 100644 interface/external/LibUSB/include/os/threads_windows.h delete mode 100644 interface/external/LibUSB/include/os/windows_usb.h delete mode 100644 interface/external/LibUSB/include/version.h delete mode 100644 interface/external/LibUSB/lib/MacOS/libusb-1.0.a delete mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.a delete mode 100644 interface/external/LibUSB/lib/UNIX/libusb-1.0.la delete mode 100644 interface/external/freenect/include/libfreenect-audio.h delete mode 100644 interface/external/freenect/include/libfreenect-registration.h delete mode 100644 interface/external/freenect/include/libfreenect.h delete mode 100644 interface/external/freenect/lib/MacOS/libfreenect.a delete mode 100644 interface/external/freenect/lib/MacOS/libfreenect_sync.a delete mode 100644 interface/external/freenect/lib/UNIX/libfreenect.a delete mode 100644 interface/external/freenect/lib/UNIX/libfreenect_sync.a diff --git a/cmake/modules/FindFreenect.cmake b/cmake/modules/FindFreenect.cmake deleted file mode 100644 index 97adc4302a..0000000000 --- a/cmake/modules/FindFreenect.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Try to find the Freenect library to read from Kinect -# -# You must provide a FREENECT_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# FREENECT_FOUND - system found Freenect -# FREENECT_INCLUDE_DIRS - the Freenect include directory -# FREENECT_LIBRARIES - Link this to use Freenect -# -# Created on 6/25/2013 by Andrzej Kapolka -# Copyright (c) 2013 High Fidelity -# - -if (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) - # in cache already - set(FREENECT_FOUND TRUE) -else (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) - find_path(FREENECT_INCLUDE_DIRS libfreenect.h ${FREENECT_ROOT_DIR}/include) - - if (APPLE) - find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/MacOS/) - elseif (UNIX) - find_library(FREENECT_LIBRARIES libfreenect.a ${FREENECT_ROOT_DIR}/lib/UNIX/) - endif () - - if (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) - set(FREENECT_FOUND TRUE) - endif (FREENECT_INCLUDE_DIRS AND FREENECT_LIBRARIES) - - if (FREENECT_FOUND) - if (NOT FREENECT_FIND_QUIETLY) - message(STATUS "Found Freenect: ${FREENECT_LIBRARIES}") - endif (NOT FREENECT_FIND_QUIETLY) - else (FREENECT_FOUND) - if (FREENECT_FIND_REQUIRED) - message(FATAL_ERROR "Could not find Freenect") - endif (FREENECT_FIND_REQUIRED) - endif (FREENECT_FOUND) - - # show the FREENECT_INCLUDE_DIRS and FREENECT_LIBRARIES variables only in the advanced view - mark_as_advanced(FREENECT_INCLUDE_DIRS FREENECT_LIBRARIES) - -endif (FREENECT_LIBRARIES AND FREENECT_INCLUDE_DIRS) diff --git a/cmake/modules/FindLibUSB.cmake b/cmake/modules/FindLibUSB.cmake deleted file mode 100644 index f9599752a1..0000000000 --- a/cmake/modules/FindLibUSB.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# Try to find the LibUSB library to interact with USB devices -# -# You must provide a LIBUSB_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# LIBUSB_FOUND - system found LibUSB -# LIBUSB_INCLUDE_DIRS - the LibUSB include directory -# LIBUSB_LIBRARIES - Link this to use LibUSB -# -# Created on 6/25/2013 by Andrzej Kapolka -# Copyright (c) 2013 High Fidelity -# - -if (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) - # in cache already - set(LIBUSB_FOUND TRUE) -else (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) - find_path(LIBUSB_INCLUDE_DIRS libusb.h ${LIBUSB_ROOT_DIR}/include) - - if (APPLE) - find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/MacOS/) - elseif (UNIX) - find_library(LIBUSB_LIBRARIES libusb-1.0.a ${LIBUSB_ROOT_DIR}/lib/UNIX/) - endif () - - if (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) - set(LIBUSB_FOUND TRUE) - endif (LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) - - if (LIBUSB_FOUND) - if (NOT LIBUSB_FIND_QUIETLY) - message(STATUS "Found LibUSB: ${LIBUSB_LIBRARIES}") - endif (NOT LIBUSB_FIND_QUIETLY) - else (LIBUSB_FOUND) - if (LIBUSB_FIND_REQUIRED) - message(FATAL_ERROR "Could not find LibUSB") - endif (LIBUSB_FIND_REQUIRED) - endif (LIBUSB_FOUND) - - # show the LIBUSB_INCLUDE_DIRS and LIBUSB_LIBRARIES variables only in the advanced view - mark_as_advanced(LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES) - -endif (LIBUSB_LIBRARIES AND LIBUSB_INCLUDE_DIRS) diff --git a/interface/external/LibUSB/include/libusb.h b/interface/external/LibUSB/include/libusb.h deleted file mode 100644 index 58b406f247..0000000000 --- a/interface/external/LibUSB/include/libusb.h +++ /dev/null @@ -1,1443 +0,0 @@ -/* - * Public libusb header file - * Copyright (C) 2007-2008 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSB_H -#define LIBUSB_H - -#ifdef _MSC_VER -/* on MS environments, the inline keyword is available in C++ only */ -#define inline __inline -/* ssize_t is also not available (copy/paste from MinGW) */ -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -#undef ssize_t -#ifdef _WIN64 - typedef __int64 ssize_t; -#else - typedef int ssize_t; -#endif /* _WIN64 */ -#endif /* _SSIZE_T_DEFINED */ -#endif /* _MSC_VER */ - -/* stdint.h is also not usually available on MS */ -#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -#else -#include -#endif - -#include -#include -#include - -#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) -#include -#endif - -/* 'interface' might be defined as a macro on Windows, so we need to - * undefine it so as not to break the current libusb API, because - * libusb_config_descriptor has an 'interface' member - * As this can be problematic if you include windows.h after libusb.h - * in your sources, we force windows.h to be included first. */ -#if defined(_WIN32) || defined(__CYGWIN__) -#include -#if defined(interface) -#undef interface -#endif -#endif - -/** \def LIBUSB_CALL - * \ingroup misc - * libusb's Windows calling convention. - * - * Under Windows, the selection of available compilers and configurations - * means that, unlike other platforms, there is not one true calling - * convention (calling convention: the manner in which parameters are - * passed to funcions in the generated assembly code). - * - * Matching the Windows API itself, libusb uses the WINAPI convention (which - * translates to the stdcall convention) and guarantees that the - * library is compiled in this way. The public header file also includes - * appropriate annotations so that your own software will use the right - * convention, even if another convention is being used by default within - * your codebase. - * - * The one consideration that you must apply in your software is to mark - * all functions which you use as libusb callbacks with this LIBUSB_CALL - * annotation, so that they too get compiled for the correct calling - * convention. - * - * On non-Windows operating systems, this macro is defined as nothing. This - * means that you can apply it to your code without worrying about - * cross-platform compatibility. - */ -/* LIBUSB_CALL must be defined on both definition and declaration of libusb - * functions. You'd think that declaration would be enough, but cygwin will - * complain about conflicting types unless both are marked this way. - * The placement of this macro is important too; it must appear after the - * return type, before the function name. See internal documentation for - * API_EXPORTED. - */ -#if defined(_WIN32) || defined(__CYGWIN__) -#define LIBUSB_CALL WINAPI -#else -#define LIBUSB_CALL -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** \def libusb_cpu_to_le16 - * \ingroup misc - * Convert a 16-bit value from host-endian to little-endian format. On - * little endian systems, this function does nothing. On big endian systems, - * the bytes are swapped. - * \param x the host-endian value to convert - * \returns the value in little-endian byte order - */ -static inline uint16_t libusb_cpu_to_le16(const uint16_t x) -{ - union { - uint8_t b8[2]; - uint16_t b16; - } _tmp; - _tmp.b8[1] = x >> 8; - _tmp.b8[0] = x & 0xff; - return _tmp.b16; -} - -/** \def libusb_le16_to_cpu - * \ingroup misc - * Convert a 16-bit value from little-endian to host-endian format. On - * little endian systems, this function does nothing. On big endian systems, - * the bytes are swapped. - * \param x the little-endian value to convert - * \returns the value in host-endian byte order - */ -#define libusb_le16_to_cpu libusb_cpu_to_le16 - -/* standard USB stuff */ - -/** \ingroup desc - * Device and/or Interface Class codes */ -enum libusb_class_code { - /** In the context of a \ref libusb_device_descriptor "device descriptor", - * this bDeviceClass value indicates that each interface specifies its - * own class information and all interfaces operate independently. - */ - LIBUSB_CLASS_PER_INTERFACE = 0, - - /** Audio class */ - LIBUSB_CLASS_AUDIO = 1, - - /** Communications class */ - LIBUSB_CLASS_COMM = 2, - - /** Human Interface Device class */ - LIBUSB_CLASS_HID = 3, - - /** Physical */ - LIBUSB_CLASS_PHYSICAL = 5, - - /** Printer class */ - LIBUSB_CLASS_PRINTER = 7, - - /** Image class */ - LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ - LIBUSB_CLASS_IMAGE = 6, - - /** Mass storage class */ - LIBUSB_CLASS_MASS_STORAGE = 8, - - /** Hub class */ - LIBUSB_CLASS_HUB = 9, - - /** Data class */ - LIBUSB_CLASS_DATA = 10, - - /** Smart Card */ - LIBUSB_CLASS_SMART_CARD = 0x0b, - - /** Content Security */ - LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, - - /** Video */ - LIBUSB_CLASS_VIDEO = 0x0e, - - /** Personal Healthcare */ - LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, - - /** Diagnostic Device */ - LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, - - /** Wireless class */ - LIBUSB_CLASS_WIRELESS = 0xe0, - - /** Application class */ - LIBUSB_CLASS_APPLICATION = 0xfe, - - /** Class is vendor-specific */ - LIBUSB_CLASS_VENDOR_SPEC = 0xff -}; - -/** \ingroup desc - * Descriptor types as defined by the USB specification. */ -enum libusb_descriptor_type { - /** Device descriptor. See libusb_device_descriptor. */ - LIBUSB_DT_DEVICE = 0x01, - - /** Configuration descriptor. See libusb_config_descriptor. */ - LIBUSB_DT_CONFIG = 0x02, - - /** String descriptor */ - LIBUSB_DT_STRING = 0x03, - - /** Interface descriptor. See libusb_interface_descriptor. */ - LIBUSB_DT_INTERFACE = 0x04, - - /** Endpoint descriptor. See libusb_endpoint_descriptor. */ - LIBUSB_DT_ENDPOINT = 0x05, - - /** HID descriptor */ - LIBUSB_DT_HID = 0x21, - - /** HID report descriptor */ - LIBUSB_DT_REPORT = 0x22, - - /** Physical descriptor */ - LIBUSB_DT_PHYSICAL = 0x23, - - /** Hub descriptor */ - LIBUSB_DT_HUB = 0x29, -}; - -/* Descriptor sizes per descriptor type */ -#define LIBUSB_DT_DEVICE_SIZE 18 -#define LIBUSB_DT_CONFIG_SIZE 9 -#define LIBUSB_DT_INTERFACE_SIZE 9 -#define LIBUSB_DT_ENDPOINT_SIZE 7 -#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define LIBUSB_DT_HUB_NONVAR_SIZE 7 - -#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define LIBUSB_ENDPOINT_DIR_MASK 0x80 - -/** \ingroup desc - * Endpoint direction. Values for bit 7 of the - * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. - */ -enum libusb_endpoint_direction { - /** In: device-to-host */ - LIBUSB_ENDPOINT_IN = 0x80, - - /** Out: host-to-device */ - LIBUSB_ENDPOINT_OUT = 0x00 -}; - -#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ - -/** \ingroup desc - * Endpoint transfer type. Values for bits 0:1 of the - * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. - */ -enum libusb_transfer_type { - /** Control endpoint */ - LIBUSB_TRANSFER_TYPE_CONTROL = 0, - - /** Isochronous endpoint */ - LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, - - /** Bulk endpoint */ - LIBUSB_TRANSFER_TYPE_BULK = 2, - - /** Interrupt endpoint */ - LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 -}; - -/** \ingroup misc - * Standard requests, as defined in table 9-3 of the USB2 specifications */ -enum libusb_standard_request { - /** Request status of the specific recipient */ - LIBUSB_REQUEST_GET_STATUS = 0x00, - - /** Clear or disable a specific feature */ - LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, - - /* 0x02 is reserved */ - - /** Set or enable a specific feature */ - LIBUSB_REQUEST_SET_FEATURE = 0x03, - - /* 0x04 is reserved */ - - /** Set device address for all future accesses */ - LIBUSB_REQUEST_SET_ADDRESS = 0x05, - - /** Get the specified descriptor */ - LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, - - /** Used to update existing descriptors or add new descriptors */ - LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, - - /** Get the current device configuration value */ - LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, - - /** Set device configuration */ - LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, - - /** Return the selected alternate setting for the specified interface */ - LIBUSB_REQUEST_GET_INTERFACE = 0x0A, - - /** Select an alternate interface for the specified interface */ - LIBUSB_REQUEST_SET_INTERFACE = 0x0B, - - /** Set then report an endpoint's synchronization frame */ - LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, -}; - -/** \ingroup misc - * Request type bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. */ -enum libusb_request_type { - /** Standard */ - LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), - - /** Class */ - LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), - - /** Vendor */ - LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), - - /** Reserved */ - LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) -}; - -/** \ingroup misc - * Recipient bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. Values 4 through 31 are reserved. */ -enum libusb_request_recipient { - /** Device */ - LIBUSB_RECIPIENT_DEVICE = 0x00, - - /** Interface */ - LIBUSB_RECIPIENT_INTERFACE = 0x01, - - /** Endpoint */ - LIBUSB_RECIPIENT_ENDPOINT = 0x02, - - /** Other */ - LIBUSB_RECIPIENT_OTHER = 0x03, -}; - -#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C - -/** \ingroup desc - * Synchronization type for isochronous endpoints. Values for bits 2:3 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ -enum libusb_iso_sync_type { - /** No synchronization */ - LIBUSB_ISO_SYNC_TYPE_NONE = 0, - - /** Asynchronous */ - LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, - - /** Adaptive */ - LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, - - /** Synchronous */ - LIBUSB_ISO_SYNC_TYPE_SYNC = 3 -}; - -#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 - -/** \ingroup desc - * Usage type for isochronous endpoints. Values for bits 4:5 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ -enum libusb_iso_usage_type { - /** Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_DATA = 0, - - /** Feedback endpoint */ - LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, - - /** Implicit feedback Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, -}; - -/** \ingroup desc - * A structure representing the standard USB device descriptor. This - * descriptor is documented in section 9.6.1 of the USB 2.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_device_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this - * context. */ - uint8_t bDescriptorType; - - /** USB specification release number in binary-coded decimal. A value of - * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ - uint16_t bcdUSB; - - /** USB-IF class code for the device. See \ref libusb_class_code. */ - uint8_t bDeviceClass; - - /** USB-IF subclass code for the device, qualified by the bDeviceClass - * value */ - uint8_t bDeviceSubClass; - - /** USB-IF protocol code for the device, qualified by the bDeviceClass and - * bDeviceSubClass values */ - uint8_t bDeviceProtocol; - - /** Maximum packet size for endpoint 0 */ - uint8_t bMaxPacketSize0; - - /** USB-IF vendor ID */ - uint16_t idVendor; - - /** USB-IF product ID */ - uint16_t idProduct; - - /** Device release number in binary-coded decimal */ - uint16_t bcdDevice; - - /** Index of string descriptor describing manufacturer */ - uint8_t iManufacturer; - - /** Index of string descriptor describing product */ - uint8_t iProduct; - - /** Index of string descriptor containing device serial number */ - uint8_t iSerialNumber; - - /** Number of possible configurations */ - uint8_t bNumConfigurations; -}; - -/** \ingroup desc - * A structure representing the standard USB endpoint descriptor. This - * descriptor is documented in section 9.6.3 of the USB 2.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_endpoint_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in - * this context. */ - uint8_t bDescriptorType; - - /** The address of the endpoint described by this descriptor. Bits 0:3 are - * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, - * see \ref libusb_endpoint_direction. - */ - uint8_t bEndpointAddress; - - /** Attributes which apply to the endpoint when it is configured using - * the bConfigurationValue. Bits 0:1 determine the transfer type and - * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for - * isochronous endpoints and correspond to \ref libusb_iso_sync_type. - * Bits 4:5 are also only used for isochronous endpoints and correspond to - * \ref libusb_iso_usage_type. Bits 6:7 are reserved. - */ - uint8_t bmAttributes; - - /** Maximum packet size this endpoint is capable of sending/receiving. */ - uint16_t wMaxPacketSize; - - /** Interval for polling endpoint for data transfers. */ - uint8_t bInterval; - - /** For audio devices only: the rate at which synchronization feedback - * is provided. */ - uint8_t bRefresh; - - /** For audio devices only: the address if the synch endpoint */ - uint8_t bSynchAddress; - - /** Extra descriptors. If libusb encounters unknown endpoint descriptors, - * it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup desc - * A structure representing the standard USB interface descriptor. This - * descriptor is documented in section 9.6.5 of the USB 2.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_interface_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE - * in this context. */ - uint8_t bDescriptorType; - - /** Number of this interface */ - uint8_t bInterfaceNumber; - - /** Value used to select this alternate setting for this interface */ - uint8_t bAlternateSetting; - - /** Number of endpoints used by this interface (excluding the control - * endpoint). */ - uint8_t bNumEndpoints; - - /** USB-IF class code for this interface. See \ref libusb_class_code. */ - uint8_t bInterfaceClass; - - /** USB-IF subclass code for this interface, qualified by the - * bInterfaceClass value */ - uint8_t bInterfaceSubClass; - - /** USB-IF protocol code for this interface, qualified by the - * bInterfaceClass and bInterfaceSubClass values */ - uint8_t bInterfaceProtocol; - - /** Index of string descriptor describing this interface */ - uint8_t iInterface; - - /** Array of endpoint descriptors. This length of this array is determined - * by the bNumEndpoints field. */ - const struct libusb_endpoint_descriptor *endpoint; - - /** Extra descriptors. If libusb encounters unknown interface descriptors, - * it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup desc - * A collection of alternate settings for a particular USB interface. - */ -struct libusb_interface { - /** Array of interface descriptors. The length of this array is determined - * by the num_altsetting field. */ - const struct libusb_interface_descriptor *altsetting; - - /** The number of alternate settings that belong to this interface */ - int num_altsetting; -}; - -/** \ingroup desc - * A structure representing the standard USB configuration descriptor. This - * descriptor is documented in section 9.6.3 of the USB 2.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_config_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG - * in this context. */ - uint8_t bDescriptorType; - - /** Total length of data returned for this configuration */ - uint16_t wTotalLength; - - /** Number of interfaces supported by this configuration */ - uint8_t bNumInterfaces; - - /** Identifier value for this configuration */ - uint8_t bConfigurationValue; - - /** Index of string descriptor describing this configuration */ - uint8_t iConfiguration; - - /** Configuration characteristics */ - uint8_t bmAttributes; - - /** Maximum power consumption of the USB device from this bus in this - * configuration when the device is fully opreation. Expressed in units - * of 2 mA. */ - uint8_t MaxPower; - - /** Array of interfaces supported by this configuration. The length of - * this array is determined by the bNumInterfaces field. */ - const struct libusb_interface *interface; - - /** Extra descriptors. If libusb encounters unknown configuration - * descriptors, it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup asyncio - * Setup packet for control transfers. */ -struct libusb_control_setup { - /** Request type. Bits 0:4 determine recipient, see - * \ref libusb_request_recipient. Bits 5:6 determine type, see - * \ref libusb_request_type. Bit 7 determines data transfer direction, see - * \ref libusb_endpoint_direction. - */ - uint8_t bmRequestType; - - /** Request. If the type bits of bmRequestType are equal to - * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD - * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to - * \ref libusb_standard_request. For other cases, use of this field is - * application-specific. */ - uint8_t bRequest; - - /** Value. Varies according to request */ - uint16_t wValue; - - /** Index. Varies according to request, typically used to pass an index - * or offset */ - uint16_t wIndex; - - /** Number of bytes to transfer */ - uint16_t wLength; -}; - -#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) - -/* libusb */ - -struct libusb_context; -struct libusb_device; -struct libusb_device_handle; - -/** \ingroup lib - * Structure representing the libusb version. - */ -struct libusb_version { - /** Library major version. */ - const uint16_t major; - - /** Library minor version. */ - const uint16_t minor; - - /** Library micro version. */ - const uint16_t micro; - - /** Library nano version. This field is only nonzero on Windows. */ - const uint16_t nano; - - /** Library release candidate suffix string, e.g. "-rc4". */ - const char *rc; - - /** Output of `git describe --tags` at library build time. */ - const char *describe; -}; - -/** \ingroup lib - * Structure representing a libusb session. The concept of individual libusb - * sessions allows for your program to use two libraries (or dynamically - * load two modules) which both independently use libusb. This will prevent - * interference between the individual libusb users - for example - * libusb_set_debug() will not affect the other user of the library, and - * libusb_exit() will not destroy resources that the other user is still - * using. - * - * Sessions are created by libusb_init() and destroyed through libusb_exit(). - * If your application is guaranteed to only ever include a single libusb - * user (i.e. you), you do not have to worry about contexts: pass NULL in - * every function call where a context is required. The default context - * will be used. - * - * For more information, see \ref contexts. - */ -typedef struct libusb_context libusb_context; - -/** \ingroup dev - * Structure representing a USB device detected on the system. This is an - * opaque type for which you are only ever provided with a pointer, usually - * originating from libusb_get_device_list(). - * - * Certain operations can be performed on a device, but in order to do any - * I/O you will have to first obtain a device handle using libusb_open(). - * - * Devices are reference counted with libusb_device_ref() and - * libusb_device_unref(), and are freed when the reference count reaches 0. - * New devices presented by libusb_get_device_list() have a reference count of - * 1, and libusb_free_device_list() can optionally decrease the reference count - * on all devices in the list. libusb_open() adds another reference which is - * later destroyed by libusb_close(). - */ -typedef struct libusb_device libusb_device; - - -/** \ingroup dev - * Structure representing a handle on a USB device. This is an opaque type for - * which you are only ever provided with a pointer, usually originating from - * libusb_open(). - * - * A device handle is used to perform I/O and other operations. When finished - * with a device handle, you should call libusb_close(). - */ -typedef struct libusb_device_handle libusb_device_handle; - -/** \ingroup dev - * Speed codes. Indicates the speed at which the device is operating. - */ -enum libusb_speed { - /** The OS doesn't report or know the device speed. */ - LIBUSB_SPEED_UNKNOWN = 0, - - /** The device is operating at low speed (1.5MBit/s). */ - LIBUSB_SPEED_LOW = 1, - - /** The device is operating at full speed (12MBit/s). */ - LIBUSB_SPEED_FULL = 2, - - /** The device is operating at high speed (480MBit/s). */ - LIBUSB_SPEED_HIGH = 3, - - /** The device is operating at super speed (5000MBit/s). */ - LIBUSB_SPEED_SUPER = 4, -}; - -/** \ingroup misc - * Error codes. Most libusb functions return 0 on success or one of these - * codes on failure. - * You can call \ref libusb_error_name() to retrieve a string representation - * of an error code. - */ -enum libusb_error { - /** Success (no error) */ - LIBUSB_SUCCESS = 0, - - /** Input/output error */ - LIBUSB_ERROR_IO = -1, - - /** Invalid parameter */ - LIBUSB_ERROR_INVALID_PARAM = -2, - - /** Access denied (insufficient permissions) */ - LIBUSB_ERROR_ACCESS = -3, - - /** No such device (it may have been disconnected) */ - LIBUSB_ERROR_NO_DEVICE = -4, - - /** Entity not found */ - LIBUSB_ERROR_NOT_FOUND = -5, - - /** Resource busy */ - LIBUSB_ERROR_BUSY = -6, - - /** Operation timed out */ - LIBUSB_ERROR_TIMEOUT = -7, - - /** Overflow */ - LIBUSB_ERROR_OVERFLOW = -8, - - /** Pipe error */ - LIBUSB_ERROR_PIPE = -9, - - /** System call interrupted (perhaps due to signal) */ - LIBUSB_ERROR_INTERRUPTED = -10, - - /** Insufficient memory */ - LIBUSB_ERROR_NO_MEM = -11, - - /** Operation not supported or unimplemented on this platform */ - LIBUSB_ERROR_NOT_SUPPORTED = -12, - - /* NB! Remember to update libusb_error_name() - when adding new error codes here. */ - - /** Other error */ - LIBUSB_ERROR_OTHER = -99, -}; - -/** \ingroup asyncio - * Transfer status codes */ -enum libusb_transfer_status { - /** Transfer completed without error. Note that this does not indicate - * that the entire amount of requested data was transferred. */ - LIBUSB_TRANSFER_COMPLETED, - - /** Transfer failed */ - LIBUSB_TRANSFER_ERROR, - - /** Transfer timed out */ - LIBUSB_TRANSFER_TIMED_OUT, - - /** Transfer was cancelled */ - LIBUSB_TRANSFER_CANCELLED, - - /** For bulk/interrupt endpoints: halt condition detected (endpoint - * stalled). For control endpoints: control request not supported. */ - LIBUSB_TRANSFER_STALL, - - /** Device was disconnected */ - LIBUSB_TRANSFER_NO_DEVICE, - - /** Device sent more data than requested */ - LIBUSB_TRANSFER_OVERFLOW, -}; - -/** \ingroup asyncio - * libusb_transfer.flags values */ -enum libusb_transfer_flags { - /** Report short frames as errors */ - LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, - - /** Automatically free() transfer buffer during libusb_free_transfer() */ - LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, - - /** Automatically call libusb_free_transfer() after callback returns. - * If this flag is set, it is illegal to call libusb_free_transfer() - * from your transfer callback, as this will result in a double-free - * when this flag is acted upon. */ - LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, - - /** Terminate transfers that are a multiple of the endpoint's - * wMaxPacketSize with an extra zero length packet. This is useful - * when a device protocol mandates that each logical request is - * terminated by an incomplete packet (i.e. the logical requests are - * not separated by other means). - * - * This flag only affects host-to-device transfers to bulk and interrupt - * endpoints. In other situations, it is ignored. - * - * This flag only affects transfers with a length that is a multiple of - * the endpoint's wMaxPacketSize. On transfers of other lengths, this - * flag has no effect. Therefore, if you are working with a device that - * needs a ZLP whenever the end of the logical request falls on a packet - * boundary, then it is sensible to set this flag on every - * transfer (you do not have to worry about only setting it on transfers - * that end on the boundary). - * - * This flag is currently only supported on Linux. - * On other systems, libusb_submit_transfer() will return - * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. - * - * Available since libusb-1.0.9. - */ - LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, -}; - -/** \ingroup asyncio - * Isochronous packet descriptor. */ -struct libusb_iso_packet_descriptor { - /** Length of data to request in this packet */ - unsigned int length; - - /** Amount of data that was actually transferred */ - unsigned int actual_length; - - /** Status code for this packet */ - enum libusb_transfer_status status; -}; - -struct libusb_transfer; - -/** \ingroup asyncio - * Asynchronous transfer callback function type. When submitting asynchronous - * transfers, you pass a pointer to a callback function of this type via the - * \ref libusb_transfer::callback "callback" member of the libusb_transfer - * structure. libusb will call this function later, when the transfer has - * completed or failed. See \ref asyncio for more information. - * \param transfer The libusb_transfer struct the callback function is being - * notified about. - */ -typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); - -/** \ingroup asyncio - * The generic USB transfer structure. The user populates this structure and - * then submits it in order to request a transfer. After the transfer has - * completed, the library populates the transfer with the results and passes - * it back to the user. - */ -struct libusb_transfer { - /** Handle of the device that this transfer will be submitted to */ - libusb_device_handle *dev_handle; - - /** A bitwise OR combination of \ref libusb_transfer_flags. */ - uint8_t flags; - - /** Address of the endpoint where this transfer will be sent. */ - unsigned char endpoint; - - /** Type of the endpoint from \ref libusb_transfer_type */ - unsigned char type; - - /** Timeout for this transfer in millseconds. A value of 0 indicates no - * timeout. */ - unsigned int timeout; - - /** The status of the transfer. Read-only, and only for use within - * transfer callback function. - * - * If this is an isochronous transfer, this field may read COMPLETED even - * if there were errors in the frames. Use the - * \ref libusb_iso_packet_descriptor::status "status" field in each packet - * to determine if errors occurred. */ - enum libusb_transfer_status status; - - /** Length of the data buffer */ - int length; - - /** Actual length of data that was transferred. Read-only, and only for - * use within transfer callback function. Not valid for isochronous - * endpoint transfers. */ - int actual_length; - - /** Callback function. This will be invoked when the transfer completes, - * fails, or is cancelled. */ - libusb_transfer_cb_fn callback; - - /** User context data to pass to the callback function. */ - void *user_data; - - /** Data buffer */ - unsigned char *buffer; - - /** Number of isochronous packets. Only used for I/O with isochronous - * endpoints. */ - int num_iso_packets; - - /** Isochronous packet descriptors, for isochronous transfers only. */ - struct libusb_iso_packet_descriptor iso_packet_desc -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - [] /* valid C99 code */ -#else - [0] /* non-standard, but usually working code */ -#endif - ; -}; - -/** \ingroup misc - * Capabilities supported by this instance of libusb. Test if the loaded - * library supports a given capability by calling - * \ref libusb_has_capability(). - */ -enum libusb_capability { - /** The libusb_has_capability() API is available. */ - LIBUSB_CAP_HAS_CAPABILITY = 0, -}; - -int LIBUSB_CALL libusb_init(libusb_context **ctx); -void LIBUSB_CALL libusb_exit(libusb_context *ctx); -void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); -const struct libusb_version * LIBUSB_CALL libusb_get_version(void); -int LIBUSB_CALL libusb_has_capability(uint32_t capability); -const char * LIBUSB_CALL libusb_error_name(int errcode); - -ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, - libusb_device ***list); -void LIBUSB_CALL libusb_free_device_list(libusb_device **list, - int unref_devices); -libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); -void LIBUSB_CALL libusb_unref_device(libusb_device *dev); - -int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, - int *config); -int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, - struct libusb_device_descriptor *desc); -int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, - struct libusb_config_descriptor **config); -int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, - uint8_t config_index, struct libusb_config_descriptor **config); -int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, - uint8_t bConfigurationValue, struct libusb_config_descriptor **config); -void LIBUSB_CALL libusb_free_config_descriptor( - struct libusb_config_descriptor *config); -uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); -uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); -int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); -int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, - unsigned char endpoint); -int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, - unsigned char endpoint); - -int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); -void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); -libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); - -int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, - int configuration); -int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, - int interface_number); - -libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( - libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); - -int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, - int interface_number, int alternate_setting); -int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, - unsigned char endpoint); -int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); - -int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, - int interface_number); - -/* async I/O */ - -/** \ingroup asyncio - * Get the data section of a control transfer. This convenience function is here - * to remind you that the data does not start until 8 bytes into the actual - * buffer, as the setup packet comes first. - * - * Calling this function only makes sense from a transfer callback function, - * or situations where you have already allocated a suitably sized buffer at - * transfer->buffer. - * - * \param transfer a transfer - * \returns pointer to the first byte of the data section - */ -static inline unsigned char *libusb_control_transfer_get_data( - struct libusb_transfer *transfer) -{ - return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; -} - -/** \ingroup asyncio - * Get the control setup packet of a control transfer. This convenience - * function is here to remind you that the control setup occupies the first - * 8 bytes of the transfer data buffer. - * - * Calling this function only makes sense from a transfer callback function, - * or situations where you have already allocated a suitably sized buffer at - * transfer->buffer. - * - * \param transfer a transfer - * \returns a casted pointer to the start of the transfer data buffer - */ -static inline struct libusb_control_setup *libusb_control_transfer_get_setup( - struct libusb_transfer *transfer) -{ - return (struct libusb_control_setup *) transfer->buffer; -} - -/** \ingroup asyncio - * Helper function to populate the setup packet (first 8 bytes of the data - * buffer) for a control transfer. The wIndex, wValue and wLength values should - * be given in host-endian byte order. - * - * \param buffer buffer to output the setup packet into - * \param bmRequestType see the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field of - * \ref libusb_control_setup - * \param bRequest see the - * \ref libusb_control_setup::bRequest "bRequest" field of - * \ref libusb_control_setup - * \param wValue see the - * \ref libusb_control_setup::wValue "wValue" field of - * \ref libusb_control_setup - * \param wIndex see the - * \ref libusb_control_setup::wIndex "wIndex" field of - * \ref libusb_control_setup - * \param wLength see the - * \ref libusb_control_setup::wLength "wLength" field of - * \ref libusb_control_setup - */ -static inline void libusb_fill_control_setup(unsigned char *buffer, - uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, - uint16_t wLength) -{ - struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; - setup->bmRequestType = bmRequestType; - setup->bRequest = bRequest; - setup->wValue = libusb_cpu_to_le16(wValue); - setup->wIndex = libusb_cpu_to_le16(wIndex); - setup->wLength = libusb_cpu_to_le16(wLength); -} - -struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); -int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); -int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); -void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for a control transfer. - * - * If you pass a transfer buffer to this function, the first 8 bytes will - * be interpreted as a control setup packet, and the wLength field will be - * used to automatically populate the \ref libusb_transfer::length "length" - * field of the transfer. Therefore the recommended approach is: - * -# Allocate a suitably sized data buffer (including space for control setup) - * -# Call libusb_fill_control_setup() - * -# If this is a host-to-device transfer with a data stage, put the data - * in place after the setup packet - * -# Call this function - * -# Call libusb_submit_transfer() - * - * It is also legal to pass a NULL buffer to this function, in which case this - * function will not attempt to populate the length field. Remember that you - * must then populate the buffer and length fields later. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param buffer data buffer. If provided, this function will interpret the - * first 8 bytes as a setup packet and infer the transfer length from that. - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_control_transfer( - struct libusb_transfer *transfer, libusb_device_handle *dev_handle, - unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, - unsigned int timeout) -{ - struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; - transfer->dev_handle = dev_handle; - transfer->endpoint = 0; - transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; - transfer->timeout = timeout; - transfer->buffer = buffer; - if (setup) - transfer->length = LIBUSB_CONTROL_SETUP_SIZE - + libusb_le16_to_cpu(setup->wLength); - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for a bulk transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, - libusb_device_handle *dev_handle, unsigned char endpoint, - unsigned char *buffer, int length, libusb_transfer_cb_fn callback, - void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_BULK; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for an interrupt transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_interrupt_transfer( - struct libusb_transfer *transfer, libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *buffer, int length, - libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for an isochronous transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param num_iso_packets the number of isochronous packets - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, - libusb_device_handle *dev_handle, unsigned char endpoint, - unsigned char *buffer, int length, int num_iso_packets, - libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->num_iso_packets = num_iso_packets; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Convenience function to set the length of all packets in an isochronous - * transfer, based on the num_iso_packets field in the transfer structure. - * - * \param transfer a transfer - * \param length the length to set in each isochronous packet descriptor - * \see libusb_get_max_packet_size() - */ -static inline void libusb_set_iso_packet_lengths( - struct libusb_transfer *transfer, unsigned int length) -{ - int i; - for (i = 0; i < transfer->num_iso_packets; i++) - transfer->iso_packet_desc[i].length = length; -} - -/** \ingroup asyncio - * Convenience function to locate the position of an isochronous packet - * within the buffer of an isochronous transfer. - * - * This is a thorough function which loops through all preceding packets, - * accumulating their lengths to find the position of the specified packet. - * Typically you will assign equal lengths to each packet in the transfer, - * and hence the above method is sub-optimal. You may wish to use - * libusb_get_iso_packet_buffer_simple() instead. - * - * \param transfer a transfer - * \param packet the packet to return the address of - * \returns the base address of the packet buffer inside the transfer buffer, - * or NULL if the packet does not exist. - * \see libusb_get_iso_packet_buffer_simple() - */ -static inline unsigned char *libusb_get_iso_packet_buffer( - struct libusb_transfer *transfer, unsigned int packet) -{ - int i; - size_t offset = 0; - int _packet; - - /* oops..slight bug in the API. packet is an unsigned int, but we use - * signed integers almost everywhere else. range-check and convert to - * signed to avoid compiler warnings. FIXME for libusb-2. */ - if (packet > INT_MAX) - return NULL; - _packet = packet; - - if (_packet >= transfer->num_iso_packets) - return NULL; - - for (i = 0; i < _packet; i++) - offset += transfer->iso_packet_desc[i].length; - - return transfer->buffer + offset; -} - -/** \ingroup asyncio - * Convenience function to locate the position of an isochronous packet - * within the buffer of an isochronous transfer, for transfers where each - * packet is of identical size. - * - * This function relies on the assumption that every packet within the transfer - * is of identical size to the first packet. Calculating the location of - * the packet buffer is then just a simple calculation: - * buffer + (packet_size * packet) - * - * Do not use this function on transfers other than those that have identical - * packet lengths for each packet. - * - * \param transfer a transfer - * \param packet the packet to return the address of - * \returns the base address of the packet buffer inside the transfer buffer, - * or NULL if the packet does not exist. - * \see libusb_get_iso_packet_buffer() - */ -static inline unsigned char *libusb_get_iso_packet_buffer_simple( - struct libusb_transfer *transfer, unsigned int packet) -{ - int _packet; - - /* oops..slight bug in the API. packet is an unsigned int, but we use - * signed integers almost everywhere else. range-check and convert to - * signed to avoid compiler warnings. FIXME for libusb-2. */ - if (packet > INT_MAX) - return NULL; - _packet = packet; - - if (_packet >= transfer->num_iso_packets) - return NULL; - - return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); -} - -/* sync I/O */ - -int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, - uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, - unsigned char *data, uint16_t wLength, unsigned int timeout); - -int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, - int *actual_length, unsigned int timeout); - -int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, - int *actual_length, unsigned int timeout); - -/** \ingroup desc - * Retrieve a descriptor from the default control pipe. - * This is a convenience function which formulates the appropriate control - * message to retrieve the descriptor. - * - * \param dev a device handle - * \param desc_type the descriptor type, see \ref libusb_descriptor_type - * \param desc_index the index of the descriptor to retrieve - * \param data output buffer for descriptor - * \param length size of data buffer - * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure - */ -static inline int libusb_get_descriptor(libusb_device_handle *dev, - uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) -{ - return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, - (uint16_t) length, 1000); -} - -/** \ingroup desc - * Retrieve a descriptor from a device. - * This is a convenience function which formulates the appropriate control - * message to retrieve the descriptor. The string returned is Unicode, as - * detailed in the USB specifications. - * - * \param dev a device handle - * \param desc_index the index of the descriptor to retrieve - * \param langid the language ID for the string descriptor - * \param data output buffer for descriptor - * \param length size of data buffer - * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure - * \see libusb_get_string_descriptor_ascii() - */ -static inline int libusb_get_string_descriptor(libusb_device_handle *dev, - uint8_t desc_index, uint16_t langid, unsigned char *data, int length) -{ - return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), - langid, data, (uint16_t) length, 1000); -} - -int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, - uint8_t desc_index, unsigned char *data, int length); - -/* polling and timeouts */ - -int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); -void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); -void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); -int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); -int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); -void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); -void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); -int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); - -int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, - struct timeval *tv); -int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, - struct timeval *tv, int *completed); -int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); -int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); -int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, - struct timeval *tv); -int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); -int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, - struct timeval *tv); - -/** \ingroup poll - * File descriptor for polling - */ -struct libusb_pollfd { - /** Numeric file descriptor */ - int fd; - - /** Event flags to poll for from . POLLIN indicates that you - * should monitor this file descriptor for becoming ready to read from, - * and POLLOUT indicates that you should monitor this file descriptor for - * nonblocking write readiness. */ - short events; -}; - -/** \ingroup poll - * Callback function, invoked when a new file descriptor should be added - * to the set of file descriptors monitored for events. - * \param fd the new file descriptor - * \param events events to monitor for, see \ref libusb_pollfd for a - * description - * \param user_data User data pointer specified in - * libusb_set_pollfd_notifiers() call - * \see libusb_set_pollfd_notifiers() - */ -typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, - void *user_data); - -/** \ingroup poll - * Callback function, invoked when a file descriptor should be removed from - * the set of file descriptors being monitored for events. After returning - * from this callback, do not use that file descriptor again. - * \param fd the file descriptor to stop monitoring - * \param user_data User data pointer specified in - * libusb_set_pollfd_notifiers() call - * \see libusb_set_pollfd_notifiers() - */ -typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); - -const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( - libusb_context *ctx); -void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, - libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, - void *user_data); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/interface/external/LibUSB/include/libusbi.h b/interface/external/LibUSB/include/libusbi.h deleted file mode 100644 index 976be0d13b..0000000000 --- a/interface/external/LibUSB/include/libusbi.h +++ /dev/null @@ -1,935 +0,0 @@ -/* - * Internal header for libusb - * Copyright (C) 2007-2009 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSBI_H -#define LIBUSBI_H - -#include - -#include -#include -#include -#include -#ifdef HAVE_POLL_H -#include -#endif - -#include -#include - -/* Inside the libusb code, mark all public functions as follows: - * return_type API_EXPORTED function_name(params) { ... } - * But if the function returns a pointer, mark it as follows: - * DEFAULT_VISIBILITY return_type * LIBUSB_CALL function_name(params) { ... } - * In the libusb public header, mark all declarations as: - * return_type LIBUSB_CALL function_name(params); - */ -#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY - -#define DEVICE_DESC_LENGTH 18 - -#define USB_MAXENDPOINTS 32 -#define USB_MAXINTERFACES 32 -#define USB_MAXCONFIG 8 - -struct list_head { - struct list_head *prev, *next; -}; - -/* Get an entry from the list - * ptr - the address of this list_head element in "type" - * type - the data type that contains "member" - * member - the list_head element in "type" - */ -#define list_entry(ptr, type, member) \ - ((type *)((uintptr_t)(ptr) - (uintptr_t)(&((type *)0L)->member))) - -/* Get each entry from a list - * pos - A structure pointer has a "member" element - * head - list head - * member - the list_head element in "pos" - * type - the type of the first parameter - */ -#define list_for_each_entry(pos, head, member, type) \ - for (pos = list_entry((head)->next, type, member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, type, member)) - -#define list_for_each_entry_safe(pos, n, head, member, type) \ - for (pos = list_entry((head)->next, type, member), \ - n = list_entry(pos->member.next, type, member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, type, member)) - -#define list_empty(entry) ((entry)->next == (entry)) - -static inline void list_init(struct list_head *entry) -{ - entry->prev = entry->next = entry; -} - -static inline void list_add(struct list_head *entry, struct list_head *head) -{ - entry->next = head->next; - entry->prev = head; - - head->next->prev = entry; - head->next = entry; -} - -static inline void list_add_tail(struct list_head *entry, - struct list_head *head) -{ - entry->next = head; - entry->prev = head->prev; - - head->prev->next = entry; - head->prev = entry; -} - -static inline void list_del(struct list_head *entry) -{ - entry->next->prev = entry->prev; - entry->prev->next = entry->next; -} - -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *mptr = (ptr); \ - (type *)( (char *)mptr - offsetof(type,member) );}) - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) - -enum usbi_log_level { - LOG_LEVEL_DEBUG, - LOG_LEVEL_INFO, - LOG_LEVEL_WARNING, - LOG_LEVEL_ERROR, -}; - -void usbi_log(struct libusb_context *ctx, enum usbi_log_level level, - const char *function, const char *format, ...); - -void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level, - const char *function, const char *format, va_list args); - -#if !defined(_MSC_VER) || _MSC_VER >= 1400 - -#ifdef ENABLE_LOGGING -#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) -#else -#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0) -#endif - -#ifdef ENABLE_DEBUG_LOGGING -#define usbi_dbg(...) _usbi_log(NULL, LOG_LEVEL_DEBUG, __VA_ARGS__) -#else -#define usbi_dbg(...) do {} while(0) -#endif - -#define usbi_info(ctx, ...) _usbi_log(ctx, LOG_LEVEL_INFO, __VA_ARGS__) -#define usbi_warn(ctx, ...) _usbi_log(ctx, LOG_LEVEL_WARNING, __VA_ARGS__) -#define usbi_err(ctx, ...) _usbi_log(ctx, LOG_LEVEL_ERROR, __VA_ARGS__) - -#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ - -/* Old MS compilers don't support variadic macros. The code is simple, so we - * repeat it for each loglevel. Note that the debug case is special. - * - * Support for variadic macros was introduced in Visual C++ 2005. - * http://msdn.microsoft.com/en-us/library/ms177415%28v=VS.80%29.aspx - */ - -static inline void usbi_info(struct libusb_context *ctx, const char *fmt, ...) -{ -#ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_INFO, "", fmt, args); - va_end(args); -#else - (void)ctx; -#endif -} - -static inline void usbi_warn(struct libusb_context *ctx, const char *fmt, ...) -{ -#ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_WARNING, "", fmt, args); - va_end(args); -#else - (void)ctx; -#endif -} - -static inline void usbi_err(struct libusb_context *ctx, const char *fmt, ...) -{ -#ifdef ENABLE_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(ctx, LOG_LEVEL_ERROR, "", fmt, args); - va_end(args); -#else - (void)ctx; -#endif -} - -static inline void usbi_dbg(const char *fmt, ...) -{ -#ifdef ENABLE_DEBUG_LOGGING - va_list args; - va_start(args, fmt); - usbi_log_v(NULL, LOG_LEVEL_DEBUG, "", fmt, args); - va_end(args); -#else - (void)fmt; -#endif -} - -#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */ - -#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context -#define DEVICE_CTX(dev) ((dev)->ctx) -#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) -#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -#define ITRANSFER_CTX(transfer) \ - (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) - -#define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) -#define IS_EPOUT(ep) (!IS_EPIN(ep)) -#define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) -#define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) - -/* Internal abstractions for thread synchronization and poll */ -#if defined(THREADS_POSIX) -#include -#elif defined(OS_WINDOWS) -#include -#endif - -#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) -#include -#include -#elif defined(OS_WINDOWS) -#include -#endif - -#if defined(OS_WINDOWS) && !defined(__GCC__) -#undef HAVE_GETTIMEOFDAY -int usbi_gettimeofday(struct timeval *tp, void *tzp); -#define LIBUSB_GETTIMEOFDAY_WIN32 -#define HAVE_USBI_GETTIMEOFDAY -#else -#ifdef HAVE_GETTIMEOFDAY -#define usbi_gettimeofday(tv, tz) gettimeofday((tv), (tz)) -#define HAVE_USBI_GETTIMEOFDAY -#endif -#endif - -extern struct libusb_context *usbi_default_context; - -struct libusb_context { - int debug; - int debug_fixed; - - /* internal control pipe, used for interrupting event handling when - * something needs to modify poll fds. */ - int ctrl_pipe[2]; - - struct list_head usb_devs; - usbi_mutex_t usb_devs_lock; - - /* A list of open handles. Backends are free to traverse this if required. - */ - struct list_head open_devs; - usbi_mutex_t open_devs_lock; - - /* this is a list of in-flight transfer handles, sorted by timeout - * expiration. URBs to timeout the soonest are placed at the beginning of - * the list, URBs that will time out later are placed after, and urbs with - * infinite timeout are always placed at the very end. */ - struct list_head flying_transfers; - usbi_mutex_t flying_transfers_lock; - - /* list of poll fds */ - struct list_head pollfds; - usbi_mutex_t pollfds_lock; - - /* a counter that is set when we want to interrupt event handling, in order - * to modify the poll fd set. and a lock to protect it. */ - unsigned int pollfd_modify; - usbi_mutex_t pollfd_modify_lock; - - /* user callbacks for pollfd changes */ - libusb_pollfd_added_cb fd_added_cb; - libusb_pollfd_removed_cb fd_removed_cb; - void *fd_cb_user_data; - - /* ensures that only one thread is handling events at any one time */ - usbi_mutex_t events_lock; - - /* used to see if there is an active thread doing event handling */ - int event_handler_active; - - /* used to wait for event completion in threads other than the one that is - * event handling */ - usbi_mutex_t event_waiters_lock; - usbi_cond_t event_waiters_cond; - -#ifdef USBI_TIMERFD_AVAILABLE - /* used for timeout handling, if supported by OS. - * this timerfd is maintained to trigger on the next pending timeout */ - int timerfd; -#endif -}; - -#ifdef USBI_TIMERFD_AVAILABLE -#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) -#else -#define usbi_using_timerfd(ctx) (0) -#endif - -struct libusb_device { - /* lock protects refcnt, everything else is finalized at initialization - * time */ - usbi_mutex_t lock; - int refcnt; - - struct libusb_context *ctx; - - uint8_t bus_number; - uint8_t device_address; - uint8_t num_configurations; - enum libusb_speed speed; - - struct list_head list; - unsigned long session_data; - unsigned char os_priv[0]; -}; - -struct libusb_device_handle { - /* lock protects claimed_interfaces */ - usbi_mutex_t lock; - unsigned long claimed_interfaces; - - struct list_head list; - struct libusb_device *dev; - unsigned char os_priv[0]; -}; - -enum { - USBI_CLOCK_MONOTONIC, - USBI_CLOCK_REALTIME -}; - -/* in-memory transfer layout: - * - * 1. struct usbi_transfer - * 2. struct libusb_transfer (which includes iso packets) [variable size] - * 3. os private data [variable size] - * - * from a libusb_transfer, you can get the usbi_transfer by rewinding the - * appropriate number of bytes. - * the usbi_transfer includes the number of allocated packets, so you can - * determine the size of the transfer and hence the start and length of the - * OS-private data. - */ - -struct usbi_transfer { - int num_iso_packets; - struct list_head list; - struct timeval timeout; - int transferred; - uint8_t flags; - - /* this lock is held during libusb_submit_transfer() and - * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate - * cancellation, submission-during-cancellation, etc). the OS backend - * should also take this lock in the handle_events path, to prevent the user - * cancelling the transfer from another thread while you are processing - * its completion (presumably there would be races within your OS backend - * if this were possible). */ - usbi_mutex_t lock; -}; - -enum usbi_transfer_flags { - /* The transfer has timed out */ - USBI_TRANSFER_TIMED_OUT = 1 << 0, - - /* Set by backend submit_transfer() if the OS handles timeout */ - USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 1, - - /* Cancellation was requested via libusb_cancel_transfer() */ - USBI_TRANSFER_CANCELLING = 1 << 2, - - /* Operation on the transfer failed because the device disappeared */ - USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 3, -}; - -#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ - ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ - + sizeof(struct usbi_transfer))) -#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ - ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ - - sizeof(struct usbi_transfer))) - -static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) -{ - return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) - + sizeof(struct libusb_transfer) - + (transfer->num_iso_packets - * sizeof(struct libusb_iso_packet_descriptor)); -} - -/* bus structures */ - -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header { - uint8_t bLength; - uint8_t bDescriptorType; -}; - -/* shared data and functions */ - -int usbi_io_init(struct libusb_context *ctx); -void usbi_io_exit(struct libusb_context *ctx); - -struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, - unsigned long session_id); -struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, - unsigned long session_id); -int usbi_sanitize_device(struct libusb_device *dev); -void usbi_handle_disconnect(struct libusb_device_handle *handle); - -int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, - enum libusb_transfer_status status); -int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); - -int usbi_parse_descriptor(unsigned char *source, const char *descriptor, - void *dest, int host_endian); -int usbi_get_config_index_by_value(struct libusb_device *dev, - uint8_t bConfigurationValue, int *idx); - -/* polling */ - -struct usbi_pollfd { - /* must come first */ - struct libusb_pollfd pollfd; - - struct list_head list; -}; - -int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); -void usbi_remove_pollfd(struct libusb_context *ctx, int fd); -void usbi_fd_notification(struct libusb_context *ctx); - -/* device discovery */ - -/* we traverse usbfs without knowing how many devices we are going to find. - * so we create this discovered_devs model which is similar to a linked-list - * which grows when required. it can be freed once discovery has completed, - * eliminating the need for a list node in the libusb_device structure - * itself. */ -struct discovered_devs { - size_t len; - size_t capacity; - struct libusb_device *devices[0]; -}; - -struct discovered_devs *discovered_devs_append( - struct discovered_devs *discdevs, struct libusb_device *dev); - -/* OS abstraction */ - -/* This is the interface that OS backends need to implement. - * All fields are mandatory, except ones explicitly noted as optional. */ -struct usbi_os_backend { - /* A human-readable name for your backend, e.g. "Linux usbfs" */ - const char *name; - - /* Perform initialization of your backend. You might use this function - * to determine specific capabilities of the system, allocate required - * data structures for later, etc. - * - * This function is called when a libusb user initializes the library - * prior to use. - * - * Return 0 on success, or a LIBUSB_ERROR code on failure. - */ - int (*init)(struct libusb_context *ctx); - - /* Deinitialization. Optional. This function should destroy anything - * that was set up by init. - * - * This function is called when the user deinitializes the library. - */ - void (*exit)(void); - - /* Enumerate all the USB devices on the system, returning them in a list - * of discovered devices. - * - * Your implementation should enumerate all devices on the system, - * regardless of whether they have been seen before or not. - * - * When you have found a device, compute a session ID for it. The session - * ID should uniquely represent that particular device for that particular - * connection session since boot (i.e. if you disconnect and reconnect a - * device immediately after, it should be assigned a different session ID). - * If your OS cannot provide a unique session ID as described above, - * presenting a session ID of (bus_number << 8 | device_address) should - * be sufficient. Bus numbers and device addresses wrap and get reused, - * but that is an unlikely case. - * - * After computing a session ID for a device, call - * usbi_get_device_by_session_id(). This function checks if libusb already - * knows about the device, and if so, it provides you with a libusb_device - * structure for it. - * - * If usbi_get_device_by_session_id() returns NULL, it is time to allocate - * a new device structure for the device. Call usbi_alloc_device() to - * obtain a new libusb_device structure with reference count 1. Populate - * the bus_number and device_address attributes of the new device, and - * perform any other internal backend initialization you need to do. At - * this point, you should be ready to provide device descriptors and so - * on through the get_*_descriptor functions. Finally, call - * usbi_sanitize_device() to perform some final sanity checks on the - * device. Assuming all of the above succeeded, we can now continue. - * If any of the above failed, remember to unreference the device that - * was returned by usbi_alloc_device(). - * - * At this stage we have a populated libusb_device structure (either one - * that was found earlier, or one that we have just allocated and - * populated). This can now be added to the discovered devices list - * using discovered_devs_append(). Note that discovered_devs_append() - * may reallocate the list, returning a new location for it, and also - * note that reallocation can fail. Your backend should handle these - * error conditions appropriately. - * - * This function should not generate any bus I/O and should not block. - * If I/O is required (e.g. reading the active configuration value), it is - * OK to ignore these suggestions :) - * - * This function is executed when the user wishes to retrieve a list - * of USB devices connected to the system. - * - * Return 0 on success, or a LIBUSB_ERROR code on failure. - */ - int (*get_device_list)(struct libusb_context *ctx, - struct discovered_devs **discdevs); - - /* Open a device for I/O and other USB operations. The device handle - * is preallocated for you, you can retrieve the device in question - * through handle->dev. - * - * Your backend should allocate any internal resources required for I/O - * and other operations so that those operations can happen (hopefully) - * without hiccup. This is also a good place to inform libusb that it - * should monitor certain file descriptors related to this device - - * see the usbi_add_pollfd() function. - * - * This function should not generate any bus I/O and should not block. - * - * This function is called when the user attempts to obtain a device - * handle for a device. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since - * discovery - * - another LIBUSB_ERROR code on other failure - * - * Do not worry about freeing the handle on failed open, the upper layers - * do this for you. - */ - int (*open)(struct libusb_device_handle *handle); - - /* Close a device such that the handle cannot be used again. Your backend - * should destroy any resources that were allocated in the open path. - * This may also be a good place to call usbi_remove_pollfd() to inform - * libusb of any file descriptors associated with this device that should - * no longer be monitored. - * - * This function is called when the user closes a device handle. - */ - void (*close)(struct libusb_device_handle *handle); - - /* Retrieve the device descriptor from a device. - * - * The descriptor should be retrieved from memory, NOT via bus I/O to the - * device. This means that you may have to cache it in a private structure - * during get_device_list enumeration. Alternatively, you may be able - * to retrieve it from a kernel interface (some Linux setups can do this) - * still without generating bus I/O. - * - * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into - * buffer, which is guaranteed to be big enough. - * - * This function is called when sanity-checking a device before adding - * it to the list of discovered devices, and also when the user requests - * to read the device descriptor. - * - * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". - * - * Return 0 on success or a LIBUSB_ERROR code on failure. - */ - int (*get_device_descriptor)(struct libusb_device *device, - unsigned char *buffer, int *host_endian); - - /* Get the ACTIVE configuration descriptor for a device. - * - * The descriptor should be retrieved from memory, NOT via bus I/O to the - * device. This means that you may have to cache it in a private structure - * during get_device_list enumeration. You may also have to keep track - * of which configuration is active when the user changes it. - * - * This function is expected to write len bytes of data into buffer, which - * is guaranteed to be big enough. If you can only do a partial write, - * return an error code. - * - * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state - * - another LIBUSB_ERROR code on other failure - */ - int (*get_active_config_descriptor)(struct libusb_device *device, - unsigned char *buffer, size_t len, int *host_endian); - - /* Get a specific configuration descriptor for a device. - * - * The descriptor should be retrieved from memory, NOT via bus I/O to the - * device. This means that you may have to cache it in a private structure - * during get_device_list enumeration. - * - * The requested descriptor is expressed as a zero-based index (i.e. 0 - * indicates that we are requesting the first descriptor). The index does - * not (necessarily) equal the bConfigurationValue of the configuration - * being requested. - * - * This function is expected to write len bytes of data into buffer, which - * is guaranteed to be big enough. If you can only do a partial write, - * return an error code. - * - * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". - * - * Return 0 on success or a LIBUSB_ERROR code on failure. - */ - int (*get_config_descriptor)(struct libusb_device *device, - uint8_t config_index, unsigned char *buffer, size_t len, - int *host_endian); - - /* Get the bConfigurationValue for the active configuration for a device. - * Optional. This should only be implemented if you can retrieve it from - * cache (don't generate I/O). - * - * If you cannot retrieve this from cache, either do not implement this - * function, or return LIBUSB_ERROR_NOT_SUPPORTED. This will cause - * libusb to retrieve the information through a standard control transfer. - * - * This function must be non-blocking. - * Return: - * - 0 on success - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - LIBUSB_ERROR_NOT_SUPPORTED if the value cannot be retrieved without - * blocking - * - another LIBUSB_ERROR code on other failure. - */ - int (*get_configuration)(struct libusb_device_handle *handle, int *config); - - /* Set the active configuration for a device. - * - * A configuration value of -1 should put the device in unconfigured state. - * - * This function can block. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if the configuration does not exist - * - LIBUSB_ERROR_BUSY if interfaces are currently claimed (and hence - * configuration cannot be changed) - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure. - */ - int (*set_configuration)(struct libusb_device_handle *handle, int config); - - /* Claim an interface. When claimed, the application can then perform - * I/O to an interface's endpoints. - * - * This function should not generate any bus I/O and should not block. - * Interface claiming is a logical operation that simply ensures that - * no other drivers/applications are using the interface, and after - * claiming, no other drivers/applicatiosn can use the interface because - * we now "own" it. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist - * - LIBUSB_ERROR_BUSY if the interface is in use by another driver/app - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*claim_interface)(struct libusb_device_handle *handle, int interface_number); - - /* Release a previously claimed interface. - * - * This function should also generate a SET_INTERFACE control request, - * resetting the alternate setting of that interface to 0. It's OK for - * this function to block as a result. - * - * You will only ever be asked to release an interface which was - * successfully claimed earlier. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*release_interface)(struct libusb_device_handle *handle, int interface_number); - - /* Set the alternate setting for an interface. - * - * You will only ever be asked to set the alternate setting for an - * interface which was successfully claimed earlier. - * - * It's OK for this function to block. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if the alternate setting does not exist - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*set_interface_altsetting)(struct libusb_device_handle *handle, - int interface_number, int altsetting); - - /* Clear a halt/stall condition on an endpoint. - * - * It's OK for this function to block. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*clear_halt)(struct libusb_device_handle *handle, - unsigned char endpoint); - - /* Perform a USB port reset to reinitialize a device. - * - * If possible, the handle should still be usable after the reset - * completes, assuming that the device descriptors did not change during - * reset and all previous interface state can be restored. - * - * If something changes, or you cannot easily locate/verify the resetted - * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application - * to close the old handle and re-enumerate the device. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the device - * has been disconnected since it was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*reset_device)(struct libusb_device_handle *handle); - - /* Determine if a kernel driver is active on an interface. Optional. - * - * The presence of a kernel driver on an interface indicates that any - * calls to claim_interface would fail with the LIBUSB_ERROR_BUSY code. - * - * Return: - * - 0 if no driver is active - * - 1 if a driver is active - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*kernel_driver_active)(struct libusb_device_handle *handle, - int interface_number); - - /* Detach a kernel driver from an interface. Optional. - * - * After detaching a kernel driver, the interface should be available - * for claim. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - another LIBUSB_ERROR code on other failure - */ - int (*detach_kernel_driver)(struct libusb_device_handle *handle, - int interface_number); - - /* Attach a kernel driver to an interface. Optional. - * - * Reattach a kernel driver to the device. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it - * was opened - * - LIBUSB_ERROR_BUSY if a program or driver has claimed the interface, - * preventing reattachment - * - another LIBUSB_ERROR code on other failure - */ - int (*attach_kernel_driver)(struct libusb_device_handle *handle, - int interface_number); - - /* Destroy a device. Optional. - * - * This function is called when the last reference to a device is - * destroyed. It should free any resources allocated in the get_device_list - * path. - */ - void (*destroy_device)(struct libusb_device *dev); - - /* Submit a transfer. Your implementation should take the transfer, - * morph it into whatever form your platform requires, and submit it - * asynchronously. - * - * This function must not block. - * - * Return: - * - 0 on success - * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * - another LIBUSB_ERROR code on other failure - */ - int (*submit_transfer)(struct usbi_transfer *itransfer); - - /* Cancel a previously submitted transfer. - * - * This function must not block. The transfer cancellation must complete - * later, resulting in a call to usbi_handle_transfer_cancellation() - * from the context of handle_events. - */ - int (*cancel_transfer)(struct usbi_transfer *itransfer); - - /* Clear a transfer as if it has completed or cancelled, but do not - * report any completion/cancellation to the library. You should free - * all private data from the transfer as if you were just about to report - * completion or cancellation. - * - * This function might seem a bit out of place. It is used when libusb - * detects a disconnected device - it calls this function for all pending - * transfers before reporting completion (with the disconnect code) to - * the user. Maybe we can improve upon this internal interface in future. - */ - void (*clear_transfer_priv)(struct usbi_transfer *itransfer); - - /* Handle any pending events. This involves monitoring any active - * transfers and processing their completion or cancellation. - * - * The function is passed an array of pollfd structures (size nfds) - * as a result of the poll() system call. The num_ready parameter - * indicates the number of file descriptors that have reported events - * (i.e. the poll() return value). This should be enough information - * for you to determine which actions need to be taken on the currently - * active transfers. - * - * For any cancelled transfers, call usbi_handle_transfer_cancellation(). - * For completed transfers, call usbi_handle_transfer_completion(). - * For control/bulk/interrupt transfers, populate the "transferred" - * element of the appropriate usbi_transfer structure before calling the - * above functions. For isochronous transfers, populate the status and - * transferred fields of the iso packet descriptors of the transfer. - * - * This function should also be able to detect disconnection of the - * device, reporting that situation with usbi_handle_disconnect(). - * - * When processing an event related to a transfer, you probably want to - * take usbi_transfer.lock to prevent races. See the documentation for - * the usbi_transfer structure. - * - * Return 0 on success, or a LIBUSB_ERROR code on failure. - */ - int (*handle_events)(struct libusb_context *ctx, - struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); - - /* Get time from specified clock. At least two clocks must be implemented - by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. - - Description of clocks: - USBI_CLOCK_REALTIME : clock returns time since system epoch. - USBI_CLOCK_MONOTONIC: clock returns time since unspecified start - time (usually boot). - */ - int (*clock_gettime)(int clkid, struct timespec *tp); - -#ifdef USBI_TIMERFD_AVAILABLE - /* clock ID of the clock that should be used for timerfd */ - clockid_t (*get_timerfd_clockid)(void); -#endif - - /* Number of bytes to reserve for per-device private backend data. - * This private data area is accessible through the "os_priv" field of - * struct libusb_device. */ - size_t device_priv_size; - - /* Number of bytes to reserve for per-handle private backend data. - * This private data area is accessible through the "os_priv" field of - * struct libusb_device. */ - size_t device_handle_priv_size; - - /* Number of bytes to reserve for per-transfer private backend data. - * This private data area is accessible by calling - * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. - */ - size_t transfer_priv_size; - - /* Mumber of additional bytes for os_priv for each iso packet. - * Can your backend use this? */ - /* FIXME: linux can't use this any more. if other OS's cannot either, - * then remove this */ - size_t add_iso_packet_size; -}; - -extern const struct usbi_os_backend * const usbi_backend; - -extern const struct usbi_os_backend linux_usbfs_backend; -extern const struct usbi_os_backend darwin_backend; -extern const struct usbi_os_backend openbsd_backend; -extern const struct usbi_os_backend windows_backend; - -#endif - diff --git a/interface/external/LibUSB/include/os/darwin_usb.h b/interface/external/LibUSB/include/os/darwin_usb.h deleted file mode 100644 index 59d0a694ce..0000000000 --- a/interface/external/LibUSB/include/os/darwin_usb.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * darwin backend for libusb 1.0 - * Copyright (C) 2008-2009 Nathan Hjelm - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#if !defined(LIBUSB_DARWIN_H) -#define LIBUSB_DARWIN_H - -#include "libusbi.h" - -#include -#include -#include -#include - -/* IOUSBInterfaceInferface */ -#if defined (kIOUSBInterfaceInterfaceID300) - -#define usb_interface_t IOUSBInterfaceInterface300 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 -#define InterfaceVersion 300 - -#elif defined (kIOUSBInterfaceInterfaceID245) - -#define usb_interface_t IOUSBInterfaceInterface245 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 -#define InterfaceVersion 245 - -#elif defined (kIOUSBInterfaceInterfaceID220) - -#define usb_interface_t IOUSBInterfaceInterface220 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 -#define InterfaceVersion 220 - -#elif defined (kIOUSBInterfaceInterfaceID197) - -#define usb_interface_t IOUSBInterfaceInterface197 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 -#define InterfaceVersion 197 - -#elif defined (kIOUSBInterfaceInterfaceID190) - -#define usb_interface_t IOUSBInterfaceInterface190 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 -#define InterfaceVersion 190 - -#elif defined (kIOUSBInterfaceInterfaceID182) - -#define usb_interface_t IOUSBInterfaceInterface182 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 -#define InterfaceVersion 182 - -#else - -#error "IOUSBFamily is too old. Please upgrade your OS" - -#endif - -/* IOUSBDeviceInterface */ -#if defined (kIOUSBDeviceInterfaceID320) - -#define usb_device_t IOUSBDeviceInterface320 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 -#define DeviceVersion 320 - -#elif defined (kIOUSBDeviceInterfaceID300) - -#define usb_device_t IOUSBDeviceInterface300 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 -#define DeviceVersion 300 - -#elif defined (kIOUSBDeviceInterfaceID245) - -#define usb_device_t IOUSBDeviceInterface245 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 -#define DeviceVersion 245 - -#elif defined (kIOUSBDeviceInterfaceID197) - -#define usb_device_t IOUSBDeviceInterface197 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 -#define DeviceVersion 197 - -#elif defined (kIOUSBDeviceInterfaceID187) - -#define usb_device_t IOUSBDeviceInterface187 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 -#define DeviceVersion 187 - -#elif defined (kIOUSBDeviceInterfaceID182) - -#define usb_device_t IOUSBDeviceInterface182 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 -#define DeviceVersion 182 - -#else - -#error "IOUSBFamily is too old. Please upgrade your OS" - -#endif - -#if !defined(IO_OBJECT_NULL) -#define IO_OBJECT_NULL ((io_object_t) 0) -#endif - -typedef IOCFPlugInInterface *io_cf_plugin_ref_t; -typedef IONotificationPortRef io_notification_port_t; - -/* private structures */ -struct darwin_device_priv { - IOUSBDeviceDescriptor dev_descriptor; - UInt32 location; - char sys_path[21]; - usb_device_t **device; - int open_count; - UInt8 first_config, active_config; -}; - -struct darwin_device_handle_priv { - int is_open; - CFRunLoopSourceRef cfSource; - int fds[2]; - - struct darwin_interface { - usb_interface_t **interface; - uint8_t num_endpoints; - CFRunLoopSourceRef cfSource; - uint64_t frames[256]; - uint8_t endpoint_addrs[USB_MAXENDPOINTS]; - } interfaces[USB_MAXINTERFACES]; -}; - -struct darwin_transfer_priv { - /* Isoc */ - IOUSBIsocFrame *isoc_framelist; - size_t num_iso_packets; - - /* Control */ -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) - IOUSBDevRequestTO req; -#else - IOUSBDevRequest req; -#endif - - /* Bulk */ -}; - -enum { - MESSAGE_DEVICE_GONE, - MESSAGE_ASYNC_IO_COMPLETE -}; - - - -#endif diff --git a/interface/external/LibUSB/include/os/linux_usbfs.h b/interface/external/LibUSB/include/os/linux_usbfs.h deleted file mode 100644 index 487acb5ac2..0000000000 --- a/interface/external/LibUSB/include/os/linux_usbfs.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * usbfs header structures - * Copyright (C) 2007 Daniel Drake - * Copyright (c) 2001 Johannes Erdfelt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSB_USBFS_H -#define LIBUSB_USBFS_H - -#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices" - -struct usbfs_ctrltransfer { - /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ - uint8_t bmRequestType; - uint8_t bRequest; - uint16_t wValue; - uint16_t wIndex; - uint16_t wLength; - - uint32_t timeout; /* in milliseconds */ - - /* pointer to data */ - void *data; -}; - -struct usbfs_bulktransfer { - /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ - unsigned int ep; - unsigned int len; - unsigned int timeout; /* in milliseconds */ - - /* pointer to data */ - void *data; -}; - -struct usbfs_setinterface { - /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ - unsigned int interface; - unsigned int altsetting; -}; - -#define USBFS_MAXDRIVERNAME 255 - -struct usbfs_getdriver { - unsigned int interface; - char driver[USBFS_MAXDRIVERNAME + 1]; -}; - -#define USBFS_URB_SHORT_NOT_OK 0x01 -#define USBFS_URB_ISO_ASAP 0x02 -#define USBFS_URB_BULK_CONTINUATION 0x04 -#define USBFS_URB_QUEUE_BULK 0x10 -#define USBFS_URB_ZERO_PACKET 0x40 - -enum usbfs_urb_type { - USBFS_URB_TYPE_ISO = 0, - USBFS_URB_TYPE_INTERRUPT = 1, - USBFS_URB_TYPE_CONTROL = 2, - USBFS_URB_TYPE_BULK = 3, -}; - -struct usbfs_iso_packet_desc { - unsigned int length; - unsigned int actual_length; - unsigned int status; -}; - -#define MAX_ISO_BUFFER_LENGTH 32768 -#define MAX_BULK_BUFFER_LENGTH 16384 -#define MAX_CTRL_BUFFER_LENGTH 4096 - -struct usbfs_urb { - unsigned char type; - unsigned char endpoint; - int status; - unsigned int flags; - void *buffer; - int buffer_length; - int actual_length; - int start_frame; - int number_of_packets; - int error_count; - unsigned int signr; - void *usercontext; - struct usbfs_iso_packet_desc iso_frame_desc[0]; -}; - -struct usbfs_connectinfo { - unsigned int devnum; - unsigned char slow; -}; - -struct usbfs_ioctl { - int ifno; /* interface 0..N ; negative numbers reserved */ - int ioctl_code; /* MUST encode size + direction of data so the - * macros in give correct values */ - void *data; /* param buffer (in, or out) */ -}; - -struct usbfs_hub_portinfo { - unsigned char numports; - unsigned char port[127]; /* port to device num mapping */ -}; - -#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer) -#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer) -#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int) -#define IOCTL_USBFS_SETINTF _IOR('U', 4, struct usbfs_setinterface) -#define IOCTL_USBFS_SETCONFIG _IOR('U', 5, unsigned int) -#define IOCTL_USBFS_GETDRIVER _IOW('U', 8, struct usbfs_getdriver) -#define IOCTL_USBFS_SUBMITURB _IOR('U', 10, struct usbfs_urb) -#define IOCTL_USBFS_DISCARDURB _IO('U', 11) -#define IOCTL_USBFS_REAPURB _IOW('U', 12, void *) -#define IOCTL_USBFS_REAPURBNDELAY _IOW('U', 13, void *) -#define IOCTL_USBFS_CLAIMINTF _IOR('U', 15, unsigned int) -#define IOCTL_USBFS_RELEASEINTF _IOR('U', 16, unsigned int) -#define IOCTL_USBFS_CONNECTINFO _IOW('U', 17, struct usbfs_connectinfo) -#define IOCTL_USBFS_IOCTL _IOWR('U', 18, struct usbfs_ioctl) -#define IOCTL_USBFS_HUB_PORTINFO _IOR('U', 19, struct usbfs_hub_portinfo) -#define IOCTL_USBFS_RESET _IO('U', 20) -#define IOCTL_USBFS_CLEAR_HALT _IOR('U', 21, unsigned int) -#define IOCTL_USBFS_DISCONNECT _IO('U', 22) -#define IOCTL_USBFS_CONNECT _IO('U', 23) - -#endif diff --git a/interface/external/LibUSB/include/os/poll_posix.h b/interface/external/LibUSB/include/os/poll_posix.h deleted file mode 100644 index 0e5e7f5b72..0000000000 --- a/interface/external/LibUSB/include/os/poll_posix.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef LIBUSB_POLL_POSIX_H -#define LIBUSB_POLL_POSIX_H - -#define usbi_write write -#define usbi_read read -#define usbi_close close -#define usbi_pipe pipe -#define usbi_poll poll - -#endif /* LIBUSB_POLL_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/poll_windows.h b/interface/external/LibUSB/include/os/poll_windows.h deleted file mode 100644 index 6e5bf2bcef..0000000000 --- a/interface/external/LibUSB/include/os/poll_windows.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Windows compat: POSIX compatibility wrapper - * Copyright (C) 2009-2010 Pete Batard - * With contributions from Michael Plante, Orin Eman et al. - * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -#pragma once - -#if defined(_MSC_VER) -// disable /W4 MSVC warnings that are benign -#pragma warning(disable:4127) // conditional expression is constant -#endif - -// Handle synchronous completion through the overlapped structure -#if !defined(STATUS_REPARSE) // reuse the REPARSE status code -#define STATUS_REPARSE ((LONG)0x00000104L) -#endif -#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE -#define HasOverlappedIoCompletedSync(lpOverlapped) (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY) - -#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2) - -enum windows_version { - WINDOWS_UNSUPPORTED, - WINDOWS_XP, - WINDOWS_2003, // also includes XP 64 - WINDOWS_VISTA_AND_LATER, -}; -extern enum windows_version windows_version; - -#define MAX_FDS 256 - -#define POLLIN 0x0001 /* There is data to read */ -#define POLLPRI 0x0002 /* There is urgent data to read */ -#define POLLOUT 0x0004 /* Writing now will not block */ -#define POLLERR 0x0008 /* Error condition */ -#define POLLHUP 0x0010 /* Hung up */ -#define POLLNVAL 0x0020 /* Invalid request: fd not open */ - -struct pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; - -// access modes -enum rw_type { - RW_NONE, - RW_READ, - RW_WRITE, -}; - -// fd struct that can be used for polling on Windows -struct winfd { - int fd; // what's exposed to libusb core - HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it - OVERLAPPED* overlapped; // what will report our I/O status - enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) -}; -extern const struct winfd INVALID_WINFD; - -int usbi_pipe(int pipefd[2]); -int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); -ssize_t usbi_write(int fd, const void *buf, size_t count); -ssize_t usbi_read(int fd, void *buf, size_t count); -int usbi_close(int fd); - -void init_polling(void); -void exit_polling(void); -struct winfd usbi_create_fd(HANDLE handle, int access_mode); -void usbi_free_fd(int fd); -struct winfd fd_to_winfd(int fd); -struct winfd handle_to_winfd(HANDLE handle); -struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); - -/* - * Timeval operations - */ -#if defined(DDKBUILD) -#include // defines timeval functions on DDK -#endif - -#if !defined(TIMESPEC_TO_TIMEVAL) -#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (long)(ts)->tv_sec; \ - (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ -} -#endif -#if !defined(timersub) -#define timersub(a, b, result) \ -do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ -} while (0) -#endif - diff --git a/interface/external/LibUSB/include/os/threads_posix.h b/interface/external/LibUSB/include/os/threads_posix.h deleted file mode 100644 index 9752208936..0000000000 --- a/interface/external/LibUSB/include/os/threads_posix.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * libusb synchronization using POSIX Threads - * - * Copyright (C) 2010 Peter Stuge - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSB_THREADS_POSIX_H -#define LIBUSB_THREADS_POSIX_H - -#include - -#define usbi_mutex_static_t pthread_mutex_t -#define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -#define usbi_mutex_static_lock pthread_mutex_lock -#define usbi_mutex_static_unlock pthread_mutex_unlock - -#define usbi_mutex_t pthread_mutex_t -#define usbi_mutex_init pthread_mutex_init -#define usbi_mutex_lock pthread_mutex_lock -#define usbi_mutex_unlock pthread_mutex_unlock -#define usbi_mutex_trylock pthread_mutex_trylock -#define usbi_mutex_destroy pthread_mutex_destroy - -#define usbi_cond_t pthread_cond_t -#define usbi_cond_init pthread_cond_init -#define usbi_cond_wait pthread_cond_wait -#define usbi_cond_timedwait pthread_cond_timedwait -#define usbi_cond_broadcast pthread_cond_broadcast -#define usbi_cond_destroy pthread_cond_destroy -#define usbi_cond_signal pthread_cond_signal - -extern int usbi_mutex_init_recursive(pthread_mutex_t *mutex, pthread_mutexattr_t *attr); - -#endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/interface/external/LibUSB/include/os/threads_windows.h b/interface/external/LibUSB/include/os/threads_windows.h deleted file mode 100644 index 7bb144af58..0000000000 --- a/interface/external/LibUSB/include/os/threads_windows.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * libusb synchronization on Microsoft Windows - * - * Copyright (C) 2010 Michael Plante - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSB_THREADS_WINDOWS_H -#define LIBUSB_THREADS_WINDOWS_H - -#define usbi_mutex_static_t volatile LONG -#define USBI_MUTEX_INITIALIZER 0 - -#define usbi_mutex_t HANDLE - -struct usbi_cond_perthread { - struct list_head list; - DWORD tid; - HANDLE event; -}; -struct usbi_cond_t_ { - // Every time a thread touches the CV, it winds up in one of these lists. - // It stays there until the CV is destroyed, even if the thread - // terminates. - struct list_head waiters; - struct list_head not_waiting; -}; -typedef struct usbi_cond_t_ usbi_cond_t; - -// We *were* getting timespec from pthread.h: -#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) -#define HAVE_STRUCT_TIMESPEC 1 -#define _TIMESPEC_DEFINED 1 -struct timespec { - long tv_sec; - long tv_nsec; -}; -#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */ - -// We *were* getting ETIMEDOUT from pthread.h: -#ifndef ETIMEDOUT -# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ -#endif - -#define usbi_mutexattr_t void -#define usbi_condattr_t void - -// all Windows mutexes are recursive -#define usbi_mutex_init_recursive(mutex, attr) usbi_mutex_init((mutex), (attr)) - -int usbi_mutex_static_lock(usbi_mutex_static_t *mutex); -int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex); - - -int usbi_mutex_init(usbi_mutex_t *mutex, - const usbi_mutexattr_t *attr); -int usbi_mutex_lock(usbi_mutex_t *mutex); -int usbi_mutex_unlock(usbi_mutex_t *mutex); -int usbi_mutex_trylock(usbi_mutex_t *mutex); -int usbi_mutex_destroy(usbi_mutex_t *mutex); - -int usbi_cond_init(usbi_cond_t *cond, - const usbi_condattr_t *attr); -int usbi_cond_destroy(usbi_cond_t *cond); -int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); -int usbi_cond_timedwait(usbi_cond_t *cond, - usbi_mutex_t *mutex, - const struct timespec *abstime); -int usbi_cond_broadcast(usbi_cond_t *cond); -int usbi_cond_signal(usbi_cond_t *cond); - -#endif /* LIBUSB_THREADS_WINDOWS_H */ - diff --git a/interface/external/LibUSB/include/os/windows_usb.h b/interface/external/LibUSB/include/os/windows_usb.h deleted file mode 100644 index ddbd68075b..0000000000 --- a/interface/external/LibUSB/include/os/windows_usb.h +++ /dev/null @@ -1,608 +0,0 @@ -/* - * Windows backend for libusb 1.0 - * Copyright (C) 2009-2010 Pete Batard - * With contributions from Michael Plante, Orin Eman et al. - * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer - * Major code testing contribution by Xiaofan Chen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#if defined(_MSC_VER) -// disable /W4 MSVC warnings that are benign -#pragma warning(disable:4127) // conditional expression is constant -#pragma warning(disable:4100) // unreferenced formal parameter -#pragma warning(disable:4214) // bit field types other than int -#pragma warning(disable:4201) // nameless struct/union -#endif - -// Windows API default is uppercase - ugh! -#if !defined(bool) -#define bool BOOL -#endif -#if !defined(true) -#define true TRUE -#endif -#if !defined(false) -#define false FALSE -#endif - -// Missing from MSVC6 setupapi.h -#if !defined(SPDRP_ADDRESS) -#define SPDRP_ADDRESS 28 -#endif -#if !defined(SPDRP_INSTALL_STATE) -#define SPDRP_INSTALL_STATE 34 -#endif - -#if defined(__CYGWIN__ ) -// cygwin produces a warning unless these prototypes are defined -extern int _snprintf(char *buffer, size_t count, const char *format, ...); -extern char *_strdup(const char *strSource); -// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread -#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f) -#endif -#define safe_free(p) do {if (p != NULL) {free((void*)p); p = NULL;}} while(0) -#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0) -#define safe_min(a, b) min((size_t)(a), (size_t)(b)) -#define safe_strcp(dst, dst_max, src, count) do {memcpy(dst, src, safe_min(count, dst_max)); \ - ((char*)dst)[safe_min(count, dst_max)-1] = 0;} while(0) -#define safe_strcpy(dst, dst_max, src) safe_strcp(dst, dst_max, src, safe_strlen(src)+1) -#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, safe_min(count, dst_max - safe_strlen(dst) - 1)) -#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, safe_strlen(src)+1) -#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2)) -#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"":str1), ((str2==NULL)?"":str2), count) -#define safe_strlen(str) ((str==NULL)?0:strlen(str)) -#define safe_sprintf _snprintf -#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) -#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) -static inline void upperize(char* str) { - size_t i; - if (str == NULL) return; - for (i=0; ios_priv; -} - -static inline void windows_device_priv_init(libusb_device* dev) { - struct windows_device_priv* p = _device_priv(dev); - int i; - p->depth = 0; - p->port = 0; - p->parent_dev = NULL; - p->path = NULL; - p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; - p->composite_api_flags = 0; - p->active_config = 0; - p->config_descriptor = NULL; - memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); - for (i=0; iusb_interface[i].path = NULL; - p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; - p->usb_interface[i].nb_endpoints = 0; - p->usb_interface[i].endpoint = NULL; - } -} - -static inline void windows_device_priv_release(libusb_device* dev) { - struct windows_device_priv* p = _device_priv(dev); - int i; - safe_free(p->path); - if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { - for (i=0; i < dev->num_configurations; i++) - safe_free(p->config_descriptor[i]); - } - safe_free(p->config_descriptor); - for (i=0; iusb_interface[i].path); - safe_free(p->usb_interface[i].endpoint); - } -} - -struct interface_handle_t { - HANDLE dev_handle; // WinUSB needs an extra handle for the file - HANDLE api_handle; // used by the API to communicate with the device -}; - -struct windows_device_handle_priv { - int active_interface; - struct interface_handle_t interface_handle[USB_MAXINTERFACES]; - int autoclaim_count[USB_MAXINTERFACES]; // For auto-release -}; - -static inline struct windows_device_handle_priv *_device_handle_priv( - struct libusb_device_handle *handle) -{ - return (struct windows_device_handle_priv *) handle->os_priv; -} - -// used for async polling functions -struct windows_transfer_priv { - struct winfd pollable_fd; - uint8_t interface_number; -}; - -// used to match a device driver (including filter drivers) against a supported API -struct driver_lookup { - char list[MAX_KEY_LENGTH+1];// REG_MULTI_SZ list of services (driver) names - const DWORD reg_prop; // SPDRP registry key to use to retreive list - const char* designation; // internal designation (for debug output) -}; - -/* - * API macros - from libusb-win32 1.x - */ -#define DLL_DECLARE_PREFIXNAME(api, ret, prefixname, name, args) \ - typedef ret (api * __dll_##name##_t)args; \ - static __dll_##name##_t prefixname = NULL - -#define DLL_LOAD_PREFIXNAME(dll, prefixname, name, ret_on_failure) \ - do { \ - HMODULE h = GetModuleHandleA(#dll); \ - if (!h) \ - h = LoadLibraryA(#dll); \ - if (!h) { \ - if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\ - else { break; } \ - } \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name); \ - if (prefixname) break; \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name "A"); \ - if (prefixname) break; \ - prefixname = (__dll_##name##_t)GetProcAddress(h, #name "W"); \ - if (prefixname) break; \ - if(ret_on_failure) \ - return LIBUSB_ERROR_NOT_FOUND; \ - } while(0) - -#define DLL_DECLARE(api, ret, name, args) DLL_DECLARE_PREFIXNAME(api, ret, name, name, args) -#define DLL_LOAD(dll, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, name, name, ret_on_failure) -#define DLL_DECLARE_PREFIXED(api, ret, prefix, name, args) DLL_DECLARE_PREFIXNAME(api, ret, prefix##name, name, args) -#define DLL_LOAD_PREFIXED(dll, prefix, name, ret_on_failure) DLL_LOAD_PREFIXNAME(dll, prefix##name, name, ret_on_failure) - -/* OLE32 dependency */ -DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); - -/* SetupAPI dependencies */ -DLL_DECLARE_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA, - const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, - PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO)); -DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO, - PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD)); -DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); -DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); - -/* - * Windows DDK API definitions. Most of it copied from MinGW's includes - */ -typedef DWORD DEVNODE, DEVINST; -typedef DEVNODE *PDEVNODE, *PDEVINST; -typedef DWORD RETURN_TYPE; -typedef RETURN_TYPE CONFIGRET; - -#define CR_SUCCESS 0x00000000 -#define CR_NO_SUCH_DEVNODE 0x0000000D - -#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE -#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG -#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING -#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE -#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT - -#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS -#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE -#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE -#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS -#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR -#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR -#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION -#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION -#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE -#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE -#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME - -#define USB_GET_NODE_INFORMATION 258 -#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 -#define USB_GET_NODE_CONNECTION_NAME 261 -#define USB_GET_HUB_CAPABILITIES 271 -#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) -#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 -#endif -#if !defined(USB_GET_HUB_CAPABILITIES_EX) -#define USB_GET_HUB_CAPABILITIES_EX 276 -#endif - -#ifndef METHOD_BUFFERED -#define METHOD_BUFFERED 0 -#endif -#ifndef FILE_ANY_ACCESS -#define FILE_ANY_ACCESS 0x00000000 -#endif -#ifndef FILE_DEVICE_UNKNOWN -#define FILE_DEVICE_UNKNOWN 0x00000022 -#endif -#ifndef FILE_DEVICE_USB -#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN -#endif - -#ifndef CTL_CODE -#define CTL_CODE(DeviceType, Function, Method, Access)( \ - ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) -#endif - -typedef enum USB_CONNECTION_STATUS { - NoDeviceConnected, - DeviceConnected, - DeviceFailedEnumeration, - DeviceGeneralFailure, - DeviceCausedOvercurrent, - DeviceNotEnoughPower, - DeviceNotEnoughBandwidth, - DeviceHubNestedTooDeeply, - DeviceInLegacyHub -} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS; - -typedef enum USB_HUB_NODE { - UsbHub, - UsbMIParent -} USB_HUB_NODE; - -/* Cfgmgr32.dll interface */ -DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG)); -DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG)); -DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG)); -DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG)); - -#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ - CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_HUB_CAPABILITIES \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_ROOT_HUB_NAME \ - CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_NODE_INFORMATION \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_USB_GET_NODE_CONNECTION_NAME \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS) - -// Most of the structures below need to be packed -#pragma pack(push, 1) - -typedef struct USB_INTERFACE_DESCRIPTOR { - UCHAR bLength; - UCHAR bDescriptorType; - UCHAR bInterfaceNumber; - UCHAR bAlternateSetting; - UCHAR bNumEndpoints; - UCHAR bInterfaceClass; - UCHAR bInterfaceSubClass; - UCHAR bInterfaceProtocol; - UCHAR iInterface; -} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; - -typedef struct USB_CONFIGURATION_DESCRIPTOR { - UCHAR bLength; - UCHAR bDescriptorType; - USHORT wTotalLength; - UCHAR bNumInterfaces; - UCHAR bConfigurationValue; - UCHAR iConfiguration; - UCHAR bmAttributes; - UCHAR MaxPower; -} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; - -typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { - struct { - ULONG ConnectionIndex; - struct { - UCHAR bmRequest; - UCHAR bRequest; - USHORT wValue; - USHORT wIndex; - USHORT wLength; - } SetupPacket; - } req; - USB_CONFIGURATION_DESCRIPTOR data; -} USB_CONFIGURATION_DESCRIPTOR_SHORT; - -typedef struct USB_ENDPOINT_DESCRIPTOR { - UCHAR bLength; - UCHAR bDescriptorType; - UCHAR bEndpointAddress; - UCHAR bmAttributes; - USHORT wMaxPacketSize; - UCHAR bInterval; -} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR; - -typedef struct USB_DESCRIPTOR_REQUEST { - ULONG ConnectionIndex; - struct { - UCHAR bmRequest; - UCHAR bRequest; - USHORT wValue; - USHORT wIndex; - USHORT wLength; - } SetupPacket; -// UCHAR Data[0]; -} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST; - -typedef struct USB_HUB_DESCRIPTOR { - UCHAR bDescriptorLength; - UCHAR bDescriptorType; - UCHAR bNumberOfPorts; - USHORT wHubCharacteristics; - UCHAR bPowerOnToPowerGood; - UCHAR bHubControlCurrent; - UCHAR bRemoveAndPowerMask[64]; -} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR; - -typedef struct USB_ROOT_HUB_NAME { - ULONG ActualLength; - WCHAR RootHubName[1]; -} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME; - -typedef struct USB_ROOT_HUB_NAME_FIXED { - ULONG ActualLength; - WCHAR RootHubName[MAX_PATH_LENGTH]; -} USB_ROOT_HUB_NAME_FIXED; - -typedef struct USB_NODE_CONNECTION_NAME { - ULONG ConnectionIndex; - ULONG ActualLength; - WCHAR NodeName[1]; -} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME; - -typedef struct USB_NODE_CONNECTION_NAME_FIXED { - ULONG ConnectionIndex; - ULONG ActualLength; - WCHAR NodeName[MAX_PATH_LENGTH]; -} USB_NODE_CONNECTION_NAME_FIXED; - -typedef struct USB_HUB_NAME_FIXED { - union { - USB_ROOT_HUB_NAME_FIXED root; - USB_NODE_CONNECTION_NAME_FIXED node; - } u; -} USB_HUB_NAME_FIXED; - -typedef struct USB_HUB_INFORMATION { - USB_HUB_DESCRIPTOR HubDescriptor; - BOOLEAN HubIsBusPowered; -} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION; - -typedef struct USB_MI_PARENT_INFORMATION { - ULONG NumberOfInterfaces; -} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION; - -typedef struct USB_NODE_INFORMATION { - USB_HUB_NODE NodeType; - union { - USB_HUB_INFORMATION HubInformation; - USB_MI_PARENT_INFORMATION MiParentInformation; - } u; -} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION; - -typedef struct USB_PIPE_INFO { - USB_ENDPOINT_DESCRIPTOR EndpointDescriptor; - ULONG ScheduleOffset; -} USB_PIPE_INFO, *PUSB_PIPE_INFO; - -typedef struct USB_NODE_CONNECTION_INFORMATION_EX { - ULONG ConnectionIndex; - USB_DEVICE_DESCRIPTOR DeviceDescriptor; - UCHAR CurrentConfigurationValue; - UCHAR Speed; - BOOLEAN DeviceIsHub; - USHORT DeviceAddress; - ULONG NumberOfOpenPipes; - USB_CONNECTION_STATUS ConnectionStatus; -// USB_PIPE_INFO PipeList[0]; -} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; - -typedef struct USB_HUB_CAP_FLAGS { - ULONG HubIsHighSpeedCapable:1; - ULONG HubIsHighSpeed:1; - ULONG HubIsMultiTtCapable:1; - ULONG HubIsMultiTt:1; - ULONG HubIsRoot:1; - ULONG HubIsArmedWakeOnConnect:1; - ULONG ReservedMBZ:26; -} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; - -typedef struct USB_HUB_CAPABILITIES { - ULONG HubIs2xCapable : 1; -} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES; - -typedef struct USB_HUB_CAPABILITIES_EX { - USB_HUB_CAP_FLAGS CapabilityFlags; -} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; - -#pragma pack(pop) - -/* winusb.dll interface */ - -#define SHORT_PACKET_TERMINATE 0x01 -#define AUTO_CLEAR_STALL 0x02 -#define PIPE_TRANSFER_TIMEOUT 0x03 -#define IGNORE_SHORT_PACKETS 0x04 -#define ALLOW_PARTIAL_READS 0x05 -#define AUTO_FLUSH 0x06 -#define RAW_IO 0x07 -#define MAXIMUM_TRANSFER_SIZE 0x08 -#define AUTO_SUSPEND 0x81 -#define SUSPEND_DELAY 0x83 -#define DEVICE_SPEED 0x01 -#define LowSpeed 0x01 -#define FullSpeed 0x02 -#define HighSpeed 0x03 - -typedef enum USBD_PIPE_TYPE { - UsbdPipeTypeControl, - UsbdPipeTypeIsochronous, - UsbdPipeTypeBulk, - UsbdPipeTypeInterrupt -} USBD_PIPE_TYPE; - -typedef struct { - USBD_PIPE_TYPE PipeType; - UCHAR PipeId; - USHORT MaximumPacketSize; - UCHAR Interval; -} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; - -#pragma pack(1) -typedef struct { - UCHAR request_type; - UCHAR request; - USHORT value; - USHORT index; - USHORT length; -} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; -#pragma pack() - -typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; - -DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); diff --git a/interface/external/LibUSB/include/version.h b/interface/external/LibUSB/include/version.h deleted file mode 100644 index 62446da871..0000000000 --- a/interface/external/LibUSB/include/version.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ -#ifndef LIBUSB_MAJOR -#define LIBUSB_MAJOR 1 -#endif -#ifndef LIBUSB_MINOR -#define LIBUSB_MINOR 0 -#endif -#ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 9 -#endif -/* LIBUSB_NANO may be used for Windows internal versioning. 0 means unused. */ -#ifndef LIBUSB_NANO -#define LIBUSB_NANO 0 -#endif -/* LIBUSB_RC is the release candidate suffix. Should normally be empty. */ -#ifndef LIBUSB_RC -#define LIBUSB_RC "" -#endif diff --git a/interface/external/LibUSB/lib/MacOS/libusb-1.0.a b/interface/external/LibUSB/lib/MacOS/libusb-1.0.a deleted file mode 100644 index ac9236f47353cc049fae6717d2af18dde3f4c0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264096 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