Merge branch 'master' of https://github.com/worklist/hifi into keep_local_voxels

This commit is contained in:
ZappoMan 2013-11-21 11:18:47 -08:00
commit e959be82f8
29 changed files with 628 additions and 99 deletions

6
.gitignore vendored
View file

@ -35,5 +35,9 @@ interface/external/Leap/lib/
interface/external/Leap/samples/
interface/external/Leap/util/
# Ignore Sixense
interface/external/Sixense/include/
interface/external/Sixense/lib/
# Ignore interfaceCache for Linux users
interface/interfaceCache/

View file

@ -0,0 +1,44 @@
# Try to find the Sixense controller library
#
# You must provide a SIXENSE_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# SIXENSE_FOUND - system found Sixense
# SIXENSE_INCLUDE_DIRS - the Sixense include directory
# SIXENSE_LIBRARIES - Link this to use Sixense
#
# Created on 11/15/2013 by Andrzej Kapolka
# Copyright (c) 2013 High Fidelity
#
if (SIXENSE_LIBRARIES AND SIXENSE_INCLUDE_DIRS)
# in cache already
set(SIXENSE_FOUND TRUE)
else (SIXENSE_LIBRARIES AND SIXENSE_INCLUDE_DIRS)
find_path(SIXENSE_INCLUDE_DIRS sixense.h ${SIXENSE_ROOT_DIR}/include)
if (APPLE)
find_library(SIXENSE_LIBRARIES libsixense_x64.dylib ${SIXENSE_ROOT_DIR}/lib/osx_x64/release_dll)
elseif (UNIX)
find_library(SIXENSE_LIBRARIES libsixense_x64.so ${SIXENSE_ROOT_DIR}/lib/linux_x64/release)
endif ()
if (SIXENSE_INCLUDE_DIRS AND SIXENSE_LIBRARIES)
set(SIXENSE_FOUND TRUE)
endif (SIXENSE_INCLUDE_DIRS AND SIXENSE_LIBRARIES)
if (SIXENSE_FOUND)
if (NOT SIXENSE_FIND_QUIETLY)
message(STATUS "Found Sixense: ${SIXENSE_LIBRARIES}")
endif (NOT SIXENSE_FIND_QUIETLY)
else (SIXENSE_FOUND)
if (SIXENSE_FIND_REQUIRED)
message(FATAL_ERROR "Could not find Sixense")
endif (SIXENSE_FIND_REQUIRED)
endif (SIXENSE_FOUND)
# show the SIXENSE_INCLUDE_DIRS and SIXENSE_LIBRARIES variables only in the advanced view
mark_as_advanced(SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
endif (SIXENSE_LIBRARIES AND SIXENSE_INCLUDE_DIRS)

View file

@ -15,6 +15,7 @@ set(LEAP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Leap)
set(MOTIONDRIVER_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/MotionDriver)
set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio)
set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV)
set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense)
set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl)
if (APPLE)
@ -95,6 +96,7 @@ find_package(Leap)
find_package(MotionDriver)
find_package(OpenCV)
find_package(OpenNI)
find_package(Sixense)
find_package(UVCCameraControl)
find_package(ZLIB)
@ -106,6 +108,14 @@ if (OPENNI_FOUND AND NOT DISABLE_OPENNI)
target_link_libraries(${TARGET_NAME} ${OPENNI_LIBRARIES})
endif (OPENNI_FOUND AND NOT DISABLE_OPENNI)
# likewise with Sixense library for Razer Hydra
if (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
add_definitions(-DHAVE_SIXENSE)
include_directories(SYSTEM ${SIXENSE_INCLUDE_DIRS})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${SIXENSE_INCLUDE_DIRS}")
target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
endif (SIXENSE_FOUND AND NOT DISABLE_SIXENSE)
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Svg WebKit WebKitWidgets)
# include headers for interface and InterfaceConfig.

11
interface/external/Sixense/readme.txt vendored Normal file
View file

@ -0,0 +1,11 @@
Instructions for adding the Sixense driver to Interface
Andrzej Kapolka, November 18, 2013
NOTE: Without doing step 2, you will crash at program start time.
1. Copy the Sixense sdk folders (lib, include) into the interface/external/Sixense folder. This readme.txt should be there as well.
2. IMPORTANT: Copy the file interface/external/Sixense/lib/osx_x64/release_dll/libsixense_x64.dylib to /usr/lib
3. Delete your build directory, run cmake and build, and you should be all set.

View file

