mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 15:35:30 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into QmlMarketplace
This commit is contained in:
commit
6123c4ec96
29 changed files with 238 additions and 166 deletions
assignment-client/src/audio
interface
libraries
animation/src
avatars/src
fbx/src
physics/src
plugins/src/plugins
scripts/system/html/js
tools/nitpick
|
@ -68,6 +68,13 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
// hash the available codecs (on the mixer)
|
||||
_availableCodecs.clear(); // Make sure struct is clean
|
||||
auto pluginManager = DependencyManager::set<PluginManager>();
|
||||
// Only load codec plugins; for now assume codec plugins have 'codec' in their name.
|
||||
auto codecPluginFilter = [](const QJsonObject& metaData) {
|
||||
QJsonValue nameValue = metaData["MetaData"]["name"];
|
||||
return nameValue.toString().contains("codec", Qt::CaseInsensitive);
|
||||
};
|
||||
pluginManager->setPluginFilter(codecPluginFilter);
|
||||
|
||||
auto codecPlugins = pluginManager->getCodecPlugins();
|
||||
for_each(codecPlugins.cbegin(), codecPlugins.cend(),
|
||||
[&](const CodecPluginPointer& codec) {
|
||||
|
|
|
@ -54,7 +54,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "LoginDialog/images/background.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 960 KiB |
BIN
interface/resources/qml/LoginDialog/images/background.png
Normal file
BIN
interface/resources/qml/LoginDialog/images/background.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 2.2 MiB |
Binary file not shown.
Before ![]() (image error) Size: 272 KiB |
BIN
interface/resources/qml/LoginDialog/images/background_tablet.png
Normal file
BIN
interface/resources/qml/LoginDialog/images/background_tablet.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 456 KiB |
|
@ -55,7 +55,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "LoginDialog/images/background.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "LoginDialog/images/background.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ FocusScope {
|
|||
Image {
|
||||
z: -10
|
||||
id: loginDialogBackground
|
||||
source: "../LoginDialog/images/background_tablet.jpg"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: "../LoginDialog/images/background_tablet.png"
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ Windows.ScrollingWindow {
|
|||
}
|
||||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
|
|
|
@ -148,7 +148,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
function canAddToWorld(path) {
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i];
|
||||
var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i, /\.jpg\b/i, /\.png\b/i, /\.gltf\b/i];
|
||||
|
||||
if (selectedItemCount > 1) {
|
||||
return false;
|
||||
|
|
2
interface/src/avatar/AvatarManager.cpp
Normal file → Executable file
2
interface/src/avatar/AvatarManager.cpp
Normal file → Executable file
|
@ -270,7 +270,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (avatar->getSkeletonModel()->isLoaded()) {
|
||||
// remove the orb if it is there
|
||||
avatar->removeOrb();
|
||||
avatar->updateCollisionGroup(_myAvatar->getOtherAvatarsCollisionsEnabled());
|
||||
if (avatar->needsPhysicsUpdate()) {
|
||||
_avatarsToChangeInPhysics.insert(avatar);
|
||||
}
|
||||
|
@ -376,7 +375,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
|
||||
|
||||
auto otherAvatar = new OtherAvatar(qApp->thread());
|
||||
otherAvatar->setSessionUUID(sessionUUID);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
|
6
interface/src/avatar/AvatarMotionState.cpp
Normal file → Executable file
6
interface/src/avatar/AvatarMotionState.cpp
Normal file → Executable file
|
@ -15,7 +15,6 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <PhysicsHelpers.h>
|
||||
|
||||
|
||||
AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
assert(_avatar);
|
||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
|
@ -172,7 +171,10 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
// virtual
|
||||
void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
|
||||
group = _collisionGroup;
|
||||
mask = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0 : Physics::getDefaultCollisionMask(group);
|
||||
mask = Physics::getDefaultCollisionMask(group);
|
||||
if (!_avatar->getCollideWithOtherAvatars()) {
|
||||
mask &= ~(BULLET_COLLISION_GROUP_MY_AVATAR | BULLET_COLLISION_GROUP_OTHER_AVATAR);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -205,12 +205,12 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
if (recordingInterface->getPlayFromCurrentLocation()) {
|
||||
setRecordingBasis();
|
||||
}
|
||||
_previousCollisionGroup = _characterController.computeCollisionGroup();
|
||||
_previousCollisionMask = _characterController.computeCollisionMask();
|
||||
_characterController.setCollisionless(true);
|
||||
} else {
|
||||
clearRecordingBasis();
|
||||
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
|
||||
if (_previousCollisionGroup != BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
_characterController.setCollisionless(false);
|
||||
}
|
||||
}
|
||||
|
@ -2534,7 +2534,7 @@ void MyAvatar::updateMotors() {
|
|||
float verticalMotorTimescale;
|
||||
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
verticalMotorTimescale = FLYING_MOTOR_TIMESCALE;
|
||||
} else {
|
||||
|
@ -2544,7 +2544,7 @@ void MyAvatar::updateMotors() {
|
|||
|
||||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
} else {
|
||||
// non-hovering = walking: follow camera twist about vertical but not lift
|
||||
|
@ -2599,7 +2599,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
qDebug() << "Warning: getParentVelocity failed" << getID();
|
||||
parentVelocity = glm::vec3();
|
||||
}
|
||||
_characterController.handleChangedCollisionGroup();
|
||||
_characterController.handleChangedCollisionMask();
|
||||
_characterController.setParentVelocity(parentVelocity);
|
||||
_characterController.setScaleFactor(getSensorToWorldScale());
|
||||
|
||||
|
@ -3279,7 +3279,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
head->setBaseRoll(ROLL(euler));
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
+ getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT);
|
||||
head->setBaseRoll(0.0f);
|
||||
}
|
||||
|
@ -3325,7 +3325,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
|
|||
|
||||
glm::vec3 direction = forward + right;
|
||||
if (state == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
|
||||
direction += up;
|
||||
}
|
||||
|
@ -3881,7 +3881,7 @@ void MyAvatar::setCollisionsEnabled(bool enabled) {
|
|||
bool MyAvatar::getCollisionsEnabled() {
|
||||
// may return 'false' even though the collisionless option was requested
|
||||
// because the zone may disallow collisionless avatars
|
||||
return _characterController.computeCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
return _characterController.computeCollisionMask() != BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||
}
|
||||
|
||||
void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
|
||||
|
@ -3890,7 +3890,11 @@ void MyAvatar::setOtherAvatarsCollisionsEnabled(bool enabled) {
|
|||
QMetaObject::invokeMethod(this, "setOtherAvatarsCollisionsEnabled", Q_ARG(bool, enabled));
|
||||
return;
|
||||
}
|
||||
bool change = _collideWithOtherAvatars != enabled;
|
||||
_collideWithOtherAvatars = enabled;
|
||||
if (change) {
|
||||
setCollisionWithOtherAvatarsFlags();
|
||||
}
|
||||
emit otherAvatarsCollisionsEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
|
@ -3898,6 +3902,11 @@ bool MyAvatar::getOtherAvatarsCollisionsEnabled() {
|
|||
return _collideWithOtherAvatars;
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||
_characterController.setCollideWithOtherAvatars(_collideWithOtherAvatars);
|
||||
_characterController.setPendingFlagsUpdateCollisionMask();
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionCapsuleCache() {
|
||||
glm::vec3 start, end;
|
||||
float radius;
|
||||
|
|
4
interface/src/avatar/MyAvatar.h
Normal file → Executable file
4
interface/src/avatar/MyAvatar.h
Normal file → Executable file
|
@ -297,6 +297,8 @@ public:
|
|||
|
||||
void reset(bool andRecenter = false, bool andReload = true, bool andHead = true);
|
||||
|
||||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.resetSensorsAndBody
|
||||
*/
|
||||
|
@ -1732,7 +1734,7 @@ private:
|
|||
SharedSoundPointer _collisionSound;
|
||||
|
||||
MyCharacterController _characterController;
|
||||
int32_t _previousCollisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR };
|
||||
int32_t _previousCollisionMask { BULLET_COLLISION_MASK_MY_AVATAR };
|
||||
|
||||
AvatarWeakPointer _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
|
|
|
@ -202,6 +202,29 @@ bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm:
|
|||
return result.hitFraction < 1.0f;
|
||||
}
|
||||
|
||||
int32_t MyCharacterController::computeCollisionMask() const {
|
||||
int32_t collisionMask = BULLET_COLLISION_MASK_MY_AVATAR;
|
||||
if (_collisionless && _collisionlessAllowed) {
|
||||
collisionMask = BULLET_COLLISION_MASK_COLLISIONLESS;
|
||||
} else if (!_collideWithOtherAvatars) {
|
||||
collisionMask &= ~BULLET_COLLISION_GROUP_OTHER_AVATAR;
|
||||
}
|
||||
return collisionMask;
|
||||
}
|
||||
|
||||
void MyCharacterController::handleChangedCollisionMask() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_MASK) {
|
||||
// ATM the easiest way to update collision groups/masks is to remove/re-add the RigidBody
|
||||
if (_dynamicsWorld) {
|
||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, collisionMask);
|
||||
}
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
updateCurrentGravity();
|
||||
}
|
||||
}
|
||||
|
||||
btConvexHullShape* MyCharacterController::computeShape() const {
|
||||
// HACK: the avatar collides using convex hull with a collision margin equal to
|
||||
// the old capsule radius. Two points define a capsule and additional points are
|
||||
|
|
6
interface/src/avatar/MyCharacterController.h
Normal file → Executable file
6
interface/src/avatar/MyCharacterController.h
Normal file → Executable file
|
@ -42,6 +42,12 @@ public:
|
|||
|
||||
void setDensity(btScalar density) { _density = density; }
|
||||
|
||||
int32_t computeCollisionMask() const override;
|
||||
void handleChangedCollisionMask() override;
|
||||
|
||||
bool _collideWithOtherAvatars{ true };
|
||||
void setCollideWithOtherAvatars(bool collideWithOtherAvatars) { _collideWithOtherAvatars = collideWithOtherAvatars; }
|
||||
|
||||
protected:
|
||||
void initRayShotgun(const btCollisionWorld* world);
|
||||
void updateMassProperties() override;
|
||||
|
|
4
interface/src/avatar/MySkeletonModel.cpp
Normal file → Executable file
4
interface/src/avatar/MySkeletonModel.cpp
Normal file → Executable file
|
@ -187,7 +187,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
}
|
||||
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
|
||||
if (isFlying != _prevIsFlying) {
|
||||
const float FLY_TO_IDLE_HIPS_TRANSITION_TIME = 0.5f;
|
||||
_flyIdleTimer = FLY_TO_IDLE_HIPS_TRANSITION_TIME;
|
||||
|
@ -198,7 +198,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
// if hips are not under direct control, estimate the hips position.
|
||||
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
|
||||
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS);
|
||||
|
||||
// timescale in seconds
|
||||
const float TRANS_HORIZ_TIMESCALE = 0.15f;
|
||||
|
|
12
interface/src/avatar/OtherAvatar.cpp
Normal file → Executable file
12
interface/src/avatar/OtherAvatar.cpp
Normal file → Executable file
|
@ -135,17 +135,9 @@ void OtherAvatar::rebuildCollisionShape() {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) {
|
||||
void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||
if (_motionState) {
|
||||
bool collides = _motionState->getCollisionGroup() == BULLET_COLLISION_GROUP_OTHER_AVATAR && myAvatarCollide;
|
||||
if (_collideWithOtherAvatars != collides) {
|
||||
if (!myAvatarCollide) {
|
||||
_collideWithOtherAvatars = false;
|
||||
}
|
||||
auto newCollisionGroup = _collideWithOtherAvatars ? BULLET_COLLISION_GROUP_OTHER_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS;
|
||||
_motionState->setCollisionGroup(newCollisionGroup);
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
}
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
4
interface/src/avatar/OtherAvatar.h
Normal file → Executable file
4
interface/src/avatar/OtherAvatar.h
Normal file → Executable file
|
@ -46,7 +46,9 @@ public:
|
|||
bool shouldBeInPhysicsSimulation() const;
|
||||
bool needsPhysicsUpdate() const;
|
||||
|
||||
void updateCollisionGroup(bool myAvatarCollide);
|
||||
bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; }
|
||||
|
||||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
void simulate(float deltaTime, bool inView) override;
|
||||
|
||||
|
|
|
@ -237,8 +237,17 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints,
|
|||
_relativeDefaultPoses = _absoluteDefaultPoses;
|
||||
convertAbsolutePosesToRelative(_relativeDefaultPoses);
|
||||
|
||||
// build _jointIndicesByName hash
|
||||
for (int i = 0; i < _jointsSize; i++) {
|
||||
_jointIndicesByName[_joints[i].name] = i;
|
||||
auto iter = _jointIndicesByName.find(_joints[i].name);
|
||||
if (iter != _jointIndicesByName.end()) {
|
||||
// prefer joints over meshes if there is a name collision.
|
||||
if (_joints[i].isSkeletonJoint && !_joints[iter.value()].isSkeletonJoint) {
|
||||
iter.value() = i;
|
||||
}
|
||||
} else {
|
||||
_jointIndicesByName.insert(_joints[i].name, i);
|
||||
}
|
||||
}
|
||||
|
||||
// build mirror map.
|
||||
|
|
19
libraries/avatars/src/AvatarData.cpp
Normal file → Executable file
19
libraries/avatars/src/AvatarData.cpp
Normal file → Executable file
|
@ -639,7 +639,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
// compute maxTranslationDimension before we send any joint data.
|
||||
float maxTranslationDimension = 0.001f;
|
||||
for (int i = sendStatus.rotationsSent; i < numJoints; ++i) {
|
||||
for (int i = sendStatus.translationsSent; i < numJoints; ++i) {
|
||||
const JointData& data = jointData[i];
|
||||
if (!data.translationIsDefaultPose) {
|
||||
maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension);
|
||||
|
@ -1172,6 +1172,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags);
|
||||
|
||||
if (collideWithOtherAvatarsChanged) {
|
||||
setCollisionWithOtherAvatarsFlags();
|
||||
}
|
||||
if (somethingChanged) {
|
||||
_additionalFlagsChanged = now;
|
||||
}
|
||||
|
@ -2428,7 +2431,8 @@ static const QString JSON_AVATAR_VERSION = QStringLiteral("version");
|
|||
enum class JsonAvatarFrameVersion : int {
|
||||
JointRotationsInRelativeFrame = 0,
|
||||
JointRotationsInAbsoluteFrame,
|
||||
JointDefaultPoseBits
|
||||
JointDefaultPoseBits,
|
||||
JointUnscaledTranslations,
|
||||
};
|
||||
|
||||
QJsonValue toJsonValue(const JointData& joint) {
|
||||
|
@ -2445,7 +2449,16 @@ JointData jointDataFromJsonValue(int version, const QJsonValue& json) {
|
|||
if (json.isArray()) {
|
||||
QJsonArray array = json.toArray();
|
||||
result.rotation = quatFromJsonValue(array[0]);
|
||||
|
||||
result.translation = vec3FromJsonValue(array[1]);
|
||||
|
||||
// In old recordings, translations are scaled by _geometryOffset. Undo that scaling.
|
||||
if (version < (int)JsonAvatarFrameVersion::JointUnscaledTranslations) {
|
||||
// because we don't have access to the actual _geometryOffset used. we have to guess.
|
||||
// most avatar FBX files were authored in centimeters.
|
||||
const float METERS_TO_CENTIMETERS = 100.0f;
|
||||
result.translation *= METERS_TO_CENTIMETERS;
|
||||
}
|
||||
if (version >= (int)JsonAvatarFrameVersion::JointDefaultPoseBits) {
|
||||
result.rotationIsDefaultPose = array[2].toBool();
|
||||
result.translationIsDefaultPose = array[3].toBool();
|
||||
|
@ -2464,7 +2477,7 @@ void AvatarData::avatarEntityDataToJson(QJsonObject& root) const {
|
|||
QJsonObject AvatarData::toJson() const {
|
||||
QJsonObject root;
|
||||
|
||||
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointDefaultPoseBits;
|
||||
root[JSON_AVATAR_VERSION] = (int)JsonAvatarFrameVersion::JointUnscaledTranslations;
|
||||
|
||||
if (!getSkeletonModelURL().isEmpty()) {
|
||||
root[JSON_AVATAR_BODY_MODEL] = getSkeletonModelURL().toString();
|
||||
|
|
2
libraries/avatars/src/AvatarData.h
Normal file → Executable file
2
libraries/avatars/src/AvatarData.h
Normal file → Executable file
|
@ -496,6 +496,8 @@ public:
|
|||
/// \return number of bytes parsed
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
|
||||
virtual void setCollisionWithOtherAvatarsFlags() {};
|
||||
|
||||
// Body Rotation (degrees)
|
||||
float getBodyYaw() const;
|
||||
void setBodyYaw(float bodyYaw);
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
glm::vec3 geometricTranslation;
|
||||
glm::quat geometricRotation;
|
||||
glm::vec3 geometricScaling;
|
||||
bool isLimbNode; // is this FBXModel transform is a "LimbNode" i.e. a joint
|
||||
};
|
||||
|
||||
glm::mat4 getGlobalTransform(const QMultiMap<QString, QString>& _connectionParentMap,
|
||||
|
@ -559,9 +560,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
glm::vec3 geometricRotation;
|
||||
|
||||
glm::vec3 rotationMin, rotationMax;
|
||||
|
||||
bool isLimbNode = object.properties.size() >= 3 && object.properties.at(2) == "LimbNode";
|
||||
FBXModel fbxModel = { name, -1, glm::vec3(), glm::mat4(), glm::quat(), glm::quat(), glm::quat(),
|
||||
glm::mat4(), glm::vec3(), glm::vec3(),
|
||||
false, glm::vec3(), glm::quat(), glm::vec3(1.0f) };
|
||||
glm::mat4(), glm::vec3(), glm::vec3(),
|
||||
false, glm::vec3(), glm::quat(), glm::vec3(1.0f), isLimbNode };
|
||||
ExtractedMesh* mesh = NULL;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
|
@ -1258,6 +1261,7 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
// convert the models to joints
|
||||
QVariantList freeJoints = mapping.values("freeJoint");
|
||||
hfmModel.hasSkeletonJoints = false;
|
||||
|
||||
foreach (const QString& modelID, modelIDs) {
|
||||
const FBXModel& fbxModel = fbxModels[modelID];
|
||||
HFMJoint joint;
|
||||
|
@ -1288,6 +1292,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
joint.geometricTranslation = fbxModel.geometricTranslation;
|
||||
joint.geometricRotation = fbxModel.geometricRotation;
|
||||
joint.geometricScaling = fbxModel.geometricScaling;
|
||||
joint.isSkeletonJoint = fbxModel.isLimbNode;
|
||||
hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint);
|
||||
|
||||
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
|
||||
|
@ -1311,14 +1317,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
joint.name = hfmModel.hfmToHifiJointNameMapping.key(fbxModel.name);
|
||||
}
|
||||
|
||||
foreach (const QString& childID, _connectionChildMap.values(modelID)) {
|
||||
QString type = typeFlags.value(childID);
|
||||
if (!type.isEmpty()) {
|
||||
hfmModel.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
joint.bindTransformFoundInCluster = false;
|
||||
|
||||
hfmModel.joints.append(joint);
|
||||
|
|
|
@ -109,7 +109,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
}
|
||||
_dynamicsWorld = nullptr;
|
||||
}
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
int32_t collisionGroup = BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
if (_rigidBody) {
|
||||
updateMassProperties();
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
// add to new world
|
||||
_dynamicsWorld = world;
|
||||
_pendingFlags &= ~PENDING_FLAG_JUMP;
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, collisionMask);
|
||||
_dynamicsWorld->addAction(this);
|
||||
// restore gravity settings because adding an object to the world overwrites its gravity setting
|
||||
_rigidBody->setGravity(_currentGravity * _currentUp);
|
||||
|
@ -127,7 +128,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE);
|
||||
_ghost.setCharacterShape(static_cast<btConvexHullShape*>(shape));
|
||||
}
|
||||
_ghost.setCollisionGroupAndMask(collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ collisionGroup));
|
||||
_ghost.setCollisionGroupAndMask(collisionGroup, collisionMask & (~ collisionGroup));
|
||||
_ghost.setCollisionWorld(_dynamicsWorld);
|
||||
_ghost.setRadiusAndHalfHeight(_radius, _halfHeight);
|
||||
if (_rigidBody) {
|
||||
|
@ -384,8 +385,8 @@ static const char* stateToStr(CharacterController::State state) {
|
|||
#endif // #ifdef DEBUG_STATE_CHANGE
|
||||
|
||||
void CharacterController::updateCurrentGravity() {
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) {
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (_state == State::Hover || collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
_currentGravity = 0.0f;
|
||||
} else {
|
||||
_currentGravity = _gravity;
|
||||
|
@ -458,28 +459,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
|||
void CharacterController::setCollisionless(bool collisionless) {
|
||||
if (collisionless != _collisionless) {
|
||||
_collisionless = collisionless;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CharacterController::computeCollisionGroup() const {
|
||||
if (_collisionless) {
|
||||
return _collisionlessAllowed ? BULLET_COLLISION_GROUP_COLLISIONLESS : BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
} else {
|
||||
return BULLET_COLLISION_GROUP_MY_AVATAR;
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::handleChangedCollisionGroup() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_GROUP) {
|
||||
// ATM the easiest way to update collision groups is to remove/re-add the RigidBody
|
||||
if (_dynamicsWorld) {
|
||||
_dynamicsWorld->removeRigidBody(_rigidBody);
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
_dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR);
|
||||
}
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
updateCurrentGravity();
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,8 +547,8 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel
|
|||
btScalar angle = motor.rotation.getAngle();
|
||||
btVector3 velocity = worldVelocity.rotate(axis, -angle);
|
||||
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ||
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS ||
|
||||
_state == State::Hover || motor.hTimescale == motor.vTimescale) {
|
||||
// modify velocity
|
||||
btScalar tau = dt / motor.hTimescale;
|
||||
|
@ -708,11 +688,11 @@ void CharacterController::updateState() {
|
|||
btVector3 rayStart = _position;
|
||||
|
||||
btScalar rayLength = _radius;
|
||||
int32_t collisionGroup = computeCollisionGroup();
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
|
||||
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||
} else {
|
||||
int32_t collisionMask = computeCollisionMask();
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
rayLength += MIN_HOVER_HEIGHT;
|
||||
} else {
|
||||
rayLength += _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT;
|
||||
}
|
||||
btVector3 rayEnd = rayStart - rayLength * _currentUp;
|
||||
|
||||
|
@ -746,69 +726,7 @@ void CharacterController::updateState() {
|
|||
|
||||
// disable normal state transitions while collisionless
|
||||
const btScalar MAX_WALKING_SPEED = 2.65f;
|
||||
if (collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) {
|
||||
switch (_state) {
|
||||
case State::Ground:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||
_takeoffToInAirStartTime = now;
|
||||
SET_STATE(State::Takeoff, "jump pressed");
|
||||
} else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) {
|
||||
SET_STATE(State::InAir, "falling");
|
||||
}
|
||||
break;
|
||||
case State::Takeoff:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground");
|
||||
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||
SET_STATE(State::InAir, "takeoff done");
|
||||
|
||||
// compute jumpSpeed based on the scaled jump height for the default avatar in default gravity.
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
velocity += jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
break;
|
||||
case State::InAir: {
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) {
|
||||
SET_STATE(State::Ground, "hit ground");
|
||||
} else if (_flyingAllowed) {
|
||||
btVector3 desiredVelocity = _targetVelocity;
|
||||
if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||
desiredVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED;
|
||||
if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) {
|
||||
SET_STATE(State::Hover, "double jump button");
|
||||
} else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||
SET_STATE(State::Hover, "jump button held");
|
||||
} else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) {
|
||||
// Transition to hover if we are above the fall threshold
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
} else if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Hover:
|
||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||
if (!_flyingAllowed && rayHasHit) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
SET_STATE(State::InAir, "near ground");
|
||||
} else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) {
|
||||
SET_STATE(State::Ground, "touching ground");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (collisionMask == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
// when collisionless: only switch between State::Ground and State::Hover
|
||||
// and bypass state debugging
|
||||
if (rayHasHit) {
|
||||
|
@ -820,6 +738,68 @@ void CharacterController::updateState() {
|
|||
} else {
|
||||
_state = State::Hover;
|
||||
}
|
||||
} else {
|
||||
switch (_state) {
|
||||
case State::Ground:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||
_takeoffToInAirStartTime = now;
|
||||
SET_STATE(State::Takeoff, "jump pressed");
|
||||
} else if (rayHasHit && !_hasSupport && _floorDistance > GROUND_TO_FLY_THRESHOLD) {
|
||||
SET_STATE(State::InAir, "falling");
|
||||
}
|
||||
break;
|
||||
case State::Takeoff:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground");
|
||||
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||
SET_STATE(State::InAir, "takeoff done");
|
||||
|
||||
// compute jumpSpeed based on the scaled jump height for the default avatar in default gravity.
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
velocity += jumpSpeed * _currentUp;
|
||||
_rigidBody->setLinearVelocity(velocity);
|
||||
}
|
||||
break;
|
||||
case State::InAir: {
|
||||
const float jumpHeight = std::max(_scaleFactor * DEFAULT_AVATAR_JUMP_HEIGHT, DEFAULT_AVATAR_MIN_JUMP_HEIGHT);
|
||||
const float jumpSpeed = sqrtf(2.0f * -DEFAULT_AVATAR_GRAVITY * jumpHeight);
|
||||
if ((velocity.dot(_currentUp) <= (jumpSpeed / 2.0f)) && ((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport)) {
|
||||
SET_STATE(State::Ground, "hit ground");
|
||||
} else if (_flyingAllowed) {
|
||||
btVector3 desiredVelocity = _targetVelocity;
|
||||
if (desiredVelocity.length2() < MIN_TARGET_SPEED_SQUARED) {
|
||||
desiredVelocity = btVector3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
bool vertTargetSpeedIsNonZero = desiredVelocity.dot(_currentUp) > MIN_TARGET_SPEED;
|
||||
if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (_takeoffJumpButtonID != _jumpButtonDownCount)) {
|
||||
SET_STATE(State::Hover, "double jump button");
|
||||
} else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||
SET_STATE(State::Hover, "jump button held");
|
||||
} else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) {
|
||||
// Transition to hover if we are above the fall threshold
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
} else if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Hover:
|
||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||
if (!_flyingAllowed && rayHasHit) {
|
||||
SET_STATE(State::InAir, "flying not allowed");
|
||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||
SET_STATE(State::InAir, "near ground");
|
||||
} else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) {
|
||||
SET_STATE(State::Ground, "touching ground");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -866,6 +846,6 @@ void CharacterController::setFlyingAllowed(bool value) {
|
|||
void CharacterController::setCollisionlessAllowed(bool value) {
|
||||
if (value != _collisionlessAllowed) {
|
||||
_collisionlessAllowed = value;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK;
|
||||
}
|
||||
}
|
||||
|
|
8
libraries/physics/src/CharacterController.h
Normal file → Executable file
8
libraries/physics/src/CharacterController.h
Normal file → Executable file
|
@ -30,7 +30,7 @@ const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0;
|
|||
const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
||||
const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2;
|
||||
const uint32_t PENDING_FLAG_JUMP = 1U << 3;
|
||||
const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4;
|
||||
const uint32_t PENDING_FLAG_UPDATE_COLLISION_MASK = 1U << 4;
|
||||
const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5;
|
||||
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
|
||||
|
||||
|
@ -120,14 +120,16 @@ public:
|
|||
bool isStuck() const { return _isStuck; }
|
||||
|
||||
void setCollisionless(bool collisionless);
|
||||
int32_t computeCollisionGroup() const;
|
||||
void handleChangedCollisionGroup();
|
||||
|
||||
virtual int32_t computeCollisionMask() const = 0;
|
||||
virtual void handleChangedCollisionMask() = 0;
|
||||
|
||||
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||
|
||||
void setFlyingAllowed(bool value);
|
||||
void setCollisionlessAllowed(bool value);
|
||||
|
||||
void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; }
|
||||
|
||||
protected:
|
||||
#ifdef DEBUG_STATE_CHANGE
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QPluginLoader>
|
||||
|
||||
//#define HIFI_PLUGINMANAGER_DEBUG
|
||||
#if defined(HIFI_PLUGINMANAGER_DEBUG)
|
||||
#include <QJsonDocument>
|
||||
#endif
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
|
@ -79,10 +84,7 @@ bool isDisabled(QJsonObject metaData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
using Loader = QSharedPointer<QPluginLoader>;
|
||||
using LoaderList = QList<Loader>;
|
||||
|
||||
const LoaderList& getLoadedPlugins() {
|
||||
auto PluginManager::getLoadedPlugins() const -> const LoaderList& {
|
||||
static std::once_flag once;
|
||||
static LoaderList loadedPlugins;
|
||||
std::call_once(once, [&] {
|
||||
|
@ -106,15 +108,25 @@ const LoaderList& getLoadedPlugins() {
|
|||
for (auto plugin : candidates) {
|
||||
qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin);
|
||||
QSharedPointer<QPluginLoader> loader(new QPluginLoader(pluginPath + plugin));
|
||||
|
||||
if (isDisabled(loader->metaData())) {
|
||||
const QJsonObject pluginMetaData = loader->metaData();
|
||||
#if defined(HIFI_PLUGINMANAGER_DEBUG)
|
||||
QJsonDocument metaDataDoc(pluginMetaData);
|
||||
qCInfo(plugins) << "Metadata for " << qPrintable(plugin) << ": " << QString(metaDataDoc.toJson());
|
||||
#endif
|
||||
if (isDisabled(pluginMetaData)) {
|
||||
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "is disabled";
|
||||
// Skip this one, it's disabled
|
||||
continue;
|
||||
}
|
||||
if (getPluginInterfaceVersionFromMetaData(loader->metaData()) != HIFI_PLUGIN_INTERFACE_VERSION) {
|
||||
|
||||
if (!_pluginFilter(pluginMetaData)) {
|
||||
qCDebug(plugins) << "Plugin" << qPrintable(plugin) << "doesn't pass provided filter";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (getPluginInterfaceVersionFromMetaData(pluginMetaData) != HIFI_PLUGIN_INTERFACE_VERSION) {
|
||||
qCWarning(plugins) << "Plugin" << qPrintable(plugin) << "interface version doesn't match, not loading:"
|
||||
<< getPluginInterfaceVersionFromMetaData(loader->metaData())
|
||||
<< getPluginInterfaceVersionFromMetaData(pluginMetaData)
|
||||
<< "doesn't match" << HIFI_PLUGIN_INTERFACE_VERSION;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
#include "Forward.h"
|
||||
|
||||
|
||||
class PluginManager;
|
||||
class QPluginLoader;
|
||||
using PluginManagerPointer = QSharedPointer<PluginManager>;
|
||||
|
||||
class PluginManager : public QObject, public Dependency {
|
||||
|
@ -47,6 +46,9 @@ public:
|
|||
void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister);
|
||||
QStringList getRunningInputDeviceNames() const;
|
||||
|
||||
using PluginFilter = std::function<bool(const QJsonObject&)>;
|
||||
void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; }
|
||||
|
||||
signals:
|
||||
void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices);
|
||||
|
||||
|
@ -60,6 +62,12 @@ private:
|
|||
PluginContainer* _container { nullptr };
|
||||
DisplayPluginList _displayPlugins;
|
||||
InputPluginList _inputPlugins;
|
||||
PluginFilter _pluginFilter { [](const QJsonObject&) { return true; } };
|
||||
|
||||
using Loader = QSharedPointer<QPluginLoader>;
|
||||
using LoaderList = QList<Loader>;
|
||||
|
||||
const LoaderList& getLoadedPlugins() const;
|
||||
};
|
||||
|
||||
// TODO: we should define this value in CMake, and then use CMake
|
||||
|
|
|
@ -3313,7 +3313,8 @@ function loaded() {
|
|||
}
|
||||
}
|
||||
|
||||
let doSelectElement = lastEntityID === '"' + selectedEntityProperties.id + '"';
|
||||
let hasSelectedEntityChanged = lastEntityID !== '"' + selectedEntityProperties.id + '"';
|
||||
let doSelectElement = !hasSelectedEntityChanged;
|
||||
|
||||
// the event bridge and json parsing handle our avatar id string differently.
|
||||
lastEntityID = '"' + selectedEntityProperties.id + '"';
|
||||
|
@ -3433,7 +3434,7 @@ function loaded() {
|
|||
property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," +
|
||||
propertyValue.green + "," +
|
||||
propertyValue.blue + ")";
|
||||
if ($(property.elColorPicker).attr('active') === 'true') {
|
||||
if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') {
|
||||
// Set the color picker inactive before setting the color,
|
||||
// otherwise an update will be sent directly after setting it here.
|
||||
$(property.elColorPicker).attr('active', 'false');
|
||||
|
|
|
@ -13,36 +13,39 @@ Nitpick has 5 functions, separated into separate tabs:
|
|||
1. Web interface
|
||||
|
||||
## Build (for developers)
|
||||
Nitpick is built as part of the High Fidelity build.
|
||||
Nitpick is built as part of the High Fidelity build.
|
||||
XXXX refers to the version number - replace as necessary. For example, replace with 3.2.11
|
||||
### Creating installers
|
||||
#### Windows
|
||||
1. Perform Release build
|
||||
1. Verify that 7Zip is installed.
|
||||
1. cd to the `build\tools\nitpick\Release` directory
|
||||
1. Delete any existing installers (named nitpick-installer-###.exe)
|
||||
1. Select all, right-click and select 7-Zip->Add to archive...
|
||||
1. Set Archive format to 7z
|
||||
1. Check "Create SFX archive
|
||||
1. Enter installer name (i.e. `nitpick-installer-v1.2.exe`)
|
||||
1. Enter installer name (i.e. `nitpick-installer-vXXXX.exe`)
|
||||
1. Click "OK"
|
||||
1. Copy created installer to https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.2.exe: aws s3 cp nitpick-installer-v1.2.exe s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.2.exe
|
||||
1. Copy created installer to https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-vXXXX.exe: aws s3 cp nitpick-installer-vXXXX.exe s3://hifi-qa/nitpick/Mac/nitpick-installer-vXXXX.exe
|
||||
#### Mac
|
||||
These steps assume the hifi repository has been cloned to `~/hifi`.
|
||||
1. (first time) Install brew
|
||||
In a terminal: `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)`
|
||||
1. (First time) install create-dmg:
|
||||
In a terminal: `brew install create-dmg`
|
||||
1. Perform Release build
|
||||
1. In a terminal: cd to the `build/tools/nitpick/Release` folder
|
||||
1. Copy the quazip dynamic library (note final period):
|
||||
In a terminal: `cp ~/hifi/build/ext/Xcode/quazip/project/lib/libquazip5.1.dylib .`
|
||||
1. Change the loader instruction to find the dynamic library locally
|
||||
In a terminal: `install_name_tool -change ~/hifi/build/ext/Xcode/quazip/project/lib/libquazip5.1.dylib libquazip5.1.dylib nitpick`
|
||||
1. Delete any existing disk images. In a terminal: `rm *.dmg`
|
||||
1. Create installer (note final period).In a terminal: `create-dmg --volname nitpick-installer-v1.2 nitpick-installer-v1.2.dmg .`
|
||||
1. Create installer (note final period).In a terminal: `create-dmg --volname nitpick-installer-vXXXX nitpick-installer-vXXXX.dmg .`
|
||||
Make sure to wait for completion.
|
||||
1. Copy created installer to AWS: `~/Library/Python/3.7/bin/aws s3 cp nitpick-installer-v1.2.dmg s3://hifi-qa/nitpick/Mac/nitpick-installer-v1.2.dmg`
|
||||
1. Copy created installer to AWS: `~/Library/Python/3.7/bin/aws s3 cp nitpick-installer-vXXXX.dmg s3://hifi-qa/nitpick/Mac/nitpick-installer-vXXXX.dmg`
|
||||
### Installation
|
||||
#### Windows
|
||||
1. (First time) download and install vc_redist.x64.exe (available at https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.2.exe)
|
||||
1. (First time) download and install vc_redist.x64.exe (available at https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-vXXXX.exe)
|
||||
1. (First time) download and install Python 3 from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/python-3.7.0-amd64.exe (also located at https://www.python.org/downloads/)
|
||||
1. After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable.
|
||||
1. (First time) download and install AWS CLI from https://hifi-qa.s3.amazonaws.com/nitpick/Windows/AWSCLI64PY3.msi (also available at https://aws.amazon.com/cli/
|
||||
|
@ -52,7 +55,7 @@ These steps assume the hifi repository has been cloned to `~/hifi`.
|
|||
1. Leave region name and ouput format as default [None]
|
||||
1. Install the latest release of Boto3 via pip: `pip install boto3`
|
||||
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-v1.2.exe>)
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Windows/nitpick-installer-vXXXX.exe>)
|
||||
1. Double click on the installer and install to a convenient location
|
||||

|
||||
|
||||
|
@ -76,14 +79,14 @@ In a terminal: `python3 get-pip.py --user`
|
|||
1. Enter the secret key
|
||||
1. Leave region name and ouput format as default [None]
|
||||
1. Install the latest release of Boto3 via pip: pip3 install boto3
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Mac/nitpick-installer-v1.2.dmg>).
|
||||
1. Download the installer by browsing to [here](<https://hifi-qa.s3.amazonaws.com/nitpick/Mac/nitpick-installer-vXXXX.dmg>).
|
||||
1. Double-click on the downloaded image to mount it
|
||||
1. Create a folder for the nitpick files (e.g. ~/nitpick)
|
||||
If this folder exists then delete all it's contents.
|
||||
1. Copy the downloaded files to the folder
|
||||
In a terminal:
|
||||
`cd ~/nitpick`
|
||||
`cp -r /Volumes/nitpick-installer-v1.2/* .`
|
||||
`cp -r /Volumes/nitpick-installer-vXXXX/* .`
|
||||
|
||||
1. __To run nitpick, cd to the folder that you copied to and run `./nitpick`__
|
||||
# Usage
|
||||
|
|
Loading…
Reference in a new issue