mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into controllers
This commit is contained in:
commit
2078c753ae
38 changed files with 949 additions and 252 deletions
|
@ -18,12 +18,19 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE)
|
|||
@ONLY
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins")
|
||||
else()
|
||||
set(PLUGIN_PATH "plugins")
|
||||
endif()
|
||||
|
||||
# add a post-build command to copy DLLs beside the executable
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DBUNDLE_EXECUTABLE=$<TARGET_FILE:${TARGET_NAME}>
|
||||
-DBUNDLE_PLUGIN_DIR=$<TARGET_FILE_DIR:${TARGET_NAME}>/${PLUGIN_PATH}
|
||||
-P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake
|
||||
)
|
||||
|
||||
|
|
|
@ -41,4 +41,16 @@ function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
|||
endfunction()
|
||||
|
||||
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@")
|
||||
|
||||
message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}")
|
||||
|
||||
if (APPLE)
|
||||
set(PLUGIN_EXTENSION "dylib")
|
||||
elseif (WIN32)
|
||||
set(PLUGIN_EXTENSION "dll")
|
||||
else()
|
||||
set(PLUGIN_EXTENSION "so")
|
||||
endif()
|
||||
|
||||
file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
|
||||
fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@")
|
188
examples/avatarMover/avatarMover.js
Normal file
188
examples/avatarMover/avatarMover.js
Normal file
|
@ -0,0 +1,188 @@
|
|||
(function() {
|
||||
this.defaultRange = 5;
|
||||
this.acceleration = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
this.onColor = {
|
||||
red: 77,
|
||||
green: 11,
|
||||
blue: 111
|
||||
};
|
||||
this.offColor = {
|
||||
red: 200,
|
||||
green: 0,
|
||||
blue: 0
|
||||
};
|
||||
var self = this;
|
||||
//Default forward direction of mover object
|
||||
this.forward = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: -1
|
||||
};
|
||||
this.isMoving = false;
|
||||
this.velocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
this.defaultThrust = 500;
|
||||
this.maxRotMixVal = 0.01;
|
||||
this.minRotMixVal = this.maxRotMixVal * 0.5;
|
||||
this.minThrustPercentage = 0.2;
|
||||
this.userData = {};
|
||||
|
||||
|
||||
this.getUserData = function() {
|
||||
if (this.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateUserData = function() {
|
||||
Entities.editEntity(this.entityId, {
|
||||
userData: JSON.stringify(this.userData)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.toggleMover = function() {
|
||||
if (!this.userData.active) {
|
||||
this.activate();
|
||||
} else if (this.userData.active) {
|
||||
this.deactivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.clickReleaseOnEntity = function(entityId, mouseEvent) {
|
||||
this.entityId = entityId
|
||||
if (mouseEvent.isLeftButton) {
|
||||
this.toggleMover();
|
||||
}
|
||||
}
|
||||
|
||||
this.activate = function() {
|
||||
//activate a light at the movers position
|
||||
this.properties = Entities.getEntityProperties(this.entityId);
|
||||
this.getUserData();
|
||||
this.userData.active = true;
|
||||
this.initUserData();
|
||||
var lightPos = this.properties.position;
|
||||
lightPos.y += .1;
|
||||
this.light = Entities.addEntity({
|
||||
type: "Light",
|
||||
position: lightPos,
|
||||
isSpotlight: false,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 2
|
||||
},
|
||||
color: this.onColor,
|
||||
intensity: 10
|
||||
// rotation: {x : 0, y: Math.PI/2, z: 0}
|
||||
});
|
||||
|
||||
this.field = Overlays.addOverlay("sphere", {
|
||||
position: this.properties.position,
|
||||
size: this.userData.range,
|
||||
solid: false,
|
||||
color: {
|
||||
red: 250,
|
||||
green: 10,
|
||||
blue: 10
|
||||
},
|
||||
})
|
||||
|
||||
//change color
|
||||
Entities.editEntity(this.entityId, {
|
||||
color: this.onColor,
|
||||
});
|
||||
}
|
||||
|
||||
this.initUserData = function() {
|
||||
this.userData.range = this.userData.range || this.defaultRange;
|
||||
this.userData.thrust = this.userData.thrust || this.defaultThrust;
|
||||
this.updateUserData();
|
||||
}
|
||||
|
||||
this.updateOverlays = function() {
|
||||
if (this.field) {
|
||||
Overlays.editOverlay(this.field, {
|
||||
size: this.userData.range
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.deactivate = function() {
|
||||
this.userData.active = false;
|
||||
this.updateUserData();
|
||||
Entities.editEntity(this.entityId, {
|
||||
color: this.offColor
|
||||
});
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
this.scriptEnding = function() {
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
self.getUserData();
|
||||
self.updateOverlays();
|
||||
if (!self.userData.active) {
|
||||
return;
|
||||
}
|
||||
self.distance = Vec3.distance(MyAvatar.position, self.properties.position);
|
||||
if (self.distance < self.userData.range) {
|
||||
self.rotationMixVal = map(self.distance, 0, self.userData.range, self.maxRotMixVal, self.minRotMixVal);
|
||||
|
||||
//We want to extract yaw from rotated object so avatars do not pith or roll, as they will be stuck that way.
|
||||
self.sanitizedRotation = Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(self.properties.rotation).y, 0);
|
||||
self.newOrientation = Quat.mix(MyAvatar.orientation, self.sanitizedRotation, self.rotationMixVal);
|
||||
MyAvatar.orientation = self.newOrientation;
|
||||
|
||||
self.rotatedDir = {
|
||||
x: self.forward.x,
|
||||
y: self.forward.y,
|
||||
z: self.forward.z
|
||||
};
|
||||
self.rotatedDir = Vec3.multiplyQbyV(self.properties.rotation, self.rotatedDir);
|
||||
|
||||
self.thrust = map(self.distance, 0, self.userData.range, self.userData.thrust, self.userData.thrust * self.minThrustPercentage);
|
||||
self.direction = Vec3.normalize(self.rotatedDir);
|
||||
self.velocity = Vec3.multiply(self.direction, self.thrust);
|
||||
MyAvatar.addThrust(Vec3.multiply(self.velocity, deltaTime));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
Script.update.disconnect(this.update);
|
||||
this.cleanUp();
|
||||
}
|
||||
|
||||
|
||||
this.cleanUp = function() {
|
||||
Entities.deleteEntity(this.light);
|
||||
Overlays.deleteOverlay(this.field);
|
||||
}
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(this.scriptEnding);
|
||||
Script.update.connect(this.update);
|
||||
|
||||
});
|
18
examples/avatarMover/avatarMoverSpawner.js
Normal file
18
examples/avatarMover/avatarMoverSpawner.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx";
|
||||
var scriptURL = Script.resolvePath('avatarMover.js');
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var avatarMover = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
userData: JSON.stringify({range: 5}),
|
||||
script: scriptURL
|
||||
});
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(avatarMover);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
10
examples/controllers/rightClickExample.js
Normal file
10
examples/controllers/rightClickExample.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
var MAPPING_NAME = "com.highfidelity.rightClickExample";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) {
|
||||
print("Keyboard.RightMouseClicked");
|
||||
});
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
});
|
|
@ -44,22 +44,11 @@
|
|||
|
||||
#include "Menu.h"
|
||||
|
||||
Menu* Menu::_instance = NULL;
|
||||
static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu";
|
||||
|
||||
Menu* Menu::getInstance() {
|
||||
static QMutex menuInstanceMutex;
|
||||
|
||||
// lock the menu instance mutex to make sure we don't race and create two menus and crash
|
||||
menuInstanceMutex.lock();
|
||||
|
||||
if (!_instance) {
|
||||
qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu.");
|
||||
_instance = new Menu();
|
||||
}
|
||||
|
||||
menuInstanceMutex.unlock();
|
||||
|
||||
return _instance;
|
||||
static Menu* instance = globalInstance<Menu>(MENU_PROPERTY_NAME);
|
||||
return instance;
|
||||
}
|
||||
|
||||
Menu::Menu() {
|
||||
|
|
|
@ -57,6 +57,7 @@ private:
|
|||
class Menu : public QMenuBar {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Menu();
|
||||
static Menu* getInstance();
|
||||
|
||||
void loadSettings();
|
||||
|
@ -103,9 +104,6 @@ public slots:
|
|||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
Menu();
|
||||
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
static void saveAction(Settings& settings, QAction& action);
|
||||
|
|
|
@ -473,18 +473,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
|
|||
continue;
|
||||
}
|
||||
|
||||
int controllerButtons = 0;
|
||||
|
||||
//Check for if we should toggle or drag the magnification window
|
||||
if (controllerButtons & BUTTON_3) {
|
||||
if (isPressed[index] == false) {
|
||||
//We are now dragging the window
|
||||
isPressed[index] = true;
|
||||
//set the pressed time in us
|
||||
pressedTime[index] = usecTimestampNow();
|
||||
stateWhenPressed[index] = _magActive[index];
|
||||
}
|
||||
} else if (isPressed[index]) {
|
||||
if (isPressed[index]) {
|
||||
isPressed[index] = false;
|
||||
//If the button was only pressed for < 250 ms
|
||||
//then disable it.
|
||||
|
|
|
@ -300,6 +300,8 @@ void Rig::setJointAnimatinoPriority(int index, float newPriority) {
|
|||
}
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation.
|
||||
void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
|
@ -350,6 +352,8 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const
|
|||
return true;
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation.
|
||||
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
|
|
|
@ -240,7 +240,8 @@ public:
|
|||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||
Q_INVOKABLE char getHandState() const { return _handState; }
|
||||
|
||||
const QVector<JointData>& getJointData() const { return _jointData; }
|
||||
const QVector<JointData>& getRawJointData() const { return _jointData; }
|
||||
void setRawJointData(QVector<JointData> data) { _jointData = data; }
|
||||
|
||||
Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation);
|
||||
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// InputPluginsLogging.cpp
|
||||
// libraries/input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Clement on 11/6/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "InputPluginsLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// InputPluginsLogging.h
|
||||
// libraries/input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Clement on 11/6/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_InputPluginsLogging_h
|
||||
#define hifi_InputPluginsLogging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||
|
||||
#endif // hifi_InputPluginsLogging_h
|
|
@ -9,55 +9,37 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
#include "SixenseManager.h"
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include <sixense.h>
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtCore/QSysInfo>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#include "SixenseManager.h"
|
||||
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include "sixense.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
static QLibrary* _sixenseLibrary { nullptr };
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: This should not be here
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include "InputPluginsLogging.h"
|
||||
|
||||
static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
||||
static const unsigned int BUTTON_1 = 1U << 5;
|
||||
static const unsigned int BUTTON_2 = 1U << 6;
|
||||
static const unsigned int BUTTON_3 = 1U << 3;
|
||||
static const unsigned int BUTTON_4 = 1U << 4;
|
||||
static const unsigned int BUTTON_FWD = 1U << 7;
|
||||
static const unsigned int BUTTON_TRIGGER = 1U << 8;
|
||||
|
||||
const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame
|
||||
const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f };
|
||||
const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f };
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef int (*SixenseBaseFunction)();
|
||||
typedef int (*SixenseTakeIntFunction)(int);
|
||||
#ifdef HAVE_SIXENSE
|
||||
typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const QString SixenseManager::NAME = "Sixense";
|
||||
const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra";
|
||||
|
@ -66,7 +48,6 @@ const QString MENU_PARENT = "Avatar";
|
|||
const QString MENU_NAME = "Sixense";
|
||||
const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
|
||||
const QString TOGGLE_SMOOTH = "Smooth Sixense Movement";
|
||||
const float DEFAULT_REACH_LENGTH = 1.5f;
|
||||
|
||||
bool SixenseManager::isSupported() const {
|
||||
#ifdef HAVE_SIXENSE
|
||||
|
@ -84,41 +65,16 @@ bool SixenseManager::isSupported() const {
|
|||
|
||||
void SixenseManager::activate() {
|
||||
InputPlugin::activate();
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
|
||||
_container->addMenu(MENU_PATH);
|
||||
_container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH,
|
||||
[this] (bool clicked) { this->setSixenseFilter(clicked); },
|
||||
[this] (bool clicked) { setSixenseFilter(clicked); },
|
||||
true, true);
|
||||
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
userInputMapper->registerDevice(_inputDevice);
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
if (!_sixenseLibrary) {
|
||||
|
||||
#ifdef SIXENSE_LIB_FILENAME
|
||||
_sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME);
|
||||
#else
|
||||
const QString SIXENSE_LIBRARY_NAME = "libsixense_x64";
|
||||
QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/"
|
||||
+ SIXENSE_LIBRARY_NAME;
|
||||
|
||||
_sixenseLibrary = new QLibrary(frameworkSixenseLibrary);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_sixenseLibrary->load()){
|
||||
qCDebug(inputplugins) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName();
|
||||
} else {
|
||||
qCDebug(inputplugins) << "Sixense library at" << _sixenseLibrary->fileName() << "failed to load."
|
||||
<< "Continuing without hydra support.";
|
||||
return;
|
||||
}
|
||||
|
||||
SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit");
|
||||
#endif
|
||||
loadSettings();
|
||||
sixenseInit();
|
||||
#endif
|
||||
|
@ -139,26 +95,14 @@ void SixenseManager::deactivate() {
|
|||
userInputMapper->removeDevice(_inputDevice->_deviceID);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit");
|
||||
#endif
|
||||
|
||||
sixenseExit();
|
||||
|
||||
#ifdef __APPLE__
|
||||
delete _sixenseLibrary;
|
||||
#endif
|
||||
|
||||
saveSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SixenseManager::setSixenseFilter(bool filter) {
|
||||
#ifdef HAVE_SIXENSE
|
||||
#ifdef __APPLE__
|
||||
SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled");
|
||||
#endif
|
||||
int newFilter = filter ? 1 : 0;
|
||||
sixenseSetFilterEnabled(newFilter);
|
||||
sixenseSetFilterEnabled(filter ? 1 : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -180,13 +124,6 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
|
|||
#ifdef HAVE_SIXENSE
|
||||
_buttonPressedMap.clear();
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseGetNumActiveControllers =
|
||||
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
|
||||
#endif
|
||||
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
|
||||
static const float MAX_DISCONNECTED_TIME = 2.0f;
|
||||
static bool disconnected { false };
|
||||
static float disconnectedInterval { 0.0f };
|
||||
|
@ -213,24 +150,11 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
|
|||
// FIXME send this message once when we've positively identified hydra hardware
|
||||
//UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseBaseFunction sixenseGetMaxControllers =
|
||||
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
|
||||
#endif
|
||||
|
||||
int maxControllers = sixenseGetMaxControllers();
|
||||
|
||||
// we only support two controllers
|
||||
sixenseControllerData controllers[2];
|
||||
|
||||
#ifdef __APPLE__
|
||||
SixenseTakeIntFunction sixenseIsControllerEnabled =
|
||||
(SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled");
|
||||
|
||||
SixenseTakeIntAndSixenseControllerData sixenseGetNewestData =
|
||||
(SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData");
|
||||
#endif
|
||||
|
||||
int numActiveControllers = 0;
|
||||
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
|
||||
if (!sixenseIsControllerEnabled(i)) {
|
||||
|
@ -293,9 +217,9 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
|
|||
// (4) assume that the orb is on a flat surface (yAxis is UP)
|
||||
// (5) compute the forward direction (zAxis = xAxis cross yAxis)
|
||||
|
||||
const float MINIMUM_ARM_REACH = 0.3f; // meters
|
||||
const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters
|
||||
const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired
|
||||
static const float MINIMUM_ARM_REACH = 0.3f; // meters
|
||||
static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters
|
||||
static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired
|
||||
|
||||
void SixenseManager::InputDevice::updateCalibration(void* controllersX) {
|
||||
auto controllers = reinterpret_cast<sixenseControllerData*>(controllersX);
|
||||
|
@ -315,14 +239,12 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) {
|
|||
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
|
||||
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y));
|
||||
xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis));
|
||||
_reachLength = glm::dot(xAxis, _reachRight - _reachLeft);
|
||||
_avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis)));
|
||||
const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f;
|
||||
_avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR;
|
||||
qCDebug(inputplugins, "succeess: sixense calibration");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_calibrationState = CALIBRATION_STATE_IDLE;
|
||||
qCDebug(inputplugins, "failed: sixense calibration");
|
||||
|
@ -479,8 +401,6 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
|
|||
glm::vec3 velocity(0.0f);
|
||||
glm::quat angularVelocity;
|
||||
|
||||
|
||||
|
||||
if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
|
||||
|
||||
velocity = (position - prevPose.getTranslation()) / deltaTime;
|
||||
|
@ -518,8 +438,6 @@ static const auto R2 = controller::A;
|
|||
static const auto R3 = controller::B;
|
||||
static const auto R4 = controller::Y;
|
||||
|
||||
using namespace controller;
|
||||
|
||||
controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const {
|
||||
using namespace controller;
|
||||
static const Input::NamedVector availableInputs {
|
||||
|
@ -563,7 +481,6 @@ void SixenseManager::saveSettings() const {
|
|||
{
|
||||
settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition);
|
||||
settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation);
|
||||
settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength));
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
@ -575,7 +492,6 @@ void SixenseManager::loadSettings() {
|
|||
{
|
||||
settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition);
|
||||
settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation);
|
||||
settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength);
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
|
|
@ -12,18 +12,6 @@
|
|||
#ifndef hifi_SixenseManager_h
|
||||
#define hifi_SixenseManager_h
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include "sixense.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <QCoreApplication>
|
||||
#include <qlibrary.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
#include <controllers/InputDevice.h>
|
||||
|
@ -31,23 +19,10 @@
|
|||
|
||||
#include "InputPlugin.h"
|
||||
|
||||
class QLibrary;
|
||||
|
||||
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
||||
const unsigned int BUTTON_1 = 1U << 5;
|
||||
const unsigned int BUTTON_2 = 1U << 6;
|
||||
const unsigned int BUTTON_3 = 1U << 3;
|
||||
const unsigned int BUTTON_4 = 1U << 4;
|
||||
const unsigned int BUTTON_FWD = 1U << 7;
|
||||
const unsigned int BUTTON_TRIGGER = 1U << 8;
|
||||
|
||||
const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
|
||||
|
||||
// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
|
||||
class SixenseManager : public InputPlugin {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual bool isJointController() const override { return true; }
|
||||
|
@ -66,18 +41,18 @@ public:
|
|||
public slots:
|
||||
void setSixenseFilter(bool filter);
|
||||
|
||||
private:
|
||||
private:
|
||||
static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s
|
||||
static const int CALIBRATION_STATE_IDLE = 0;
|
||||
static const int CALIBRATION_STATE_IN_PROGRESS = 1;
|
||||
static const int CALIBRATION_STATE_COMPLETE = 2;
|
||||
static const glm::vec3 DEFAULT_AVATAR_POSITION;
|
||||
static const float CONTROLLER_THRESHOLD;
|
||||
static const float DEFAULT_REACH_LENGTH;
|
||||
|
||||
|
||||
using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >;
|
||||
using MovingAverageMap = std::map< int, Samples >;
|
||||
|
||||
template<typename T>
|
||||
using SampleAverage = MovingAverage<T, MAX_NUM_AVERAGING_SAMPLES>;
|
||||
using Samples = std::pair<SampleAverage<glm::vec3>, SampleAverage<glm::vec4>>;
|
||||
using MovingAverageMap = std::map<int, Samples>;
|
||||
|
||||
class InputDevice : public controller::InputDevice {
|
||||
public:
|
||||
|
@ -103,7 +78,6 @@ private:
|
|||
glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame
|
||||
glm::quat _avatarRotation; // in hydra-frame
|
||||
|
||||
float _reachLength { DEFAULT_REACH_LENGTH };
|
||||
float _lastDistance;
|
||||
// these are measured values used to compute the calibration results
|
||||
quint64 _lockExpiry;
|
||||
|
@ -113,9 +87,6 @@ private:
|
|||
glm::vec3 _reachRight;
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool _useSixenseFilter = true;
|
||||
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
|
||||
|
||||
static const QString NAME;
|
||||
|
|
153
libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp
Normal file
153
libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// SixenseSupportOSX.cpp
|
||||
// libraries/input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Clement on 10/20/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Mock implementation of sixense.h to hide dynamic linking on OS X
|
||||
#if defined(__APPLE__) && defined(HAVE_SIXENSE)
|
||||
#include <type_traits>
|
||||
|
||||
#include <sixense.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QLibrary>
|
||||
|
||||
#include "InputPluginsLogging.h"
|
||||
|
||||
#ifndef SIXENSE_LIB_FILENAME
|
||||
#define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"
|
||||
#endif
|
||||
|
||||
using Library = std::unique_ptr<QLibrary>;
|
||||
static Library SIXENSE;
|
||||
|
||||
struct Callable {
|
||||
template<typename... Args>
|
||||
int operator() (Args&&... args){
|
||||
return reinterpret_cast<int(*)(Args...)>(function)(std::forward<Args>(args)...);
|
||||
}
|
||||
QFunctionPointer function;
|
||||
};
|
||||
|
||||
Callable resolve(const Library& library, const char* name) {
|
||||
Q_ASSERT_X(library && library->isLoaded(), __FUNCTION__, "Sixense library not loaded");
|
||||
auto function = library->resolve(name);
|
||||
Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str());
|
||||
return Callable { function };
|
||||
}
|
||||
#define FORWARD resolve(SIXENSE, __FUNCTION__)
|
||||
|
||||
|
||||
void loadSixense() {
|
||||
Q_ASSERT_X(!(SIXENSE && SIXENSE->isLoaded()), __FUNCTION__, "Sixense library already loaded");
|
||||
SIXENSE.reset(new QLibrary(SIXENSE_LIB_FILENAME));
|
||||
Q_CHECK_PTR(SIXENSE);
|
||||
|
||||
if (SIXENSE->load()){
|
||||
qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName();
|
||||
} else {
|
||||
qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString();
|
||||
qDebug() << "Continuing without hydra support.";
|
||||
}
|
||||
}
|
||||
void unloadSixense() {
|
||||
SIXENSE->unload();
|
||||
}
|
||||
|
||||
|
||||
// sixense.h wrapper for OSX dynamic linking
|
||||
int sixenseInit() {
|
||||
loadSixense();
|
||||
return FORWARD();
|
||||
}
|
||||
int sixenseExit() {
|
||||
auto returnCode = FORWARD();
|
||||
unloadSixense();
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
int sixenseGetMaxBases() {
|
||||
return FORWARD();
|
||||
}
|
||||
int sixenseSetActiveBase(int i) {
|
||||
return FORWARD(i);
|
||||
}
|
||||
int sixenseIsBaseConnected(int i) {
|
||||
return FORWARD(i);
|
||||
}
|
||||
|
||||
int sixenseGetMaxControllers() {
|
||||
return FORWARD();
|
||||
}
|
||||
int sixenseIsControllerEnabled(int which) {
|
||||
return FORWARD(which);
|
||||
}
|
||||
int sixenseGetNumActiveControllers() {
|
||||
return FORWARD();
|
||||
}
|
||||
|
||||
int sixenseGetHistorySize() {
|
||||
return FORWARD();
|
||||
}
|
||||
|
||||
int sixenseGetData(int which, int index_back, sixenseControllerData* data) {
|
||||
return FORWARD(which, index_back, data);
|
||||
}
|
||||
int sixenseGetAllData(int index_back, sixenseAllControllerData* data) {
|
||||
return FORWARD(index_back, data);
|
||||
}
|
||||
int sixenseGetNewestData(int which, sixenseControllerData* data) {
|
||||
return FORWARD(which, data);
|
||||
}
|
||||
int sixenseGetAllNewestData(sixenseAllControllerData* data) {
|
||||
return FORWARD(data);
|
||||
}
|
||||
|
||||
int sixenseSetHemisphereTrackingMode(int which_controller, int state) {
|
||||
return FORWARD(which_controller, state);
|
||||
}
|
||||
int sixenseGetHemisphereTrackingMode(int which_controller, int* state) {
|
||||
return FORWARD(which_controller, state);
|
||||
}
|
||||
int sixenseAutoEnableHemisphereTracking(int which_controller) {
|
||||
return FORWARD(which_controller);
|
||||
}
|
||||
|
||||
int sixenseSetHighPriorityBindingEnabled(int on_or_off) {
|
||||
return FORWARD(on_or_off);
|
||||
}
|
||||
int sixenseGetHighPriorityBindingEnabled(int* on_or_off) {
|
||||
return FORWARD(on_or_off);
|
||||
}
|
||||
|
||||
int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) {
|
||||
return FORWARD(controller_id, duration_100ms, pattern_id);
|
||||
}
|
||||
|
||||
int sixenseSetFilterEnabled(int on_or_off) {
|
||||
return FORWARD(on_or_off);
|
||||
}
|
||||
int sixenseGetFilterEnabled(int* on_or_off) {
|
||||
return FORWARD(on_or_off);
|
||||
}
|
||||
|
||||
int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) {
|
||||
return FORWARD(near_range, near_val, far_range, far_val);
|
||||
}
|
||||
int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) {
|
||||
return FORWARD(near_range, near_val, far_range, far_val);
|
||||
}
|
||||
|
||||
int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) {
|
||||
return FORWARD(red, green, blue);
|
||||
}
|
||||
int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) {
|
||||
return FORWARD(red, green, blue);
|
||||
}
|
||||
#endif
|
|
@ -16,33 +16,34 @@
|
|||
using namespace recording;
|
||||
|
||||
Clip::Pointer Clip::fromFile(const QString& filePath) {
|
||||
return std::make_shared<FileClip>(filePath);
|
||||
auto result = std::make_shared<FileClip>(filePath);
|
||||
if (result->frameCount() == 0) {
|
||||
return Clip::Pointer();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Clip::toFile(Clip::Pointer clip, const QString& filePath) {
|
||||
// FIXME
|
||||
void Clip::toFile(const QString& filePath, Clip::Pointer clip) {
|
||||
FileClip::write(filePath, clip->duplicate());
|
||||
}
|
||||
|
||||
Clip::Pointer Clip::newClip() {
|
||||
return std::make_shared<BufferClip>();
|
||||
}
|
||||
|
||||
Clip::Pointer Clip::duplicate() {
|
||||
Clip::Pointer result = std::make_shared<BufferClip>();
|
||||
|
||||
Locker lock(_mutex);
|
||||
float currentPosition = position();
|
||||
seek(0);
|
||||
|
||||
Frame::Pointer frame = nextFrame();
|
||||
while (frame) {
|
||||
result->appendFrame(frame);
|
||||
result->addFrame(frame);
|
||||
frame = nextFrame();
|
||||
}
|
||||
|
||||
seek(currentPosition);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
Clip::Pointer Clip::fromIODevice(QIODevice * device) {
|
||||
return std::make_shared<IOClip>(device);
|
||||
}
|
||||
|
||||
void Clip::fromIODevice(Clip::Pointer clip, QIODevice * device) {
|
||||
}
|
||||
#endif
|
|
@ -12,35 +12,44 @@
|
|||
|
||||
#include "Forward.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class QIODevice;
|
||||
|
||||
namespace recording {
|
||||
|
||||
class Clip : public QObject {
|
||||
class Clip {
|
||||
public:
|
||||
using Pointer = std::shared_ptr<Clip>;
|
||||
|
||||
Clip(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~Clip() {}
|
||||
|
||||
Pointer duplicate();
|
||||
|
||||
virtual float duration() const = 0;
|
||||
virtual size_t frameCount() const = 0;
|
||||
|
||||
virtual void seek(float offset) = 0;
|
||||
virtual float position() const = 0;
|
||||
|
||||
virtual FramePointer peekFrame() const = 0;
|
||||
virtual FramePointer nextFrame() = 0;
|
||||
virtual void skipFrame() = 0;
|
||||
virtual void appendFrame(FramePointer) = 0;
|
||||
|
||||
virtual void addFrame(FramePointer) = 0;
|
||||
|
||||
static Pointer fromFile(const QString& filePath);
|
||||
static void toFile(Pointer clip, const QString& filePath);
|
||||
static void toFile(const QString& filePath, Pointer clip);
|
||||
static Pointer newClip();
|
||||
|
||||
protected:
|
||||
using Mutex = std::recursive_mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
|
||||
virtual void reset() = 0;
|
||||
|
||||
mutable Mutex _mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
float timeOffset { 0 };
|
||||
QByteArray data;
|
||||
|
||||
Frame() {}
|
||||
Frame(FrameType type, float timeOffset, const QByteArray& data)
|
||||
: type(type), timeOffset(timeOffset), data(data) {}
|
||||
|
||||
static FrameType registerFrameType(const QString& frameTypeName);
|
||||
static QMap<QString, FrameType> getFrameTypes();
|
||||
static QMap<FrameType, QString> getFrameTypeNames();
|
||||
|
|
11
libraries/recording/src/recording/Logging.cpp
Normal file
11
libraries/recording/src/recording/Logging.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(recordingLog, "hifi.recording")
|
16
libraries/recording/src/recording/Logging.h
Normal file
16
libraries/recording/src/recording/Logging.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis 2015/10/11
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Controllers_Logging_h
|
||||
#define hifi_Controllers_Logging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(recordingLog)
|
||||
|
||||
#endif
|
|
@ -51,7 +51,7 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) {
|
|||
frame->type = type;
|
||||
frame->data = frameData;
|
||||
frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND;
|
||||
_clip->appendFrame(frame);
|
||||
_clip->addFrame(frame);
|
||||
}
|
||||
|
||||
ClipPointer Recorder::getClip() {
|
||||
|
|
|
@ -51,11 +51,15 @@ FramePointer BufferClip::nextFrame() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void BufferClip::appendFrame(FramePointer newFrame) {
|
||||
void BufferClip::addFrame(FramePointer newFrame) {
|
||||
if (newFrame->timeOffset < 0.0f) {
|
||||
throw std::runtime_error("Frames may not have negative time offsets");
|
||||
}
|
||||
auto currentPosition = position();
|
||||
seek(newFrame->timeOffset);
|
||||
{
|
||||
Locker lock(_mutex);
|
||||
|
||||
_frames.insert(_frames.begin() + _frameIndex, newFrame);
|
||||
}
|
||||
seek(currentPosition);
|
||||
|
@ -72,3 +76,15 @@ void BufferClip::reset() {
|
|||
Locker lock(_mutex);
|
||||
_frameIndex = 0;
|
||||
}
|
||||
|
||||
float BufferClip::duration() const {
|
||||
if (_frames.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return (*_frames.rbegin())->timeOffset;
|
||||
}
|
||||
|
||||
size_t BufferClip::frameCount() const {
|
||||
return _frames.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,25 +20,23 @@ class BufferClip : public Clip {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<BufferClip>;
|
||||
|
||||
BufferClip(QObject* parent = nullptr) : Clip(parent) {}
|
||||
virtual ~BufferClip() {}
|
||||
|
||||
virtual float duration() const override;
|
||||
virtual size_t frameCount() const override;
|
||||
|
||||
virtual void seek(float offset) override;
|
||||
virtual float position() const override;
|
||||
|
||||
virtual FramePointer peekFrame() const override;
|
||||
virtual FramePointer nextFrame() override;
|
||||
virtual void skipFrame() override;
|
||||
virtual void appendFrame(FramePointer) override;
|
||||
virtual void addFrame(FramePointer) override;
|
||||
|
||||
private:
|
||||
using Mutex = std::mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
std::vector<FramePointer> _frames;
|
||||
mutable Mutex _mutex;
|
||||
mutable size_t _frameIndex { 0 };
|
||||
};
|
||||
|
||||
|
|
|
@ -8,42 +8,197 @@
|
|||
|
||||
#include "FileClip.h"
|
||||
|
||||
#include "../Frame.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include <Finally.h>
|
||||
|
||||
#include "../Frame.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
|
||||
using namespace recording;
|
||||
|
||||
static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1;
|
||||
static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t);
|
||||
|
||||
FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) {
|
||||
auto size = _file.size();
|
||||
_map = _file.map(0, size, QFile::MapPrivateOption);
|
||||
static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes");
|
||||
|
||||
auto current = _map;
|
||||
using FrameHeaderList = std::list<FileClip::FrameHeader>;
|
||||
using FrameTranslationMap = QMap<FrameType, FrameType>;
|
||||
|
||||
FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) {
|
||||
FrameTranslationMap results;
|
||||
auto headerObj = doc.object();
|
||||
if (headerObj.contains(FRAME_TYPE_MAP)) {
|
||||
auto frameTypeObj = headerObj[FRAME_TYPE_MAP].toObject();
|
||||
auto currentFrameTypes = Frame::getFrameTypes();
|
||||
for (auto frameTypeName : frameTypeObj.keys()) {
|
||||
qDebug() << frameTypeName;
|
||||
if (!currentFrameTypes.contains(frameTypeName)) {
|
||||
continue;
|
||||
}
|
||||
FrameType currentTypeEnum = currentFrameTypes[frameTypeName];
|
||||
FrameType storedTypeEnum = static_cast<FrameType>(frameTypeObj[frameTypeName].toInt());
|
||||
results[storedTypeEnum] = currentTypeEnum;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) {
|
||||
using FrameHeader = FileClip::FrameHeader;
|
||||
FrameHeaderList results;
|
||||
auto current = start;
|
||||
auto end = current + size;
|
||||
// Read all the frame headers
|
||||
while (end - current < MINIMUM_FRAME_SIZE) {
|
||||
// FIXME move to Frame::readHeader?
|
||||
while (end - current >= MINIMUM_FRAME_SIZE) {
|
||||
FrameHeader header;
|
||||
memcpy(&(header.type), current, sizeof(FrameType));
|
||||
current += sizeof(FrameType);
|
||||
memcpy(&(header.timeOffset), current, sizeof(FrameType));
|
||||
memcpy(&(header.timeOffset), current, sizeof(float));
|
||||
current += sizeof(float);
|
||||
memcpy(&(header.size), current, sizeof(uint16_t));
|
||||
current += sizeof(uint16_t);
|
||||
header.fileOffset = current - _map;
|
||||
header.fileOffset = current - start;
|
||||
if (end - current < header.size) {
|
||||
current = end;
|
||||
break;
|
||||
}
|
||||
|
||||
_frameHeaders.push_back(header);
|
||||
current += header.size;
|
||||
results.push_back(header);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
FileClip::FileClip(const QString& fileName) : _file(fileName) {
|
||||
auto size = _file.size();
|
||||
bool opened = _file.open(QIODevice::ReadOnly);
|
||||
if (!opened) {
|
||||
qCWarning(recordingLog) << "Unable to open file " << fileName;
|
||||
return;
|
||||
}
|
||||
_map = _file.map(0, size, QFile::MapPrivateOption);
|
||||
if (!_map) {
|
||||
qCWarning(recordingLog) << "Unable to map file " << fileName;
|
||||
return;
|
||||
}
|
||||
|
||||
FrameHeaderList parsedFrameHeaders = parseFrameHeaders(_map, size);
|
||||
|
||||
// Verify that at least one frame exists and that the first frame is a header
|
||||
if (0 == parsedFrameHeaders.size()) {
|
||||
qWarning() << "No frames found, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the file header
|
||||
{
|
||||
auto fileHeaderFrameHeader = *parsedFrameHeaders.begin();
|
||||
parsedFrameHeaders.pop_front();
|
||||
if (fileHeaderFrameHeader.type != Frame::TYPE_HEADER) {
|
||||
qWarning() << "Missing header frame, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray fileHeaderData((char*)_map + fileHeaderFrameHeader.fileOffset, fileHeaderFrameHeader.size);
|
||||
_fileHeader = QJsonDocument::fromBinaryData(fileHeaderData);
|
||||
}
|
||||
|
||||
// Find the type enum translation map and fix up the frame headers
|
||||
{
|
||||
FrameTranslationMap translationMap = parseTranslationMap(_fileHeader);
|
||||
if (translationMap.empty()) {
|
||||
qWarning() << "Header missing frame type map, invalid file";
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the loaded headers with the frame data
|
||||
_frameHeaders.reserve(parsedFrameHeaders.size());
|
||||
for (auto& frameHeader : parsedFrameHeaders) {
|
||||
if (!translationMap.contains(frameHeader.type)) {
|
||||
continue;
|
||||
}
|
||||
frameHeader.type = translationMap[frameHeader.type];
|
||||
_frameHeaders.push_back(frameHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME move to frame?
|
||||
bool writeFrame(QIODevice& output, const Frame& frame) {
|
||||
auto written = output.write((char*)&(frame.type), sizeof(FrameType));
|
||||
if (written != sizeof(FrameType)) {
|
||||
return false;
|
||||
}
|
||||
written = output.write((char*)&(frame.timeOffset), sizeof(float));
|
||||
if (written != sizeof(float)) {
|
||||
return false;
|
||||
}
|
||||
uint16_t dataSize = frame.data.size();
|
||||
written = output.write((char*)&dataSize, sizeof(uint16_t));
|
||||
if (written != sizeof(uint16_t)) {
|
||||
return false;
|
||||
}
|
||||
if (dataSize != 0) {
|
||||
written = output.write(frame.data);
|
||||
if (written != dataSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileClip::write(const QString& fileName, Clip::Pointer clip) {
|
||||
qCDebug(recordingLog) << "Writing clip to file " << fileName;
|
||||
|
||||
if (0 == clip->frameCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile outputFile(fileName);
|
||||
if (!outputFile.open(QFile::Truncate | QFile::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Finally closer([&] { outputFile.close(); });
|
||||
{
|
||||
auto frameTypes = Frame::getFrameTypes();
|
||||
QJsonObject frameTypeObj;
|
||||
for (const auto& frameTypeName : frameTypes.keys()) {
|
||||
frameTypeObj[frameTypeName] = frameTypes[frameTypeName];
|
||||
}
|
||||
|
||||
QJsonObject rootObject;
|
||||
rootObject.insert(FRAME_TYPE_MAP, frameTypeObj);
|
||||
QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData();
|
||||
if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
clip->seek(0);
|
||||
for (auto frame = clip->nextFrame(); frame; frame = clip->nextFrame()) {
|
||||
if (!writeFrame(outputFile, *frame)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
outputFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
FileClip::~FileClip() {
|
||||
Locker lock(_mutex);
|
||||
_file.unmap(_map);
|
||||
_map = nullptr;
|
||||
if (_file.isOpen()) {
|
||||
_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void FileClip::seek(float offset) {
|
||||
|
@ -72,7 +227,9 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const {
|
|||
const FrameHeader& header = _frameHeaders[frameIndex];
|
||||
result->type = header.type;
|
||||
result->timeOffset = header.timeOffset;
|
||||
result->data.insert(0, reinterpret_cast<char*>(_map)+header.fileOffset, header.size);
|
||||
if (header.size) {
|
||||
result->data.insert(0, reinterpret_cast<char*>(_map)+header.fileOffset, header.size);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -99,7 +256,18 @@ void FileClip::reset() {
|
|||
_frameIndex = 0;
|
||||
}
|
||||
|
||||
void FileClip::appendFrame(FramePointer) {
|
||||
void FileClip::addFrame(FramePointer) {
|
||||
throw std::runtime_error("File clips are read only");
|
||||
}
|
||||
|
||||
float FileClip::duration() const {
|
||||
if (_frameHeaders.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return _frameHeaders.rbegin()->timeOffset;
|
||||
}
|
||||
|
||||
size_t FileClip::frameCount() const {
|
||||
return _frameHeaders.size();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../Clip.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
|
@ -22,22 +23,25 @@ class FileClip : public Clip {
|
|||
public:
|
||||
using Pointer = std::shared_ptr<FileClip>;
|
||||
|
||||
FileClip(const QString& file, QObject* parent = nullptr);
|
||||
FileClip(const QString& file);
|
||||
virtual ~FileClip();
|
||||
|
||||
virtual float duration() const override;
|
||||
virtual size_t frameCount() const override;
|
||||
|
||||
virtual void seek(float offset) override;
|
||||
virtual float position() const override;
|
||||
|
||||
virtual FramePointer peekFrame() const override;
|
||||
virtual FramePointer nextFrame() override;
|
||||
virtual void appendFrame(FramePointer) override;
|
||||
virtual void skipFrame() override;
|
||||
virtual void addFrame(FramePointer) override;
|
||||
|
||||
private:
|
||||
using Mutex = std::mutex;
|
||||
using Locker = std::unique_lock<Mutex>;
|
||||
const QJsonDocument& getHeader() {
|
||||
return _fileHeader;
|
||||
}
|
||||
|
||||
virtual void reset() override;
|
||||
static bool write(const QString& filePath, Clip::Pointer clip);
|
||||
|
||||
struct FrameHeader {
|
||||
FrameType type;
|
||||
|
@ -46,15 +50,20 @@ private:
|
|||
quint64 fileOffset;
|
||||
};
|
||||
|
||||
using FrameHeaders = std::vector<FrameHeader>;
|
||||
private:
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
|
||||
using FrameHeaderVector = std::vector<FrameHeader>;
|
||||
|
||||
FramePointer readFrame(uint32_t frameIndex) const;
|
||||
|
||||
mutable Mutex _mutex;
|
||||
QJsonDocument _fileHeader;
|
||||
QFile _file;
|
||||
uint32_t _frameIndex { 0 };
|
||||
uchar* _map;
|
||||
FrameHeaders _frameHeaders;
|
||||
uchar* _map { nullptr };
|
||||
FrameHeaderVector _frameHeaders;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,16 @@
|
|||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
DependencyManager DependencyManager::_manager;
|
||||
#include "SharedUtil.h"
|
||||
#include "Finally.h"
|
||||
|
||||
static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger";
|
||||
|
||||
DependencyManager& DependencyManager::manager() {
|
||||
static DependencyManager* instance = globalInstance<DependencyManager>(DEPENDENCY_PROPERTY_NAME);
|
||||
return *instance;
|
||||
}
|
||||
|
||||
QSharedPointer<Dependency>& DependencyManager::safeGet(size_t hashCode) {
|
||||
return _instanceHash[hashCode];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ public:
|
|||
static void registerInheritance();
|
||||
|
||||
private:
|
||||
static DependencyManager _manager;
|
||||
|
||||
static DependencyManager& manager();
|
||||
|
||||
template<typename T>
|
||||
size_t getHashCode();
|
||||
|
||||
|
@ -75,11 +75,11 @@ private:
|
|||
|
||||
template <typename T>
|
||||
QSharedPointer<T> DependencyManager::get() {
|
||||
static size_t hashCode = _manager.getHashCode<T>();
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
static QWeakPointer<T> instance;
|
||||
|
||||
if (instance.isNull()) {
|
||||
instance = qSharedPointerCast<T>(_manager.safeGet(hashCode));
|
||||
instance = qSharedPointerCast<T>(manager().safeGet(hashCode));
|
||||
|
||||
if (instance.isNull()) {
|
||||
qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name();
|
||||
|
@ -91,9 +91,9 @@ QSharedPointer<T> DependencyManager::get() {
|
|||
|
||||
template <typename T, typename ...Args>
|
||||
QSharedPointer<T> DependencyManager::set(Args&&... args) {
|
||||
static size_t hashCode = _manager.getHashCode<T>();
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
|
||||
QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
|
||||
QSharedPointer<Dependency>& instance = manager().safeGet(hashCode);
|
||||
instance.clear(); // Clear instance before creation of new one to avoid edge cases
|
||||
QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);
|
||||
QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
|
||||
|
@ -104,9 +104,9 @@ QSharedPointer<T> DependencyManager::set(Args&&... args) {
|
|||
|
||||
template <typename T, typename I, typename ...Args>
|
||||
QSharedPointer<T> DependencyManager::set(Args&&... args) {
|
||||
static size_t hashCode = _manager.getHashCode<T>();
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
|
||||
QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
|
||||
QSharedPointer<Dependency>& instance = manager().safeGet(hashCode);
|
||||
instance.clear(); // Clear instance before creation of new one to avoid edge cases
|
||||
QSharedPointer<T> newInstance(new I(args...), &I::customDeleter);
|
||||
QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
|
||||
|
@ -117,15 +117,15 @@ QSharedPointer<T> DependencyManager::set(Args&&... args) {
|
|||
|
||||
template <typename T>
|
||||
void DependencyManager::destroy() {
|
||||
static size_t hashCode = _manager.getHashCode<T>();
|
||||
_manager.safeGet(hashCode).clear();
|
||||
static size_t hashCode = manager().getHashCode<T>();
|
||||
manager().safeGet(hashCode).clear();
|
||||
}
|
||||
|
||||
template<typename Base, typename Derived>
|
||||
void DependencyManager::registerInheritance() {
|
||||
size_t baseHashCode = typeid(Base).hash_code();
|
||||
size_t derivedHashCode = typeid(Derived).hash_code();
|
||||
_manager._inheritanceHash.insert(baseHashCode, derivedHashCode);
|
||||
manager()._inheritanceHash.insert(baseHashCode, derivedHashCode);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_SharedUtil_h
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -20,7 +21,36 @@
|
|||
#include <unistd.h> // not on windows, not needed for mac or windows
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
// Provides efficient access to a named global type. By storing the value
|
||||
// in the QApplication by name we can implement the singleton pattern and
|
||||
// have the single instance function across DLL boundaries.
|
||||
template <typename T, typename... Args>
|
||||
T* globalInstance(const char* propertyName, Args&&... args) {
|
||||
static std::unique_ptr<T> instancePtr;
|
||||
static T* resultInstance { nullptr };
|
||||
static std::mutex mutex;
|
||||
if (!resultInstance) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (!resultInstance) {
|
||||
auto variant = qApp->property(propertyName);
|
||||
if (variant.isNull()) {
|
||||
// Since we're building the object, store it in a shared_ptr so it's
|
||||
// destroyed by the destructor of the static instancePtr
|
||||
instancePtr = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
|
||||
void* voidInstance = &(*instancePtr);
|
||||
variant = QVariant::fromValue(voidInstance);
|
||||
qApp->setProperty(propertyName, variant);
|
||||
}
|
||||
void* returnedVoidInstance = variant.value<void*>();
|
||||
resultInstance = static_cast<T*>(returnedVoidInstance);
|
||||
}
|
||||
}
|
||||
return resultInstance;
|
||||
}
|
||||
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
const int BYTES_PER_FLAGS = 1;
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
set(TARGET_NAME recording-test)
|
||||
# This is not a testcase -- just set it up as a regular hifi project
|
||||
setup_hifi_project(Test)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
link_hifi_libraries(shared recording)
|
||||
copy_dlls_beside_windows_executable()
|
||||
|
||||
# FIXME convert to unit tests
|
||||
# Declare dependencies
|
||||
macro (setup_testcase_dependencies)
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared recording)
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
endmacro ()
|
||||
|
||||
setup_hifi_testcase()
|
||||
#macro (setup_testcase_dependencies)
|
||||
# # link in the shared libraries
|
||||
# link_hifi_libraries(shared recording)
|
||||
#
|
||||
# copy_dlls_beside_windows_executable()
|
||||
#endmacro ()
|
||||
#setup_hifi_testcase()
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#ifndef hifi_Constants_h
|
||||
#define hifi_Constants_h
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
static const QString HEADER_NAME = "com.highfidelity.recording.Header";
|
||||
static const QString TEST_NAME = "com.highfidelity.recording.Test";
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "FrameTests.h"
|
||||
#include "Constants.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
|
||||
#include <recording/Frame.h>
|
||||
|
@ -27,3 +30,4 @@ void FrameTests::registerFrameTypeTest() {
|
|||
QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef hifi_FrameTests_h
|
||||
#define hifi_FrameTests_h
|
||||
|
||||
#if 0
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class FrameTests : public QObject {
|
||||
|
@ -18,4 +19,6 @@ private slots:
|
|||
void registerFrameTypeTest();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_FrameTests_h
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "RecorderTests.h"
|
||||
#include "Constants.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
|
||||
#include <recording/Recorder.h>
|
||||
|
@ -23,3 +26,4 @@ void RecorderTests::recorderTest() {
|
|||
//QCOMPARE(recoreder.isRecording(), false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef hifi_RecorderTests_h
|
||||
#define hifi_RecorderTests_h
|
||||
|
||||
#if 0
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class RecorderTests : public QObject {
|
||||
|
@ -19,3 +21,5 @@ private slots:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
114
tests/recording/src/main.cpp
Normal file
114
tests/recording/src/main.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include <QtGlobal>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <recording/Clip.h>
|
||||
#include <recording/Frame.h>
|
||||
|
||||
#include "Constants.h"
|
||||
|
||||
#define QVERIFY Q_ASSERT
|
||||
|
||||
using namespace recording;
|
||||
FrameType TEST_FRAME_TYPE { Frame::TYPE_INVALID };
|
||||
|
||||
void testFrameTypeRegistration() {
|
||||
TEST_FRAME_TYPE = Frame::registerFrameType(TEST_NAME);
|
||||
QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_INVALID);
|
||||
QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_HEADER);
|
||||
|
||||
auto forwardMap = recording::Frame::getFrameTypes();
|
||||
QVERIFY(forwardMap.count(TEST_NAME) == 1);
|
||||
QVERIFY(forwardMap[TEST_NAME] == TEST_FRAME_TYPE);
|
||||
QVERIFY(forwardMap[HEADER_NAME] == recording::Frame::TYPE_HEADER);
|
||||
|
||||
auto backMap = recording::Frame::getFrameTypeNames();
|
||||
QVERIFY(backMap.count(TEST_FRAME_TYPE) == 1);
|
||||
QVERIFY(backMap[TEST_FRAME_TYPE] == TEST_NAME);
|
||||
QVERIFY(backMap[recording::Frame::TYPE_HEADER] == HEADER_NAME);
|
||||
}
|
||||
|
||||
void testFilePersist() {
|
||||
QTemporaryFile file;
|
||||
QString fileName;
|
||||
if (file.open()) {
|
||||
fileName = file.fileName();
|
||||
file.close();
|
||||
}
|
||||
auto readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(Clip::Pointer() == readClip);
|
||||
auto writeClip = Clip::newClip();
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 1);
|
||||
QVERIFY(writeClip->duration() == 5.0f);
|
||||
|
||||
Clip::toFile(fileName, writeClip);
|
||||
readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(readClip != Clip::Pointer());
|
||||
QVERIFY(readClip->frameCount() == 1);
|
||||
QVERIFY(readClip->duration() == 5.0f);
|
||||
readClip->seek(0);
|
||||
writeClip->seek(0);
|
||||
|
||||
size_t count = 0;
|
||||
for (auto readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(); readFrame && writeFrame;
|
||||
readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(), ++count) {
|
||||
QVERIFY(readFrame->type == writeFrame->type);
|
||||
QVERIFY(readFrame->timeOffset == writeFrame->timeOffset);
|
||||
QVERIFY(readFrame->data == writeFrame->data);
|
||||
}
|
||||
QVERIFY(readClip->frameCount() == count);
|
||||
|
||||
|
||||
writeClip = Clip::newClip();
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
// Simulate an unknown frametype
|
||||
writeClip->addFrame(std::make_shared<Frame>(Frame::TYPE_INVALID - 1, 10.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 2);
|
||||
QVERIFY(writeClip->duration() == 10.0f);
|
||||
Clip::toFile(fileName, writeClip);
|
||||
|
||||
// Verify that the read version of the clip ignores the unknown frame type
|
||||
readClip = Clip::fromFile(fileName);
|
||||
QVERIFY(readClip != Clip::Pointer());
|
||||
QVERIFY(readClip->frameCount() == 1);
|
||||
QVERIFY(readClip->duration() == 5.0f);
|
||||
}
|
||||
|
||||
void testClipOrdering() {
|
||||
auto writeClip = Clip::newClip();
|
||||
// simulate our of order addition of frames
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 10.0f, QByteArray()));
|
||||
writeClip->addFrame(std::make_shared<Frame>(TEST_FRAME_TYPE, 5.0f, QByteArray()));
|
||||
QVERIFY(writeClip->frameCount() == 2);
|
||||
QVERIFY(writeClip->duration() == 10.0f);
|
||||
|
||||
QVERIFY(std::numeric_limits<float>::max() == writeClip->position());
|
||||
writeClip->seek(0);
|
||||
QVERIFY(5.0f == writeClip->position());
|
||||
float lastFrameTimeOffset { 0 };
|
||||
for (auto writeFrame = writeClip->nextFrame(); writeFrame; writeFrame = writeClip->nextFrame()) {
|
||||
QVERIFY(writeClip->position() >= lastFrameTimeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
void myMessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg) {
|
||||
OutputDebugStringA(msg.toLocal8Bit().toStdString().c_str());
|
||||
OutputDebugStringA("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int, const char**) {
|
||||
#ifdef Q_OS_WIN32
|
||||
qInstallMessageHandler(myMessageHandler);
|
||||
#endif
|
||||
testFrameTypeRegistration();
|
||||
testFilePersist();
|
||||
testClipOrdering();
|
||||
}
|
|
@ -82,7 +82,7 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
|||
z: 0
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
collisionsSoundURL: basketballCollisionSoundURL,
|
||||
collisionSoundURL: basketballCollisionSoundURL,
|
||||
ignoreForCollisions: false,
|
||||
modelURL: basketballURL,
|
||||
userData: JSON.stringify({
|
||||
|
|
|
@ -308,6 +308,7 @@
|
|||
z: 0
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav',
|
||||
ignoreForCollisions: false,
|
||||
modelURL: basketballURL,
|
||||
userData: JSON.stringify({
|
||||
|
@ -1258,4 +1259,4 @@
|
|||
};
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new ResetSwitch();
|
||||
});
|
||||
});
|
|
@ -248,7 +248,7 @@ MasterReset = function() {
|
|||
},
|
||||
grabbableKey: {
|
||||
grabbable: false,
|
||||
wantsTrigger:true
|
||||
wantsTrigger: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -289,6 +289,7 @@ MasterReset = function() {
|
|||
z: 0
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav',
|
||||
ignoreForCollisions: false,
|
||||
modelURL: basketballURL,
|
||||
userData: JSON.stringify({
|
||||
|
@ -334,7 +335,7 @@ MasterReset = function() {
|
|||
name: "Basketball Resetter",
|
||||
script: basketballResetterScriptURL,
|
||||
dimensions: dimensions,
|
||||
visible:false,
|
||||
visible: false,
|
||||
userData: JSON.stringify({
|
||||
resetMe: {
|
||||
resetMe: true
|
||||
|
@ -367,7 +368,7 @@ MasterReset = function() {
|
|||
name: "Target Resetter",
|
||||
script: targetsResetterScriptURL,
|
||||
dimensions: dimensions,
|
||||
visible:false,
|
||||
visible: false,
|
||||
userData: JSON.stringify({
|
||||
resetMe: {
|
||||
resetMe: true
|
||||
|
@ -1238,4 +1239,4 @@ MasterReset = function() {
|
|||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
}
|
||||
};
|
||||
};
|
Loading…
Reference in a new issue