@ -1603,22 +1603,11 @@ void Application::copyVoxels() {
}
}
void Application::pasteVoxels() {
unsigned char* calculatedOctCode = NULL;
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination) {
// Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to
// the server as an set voxel message, this will also rebase the voxels to the new location
SendVoxelsOperationArgs args;
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
// target octalCode for where the user is pointing.
if (selectedNode) {
args.newBaseOctCode = selectedNode->getOctalCode();
} else {
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
}
args.newBaseOctCode = octalCodeDestination;
_sharedVoxelSystem.getTree()->recurseTreeWithOperation(sendVoxelsOperation, &args);
@ -1628,6 +1617,23 @@ void Application::pasteVoxels() {
}
_voxelEditSender.releaseQueuedMessages();
}
void Application::pasteVoxels() {
unsigned char* calculatedOctCode = NULL;
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
// target octalCode for where the user is pointing.
const unsigned char* octalCodeDestination;
if (selectedNode) {
octalCodeDestination = selectedNode->getOctalCode();
} else {
octalCodeDestination = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
}
pasteVoxelsToOctalCode(octalCodeDestination);
if (calculatedOctCode) {
delete[] calculatedOctCode;
@ -2006,14 +2012,14 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
if (!_lookatTargetAvatar) {
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
lookAtSpot = _myCamera.getPosition();
} else if (!_lookatTargetAvatar) {
if (_isHoverVoxel) {
// Look at the hovered voxel
lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel);
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
lookAtSpot = _myCamera.getPosition();
} else {
// Just look in direction of the mouse ray
const float FAR_AWAY_STARE = TREE_SCALE;
@ -2024,10 +2030,9 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
// deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
const float PITCH_SCALE = 0.25f;
const float YAW_SCALE = 0.25f;
float deflection = Menu::getInstance()->getFaceshiftEyeDeflection();
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
_faceshift.getEstimatedEyePitch() * pitchSign * PITCH_SCALE, _faceshift.getEstimatedEyeYaw() * YAW_SCALE, 0.0f))) *
_faceshift.getEstimatedEyePitch() * pitchSign * deflection, _faceshift.getEstimatedEyeYaw() * deflection, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
}
_myAvatar.getHead().setLookAtPosition(lookAtSpot);
@ -2184,8 +2189,10 @@ void Application::updateHandAndTouch(float deltaTime) {
float TOUCH_YAW_SCALE = -0.25f;
float TOUCH_PITCH_SCALE = -12.5f;
float FIXED_TOUCH_TIMESTEP = 0.016f;
const float MAX_PITCH = 90.0f;
_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP);
_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP);
_pitchFromTouch = glm::clamp(_pitchFromTouch + (_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE *
FIXED_TOUCH_TIMESTEP, -MAX_PITCH, MAX_PITCH);
_lastTouchAvgX = _touchAvgX;
_lastTouchAvgY = _touchAvgY;
}
@ -2197,7 +2204,14 @@ void Application::updateLeap(float deltaTime) {
LeapManager::enableFakeFingers(Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand));
_myAvatar.getHand().setRaveGloveActive(Menu::getInstance()->isOptionChecked(MenuOption::TestRaveGlove));
LeapManager::nextFrame(_myAvatar);
LeapManager::nextFrame();
}
void Application::updateSixense() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateSixense()");
_sixenseManager.update();
}
void Application::updateSerialDevices(float deltaTime) {
@ -2407,6 +2421,7 @@ void Application::update(float deltaTime) {
updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels
updateHandAndTouch(deltaTime); // Update state for touch sensors
updateLeap(deltaTime); // Leap finger-sensing device
updateSixense(); // Razer Hydra controllers
updateSerialDevices(deltaTime); // Read serial port interface devices
updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
@ -4087,6 +4102,7 @@ void Application::resetSensors() {
}
_webcam.reset();
_faceshift.reset();
LeapManager::reset();
QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.reset();
_myTransmitter.resetLevels();
@ -4183,9 +4199,11 @@ void Application::nodeKilled(Node* node) {
}
// also clean up scene stats for that server
_voxelSceneStatsLock.lockForWrite();
if (_voxelServerSceneStats.find(nodeUUID) != _voxelServerSceneStats.end()) {
_voxelServerSceneStats.erase(nodeUUID);
}
_voxelSceneStatsLock.unlock();
} else if (node->getLinkedData() == _lookatTargetAvatar) {
_lookatTargetAvatar = NULL;
}
@ -4206,27 +4224,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
QUuid nodeUUID = voxelServer->getUUID();
// now that we know the node ID, let's add these stats to the stats for that node...
_voxelSceneStatsLock.lockForWrite();
if (_voxelServerSceneStats.find(nodeUUID) != _voxelServerSceneStats.end()) {
VoxelSceneStats& oldStats = _voxelServerSceneStats[nodeUUID];
// this if construct is a little strange because we aren't quite using it yet. But
// we want to keep this logic in here for now because we plan to use it soon to determine
// additional network optimization states and better rate control
if (!oldStats.isMoving() && temp.isMoving()) {
// we think we are starting to move
_voxelServerSceneStats[nodeUUID].unpackFromMessage(messageData, messageLength);
} else if (oldStats.isMoving() && !temp.isMoving()) {
// we think we are done moving
_voxelServerSceneStats[nodeUUID].unpackFromMessage(messageData, messageLength);
} else if (!oldStats.isMoving() && !temp.isMoving()) {
// we think we are still not moving
} else {
// we think we are still moving
}
_voxelServerSceneStats[nodeUUID].unpackFromMessage(messageData, messageLength);
} else {
_voxelServerSceneStats[nodeUUID] = temp;
}
_voxelSceneStatsLock.unlock();
VoxelPositionSize rootDetails;
voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails);

View file

@ -50,6 +50,7 @@
#include "avatar/HandControl.h"
#include "devices/Faceshift.h"
#include "devices/SerialInterface.h"
#include "devices/SixenseManager.h"
#include "devices/Webcam.h"
#include "renderer/AmbientOcclusionEffect.h"
#include "renderer/GeometryCache.h"
@ -136,6 +137,8 @@ public:
Swatch* getSwatch() { return &_swatch; }
QMainWindow* getWindow() { return _window; }
NodeToVoxelSceneStats* getVoxelSceneStats() { return &_voxelServerSceneStats; }
void lockVoxelSceneStats() { _voxelSceneStatsLock.lockForRead(); }
void unlockVoxelSceneStats() { _voxelSceneStatsLock.unlock(); }
QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
GeometryCache* getGeometryCache() { return &_geometryCache; }
@ -171,7 +174,7 @@ public:
glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination);
public slots:
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
@ -242,6 +245,7 @@ private:
glm::vec3& eyePosition);
void updateHandAndTouch(float deltaTime);
void updateLeap(float deltaTime);
void updateSixense();
void updateSerialDevices(float deltaTime);
void updateThreads(float deltaTime);
void updateMyAvatarSimulation(float deltaTime);
@ -337,6 +341,8 @@ private:
Faceshift _faceshift;
SixenseManager _sixenseManager;
Camera _myCamera; // My view onto the world
Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode
Camera _mirrorCamera; // Cammera for mirror view
@ -454,6 +460,7 @@ private:
NodeToJurisdictionMap _voxelServerJurisdictions;
NodeToVoxelSceneStats _voxelServerSceneStats;
QReadWriteLock _voxelSceneStatsLock;
std::vector<VoxelFade> _voxelFades;
};

View file

