mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 05:52:38 +02:00
Merge remote-tracking branch 'upstream/master' into vive-ui
This commit is contained in:
commit
b7d1659852
39 changed files with 1129 additions and 212 deletions
|
@ -994,7 +994,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
if (_keyboardFocusedItem != entityItemID) {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
|
||||
if (EntityTypes::Web == properties.getType() && !properties.getLocked()) {
|
||||
if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) {
|
||||
auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID);
|
||||
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
||||
if (webEntity) {
|
||||
|
@ -1049,6 +1049,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
}
|
||||
});
|
||||
|
||||
connect(this, &Application::aboutToQuit, [=]() {
|
||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||
if (_keyboardFocusHighlight) {
|
||||
_keyboardFocusHighlight->setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
|
@ -1503,7 +1510,13 @@ void Application::paintGL() {
|
|||
// FIXME not needed anymore?
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
displayPlugin->beginFrameRender(_frameCount);
|
||||
// If a display plugin loses it's underlying support, it
|
||||
// needs to be able to signal us to not use it
|
||||
if (!displayPlugin->beginFrameRender(_frameCount)) {
|
||||
_inPaint = false;
|
||||
updateDisplayMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// update the avatar with a fresh HMD pose
|
||||
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
|
||||
|
@ -5099,9 +5112,17 @@ void Application::updateDisplayMode() {
|
|||
|
||||
foreach(auto displayPlugin, standard) {
|
||||
addDisplayPluginToMenu(displayPlugin, first);
|
||||
auto displayPluginName = displayPlugin->getName();
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
|
||||
resizeGL();
|
||||
});
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::outputDeviceLost, [this, displayPluginName] {
|
||||
PluginManager::getInstance()->disableDisplayPlugin(displayPluginName);
|
||||
auto menu = Menu::getInstance();
|
||||
if (menu->menuItemExists(MenuOption::OutputMenu, displayPluginName)) {
|
||||
menu->removeMenuItem(MenuOption::OutputMenu, displayPluginName);
|
||||
}
|
||||
});
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
@ -5117,6 +5138,10 @@ void Application::updateDisplayMode() {
|
|||
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
||||
QString name = displayPlugin->getName();
|
||||
QAction* action = menu->getActionForOption(name);
|
||||
// Menu might have been removed if the display plugin lost
|
||||
if (!action) {
|
||||
continue;
|
||||
}
|
||||
if (action->isChecked()) {
|
||||
newDisplayPlugin = displayPlugin;
|
||||
break;
|
||||
|
|
|
@ -322,7 +322,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
|||
const auto characterController = myAvatar->getCharacterController();
|
||||
const float avatarVelocityChange = (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f);
|
||||
const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange;
|
||||
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01f;
|
||||
const float MIN_AVATAR_COLLISION_ACCELERATION = 2.4f; // walking speed
|
||||
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
|
||||
|
||||
if (!isSound) {
|
||||
|
@ -330,14 +330,24 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
|||
}
|
||||
// Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for.
|
||||
const float energy = velocityChange * velocityChange;
|
||||
const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f;
|
||||
const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f;
|
||||
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
|
||||
|
||||
// For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object,
|
||||
// but most avatars are roughly the same size, so let's not be so fancy yet.
|
||||
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
||||
|
||||
AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
|
||||
|
||||
_collisionInjectors.remove_if([](QPointer<AudioInjector>& injector) {
|
||||
return !injector || injector->isFinished();
|
||||
});
|
||||
|
||||
static const int MAX_INJECTOR_COUNT = 3;
|
||||
if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) {
|
||||
auto injector = AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR,
|
||||
myAvatar->getPosition());
|
||||
_collisionInjectors.emplace_back(injector);
|
||||
}
|
||||
myAvatar->collisionWithEntity(collision);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "AvatarMotionState.h"
|
||||
|
||||
class MyAvatar;
|
||||
class AudioInjector;
|
||||
|
||||
class AvatarManager : public AvatarHashMap {
|
||||
Q_OBJECT
|
||||
|
@ -94,6 +95,8 @@ private:
|
|||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
||||
std::list<QPointer<AudioInjector>> _collisionInjectors;
|
||||
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
|
|
|
@ -739,7 +739,7 @@ void MyAvatar::saveData() {
|
|||
settings.endGroup();
|
||||
}
|
||||
|
||||
float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
||||
float loadSetting(Settings& settings, const QString& name, float defaultValue) {
|
||||
float value = settings.value(name, defaultValue).toFloat();
|
||||
if (glm::isnan(value)) {
|
||||
value = defaultValue;
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "AudioRingBuffer.h"
|
||||
#include "AudioLogging.h"
|
||||
#include "AudioSRC.h"
|
||||
|
||||
#include "Sound.h"
|
||||
|
||||
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
|
||||
|
@ -89,49 +91,22 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
|||
// we want to convert it to the format that the audio-mixer wants
|
||||
// which is signed, 16-bit, 24Khz
|
||||
|
||||
int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
|
||||
int numChannels = _isStereo ? 2 : 1;
|
||||
AudioSRC resampler(48000, AudioConstants::SAMPLE_RATE, numChannels);
|
||||
|
||||
if (_isStereo && numSourceSamples % 2 != 0){
|
||||
// in the unlikely case that we have stereo audio but we seem to be missing a sample
|
||||
// (the sample for one channel is missing in a set of interleaved samples)
|
||||
// then drop the odd sample
|
||||
--numSourceSamples;
|
||||
}
|
||||
// resize to max possible output
|
||||
int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample));
|
||||
int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames);
|
||||
int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
|
||||
_byteArray.resize(maxDestinationBytes);
|
||||
|
||||
int numDestinationSamples = numSourceSamples / 2.0f;
|
||||
|
||||
if (_isStereo && numDestinationSamples % 2 != 0) {
|
||||
// if this is stereo we need to make sure we produce stereo output
|
||||
// which means we should have an even number of output samples
|
||||
numDestinationSamples += 1;
|
||||
}
|
||||
|
||||
int numDestinationBytes = numDestinationSamples * sizeof(AudioConstants::AudioSample);
|
||||
int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(),
|
||||
(int16_t*)_byteArray.data(),
|
||||
numSourceFrames);
|
||||
|
||||
// truncate to actual output
|
||||
int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
|
||||
_byteArray.resize(numDestinationBytes);
|
||||
|
||||
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
|
||||
int16_t* destinationSamples = (int16_t*) _byteArray.data();
|
||||
|
||||
if (_isStereo) {
|
||||
for (int i = 0; i < numSourceSamples; i += 4) {
|
||||
if (i + 2 >= numSourceSamples) {
|
||||
destinationSamples[i / 2] = sourceSamples[i];
|
||||
destinationSamples[(i / 2) + 1] = sourceSamples[i + 1];
|
||||
} else {
|
||||
destinationSamples[i / 2] = (sourceSamples[i] + sourceSamples[i + 2]) / 2;
|
||||
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] + sourceSamples[i + 3]) / 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i < numSourceSamples; i += 2) {
|
||||
if (i + 1 >= numSourceSamples) {
|
||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] + sourceSamples[i]) / 2;
|
||||
} else {
|
||||
destinationSamples[(i - 1) / 2] = ((sourceSamples[i - 1] + sourceSamples[i + 1]) / 4) + (sourceSamples[i] / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -599,11 +599,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
|
||||
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||
ShapeType type = getShapeType();
|
||||
if (type != SHAPE_TYPE_COMPOUND) {
|
||||
ModelEntityItem::computeShapeInfo(info);
|
||||
info.setParams(type, 0.5f * getDimensions());
|
||||
adjustShapeInfoByRegistration(info);
|
||||
} else {
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
updateModelBounds();
|
||||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
|
@ -612,25 +608,27 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const FBXGeometry& renderGeometry = _model->getFBXGeometry();
|
||||
const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
|
||||
|
||||
_points.clear();
|
||||
unsigned int i = 0;
|
||||
QVector<QVector<glm::vec3>>& points = info.getPoints();
|
||||
points.clear();
|
||||
uint32_t i = 0;
|
||||
|
||||
// the way OBJ files get read, each section under a "g" line is its own meshPart. We only expect
|
||||
// to find one actual "mesh" (with one or more meshParts in it), but we loop over the meshes, just in case.
|
||||
const uint32_t TRIANGLE_STRIDE = 3;
|
||||
const uint32_t QUAD_STRIDE = 4;
|
||||
foreach (const FBXMesh& mesh, collisionGeometry.meshes) {
|
||||
// each meshPart is a convex hull
|
||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||
QVector<glm::vec3> pointsInPart;
|
||||
points.push_back(QVector<glm::vec3>());
|
||||
QVector<glm::vec3>& pointsInPart = points[i];
|
||||
|
||||
// run through all the triangles and (uniquely) add each point to the hull
|
||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
||||
for (unsigned int j = 0; j < triangleCount; j++) {
|
||||
unsigned int p0Index = meshPart.triangleIndices[j*3];
|
||||
unsigned int p1Index = meshPart.triangleIndices[j*3+1];
|
||||
unsigned int p2Index = meshPart.triangleIndices[j*3+2];
|
||||
glm::vec3 p0 = mesh.vertices[p0Index];
|
||||
glm::vec3 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
uint32_t numIndices = (uint32_t)meshPart.triangleIndices.size();
|
||||
assert(numIndices % TRIANGLE_STRIDE == 0);
|
||||
for (uint32_t j = 0; j < numIndices; j += TRIANGLE_STRIDE) {
|
||||
glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[j]];
|
||||
glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[j + 1]];
|
||||
glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[j + 2]];
|
||||
if (!pointsInPart.contains(p0)) {
|
||||
pointsInPart << p0;
|
||||
}
|
||||
|
@ -643,17 +641,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
}
|
||||
|
||||
// run through all the quads and (uniquely) add each point to the hull
|
||||
unsigned int quadCount = meshPart.quadIndices.size() / 4;
|
||||
assert((unsigned int)meshPart.quadIndices.size() == quadCount*4);
|
||||
for (unsigned int j = 0; j < quadCount; j++) {
|
||||
unsigned int p0Index = meshPart.quadIndices[j*4];
|
||||
unsigned int p1Index = meshPart.quadIndices[j*4+1];
|
||||
unsigned int p2Index = meshPart.quadIndices[j*4+2];
|
||||
unsigned int p3Index = meshPart.quadIndices[j*4+3];
|
||||
glm::vec3 p0 = mesh.vertices[p0Index];
|
||||
glm::vec3 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
glm::vec3 p3 = mesh.vertices[p3Index];
|
||||
numIndices = (uint32_t)meshPart.quadIndices.size();
|
||||
assert(numIndices % QUAD_STRIDE == 0);
|
||||
for (uint32_t j = 0; j < numIndices; j += QUAD_STRIDE) {
|
||||
glm::vec3 p0 = mesh.vertices[meshPart.quadIndices[j]];
|
||||
glm::vec3 p1 = mesh.vertices[meshPart.quadIndices[j + 1]];
|
||||
glm::vec3 p2 = mesh.vertices[meshPart.quadIndices[j + 2]];
|
||||
glm::vec3 p3 = mesh.vertices[meshPart.quadIndices[j + 3]];
|
||||
if (!pointsInPart.contains(p0)) {
|
||||
pointsInPart << p0;
|
||||
}
|
||||
|
@ -670,14 +664,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
|
||||
if (pointsInPart.size() == 0) {
|
||||
qCDebug(entitiesrenderer) << "Warning -- meshPart has no faces";
|
||||
points.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// add next convex hull
|
||||
QVector<glm::vec3> newMeshPoints;
|
||||
_points << newMeshPoints;
|
||||
// add points to the new convex hull
|
||||
_points[i++] << pointsInPart;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,23 +681,26 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||
// also determine the extents of the collision model.
|
||||
AABox box;
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
for (int j = 0; j < _points[i].size(); j++) {
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
for (int j = 0; j < points[i].size(); j++) {
|
||||
// compensate for registration
|
||||
_points[i][j] += _model->getOffset();
|
||||
points[i][j] += _model->getOffset();
|
||||
// scale so the collision points match the model points
|
||||
_points[i][j] *= scale;
|
||||
points[i][j] *= scale;
|
||||
// this next subtraction is done so we can give info the offset, which will cause
|
||||
// the shape-key to change.
|
||||
_points[i][j] -= _model->getOffset();
|
||||
box += _points[i][j];
|
||||
points[i][j] -= _model->getOffset();
|
||||
box += points[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 collisionModelDimensions = box.getDimensions();
|
||||
info.setParams(type, collisionModelDimensions, _compoundShapeURL);
|
||||
info.setConvexHulls(_points);
|
||||
info.setOffset(_model->getOffset());
|
||||
} else {
|
||||
ModelEntityItem::computeShapeInfo(info);
|
||||
info.setParams(type, 0.5f * getDimensions());
|
||||
adjustShapeInfoByRegistration(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,6 @@ private:
|
|||
QVariantMap _currentTextures;
|
||||
QVariantMap _originalTextures;
|
||||
bool _originalTexturesRead = false;
|
||||
QVector<QVector<glm::vec3>> _points;
|
||||
bool _dimensionsInitialized = true;
|
||||
|
||||
AnimationPropertyGroup _renderAnimationProperties;
|
||||
|
|
|
@ -226,10 +226,15 @@ void RenderableWebEntityItem::setSourceUrl(const QString& value) {
|
|||
}
|
||||
|
||||
void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) {
|
||||
_webSurface->setProxyWindow(proxyWindow);
|
||||
if (_webSurface) {
|
||||
_webSurface->setProxyWindow(proxyWindow);
|
||||
}
|
||||
}
|
||||
|
||||
QObject* RenderableWebEntityItem::getEventHandler() {
|
||||
if (!_webSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
return _webSurface->getEventHandler();
|
||||
}
|
||||
|
||||
|
|
|
@ -687,15 +687,80 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
}
|
||||
}
|
||||
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotationFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
// we also want to ignore any duplicate packets that have the same "recently updated" values
|
||||
// as a packet we've already recieved. This is because we want multiple edits of the same
|
||||
// information to be idempotent, but if we applied new physics properties we'd resimulation
|
||||
// with small differences in results.
|
||||
|
||||
// Because the regular streaming property "setters" only have access to the new value, we've
|
||||
// made these lambdas that can access other details about the previous updates to suppress
|
||||
// any duplicates.
|
||||
|
||||
// Note: duplicate packets are expected and not wrong. They may be sent for any number of
|
||||
// reasons and the contract is that the client handles them in an idempotent manner.
|
||||
auto lastEdited = lastEditedFromBufferAdjusted;
|
||||
auto customUpdatePositionFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||
bool simulationChanged = lastEdited > _lastUpdatedPositionTimestamp;
|
||||
bool valueChanged = value != _lastUpdatedPositionValue;
|
||||
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||
if (shouldUpdate) {
|
||||
updatePositionFromNetwork(value);
|
||||
_lastUpdatedPositionTimestamp = lastEdited;
|
||||
_lastUpdatedPositionValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
auto customUpdateRotationFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::quat value){
|
||||
bool simulationChanged = lastEdited > _lastUpdatedRotationTimestamp;
|
||||
bool valueChanged = value != _lastUpdatedRotationValue;
|
||||
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||
if (shouldUpdate) {
|
||||
updateRotationFromNetwork(value);
|
||||
_lastUpdatedRotationTimestamp = lastEdited;
|
||||
_lastUpdatedRotationValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
auto customUpdateVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||
bool simulationChanged = lastEdited > _lastUpdatedVelocityTimestamp;
|
||||
bool valueChanged = value != _lastUpdatedVelocityValue;
|
||||
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||
if (shouldUpdate) {
|
||||
updateVelocityFromNetwork(value);
|
||||
_lastUpdatedVelocityTimestamp = lastEdited;
|
||||
_lastUpdatedVelocityValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
auto customUpdateAngularVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||
bool simulationChanged = lastEdited > _lastUpdatedAngularVelocityTimestamp;
|
||||
bool valueChanged = value != _lastUpdatedAngularVelocityValue;
|
||||
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||
if (shouldUpdate) {
|
||||
updateAngularVelocityFromNetwork(value);
|
||||
_lastUpdatedAngularVelocityTimestamp = lastEdited;
|
||||
_lastUpdatedAngularVelocityValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
auto customSetAcceleration = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||
bool simulationChanged = lastEdited > _lastUpdatedAccelerationTimestamp;
|
||||
bool valueChanged = value != _lastUpdatedAccelerationValue;
|
||||
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||
if (shouldUpdate) {
|
||||
setAcceleration(value);
|
||||
_lastUpdatedAccelerationTimestamp = lastEdited;
|
||||
_lastUpdatedAccelerationValue = value;
|
||||
}
|
||||
};
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, customUpdatePositionFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, customUpdateRotationFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, customUpdateVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, customUpdateAngularVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, customSetAcceleration);
|
||||
|
||||
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
|
@ -922,13 +987,11 @@ void EntityItem::simulate(const quint64& now) {
|
|||
qCDebug(entities) << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
|
||||
#endif
|
||||
|
||||
if (!hasActions()) {
|
||||
if (!stepKinematicMotion(timeElapsed)) {
|
||||
// this entity is no longer moving
|
||||
// flag it to transition from KINEMATIC to STATIC
|
||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
setAcceleration(Vectors::ZERO);
|
||||
}
|
||||
if (!stepKinematicMotion(timeElapsed)) {
|
||||
// this entity is no longer moving
|
||||
// flag it to transition from KINEMATIC to STATIC
|
||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
setAcceleration(Vectors::ZERO);
|
||||
}
|
||||
_lastSimulated = now;
|
||||
}
|
||||
|
|
|
@ -550,6 +550,22 @@ protected:
|
|||
|
||||
bool _clientOnly { false };
|
||||
QUuid _owningAvatarID;
|
||||
|
||||
// physics related changes from the network to suppress any duplicates and make
|
||||
// sure redundant applications are idempotent
|
||||
glm::vec3 _lastUpdatedPositionValue;
|
||||
glm::quat _lastUpdatedRotationValue;
|
||||
glm::vec3 _lastUpdatedVelocityValue;
|
||||
glm::vec3 _lastUpdatedAngularVelocityValue;
|
||||
glm::vec3 _lastUpdatedAccelerationValue;
|
||||
|
||||
quint64 _lastUpdatedPositionTimestamp { 0 };
|
||||
quint64 _lastUpdatedRotationTimestamp { 0 };
|
||||
quint64 _lastUpdatedVelocityTimestamp { 0 };
|
||||
quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
|
||||
quint64 _lastUpdatedAccelerationTimestamp { 0 };
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
|
@ -88,7 +88,7 @@ void EntityItemProperties::setLastEdited(quint64 usecTime) {
|
|||
_lastEdited = usecTime > _created ? usecTime : _created;
|
||||
}
|
||||
|
||||
const char* shapeTypeNames[] = {"none", "box", "sphere", "ellipsoid", "plane", "compound", "capsule-x",
|
||||
const char* shapeTypeNames[] = {"none", "box", "sphere", "plane", "compound", "capsule-x",
|
||||
"capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
|
||||
|
||||
QHash<QString, ShapeType> stringToShapeTypeLookup;
|
||||
|
@ -101,7 +101,6 @@ void buildStringToShapeTypeLookup() {
|
|||
addShapeType(SHAPE_TYPE_NONE);
|
||||
addShapeType(SHAPE_TYPE_BOX);
|
||||
addShapeType(SHAPE_TYPE_SPHERE);
|
||||
addShapeType(SHAPE_TYPE_ELLIPSOID);
|
||||
addShapeType(SHAPE_TYPE_PLANE);
|
||||
addShapeType(SHAPE_TYPE_COMPOUND);
|
||||
addShapeType(SHAPE_TYPE_CAPSULE_X);
|
||||
|
|
|
@ -60,7 +60,7 @@ class LineEntityItem : public EntityItem {
|
|||
|
||||
const QVector<glm::vec3>& getLinePoints() const{ return _points; }
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; }
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
||||
|
||||
// never have a ray intersection pick a LineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
|
|
|
@ -78,7 +78,7 @@ class PolyLineEntityItem : public EntityItem {
|
|||
|
||||
virtual bool needsToCallUpdate() const { return true; }
|
||||
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_LINE; }
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
||||
|
||||
// never have a ray intersection pick a PolyLineEntityItem.
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
|
|
|
@ -401,11 +401,10 @@ glm::vec3 CharacterController::getLinearVelocity() const {
|
|||
}
|
||||
|
||||
glm::vec3 CharacterController::getVelocityChange() const {
|
||||
glm::vec3 velocity(0.0f);
|
||||
if (_rigidBody) {
|
||||
velocity = bulletToGLM(_rigidBody->getLinearVelocity());
|
||||
return bulletToGLM(_velocityChange);
|
||||
}
|
||||
return velocity;
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
void CharacterController::clearMotors() {
|
||||
|
|
|
@ -17,6 +17,54 @@
|
|||
#include "ShapeFactory.h"
|
||||
#include "BulletUtil.h"
|
||||
|
||||
// These are the same normalized directions used by the btShapeHull class.
|
||||
// 12 points for the face centers of a duodecohedron plus another 30 points
|
||||
// for the midpoints the edges, for a total of 42.
|
||||
const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42;
|
||||
static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = {
|
||||
btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)),
|
||||
btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)),
|
||||
btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)),
|
||||
btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)),
|
||||
btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)),
|
||||
btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)),
|
||||
btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)),
|
||||
btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)),
|
||||
btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)),
|
||||
btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)),
|
||||
btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)),
|
||||
btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)),
|
||||
btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)),
|
||||
btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)),
|
||||
btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)),
|
||||
btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)),
|
||||
btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)),
|
||||
btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)),
|
||||
btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)),
|
||||
btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)),
|
||||
btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)),
|
||||
btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)),
|
||||
btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)),
|
||||
btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)),
|
||||
btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)),
|
||||
btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)),
|
||||
btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)),
|
||||
btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)),
|
||||
btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)),
|
||||
btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)),
|
||||
btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)),
|
||||
btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)),
|
||||
btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)),
|
||||
btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)),
|
||||
btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)),
|
||||
btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)),
|
||||
btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)),
|
||||
btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)),
|
||||
btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)),
|
||||
btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)),
|
||||
btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)),
|
||||
btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654))
|
||||
};
|
||||
|
||||
|
||||
btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& points) {
|
||||
|
@ -66,15 +114,40 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& poin
|
|||
hull->addPoint(btVector3(correctedPoint[0], correctedPoint[1], correctedPoint[2]), false);
|
||||
}
|
||||
|
||||
if (points.size() > MAX_HULL_POINTS) {
|
||||
// create hull approximation
|
||||
btShapeHull shapeHull(hull);
|
||||
shapeHull.buildHull(margin);
|
||||
uint32_t numPoints = (uint32_t)hull->getNumPoints();
|
||||
if (numPoints > MAX_HULL_POINTS) {
|
||||
// we have too many points, so we compute point projections along canonical unit vectors
|
||||
// and keep the those that project the farthest
|
||||
btVector3 btCenter = glmToBullet(center);
|
||||
btVector3* shapePoints = hull->getUnscaledPoints();
|
||||
std::vector<uint32_t> finalIndices;
|
||||
finalIndices.reserve(NUM_UNIT_SPHERE_DIRECTIONS);
|
||||
for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) {
|
||||
uint32_t bestIndex = 0;
|
||||
btScalar maxDistance = _unitSphereDirections[i].dot(shapePoints[0] - btCenter);
|
||||
for (uint32_t j = 1; j < numPoints; ++j) {
|
||||
btScalar distance = _unitSphereDirections[i].dot(shapePoints[j] - btCenter);
|
||||
if (distance > maxDistance) {
|
||||
maxDistance = distance;
|
||||
bestIndex = j;
|
||||
}
|
||||
}
|
||||
bool keep = true;
|
||||
for (uint32_t j = 0; j < finalIndices.size(); ++j) {
|
||||
if (finalIndices[j] == bestIndex) {
|
||||
keep = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
finalIndices.push_back(bestIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// we cannot copy Bullet shapes so we must create a new one...
|
||||
btConvexHullShape* newHull = new btConvexHullShape();
|
||||
const btVector3* newPoints = shapeHull.getVertexPointer();
|
||||
for (int i = 0; i < shapeHull.numVertices(); ++i) {
|
||||
newHull->addPoint(newPoints[i], false);
|
||||
for (uint32_t i = 0; i < finalIndices.size(); ++i) {
|
||||
newHull->addPoint(shapePoints[finalIndices[i]], false);
|
||||
}
|
||||
// ...and delete the old one
|
||||
delete hull;
|
||||
|
|
|
@ -137,7 +137,7 @@ public:
|
|||
}
|
||||
|
||||
// will query the underlying hmd api to compute the most recent head pose
|
||||
virtual void beginFrameRender(uint32_t frameIndex) {}
|
||||
virtual bool beginFrameRender(uint32_t frameIndex) { return true; }
|
||||
|
||||
// returns a copy of the most recent head pose, computed via updateHeadPose
|
||||
virtual glm::mat4 getHeadPose() const {
|
||||
|
@ -170,6 +170,10 @@ public:
|
|||
|
||||
signals:
|
||||
void recommendedFramebufferSizeChanged(const QSize & size);
|
||||
// Indicates that this display plugin is no longer valid for use.
|
||||
// For instance if a user exits Oculus Home or Steam VR while
|
||||
// using the corresponding plugin, that plugin should be disabled.
|
||||
void outputDeviceLost();
|
||||
|
||||
protected:
|
||||
void incrementPresentCount();
|
||||
|
|
|
@ -62,11 +62,10 @@ PluginManager::PluginManager() {
|
|||
extern DisplayPluginList getDisplayPlugins();
|
||||
extern InputPluginList getInputPlugins();
|
||||
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
||||
static DisplayPluginList displayPlugins;
|
||||
|
||||
const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
||||
static DisplayPluginList displayPlugins;
|
||||
static std::once_flag once;
|
||||
|
||||
std::call_once(once, [&] {
|
||||
// Grab the built in plugins
|
||||
displayPlugins = ::getDisplayPlugins();
|
||||
|
@ -90,6 +89,16 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
|||
return displayPlugins;
|
||||
}
|
||||
|
||||
void PluginManager::disableDisplayPlugin(const QString& name) {
|
||||
for (size_t i = 0; i < displayPlugins.size(); ++i) {
|
||||
if (displayPlugins[i]->getName() == name) {
|
||||
displayPlugins.erase(displayPlugins.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const InputPluginList& PluginManager::getInputPlugins() {
|
||||
static InputPluginList inputPlugins;
|
||||
static std::once_flag once;
|
||||
|
|
|
@ -17,6 +17,7 @@ public:
|
|||
PluginManager();
|
||||
|
||||
const DisplayPluginList& getDisplayPlugins();
|
||||
void disableDisplayPlugin(const QString& name);
|
||||
const InputPluginList& getInputPlugins();
|
||||
void saveSettings();
|
||||
};
|
||||
|
|
|
@ -35,7 +35,46 @@ int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
|
|||
float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
||||
#define HTTP_INVALID_COM "http://invalid.com"
|
||||
|
||||
model::MaterialPointer Model::_collisionHullMaterial;
|
||||
const int NUM_COLLISION_HULL_COLORS = 24;
|
||||
std::vector<model::MaterialPointer> _collisionHullMaterials;
|
||||
|
||||
void initCollisionHullMaterials() {
|
||||
// generates bright colors in red, green, blue, yellow, magenta, and cyan spectrums
|
||||
// (no browns, greys, or dark shades)
|
||||
float component[NUM_COLLISION_HULL_COLORS] = {
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.2f, 0.4f, 0.6f, 0.8f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
0.8f, 0.6f, 0.4f, 0.2f
|
||||
};
|
||||
_collisionHullMaterials.reserve(NUM_COLLISION_HULL_COLORS);
|
||||
|
||||
// each component gets the same cuve
|
||||
// but offset by a multiple of one third the full width
|
||||
int numComponents = 3;
|
||||
int sectionWidth = NUM_COLLISION_HULL_COLORS / numComponents;
|
||||
int greenPhase = sectionWidth;
|
||||
int bluePhase = 2 * sectionWidth;
|
||||
|
||||
// we stride through the colors to scatter adjacent shades
|
||||
// so they don't tend to group together for large models
|
||||
for (int i = 0; i < sectionWidth; ++i) {
|
||||
for (int j = 0; j < numComponents; ++j) {
|
||||
model::MaterialPointer material;
|
||||
material = std::make_shared<model::Material>();
|
||||
int index = j * sectionWidth + i;
|
||||
float red = component[index];
|
||||
float green = component[(index + greenPhase) % NUM_COLLISION_HULL_COLORS];
|
||||
float blue = component[(index + bluePhase) % NUM_COLLISION_HULL_COLORS];
|
||||
material->setAlbedo(glm::vec3(red, green, blue));
|
||||
material->setMetallic(0.02f);
|
||||
material->setRoughness(0.5f);
|
||||
_collisionHullMaterials.push_back(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Model::Model(RigPointer rig, QObject* parent) :
|
||||
QObject(parent),
|
||||
|
@ -1217,13 +1256,10 @@ void Model::segregateMeshGroups() {
|
|||
int totalParts = mesh.parts.size();
|
||||
for (int partIndex = 0; partIndex < totalParts; partIndex++) {
|
||||
if (showingCollisionHull) {
|
||||
if (!_collisionHullMaterial) {
|
||||
_collisionHullMaterial = std::make_shared<model::Material>();
|
||||
_collisionHullMaterial->setAlbedo(glm::vec3(1.0f, 0.5f, 0.0f));
|
||||
_collisionHullMaterial->setMetallic(0.02f);
|
||||
_collisionHullMaterial->setRoughness(0.5f);
|
||||
if (_collisionHullMaterials.empty()) {
|
||||
initCollisionHullMaterials();
|
||||
}
|
||||
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterial, transform, offset);
|
||||
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(networkMesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset);
|
||||
} else {
|
||||
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
}
|
||||
|
|
|
@ -10,11 +10,76 @@
|
|||
//
|
||||
|
||||
#include "SettingHandle.h"
|
||||
#include "SettingManager.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
||||
const QString Settings::firstRun { "firstRun" };
|
||||
|
||||
Settings::Settings() :
|
||||
_manager(DependencyManager::get<Setting::Manager>()),
|
||||
_locker(&(_manager->getLock()))
|
||||
{
|
||||
}
|
||||
|
||||
Settings::~Settings() {
|
||||
}
|
||||
|
||||
void Settings::remove(const QString& key) {
|
||||
_manager->remove(key);
|
||||
}
|
||||
|
||||
QStringList Settings::childGroups() const {
|
||||
return _manager->childGroups();
|
||||
}
|
||||
|
||||
QStringList Settings::childKeys() const {
|
||||
return _manager->childKeys();
|
||||
}
|
||||
|
||||
QStringList Settings::allKeys() const {
|
||||
return _manager->allKeys();
|
||||
}
|
||||
|
||||
bool Settings::contains(const QString& key) const {
|
||||
return _manager->contains(key);
|
||||
}
|
||||
|
||||
int Settings::beginReadArray(const QString & prefix) {
|
||||
return _manager->beginReadArray(prefix);
|
||||
}
|
||||
|
||||
void Settings::beginWriteArray(const QString& prefix, int size) {
|
||||
_manager->beginWriteArray(prefix, size);
|
||||
}
|
||||
|
||||
void Settings::endArray() {
|
||||
_manager->endArray();
|
||||
}
|
||||
|
||||
void Settings::setArrayIndex(int i) {
|
||||
_manager->setArrayIndex(i);
|
||||
}
|
||||
|
||||
void Settings::beginGroup(const QString& prefix) {
|
||||
_manager->beginGroup(prefix);
|
||||
}
|
||||
|
||||
void Settings::endGroup() {
|
||||
_manager->endGroup();
|
||||
}
|
||||
|
||||
void Settings::setValue(const QString& name, const QVariant& value) {
|
||||
_manager->setValue(name, value);
|
||||
}
|
||||
|
||||
QVariant Settings::value(const QString& name, const QVariant& defaultValue) const {
|
||||
return _manager->value(name, defaultValue);
|
||||
}
|
||||
|
||||
|
||||
void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
|
||||
const QVariant badDefaultValue = NAN;
|
||||
bool ok = true;
|
||||
|
|
|
@ -14,19 +14,40 @@
|
|||
|
||||
#include <type_traits>
|
||||
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QStack>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include "SettingInterface.h"
|
||||
|
||||
|
||||
// TODO: remove
|
||||
class Settings : public QSettings {
|
||||
class Settings {
|
||||
public:
|
||||
static const QString firstRun;
|
||||
Settings();
|
||||
~Settings();
|
||||
|
||||
void remove(const QString& key);
|
||||
QStringList childGroups() const;
|
||||
QStringList childKeys() const;
|
||||
QStringList allKeys() const;
|
||||
bool contains(const QString& key) const;
|
||||
int beginReadArray(const QString & prefix);
|
||||
void beginWriteArray(const QString& prefix, int size = -1);
|
||||
void endArray();
|
||||
void setArrayIndex(int i);
|
||||
|
||||
void beginGroup(const QString& prefix);
|
||||
void endGroup();
|
||||
|
||||
void setValue(const QString& name, const QVariant& value);
|
||||
QVariant value(const QString& name, const QVariant& defaultValue = QVariant()) const;
|
||||
|
||||
void getFloatValueIfValid(const QString& name, float& floatValue);
|
||||
void getBoolValue(const QString& name, bool& boolValue);
|
||||
|
@ -36,6 +57,9 @@ public:
|
|||
|
||||
void setQuatValue(const QString& name, const glm::quat& quatValue);
|
||||
void getQuatValueIfValid(const QString& name, glm::quat& quatValue);
|
||||
|
||||
QSharedPointer<Setting::Manager> _manager;
|
||||
QWriteLocker _locker;
|
||||
};
|
||||
|
||||
namespace Setting {
|
||||
|
|
|
@ -9,27 +9,33 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "SettingInterface.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
|
||||
#include "PathUtils.h"
|
||||
#include "SettingInterface.h"
|
||||
#include "SettingManager.h"
|
||||
#include "SharedLogging.h"
|
||||
|
||||
namespace Setting {
|
||||
static Manager* privateInstance = nullptr;
|
||||
static QSharedPointer<Manager> globalManager;
|
||||
|
||||
const QString Interface::FIRST_RUN { "firstRun" };
|
||||
|
||||
// cleans up the settings private instance. Should only be run once at closing down.
|
||||
void cleanupPrivateInstance() {
|
||||
// grab the thread before we nuke the instance
|
||||
QThread* settingsManagerThread = privateInstance->thread();
|
||||
|
||||
QThread* settingsManagerThread = DependencyManager::get<Manager>()->thread();
|
||||
|
||||
// tell the private instance to clean itself up on its thread
|
||||
privateInstance->deleteLater();
|
||||
privateInstance = NULL;
|
||||
DependencyManager::destroy<Manager>();
|
||||
|
||||
//
|
||||
globalManager->deleteLater();
|
||||
globalManager.reset();
|
||||
|
||||
// quit the settings manager thread and wait on it to make sure it's gone
|
||||
settingsManagerThread->quit();
|
||||
|
@ -63,14 +69,13 @@ namespace Setting {
|
|||
QThread* thread = new QThread();
|
||||
Q_CHECK_PTR(thread);
|
||||
thread->setObjectName("Settings Thread");
|
||||
|
||||
privateInstance = new Manager();
|
||||
Q_CHECK_PTR(privateInstance);
|
||||
|
||||
QObject::connect(privateInstance, SIGNAL(destroyed()), thread, SLOT(quit()));
|
||||
QObject::connect(thread, SIGNAL(started()), privateInstance, SLOT(startTimer()));
|
||||
globalManager = DependencyManager::set<Manager>();
|
||||
|
||||
QObject::connect(globalManager.data(), SIGNAL(destroyed()), thread, SLOT(quit()));
|
||||
QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer()));
|
||||
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||
privateInstance->moveToThread(thread);
|
||||
globalManager->moveToThread(thread);
|
||||
thread->start();
|
||||
qCDebug(shared) << "Settings thread started.";
|
||||
|
||||
|
@ -79,7 +84,7 @@ namespace Setting {
|
|||
}
|
||||
|
||||
void Interface::init() {
|
||||
if (!privateInstance) {
|
||||
if (!DependencyManager::isSet<Manager>()) {
|
||||
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle
|
||||
// in an assignment-client - the QSettings backing we use for this means persistence of these
|
||||
// settings from an AC (when there can be multiple terminating at same time on one machine)
|
||||
|
@ -87,9 +92,13 @@ namespace Setting {
|
|||
qWarning() << "Setting::Interface::init() for key" << _key << "- Manager not yet created." <<
|
||||
"Settings persistence disabled.";
|
||||
} else {
|
||||
// Register Handle
|
||||
privateInstance->registerHandle(this);
|
||||
_isInitialized = true;
|
||||
_manager = DependencyManager::get<Manager>();
|
||||
auto manager = _manager.lock();
|
||||
if (manager) {
|
||||
// Register Handle
|
||||
manager->registerHandle(this);
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
// Load value from disk
|
||||
load();
|
||||
|
@ -97,11 +106,13 @@ namespace Setting {
|
|||
}
|
||||
|
||||
void Interface::deinit() {
|
||||
if (_isInitialized && privateInstance) {
|
||||
// Save value to disk
|
||||
save();
|
||||
|
||||
privateInstance->removeHandle(_key);
|
||||
if (_isInitialized && _manager) {
|
||||
auto manager = _manager.lock();
|
||||
if (manager) {
|
||||
// Save value to disk
|
||||
save();
|
||||
manager->removeHandle(_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,14 +124,16 @@ namespace Setting {
|
|||
}
|
||||
|
||||
void Interface::save() {
|
||||
if (privateInstance) {
|
||||
privateInstance->saveSetting(this);
|
||||
auto manager = _manager.lock();
|
||||
if (manager) {
|
||||
manager->saveSetting(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::load() {
|
||||
if (privateInstance) {
|
||||
privateInstance->loadSetting(this);
|
||||
auto manager = _manager.lock();
|
||||
if (manager) {
|
||||
manager->loadSetting(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,23 @@
|
|||
#ifndef hifi_SettingInterface_h
|
||||
#define hifi_SettingInterface_h
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <memory>
|
||||
#include <QtCore/QWeakPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
namespace Setting {
|
||||
class Manager;
|
||||
|
||||
void preInit();
|
||||
void init();
|
||||
void cleanupSettings();
|
||||
|
||||
class Interface {
|
||||
public:
|
||||
QString getKey() const { return _key; }
|
||||
static const QString FIRST_RUN;
|
||||
|
||||
const QString& getKey() const { return _key; }
|
||||
bool isSet() const { return _isSet; }
|
||||
|
||||
virtual void setVariant(const QVariant& variant) = 0;
|
||||
|
@ -44,6 +50,8 @@ namespace Setting {
|
|||
const QString _key;
|
||||
|
||||
friend class Manager;
|
||||
|
||||
QWeakPointer<Manager> _manager;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "SettingInterface.h"
|
||||
#include "SettingManager.h"
|
||||
|
||||
namespace Setting {
|
||||
|
||||
Manager::~Manager() {
|
||||
// Cleanup timer
|
||||
stopTimer();
|
||||
|
@ -27,6 +29,10 @@ namespace Setting {
|
|||
// sync will be called in the QSettings destructor
|
||||
}
|
||||
|
||||
// Custom deleter does nothing, because we need to shutdown later than the dependency manager
|
||||
void Manager::customDeleter() { }
|
||||
|
||||
|
||||
void Manager::registerHandle(Setting::Interface* handle) {
|
||||
QString key = handle->getKey();
|
||||
withWriteLock([&] {
|
||||
|
@ -44,15 +50,29 @@ namespace Setting {
|
|||
}
|
||||
|
||||
void Manager::loadSetting(Interface* handle) {
|
||||
handle->setVariant(value(handle->getKey()));
|
||||
const auto& key = handle->getKey();
|
||||
withWriteLock([&] {
|
||||
QVariant loadedValue;
|
||||
if (_pendingChanges.contains(key)) {
|
||||
loadedValue = _pendingChanges[key];
|
||||
} else {
|
||||
loadedValue = value(key);
|
||||
}
|
||||
handle->setVariant(loadedValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Manager::saveSetting(Interface* handle) {
|
||||
const auto& key = handle->getKey();
|
||||
QVariant handleValue = UNSET_VALUE;
|
||||
if (handle->isSet()) {
|
||||
setValue(handle->getKey(), handle->getVariant());
|
||||
} else {
|
||||
remove(handle->getKey());
|
||||
handleValue = handle->getVariant();
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_pendingChanges[key] = handleValue;
|
||||
});
|
||||
}
|
||||
|
||||
static const int SAVE_INTERVAL_MSEC = 5 * 1000; // 5 sec
|
||||
|
@ -74,10 +94,20 @@ namespace Setting {
|
|||
}
|
||||
|
||||
void Manager::saveAll() {
|
||||
withReadLock([&] {
|
||||
for (auto handle : _handles) {
|
||||
saveSetting(handle);
|
||||
withWriteLock([&] {
|
||||
for (auto key : _pendingChanges.keys()) {
|
||||
auto newValue = _pendingChanges[key];
|
||||
auto savedValue = value(key, UNSET_VALUE);
|
||||
if (newValue == savedValue) {
|
||||
continue;
|
||||
}
|
||||
if (newValue == UNSET_VALUE) {
|
||||
remove(key);
|
||||
} else {
|
||||
setValue(key, newValue);
|
||||
}
|
||||
}
|
||||
_pendingChanges.clear();
|
||||
});
|
||||
|
||||
// Restart timer
|
||||
|
|
|
@ -12,17 +12,23 @@
|
|||
#ifndef hifi_SettingManager_h
|
||||
#define hifi_SettingManager_h
|
||||
|
||||
#include <QPointer>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "shared/ReadWriteLockable.h"
|
||||
|
||||
namespace Setting {
|
||||
class Interface;
|
||||
|
||||
class Manager : public QSettings, public ReadWriteLockable {
|
||||
class Manager : public QSettings, public ReadWriteLockable, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void customDeleter() override;
|
||||
|
||||
protected:
|
||||
~Manager();
|
||||
void registerHandle(Interface* handle);
|
||||
|
@ -40,6 +46,8 @@ namespace Setting {
|
|||
private:
|
||||
QHash<QString, Interface*> _handles;
|
||||
QPointer<QTimer> _saveTimer = nullptr;
|
||||
const QVariant UNSET_VALUE { QUuid::createUuid().variant() };
|
||||
QHash<QString, QVariant> _pendingChanges;
|
||||
|
||||
friend class Interface;
|
||||
friend void cleanupPrivateInstance();
|
||||
|
|
|
@ -23,7 +23,6 @@ void ShapeInfo::clear() {
|
|||
|
||||
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
|
||||
_type = type;
|
||||
_points.clear();
|
||||
switch(type) {
|
||||
case SHAPE_TYPE_NONE:
|
||||
_halfExtents = glm::vec3(0.0f);
|
||||
|
@ -52,7 +51,6 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
|||
_url = "";
|
||||
_type = SHAPE_TYPE_BOX;
|
||||
_halfExtents = halfExtents;
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
|
@ -60,15 +58,6 @@ void ShapeInfo::setSphere(float radius) {
|
|||
_url = "";
|
||||
_type = SHAPE_TYPE_SPHERE;
|
||||
_halfExtents = glm::vec3(radius, radius, radius);
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_ELLIPSOID;
|
||||
_halfExtents = halfExtents;
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
|
@ -82,7 +71,6 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
|||
_url = "";
|
||||
_type = SHAPE_TYPE_CAPSULE_Y;
|
||||
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
||||
_points.clear();
|
||||
_doubleHashKey.clear();
|
||||
}
|
||||
|
||||
|
@ -146,10 +134,6 @@ bool ShapeInfo::contains(const glm::vec3& point) const {
|
|||
switch(_type) {
|
||||
case SHAPE_TYPE_SPHERE:
|
||||
return glm::length(point) <= _halfExtents.x;
|
||||
case SHAPE_TYPE_ELLIPSOID: {
|
||||
glm::vec3 scaledPoint = glm::abs(point) / _halfExtents;
|
||||
return glm::length(scaledPoint) <= 1.0f;
|
||||
}
|
||||
case SHAPE_TYPE_CYLINDER_X:
|
||||
return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z;
|
||||
case SHAPE_TYPE_CYLINDER_Y:
|
||||
|
|
|
@ -30,7 +30,6 @@ enum ShapeType {
|
|||
SHAPE_TYPE_NONE,
|
||||
SHAPE_TYPE_BOX,
|
||||
SHAPE_TYPE_SPHERE,
|
||||
SHAPE_TYPE_ELLIPSOID,
|
||||
SHAPE_TYPE_PLANE,
|
||||
SHAPE_TYPE_COMPOUND,
|
||||
SHAPE_TYPE_CAPSULE_X,
|
||||
|
@ -39,7 +38,7 @@ enum ShapeType {
|
|||
SHAPE_TYPE_CYLINDER_X,
|
||||
SHAPE_TYPE_CYLINDER_Y,
|
||||
SHAPE_TYPE_CYLINDER_Z,
|
||||
SHAPE_TYPE_LINE
|
||||
SHAPE_TYPE_STATIC_MESH
|
||||
};
|
||||
|
||||
class ShapeInfo {
|
||||
|
@ -50,7 +49,6 @@ public:
|
|||
void setParams(ShapeType type, const glm::vec3& halfExtents, QString url="");
|
||||
void setBox(const glm::vec3& halfExtents);
|
||||
void setSphere(float radius);
|
||||
void setEllipsoid(const glm::vec3& halfExtents);
|
||||
void setConvexHulls(const QVector<QVector<glm::vec3>>& points);
|
||||
void setCapsuleY(float radius, float halfHeight);
|
||||
void setOffset(const glm::vec3& offset);
|
||||
|
@ -60,10 +58,10 @@ public:
|
|||
const glm::vec3& getHalfExtents() const { return _halfExtents; }
|
||||
const glm::vec3& getOffset() const { return _offset; }
|
||||
|
||||
QVector<QVector<glm::vec3>>& getPoints() { return _points; }
|
||||
const QVector<QVector<glm::vec3>>& getPoints() const { return _points; }
|
||||
uint32_t getNumSubShapes() const;
|
||||
|
||||
void clearPoints () { _points.clear(); }
|
||||
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
|
||||
int getMaxNumPoints() const;
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ public:
|
|||
template <typename F>
|
||||
bool withTryReadLock(F&& f, int timeout) const;
|
||||
|
||||
QReadWriteLock& getLock() const { return _lock; }
|
||||
|
||||
private:
|
||||
mutable QReadWriteLock _lock { QReadWriteLock::Recursive };
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ void OculusBaseDisplayPlugin::resetSensors() {
|
|||
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity
|
||||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
_currentRenderFrameInfo = FrameInfo();
|
||||
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();;
|
||||
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
|
||||
|
@ -26,6 +26,7 @@ void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
|||
_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
|
||||
Lock lock(_mutex);
|
||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OculusBaseDisplayPlugin::isSupported() const {
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
||||
using Parent = HmdDisplayPlugin;
|
||||
public:
|
||||
virtual bool isSupported() const override;
|
||||
bool isSupported() const override;
|
||||
|
||||
// Stereo specific methods
|
||||
virtual void resetSensors() override final;
|
||||
virtual void beginFrameRender(uint32_t frameIndex) override;
|
||||
void resetSensors() override final;
|
||||
bool beginFrameRender(uint32_t frameIndex) override;
|
||||
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
|
||||
|
||||
|
||||
|
|
|
@ -40,13 +40,14 @@ void OculusLegacyDisplayPlugin::resetSensors() {
|
|||
ovrHmd_RecenterPose(_hmd);
|
||||
}
|
||||
|
||||
void OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
bool OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
_currentRenderFrameInfo = FrameInfo();
|
||||
_currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
||||
_trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime);
|
||||
_currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose);
|
||||
Lock lock(_mutex);
|
||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OculusLegacyDisplayPlugin::isSupported() const {
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
|
||||
// Stereo specific methods
|
||||
void resetSensors() override;
|
||||
void beginFrameRender(uint32_t frameIndex) override;
|
||||
bool beginFrameRender(uint32_t frameIndex) override;
|
||||
|
||||
float getTargetFrameRate() const override;
|
||||
|
||||
|
|
|
@ -126,7 +126,12 @@ void OpenVrDisplayPlugin::resetSensors() {
|
|||
}
|
||||
|
||||
|
||||
void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||
handleOpenVrEvents();
|
||||
if (openVrQuitRequested()) {
|
||||
emit outputDeviceLost();
|
||||
return false;
|
||||
}
|
||||
double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
|
||||
double frameDuration = 1.f / displayFrequency;
|
||||
double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
||||
|
@ -153,6 +158,7 @@ void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
|||
|
||||
Lock lock(_mutex);
|
||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::hmdPresent() {
|
||||
|
|
|
@ -18,16 +18,16 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p
|
|||
class OpenVrDisplayPlugin : public HmdDisplayPlugin {
|
||||
using Parent = HmdDisplayPlugin;
|
||||
public:
|
||||
virtual bool isSupported() const override;
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
bool isSupported() const override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
virtual float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
|
||||
float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
|
||||
|
||||
virtual void customizeContext() override;
|
||||
void customizeContext() override;
|
||||
|
||||
// Stereo specific methods
|
||||
virtual void resetSensors() override;
|
||||
virtual void beginFrameRender(uint32_t frameIndex) override;
|
||||
void resetSensors() override;
|
||||
bool beginFrameRender(uint32_t frameIndex) override;
|
||||
void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -26,6 +26,11 @@ using Lock = std::unique_lock<Mutex>;
|
|||
static int refCount { 0 };
|
||||
static Mutex mutex;
|
||||
static vr::IVRSystem* activeHmd { nullptr };
|
||||
static bool _openVrQuitRequested { false };
|
||||
|
||||
bool openVrQuitRequested() {
|
||||
return _openVrQuitRequested;
|
||||
}
|
||||
|
||||
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
|
||||
|
||||
|
@ -56,17 +61,17 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
|||
if (hmdPresent) {
|
||||
Lock lock(mutex);
|
||||
if (!activeHmd) {
|
||||
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
|
||||
qCDebug(displayplugins) << "OpenVR: No vr::IVRSystem instance active, building";
|
||||
vr::EVRInitError eError = vr::VRInitError_None;
|
||||
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
||||
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
|
||||
qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;
|
||||
}
|
||||
if (activeHmd) {
|
||||
qCDebug(displayplugins) << "openvr: incrementing refcount";
|
||||
qCDebug(displayplugins) << "OpenVR: incrementing refcount";
|
||||
++refCount;
|
||||
}
|
||||
} else {
|
||||
qCDebug(displayplugins) << "openvr: no hmd present";
|
||||
qCDebug(displayplugins) << "OpenVR: no hmd present";
|
||||
}
|
||||
return activeHmd;
|
||||
}
|
||||
|
@ -74,12 +79,38 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
|||
void releaseOpenVrSystem() {
|
||||
if (activeHmd) {
|
||||
Lock lock(mutex);
|
||||
qCDebug(displayplugins) << "openvr: decrementing refcount";
|
||||
qCDebug(displayplugins) << "OpenVR: decrementing refcount";
|
||||
--refCount;
|
||||
if (0 == refCount) {
|
||||
qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system";
|
||||
qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
|
||||
vr::VR_Shutdown();
|
||||
_openVrQuitRequested = false;
|
||||
activeHmd = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleOpenVrEvents() {
|
||||
if (!activeHmd) {
|
||||
return;
|
||||
}
|
||||
Lock lock(mutex);
|
||||
if (!activeHmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
vr::VREvent_t event;
|
||||
while (activeHmd->PollNextEvent(&event, sizeof(event))) {
|
||||
switch (event.eventType) {
|
||||
case vr::VREvent_Quit:
|
||||
_openVrQuitRequested = true;
|
||||
activeHmd->AcknowledgeQuit_Exiting();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qDebug() << "OpenVR: Event " << event.eventType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ bool openVrSupported();
|
|||
|
||||
vr::IVRSystem* acquireOpenVrSystem();
|
||||
void releaseOpenVrSystem();
|
||||
void handleOpenVrEvents();
|
||||
bool openVrQuitRequested();
|
||||
|
||||
template<typename F>
|
||||
void openvr_for_each_eye(F f) {
|
||||
|
|
|
@ -214,6 +214,11 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
|
|||
|
||||
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
handleOpenVrEvents();
|
||||
if (openVrQuitRequested()) {
|
||||
deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
// because update mutates the internal state we need to lock
|
||||
userInputMapper->withLock([&, this]() {
|
||||
|
|
527
scripts/system/controllers/leapHands.js
Normal file
527
scripts/system/controllers/leapHands.js
Normal file
|
@ -0,0 +1,527 @@
|
|||
//
|
||||
// leapHands.js
|
||||
// examples
|
||||
//
|
||||
// Created by David Rowe on 8 Sep 2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that uses the Leap Motion to make the avatar's hands replicate the user's hand actions.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var leftTriggerValue = 0;
|
||||
var rightTriggerValue = 0;
|
||||
|
||||
var LEAP_TRIGGER_START_ANGLE = 15.0;
|
||||
var LEAP_TRIGGER_END_ANGLE = 40.0;
|
||||
|
||||
function getLeapMotionLeftTrigger() {
|
||||
//print("left trigger = " + leftTriggerValue);
|
||||
return leftTriggerValue;
|
||||
}
|
||||
function getLeapMotionRightTrigger() {
|
||||
//print("right trigger = " + rightTriggerValue);
|
||||
return rightTriggerValue;
|
||||
}
|
||||
|
||||
var leapHands = (function () {
|
||||
|
||||
var isOnHMD,
|
||||
LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD",
|
||||
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
|
||||
HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
|
||||
hasHandAndWristJoints,
|
||||
handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position
|
||||
HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle
|
||||
handAnimationStateHandlers,
|
||||
handAnimationStateFunctions,
|
||||
handAnimationStateProperties,
|
||||
hands,
|
||||
wrists,
|
||||
NUM_HANDS = 2, // 0 = left; 1 = right
|
||||
fingers,
|
||||
NUM_FINGERS = 5, // 0 = thumb; ...; 4 = pinky
|
||||
THUMB = 0,
|
||||
MIDDLE_FINGER = 2,
|
||||
NUM_FINGER_JOINTS = 3, // 0 = metacarpal(hand)-proximal(finger) joint; ...; 2 = intermediate-distal joint
|
||||
MAX_HAND_INACTIVE_COUNT = 20,
|
||||
calibrationStatus,
|
||||
UNCALIBRATED = 0,
|
||||
CALIBRATING = 1,
|
||||
CALIBRATED = 2,
|
||||
CALIBRATION_TIME = 1000, // milliseconds
|
||||
avatarScale,
|
||||
avatarFaceModelURL,
|
||||
avatarSkeletonModelURL,
|
||||
settingsTimer,
|
||||
HMD_CAMERA_TO_AVATAR_ROTATION = [
|
||||
Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }),
|
||||
Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 })
|
||||
],
|
||||
DESKTOP_CAMERA_TO_AVATAR_ROTATION =
|
||||
Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })),
|
||||
LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)];
|
||||
|
||||
function printSkeletonJointNames() {
|
||||
var jointNames,
|
||||
i;
|
||||
|
||||
print(MyAvatar.skeletonModelURL);
|
||||
|
||||
print("Skeleton joint names ...");
|
||||
jointNames = MyAvatar.getJointNames();
|
||||
for (i = 0; i < jointNames.length; i += 1) {
|
||||
print(i + ": " + jointNames[i]);
|
||||
}
|
||||
print("... skeleton joint names");
|
||||
}
|
||||
|
||||
function animateLeftHand() {
|
||||
var ROTATION_AND_POSITION = 0;
|
||||
|
||||
return {
|
||||
leftHandType: ROTATION_AND_POSITION,
|
||||
leftHandPosition: hands[0].position,
|
||||
leftHandRotation: hands[0].rotation
|
||||
};
|
||||
}
|
||||
|
||||
function animateRightHand() {
|
||||
var ROTATION_AND_POSITION = 0;
|
||||
|
||||
return {
|
||||
rightHandType: ROTATION_AND_POSITION,
|
||||
rightHandPosition: hands[1].position,
|
||||
rightHandRotation: hands[1].rotation
|
||||
};
|
||||
}
|
||||
|
||||
function finishCalibration() {
|
||||
var avatarPosition,
|
||||
handPosition,
|
||||
middleFingerPosition,
|
||||
leapHandHeight,
|
||||
h;
|
||||
|
||||
if (!isOnHMD) {
|
||||
if (hands[0].controller.isActive() && hands[1].controller.isActive()) {
|
||||
leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0;
|
||||
} else {
|
||||
calibrationStatus = UNCALIBRATED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
avatarPosition = MyAvatar.position;
|
||||
|
||||
for (h = 0; h < NUM_HANDS; h += 1) {
|
||||
handPosition = MyAvatar.getJointPosition(hands[h].jointName);
|
||||
if (!hasHandAndWristJoints) {
|
||||
middleFingerPosition = MyAvatar.getJointPosition(fingers[h][MIDDLE_FINGER][0].jointName);
|
||||
handToWristOffset[h] = Vec3.multiply(Vec3.subtract(handPosition, middleFingerPosition), 1.0 - HAND_OFFSET);
|
||||
}
|
||||
|
||||
if (isOnHMD) {
|
||||
// Offset of Leap Motion origin from physical eye position
|
||||
hands[h].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET };
|
||||
} else {
|
||||
hands[h].zeroPosition = {
|
||||
x: handPosition.x - avatarPosition.x,
|
||||
y: handPosition.y - avatarPosition.y,
|
||||
z: avatarPosition.z - handPosition.z
|
||||
};
|
||||
hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition);
|
||||
hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight;
|
||||
}
|
||||
}
|
||||
|
||||
MyAvatar.clearJointData("LeftHand");
|
||||
MyAvatar.clearJointData("LeftForeArm");
|
||||
MyAvatar.clearJointData("RightHand");
|
||||
MyAvatar.clearJointData("RightForeArm");
|
||||
|
||||
calibrationStatus = CALIBRATED;
|
||||
print("Leap Motion: Calibrated");
|
||||
}
|
||||
|
||||
function calibrate() {
|
||||
var jointNames,
|
||||
i;
|
||||
|
||||
calibrationStatus = CALIBRATING;
|
||||
|
||||
avatarScale = MyAvatar.scale;
|
||||
avatarFaceModelURL = MyAvatar.faceModelURL;
|
||||
avatarSkeletonModelURL = MyAvatar.skeletonModelURL;
|
||||
|
||||
// Does this skeleton have both wrist and hand joints?
|
||||
hasHandAndWristJoints = false;
|
||||
jointNames = MyAvatar.getJointNames();
|
||||
for (i = 0; i < jointNames.length; i += 1) {
|
||||
hasHandAndWristJoints = hasHandAndWristJoints || jointNames[i].toLowerCase() === "leftwrist";
|
||||
}
|
||||
|
||||
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
|
||||
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0));
|
||||
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0));
|
||||
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0));
|
||||
|
||||
// Wait for arms to assume their positions before calculating
|
||||
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
|
||||
}
|
||||
|
||||
function checkCalibration() {
|
||||
|
||||
if (calibrationStatus === CALIBRATED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (calibrationStatus !== CALIBRATING) {
|
||||
calibrate();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setIsOnHMD() {
|
||||
isOnHMD = Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM);
|
||||
print("Leap Motion: " + (isOnHMD ? "Is on HMD" : "Is on desk"));
|
||||
}
|
||||
|
||||
function checkSettings() {
|
||||
if (calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale
|
||||
|| MyAvatar.faceModelURL !== avatarFaceModelURL
|
||||
|| MyAvatar.skeletonModelURL !== avatarSkeletonModelURL
|
||||
|| Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM) !== isOnHMD)) {
|
||||
print("Leap Motion: Recalibrate...");
|
||||
calibrationStatus = UNCALIBRATED;
|
||||
|
||||
setIsOnHMD();
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
|
||||
wrists = [
|
||||
{
|
||||
jointName: "LeftWrist",
|
||||
controller: Controller.createInputController("Spatial", "joint_L_wrist")
|
||||
},
|
||||
{
|
||||
jointName: "RightWrist",
|
||||
controller: Controller.createInputController("Spatial", "joint_R_wrist")
|
||||
}
|
||||
];
|
||||
|
||||
hands = [
|
||||
{
|
||||
jointName: "LeftHand",
|
||||
controller: Controller.createInputController("Spatial", "joint_L_hand"),
|
||||
inactiveCount: 0
|
||||
},
|
||||
{
|
||||
jointName: "RightHand",
|
||||
controller: Controller.createInputController("Spatial", "joint_R_hand"),
|
||||
inactiveCount: 0
|
||||
}
|
||||
];
|
||||
|
||||
// The Leap controller's first joint is the hand-metacarpal joint but this joint's data is not used because it's too
|
||||
// dependent on the model skeleton exactly matching the Leap skeleton; using just the second and subsequent joints
|
||||
// seems to work better over all.
|
||||
fingers = [{}, {}];
|
||||
fingers[0] = [
|
||||
[
|
||||
{ jointName: "LeftHandThumb1", controller: Controller.createInputController("Spatial", "joint_L_thumb2") },
|
||||
{ jointName: "LeftHandThumb2", controller: Controller.createInputController("Spatial", "joint_L_thumb3") },
|
||||
{ jointName: "LeftHandThumb3", controller: Controller.createInputController("Spatial", "joint_L_thumb4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "LeftHandIndex1", controller: Controller.createInputController("Spatial", "joint_L_index2") },
|
||||
{ jointName: "LeftHandIndex2", controller: Controller.createInputController("Spatial", "joint_L_index3") },
|
||||
{ jointName: "LeftHandIndex3", controller: Controller.createInputController("Spatial", "joint_L_index4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "LeftHandMiddle1", controller: Controller.createInputController("Spatial", "joint_L_middle2") },
|
||||
{ jointName: "LeftHandMiddle2", controller: Controller.createInputController("Spatial", "joint_L_middle3") },
|
||||
{ jointName: "LeftHandMiddle3", controller: Controller.createInputController("Spatial", "joint_L_middle4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "LeftHandRing1", controller: Controller.createInputController("Spatial", "joint_L_ring2") },
|
||||
{ jointName: "LeftHandRing2", controller: Controller.createInputController("Spatial", "joint_L_ring3") },
|
||||
{ jointName: "LeftHandRing3", controller: Controller.createInputController("Spatial", "joint_L_ring4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "LeftHandPinky1", controller: Controller.createInputController("Spatial", "joint_L_pinky2") },
|
||||
{ jointName: "LeftHandPinky2", controller: Controller.createInputController("Spatial", "joint_L_pinky3") },
|
||||
{ jointName: "LeftHandPinky3", controller: Controller.createInputController("Spatial", "joint_L_pinky4") }
|
||||
]
|
||||
];
|
||||
fingers[1] = [
|
||||
[
|
||||
{ jointName: "RightHandThumb1", controller: Controller.createInputController("Spatial", "joint_R_thumb2") },
|
||||
{ jointName: "RightHandThumb2", controller: Controller.createInputController("Spatial", "joint_R_thumb3") },
|
||||
{ jointName: "RightHandThumb3", controller: Controller.createInputController("Spatial", "joint_R_thumb4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "RightHandIndex1", controller: Controller.createInputController("Spatial", "joint_R_index2") },
|
||||
{ jointName: "RightHandIndex2", controller: Controller.createInputController("Spatial", "joint_R_index3") },
|
||||
{ jointName: "RightHandIndex3", controller: Controller.createInputController("Spatial", "joint_R_index4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "RightHandMiddle1", controller: Controller.createInputController("Spatial", "joint_R_middle2") },
|
||||
{ jointName: "RightHandMiddle2", controller: Controller.createInputController("Spatial", "joint_R_middle3") },
|
||||
{ jointName: "RightHandMiddle3", controller: Controller.createInputController("Spatial", "joint_R_middle4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "RightHandRing1", controller: Controller.createInputController("Spatial", "joint_R_ring2") },
|
||||
{ jointName: "RightHandRing2", controller: Controller.createInputController("Spatial", "joint_R_ring3") },
|
||||
{ jointName: "RightHandRing3", controller: Controller.createInputController("Spatial", "joint_R_ring4") }
|
||||
],
|
||||
[
|
||||
{ jointName: "RightHandPinky1", controller: Controller.createInputController("Spatial", "joint_R_pinky2") },
|
||||
{ jointName: "RightHandPinky2", controller: Controller.createInputController("Spatial", "joint_R_pinky3") },
|
||||
{ jointName: "RightHandPinky3", controller: Controller.createInputController("Spatial", "joint_R_pinky4") }
|
||||
]
|
||||
];
|
||||
|
||||
handAnimationStateHandlers = [null, null];
|
||||
handAnimationStateFunctions = [animateLeftHand, animateRightHand];
|
||||
handAnimationStateProperties = [
|
||||
["leftHandType", "leftHandPosition", "leftHandRotation"],
|
||||
["rightHandType", "rightHandPosition", "rightHandPosition"]
|
||||
];
|
||||
|
||||
setIsOnHMD();
|
||||
|
||||
settingsTimer = Script.setInterval(checkSettings, 2000);
|
||||
|
||||
calibrationStatus = UNCALIBRATED;
|
||||
|
||||
{
|
||||
var mapping = Controller.newMapping("LeapmotionTrigger");
|
||||
mapping.from(getLeapMotionLeftTrigger).to(Controller.Standard.LT);
|
||||
mapping.from(getLeapMotionRightTrigger).to(Controller.Standard.RT);
|
||||
mapping.enable();
|
||||
}
|
||||
}
|
||||
|
||||
function moveHands() {
|
||||
var h,
|
||||
i,
|
||||
j,
|
||||
side,
|
||||
handOffset,
|
||||
wristOffset,
|
||||
handRotation,
|
||||
locRotation,
|
||||
cameraOrientation,
|
||||
inverseAvatarOrientation;
|
||||
|
||||
for (h = 0; h < NUM_HANDS; h += 1) {
|
||||
side = h === 0 ? -1.0 : 1.0;
|
||||
|
||||
if (hands[h].controller.isActive()) {
|
||||
|
||||
// Calibrate if necessary.
|
||||
if (!checkCalibration()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hand animation handlers ...
|
||||
if (handAnimationStateHandlers[h] === null) {
|
||||
handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h],
|
||||
handAnimationStateProperties[h]);
|
||||
}
|
||||
|
||||
// Hand position ...
|
||||
handOffset = hands[h].controller.getAbsTranslation();
|
||||
handRotation = hands[h].controller.getAbsRotation();
|
||||
|
||||
if (isOnHMD) {
|
||||
|
||||
// Adjust to control wrist position if "hand" joint is at wrist ...
|
||||
if (!hasHandAndWristJoints) {
|
||||
wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]);
|
||||
handOffset = Vec3.sum(handOffset, wristOffset);
|
||||
}
|
||||
|
||||
// Hand offset in camera coordinates ...
|
||||
handOffset = {
|
||||
x: -handOffset.x,
|
||||
y: -handOffset.z,
|
||||
z: -handOffset.y - hands[h].zeroPosition.z
|
||||
};
|
||||
|
||||
// Hand offset in world coordinates ...
|
||||
cameraOrientation = Camera.getOrientation();
|
||||
handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset));
|
||||
|
||||
// Hand offset in avatar coordinates ...
|
||||
inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation);
|
||||
handOffset = Vec3.subtract(handOffset, MyAvatar.position);
|
||||
handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset);
|
||||
handOffset.z = -handOffset.z;
|
||||
handOffset.x = -handOffset.x;
|
||||
|
||||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: -handRotation.y,
|
||||
y: -handRotation.z,
|
||||
z: -handRotation.x,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation);
|
||||
cameraOrientation = {
|
||||
x: cameraOrientation.z,
|
||||
y: cameraOrientation.y,
|
||||
z: cameraOrientation.x,
|
||||
w: cameraOrientation.w
|
||||
};
|
||||
cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation));
|
||||
handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!!
|
||||
|
||||
} else {
|
||||
|
||||
// Adjust to control wrist position if "hand" joint is at wrist ...
|
||||
if (!hasHandAndWristJoints) {
|
||||
wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]);
|
||||
handOffset = Vec3.sum(handOffset, wristOffset);
|
||||
}
|
||||
|
||||
// Hand offset in camera coordinates ...
|
||||
handOffset = {
|
||||
x: -handOffset.x,
|
||||
y: hands[h].zeroPosition.y + handOffset.y,
|
||||
z: hands[h].zeroPosition.z - handOffset.z
|
||||
};
|
||||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: handRotation.z,
|
||||
y: handRotation.y,
|
||||
z: handRotation.x,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation);
|
||||
}
|
||||
|
||||
// Set hand position and orientation for animation state handler ...
|
||||
hands[h].position = handOffset;
|
||||
hands[h].rotation = handRotation;
|
||||
|
||||
// Set finger joints ...
|
||||
var summed = 0;
|
||||
var closeAngle = 0;
|
||||
for (i = 0; i < NUM_FINGERS; i += 1) {
|
||||
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
|
||||
if (fingers[h][i][j].controller !== null) {
|
||||
locRotation = fingers[h][i][j].controller.getLocRotation();
|
||||
var eulers = Quat.safeEulerAngles(locRotation);
|
||||
closeAngle += eulers.x;
|
||||
|
||||
summed++;
|
||||
|
||||
if (i === THUMB) {
|
||||
locRotation = {
|
||||
x: side * locRotation.y,
|
||||
y: side * -locRotation.z,
|
||||
z: side * -locRotation.x,
|
||||
w: locRotation.w
|
||||
};
|
||||
if (j === 0) {
|
||||
// Adjust avatar thumb root joint rotation to make avatar hands look better
|
||||
locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation);
|
||||
}
|
||||
} else {
|
||||
locRotation = {
|
||||
x: -locRotation.x,
|
||||
y: -locRotation.z,
|
||||
z: -locRotation.y,
|
||||
w: locRotation.w
|
||||
};
|
||||
}
|
||||
MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hands[h].inactiveCount = 0;
|
||||
if (summed > 0) {
|
||||
closeAngle /= summed;
|
||||
}
|
||||
|
||||
var triggerValue = (-closeAngle - LEAP_TRIGGER_START_ANGLE) / (LEAP_TRIGGER_END_ANGLE - LEAP_TRIGGER_START_ANGLE);
|
||||
triggerValue = Math.max(0.0, Math.min(triggerValue, 1.0));
|
||||
|
||||
if (h == 0) {
|
||||
leftTriggerValue = triggerValue;
|
||||
} else {
|
||||
rightTriggerValue = triggerValue;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (hands[h].inactiveCount < MAX_HAND_INACTIVE_COUNT) {
|
||||
|
||||
hands[h].inactiveCount += 1;
|
||||
|
||||
if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) {
|
||||
if (handAnimationStateHandlers[h] !== null) {
|
||||
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
|
||||
handAnimationStateHandlers[h] = null;
|
||||
leftTriggerValue = 0.0;
|
||||
rightTriggerValue = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
var h,
|
||||
i,
|
||||
j;
|
||||
|
||||
Script.clearInterval(settingsTimer);
|
||||
|
||||
for (h = 0; h < NUM_HANDS; h += 1) {
|
||||
Controller.releaseInputController(hands[h].controller);
|
||||
Controller.releaseInputController(wrists[h].controller);
|
||||
if (handAnimationStateHandlers[h] !== null) {
|
||||
MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]);
|
||||
}
|
||||
for (i = 0; i < NUM_FINGERS; i += 1) {
|
||||
for (j = 0; j < NUM_FINGER_JOINTS; j += 1) {
|
||||
if (fingers[h][i][j].controller !== null) {
|
||||
Controller.releaseInputController(fingers[h][i][j].controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
printSkeletonJointNames: printSkeletonJointNames,
|
||||
setUp : setUp,
|
||||
moveHands : moveHands,
|
||||
tearDown : tearDown
|
||||
};
|
||||
}());
|
||||
|
||||
|
||||
//leapHands.printSkeletonJointNames();
|
||||
|
||||
leapHands.setUp();
|
||||
Script.update.connect(leapHands.moveHands);
|
||||
Script.scriptEnding.connect(leapHands.tearDown);
|
|
@ -256,7 +256,6 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
y: y - ToolBar.SPACING
|
||||
});
|
||||
}
|
||||
//this.save();
|
||||
}
|
||||
|
||||
this.setAlpha = function(alpha, tool) {
|
||||
|
@ -421,6 +420,9 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
|
|||
for (var tool in that.tools) {
|
||||
that.tools[tool].buttonDown(false);
|
||||
}
|
||||
if (that.mightBeDragging) {
|
||||
that.save();
|
||||
}
|
||||
}
|
||||
this.mouseMove = function (event) {
|
||||
if (!that.mightBeDragging || !event.isLeftButton) {
|
||||
|
|
Loading…
Reference in a new issue