@ -46,12 +46,14 @@ Menu* Menu::getInstance() {
}
const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f};
const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
Menu::Menu() :
_actionHash(),
_audioJitterBufferSamples(0),
_bandwidthDialog(NULL),
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
_faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION),
_frustumDrawMode(FRUSTUM_DRAW_MODE_ALL),
_viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET),
_voxelModeActionsGroup(NULL),
@ -70,7 +72,8 @@ Menu::Menu() :
MenuOption::AboutApp,
0,
this,
SLOT(aboutApp()));
SLOT(aboutApp()),
QAction::AboutRole);
#endif
(addActionToQMenuAndActionHash(fileMenu,
@ -118,7 +121,9 @@ Menu::Menu() :
MenuOption::Quit,
Qt::CTRL | Qt::Key_Q,
appInstance,
SLOT(quit()));
SLOT(quit()),
QAction::QuitRole);
QMenu* editMenu = addMenu("Edit");
@ -126,7 +131,8 @@ Menu::Menu() :
MenuOption::Preferences,
Qt::CTRL | Qt::Key_Comma,
this,
SLOT(editPreferences()));
SLOT(editPreferences()),
QAction::PreferencesRole);
addDisabledActionAndSeparator(editMenu, "Voxels");
@ -241,6 +247,7 @@ Menu::Menu() :
MenuOption::TurnWithHead,
0,
true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false);
@ -359,6 +366,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::DisplayLeapHands, 0, true);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::LeapDrive, 0, false);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove);
QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options");
@ -486,7 +494,11 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel);
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ExtraDebugging);
addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel,
Qt::CTRL | Qt::SHIFT | Qt::Key_V,
this,
SLOT(pasteToVoxel()));
#ifndef Q_OS_MAC
QMenu* helpMenu = addMenu("Help");
@ -508,6 +520,7 @@ void Menu::loadSettings(QSettings* settings) {
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_VOXEL_SIZE_SCALE);
_boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0);
@ -536,6 +549,7 @@ void Menu::saveSettings(QSettings* settings) {
settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples);
settings->setValue("fieldOfView", _fieldOfView);
settings->setValue("faceshiftEyeDeflection", _faceshiftEyeDeflection);
settings->setValue("maxVoxels", _maxVoxels);
settings->setValue("voxelSizeScale", _voxelSizeScale);
settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust);
@ -671,7 +685,8 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString actionName,
const QKeySequence& shortcut,
const QObject* receiver,
const char* member) {
const char* member,
QAction::MenuRole role) {
QAction* action;
if (receiver && member) {
@ -680,6 +695,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
action = destinationMenu->addAction(actionName);
action->setShortcut(shortcut);
}
action->setMenuRole(role);
_actionHash.insert(actionName, action);
@ -786,6 +802,10 @@ void Menu::editPreferences() {
pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum());
form->addRow("Pupil Dilation:", pupilDilation);
QSlider* faceshiftEyeDeflection = new QSlider(Qt::Horizontal);
faceshiftEyeDeflection->setValue(_faceshiftEyeDeflection * faceshiftEyeDeflection->maximum());
form->addRow("Faceshift Eye Deflection:", faceshiftEyeDeflection);
QSpinBox* fieldOfView = new QSpinBox();
fieldOfView->setMaximum(180);
fieldOfView->setMinimum(1);
@ -861,6 +881,8 @@ void Menu::editPreferences() {
_fieldOfView = fieldOfView->value();
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
_faceshiftEyeDeflection = faceshiftEyeDeflection->value() / (float)faceshiftEyeDeflection->maximum();
}
sendFakeEnterEvent();
@ -958,6 +980,33 @@ void Menu::goToUser() {
sendFakeEnterEvent();
}
void Menu::pasteToVoxel() {
QInputDialog pasteToOctalCodeDialog(Application::getInstance()->getWindow());
pasteToOctalCodeDialog.setWindowTitle("Paste to Voxel");
pasteToOctalCodeDialog.setLabelText("Octal Code:");
QString octalCode = "";
pasteToOctalCodeDialog.setTextValue(octalCode);
pasteToOctalCodeDialog.setWindowFlags(Qt::Sheet);
pasteToOctalCodeDialog.resize(pasteToOctalCodeDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW,
pasteToOctalCodeDialog.size().height());
int dialogReturn = pasteToOctalCodeDialog.exec();
if (dialogReturn == QDialog::Accepted && !pasteToOctalCodeDialog.textValue().isEmpty()) {
// we got an octalCode to paste to...
QString locationToPaste = pasteToOctalCodeDialog.textValue();
unsigned char* octalCodeDestination = hexStringToOctalCode(locationToPaste);
// check to see if it was a legit octcode...
if (locationToPaste == octalCodeToHexString(octalCodeDestination)) {
Application::getInstance()->pasteVoxelsToOctalCode(octalCodeDestination);
} else {
qDebug() << "problem with octcode...\n";
}
}
sendFakeEnterEvent();
}
void Menu::bandwidthDetails() {
if (! _bandwidthDialog) {
_bandwidthDialog = new BandwidthDialog(Application::getInstance()->getGLWidget(),

View file

@ -50,6 +50,7 @@ public:
float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; }
float getFieldOfView() const { return _fieldOfView; }
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
BandwidthDialog* getBandwidthDialog() const { return _bandwidthDialog; }
FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; }
ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; }
@ -76,6 +77,7 @@ public slots:
void importSettings();
void exportSettings();
void goToUser();
void pasteToVoxel();
private slots:
void aboutApp();
@ -111,7 +113,9 @@ private:
const QString actionName,
const QKeySequence& shortcut = 0,
const QObject* receiver = NULL,
const char* member = NULL);
const char* member = NULL,
QAction::MenuRole role = QAction::NoRole);
QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString actionName,
const QKeySequence& shortcut = 0,
@ -126,6 +130,7 @@ private:
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
BandwidthDialog* _bandwidthDialog;
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
float _faceshiftEyeDeflection;
FrustumDrawMode _frustumDrawMode;
ViewFrustumOffset _viewFrustumOffset;
QActionGroup* _voxelModeActionsGroup;
@ -195,6 +200,7 @@ namespace MenuOption {
const QString GoHome = "Go Home";
const QString Gravity = "Use Gravity";
const QString ParticleCloud = "Particle Cloud";
const QString LeapDrive = "Leap Drive";
const QString LodTools = "LOD Tools";
const QString Log = "Log";
const QString Login = "Login";
@ -202,6 +208,7 @@ namespace MenuOption {
const QString LookAtVectors = "Look-at Vectors";
const QString LowRes = "Lower Resolution While Moving";
const QString Mirror = "Mirror";
const QString MoveWithLean = "Move with Lean";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString NudgeVoxels = "Nudge";
const QString OcclusionCulling = "Occlusion Culling";
@ -212,6 +219,7 @@ namespace MenuOption {
const QString Oscilloscope = "Audio Oscilloscope";
const QString Pair = "Pair";
const QString PasteVoxels = "Paste";
const QString PasteToVoxel = "Paste to Voxel...";
const QString PipelineWarnings = "Show Render Pipeline Warnings";
const QString Preferences = "Preferences...";
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";

View file

@ -398,6 +398,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
headPosition = _bodyBall[BODY_BALL_HEAD_BASE].position;
}
_head.setPosition(headPosition);
_head.setScale(_scale);
_head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]));
_head.simulate(deltaTime, false);

View file

@ -100,7 +100,9 @@ enum DriveKeys {
UP,
DOWN,
ROT_LEFT,
ROT_RIGHT,
ROT_RIGHT,
ROT_UP,
ROT_DOWN,
MAX_DRIVE_KEYS
};

View file

@ -264,7 +264,7 @@ void Hand::renderLeapHands() {
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
const float palmThickness = 0.002f;
const float palmThickness = 0.02f;
glColor4f(handColor.r, handColor.g, handColor.b, 0.25);
glm::vec3 tip = palm.getPosition();
glm::vec3 root = palm.getPosition() + palm.getNormal() * palmThickness;

View file

@ -27,6 +27,7 @@ using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
const float YAW_MAG = 500.0;
const float PITCH_MAG = 100.0f;
const float COLLISION_RADIUS_SCALAR = 1.2; // pertains to avatar-to-avatar collisions
const float COLLISION_BALL_FORCE = 200.0; // pertains to avatar-to-avatar collisions
const float COLLISION_BODY_FORCE = 30.0; // pertains to avatar-to-avatar collisions
@ -42,6 +43,7 @@ MyAvatar::MyAvatar(Node* owningNode) :
_mousePressed(false),
_bodyPitchDelta(0.0f),
_bodyRollDelta(0.0f),
_mousePitchDelta(0.0f),
_shouldJump(false),
_gravity(0.0f, -1.0f, 0.0f),
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
@ -57,7 +59,7 @@ MyAvatar::MyAvatar(Node* owningNode) :
_moveTargetStepCounter(0)
{
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = false;
_driveKeys[i] = 0.0f;
}
_collisionRadius = _height * COLLISION_RADIUS_SCALE;
@ -368,6 +370,8 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) {
}
const float MAX_PITCH = 90.0f;
// Update avatar head rotation with sensor data
void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHead) {
Faceshift* faceshift = Application::getInstance()->getFaceshift();
@ -375,6 +379,7 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
Webcam* webcam = Application::getInstance()->getWebcam();
glm::vec3 estimatedPosition, estimatedRotation;
float combinedPitch = glm::clamp(pitchFromTouch + _mousePitchDelta, -MAX_PITCH, MAX_PITCH);
if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
@ -395,8 +400,8 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
} else {
if (!_leadingAvatar) {
_head.setMousePitch(pitchFromTouch);
_head.setPitch(pitchFromTouch);
_head.setMousePitch(combinedPitch);
_head.setPitch(combinedPitch);
}
_head.getVideoFace().clearFrame();
@ -408,7 +413,7 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
_head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, RESTORE_RATE));
return;
}
_head.setMousePitch(pitchFromTouch);
_head.setMousePitch(combinedPitch);
if (webcam->isActive()) {
estimatedPosition = webcam->getEstimatedPosition();
@ -444,12 +449,35 @@ void MyAvatar::updateFromGyrosAndOrWebcam(float pitchFromTouch, bool turnWithHea
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
// Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = _scale * 0.5f;
const float TORSO_LENGTH = 0.5f;
glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f);
const float MAX_LEAN = 45.0f;
_head.setLeanSideways(glm::clamp(glm::degrees(atanf(estimatedPosition.x * _leanScale / TORSO_LENGTH)),
_head.setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
_head.setLeanForward(glm::clamp(glm::degrees(atanf(estimatedPosition.z * _leanScale / TORSO_LENGTH)),
_head.setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)),
-MAX_LEAN, MAX_LEAN));
// if Faceshift drive is enabled, set the avatar drive based on the head position
if (!Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) {
return;
}
const float ANGULAR_DRIVE_SCALE = 0.1f;
const float ANGULAR_DEAD_ZONE = 0.3f;
setDriveKeys(FWD, glm::clamp(-_head.getLeanForward() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(BACK, glm::clamp(_head.getLeanForward() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(LEFT, glm::clamp(_head.getLeanSideways() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
setDriveKeys(RIGHT, glm::clamp(-_head.getLeanSideways() * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
// only consider going up if we're not going in any of the four horizontal directions
if (_driveKeys[FWD] == 0.0f && _driveKeys[BACK] == 0.0f && _driveKeys[LEFT] == 0.0f && _driveKeys[RIGHT] == 0.0f) {
const float LINEAR_DRIVE_SCALE = 5.0f;
const float LINEAR_DEAD_ZONE = 0.95f;
float torsoDelta = glm::length(relativePosition) - TORSO_LENGTH;
setDriveKeys(UP, glm::clamp(torsoDelta * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
} else {
setDriveKeys(UP, 0.0f);
}
}
static TextRenderer* textRenderer() {
@ -707,14 +735,16 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {
const float THRUST_JUMP = 120.f;
// Add Thrusts from keyboard
if (_driveKeys[FWD]) {_thrust += _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front;}
if (_driveKeys[BACK]) {_thrust -= _scale * THRUST_MAG_BACK * _thrustMultiplier * deltaTime * front;}
if (_driveKeys[RIGHT]) {_thrust += _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;}
if (_driveKeys[LEFT]) {_thrust -= _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;}
if (_driveKeys[UP]) {_thrust += _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up;}
if (_driveKeys[DOWN]) {_thrust -= _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;}
if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;}
if (_driveKeys[ROT_LEFT]) {_bodyYawDelta += YAW_MAG * deltaTime;}
_thrust += _driveKeys[FWD] * _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front;
_thrust -= _driveKeys[BACK] * _scale * THRUST_MAG_BACK * _thrustMultiplier * deltaTime * front;
_thrust += _driveKeys[RIGHT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;
_thrust -= _driveKeys[LEFT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;
_thrust += _driveKeys[UP] * _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up;
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime;
_mousePitchDelta = min(_mousePitchDelta + _driveKeys[ROT_UP] * PITCH_MAG * deltaTime, MAX_PITCH);
_mousePitchDelta = max(_mousePitchDelta - _driveKeys[ROT_DOWN] * PITCH_MAG * deltaTime, -MAX_PITCH);
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {

View file

@ -54,7 +54,7 @@ public:
void loadData(QSettings* settings);
// Set what driving keys are being pressed to control thrust levels
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
bool getDriveKeys(int key) { return _driveKeys[key]; };
void jump() { _shouldJump = true; };
@ -66,8 +66,9 @@ private:
bool _mousePressed;
float _bodyPitchDelta;
float _bodyRollDelta;
float _mousePitchDelta;
bool _shouldJump;
int _driveKeys[MAX_DRIVE_KEYS];
float _driveKeys[MAX_DRIVE_KEYS];
glm::vec3 _gravity;
float _distanceToNearestAvatar; // How close is the nearest avatar?
Avatar* _interactingOther;

View file

@ -10,6 +10,7 @@
#include <SharedUtil.h>
#include "Application.h"
#include "Faceshift.h"
#include "Menu.h"
#include "Util.h"

View file

@ -6,12 +6,16 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "LeapManager.h"
#include "avatar/Avatar.h"
#include <Leap.h>
#include <dlfcn.h> // needed for RTLD_LAZY
#include <sstream>
#include <Leap.h>
#include "Application.h"
#include "LeapManager.h"
#include "Menu.h"
// Uncomment the next line to use Leap-smoothed stabilized (slower) data.
//#define USE_STABILIZED_DATA
@ -19,6 +23,7 @@ bool LeapManager::_libraryExists = false;
bool LeapManager::_doFakeFingers = false;
Leap::Controller* LeapManager::_controller = NULL;
HifiLeapListener* LeapManager::_listener = NULL;
glm::vec3 LeapManager::_baseDrivePosition(0.0f, 150.0f, 0.0f); // experimentally derived
class HifiLeapListener : public Leap::Listener {
public:
@ -44,6 +49,25 @@ void LeapManager::initialize() {
#endif
}
static PalmData* getRightmostPalm() {
PalmData* rightmostPalm = NULL;
Hand& hand = Application::getInstance()->getAvatar()->getHand();
for (int i = 0; i < hand.getNumPalms(); i++) {
PalmData* palm = &hand.getPalms()[i];
if (palm->isActive() && (rightmostPalm == NULL || palm->getRawPosition().x > rightmostPalm->getRawPosition().x)) {
rightmostPalm = palm;
}
}
return rightmostPalm;
}
void LeapManager::reset() {
PalmData* rightmostPalm = getRightmostPalm();
if (rightmostPalm != NULL) {
_baseDrivePosition = rightmostPalm->getRawPosition();
}
}
void LeapManager::terminate() {
delete _listener;
delete _controller;
@ -51,9 +75,10 @@ void LeapManager::terminate() {
_controller = NULL;
}
void LeapManager::nextFrame(Avatar& avatar) {
void LeapManager::nextFrame() {
// Apply the frame data directly to the avatar.
Hand& hand = avatar.getHand();
MyAvatar* avatar = Application::getInstance()->getAvatar();
Hand& hand = avatar->getHand();
// If we actually get valid Leap data, this will be set to true;
bool gotRealData = false;
@ -244,6 +269,55 @@ void LeapManager::nextFrame(Avatar& avatar) {
}
}
hand.updateFingerTrails();
// if Leap drive is enabled, drive avatar based on Leap input
if (!Menu::getInstance()->isOptionChecked(MenuOption::LeapDrive)) {
return;
}
glm::vec3 relativePosition;
glm::vec3 eulerAngles;
PalmData* rightmostPalm = getRightmostPalm();
if (rightmostPalm != NULL) {
relativePosition = rightmostPalm->getRawPosition() - _baseDrivePosition;
glm::vec3 directionSum;
int activeFingerCount = 0;
for (int i = 0; i < rightmostPalm->getNumFingers(); i++) {
FingerData& finger = rightmostPalm->getFingers()[i];
glm::vec3 fingerVector = finger.getTipRawPosition() - rightmostPalm->getRawPosition();
if (finger.isActive() && glm::length(fingerVector) > EPSILON) {
directionSum += glm::normalize(fingerVector);
activeFingerCount++;
}
}
const int MIN_DIRECTION_FINGER_COUNT = 3;
glm::vec3 right;
if (activeFingerCount >= MIN_DIRECTION_FINGER_COUNT) {
right = glm::normalize(glm::cross(glm::normalize(directionSum), -rightmostPalm->getRawNormal()));
} else {
right = glm::normalize(glm::cross(IDENTITY_FRONT, -rightmostPalm->getRawNormal()));
}
eulerAngles = safeEulerAngles(glm::quat_cast(glm::mat3(right, -rightmostPalm->getRawNormal(),
glm::cross(right, -rightmostPalm->getRawNormal()))));
}
const float LINEAR_DRIVE_SCALE = 0.01f;
const float LINEAR_DEAD_ZONE = 0.25f;
avatar->setDriveKeys(FWD, glm::clamp(-relativePosition.z * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(BACK, glm::clamp(relativePosition.z * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(LEFT, glm::clamp(-relativePosition.x * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(RIGHT, glm::clamp(relativePosition.x * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(UP, glm::clamp(relativePosition.y * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(DOWN, glm::clamp(-relativePosition.y * LINEAR_DRIVE_SCALE - LINEAR_DEAD_ZONE, 0.0f, 1.0f));
const float ANGULAR_DRIVE_SCALE = 0.05f;
const float ANGULAR_DEAD_ZONE = 0.75f;
avatar->setDriveKeys(ROT_LEFT, glm::clamp(glm::max(eulerAngles.y, eulerAngles.z) * ANGULAR_DRIVE_SCALE -
ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(ROT_RIGHT, glm::clamp(glm::max(-eulerAngles.y, -eulerAngles.z) * ANGULAR_DRIVE_SCALE -
ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(ROT_UP, glm::clamp(eulerAngles.x * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
avatar->setDriveKeys(ROT_DOWN, glm::clamp(-eulerAngles.x * ANGULAR_DRIVE_SCALE - ANGULAR_DEAD_ZONE, 0.0f, 1.0f));
}
void LeapManager::enableFakeFingers(bool enable) {

View file

@ -21,7 +21,7 @@ namespace Leap {
class LeapManager {
public:
static void nextFrame(Avatar& avatar); // called once per frame to get new Leap data
static void nextFrame(); // called once per frame to get new Leap data
static bool controllersExist(); // Returns true if there's at least one active Leap plugged in
static void enableFakeFingers(bool enable); // put fake data in if there's no Leap plugged in
static const std::vector<glm::vec3>& getFingerTips();
@ -30,6 +30,7 @@ public:
static const std::vector<glm::vec3>& getHandNormals();
static std::string statusString();
static void initialize();
static void reset();
static void terminate();
private:
@ -37,6 +38,7 @@ private:
static bool _doFakeFingers;
static Leap::Controller* _controller;
static HifiLeapListener* _listener;
static glm::vec3 _baseDrivePosition;
};
#endif /* defined(__hifi__LeapManager__) */

View file

@ -0,0 +1,87 @@
//
// Sixense.cpp
// interface
//
// Created by Andrzej Kapolka on 11/15/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifdef HAVE_SIXENSE
#include "sixense.h"
#endif
#include "Application.h"
#include "SixenseManager.h"
SixenseManager::SixenseManager() {
#ifdef HAVE_SIXENSE
sixenseInit();
#endif
}
SixenseManager::~SixenseManager() {
#ifdef HAVE_SIXENSE
sixenseExit();
#endif
}
void SixenseManager::update() {
#ifdef HAVE_SIXENSE
if (sixenseGetNumActiveControllers() == 0) {
return;
}
MyAvatar* avatar = Application::getInstance()->getAvatar();
Hand& hand = avatar->getHand();
hand.getPalms().clear();
int maxControllers = sixenseGetMaxControllers();
for (int i = 0; i < maxControllers; i++) {
if (!sixenseIsControllerEnabled(i)) {
continue;
}
sixenseControllerData data;
sixenseGetNewestData(i, &data);
// drive avatar with joystick and triggers
if (data.controller_index) {
avatar->setDriveKeys(ROT_LEFT, qMax(0.0f, -data.joystick_x));
avatar->setDriveKeys(ROT_RIGHT, qMax(0.0f, data.joystick_x));
avatar->setDriveKeys(ROT_UP, qMax(0.0f, data.joystick_y));
avatar->setDriveKeys(ROT_DOWN, qMax(0.0f, -data.joystick_y));
avatar->setDriveKeys(UP, data.trigger);
} else {
avatar->setDriveKeys(FWD, qMax(0.0f, data.joystick_y));
avatar->setDriveKeys(BACK, qMax(0.0f, -data.joystick_y));
avatar->setDriveKeys(LEFT, qMax(0.0f, -data.joystick_x));
avatar->setDriveKeys(RIGHT, qMax(0.0f, data.joystick_x));
avatar->setDriveKeys(DOWN, data.trigger);
}
// set palm position and normal based on Hydra position/orientation
PalmData palm(&hand);
palm.setActive(true);
glm::vec3 position(-data.pos[0], data.pos[1], -data.pos[2]);
palm.setRawPosition(position);
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
palm.setRawNormal(rotation * PALM_VECTOR);
// initialize the "finger" based on the direction
FingerData finger(&palm, &hand);
finger.setActive(true);
finger.setRawRootPosition(position);
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, 100.0f);
finger.setRawTipPosition(position + rotation * FINGER_VECTOR);
// three fingers indicates to the skeleton that we have enough data to determine direction
palm.getFingers().clear();
palm.getFingers().push_back(finger);
palm.getFingers().push_back(finger);
palm.getFingers().push_back(finger);
hand.getPalms().push_back(palm);
}
#endif
}

View file

@ -0,0 +1,22 @@
//
// SixenseManager.h
// interface
//
// Created by Andrzej Kapolka on 11/15/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__SixenseManager__
#define __interface__SixenseManager__
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
class SixenseManager {
public:
SixenseManager();
~SixenseManager();
void update();
};
#endif /* defined(__interface__SixenseManager__) */

View file

@ -1234,7 +1234,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
// find the clusters with which the mesh is associated
extracted.mesh.isEye = false;
QVector<QString> clusterIDs;
foreach (const QString& childID, childMap.values(it.key())) {
foreach (const QString& clusterID, childMap.values(childID)) {
@ -1245,12 +1244,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
const Cluster& cluster = clusters[clusterID];
clusterIDs.append(clusterID);
QString jointID = childMap.value(clusterID);
if (jointID == jointEyeLeftID || jointID == jointEyeRightID) {
extracted.mesh.isEye = true;
}
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
// of skinning information in FBX
QString jointID = childMap.value(clusterID);
fbxCluster.jointIndex = modelIDs.indexOf(jointID);
if (fbxCluster.jointIndex == -1) {
qDebug() << "Joint not in model list: " << jointID << "\n";
@ -1278,16 +1274,19 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
// whether we're skinned depends on how many clusters are attached
int maxJointIndex = extracted.mesh.clusters.at(0).jointIndex;
if (clusterIDs.size() > 1) {
extracted.mesh.clusterIndices.resize(extracted.mesh.vertices.size());
extracted.mesh.clusterWeights.resize(extracted.mesh.vertices.size());
float maxWeight = 0.0f;
for (int i = 0; i < clusterIDs.size(); i++) {
QString clusterID = clusterIDs.at(i);
const Cluster& cluster = clusters[clusterID];
float totalWeight = 0.0f;
for (int j = 0; j < cluster.indices.size(); j++) {
int oldIndex = cluster.indices.at(j);
float weight = cluster.weights.at(j);
totalWeight += weight;
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
glm::vec4& weights = extracted.mesh.clusterWeights[it.value()];
@ -1302,8 +1301,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
}
}
if (totalWeight > maxWeight) {
maxWeight = totalWeight;
maxJointIndex = extracted.mesh.clusters.at(i).jointIndex;
}
}
}
extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex);
// extract spring edges, connections if springy
if (extracted.mesh.springiness > 0.0f) {

View file

@ -154,6 +154,8 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
unsigned long totalNodes = 0;
unsigned long totalInternal = 0;
unsigned long totalLeaves = 0;
Application::getInstance()->lockVoxelSceneStats();
NodeToVoxelSceneStats* sceneStats = Application::getInstance()->getVoxelSceneStats();
for(NodeToVoxelSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) {
//const QUuid& uuid = i->first;
@ -176,6 +178,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
sendingMode << "S";
}
}
Application::getInstance()->unlockVoxelSceneStats();
sendingMode << " - " << serverCount << " servers";
if (movingServerCount > 0) {
sendingMode << " <SCENE NOT STABLE>";

View file

@ -60,19 +60,21 @@ bool VoxelPersistThread::process() {
VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
_lastSave = usecTimestampNow(); // we just loaded, no need to save again
_lastCheck = usecTimestampNow(); // we just loaded, no need to save again
}
if (isStillRunning()) {
uint64_t MSECS_TO_USECS = 1000;
uint64_t USECS_TO_SLEEP = 100 * MSECS_TO_USECS; // every 100ms
usleep(USECS_TO_SLEEP);
if ((usecTimestampNow() - _lastSave) > (_persistInterval * MSECS_TO_USECS)) {
qDebug("checking if voxels are dirty.\n");
uint64_t now = usecTimestampNow();
uint64_t sinceLastSave = now - _lastCheck;
uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS;
if (sinceLastSave > intervalToCheck) {
// check the dirty bit and persist here...
_lastCheck = usecTimestampNow();
if (_tree->isDirty()) {
_lastSave = usecTimestampNow();
qDebug("saving voxels to file %s...\n",_filename);
_tree->writeToSVOFile(_filename);
_tree->clearDirtyBit(); // tree is clean after saving

View file

@ -38,7 +38,7 @@ private:
time_t _loadCompleted;
uint64_t _loadTimeUSecs;
uint64_t _lastSave;
uint64_t _lastCheck;
};
#endif // __voxel_server__VoxelPersistThread__

View file

@ -189,17 +189,22 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display voxel file load time
if (theServer->isInitialLoadComplete()) {
tm* voxelsLoadedAtLocal = localtime(theServer->getLoadCompleted());
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
mg_printf(connection, "Voxels Loaded At: %s", buffer);
time_t* loadCompleted = theServer->getLoadCompleted();
if (loadCompleted) {
tm* voxelsLoadedAtLocal = localtime(loadCompleted);
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
mg_printf(connection, "Voxels Loaded At: %s", buffer);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
mg_printf(connection, " [%s UTM] ", buffer);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
mg_printf(connection, " [%s UTM] ", buffer);
}
} else {
mg_printf(connection, "%s", "Voxel Persist Disabled...\r\n");
}
mg_printf(connection, "%s", "\r\n");
@ -259,7 +264,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display inbound packet stats
mg_printf(connection, "%s", "<b>Voxel Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n");
uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverateTransitTimePerPacket();
uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerVoxel();
@ -297,7 +302,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "\r\n Stats for sender %d uuid: %s\r\n", senderNumber,
senderID.toString().toLocal8Bit().constData());
averageTransitTimePerPacket = senderStats.getAverateTransitTimePerPacket();
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
averageProcessTimePerVoxel = senderStats.getAverageProcessTimePerVoxel();
@ -305,6 +310,8 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
totalVoxelsProcessed = senderStats.getTotalVoxelsProcessed();
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Inbound Voxels: %s voxels\r\n",

View file

@ -20,7 +20,7 @@ class SingleSenderStats {
public:
SingleSenderStats();
uint64_t getAverateTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }
@ -48,7 +48,7 @@ class VoxelServerPacketProcessor : public ReceivedPacketProcessor {
public:
VoxelServerPacketProcessor(VoxelServer* myServer);
uint64_t getAverateTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }

View file

@ -1418,25 +1418,32 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
return distance;
}
QReadWriteLock VoxelNode::_deleteHooksLock;
std::vector<VoxelNodeDeleteHook*> VoxelNode::_deleteHooks;
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
_deleteHooksLock.lockForWrite();
_deleteHooks.push_back(hook);
_deleteHooksLock.unlock();
}
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
_deleteHooksLock.lockForWrite();
for (int i = 0; i < _deleteHooks.size(); i++) {
if (_deleteHooks[i] == hook) {
_deleteHooks.erase(_deleteHooks.begin() + i);
return;
break;
}
}
_deleteHooksLock.unlock();
}
void VoxelNode::notifyDeleteHooks() {
_deleteHooksLock.lockForRead();
for (int i = 0; i < _deleteHooks.size(); i++) {
_deleteHooks[i]->voxelDeleted(this);
}
_deleteHooksLock.unlock();
}
std::vector<VoxelNodeUpdateHook*> VoxelNode::_updateHooks;

View file

@ -13,6 +13,8 @@
//#define SIMPLE_CHILD_ARRAY
#define SIMPLE_EXTERNAL_CHILDREN
#include <QReadWriteLock>
#include <SharedUtil.h>
#include "AABox.h"
#include "ViewFrustum.h"
@ -239,7 +241,10 @@ private:
_unknownBufferIndex : 1,
_childrenExternal : 1; /// Client only, is this voxel's VBO buffer the unknown buffer index, 1 bit
static QReadWriteLock _deleteHooksLock;
static std::vector<VoxelNodeDeleteHook*> _deleteHooks;
//static QReadWriteLock _updateHooksLock;
static std::vector<VoxelNodeUpdateHook*> _updateHooks;
static uint64_t _voxelNodeCount;

View file

@ -28,6 +28,97 @@ VoxelSceneStats::VoxelSceneStats() :
_isStarted = false;
}
// copy constructor
VoxelSceneStats::VoxelSceneStats(const VoxelSceneStats& other) :
_jurisdictionRoot(NULL) {
copyFromOther(other);
}
// copy assignment
VoxelSceneStats& VoxelSceneStats::operator=(const VoxelSceneStats& other) {
copyFromOther(other);
return *this;
}
void VoxelSceneStats::copyFromOther(const VoxelSceneStats& other) {
_totalEncodeTime = other._totalEncodeTime;
_encodeStart = other._encodeStart;
_packets = other._packets;
_bytes = other._bytes;
_passes = other._passes;
_totalVoxels = other._totalVoxels;
_totalInternal = other._totalInternal;
_totalLeaves = other._totalLeaves;
_traversed = other._traversed;
_internal = other._internal;
_leaves = other._leaves;
_skippedDistance = other._skippedDistance;
_internalSkippedDistance = other._internalSkippedDistance;
_leavesSkippedDistance = other._leavesSkippedDistance;
_skippedOutOfView = other._skippedOutOfView;
_internalSkippedOutOfView = other._internalSkippedOutOfView;
_leavesSkippedOutOfView = other._leavesSkippedOutOfView;
_skippedWasInView = other._skippedWasInView;
_internalSkippedWasInView = other._internalSkippedWasInView;
_leavesSkippedWasInView = other._leavesSkippedWasInView;
_skippedNoChange = other._skippedNoChange;
_internalSkippedNoChange = other._internalSkippedNoChange;
_leavesSkippedNoChange = other._leavesSkippedNoChange;
_skippedOccluded = other._skippedOccluded;
_internalSkippedOccluded = other._internalSkippedOccluded;
_leavesSkippedOccluded = other._leavesSkippedOccluded;
_colorSent = other._colorSent;
_internalColorSent = other._internalColorSent;
_leavesColorSent = other._leavesColorSent;
_didntFit = other._didntFit;
_internalDidntFit = other._internalDidntFit;
_leavesDidntFit = other._leavesDidntFit;
_colorBitsWritten = other._colorBitsWritten;
_existsBitsWritten = other._existsBitsWritten;
_existsInPacketBitsWritten = other._existsInPacketBitsWritten;
_treesRemoved = other._treesRemoved;
// before copying the jurisdictions, delete any current values...
if (_jurisdictionRoot) {
delete[] _jurisdictionRoot;
_jurisdictionRoot = NULL;
}
for (int i=0; i < _jurisdictionEndNodes.size(); i++) {
if (_jurisdictionEndNodes[i]) {
delete[] _jurisdictionEndNodes[i];
}
}
_jurisdictionEndNodes.clear();
// Now copy the values from the other
if (other._jurisdictionRoot) {
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(other._jurisdictionRoot));
_jurisdictionRoot = new unsigned char[bytes];
memcpy(_jurisdictionRoot, other._jurisdictionRoot, bytes);
}
for (int i=0; i < other._jurisdictionEndNodes.size(); i++) {
unsigned char* endNodeCode = other._jurisdictionEndNodes[i];
if (endNodeCode) {
int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode));
unsigned char* endNodeCodeCopy = new unsigned char[bytes];
memcpy(endNodeCodeCopy, endNodeCode, bytes);
_jurisdictionEndNodes.push_back(endNodeCodeCopy);
}
}
}
VoxelSceneStats::~VoxelSceneStats() {
reset();
}
@ -48,6 +139,15 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r
delete[] _jurisdictionRoot;
_jurisdictionRoot = NULL;
}
// clear existing endNodes before copying new ones...
for (int i=0; i < _jurisdictionEndNodes.size(); i++) {
if (_jurisdictionEndNodes[i]) {
delete[] _jurisdictionEndNodes[i];
}
}
_jurisdictionEndNodes.clear();
// setup jurisdictions
if (jurisdictionMap) {
unsigned char* jurisdictionRoot = jurisdictionMap->getRootOctalCode();
if (jurisdictionRoot) {
@ -56,6 +156,7 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r
memcpy(_jurisdictionRoot, jurisdictionRoot, bytes);
}
// copy new endNodes...
for (int i=0; i < jurisdictionMap->getEndNodeCount(); i++) {
unsigned char* endNodeCode = jurisdictionMap->getEndNodeOctalCode(i);
if (endNodeCode) {
@ -436,6 +537,20 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
memcpy(&_treesRemoved, sourceBuffer, sizeof(_treesRemoved));
sourceBuffer += sizeof(_treesRemoved);
// before allocating new juridiction, clean up existing ones
if (_jurisdictionRoot) {
delete[] _jurisdictionRoot;
_jurisdictionRoot = NULL;
}
// clear existing endNodes before copying new ones...
for (int i=0; i < _jurisdictionEndNodes.size(); i++) {
if (_jurisdictionEndNodes[i]) {
delete[] _jurisdictionEndNodes[i];
}
}
_jurisdictionEndNodes.clear();
// read the root jurisdiction
int bytes = 0;
memcpy(&bytes, sourceBuffer, sizeof(bytes));

View file

@ -27,6 +27,9 @@ public:
~VoxelSceneStats();
void reset();
VoxelSceneStats(const VoxelSceneStats& other); // copy constructor
VoxelSceneStats& operator= (const VoxelSceneStats& other); // copy assignment
/// Call when beginning the computation of a scene. Initializes internal structures
void sceneStarted(bool fullScene, bool moving, VoxelNode* root, JurisdictionMap* jurisdictionMap);
@ -144,6 +147,9 @@ public:
unsigned long getTotalLeaves() const { return _totalLeaves; }
private:
void copyFromOther(const VoxelSceneStats& other);
bool _isReadyToSend;
unsigned char _statsMessage[MAX_PACKET_SIZE];
int _statsMessageLength;

View file

@ -272,7 +272,30 @@ int main(int argc, const char * argv[])
} else {
qDebug() << "Unexpected number of parameters for getOctCode\n";
}
return 0;
}
const char* DECODE_OCTCODE = "--decodeOctCode";
const char* decodeParam = getCmdOption(argc, argv, DECODE_OCTCODE);
if (decodeParam) {
QString decodeParamsString(decodeParam);
unsigned char* octalCodeToDecode = hexStringToOctalCode(decodeParamsString);
VoxelPositionSize details;
voxelDetailsForCode(octalCodeToDecode, details);
delete[] octalCodeToDecode;
qDebug() << "octal code to decode: " << decodeParamsString << "\n";
qDebug() << "Details for Octal Code:\n";
qDebug() << " x:" << details.x << "[" << details.x * TREE_SCALE << "]" << "\n";
qDebug() << " y:" << details.y << "[" << details.y * TREE_SCALE << "]" << "\n";
qDebug() << " z:" << details.z << "[" << details.z * TREE_SCALE << "]" << "\n";
qDebug() << " s:" << details.s << "[" << details.s * TREE_SCALE << "]" << "\n";
return 0;
}
// Handles taking and SVO and splitting it into multiple SVOs based on
// jurisdiction details