From d604f9adfb265b9311c2b66a2027256e9cd8b064 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 13 Feb 2019 18:18:47 -0800 Subject: [PATCH 01/88] Add AudioSoloRequest, BulkAvatarTraitsAck; also decode obfuscated protocols --- tools/dissectors/1-hfudt.lua | 98 ++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/tools/dissectors/1-hfudt.lua b/tools/dissectors/1-hfudt.lua index de99c1ce3c..8179276dbb 100644 --- a/tools/dissectors/1-hfudt.lua +++ b/tools/dissectors/1-hfudt.lua @@ -152,7 +152,9 @@ local packet_types = { [97] = "OctreeDataPersist", [98] = "EntityClone", [99] = "EntityQueryInitialResultsComplete", - [100] = "BulkAvatarTraits" + [100] = "BulkAvatarTraits", + [101] = "AudioSoloRequest", + [102] = "BulkAvatarTraitsAck" } local unsourced_packet_types = { @@ -301,55 +303,53 @@ function p_hfudt.dissector(buf, pinfo, tree) -- check if we have part of a message that we need to re-assemble -- before it can be dissected - if obfuscation_bits == 0 then - if message_bit == 1 and message_position ~= 0 then - if fragments[message_number] == nil then - fragments[message_number] = {} - end - - if fragments[message_number][message_part_number] == nil then - fragments[message_number][message_part_number] = {} - end - - -- set the properties for this fragment - fragments[message_number][message_part_number] = { - payload = buf(i):bytes() - } - - -- if this is the last part, set our maximum part number - if message_position == 1 then - fragments[message_number].last_part_number = message_part_number - end - - -- if we have the last part - -- enumerate our parts for this message and see if everything is present - if fragments[message_number].last_part_number ~= nil then - local i = 0 - local has_all = true - - local finalMessage = ByteArray.new() - local message_complete = true - - while i <= fragments[message_number].last_part_number do - if fragments[message_number][i] ~= nil then - finalMessage = finalMessage .. fragments[message_number][i].payload - else - -- missing this part, have to break until we have it - message_complete = false - end - - i = i + 1 - end - - if message_complete then - debug("Message " .. message_number .. " is " .. finalMessage:len()) - payload_to_dissect = ByteArray.tvb(finalMessage, message_number) - end - end - - else - payload_to_dissect = buf(i):tvb() + if message_bit == 1 and message_position ~= 0 then + if fragments[message_number] == nil then + fragments[message_number] = {} end + + if fragments[message_number][message_part_number] == nil then + fragments[message_number][message_part_number] = {} + end + + -- set the properties for this fragment + fragments[message_number][message_part_number] = { + payload = buf(i):bytes() + } + + -- if this is the last part, set our maximum part number + if message_position == 1 then + fragments[message_number].last_part_number = message_part_number + end + + -- if we have the last part + -- enumerate our parts for this message and see if everything is present + if fragments[message_number].last_part_number ~= nil then + local i = 0 + local has_all = true + + local finalMessage = ByteArray.new() + local message_complete = true + + while i <= fragments[message_number].last_part_number do + if fragments[message_number][i] ~= nil then + finalMessage = finalMessage .. fragments[message_number][i].payload + else + -- missing this part, have to break until we have it + message_complete = false + end + + i = i + 1 + end + + if message_complete then + debug("Message " .. message_number .. " is " .. finalMessage:len()) + payload_to_dissect = ByteArray.tvb(finalMessage, message_number) + end + end + + else + payload_to_dissect = buf(i):tvb() end if payload_to_dissect ~= nil then From 50a1e07ed275fec230f771663bea8536b81317bb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 18 Feb 2019 18:32:49 +1300 Subject: [PATCH 02/88] Stub missing MyAvatar, Avatar, and Agent functions and properties JSDoc --- .../src/avatars/ScriptableAvatar.h | 3 + interface/src/avatar/MyAvatar.h | 76 ++++++++++++++----- .../src/avatars-renderer/Avatar.h | 6 ++ 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index e93be897d5..4562ad6134 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -213,6 +213,9 @@ public: Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override; public slots: + /**jsdoc + * @function MyAvatar.update + */ void update(float deltatime); /**jsdoc diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 984d7b297b..4e75c93403 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -69,6 +69,7 @@ class MyAvatar : public Avatar { * @hifi-avatar * * @property {Vec3} qmlPosition - A synonym for position for use by QML. + * * @property {boolean} shouldRenderLocally=true - If true then your avatar is rendered for you in Interface, * otherwise it is not rendered for you (but it is still rendered for other users). * @property {Vec3} motorVelocity=Vec3.ZERO - The target velocity of your avatar to be achieved by a scripted motor. @@ -90,29 +91,38 @@ class MyAvatar : public Avatar { * @property {number} audioListenerModeCamera=1 - The audio listening position is at the camera. Read-only. * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the * customListenPosition and customListenOrientation property values. Read-only. + * @property {Vec3} customListenPosition=Vec3.ZERO - The listening position used when the audioListenerMode + * property value is audioListenerModeCustom. + * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the + * audioListenerMode property value is audioListenerModeCustom. * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true. * @property {boolean} hasProceduralBlinkFaceMovement=true - procedural blinking will be turned on if set to true. * @property {boolean} hasProceduralEyeFaceMovement=true - procedural eye movement will be turned on if set to true. * @property {boolean} hasAudioEnabledFaceMovement=true - If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. - * @property {Vec3} customListenPosition=Vec3.ZERO - The listening position used when the audioListenerMode - * property value is audioListenerModeCustom. - * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the - * audioListenerMode property value is audioListenerModeCustom. + * @property {number} rotationRecenterFilterLength + * @property {number} rotationThreshold + * @property {boolean} enableStepResetRotation + * @property {boolean} enableDrawAverageFacing + * * @property {Vec3} leftHandPosition - The position of the left hand in avatar coordinates if it's being positioned by * controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. * @property {Vec3} rightHandPosition - The position of the right hand in avatar coordinates if it's being positioned by * controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} leftHandTipPosition - The position 30cm offset from the left hand in avatar coordinates if it's being * positioned by controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. * @property {Vec3} rightHandTipPosition - The position 30cm offset from the right hand in avatar coordinates if it's being * positioned by controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. + * * @property {Pose} leftHandPose - The pose of the left hand as determined by the hand controllers. Read-only. * @property {Pose} rightHandPose - The pose right hand position as determined by the hand controllers. Read-only. * @property {Pose} leftHandTipPose - The pose of the left hand as determined by the hand controllers, with the position * by 30cm. Read-only. * @property {Pose} rightHandTipPose - The pose of the right hand as determined by the hand controllers, with the position * by 30cm. Read-only. + * + * @property {number} energy + * @property {boolean} isAway + * * @property {boolean} centerOfGravityModelEnabled=true - If true then the avatar hips are placed according to the center of * gravity model that balance the center of gravity over the base of support of the feet. Setting the value false * will result in the default behaviour where the hips are placed under the head. @@ -122,30 +132,38 @@ class MyAvatar : public Avatar { * @property {boolean} collisionsEnabled - Set to true to enable collisions for the avatar, false * to disable collisions. May return true even though the value was set false because the * zone may disallow collisionless avatars. + * @property {boolean} otherAvatarsCollisionsEnabled * @property {boolean} characterControllerEnabled - Synonym of collisionsEnabled. * Deprecated: Use collisionsEnabled instead. * @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings > * Walking and teleporting. Note: Setting the value has no effect unless Interface is restarted. * @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Show room boundaries * while teleporting. Note: Setting the value has no effect unless Interface is restarted. + * * @property {number} yawSpeed=75 * @property {number} pitchSpeed=50 + * * @property {boolean} hmdRollControlEnabled=true - If true, the roll angle of your HMD turns your avatar * while flying. * @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if * hmdRollControlEnabled is enabled. * @property {number} hmdRollControlRate If hmdRollControlEnabled is true, this value determines the maximum turn rate of * your avatar when rolling your HMD in degrees per second. + * * @property {number} userHeight=1.75 - The height of the user in sensor space. * @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. Read-only. + * * @property {Uuid} SELF_ID - UUID representing "my avatar". Only use for local-only entities in situations * where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated. * Read-only. + * * @property {number} walkSpeed * @property {number} walkBackwardSpeed * @property {number} sprintSpeed * @property {number} isInSittingState - * @property {number} userRecenterModel + * @property {MyAvatar.SitStandModelType} userRecenterModel + * @property {boolean} isSitStandStateLocked + * @property {boolean} allowTeleporting * * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the * registration point of the 3D model. @@ -160,6 +178,7 @@ class MyAvatar : public Avatar { * sometimes called "elevation". * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is * sometimes called "bank". + * * @property {Quat} orientation * @property {Quat} headOrientation - The orientation of the avatar's head. * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is @@ -168,18 +187,24 @@ class MyAvatar : public Avatar { * head. Yaw is sometimes called "heading". * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is * sometimes called "bank". + * * @property {Vec3} velocity * @property {Vec3} angularVelocity + * * @property {number} audioLoudness * @property {number} audioAverageLoudness + * * @property {string} displayName * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer * rather than by Interface clients. The result is unique among all avatars present at the time. * @property {boolean} lookAtSnappingEnabled * @property {string} skeletonModelURL * @property {AttachmentData[]} attachmentData + * * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. + * * @property {Uuid} sessionUUID Read-only. + * * @property {Mat4} sensorToWorldMatrix Read-only. * @property {Mat4} controllerLeftHandMatrix Read-only. * @property {Mat4} controllerRightHandMatrix Read-only. @@ -196,11 +221,11 @@ class MyAvatar : public Avatar { Q_PROPERTY(QString motorMode READ getScriptedMotorMode WRITE setScriptedMotorMode) Q_PROPERTY(QString collisionSoundURL READ getCollisionSoundURL WRITE setCollisionSoundURL) Q_PROPERTY(AudioListenerMode audioListenerMode READ getAudioListenerMode WRITE setAudioListenerMode) - Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition) - Q_PROPERTY(glm::quat customListenOrientation READ getCustomListenOrientation WRITE setCustomListenOrientation) Q_PROPERTY(AudioListenerMode audioListenerModeHead READ getAudioListenerModeHead) Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) + Q_PROPERTY(glm::vec3 customListenPosition READ getCustomListenPosition WRITE setCustomListenPosition) + Q_PROPERTY(glm::quat customListenOrientation READ getCustomListenOrientation WRITE setCustomListenOrientation) Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) Q_PROPERTY(bool hasProceduralBlinkFaceMovement READ getHasProceduralBlinkFaceMovement WRITE setHasProceduralBlinkFaceMovement) Q_PROPERTY(bool hasProceduralEyeFaceMovement READ getHasProceduralEyeFaceMovement WRITE setHasProceduralEyeFaceMovement) @@ -277,6 +302,9 @@ public: }; Q_ENUM(DriveKeys) + /**jsdoc + * @typedef {number} MyAvatar.SitStandModelType + */ enum SitStandModelType { ForceSit = 0, ForceStand, @@ -491,6 +519,9 @@ public: // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. // It is not specified in what order multiple handlers are called. + /**jsdoc + * @function MyAvatar.addAnimationStateHandler + */ Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); } /**jsdoc @@ -530,7 +561,7 @@ public: */ Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand); /**jsdoc - * @function MyAvatar.setHmdAvatarAlignmentType + * @function MyAvatar.getHmdAvatarAlignmentType * @returns {string} */ Q_INVOKABLE QString getHmdAvatarAlignmentType() const; @@ -649,7 +680,7 @@ public: /**jsdoc * Recenter the avatar in the horizontal direction, if {@link MyAvatar|MyAvatar.hmdLeanRecenterEnabled} is * false. - * @ function MyAvatar.triggerHorizontalRecenter + * @function MyAvatar.triggerHorizontalRecenter */ Q_INVOKABLE void triggerHorizontalRecenter(); @@ -935,7 +966,7 @@ public: /**jsdoc * Function returns list of avatar entities - * @function MyAvatar.getAvatarEntitiesVariant() + * @function MyAvatar.getAvatarEntitiesVariant * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); @@ -1244,7 +1275,6 @@ public slots: * @param {boolean} [shouldFaceLocation=false] - Set to true to position the avatar a short distance away from * the new position and orientate the avatar to face the position. */ - void goToFeetLocation(const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation); @@ -1382,8 +1412,20 @@ public slots: */ bool getEnableMeshVisible() const override; + /**jsdoc + * @function MyAvatar.storeAvatarEntityDataPayload + */ void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload) override; + + /**jsdoc + * @function MyAvatar.clearAvatarEntity + * @param {Uuid} entityID + * @param {boolean} requiresRemovalFromTree + */ void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true) override; + /**jsdoc + * @function MyAvatar.sanitizeAvatarEntityProperties + */ void sanitizeAvatarEntityProperties(EntityItemProperties& properties) const; /**jsdoc @@ -1489,11 +1531,11 @@ signals: void collisionsEnabledChanged(bool enabled); /**jsdoc - * Triggered when collisions with other avatars enabled or disabled - * @function MyAvatar.otherAvatarsCollisionsEnabledChanged - * @param {boolean} enabled - * @returns {Signal} - */ + * Triggered when collisions with other avatars enabled or disabled + * @function MyAvatar.otherAvatarsCollisionsEnabledChanged + * @param {boolean} enabled + * @returns {Signal} + */ void otherAvatarsCollisionsEnabledChanged(bool enabled); /**jsdoc diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 06942a13d8..7ebb8cad01 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -499,6 +499,9 @@ public: void tearDownGrabs(); signals: + /**jsdoc + * @function MyAvatar.targetScaleChanged + */ void targetScaleChanged(float targetScale); public slots: @@ -541,6 +544,9 @@ public slots: */ glm::quat getRightPalmRotation() const; + /**jsdoc + * @function MyAvatar.setModelURLFinished + */ // hooked up to Model::setURLFinished signal void setModelURLFinished(bool success); From f58a5db0b0931ce0244007a748ed545d51196194 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 19 Feb 2019 09:47:51 +1300 Subject: [PATCH 03/88] Reorganize JSDoc inheritance for MyAvatar and Avatar --- .../src/avatars/ScriptableAvatar.h | 109 +++------- interface/src/avatar/MyAvatar.h | 146 +++++++++----- .../src/avatars-renderer/Avatar.h | 7 +- libraries/avatars/src/AvatarData.h | 189 ++++++++++++------ 4 files changed, 262 insertions(+), 189 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 4562ad6134..b2ad4527b0 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -22,17 +22,16 @@ * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the * {@link MyAvatar} API. * - *

Note: In the examples, use "Avatar" instead of "MyAvatar".

- * * @namespace Avatar * * @hifi-assignment-client * + * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. * @property {Vec3} position - * @property {number} scale + * @property {number} scale - Returns the clamped scale of the avatar. * @property {number} density Read-only. * @property {Vec3} handPosition - * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". @@ -63,62 +62,6 @@ * @property {Mat4} controllerRightHandMatrix Read-only. * @property {number} sensorToWorldScale Read-only. * - * @borrows MyAvatar.getDomainMinScale as getDomainMinScale - * @borrows MyAvatar.getDomainMaxScale as getDomainMaxScale - * @borrows MyAvatar.canMeasureEyeHeight as canMeasureEyeHeight - * @borrows MyAvatar.getEyeHeight as getEyeHeight - * @borrows MyAvatar.getHeight as getHeight - * @borrows MyAvatar.setHandState as setHandState - * @borrows MyAvatar.getHandState as getHandState - * @borrows MyAvatar.setRawJointData as setRawJointData - * @borrows MyAvatar.setJointData as setJointData - * @borrows MyAvatar.setJointRotation as setJointRotation - * @borrows MyAvatar.setJointTranslation as setJointTranslation - * @borrows MyAvatar.clearJointData as clearJointData - * @borrows MyAvatar.isJointDataValid as isJointDataValid - * @borrows MyAvatar.getJointRotation as getJointRotation - * @borrows MyAvatar.getJointTranslation as getJointTranslation - * @borrows MyAvatar.getJointRotations as getJointRotations - * @borrows MyAvatar.getJointTranslations as getJointTranslations - * @borrows MyAvatar.setJointRotations as setJointRotations - * @borrows MyAvatar.setJointTranslations as setJointTranslations - * @borrows MyAvatar.clearJointsData as clearJointsData - * @borrows MyAvatar.getJointIndex as getJointIndex - * @borrows MyAvatar.getJointNames as getJointNames - * @borrows MyAvatar.setBlendshape as setBlendshape - * @borrows MyAvatar.getAttachmentsVariant as getAttachmentsVariant - * @borrows MyAvatar.setAttachmentsVariant as setAttachmentsVariant - * @borrows MyAvatar.updateAvatarEntity as updateAvatarEntity - * @borrows MyAvatar.clearAvatarEntity as clearAvatarEntity - * @borrows MyAvatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected - * @borrows MyAvatar.getAttachmentData as getAttachmentData - * @borrows MyAvatar.setAttachmentData as setAttachmentData - * @borrows MyAvatar.attach as attach - * @borrows MyAvatar.detachOne as detachOne - * @borrows MyAvatar.detachAll as detachAll - * @borrows MyAvatar.getAvatarEntityData as getAvatarEntityData - * @borrows MyAvatar.setAvatarEntityData as setAvatarEntityData - * @borrows MyAvatar.getSensorToWorldMatrix as getSensorToWorldMatrix - * @borrows MyAvatar.getSensorToWorldScale as getSensorToWorldScale - * @borrows MyAvatar.getControllerLeftHandMatrix as getControllerLeftHandMatrix - * @borrows MyAvatar.getControllerRightHandMatrix as getControllerRightHandMatrix - * @borrows MyAvatar.getDataRate as getDataRate - * @borrows MyAvatar.getUpdateRate as getUpdateRate - * @borrows MyAvatar.displayNameChanged as displayNameChanged - * @borrows MyAvatar.sessionDisplayNameChanged as sessionDisplayNameChanged - * @borrows MyAvatar.skeletonModelURLChanged as skeletonModelURLChanged - * @borrows MyAvatar.lookAtSnappingChanged as lookAtSnappingChanged - * @borrows MyAvatar.sessionUUIDChanged as sessionUUIDChanged - * @borrows MyAvatar.sendAvatarDataPacket as sendAvatarDataPacket - * @borrows MyAvatar.sendIdentityPacket as sendIdentityPacket - * @borrows MyAvatar.setJointMappingsFromNetworkReply as setJointMappingsFromNetworkReply - * @borrows MyAvatar.setSessionUUID as setSessionUUID - * @borrows MyAvatar.getAbsoluteJointRotationInObjectFrame as getAbsoluteJointRotationInObjectFrame - * @borrows MyAvatar.getAbsoluteJointTranslationInObjectFrame as getAbsoluteJointTranslationInObjectFrame - * @borrows MyAvatar.setAbsoluteJointRotationInObjectFrame as setAbsoluteJointRotationInObjectFrame - * @borrows MyAvatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame - * @borrows MyAvatar.getTargetScale as getTargetScale - * @borrows MyAvatar.resetLastSent as resetLastSent */ class ScriptableAvatar : public AvatarData, public Dependency { @@ -159,23 +102,25 @@ public: Q_INVOKABLE AnimationDetails getAnimationDetails(); /**jsdoc - * Get the names of all the joints in the current avatar. - * @function MyAvatar.getJointNames - * @returns {string[]} The joint names. - * @example Report the names of all the joints in your current avatar. - * print(JSON.stringify(MyAvatar.getJointNames())); - */ + * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
+ * Get the names of all the joints in the current avatar. + * @function Avatar.getJointNames + * @returns {string[]} The joint names. + * @example Report the names of all the joints in your current avatar. + * print(JSON.stringify(Avatar.getJointNames())); + */ Q_INVOKABLE virtual QStringList getJointNames() const override; /**jsdoc - * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by - * {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. - * @function MyAvatar.getJointIndex - * @param {string} name - The name of the joint. - * @returns {number} The index of the joint. - * @example Report the index of your avatar's left arm joint. - * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm")); - */ + * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
+ * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by + * {@link Avatar.getJointNames}. + * @function Avatar.getJointIndex + * @param {string} name - The name of the joint. + * @returns {number} The index of the joint. + * @example Report the index of your avatar's left arm joint. + * print(JSON.stringify(Avatar.getJointIndex("LeftArm")); + */ /// Returns the index of the joint with the specified name, or -1 if not found/unknown. Q_INVOKABLE virtual int getJointIndex(const QString& name) const override; @@ -193,6 +138,8 @@ public: bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } /**jsdoc + * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
+ * ####### Also need to resolve with MyAvatar.
* Potentially Very Expensive. Do not use. * @function Avatar.getAvatarEntityData * @returns {object} @@ -200,13 +147,15 @@ public: Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; /**jsdoc - * @function MyAvatar.setAvatarEntityData - * @param {object} avatarEntityData - */ + * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc. + * @function Avatar.setAvatarEntityData + * @param {object} avatarEntityData + */ Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; /**jsdoc - * @function MyAvatar.updateAvatarEntity + * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc. + * @function Avatar.updateAvatarEntity * @param {Uuid} entityID * @param {string} entityData */ @@ -214,12 +163,12 @@ public: public slots: /**jsdoc - * @function MyAvatar.update + * @function Avatar.update */ void update(float deltatime); /**jsdoc - * @function MyAvatar.setJointMappingsFromNetworkReply + * @function Avatar.setJointMappingsFromNetworkReply */ void setJointMappingsFromNetworkReply(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4e75c93403..d689d2f215 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -68,6 +68,46 @@ class MyAvatar : public Avatar { * @hifi-client-entity * @hifi-avatar * + * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. + * @property {Vec3} position + * @property {number} scale - Returns the clamped scale of the avatar. + * @property {number} density Read-only. + * @property {Vec3} handPosition + * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * Yaw is sometimes called "heading". + * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is + * sometimes called "elevation". + * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is + * sometimes called "bank". + * @property {Quat} orientation + * @property {Quat} headOrientation - The orientation of the avatar's head. + * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is + * sometimes called "elevation". + * @property {number} headYaw - The rotation left or right about an axis running from the base to the crown of the avatar's + * head. Yaw is sometimes called "heading". + * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is + * sometimes called "bank". + * @property {Vec3} velocity + * @property {Vec3} angularVelocity + * @property {number} audioLoudness + * @property {number} audioAverageLoudness + * @property {string} displayName + * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer + * rather than by Interface clients. The result is unique among all avatars present at the time. + * @property {boolean} lookAtSnappingEnabled + * @property {string} skeletonModelURL + * @property {AttachmentData[]} attachmentData + * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. + * @property {Uuid} sessionUUID Read-only. + * @property {Mat4} sensorToWorldMatrix Read-only. + * @property {Mat4} controllerLeftHandMatrix Read-only. + * @property {Mat4} controllerRightHandMatrix Read-only. + * @property {number} sensorToWorldScale Read-only. + * + * @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here. + * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the + * registration point of the 3D model. + * * @property {Vec3} qmlPosition - A synonym for position for use by QML. * * @property {boolean} shouldRenderLocally=true - If true then your avatar is rendered for you in Interface, @@ -165,50 +205,60 @@ class MyAvatar : public Avatar { * @property {boolean} isSitStandStateLocked * @property {boolean} allowTeleporting * - * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the - * registration point of the 3D model. - * - * @property {Vec3} position - * @property {number} scale - Returns the clamped scale of the avatar. - * @property {number} density Read-only. - * @property {Vec3} handPosition - * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. - * Yaw is sometimes called "heading". - * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is - * sometimes called "elevation". - * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is - * sometimes called "bank". - * - * @property {Quat} orientation - * @property {Quat} headOrientation - The orientation of the avatar's head. - * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is - * sometimes called "elevation". - * @property {number} headYaw - The rotation left or right about an axis running from the base to the crown of the avatar's - * head. Yaw is sometimes called "heading". - * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is - * sometimes called "bank". - * - * @property {Vec3} velocity - * @property {Vec3} angularVelocity - * - * @property {number} audioLoudness - * @property {number} audioAverageLoudness - * - * @property {string} displayName - * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer - * rather than by Interface clients. The result is unique among all avatars present at the time. - * @property {boolean} lookAtSnappingEnabled - * @property {string} skeletonModelURL - * @property {AttachmentData[]} attachmentData - * - * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. - * - * @property {Uuid} sessionUUID Read-only. - * - * @property {Mat4} sensorToWorldMatrix Read-only. - * @property {Mat4} controllerLeftHandMatrix Read-only. - * @property {Mat4} controllerRightHandMatrix Read-only. - * @property {number} sensorToWorldScale Read-only. + * @borrows Avatar.getDomainMinScale as getDomainMinScale + * @borrows Avatar.getDomainMaxScale as getDomainMaxScale + * @borrows Avatar.getEyeHeight as getEyeHeight + * @borrows Avatar.getHeight as getHeight + * @borrows Avatar.setHandState as setHandState + * @borrows Avatar.getHandState as getHandState + * @borrows Avatar.setRawJointData as setRawJointData + * @borrows Avatar.setJointData as setJointData + * @borrows Avatar.setJointRotation as setJointRotation + * @borrows Avatar.setJointTranslation as setJointTranslation + * @borrows Avatar.clearJointData as clearJointData + * @borrows Avatar.isJointDataValid as isJointDataValid + * @borrows Avatar.getJointRotation as getJointRotation + * @borrows Avatar.getJointTranslation as getJointTranslation + * @borrows Avatar.getJointRotations as getJointRotations + * @borrows Avatar.getJointTranslations as getJointTranslations + * @borrows Avatar.setJointRotations as setJointRotations + * @borrows Avatar.setJointTranslations as setJointTranslations + * @borrows Avatar.clearJointsData as clearJointsData + * @borrows Avatar.getJointIndex as getJointIndex + * @borrows Avatar.getJointNames as getJointNames + * @borrows Avatar.setBlendshape as setBlendshape + * @borrows Avatar.getAttachmentsVariant as getAttachmentsVariant + * @borrows Avatar.setAttachmentsVariant as setAttachmentsVariant + * @borrows Avatar.updateAvatarEntity as updateAvatarEntity + * @borrows Avatar.clearAvatarEntity as clearAvatarEntity + * @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected + * @borrows Avatar.getAttachmentData as getAttachmentData + * @borrows Avatar.setAttachmentData as setAttachmentData + * @borrows Avatar.attach as attach + * @borrows Avatar.detachOne as detachOne + * @borrows Avatar.detachAll as detachAll + * @borrows Avatar.getAvatarEntityData as getAvatarEntityData + * @borrows Avatar.setAvatarEntityData as setAvatarEntityData + * @borrows Avatar.getSensorToWorldMatrix as getSensorToWorldMatrix + * @borrows Avatar.getSensorToWorldScale as getSensorToWorldScale + * @borrows Avatar.getControllerLeftHandMatrix as getControllerLeftHandMatrix + * @borrows Avatar.getControllerRightHandMatrix as getControllerRightHandMatrix + * @borrows Avatar.getDataRate as getDataRate + * @borrows Avatar.getUpdateRate as getUpdateRate + * @borrows Avatar.displayNameChanged as displayNameChanged + * @borrows Avatar.sessionDisplayNameChanged as sessionDisplayNameChanged + * @borrows Avatar.skeletonModelURLChanged as skeletonModelURLChanged + * @borrows Avatar.lookAtSnappingChanged as lookAtSnappingChanged + * @borrows Avatar.sessionUUIDChanged as sessionUUIDChanged + * @borrows Avatar.sendAvatarDataPacket as sendAvatarDataPacket + * @borrows Avatar.sendIdentityPacket as sendIdentityPacket + * @borrows Avatar.setSessionUUID as setSessionUUID + * @borrows Avatar.getAbsoluteJointRotationInObjectFrame as getAbsoluteJointRotationInObjectFrame + * @borrows Avatar.getAbsoluteJointTranslationInObjectFrame as getAbsoluteJointTranslationInObjectFrame + * @borrows Avatar.setAbsoluteJointRotationInObjectFrame as setAbsoluteJointRotationInObjectFrame + * @borrows Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame + * @borrows Avatar.getTargetScale as getTargetScale + * @borrows Avatar.resetLastSent as resetLastSent */ // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) @@ -1314,7 +1364,7 @@ public slots: /**jsdoc * @function MyAvatar.restrictScaleFromDomainSettings - * @param {objecct} domainSettingsObject + * @param {object} domainSettingsObject */ void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject); @@ -1345,6 +1395,7 @@ public slots: /**jsdoc + * ####### Why Q_INVOKABLE? * @function MyAvatar.updateMotionBehaviorFromMenu */ Q_INVOKABLE void updateMotionBehaviorFromMenu(); @@ -1413,16 +1464,19 @@ public slots: bool getEnableMeshVisible() const override; /**jsdoc + * ####### TODO; Should this really be exposed in the API? * @function MyAvatar.storeAvatarEntityDataPayload */ void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload) override; /**jsdoc + * ####### Does override change functionality? If so, document here and don't borrow; if not, borrow and don't document here. * @function MyAvatar.clearAvatarEntity * @param {Uuid} entityID - * @param {boolean} requiresRemovalFromTree + * @param {boolean} [requiresRemovalFromTree] */ void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true) override; + /**jsdoc * @function MyAvatar.sanitizeAvatarEntityProperties */ diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 7ebb8cad01..1aa6829160 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -127,7 +127,12 @@ private: class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaModelPayload { Q_OBJECT - // This property has JSDoc in MyAvatar.h. + /*jsdoc + * @comment IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h. + * + * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the + * registration point of the 3D model. + */ Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 63396a59ac..7a4d235565 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -411,7 +411,43 @@ class ClientTraitsHandler; class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT - // The following properties have JSDoc in MyAvatar.h and ScriptableAvatar.h + // IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h and ScriptableAvatar.h. + /* + * @property {Vec3} position + * @property {number} scale - Returns the clamped scale of the avatar. + * @property {number} density Read-only. + * @property {Vec3} handPosition + * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * Yaw is sometimes called "heading". + * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is + * sometimes called "elevation". + * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is + * sometimes called "bank". + * @property {Quat} orientation + * @property {Quat} headOrientation - The orientation of the avatar's head. + * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is + * sometimes called "elevation". + * @property {number} headYaw - The rotation left or right about an axis running from the base to the crown of the avatar's + * head. Yaw is sometimes called "heading". + * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is + * sometimes called "bank". + * @property {Vec3} velocity + * @property {Vec3} angularVelocity + * @property {number} audioLoudness + * @property {number} audioAverageLoudness + * @property {string} displayName + * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer + * rather than by Interface clients. The result is unique among all avatars present at the time. + * @property {boolean} lookAtSnappingEnabled + * @property {string} skeletonModelURL + * @property {AttachmentData[]} attachmentData + * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. + * @property {Uuid} sessionUUID Read-only. + * @property {Mat4} sensorToWorldMatrix Read-only. + * @property {Mat4} controllerLeftHandMatrix Read-only. + * @property {Mat4} controllerRightHandMatrix Read-only. + * @property {number} sensorToWorldScale Read-only. + */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) Q_PROPERTY(float density READ getDensity) @@ -567,7 +603,7 @@ public: /**jsdoc * Returns the minimum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. - * @function MyAvatar.getDomainMinScale + * @function Avatar.getDomainMinScale * @returns {number} minimum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMinScale() const; @@ -575,7 +611,7 @@ public: /**jsdoc * Returns the maximum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. - * @function MyAvatar.getDomainMaxScale + * @function Avatar.getDomainMaxScale * @returns {number} maximum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMaxScale() const; @@ -591,7 +627,7 @@ public: /**jsdoc * Provides read only access to the current eye height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. - * @function MyAvatar.getEyeHeight + * @function Avatar.getEyeHeight * @returns {number} Eye height of avatar in meters. */ Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); } @@ -599,7 +635,7 @@ public: /**jsdoc * Provides read only access to the current height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. - * @function MyAvatar.getHeight + * @function Avatar.getHeight * @returns {number} Height of avatar in meters. */ Q_INVOKABLE virtual float getHeight() const; @@ -610,13 +646,13 @@ public: void setDomainMaximumHeight(float domainMaximumHeight); /**jsdoc - * @function MyAvatar.setHandState + * @function Avatar.setHandState * @param {string} state */ Q_INVOKABLE void setHandState(char s) { _handState = s; } /**jsdoc - * @function MyAvatar.getHandState + * @function Avatar.getHandState * @returns {string} */ Q_INVOKABLE char getHandState() const { return _handState; } @@ -624,7 +660,7 @@ public: const QVector& getRawJointData() const { return _jointData; } /**jsdoc - * @function MyAvatar.setRawJointData + * @function Avatar.setRawJointData * @param {JointData[]} data */ Q_INVOKABLE void setRawJointData(QVector data); @@ -636,7 +672,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointData + * @function Avatar.setJointData * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent. @@ -654,6 +690,8 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); @@ -664,7 +702,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointRotation + * @function Avatar.setJointRotation * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. */ @@ -677,7 +715,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointTranslation + * @function Avatar.setJointTranslation * @param {number} index - The index of the joint. * @param {Vec3} translation - The translation of the joint relative to its parent. */ @@ -687,13 +725,13 @@ public: * Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly faster than the function variation that specifies the joint name.

- * @function MyAvatar.clearJointData + * @function Avatar.clearJointData * @param {number} index - The index of the joint. */ Q_INVOKABLE virtual void clearJointData(int index); /**jsdoc - * @function MyAvatar.isJointDataValid + * @function Avatar.isJointDataValid * @param {number} index * @returns {boolean} */ @@ -702,7 +740,7 @@ public: /**jsdoc * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. - * @function MyAvatar.getJointRotation + * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. */ @@ -711,7 +749,7 @@ public: /**jsdoc * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. - * @function MyAvatar.getJointTranslation + * @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent. */ @@ -724,7 +762,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointData + * @function Avatar.setJointData * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent. @@ -738,7 +776,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointRotation + * @function Avatar.setJointRotation * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @example Set your avatar to its default T-pose then rotate its right arm.
@@ -759,6 +797,8 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation); @@ -769,7 +809,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointTranslation + * @function Avatar.setJointTranslation * @param {string} name - The name of the joint. * @param {Vec3} translation - The translation of the joint relative to its parent. * @example Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between @@ -782,6 +822,8 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointData("Neck"); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation); @@ -789,7 +831,7 @@ public: * Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly slower than the function variation that specifies the joint index.

- * @function MyAvatar.clearJointData + * @function Avatar.clearJointData * @param {string} name - The name of the joint. * @example Offset and restore the position of your avatar's head. * // Move your avatar's head up by 25cm from where it should be. @@ -799,11 +841,13 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointData("Neck"); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void clearJointData(const QString& name); /**jsdoc - * @function MyAvatar.isJointDataValid + * @function Avatar.isJointDataValid * @param {string} name * @returns {boolean} */ @@ -812,37 +856,43 @@ public: /**jsdoc * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. - * @function MyAvatar.getJointRotation + * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. * @example Report the rotation of your avatar's hips joint. * print(JSON.stringify(MyAvatar.getJointRotation("Hips"))); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const; /**jsdoc * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. - * @function MyAvatar.getJointTranslation + * @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent. * @example Report the translation of your avatar's hips joint. * print(JSON.stringify(MyAvatar.getJointRotation("Hips"))); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const; /**jsdoc * Get the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. - * @function MyAvatar.getJointRotations + * @function Avatar.getJointRotations * @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array * returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. * @example Report the rotations of all your avatar's joints. * print(JSON.stringify(MyAvatar.getJointRotations())); + * + * // Note: If using from the Avatar API, replace all "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QVector getJointRotations() const; /**jsdoc - * @function MyAvatar.getJointTranslations + * @function Avatar.getJointTranslations * @returns {Vec3[]} */ Q_INVOKABLE virtual QVector getJointTranslations() const; @@ -854,7 +904,7 @@ public: * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

- * @function MyAvatar.setJointRotations + * @function Avatar.setJointRotations * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the * array returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. * @example Set your avatar to its default T-pose then rotate its right arm.
@@ -880,11 +930,13 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointRotations(const QVector& jointRotations); /**jsdoc - * @function MyAvatar.setJointTranslations + * @function Avatar.setJointTranslations * @param {Vec3[]} translations */ Q_INVOKABLE virtual void setJointTranslations(const QVector& jointTranslations); @@ -892,7 +944,7 @@ public: /**jsdoc * Clear all joint translations and rotations that have been set by script. This restores all motion from the default * animation system including inverse kinematics for all joints. - * @function MyAvatar.clearJointsData + * @function Avatar.clearJointsData * @example Set your avatar to it's default T-pose for a while. * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; @@ -906,33 +958,39 @@ public: * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void clearJointsData(); /**jsdoc * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by * {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. - * @function MyAvatar.getJointIndex + * @function Avatar.getJointIndex * @param {string} name - The name of the joint. * @returns {number} The index of the joint. * @example Report the index of your avatar's left arm joint. * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm")); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ /// Returns the index of the joint with the specified name, or -1 if not found/unknown. Q_INVOKABLE virtual int getJointIndex(const QString& name) const; /**jsdoc * Get the names of all the joints in the current avatar. - * @function MyAvatar.getJointNames + * @function Avatar.getJointNames * @returns {string[]} The joint names. * @example Report the names of all the joints in your current avatar. * print(JSON.stringify(MyAvatar.getJointNames())); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QStringList getJointNames() const; /**jsdoc - * @function MyAvatar.setBlendshape + * @function Avatar.setBlendshape * @param {string} name * @param {number} value */ @@ -940,14 +998,14 @@ public: /**jsdoc - * @function MyAvatar.getAttachmentsVariant + * @function Avatar.getAttachmentsVariant * @returns {object} */ // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; /**jsdoc - * @function MyAvatar.setAttachmentsVariant + * @function Avatar.setAttachmentsVariant * @param {object} variant */ // FIXME: Can this name be improved? Can it be deprecated? @@ -956,21 +1014,22 @@ public: virtual void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload); /**jsdoc - * @function MyAvatar.updateAvatarEntity + * @function Avatar.updateAvatarEntity * @param {Uuid} entityID * @param {string} entityData */ Q_INVOKABLE virtual void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc - * @function MyAvatar.clearAvatarEntity + * @function Avatar.clearAvatarEntity * @param {Uuid} entityID + * @param {boolean} [requiresRemovalFromTree] */ Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc - * @function MyAvatar.setForceFaceTrackerConnected + * @function Avatar.setForceFaceTrackerConnected * @param {boolean} connected */ Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } @@ -1023,13 +1082,15 @@ public: /**jsdoc * Get information about all models currently attached to your avatar. - * @function MyAvatar.getAttachmentData + * @function Avatar.getAttachmentData * @returns {AttachmentData[]} Information about all models attached to your avatar. * @example Report the URLs of all current attachments. * var attachments = MyAvatar.getaAttachmentData(); * for (var i = 0; i < attachments.length; i++) { * print (attachments[i].modelURL); * } + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QVector getAttachmentData() const; @@ -1037,7 +1098,7 @@ public: * Set all models currently attached to your avatar. For example, if you retrieve attachment data using * {@link MyAvatar.getAttachmentData} or {@link Avatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the * changed data. You can also remove all attachments by using setting attachmentData to null. - * @function MyAvatar.setAttachmentData + * @function Avatar.setAttachmentData * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use * null to remove all attachments. * @example Remove a hat attachment if your avatar is wearing it. @@ -1051,6 +1112,8 @@ public: * break; * } * } + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); @@ -1059,7 +1122,7 @@ public: * stand on. *

Note: Attached models are models only; they are not entities and can not be manipulated using the {@link Entities} API. * Nor can you use this function to attach an entity (such as a sphere or a box) to your avatar.

- * @function MyAvatar.attach + * @function Avatar.attach * @param {string} modelURL - The URL of the model to attach. Models can be .FBX or .OBJ format. * @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}) to attach the model * to. @@ -1089,6 +1152,8 @@ public: * attachment.rotation, * attachment.scale, * attachment.isSoft); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), @@ -1097,7 +1162,7 @@ public: /**jsdoc * Detach the most recently attached instance of a particular model from either a specific joint or any joint. - * @function MyAvatar.detachOne + * @function Avatar.detachOne * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most * recently attached model is removed from which ever joint it was attached to. @@ -1106,7 +1171,7 @@ public: /**jsdoc * Detach all instances of a particular model from either a specific joint or all joints. - * @function MyAvatar.detachAll + * @function Avatar.detachAll * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is * detached from all joints. @@ -1136,13 +1201,13 @@ public: AABox getDefaultBubbleBox() const; /**jsdoc - * @function MyAvatar.getAvatarEntityData + * @function Avatar.getAvatarEntityData * @returns {object} */ Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; /**jsdoc - * @function MyAvatar.setAvatarEntityData + * @function Avatar.setAvatarEntityData * @param {object} avatarEntityData */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); @@ -1151,28 +1216,28 @@ public: AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc - * @function MyAvatar.getSensorToWorldMatrix + * @function Avatar.getSensorToWorldMatrix * @returns {Mat4} */ // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; /**jsdoc - * @function MyAvatar.getSensorToWorldScale + * @function Avatar.getSensorToWorldScale * @returns {number} */ // thread safe Q_INVOKABLE float getSensorToWorldScale() const; /**jsdoc - * @function MyAvatar.getControllerLeftHandMatrix + * @function Avatar.getControllerLeftHandMatrix * @returns {Mat4} */ // thread safe Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; /**jsdoc - * @function MyAvatar.getControllerRightHandMatrix + * @function Avatar.getControllerRightHandMatrix * @returns {Mat4} */ // thread safe @@ -1180,14 +1245,14 @@ public: /**jsdoc - * @function MyAvatar.getDataRate + * @function Avatar.getDataRate * @param {string} [rateName=""] * @returns {number} */ Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const; /**jsdoc - * @function MyAvatar.getUpdateRate + * @function Avatar.getUpdateRate * @param {string} [rateName=""] * @returns {number} */ @@ -1235,32 +1300,32 @@ public: signals: /**jsdoc - * @function MyAvatar.displayNameChanged + * @function Avatar.displayNameChanged * @returns {Signal} */ void displayNameChanged(); /**jsdoc - * @function MyAvatar.sessionDisplayNameChanged + * @function Avatar.sessionDisplayNameChanged * @returns {Signal} */ void sessionDisplayNameChanged(); /**jsdoc - * @function MyAvatar.skeletonModelURLChanged + * @function Avatar.skeletonModelURLChanged * @returns {Signal} */ void skeletonModelURLChanged(); /**jsdoc - * @function MyAvatar.lookAtSnappingChanged + * @function Avatar.lookAtSnappingChanged * @param {boolean} enabled * @returns {Signal} */ void lookAtSnappingChanged(bool enabled); /**jsdoc - * @function MyAvatar.sessionUUIDChanged + * @function Avatar.sessionUUIDChanged * @returns {Signal} */ void sessionUUIDChanged(); @@ -1268,18 +1333,18 @@ signals: public slots: /**jsdoc - * @function MyAvatar.sendAvatarDataPacket + * @function Avatar.sendAvatarDataPacket * @param {boolean} [sendAll=false] */ virtual int sendAvatarDataPacket(bool sendAll = false); /**jsdoc - * @function MyAvatar.sendIdentityPacket + * @function Avatar.sendIdentityPacket */ int sendIdentityPacket(); /**jsdoc - * @function MyAvatar.setSessionUUID + * @function Avatar.setSessionUUID * @param {Uuid} sessionUUID */ virtual void setSessionUUID(const QUuid& sessionUUID) { @@ -1294,21 +1359,21 @@ public slots: } /**jsdoc - * @function MyAvatar.getAbsoluteJointRotationInObjectFrame + * @function Avatar.getAbsoluteJointRotationInObjectFrame * @param {number} index * @returns {Quat} */ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; /**jsdoc - * @function MyAvatar.getAbsoluteJointTranslationInObjectFrame + * @function Avatar.getAbsoluteJointTranslationInObjectFrame * @param {number} index * @returns {Vec3} */ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; /**jsdoc - * @function MyAvatar.setAbsoluteJointRotationInObjectFrame + * @function Avatar.setAbsoluteJointRotationInObjectFrame * @param {number} index * @param {Quat} rotation * @returns {boolean} @@ -1316,7 +1381,7 @@ public slots: virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } /**jsdoc - * @function MyAvatar.setAbsoluteJointTranslationInObjectFrame + * @function Avatar.setAbsoluteJointTranslationInObjectFrame * @param {number} index * @param {Vec3} translation * @returns {boolean} @@ -1324,13 +1389,13 @@ public slots: virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } /**jsdoc - * @function MyAvatar.getTargetScale + * @function Avatar.getTargetScale * @returns {number} */ float getTargetScale() const { return _targetScale; } // why is this a slot? /**jsdoc - * @function MyAvatar.resetLastSent + * @function Avatar.resetLastSent */ void resetLastSent() { _lastToByteArray = 0; } From 01119a5b5d980be426bcb20bb773d2be6591ed9e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 21 Feb 2019 11:19:29 +1300 Subject: [PATCH 04/88] Fill in Agent API JSDoc --- .../src/AgentScriptingInterface.h | 31 +++++++++++++------ .../src/avatars/ScriptableAvatar.h | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/AgentScriptingInterface.h b/assignment-client/src/AgentScriptingInterface.h index 9fa7688778..1f592a9f18 100644 --- a/assignment-client/src/AgentScriptingInterface.h +++ b/assignment-client/src/AgentScriptingInterface.h @@ -18,16 +18,25 @@ #include "Agent.h" /**jsdoc + * The Agent API enables an assignment client to emulate an avatar. In particular, setting isAvatar = + * true connects the assignment client to the avatar and audio mixers and enables the {@link Avatar} API to be used. + * * @namespace Agent * * @hifi-assignment-client * - * @property {boolean} isAvatar - * @property {boolean} isPlayingAvatarSound Read-only. - * @property {boolean} isListeningToAudioStream - * @property {boolean} isNoiseGateEnabled - * @property {number} lastReceivedAudioLoudness Read-only. - * @property {Uuid} sessionUUID Read-only. + * @property {boolean} isAvatar - true if the assignment client script is emulating an avatar, otherwise + * false. + * @property {boolean} isPlayingAvatarSound - true if the script has a sound to play, otherwise false. + * Sounds are played when isAvatar is true, from the position and with the orientation of the + * scripted avatar's head.Read-only. + * @property {boolean} isListeningToAudioStream - true if the agent is "listening" to the audio stream from the + * domain, otherwise false. + * @property {boolean} isNoiseGateEnabled - true if the noise gate is enabled, otherwise false. When + * enabled, the input audio stream is blocked (fully attenuated) if it falls below an adaptive threshold. + * @property {number} lastReceivedAudioLoudness - The current loudness of the audio input, nominal range 0.0 (no + * sound) – 1.0 (the onset of clipping). Read-only. + * @property {Uuid} sessionUUID - The unique ID associated with the agent's current session in the domain. Read-only. */ class AgentScriptingInterface : public QObject { Q_OBJECT @@ -54,20 +63,24 @@ public: public slots: /**jsdoc + * Set whether or not the script should emulate an avatar. * @function Agent.setIsAvatar - * @param {boolean} isAvatar + * @param {boolean} isAvatar - true if the script should act as if an avatar, otherwise false. */ void setIsAvatar(bool isAvatar) const { _agent->setIsAvatar(isAvatar); } /**jsdoc + * Check whether or not the script is emulating an avatar. * @function Agent.isAvatar - * @returns {boolean} + * @returns {boolean} true if the script is acting as if an avatar, otherwise false. */ bool isAvatar() const { return _agent->isAvatar(); } /**jsdoc + * Play a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless + * isAvatar == true. * @function Agent.playAvatarSound - * @param {object} avatarSound + * @param {SoundObject} avatarSound - The sound to play. */ void playAvatarSound(SharedSoundPointer avatarSound) const { _agent->playAvatarSound(avatarSound); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index b2ad4527b0..6b78f666e1 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -20,7 +20,7 @@ /**jsdoc * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the - * {@link MyAvatar} API. + * {@link MyAvatar} API. To enable this API, set {@link Agent|Agent.isAvatatr} to true. * * @namespace Avatar * From 7f1ae634391eb8dce84f086ad60835b109c93b2f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 21 Feb 2019 14:30:38 +1300 Subject: [PATCH 05/88] Revise current Avatar API JSDoc --- .../src/avatars/ScriptableAvatar.h | 48 +++++++---------- libraries/avatars/src/AvatarData.h | 54 ++++++++++--------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 6b78f666e1..3cf10cc129 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -28,7 +28,8 @@ * * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. * @property {Vec3} position - * @property {number} scale - Returns the clamped scale of the avatar. + * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 + * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. * @property {number} density Read-only. * @property {Vec3} handPosition * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. @@ -50,8 +51,9 @@ * @property {number} audioLoudness * @property {number} audioAverageLoudness * @property {string} displayName - * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer - * rather than by Interface clients. The result is unique among all avatars present at the time. + * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the + * avatar mixer rather than by Interface clients. The result is unique among all avatars present on the domain at the + * time. * @property {boolean} lookAtSnappingEnabled * @property {string} skeletonModelURL * @property {AttachmentData[]} attachmentData @@ -102,24 +104,12 @@ public: Q_INVOKABLE AnimationDetails getAnimationDetails(); /**jsdoc - * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
- * Get the names of all the joints in the current avatar. - * @function Avatar.getJointNames - * @returns {string[]} The joint names. - * @example Report the names of all the joints in your current avatar. - * print(JSON.stringify(Avatar.getJointNames())); + * @comment Uses the base class's JSDoc. */ Q_INVOKABLE virtual QStringList getJointNames() const override; /**jsdoc - * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
- * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by - * {@link Avatar.getJointNames}. - * @function Avatar.getJointIndex - * @param {string} name - The name of the joint. - * @returns {number} The index of the joint. - * @example Report the index of your avatar's left arm joint. - * print(JSON.stringify(Avatar.getJointIndex("LeftArm")); + * @comment Uses the base class's JSDoc. */ /// Returns the index of the joint with the specified name, or -1 if not found/unknown. Q_INVOKABLE virtual int getJointIndex(const QString& name) const override; @@ -137,39 +127,37 @@ public: void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement); bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } - /**jsdoc - * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc.
- * ####### Also need to resolve with MyAvatar.
- * Potentially Very Expensive. Do not use. + /**jsdoc + * Get the avatar entities as binary data. + *

Warning: Potentially a very expensive call. Do not use if possible.

* @function Avatar.getAvatarEntityData - * @returns {object} + * @returns {AvatarEntityMap} */ Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; /**jsdoc - * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc. + * Set the avatar entities from binary data. + *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.setAvatarEntityData - * @param {object} avatarEntityData + * @param {AvatarEntityMap} avatarEntityData */ Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; /**jsdoc - * ####### TODO: If this override changes the function use @override and do JSDoc here, otherwise @comment that uses base class's JSDoc. - * @function Avatar.updateAvatarEntity - * @param {Uuid} entityID - * @param {string} entityData + * @comment Uses the base class's JSDoc. */ Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override; public slots: /**jsdoc * @function Avatar.update + * @param {number} deltaTime */ void update(float deltatime); /**jsdoc - * @function Avatar.setJointMappingsFromNetworkReply - */ + * @function Avatar.setJointMappingsFromNetworkReply + */ void setJointMappingsFromNetworkReply(); private: diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 7a4d235565..0a5509f46c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -61,6 +61,10 @@ using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; +/**jsdoc + * An object with the UUIDs of avatar entities as keys and binary blobs, being the entity properties, as values. + * @typedef {Object.>} AvatarEntityMap + */ using AvatarEntityMap = QMap; using PackedAvatarEntityMap = QMap; // similar to AvatarEntityMap, but different internal format using AvatarEntityIDs = QSet; @@ -414,7 +418,8 @@ class AvatarData : public QObject, public SpatiallyNestable { // IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h and ScriptableAvatar.h. /* * @property {Vec3} position - * @property {number} scale - Returns the clamped scale of the avatar. + * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 + * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. * @property {number} density Read-only. * @property {Vec3} handPosition * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. @@ -436,8 +441,9 @@ class AvatarData : public QObject, public SpatiallyNestable { * @property {number} audioLoudness * @property {number} audioAverageLoudness * @property {string} displayName - * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer - * rather than by Interface clients. The result is unique among all avatars present at the time. + * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the + * avatar mixer rather than by Interface clients. The result is unique among all avatars present on the domain at the + * time. * @property {boolean} lookAtSnappingEnabled * @property {string} skeletonModelURL * @property {AttachmentData[]} attachmentData @@ -601,18 +607,18 @@ public: virtual bool getHasAudioEnabledFaceMovement() const { return false; } /**jsdoc - * Returns the minimum scale allowed for this avatar in the current domain. + * Get the minimum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMinScale - * @returns {number} minimum scale allowed for this avatar in the current domain. + * @returns {number} The minimum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMinScale() const; /**jsdoc - * Returns the maximum scale allowed for this avatar in the current domain. + * Get the maximum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMaxScale - * @returns {number} maximum scale allowed for this avatar in the current domain. + * @returns {number} The maximum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMaxScale() const; @@ -625,18 +631,18 @@ public: virtual bool canMeasureEyeHeight() const { return false; } /**jsdoc - * Provides read only access to the current eye height of the avatar. + * Get the current eye height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getEyeHeight - * @returns {number} Eye height of avatar in meters. + * @returns {number} The eye height of the avatar. */ Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); } /**jsdoc - * Provides read only access to the current height of the avatar. + * Get the current height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getHeight - * @returns {number} Height of avatar in meters. + * @returns {number} The height of the avatar. */ Q_INVOKABLE virtual float getHeight() const; @@ -739,7 +745,7 @@ public: /**jsdoc * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -748,7 +754,7 @@ public: /**jsdoc * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent. @@ -855,7 +861,7 @@ public: /**jsdoc * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -868,7 +874,7 @@ public: /**jsdoc * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent. @@ -883,7 +889,7 @@ public: * Get the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. * @function Avatar.getJointRotations * @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array - * returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. + * returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example Report the rotations of all your avatar's joints. * print(JSON.stringify(MyAvatar.getJointRotations())); * @@ -906,7 +912,7 @@ public: * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointRotations * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the - * array returned by {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. + * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example Set your avatar to its default T-pose then rotate its right arm.
* Avatar in T-pose * // Set all joint translations and rotations to defaults. @@ -965,10 +971,10 @@ public: /**jsdoc * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by - * {@link MyAvatar.getJointNames} or {@link Avatar.getJointNames}. + * {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @function Avatar.getJointIndex * @param {string} name - The name of the joint. - * @returns {number} The index of the joint. + * @returns {number} The index of the joint if valid, otherwise -1. * @example Report the index of your avatar's left arm joint. * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm")); * @@ -1016,14 +1022,14 @@ public: /**jsdoc * @function Avatar.updateAvatarEntity * @param {Uuid} entityID - * @param {string} entityData + * @param {Array.} entityData */ Q_INVOKABLE virtual void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc * @function Avatar.clearAvatarEntity * @param {Uuid} entityID - * @param {boolean} [requiresRemovalFromTree] + * @param {boolean} [requiresRemovalFromTree=true] */ Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); @@ -1201,14 +1207,12 @@ public: AABox getDefaultBubbleBox() const; /**jsdoc - * @function Avatar.getAvatarEntityData - * @returns {object} + * @comment Documented in derived classes' JSDoc. */ Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; /**jsdoc - * @function Avatar.setAvatarEntityData - * @param {object} avatarEntityData + * @comment Documented in derived classes' JSDoc. */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); From 0c59f983daacdd96ab12939e90361ce2a3186756 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 27 Feb 2019 09:34:05 +1300 Subject: [PATCH 06/88] Agent JSDoc tidying --- assignment-client/src/AgentScriptingInterface.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/AgentScriptingInterface.h b/assignment-client/src/AgentScriptingInterface.h index 1f592a9f18..1242634dd5 100644 --- a/assignment-client/src/AgentScriptingInterface.h +++ b/assignment-client/src/AgentScriptingInterface.h @@ -63,21 +63,27 @@ public: public slots: /**jsdoc - * Set whether or not the script should emulate an avatar. + * Sets whether or not the script should emulate an avatar. * @function Agent.setIsAvatar * @param {boolean} isAvatar - true if the script should act as if an avatar, otherwise false. + * @example Make an assignment client script emulate an avatar. + * (function () { + * Agent.setIsAvatar(true); + * Avatar.displayName = "AC avatar"; + * print("Position: " + JSON.stringify(Avatar.position)); // 0, 0, 0 + * }()); */ void setIsAvatar(bool isAvatar) const { _agent->setIsAvatar(isAvatar); } /**jsdoc - * Check whether or not the script is emulating an avatar. + * Checks whether or not the script is emulating an avatar. * @function Agent.isAvatar * @returns {boolean} true if the script is acting as if an avatar, otherwise false. */ bool isAvatar() const { return _agent->isAvatar(); } /**jsdoc - * Play a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless + * Plays a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless * isAvatar == true. * @function Agent.playAvatarSound * @param {SoundObject} avatarSound - The sound to play. From e79594ef53458f33f63ea5c135b48ce2d9698152 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 27 Feb 2019 09:53:11 +1300 Subject: [PATCH 07/88] Fill in and tidy Avatar JSDoc --- .../src/avatars/ScriptableAvatar.h | 82 ++++-- .../src/avatars-renderer/Avatar.h | 22 +- libraries/avatars/src/AvatarData.cpp | 85 +++++- libraries/avatars/src/AvatarData.h | 277 ++++++++++++------ libraries/shared/src/RegisteredMetaTypes.cpp | 26 +- 5 files changed, 352 insertions(+), 140 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 3cf10cc129..2d8dce23de 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -22,23 +22,27 @@ * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the * {@link MyAvatar} API. To enable this API, set {@link Agent|Agent.isAvatatr} to true. * + *

For Interface, client entity, and avatar scripts, see {@link MyAvatar}.

+ * * @namespace Avatar * * @hifi-assignment-client * * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. - * @property {Vec3} position + * @property {Vec3} position - The position of the avatar. * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. - * @property {number} density Read-only. - * @property {Vec3} handPosition + * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in + * the application of physics. Read-only. + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar + * but is otherwise not used or changed by Interface. * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is * sometimes called "bank". - * @property {Quat} orientation + * @property {Quat} orientation - The orientation of the avatar. * @property {Quat} headOrientation - The orientation of the avatar's head. * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is * sometimes called "elevation". @@ -46,24 +50,31 @@ * head. Yaw is sometimes called "heading". * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is * sometimes called "bank". - * @property {Vec3} velocity - * @property {Vec3} angularVelocity - * @property {number} audioLoudness - * @property {number} audioAverageLoudness - * @property {string} displayName + * @property {Vec3} velocity - The current velocity of the avatar. + * @property {Vec3} angularVelocity - The current angular velocity of the avatar. + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting + * into the domain. + * @property {string} displayName - The avatar's display name. * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the - * avatar mixer rather than by Interface clients. The result is unique among all avatars present on the domain at the + * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the * time. - * @property {boolean} lookAtSnappingEnabled - * @property {string} skeletonModelURL - * @property {AttachmentData[]} attachmentData + * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's + * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. + * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. - * @property {Uuid} sessionUUID Read-only. - * @property {Mat4} sensorToWorldMatrix Read-only. - * @property {Mat4} controllerLeftHandMatrix Read-only. - * @property {Mat4} controllerRightHandMatrix Read-only. - * @property {number} sensorToWorldScale Read-only. - * + * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * avatar's size, orientation, and position in the virtual world. Read-only. + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the + * avatar. Read-only. + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * avatar. Read-only. + * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's + * size in the virtual world. Read-only. */ class ScriptableAvatar : public AvatarData, public Dependency { @@ -77,15 +88,17 @@ public: ScriptableAvatar(); /**jsdoc + * Starts playing an animation on the avatar. * @function Avatar.startAnimation - * @param {string} url - * @param {number} [fps=30] - * @param {number} [priority=1] - * @param {boolean} [loop=false] - * @param {boolean} [hold=false] - * @param {number} [firstFrame=0] - * @param {number} [lastFrame=3.403e+38] - * @param {string[]} [maskedJoints=[]] + * @param {string} url - The URL to the animation file. Animation files need to be .FBX format but only need to contain + * the avatar skeleton and animation data. + * @param {number} [fps=30] - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. + * @param {number} [priority=1] - Not used. + * @param {boolean} [loop=false] - true if the animation should loop, false if it shouldn't. + * @param {boolean} [hold=false] - Not used. + * @param {number} [firstFrame=0] - The frame the animation should start at. + * @param {number} [lastFrame=3.403e+38] - The frame the animation should stop at. + * @param {string[]} [maskedJoints=[]] - The names of joints that should not be animated. */ /// Allows scripts to run animations. Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, @@ -93,13 +106,15 @@ public: const QStringList& maskedJoints = QStringList()); /**jsdoc + * Stops playing the current animation. * @function Avatar.stopAnimation */ Q_INVOKABLE void stopAnimation(); /**jsdoc + * Gets the details of the current avatar animation that is being or was recently played. * @function Avatar.getAnimationDetails - * @returns {Avatar.AnimationDetails} + * @returns {Avatar.AnimationDetails} The current or recent avatar animation. */ Q_INVOKABLE AnimationDetails getAnimationDetails(); @@ -116,6 +131,9 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; + /**jsdoc + * @comment Uses the base class's JSDoc. + */ int sendAvatarDataPacket(bool sendAll = false) override; virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false) override; @@ -128,7 +146,7 @@ public: bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } /**jsdoc - * Get the avatar entities as binary data. + * Gets the avatar entities as binary data. *

Warning: Potentially a very expensive call. Do not use if possible.

* @function Avatar.getAvatarEntityData * @returns {AvatarEntityMap} @@ -136,7 +154,7 @@ public: Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; /**jsdoc - * Set the avatar entities from binary data. + * Sets the avatar entities from binary data. *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.setAvatarEntityData * @param {AvatarEntityMap} avatarEntityData @@ -151,12 +169,14 @@ public: public slots: /**jsdoc * @function Avatar.update - * @param {number} deltaTime + * @param {number} deltaTime - Delta time. + * @deprecated This function is deprecated and will be removed. */ void update(float deltatime); /**jsdoc * @function Avatar.setJointMappingsFromNetworkReply + * @deprecated This function is deprecated and will be removed. */ void setJointMappingsFromNetworkReply(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 1aa6829160..98aa255641 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -219,7 +219,7 @@ public: * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame - * @param index {number} index number + * @param index {number} -The joint index. * @returns {Quat} The rotation of this joint in avatar coordinates. */ Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const; @@ -229,7 +229,7 @@ public: * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame - * @param index {number} index number + * @param index {number} - The joint index. * @returns {Vec3} The position of this joint in avatar coordinates. */ Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const; @@ -238,7 +238,25 @@ public: virtual glm::vec3 getAbsoluteJointScaleInObjectFrame(int index) const override; virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + + /**jsdoc + * Sets the rotation of a joint relative to the avatar. + *

Warning: Not able to be used in the MyAvatar API.

+ * @function MyAvatar.setAbsoluteJointRotationInObjectFrame + * @param {number} index - The index of the joint. Not used. + * @param {Quat} rotation - The rotation of the joint relative to the avatar. Not used. + * @returns {boolean} false. + */ virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } + + /**jsdoc + * Sets the translation of a joint relative to the avatar. + *

Warning: Not able to be used in the MyAvatar API.

+ * @function MyAvatar.setAbsoluteJointTranslationInObjectFrame + * @param {number} index - The index of the joint. Not used. + * @param {Vec3} translation - The translation of the joint relative to the avatar. Not used. + * @returns {boolean} false. + */ virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } // world-space to avatar-space rigconversion functions diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c733cfa291..88949900ce 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1425,6 +1425,47 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { return numBytesRead; } +/**jsdoc + * The avatar mixer data comprises different types of data, with the data rates of each being tracked in kbps. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Rate NameDescription
"globalPosition"Incoming global position.
"localPosition"Incoming local position.
"avatarBoundingBox"Incoming avatar bounding box.
"avatarOrientation"Incoming avatar orientation.
"avatarScale"Incoming avatar scale.
"lookAtPosition"Incoming look-at position.
"audioLoudness"Incoming audio loudness.
"sensorToWorkMatrix"Incoming sensor-to-world matrix.
"additionalFlags"Incoming additional avatar flags.
"parentInfo"Incoming parent information.
"faceTracker"Incoming face tracker data.
"jointData"Incoming joint data.
"jointDefaultPoseFlagsRate"Incoming joint default pose flags.
"farGrabJointRate"Incoming far grab joint.
"globalPositionOutbound"Outgoing global position.
"localPositionOutbound"Outgoing local position.
"avatarBoundingBoxOutbound"Outgoing avatar bounding box.
"avatarOrientationOutbound"Outgoing avatar orientation.
"avatarScaleOutbound"Outgoing avatar scale.
"lookAtPositionOutbound"Outgoing look-at position.
"audioLoudnessOutbound"Outgoing audio loudness.
"sensorToWorkMatrixOutbound"Outgoing sensor-to-world matrix.
"additionalFlagsOutbound"Outgoing additional avatar flags.
"parentInfoOutbound"Outgoing parent information.
"faceTrackerOutbound"Outgoing face tracker data.
"jointDataOutbound"Outgoing joint data.
"jointDefaultPoseFlagsOutbound"Outgoing joint default pose flags.
""When no rate name is specified, the total incoming data rate is provided.
+ * + * @typedef {string} AvatarDataRate + */ float AvatarData::getDataRate(const QString& rateName) const { if (rateName == "") { return _parseBufferRate.rate() / BYTES_PER_KILOBIT; @@ -1486,6 +1527,35 @@ float AvatarData::getDataRate(const QString& rateName) const { return 0.0f; } +/**jsdoc + * The avatar mixer data comprises different types of data updated at different rates, in Hz. + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + *
Rate NameDescription
"globalPosition"Global position.
"localPosition"Local position.
"avatarBoundingBox"Avatar bounding box.
"avatarOrientation"Avatar orientation.
"avatarScale"Avatar scale.
"lookAtPosition"Look-at position.
"audioLoudness"Audio loudness.
"sensorToWorkMatrix"Sensor-to-world matrix.
"additionalFlags"Additional avatar flags.
"parentInfo"Parent information.
"faceTracker"Face tracker data.
"jointData"Joint data.
"farGrabJointData"Far grab joint data.
""When no rate name is specified, the overall update rate is provided.
+ * + * @typedef {string} AvatarUpdateRate + */ float AvatarData::getUpdateRate(const QString& rateName) const { if (rateName == "") { return _parseBufferUpdateRate.rate(); @@ -2721,13 +2791,16 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const } /**jsdoc + * Information on an attachment worn by the avatar. * @typedef {object} AttachmentData - * @property {string} modelUrl - * @property {string} jointName - * @property {Vec3} translation - * @property {Vec3} rotation - * @property {number} scale - * @property {boolean} soft + * @property {string} modelUrl - The URL of the model file. Models can be .FBX or .OBJ format. + * @property {string} jointName - The offset to apply to the model relative to the joint position. + * @property {Vec3} translation - The offset from the joint that the attachment is positioned at. + * @property {Vec3} rotation - The rotation applied to the model relative to the joint orientation. + * @property {number} scale - The scale applied to the attachment model. + * @property {boolean} soft - If true and the model has a skeleton, the bones of the attached model's skeleton are + * rotated to fit the avatar's current pose. If true, the translation, rotation, and + * scale parameters are ignored. */ QVariant AttachmentData::toVariant() const { QVariantMap result; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0a5509f46c..a20518076f 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -116,7 +116,24 @@ const int PROCEDURAL_EYE_FACE_MOVEMENT = 9; // 10th bit const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit - +/**jsdoc + *

The pointing state of the hands is specified by the following values: +

+ * + * + * + * + * + * + * + * + * + * + *
ValueDescription
0No hand is pointing.
1The left hand is pointing.
2The right hand is pointing.
4It is the index finger that is pointing.
+ *

The values for the hand states are added together to give the HandState value. For example, if the left + * hand's finger is pointing, the value is 1 + 4 == 5. + * @typedef {number} HandState + */ const char HAND_STATE_NULL = 0; const char LEFT_HAND_POINTING_FLAG = 1; const char RIGHT_HAND_POINTING_FLAG = 2; @@ -417,18 +434,20 @@ class AvatarData : public QObject, public SpatiallyNestable { // IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h and ScriptableAvatar.h. /* - * @property {Vec3} position + * @property {Vec3} position - The position of the avatar. * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. - * @property {number} density Read-only. - * @property {Vec3} handPosition + * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in + * the application of physics. Read-only. + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar + * but is otherwise not used or changed by Interface. * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is * sometimes called "bank". - * @property {Quat} orientation + * @property {Quat} orientation - The orientation of the avatar. * @property {Quat} headOrientation - The orientation of the avatar's head. * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is * sometimes called "elevation". @@ -436,23 +455,31 @@ class AvatarData : public QObject, public SpatiallyNestable { * head. Yaw is sometimes called "heading". * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is * sometimes called "bank". - * @property {Vec3} velocity - * @property {Vec3} angularVelocity - * @property {number} audioLoudness - * @property {number} audioAverageLoudness - * @property {string} displayName + * @property {Vec3} velocity - The current velocity of the avatar. + * @property {Vec3} angularVelocity - The current angular velocity of the avatar. + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting + * into the domain. + * @property {string} displayName - The avatar's display name. * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the - * avatar mixer rather than by Interface clients. The result is unique among all avatars present on the domain at the + * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the * time. - * @property {boolean} lookAtSnappingEnabled - * @property {string} skeletonModelURL - * @property {AttachmentData[]} attachmentData + * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's + * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. + * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. - * @property {Uuid} sessionUUID Read-only. - * @property {Mat4} sensorToWorldMatrix Read-only. - * @property {Mat4} controllerLeftHandMatrix Read-only. - * @property {Mat4} controllerRightHandMatrix Read-only. - * @property {number} sensorToWorldScale Read-only. + * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * avatar's size, orientation, and position in the virtual world. Read-only. + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the + * avatar. Read-only. + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * avatar. Read-only. + * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's + * size in the virtual world. Read-only. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) @@ -607,7 +634,7 @@ public: virtual bool getHasAudioEnabledFaceMovement() const { return false; } /**jsdoc - * Get the minimum scale allowed for this avatar in the current domain. + * Gets the minimum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMinScale * @returns {number} The minimum scale allowed for this avatar in the current domain. @@ -615,7 +642,7 @@ public: Q_INVOKABLE float getDomainMinScale() const; /**jsdoc - * Get the maximum scale allowed for this avatar in the current domain. + * Gets the maximum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMaxScale * @returns {number} The maximum scale allowed for this avatar in the current domain. @@ -631,7 +658,7 @@ public: virtual bool canMeasureEyeHeight() const { return false; } /**jsdoc - * Get the current eye height of the avatar. + * Gets the current eye height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getEyeHeight * @returns {number} The eye height of the avatar. @@ -639,7 +666,7 @@ public: Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); } /**jsdoc - * Get the current height of the avatar. + * Gets the current height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getHeight * @returns {number} The height of the avatar. @@ -652,27 +679,33 @@ public: void setDomainMaximumHeight(float domainMaximumHeight); /**jsdoc + * Sets the pointing state of the hands to control where the laser emanates from. If the right index finger is pointing, the + * laser emanates from the tip of that finger, otherwise it emanates from the palm. * @function Avatar.setHandState - * @param {string} state + * @param {HandState} state - The pointing state of the hand. */ Q_INVOKABLE void setHandState(char s) { _handState = s; } /**jsdoc + * Gets the pointing state of the hands to control where the laser emanates from. If the right index finger is pointing, the + * laser emanates from the tip of that finger, otherwise it emanates from the palm. * @function Avatar.getHandState - * @returns {string} + * @returns {HandState} The pointing state of the hand. */ Q_INVOKABLE char getHandState() const { return _handState; } const QVector& getRawJointData() const { return _jointData; } /**jsdoc + * Sets joint translations and rotations from raw joint data. * @function Avatar.setRawJointData - * @param {JointData[]} data + * @param {JointData[]} data - The raw joint data. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setRawJointData(QVector data); /**jsdoc - * Set a specific joint's rotation and position relative to its parent. + * Sets a specific joint's rotation and position relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -702,7 +735,7 @@ public: Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); /**jsdoc - * Set a specific joint's rotation relative to its parent. + * Sets a specific joint's rotation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -715,7 +748,7 @@ public: Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); /**jsdoc - * Set a specific joint's translation relative to its parent. + * Sets a specific joint's translation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -728,7 +761,7 @@ public: Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation); /**jsdoc - * Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default + * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly faster than the function variation that specifies the joint name.

* @function Avatar.clearJointData @@ -737,14 +770,15 @@ public: Q_INVOKABLE virtual void clearJointData(int index); /**jsdoc + * Checks that the data for a joint are valid. * @function Avatar.isJointDataValid - * @param {number} index - * @returns {boolean} + * @param {number} index - The index of the joint. + * @returns {boolean} true if the joint data is valid, false if not. */ Q_INVOKABLE bool isJointDataValid(int index) const; /**jsdoc - * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see + * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. @@ -753,7 +787,7 @@ public: Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; /**jsdoc - * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see + * Gets the translation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointTranslation * @param {number} index - The index of the joint. @@ -762,7 +796,7 @@ public: Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; /**jsdoc - * Set a specific joint's rotation and position relative to its parent. + * Sets a specific joint's rotation and position relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -776,7 +810,7 @@ public: Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); /**jsdoc - * Set a specific joint's rotation relative to its parent. + * Sets a specific joint's rotation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -809,7 +843,7 @@ public: Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation); /**jsdoc - * Set a specific joint's translation relative to its parent. + * Sets a specific joint's translation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -834,7 +868,7 @@ public: Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation); /**jsdoc - * Clear joint translations and rotations set by script for a specific joint. This restores all motion from the default + * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly slower than the function variation that specifies the joint index.

* @function Avatar.clearJointData @@ -853,14 +887,15 @@ public: Q_INVOKABLE virtual void clearJointData(const QString& name); /**jsdoc + * Checks that the data for a joint are valid. * @function Avatar.isJointDataValid - * @param {string} name - * @returns {boolean} + * @param {string} name - The name of the joint. + * @returns {boolean} true if the joint data is valid, false if not. */ Q_INVOKABLE virtual bool isJointDataValid(const QString& name) const; /**jsdoc - * Get the rotation of a joint relative to its parent. For information on the joint hierarchy used, see + * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. @@ -873,7 +908,7 @@ public: Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const; /**jsdoc - * Get the translation of a joint relative to its parent. For information on the joint hierarchy used, see + * Gets the translation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointTranslation * @param {number} name - The name of the joint. @@ -886,7 +921,7 @@ public: Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const; /**jsdoc - * Get the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. + * Gets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. * @function Avatar.getJointRotations * @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array * returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. @@ -898,13 +933,15 @@ public: Q_INVOKABLE virtual QVector getJointRotations() const; /**jsdoc + * Gets the translations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. * @function Avatar.getJointTranslations - * @returns {Vec3[]} + * @returns {Vec3[]} The translations of all joints relative to each's parent. The values are in the same order as the array + * returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. */ Q_INVOKABLE virtual QVector getJointTranslations() const; /**jsdoc - * Set the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. + * Sets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate @@ -942,13 +979,20 @@ public: Q_INVOKABLE virtual void setJointRotations(const QVector& jointRotations); /**jsdoc + * Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint. + *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse + * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, + * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate + * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set + * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslations - * @param {Vec3[]} translations + * @param {Vec3[]} translations - The translations for all joints in the avatar. The values are in the same order as the + * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. */ Q_INVOKABLE virtual void setJointTranslations(const QVector& jointTranslations); /**jsdoc - * Clear all joint translations and rotations that have been set by script. This restores all motion from the default + * Clears all joint translations and rotations that have been set by script. This restores all motion from the default * animation system including inverse kinematics for all joints. * @function Avatar.clearJointsData * @example Set your avatar to it's default T-pose for a while. @@ -970,7 +1014,7 @@ public: Q_INVOKABLE virtual void clearJointsData(); /**jsdoc - * Get the joint index for a named joint. The joint index value is the position of the joint in the array returned by + * Gets the joint index for a named joint. The joint index value is the position of the joint in the array returned by * {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @function Avatar.getJointIndex * @param {string} name - The name of the joint. @@ -984,7 +1028,7 @@ public: Q_INVOKABLE virtual int getJointIndex(const QString& name) const; /**jsdoc - * Get the names of all the joints in the current avatar. + * Gets the names of all the joints in the current avatar. * @function Avatar.getJointNames * @returns {string[]} The joint names. * @example Report the names of all the joints in your current avatar. @@ -996,23 +1040,32 @@ public: /**jsdoc + * Sets the value of a blendshape to animate your avatar's face. To enable other users to see the resulting animation of + * your avatar's face, use {@link Avatar.setForceFaceTrackerConnected} or {@link MyAvatar.setForceFaceTrackerConnected}. * @function Avatar.setBlendshape - * @param {string} name - * @param {number} value + * @param {string} name - The name of the blendshape, per the + * {@link https://docs.highfidelity.com/create/avatars/create-avatars/avatar-standards.html#blendshapes Avatar Standards}. + * @param {number} value - A value between 0.0 and 1.0. */ Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } /**jsdoc + * Gets information about the models currently attached to your avatar. * @function Avatar.getAttachmentsVariant - * @returns {object} + * @returns {AttachmentData[]} Information about all models attached to your avatar. + * @deprecated Use avatar entities instead. */ // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; /**jsdoc + * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using + * {@link MyAvatar.getAttachmentsVariant} or {@link Avatar.getAttachmentsVariant}, make changes to it, and then want to + * update your avatar's attachments per the changed data. * @function Avatar.setAttachmentsVariant - * @param {object} variant + * @param {AttachmentData[]} variant - The attachment data defining the models to have attached to your avatar. + * @deprecated Use avatar entities instead. */ // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); @@ -1021,22 +1074,27 @@ public: /**jsdoc * @function Avatar.updateAvatarEntity - * @param {Uuid} entityID - * @param {Array.} entityData + * @param {Uuid} entityID - The entity ID. + * @param {Array.} entityData - Entity data. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc * @function Avatar.clearAvatarEntity - * @param {Uuid} entityID - * @param {boolean} [requiresRemovalFromTree=true] + * @param {Uuid} entityID - The entity ID. + * @param {boolean} [requiresRemovalFromTree=true] - Requires removal from tree. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc + * Enables blendshapes set using {@link Avatar.setBlendshape} or {@link MyAvatar.setBlendshape} to be transmitted to other + * users so that they can see the animation of your avatar's face. * @function Avatar.setForceFaceTrackerConnected - * @param {boolean} connected + * @param {boolean} connected - true to enable blendshape changes to be transmitted to other users, + * false to disable. */ Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } @@ -1087,9 +1145,10 @@ public: } /**jsdoc - * Get information about all models currently attached to your avatar. + * Gets information about the models currently attached to your avatar. * @function Avatar.getAttachmentData * @returns {AttachmentData[]} Information about all models attached to your avatar. + * @deprecated Use avatar entities instead. * @example Report the URLs of all current attachments. * var attachments = MyAvatar.getaAttachmentData(); * for (var i = 0; i < attachments.length; i++) { @@ -1101,12 +1160,13 @@ public: Q_INVOKABLE virtual QVector getAttachmentData() const; /**jsdoc - * Set all models currently attached to your avatar. For example, if you retrieve attachment data using + * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using * {@link MyAvatar.getAttachmentData} or {@link Avatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the * changed data. You can also remove all attachments by using setting attachmentData to null. * @function Avatar.setAttachmentData - * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use + * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use * null to remove all attachments. + * @deprecated Use avatar entities instead. * @example Remove a hat attachment if your avatar is wearing it. * var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx"; * var attachments = MyAvatar.getAttachmentData(); @@ -1124,7 +1184,7 @@ public: Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); /**jsdoc - * Attach a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to + * Attaches a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to * stand on. *

Note: Attached models are models only; they are not entities and can not be manipulated using the {@link Entities} API. * Nor can you use this function to attach an entity (such as a sphere or a box) to your avatar.

@@ -1136,12 +1196,14 @@ public: * @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation. * @param {number} [scale=1.0] - The scale to apply to the model. * @param {boolean} [isSoft=false] - If the model has a skeleton, set this to true so that the bones of the - * attached model's skeleton are be rotated to fit the avatar's current pose. isSoft is used, for example, + * attached model's skeleton are rotated to fit the avatar's current pose. isSoft is used, for example, * to have clothing that moves with the avatar.
* If true, the translation, rotation, and scale parameters are * ignored. - * @param {boolean} [allowDuplicates=false] - * @param {boolean} [useSaved=true] + * @param {boolean} [allowDuplicates=false] - If true then more than one copy of any particular model may be + * attached to the same joint; if false then the same model cannot be attached to the same joint. + * @param {boolean} [useSaved=true] - Not used. + * @deprecated Use avatar entities instead. * @example Attach a cowboy hat to your avatar's head. * var attachment = { * modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx", @@ -1167,20 +1229,22 @@ public: bool allowDuplicates = false, bool useSaved = true); /**jsdoc - * Detach the most recently attached instance of a particular model from either a specific joint or any joint. + * Detaches the most recently attached instance of a particular model from either a specific joint or any joint. * @function Avatar.detachOne * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most * recently attached model is removed from which ever joint it was attached to. + * @deprecated Use avatar entities instead. */ Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString()); /**jsdoc - * Detach all instances of a particular model from either a specific joint or all joints. + * Detaches all instances of a particular model from either a specific joint or all joints. * @function Avatar.detachAll * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is * detached from all joints. + * @deprecated Use avatar entities instead. */ Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); @@ -1220,45 +1284,53 @@ public: AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc + * Gets the transform from the user's real world to the avatar's size, orientation, and position in the virtual world. * @function Avatar.getSensorToWorldMatrix - * @returns {Mat4} + * @returns {Mat4} The scale, rotation, and translation transform from the user's real world to the avatar's size, + * orientation, and position in the virtual world. */ // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; /**jsdoc + * Gets the scale that transforms dimensions in the user's real world to the avatar's size in the virtual world. * @function Avatar.getSensorToWorldScale - * @returns {number} + * @returns {number} The scale that transforms dimensions in the user's real world to the avatar's size in the virtual + * world. */ // thread safe Q_INVOKABLE float getSensorToWorldScale() const; /**jsdoc + * Gets the rotation and translation of the left hand controller relative to the avatar. * @function Avatar.getControllerLeftHandMatrix - * @returns {Mat4} + * @returns {Mat4} The rotation and translation of the left hand controller relative to the avatar. */ // thread safe Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; /**jsdoc + * Gets the rotation and translation of the right hand controller relative to the avatar. * @function Avatar.getControllerRightHandMatrix - * @returns {Mat4} + * @returns {Mat4} The rotation and translation of the right hand controller relative to the avatar. */ // thread safe Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const; /**jsdoc + * Gets the amount of avatar mixer data being generated by the avatar. * @function Avatar.getDataRate - * @param {string} [rateName=""] - * @returns {number} + * @param {AvatarDataRate} [rateName=""] - The type of avatar mixer data to get the data rate of. + * @returns {number} The data rate in kbps. */ Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const; /**jsdoc + * Gets the update rate of avatar mixer data being generated by the avatar. * @function Avatar.getUpdateRate - * @param {string} [rateName=""] - * @returns {number} + * @param {AvatarUpdateRate} [rateName=""] - The type of avatar mixer data to get the update rate of. + * @returns {number} The update rate in Hz. */ Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const; @@ -1304,31 +1376,36 @@ public: signals: /**jsdoc + * Triggered when the avatar's displayName property value changes. * @function Avatar.displayNameChanged * @returns {Signal} */ void displayNameChanged(); /**jsdoc + * Triggered when the avattr's sessionDisplayName property value changes. * @function Avatar.sessionDisplayNameChanged * @returns {Signal} */ void sessionDisplayNameChanged(); /**jsdoc + * Triggered when the avatar's skeletonModelURL property value changes. * @function Avatar.skeletonModelURLChanged * @returns {Signal} */ void skeletonModelURLChanged(); /**jsdoc + * Triggered when the avatar's lookAtSnappingEnabled property value changes. * @function Avatar.lookAtSnappingChanged - * @param {boolean} enabled + * @param {boolean} enabled - true if look-at snapping is enabled, false if not. * @returns {Signal} */ void lookAtSnappingChanged(bool enabled); /**jsdoc + * Triggered when the avatar's sessionUUID property value changes. * @function Avatar.sessionUUIDChanged * @returns {Signal} */ @@ -1338,18 +1415,23 @@ public slots: /**jsdoc * @function Avatar.sendAvatarDataPacket - * @param {boolean} [sendAll=false] + * @param {boolean} [sendAll=false] - Send all. + * @returns {number} + * @deprecated This function is deprecated and will be removed. */ virtual int sendAvatarDataPacket(bool sendAll = false); /**jsdoc * @function Avatar.sendIdentityPacket + * @returns {number} + * @deprecated This function is deprecated and will be removed. */ int sendIdentityPacket(); /**jsdoc * @function Avatar.setSessionUUID - * @param {Uuid} sessionUUID + * @param {Uuid} sessionUUID - Session UUID. + * @deprecated This function is deprecated and will be removed. */ virtual void setSessionUUID(const QUuid& sessionUUID) { if (sessionUUID != getID()) { @@ -1362,44 +1444,61 @@ public slots: } } + /**jsdoc + * Gets the rotation of a joint relative to the avatar. + *

Warning: Not able to be used in the Avatar API.

* @function Avatar.getAbsoluteJointRotationInObjectFrame - * @param {number} index - * @returns {Quat} + * @param {number} index - The index of the joint. Not used. + * @returns {Quat} Quat.IDENTITY. */ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; /**jsdoc + * Gets the translation of a joint relative to the avatar. + *

Warning: Not able to be used in the Avatar API.

* @function Avatar.getAbsoluteJointTranslationInObjectFrame - * @param {number} index - * @returns {Vec3} + * @param {number} index - The index of the joint. Not used. + * @returns {Vec3} Vec3.ZERO. */ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; /**jsdoc + * Sets the rotation of a joint relative to the avatar. + *

Warning: Not able to be used in the Avatar API.

* @function Avatar.setAbsoluteJointRotationInObjectFrame - * @param {number} index - * @param {Quat} rotation - * @returns {boolean} + * @param {number} index - The index of the joint. Not used. + * @param {Quat} rotation - The rotation of the joint relative to the avatar. Not used. + * @returns {boolean} false. */ virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } /**jsdoc + * Sets the translation of a joint relative to the avatar. + *

Warning: Not able to be used in the Avatar API.

* @function Avatar.setAbsoluteJointTranslationInObjectFrame - * @param {number} index - * @param {Vec3} translation - * @returns {boolean} + * @param {number} index - The index of the joint. Not used. + * @param {Vec3} translation - The translation of the joint relative to the avatar. Not used. + * @returns {boolean} false. */ virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } /**jsdoc + * Gets the target scale of the avatar without any restrictions on permissible values imposed by the domain. In contrast, the + * scale property's value may be limited by the domain's settings. * @function Avatar.getTargetScale - * @returns {number} + * @returns {number} The target scale of the avatar. + * @example Compare the target and current avatar scales. + * print("Current avatar scale: " + MyAvatar.scale); + * print("Target avatar scale: " + MyAvatar.getTargetScale()); + * + * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ float getTargetScale() const { return _targetScale; } // why is this a slot? /**jsdoc * @function Avatar.resetLastSent + * @deprecated This function is deprecated and will be removed. */ void resetLastSent() { _lastToByteArray = 0; } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index ec1126c92f..614858a77d 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -1147,19 +1147,21 @@ AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float prio } /**jsdoc + * The details of an animation that is playing. * @typedef {object} Avatar.AnimationDetails - * @property {string} role - * @property {string} url - * @property {number} fps - * @property {number} priority - * @property {boolean} loop - * @property {boolean} hold - * @property {boolean} startAutomatically - * @property {number} firstFrame - * @property {number} lastFrame - * @property {boolean} running - * @property {number} currentFrame - * @property {boolean} allowTranslation + * @property {string} role - Not used. + * @property {string} url - The URL to the animation file. Animation files need to be in .FBX format but only need to contain +* the avatar skeleton and animation data. + * @property {number} fps - The frames per second(FPS) rate for the animation playback. 30 FPS is normal speed. + * @property {number} priority - Not used. + * @property {boolean} loop - true if the animation should loop, false if it shouldn't. + * @property {boolean} hold - Not used. + * @property {number} firstFrame - The frame the animation should start at. + * @property {number} lastFrame - The frame the animation should stop at. + * @property {boolean} running - Not used. + * @property {number} currentFrame - The current frame being played. + * @property {boolean} startAutomatically - Not used. + * @property {boolean} allowTranslation - Not used. */ QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) { QScriptValue obj = engine->newObject(); From aa53ab7492b15f183754fc11f7886539364ae29a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 27 Feb 2019 09:54:11 +1300 Subject: [PATCH 08/88] Distinguish between Mat4 type and Mat4 API in JSDoc --- libraries/script-engine/src/Mat4.h | 33 +++++++++++----------- libraries/shared/src/RegisteredMetaTypes.h | 21 ++++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 7ad77b9b24..0cdc70e79c 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -23,6 +23,7 @@ /**jsdoc * @namespace Mat4 + * @variation 0 * * @hifi-interface * @hifi-client-entity @@ -38,7 +39,7 @@ class Mat4 : public QObject, protected QScriptable { public slots: /**jsdoc - * @function Mat4.multiply + * @function Mat4(0).multiply * @param {Mat4} m1 * @param {Mat4} m2 * @returns {Mat4} @@ -47,7 +48,7 @@ public slots: /**jsdoc - * @function Mat4.createFromRotAndTrans + * @function Mat4(0).createFromRotAndTrans * @param {Quat} rot * @param {Vec3} trans * @returns {Mat4} @@ -55,7 +56,7 @@ public slots: glm::mat4 createFromRotAndTrans(const glm::quat& rot, const glm::vec3& trans) const; /**jsdoc - * @function Mat4.createFromScaleRotAndTrans + * @function Mat4(0).createFromScaleRotAndTrans * @param {Vec3} scale * @param {Quat} rot * @param {Vec3} trans @@ -64,7 +65,7 @@ public slots: glm::mat4 createFromScaleRotAndTrans(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) const; /**jsdoc - * @function Mat4.createFromColumns + * @function Mat4(0).createFromColumns * @param {Vec4} col0 * @param {Vec4} col1 * @param {Vec4} col2 @@ -74,7 +75,7 @@ public slots: glm::mat4 createFromColumns(const glm::vec4& col0, const glm::vec4& col1, const glm::vec4& col2, const glm::vec4& col3) const; /**jsdoc - * @function Mat4.createFromArray + * @function Mat4(0).createFromArray * @param {number[]} numbers * @returns {Mat4} */ @@ -82,21 +83,21 @@ public slots: /**jsdoc - * @function Mat4.extractTranslation + * @function Mat4(0).extractTranslation * @param {Mat4} m * @returns {Vec3} */ glm::vec3 extractTranslation(const glm::mat4& m) const; /**jsdoc - * @function Mat4.extractRotation + * @function Mat4(0).extractRotation * @param {Mat4} m * @returns {Vec3} */ glm::quat extractRotation(const glm::mat4& m) const; /**jsdoc - * @function Mat4.extractScale + * @function Mat4(0).extractScale * @param {Mat4} m * @returns {Vec3} */ @@ -104,7 +105,7 @@ public slots: /**jsdoc - * @function Mat4.transformPoint + * @function Mat4(0).transformPoint * @param {Mat4} m * @param {Vec3} point * @returns {Vec3} @@ -112,7 +113,7 @@ public slots: glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& point) const; /**jsdoc - * @function Mat4.transformVector + * @function Mat4(0).transformVector * @param {Mat4} m * @param {Vec3} vector * @returns {Vec3} @@ -121,7 +122,7 @@ public slots: /**jsdoc - * @function Mat4.inverse + * @function Mat4(0).inverse * @param {Mat4} m * @returns {Mat4} */ @@ -129,7 +130,7 @@ public slots: /**jsdoc - * @function Mat4.getFront + * @function Mat4(0).getFront * @param {Mat4} m * @returns {Vec3} */ @@ -137,28 +138,28 @@ public slots: glm::vec3 getFront(const glm::mat4& m) const { return getForward(m); } /**jsdoc - * @function Mat4.getForward + * @function Mat4(0).getForward * @param {Mat4} m * @returns {Vec3} */ glm::vec3 getForward(const glm::mat4& m) const; /**jsdoc - * @function Mat4.getRight + * @function Mat4(0).getRight * @param {Mat4} m * @returns {Vec3} */ glm::vec3 getRight(const glm::mat4& m) const; /**jsdoc - * @function Mat4.getUp + * @function Mat4(0).getUp * @param {Mat4} m * @returns {Vec3} */ glm::vec3 getUp(const glm::mat4& m) const; /**jsdoc - * @function Mat4.print + * @function Mat4(0).print * @param {string} label * @param {Mat4} m * @param {boolean} [transpose=false] diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 1edb303455..ea2c5b8354 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -43,6 +43,27 @@ Q_DECLARE_METATYPE(std::function); void registerMetaTypes(QScriptEngine* engine); // Mat4 +/**jsdoc + * A 4 x 4 matrix, typically containing a scale, rotation, and translation transform. See also the {@link Mat4(0)|Mat4} object. + * + * @typedef {object} Mat4 + * @property {number} r0c0 - Row 0, column 0 value. + * @property {number} r1c0 - Row 1, column 0 value. + * @property {number} r2c0 - Row 2, column 0 value. + * @property {number} r3c0 - Row 3, column 0 value. + * @property {number} r0c1 - Row 0, column 1 value. + * @property {number} r1c1 - Row 1, column 1 value. + * @property {number} r2c1 - Row 2, column 1 value. + * @property {number} r3c1 - Row 3, column 1 value. + * @property {number} r0c2 - Row 0, column 2 value. + * @property {number} r1c2 - Row 1, column 2 value. + * @property {number} r2c2 - Row 2, column 2 value. + * @property {number} r3c2 - Row 3, column 2 value. + * @property {number} r0c3 - Row 0, column 3 value. + * @property {number} r1c3 - Row 1, column 3 value. + * @property {number} r2c3 - Row 2, column 3 value. + * @property {number} r3c3 - Row 3, column 3 value. + */ QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); From d73ff2e8558b27b861d1602c63b05cfbffa89587 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 28 Feb 2019 10:40:10 +1300 Subject: [PATCH 09/88] Revise current MyAvatar API JSDoc --- interface/src/avatar/MyAvatar.cpp | 14 +- interface/src/avatar/MyAvatar.h | 425 +++++++++++------- .../src/avatars-renderer/Avatar.h | 99 ++-- 3 files changed, 326 insertions(+), 212 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index afb7a218f6..30e8733a42 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2381,7 +2381,19 @@ void MyAvatar::clearWornAvatarEntities() { } } - +/**jsdoc + * Information about an avatar entity. + * + * + * + * + * + * + * + * + *
PropertyTypeDescription
idUuidEntity ID.
properties{@link Entities.EntityProperties}Entity properties.
+ * @typedef {object} MyAvatar.AvatarEntityData + */ QVariantList MyAvatar::getAvatarEntitiesVariant() { // NOTE: this method is NOT efficient QVariantList avatarEntitiesData; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d689d2f215..a0f1531e64 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -60,7 +60,9 @@ class MyAvatar : public Avatar { /**jsdoc * Your avatar is your in-world representation of you. The MyAvatar API is used to manipulate the avatar. * For example, you can customize the avatar's appearance, run custom avatar animations, - * change the avatar's position within the domain, or manage the avatar's collisions with other objects. + * change the avatar's position within the domain, or manage the avatar's collisions with the environment and other avatars. + * + *

For assignment client scripts, see {@link Avatar}.

* * @namespace MyAvatar * @@ -69,17 +71,20 @@ class MyAvatar : public Avatar { * @hifi-avatar * * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. - * @property {Vec3} position - * @property {number} scale - Returns the clamped scale of the avatar. - * @property {number} density Read-only. - * @property {Vec3} handPosition + * @property {Vec3} position - The position of the avatar. + * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 + * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. + * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in + * the application of physics. Read-only. + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar + * but is otherwise not used or changed by Interface. * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is * sometimes called "bank". - * @property {Quat} orientation + * @property {Quat} orientation - The orientation of the avatar. * @property {Quat} headOrientation - The orientation of the avatar's head. * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is * sometimes called "elevation". @@ -87,22 +92,31 @@ class MyAvatar : public Avatar { * head. Yaw is sometimes called "heading". * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is * sometimes called "bank". - * @property {Vec3} velocity - * @property {Vec3} angularVelocity - * @property {number} audioLoudness - * @property {number} audioAverageLoudness - * @property {string} displayName - * @property {string} sessionDisplayName - Sanitized, defaulted version displayName that is defined by the AvatarMixer - * rather than by Interface clients. The result is unique among all avatars present at the time. - * @property {boolean} lookAtSnappingEnabled - * @property {string} skeletonModelURL - * @property {AttachmentData[]} attachmentData + * @property {Vec3} velocity - The current velocity of the avatar. + * @property {Vec3} angularVelocity - The current angular velocity of the avatar. + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting + * into the domain. + * @property {string} displayName - The avatar's display name. + * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the + * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the + * time. + * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's + * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. + * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. - * @property {Uuid} sessionUUID Read-only. - * @property {Mat4} sensorToWorldMatrix Read-only. - * @property {Mat4} controllerLeftHandMatrix Read-only. - * @property {Mat4} controllerRightHandMatrix Read-only. - * @property {number} sensorToWorldScale Read-only. + * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * avatar's size, orientation, and position in the virtual world. Read-only. + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the + * avatar. Read-only. + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * avatar. Read-only. + * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's + * size in the virtual world. Read-only. * * @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here. * @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the @@ -124,9 +138,9 @@ class MyAvatar : public Avatar { * by the audio mixer, so all audio effectively plays back at a 24khz. 48kHz RAW files are also supported. * @property {number} audioListenerMode=0 - Specifies the listening position when hearing spatialized audio. Must be one * of the following property values:
- * audioListenerModeHead
- * audioListenerModeCamera
- * audioListenerModeCustom + * Myavatar.audioListenerModeHead
+ * Myavatar.audioListenerModeCamera
+ * Myavatar.audioListenerModeCustom * @property {number} audioListenerModeHead=0 - The audio listening position is at the avatar's head. Read-only. * @property {number} audioListenerModeCamera=1 - The audio listening position is at the camera. Read-only. * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the @@ -135,10 +149,12 @@ class MyAvatar : public Avatar { * property value is audioListenerModeCustom. * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the * audioListenerMode property value is audioListenerModeCustom. - * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true. - * @property {boolean} hasProceduralBlinkFaceMovement=true - procedural blinking will be turned on if set to true. - * @property {boolean} hasProceduralEyeFaceMovement=true - procedural eye movement will be turned on if set to true. - * @property {boolean} hasAudioEnabledFaceMovement=true - If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. + * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true.
+ * Note: Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead. + * @property {boolean} hasProceduralBlinkFaceMovement=true - If true then procedural blinking is turned on. + * @property {boolean} hasProceduralEyeFaceMovement=true - If true then procedural eye movement is turned on. + * @property {boolean} hasAudioEnabledFaceMovement=true - If true then voice audio will move the mouth + * blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property {number} rotationRecenterFilterLength * @property {number} rotationThreshold * @property {boolean} enableStepResetRotation @@ -169,16 +185,18 @@ class MyAvatar : public Avatar { * @property {boolean} hmdLeanRecenterEnabled=true - If true then the avatar is re-centered to be under the * head's position. In room-scale VR, this behavior is what causes your avatar to follow your HMD as you walk around * the room. Setting the value false is useful if you want to pin the avatar to a fixed position. - * @property {boolean} collisionsEnabled - Set to true to enable collisions for the avatar, false - * to disable collisions. May return true even though the value was set false because the - * zone may disallow collisionless avatars. - * @property {boolean} otherAvatarsCollisionsEnabled - * @property {boolean} characterControllerEnabled - Synonym of collisionsEnabled. + * @property {boolean} collisionsEnabled - Set to true to enable the avatar to collide with the environment, + * false to disable collisions with the environment. May return true even though the value + * was set false because the zone may disallow collisionless avatars. + * @property {boolean} otherAvatarsCollisionsEnabled - Set to true to enable the avatar to collide with other + * avatars, false to disable collisions with other avatars. + * @property {boolean} characterControllerEnabled - Synonym of collisionsEnabled.
* Deprecated: Use collisionsEnabled instead. * @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings > - * Walking and teleporting. Note: Setting the value has no effect unless Interface is restarted. - * @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Show room boundaries - * while teleporting. Note: Setting the value has no effect unless Interface is restarted. + * Controls > Walking. Note: Setting the value has no effect unless Interface is restarted. + * @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Controls > Show room + * boundaries while teleporting.
+ * Note: Setting the value has no effect unless Interface is restarted. * * @property {number} yawSpeed=75 * @property {number} pitchSpeed=50 @@ -187,8 +205,8 @@ class MyAvatar : public Avatar { * while flying. * @property {number} hmdRollControlDeadZone=8 - The amount of HMD roll, in degrees, required before your avatar turns if * hmdRollControlEnabled is enabled. - * @property {number} hmdRollControlRate If hmdRollControlEnabled is true, this value determines the maximum turn rate of - * your avatar when rolling your HMD in degrees per second. + * @property {number} hmdRollControlRate If MyAvatar.hmdRollControlEnabled is true, this value determines the + * maximum turn rate of your avatar when rolling your HMD in degrees per second. * * @property {number} userHeight=1.75 - The height of the user in sensor space. * @property {number} userEyeHeight=1.65 - The estimated height of the user's eyes in sensor space. Read-only. @@ -237,8 +255,8 @@ class MyAvatar : public Avatar { * @borrows Avatar.attach as attach * @borrows Avatar.detachOne as detachOne * @borrows Avatar.detachAll as detachAll - * @borrows Avatar.getAvatarEntityData as getAvatarEntityData - * @borrows Avatar.setAvatarEntityData as setAvatarEntityData + * @comment Avatar.getAvatarEntityData as getAvatarEntityData - Don't borrow because implementation is different. + * @comment Avatar.setAvatarEntityData as setAvatarEntityData - Don't borrow because implementation is different. * @borrows Avatar.getSensorToWorldMatrix as getSensorToWorldMatrix * @borrows Avatar.getSensorToWorldScale as getSensorToWorldScale * @borrows Avatar.getControllerLeftHandMatrix as getControllerLeftHandMatrix @@ -253,10 +271,10 @@ class MyAvatar : public Avatar { * @borrows Avatar.sendAvatarDataPacket as sendAvatarDataPacket * @borrows Avatar.sendIdentityPacket as sendIdentityPacket * @borrows Avatar.setSessionUUID as setSessionUUID - * @borrows Avatar.getAbsoluteJointRotationInObjectFrame as getAbsoluteJointRotationInObjectFrame - * @borrows Avatar.getAbsoluteJointTranslationInObjectFrame as getAbsoluteJointTranslationInObjectFrame - * @borrows Avatar.setAbsoluteJointRotationInObjectFrame as setAbsoluteJointRotationInObjectFrame - * @borrows Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame + * @comment Avatar.getAbsoluteJointRotationInObjectFrame as getAbsoluteJointRotationInObjectFrame - Don't borrow because implementation is different. + * @comment Avatar.getAbsoluteJointTranslationInObjectFrame as getAbsoluteJointTranslationInObjectFrame - Don't borrow because implementation is different. + * @comment Avatar.setAbsoluteJointRotationInObjectFrame as setAbsoluteJointRotationInObjectFrame - Don't borrow because implementation is different. + * @comment Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame - Don't borrow because implementation is different. * @borrows Avatar.getTargetScale as getTargetScale * @borrows Avatar.resetLastSent as resetLastSent */ @@ -393,8 +411,9 @@ public: /**jsdoc - * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent - * contorted joints. + * Clears inverse kinematics joint limit history. + *

The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to + * forget this history, to prevent contorted joints.

* @function MyAvatar.clearIKJointLimitHistory */ Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe @@ -441,7 +460,7 @@ public: void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } /**jsdoc - * Get the position in world coordinates of the point directly between your avatar's eyes assuming your avatar was in its + * Gets the position in world coordinates of the point directly between your avatar's eyes assuming your avatar was in its * default pose. This is a reference position; it does not change as your avatar's head moves relative to the avatar * position. * @function MyAvatar.getDefaultEyePosition @@ -455,15 +474,16 @@ public: float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). overrideAnimation() is used to completely - * override all motion from the default animation system (including inverse kinematics for hand and head controllers) and - * play a set of specified animations. To end these animations and restore the default animations, use - * {@link MyAvatar.restoreAnimation}.
+ * Overrides the default avatar animations. + *

The avatar animation system includes a set of default animations along with rules for how those animations are blended + * together with procedural data (such as look at vectors, hand sensors etc.). overrideAnimation() is used to + * completely override all motion from the default animation system (including inverse kinematics for hand and head + * controllers) and play a set of specified animations. To end these animations and restore the default animations, use + * {@link MyAvatar.restoreAnimation}.

*

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.overrideAnimation * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the * avatar skeleton and animation data. @@ -482,10 +502,12 @@ public: Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will override the default animations. - * restoreAnimation() is used to restore all motion from the default animation system including inverse kinematics for hand and head - * controllers. If you aren't currently playing an override animation, this function will have no effect. + * Restores the default animations. + *

The avatar animation system includes a set of default animations along with rules for how those animations are blended + * together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will + * override the default animations. restoreAnimation() is used to restore all motion from the default + * animation system including inverse kinematics for hand and head controllers. If you aren't currently playing an override + * animation, this function has no effect.

* @function MyAvatar.restoreAnimation * @example Play a clapping animation on your avatar for three seconds. * // Clap your hands for 3 seconds then restore animation back to the avatar. @@ -498,10 +520,12 @@ public: Q_INVOKABLE void restoreAnimation(); /**jsdoc - * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data - * (such as look at vectors, hand sensors etc.). Each animation specified in the avatar-animation.json file is known as an animation role. - * Animation roles map to easily understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd." - * getAnimationRoles() is used get the list of animation roles defined in the avatar-animation.json. + * Gets the current animation roles. + *

Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together + * with procedural data (such as look at vectors, hand sensors etc.). Each animation specified in the avatar-animation.json + * file is known as an animation role. Animation roles map to easily understandable actions that the avatar can perform, + * such as "idleStand", "idleTalk", or "walkFwd". getAnimationRoles() + * is used get the list of animation roles defined in the avatar-animation.json.

* @function MyAvatar.getAnimationRoles * @returns {string[]} Array of role strings. * @example Print the list of animation roles defined in the avatar's avatar-animation.json file to the debug log. @@ -514,17 +538,19 @@ public: Q_INVOKABLE QStringList getAnimationRoles(); /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions - * that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). - * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). - * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation role. To end - * the animations and restore the default animations, use {@link MyAvatar.restoreRoleAnimation}.
+ * Overrides a specific animation role. + *

Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily + * understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or + * "walkFwd". To get the full list of roles, use {@ link MyAvatar.getAnimationRoles}. + * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how + * animations are blended together with procedural data (such as look at vectors, hand sensors etc.). + * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation + * role. To end the role animation and restore the default, use {@link MyAvatar.restoreRoleAnimation}.

*

Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body.

*

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards. + * Avatar Standards. * @function MyAvatar.overrideRoleAnimation * @param role {string} The animation role to override * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. @@ -548,13 +574,15 @@ public: Q_INVOKABLE void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions that - * the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). For each role, - * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). You can change the animation clip (.FBX) associated with a specified animation - * role using overrideRoleAnimation(). - * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have not specified an override animation - * for the specified role, this function will have no effect. + * Restores a default role animation. + *

Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily + * understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or + * "walkFwd". To get the full list of roles, use {#link MyAvatar.getAnimationRoles}. For each role, + * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are + * blended together with procedural data (such as look-at vectors, hand sensors etc.). You can change the animation clip + * (.FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}. + * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have + * not specified an override animation for the specified role, this function has no effect. * @function MyAvatar.restoreRoleAnimation * @param role {string} The animation role clip to restore. */ @@ -599,6 +627,7 @@ public: * @param {string} hand */ Q_INVOKABLE void setDominantHand(const QString& hand); + /**jsdoc * @function MyAvatar.getDominantHand * @returns {string} @@ -610,6 +639,7 @@ public: * @param {string} hand */ Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand); + /**jsdoc * @function MyAvatar.getHmdAvatarAlignmentType * @returns {string} @@ -617,45 +647,61 @@ public: Q_INVOKABLE QString getHmdAvatarAlignmentType() const; /**jsdoc - * @function MyAvatar.setCenterOfGravityModelEnabled - * @param {boolean} enabled - */ + * @function MyAvatar.setCenterOfGravityModelEnabled + * @param {boolean} enabled + */ Q_INVOKABLE void setCenterOfGravityModelEnabled(bool value) { _centerOfGravityModelEnabled = value; } + /**jsdoc - * @function MyAvatar.getCenterOfGravityModelEnabled - * @returns {boolean} - */ + * @function MyAvatar.getCenterOfGravityModelEnabled + * @returns {boolean} + */ Q_INVOKABLE bool getCenterOfGravityModelEnabled() const { return _centerOfGravityModelEnabled; } /**jsdoc * @function MyAvatar.setHMDLeanRecenterEnabled * @param {boolean} enabled */ Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } + /**jsdoc * @function MyAvatar.getHMDLeanRecenterEnabled * @returns {boolean} */ Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } + /**jsdoc - * Request to enable hand touch effect globally + * Requests that the hand touch effect is disabled for your avatar. Any resulting change in the status of the hand touch + * effect will be signaled by {@link MyAvatar.shouldDisableHandTouchChanged}. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.requestEnableHandTouch */ Q_INVOKABLE void requestEnableHandTouch(); + /**jsdoc - * Request to disable hand touch effect globally + * Requests that the hand touch effect is enabled for your avatar. Any resulting change in the status of the hand touch + * effect will be signaled by {@link MyAvatar.shouldDisableHandTouchChanged}. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.requestDisableHandTouch */ Q_INVOKABLE void requestDisableHandTouch(); + /**jsdoc - * Disables hand touch effect on a specific entity + * Disables the hand touch effect on a specific entity. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.disableHandTouchForID - * @param {Uuid} entityID - ID of the entity that will disable hand touch effect + * @param {Uuid} entityID - The entity that the hand touch effect will be disabled for. */ Q_INVOKABLE void disableHandTouchForID(const QUuid& entityID); + /**jsdoc - * Enables hand touch effect on a specific entity + * Enables the hand touch effect on a specific entity. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.enableHandTouchForID - * @param {Uuid} entityID - ID of the entity that will enable hand touch effect + * @param {Uuid} entityID - The entity that the hand touch effect will be enabled for. */ Q_INVOKABLE void enableHandTouchForID(const QUuid& entityID); @@ -712,6 +758,7 @@ public: * @param {DriveKeys} key */ Q_INVOKABLE void enableDriveKey(DriveKeys key); + /**jsdoc * @function MyAvatar.isDriveKeyDisabled * @param {DriveKeys} key @@ -741,10 +788,10 @@ public: Q_INVOKABLE void triggerRotationRecenter(); /**jsdoc - *The isRecenteringHorizontally function returns true if MyAvatar - *is translating the root of the Avatar to keep the center of gravity under the head. - *isActive(Horizontal) is returned. - *@function MyAvatar.isRecenteringHorizontally + * Gets whether or not the avatar is configured to keep its center of gravity under its head. + * @function MyAvatar.isRecenteringHorizontally + * @returns {boolean} true if the avatar is keeping its center of gravity under its head position, + * false if not. */ Q_INVOKABLE bool isRecenteringHorizontally() const; @@ -753,7 +800,7 @@ public: const MyHead* getMyHead() const; /**jsdoc - * Get the current position of the avatar's "Head" joint. + * Gets the current position of the avatar's "Head" joint. * @function MyAvatar.getHeadPosition * @returns {Vec3} The current position of the avatar's "Head" joint. * @example Report the current position of your avatar's head. @@ -786,7 +833,7 @@ public: Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); } /**jsdoc - * Get the current position of the point directly between the avatar's eyes. + * Gets the current position of the point directly between the avatar's eyes. * @function MyAvatar.getEyePosition * @returns {Vec3} The current position of the point directly between the avatar's eyes. * @example Report your avatar's current eye position. @@ -796,8 +843,9 @@ public: Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } /**jsdoc + * Gets the position of the avatar your avatar is currently looking at. * @function MyAvatar.getTargetAvatarPosition - * @returns {Vec3} The position of the avatar you're currently looking at. + * @returns {Vec3} The position of the avatar your avatar is currently looking at. * @example Report the position of the avatar you're currently looking at. * print(JSON.stringify(MyAvatar.getTargetAvatarPosition())); */ @@ -811,7 +859,7 @@ public: /**jsdoc - * Get the position of the avatar's left hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
+ * Gets the position of the avatar's left hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
*

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.)

* @function MyAvatar.getLeftHandPosition @@ -823,7 +871,7 @@ public: Q_INVOKABLE glm::vec3 getLeftHandPosition() const; /**jsdoc - * Get the position of the avatar's right hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
+ * Gets the position of the avatar's right hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
*

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.)

* @function MyAvatar.getRightHandPosition @@ -848,7 +896,7 @@ public: /**jsdoc - * Get the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a + * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a * hand controller (e.g., Oculus Touch or Vive).
*

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.) If you are using the Leap Motion, the return value's valid property will be @@ -861,7 +909,7 @@ public: Q_INVOKABLE controller::Pose getLeftHandPose() const; /**jsdoc - * Get the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a + * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a * hand controller (e.g., Oculus Touch or Vive).
*

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.) If you are using the Leap Motion, the return value's valid property will be @@ -935,7 +983,7 @@ public: Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); /**jsdoc - * Get the complete URL for the current avatar. + * Gets the complete URL for the current avatar. * @function MyAvatar.getFullAvatarURLFromPreferences * @returns {string} The full avatar model name. * @example Report the URL for the current avatar. @@ -944,7 +992,7 @@ public: Q_INVOKABLE QUrl getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } /**jsdoc - * Get the full avatar model name for the current avatar. + * Gets the full avatar model name for the current avatar. * @function MyAvatar.getFullAvatarModelName * @returns {string} The full avatar model name. * @example Report the current full avatar model name. @@ -1015,24 +1063,24 @@ public: bool hasDriveInput() const; /**jsdoc - * Function returns list of avatar entities - * @function MyAvatar.getAvatarEntitiesVariant - * @returns {object[]} - */ + * Gets the list of avatar entities and their properties. + * @function MyAvatar.getAvatarEntitiesVariant + * @returns {MyAvatar.AvatarEntityData[]} + */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); + void removeWornAvatarEntity(const EntityItemID& entityID); void clearWornAvatarEntities(); /**jsdoc - * Check whether your avatar is flying or not. + * Checks whether your avatar is flying or not. * @function MyAvatar.isFlying - * @returns {boolean} true if your avatar is flying and not taking off or falling, otherwise - * false. + * @returns {boolean} true if your avatar is flying and not taking off or falling, false if not. */ Q_INVOKABLE bool isFlying(); /**jsdoc - * Check whether your avatar is in the air or not. + * Checks whether your avatar is in the air or not. * @function MyAvatar.isInAir * @returns {boolean} true if your avatar is taking off, flying, or falling, otherwise false * because your avatar is on the ground. @@ -1040,7 +1088,7 @@ public: Q_INVOKABLE bool isInAir(); /**jsdoc - * Set your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * Sets your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends * on whether the domain you're in allows you to fly. * @function MyAvatar.setFlyingEnabled * @param {boolean} enabled - Set true if you want to enable flying in your current desktop or HMD display @@ -1049,7 +1097,7 @@ public: Q_INVOKABLE void setFlyingEnabled(bool enabled); /**jsdoc - * Get your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * Gets your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends * on whether the domain you're in allows you to fly. * @function MyAvatar.getFlyingEnabled * @returns {boolean} true if your preference is to enable flying in your current desktop or HMD display mode, @@ -1058,7 +1106,7 @@ public: Q_INVOKABLE bool getFlyingEnabled(); /**jsdoc - * Set your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * Sets your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain * you're in allows you to fly. * @function MyAvatar.setFlyingDesktopPref * @param {boolean} enabled - Set true if you want to enable flying in desktop display mode, otherwise set @@ -1067,7 +1115,7 @@ public: Q_INVOKABLE void setFlyingDesktopPref(bool enabled); /**jsdoc - * Get your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * Gets your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain * you're in allows you to fly. * @function MyAvatar.getFlyingDesktopPref * @returns {boolean} true if your preference is to enable flying in desktop display mode, otherwise @@ -1076,7 +1124,7 @@ public: Q_INVOKABLE bool getFlyingDesktopPref(); /**jsdoc - * Set your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * Sets your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain * you're in allows you to fly. * @function MyAvatar.setFlyingHMDPref * @param {boolean} enabled - Set true if you want to enable flying in HMD display mode, otherwise set @@ -1085,7 +1133,7 @@ public: Q_INVOKABLE void setFlyingHMDPref(bool enabled); /**jsdoc - * Get your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * Gets your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain * you're in allows you to fly. * @function MyAvatar.getFlyingHMDPref * @returns {boolean} true if your preference is to enable flying in HMD display mode, otherwise @@ -1120,38 +1168,53 @@ public: Q_INVOKABLE bool getCollisionsEnabled(); /**jsdoc - * @function MyAvatar.setOtherAvatarsCollisionsEnabled - * @param {boolean} enabled - */ + * @function MyAvatar.setOtherAvatarsCollisionsEnabled + * @param {boolean} enabled + */ Q_INVOKABLE void setOtherAvatarsCollisionsEnabled(bool enabled); /**jsdoc - * @function MyAvatar.getOtherAvatarsCollisionsEnabled - * @returns {boolean} - */ + * @function MyAvatar.getOtherAvatarsCollisionsEnabled + * @returns {boolean} + */ Q_INVOKABLE bool getOtherAvatarsCollisionsEnabled(); /**jsdoc - * @function MyAvatar.getCollisionCapsule - * @returns {object} - */ + * @function MyAvatar.getCollisionCapsule + * @returns {object} + */ Q_INVOKABLE QVariantMap getCollisionCapsule() const; /**jsdoc * @function MyAvatar.setCharacterControllerEnabled * @param {boolean} enabled - * @deprecated + * @deprecated Use {@link MyAvatar.setCollisionsEnabled} instead. */ Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated /**jsdoc * @function MyAvatar.getCharacterControllerEnabled * @returns {boolean} - * @deprecated + * @deprecated Use {@link MyAvatar.getCollisionsEnabled} instead. */ Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated + /**jsdoc + * @comment Different behavior to the Avatar version of this method. + * Gets the rotation of a joint relative to the avatar. + * @function MyAvatar.getAbsoluteJointRotationInObjectFrame + * @param {number} index - The index of the joint. + * @returns {Quat} The rotation of the joint relative to the avatar. + */ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; + + /**jsdoc + * @comment Different behavior to the Avatar version of this method. + * Gets the translation of a joint relative to the avatar. + * @function MyAvatar.getAbsoluteJointTranslationInObjectFrame + * @param {number} index - The index of the joint. + * @returns {Vec3} The translation of the joint relative to the avatar. + */ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; // all calibration matrices are in absolute sensor space. @@ -1241,34 +1304,56 @@ public: void prepareAvatarEntityDataForReload(); /**jsdoc - * Create a new grab. + * Creates a new grab, that grabs an entity. * @function MyAvatar.grab - * @param {Uuid} targetID - id of grabbed thing - * @param {number} parentJointIndex - avatar joint being used to grab - * @param {Vec3} offset - target's positional offset from joint - * @param {Quat} rotationalOffset - target's rotational offset from joint - * @returns {Uuid} id of the new grab + * @param {Uuid} targetID - The ID of the entity to grab. + * @param {number} parentJointIndex - The avatar joint to use to grab the entity. + * @param {Vec3} offset - The target's local positional relative to the joint. + * @param {Quat} rotationalOffset - The target's local rotation relative to the joint. + * @returns {Uuid} The ID of the new grab. */ Q_INVOKABLE const QUuid grab(const QUuid& targetID, int parentJointIndex, glm::vec3 positionalOffset, glm::quat rotationalOffset); /**jsdoc - * Release (delete) a grab. + * Releases (deletes) a grab, to stop grabbing an entity. * @function MyAvatar.releaseGrab - * @param {Uuid} grabID - id of grabbed thing + * @param {Uuid} grabID - The ID of the grab to release. */ Q_INVOKABLE void releaseGrab(const QUuid& grabID); + /**jsdoc + * Gets the avatar entities as binary data. + * @function MyAvatar.getAvatarEntityData + * @override + * @returns {AvatarEntityMap} + */ AvatarEntityMap getAvatarEntityData() const override; + + /**jsdoc + * Sets the avatar entities from binary data. + * @function MyAvatar.setAvatarEntityData + * @param {AvatarEntityMap} avatarEntityData + */ void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; + + /**jsdoc + * @comment Uses the base class's JSDoc. + */ void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) override; + void avatarEntityDataToJson(QJsonObject& root) const override; + + /**jsdoc + * @function MyAvatar.sendAvatarDataPacket + * @param {boolean} sendAll + */ int sendAvatarDataPacket(bool sendAll = false) override; public slots: /**jsdoc - * Increase the avatar's scale by five percent, up to a minimum scale of 1000. + * Increases the avatar's scale by five percent, up to a minimum scale of 1000. * @function MyAvatar.increaseSize * @example Reset your avatar's size to default then grow it 5 times. * MyAvatar.resetSize(); @@ -1281,7 +1366,7 @@ public slots: void increaseSize(); /**jsdoc - * Decrease the avatar's scale by five percent, down to a minimum scale of 0.25. + * Decreases the avatar's scale by five percent, down to a minimum scale of 0.25. * @function MyAvatar.decreaseSize * @example Reset your avatar's size to default then shrink it 5 times. * MyAvatar.resetSize(); @@ -1294,7 +1379,7 @@ public slots: void decreaseSize(); /**jsdoc - * Reset the avatar's scale back to the default scale of 1.0. + * Resets the avatar's scale back to the default scale of 1.0. * @function MyAvatar.resetSize */ void resetSize(); @@ -1317,7 +1402,7 @@ public slots: float getGravity(); /**jsdoc - * Move the avatar to a new position and/or orientation in the domain, while taking into account Avatar leg-length. + * Moves the avatar to a new position and/or orientation in the domain, while taking into account Avatar leg-length. * @function MyAvatar.goToFeetLocation * @param {Vec3} position - The new position for the avatar, in world coordinates. * @param {boolean} [hasOrientation=false] - Set to true to set the orientation of the avatar. @@ -1330,7 +1415,7 @@ public slots: bool shouldFaceLocation); /**jsdoc - * Move the avatar to a new position and/or orientation in the domain. + * Moves the avatar to a new position and/or orientation in the domain. * @function MyAvatar.goToLocation * @param {Vec3} position - The new position for the avatar, in world coordinates. * @param {boolean} [hasOrientation=false] - Set to true to set the orientation of the avatar. @@ -1395,61 +1480,70 @@ public slots: /**jsdoc - * ####### Why Q_INVOKABLE? * @function MyAvatar.updateMotionBehaviorFromMenu */ Q_INVOKABLE void updateMotionBehaviorFromMenu(); /**jsdoc - * @function MyAvatar.setToggleHips - * @param {boolean} enabled - */ + * @function MyAvatar.setToggleHips + * @param {boolean} enabled + */ void setToggleHips(bool followHead); + /**jsdoc - * @function MyAvatar.setEnableDebugDrawBaseOfSupport - * @param {boolean} enabled - */ + * @function MyAvatar.setEnableDebugDrawBaseOfSupport + * @param {boolean} enabled + */ void setEnableDebugDrawBaseOfSupport(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawDefaultPose * @param {boolean} enabled */ void setEnableDebugDrawDefaultPose(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawAnimPose * @param {boolean} enabled */ void setEnableDebugDrawAnimPose(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawPosition * @param {boolean} enabled */ void setEnableDebugDrawPosition(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawHandControllers * @param {boolean} enabled */ void setEnableDebugDrawHandControllers(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawSensorToWorldMatrix * @param {boolean} enabled */ void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawIKTargets * @param {boolean} enabled */ void setEnableDebugDrawIKTargets(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawIKConstraints * @param {boolean} enabled */ void setEnableDebugDrawIKConstraints(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawIKChains * @param {boolean} enabled */ void setEnableDebugDrawIKChains(bool isEnabled); + /**jsdoc * @function MyAvatar.setEnableDebugDrawDetailedCollision * @param {boolean} enabled @@ -1457,23 +1551,20 @@ public slots: void setEnableDebugDrawDetailedCollision(bool isEnabled); /**jsdoc - * Get whether or not your avatar mesh is visible. + * Gets whether or not your avatar mesh is visible. * @function MyAvatar.getEnableMeshVisible * @returns {boolean} true if your avatar's mesh is visible, otherwise false. */ bool getEnableMeshVisible() const override; /**jsdoc - * ####### TODO; Should this really be exposed in the API? * @function MyAvatar.storeAvatarEntityDataPayload + * @deprecated This function is deprecated and will be removed. */ void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload) override; /**jsdoc - * ####### Does override change functionality? If so, document here and don't borrow; if not, borrow and don't document here. - * @function MyAvatar.clearAvatarEntity - * @param {Uuid} entityID - * @param {boolean} [requiresRemovalFromTree] + * @comment Uses the base class's JSDoc. */ void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true) override; @@ -1483,7 +1574,7 @@ public slots: void sanitizeAvatarEntityProperties(EntityItemProperties& properties) const; /**jsdoc - * Set whether or not your avatar mesh is visible. + * Sets whether or not your avatar mesh is visible. * @function MyAvatar.setEnableMeshVisible * @param {boolean} visible - true to set your avatar mesh visible; false to set it invisible. * @example Make your avatar invisible for 10s. @@ -1577,25 +1668,27 @@ signals: void collisionWithEntity(const Collision& collision); /**jsdoc - * Triggered when collisions with avatar enabled or disabled + * Triggered when collisions with the environment are enabled or disabled. * @function MyAvatar.collisionsEnabledChanged - * @param {boolean} enabled + * @param {boolean} enabled - true if collisions with the environment are enabled, false if + * they're not. * @returns {Signal} */ void collisionsEnabledChanged(bool enabled); /**jsdoc - * Triggered when collisions with other avatars enabled or disabled + * Triggered when collisions with other avatars are enabled or disabled. * @function MyAvatar.otherAvatarsCollisionsEnabledChanged - * @param {boolean} enabled + * @param {boolean} enabled - true if collisions with other avatars are enabled, false if they're + * not. * @returns {Signal} */ void otherAvatarsCollisionsEnabledChanged(bool enabled); /**jsdoc - * Triggered when avatar's animation url changes + * Triggered when the avatar's animation changes. * @function MyAvatar.animGraphUrlChanged - * @param {url} url + * @param {url} url - The URL of the new animation. * @returns {Signal} */ void animGraphUrlChanged(const QUrl& url); @@ -1672,18 +1765,24 @@ signals: void scaleChanged(); /**jsdoc - * Triggered when hand touch is globally enabled or disabled + * Triggered when the hand touch effect is enabled or disabled for the avatar. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.shouldDisableHandTouchChanged - * @param {boolean} shouldDisable + * @param {boolean} disabled - true if the hand touch effect is disabled for the avatar, + * false if it isn't disabled. * @returns {Signal} */ void shouldDisableHandTouchChanged(bool shouldDisable); /**jsdoc - * Triggered when hand touch is enabled or disabled for an specific entity + * Triggered when the hand touch is enabled or disabled on a specific entity. + *

The hand touch effect makes the avatar's fingers adapt to the shape of any object grabbed, creating the effect that + * it is really touching that object.

* @function MyAvatar.disableHandTouchForIDChanged - * @param {Uuid} entityID - ID of the entity that will enable hand touch effect - * @param {boolean} disable + * @param {Uuid} entityID - The entity that the hand touch effect has been enabled or disabled for. + * @param {boolean} disabled - true if the hand touch effect is disabled for the entity, + * false if it isn't disabled. * @returns {Signal} */ void disableHandTouchForIDChanged(const QUuid& entityID, bool disable); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 98aa255641..11940ad76a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -215,17 +215,17 @@ public: Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const; /**jsdoc - * Provides read only access to the default joint rotations in avatar coordinates. + * Provides read-only access to the default joint rotations in avatar coordinates. * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame - * @param index {number} -The joint index. + * @param index {number} - The joint index. * @returns {Quat} The rotation of this joint in avatar coordinates. */ Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const; /**jsdoc - * Provides read only access to the default joint translations in avatar coordinates. + * Provides read-only access to the default joint translations in avatar coordinates. * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame @@ -261,52 +261,51 @@ public: // world-space to avatar-space rigconversion functions /**jsdoc - * @function MyAvatar.worldToJointPoint - * @param {Vec3} position - * @param {number} [jointIndex=-1] - * @returns {Vec3} - */ + * @function MyAvatar.worldToJointPoint + * @param {Vec3} position + * @param {number} [jointIndex=-1] + * @returns {Vec3} + */ Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const; /**jsdoc - * @function MyAvatar.worldToJointDirection - * @param {Vec3} direction - * @param {number} [jointIndex=-1] - * @returns {Vec3} - */ + * @function MyAvatar.worldToJointDirection + * @param {Vec3} direction + * @param {number} [jointIndex=-1] + * @returns {Vec3} + */ Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const; /**jsdoc - * @function MyAvatar.worldToJointRotation - * @param {Quat} rotation - * @param {number} [jointIndex=-1] - * @returns {Quat} + * @function MyAvatar.worldToJointRotation + * @param {Quat} rotation + * @param {number} [jointIndex=-1] + * @returns {Quat} */ Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const; - /**jsdoc - * @function MyAvatar.jointToWorldPoint - * @param {vec3} position - * @param {number} [jointIndex=-1] - * @returns {Vec3} - */ + * @function MyAvatar.jointToWorldPoint + * @param {vec3} position + * @param {number} [jointIndex=-1] + * @returns {Vec3} + */ Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const; /**jsdoc - * @function MyAvatar.jointToWorldDirection - * @param {Vec3} direction - * @param {number} [jointIndex=-1] - * @returns {Vec3} - */ + * @function MyAvatar.jointToWorldDirection + * @param {Vec3} direction + * @param {number} [jointIndex=-1] + * @returns {Vec3} + */ Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const; /**jsdoc - * @function MyAvatar.jointToWorldRotation - * @param {Quat} rotation - * @param {number} [jointIndex=-1] - * @returns {Quat} - */ + * @function MyAvatar.jointToWorldRotation + * @param {Quat} rotation + * @param {number} [jointIndex=-1] + * @returns {Quat} + */ Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; @@ -321,7 +320,7 @@ public: float radius1, float radius2, const glm::vec4& color); /**jsdoc - * Set the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example, + * Sets the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example, * with an offset of { x: 0, y: 0.1, z: 0 }, your avatar will appear to be raised off the ground slightly. * @function MyAvatar.setSkeletonOffset * @param {Vec3} offset - The skeleton offset to set. @@ -337,7 +336,7 @@ public: Q_INVOKABLE void setSkeletonOffset(const glm::vec3& offset); /**jsdoc - * Get the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example, + * Gets the offset applied to the current avatar. The offset adjusts the position that the avatar is rendered. For example, * with an offset of { x: 0, y: 0.1, z: 0 }, your avatar will appear to be raised off the ground slightly. * @function MyAvatar.getSkeletonOffset * @returns {Vec3} The current skeleton offset. @@ -349,7 +348,7 @@ public: virtual glm::vec3 getSkeletonPosition() const; /**jsdoc - * Get the position of a joint in the current avatar. + * Gets the position of a joint in the current avatar. * @function MyAvatar.getJointPosition * @param {number} index - The index of the joint. * @returns {Vec3} The position of the joint in world coordinates. @@ -357,7 +356,7 @@ public: Q_INVOKABLE glm::vec3 getJointPosition(int index) const; /**jsdoc - * Get the position of a joint in the current avatar. + * Gets the position of a joint in the current avatar. * @function MyAvatar.getJointPosition * @param {string} name - The name of the joint. * @returns {Vec3} The position of the joint in world coordinates. @@ -367,7 +366,7 @@ public: Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const; /**jsdoc - * Get the position of the current avatar's neck in world coordinates. + * Gets the position of the current avatar's neck in world coordinates. * @function MyAvatar.getNeckPosition * @returns {Vec3} The position of the neck in world coordinates. * @example Report the position of your avatar's neck. @@ -401,10 +400,10 @@ public: void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); float computeMass(); /**jsdoc - * Get the position of the current avatar's feet (or rather, bottom of its collision capsule) in world coordinates. + * Gets the position of the current avatar's feet (or rather, bottom of its collision capsule) in world coordinates. * @function MyAvatar.getWorldFeetPosition * @returns {Vec3} The position of the avatar's feet in world coordinates. - */ + */ Q_INVOKABLE glm::vec3 getWorldFeetPosition(); void setPositionViaScript(const glm::vec3& position) override; @@ -439,9 +438,9 @@ public: Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override; /**jsdoc - * Returns an array of joints, where each joint is an object containing name, index, and parentIndex fields. + * Gets information on all the joints in the avatar's skeleton. * @function MyAvatar.getSkeleton - * @returns {MyAvatar.SkeletonJoint[]} A list of information about each joint in this avatar's skeleton. + * @returns {MyAvatar.SkeletonJoint[]} Information about each joint in the avatar's skeleton. */ /**jsdoc * Information about a single joint in an Avatar's skeleton hierarchy. @@ -524,6 +523,8 @@ public: signals: /**jsdoc * @function MyAvatar.targetScaleChanged + * @param {number} targetScale + * @returns Signal */ void targetScaleChanged(float targetScale); @@ -533,7 +534,7 @@ public slots: // thread safe, will return last valid palm from cache /**jsdoc - * Get the position of the left palm in world coordinates. + * Gets the position of the left palm in world coordinates. * @function MyAvatar.getLeftPalmPosition * @returns {Vec3} The position of the left palm in world coordinates. * @example Report the position of your avatar's left palm. @@ -542,15 +543,16 @@ public slots: glm::vec3 getLeftPalmPosition() const; /**jsdoc - * Get the rotation of the left palm in world coordinates. + * Gets the rotation of the left palm in world coordinates. * @function MyAvatar.getLeftPalmRotation * @returns {Quat} The rotation of the left palm in world coordinates. * @example Report the rotation of your avatar's left palm. * print(JSON.stringify(MyAvatar.getLeftPalmRotation())); */ glm::quat getLeftPalmRotation() const; + /**jsdoc - * Get the position of the right palm in world coordinates. + * Gets the position of the right palm in world coordinates. * @function MyAvatar.getRightPalmPosition * @returns {Vec3} The position of the right palm in world coordinates. * @example Report the position of your avatar's right palm. @@ -569,22 +571,23 @@ public slots: /**jsdoc * @function MyAvatar.setModelURLFinished + * @param {boolean} success */ // hooked up to Model::setURLFinished signal void setModelURLFinished(bool success); /**jsdoc * @function MyAvatar.rigReady - * @returns {Signal} + * @deprecated This function is deprecated and will be removed. */ // Hooked up to Model::rigReady signal void rigReady(); /**jsdoc * @function MyAvatar.rigReset - * @returns {Signal} + * @deprecated This function is deprecated and will be removed. */ - // Jooked up to Model::rigReset signal + // Hooked up to Model::rigReset signal void rigReset(); protected: From 0b7cddb886ffd24f7e150b9150fe7a582626be77 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 8 Mar 2019 13:49:10 +1300 Subject: [PATCH 10/88] Fill in and tidy MyAvatar JSDoc --- interface/src/avatar/MyAvatar.cpp | 14 + interface/src/avatar/MyAvatar.h | 533 +++++++++++++----- .../src/avatars-renderer/Avatar.h | 93 ++- libraries/shared/src/RegisteredMetaTypes.cpp | 9 +- 4 files changed, 483 insertions(+), 166 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 30e8733a42..f62896772d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3531,6 +3531,12 @@ void MyAvatar::clearScaleRestriction() { _haveReceivedHeightLimitsFromDomain = false; } +/**jsdoc + * A teleport target. + * @typedef {object} MyAvatar.GoToProperties + * @property {Vec3} position - The new position for the avatar, in world coordinates. + * @property {Quat} [orientation] - The new orientation for the avatar. + */ void MyAvatar::goToLocation(const QVariant& propertiesVar) { qCDebug(interfaceapp, "MyAvatar QML goToLocation"); auto properties = propertiesVar.toMap(); @@ -3887,6 +3893,14 @@ void MyAvatar::setCollisionWithOtherAvatarsFlags() { _characterController.setPendingFlagsUpdateCollisionMask(); } +/**jsdoc + * A collision capsule is a cylinder with hemispherical ends. It is used, in particular, to approximate the extents of an + * avatar. + * @typedef {object} MyAvatar.CollisionCapsule + * @property {Vec3} start - The bottom end of the cylinder, excluding the bottom hemisphere. + * @property {Vec3} end - The top end of the cylinder, excluding the top hemisphere. + * @property {number} radius - The radius of the cylinder and the hemispheres. + */ void MyAvatar::updateCollisionCapsuleCache() { glm::vec3 start, end; float radius; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a0f1531e64..f05bdd5184 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -155,33 +155,45 @@ class MyAvatar : public Avatar { * @property {boolean} hasProceduralEyeFaceMovement=true - If true then procedural eye movement is turned on. * @property {boolean} hasAudioEnabledFaceMovement=true - If true then voice audio will move the mouth * blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. - * @property {number} rotationRecenterFilterLength - * @property {number} rotationThreshold - * @property {boolean} enableStepResetRotation - * @property {boolean} enableDrawAverageFacing + * @property {number} rotationRecenterFilterLength - Configures how quickly the avatar root rotates to recenter its facing + * direction to match that of the user's torso based on head and hands orientation. A smaller value makes the + * recentering happen more quickly. The minimum value is 0.01. + * @property {number} rotationThreshold - The angle in radians that the user's torso facing direction (based on head and + * hands orientation) can differ from that of the avatar before the avatar's root is rotated to match the user's torso. + * @property {boolean} enableStepResetRotation - If true then after the user's avatar takes a step, the + * avatar's root immediately rotates to recenter its facing direction to match that of the user's torso based on head + * and hands orientation. + * @property {boolean} enableDrawAverageFacing - If true, debug graphics are drawn that show the average + * facing direction of the user's torso (based on head and hands orientation). This can be useful if you want to try + * out different filter lengths and thresholds. * * @property {Vec3} leftHandPosition - The position of the left hand in avatar coordinates if it's being positioned by * controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. * @property {Vec3} rightHandPosition - The position of the right hand in avatar coordinates if it's being positioned by * controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} leftHandTipPosition - The position 30cm offset from the left hand in avatar coordinates if it's being - * positioned by controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. - * @property {Vec3} rightHandTipPosition - The position 30cm offset from the right hand in avatar coordinates if it's being - * positioned by controllers, otherwise {@link Vec3(0)|Vec3.ZERO}. Read-only. + * @property {Vec3} leftHandTipPosition - The position 0.3m in front of the left hand's position, in the direction along the + * palm, in avatar coordinates. If the hand isn't being positioned by a controller, the value is + * {@link Vec3(0)|Vec3.ZERO}. Read-only. + * @property {Vec3} rightHandTipPosition - The position 0.3m in front of the right hand's position, in the direction along + * the palm, in avatar coordinates. If the hand isn't being positioned by a controller, the value is + * {@link Vec3(0)|Vec3.ZERO}. Read-only. * - * @property {Pose} leftHandPose - The pose of the left hand as determined by the hand controllers. Read-only. - * @property {Pose} rightHandPose - The pose right hand position as determined by the hand controllers. Read-only. - * @property {Pose} leftHandTipPose - The pose of the left hand as determined by the hand controllers, with the position - * by 30cm. Read-only. - * @property {Pose} rightHandTipPose - The pose of the right hand as determined by the hand controllers, with the position - * by 30cm. Read-only. + * @property {Pose} leftHandPose - The pose of the left hand as determined by the hand controllers, relative to the avatar. + * Read-only. + * @property {Pose} rightHandPose - The pose right hand position as determined by the hand controllers, relative to the + * avatar. Read-only. + * @property {Pose} leftHandTipPose - The pose of the left hand as determined by the hand controllers, relative to the + * avatar, with the position adjusted to be 0.3m along the direction of the palm. Read-only. + * @property {Pose} rightHandTipPose - The pose of the right hand as determined by the hand controllers, relative to the + * avatar, with the position adjusted by 0.3m along the direction of the palm. Read-only. * - * @property {number} energy - * @property {boolean} isAway + * @property {number} energy - Deprecated: This property will be removed from the API. + * @property {boolean} isAway - true if your avatar is away (i.e., inactive), false if it is + * active. * - * @property {boolean} centerOfGravityModelEnabled=true - If true then the avatar hips are placed according to the center of - * gravity model that balance the center of gravity over the base of support of the feet. Setting the value false - * will result in the default behaviour where the hips are placed under the head. + * @property {boolean} centerOfGravityModelEnabled=true - If true then the avatar hips are placed according to + * the center of gravity model that balances the center of gravity over the base of support of the feet. Setting the + * value false results in the default behavior where the hips are positioned under the head. * @property {boolean} hmdLeanRecenterEnabled=true - If true then the avatar is re-centered to be under the * head's position. In room-scale VR, this behavior is what causes your avatar to follow your HMD as you walk around * the room. Setting the value false is useful if you want to pin the avatar to a fixed position. @@ -198,8 +210,8 @@ class MyAvatar : public Avatar { * boundaries while teleporting.
* Note: Setting the value has no effect unless Interface is restarted. * - * @property {number} yawSpeed=75 - * @property {number} pitchSpeed=50 + * @property {number} yawSpeed=75 - The mouse X sensitivity value in Settings > General. Read-only. + * @property {number} pitchSpeed=50 - The mouse Y sensitivity value in Settings > General. Read-only. * * @property {boolean} hmdRollControlEnabled=true - If true, the roll angle of your HMD turns your avatar * while flying. @@ -215,13 +227,20 @@ class MyAvatar : public Avatar { * where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated. * Read-only. * - * @property {number} walkSpeed - * @property {number} walkBackwardSpeed - * @property {number} sprintSpeed - * @property {number} isInSittingState - * @property {MyAvatar.SitStandModelType} userRecenterModel - * @property {boolean} isSitStandStateLocked - * @property {boolean} allowTeleporting + * @property {number} walkSpeed - Adjusts the walk speed of your avatar. + * @property {number} walkBackwardSpeed - Adjusts the walk backward speed of your avatar. + * @property {number} sprintSpeed - Adjusts the sprint speed of your avatar. + * @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior. + * @property {number} isInSittingState - true if your avatar is sitting (avatar leaning is disabled, + * recenntering is enabled), false if it is standing (avatar leaning is enabled, and avatar recenters if it + * leans too far). If userRecenterModel == 2 (i.e., auto) the property value automatically updates as the + * user sits or stands, unless isSitStandStateLocked == true. Setting the property value overrides the + * current siting / standing state, which is updated when the user next sits or stands unless + * isSitStandStateLocked == true. + * @property {boolean} isSitStandStateLocked - true locks the avatar sitting / standing state, i.e., disables + * automatically changing it based on the user sitting or standing. + * @property {boolean} allowTeleporting - true if teleporting is enabled in the Interface settings, + * false if it isn't. Read-only. * * @borrows Avatar.getDomainMinScale as getDomainMinScale * @borrows Avatar.getDomainMaxScale as getDomainMaxScale @@ -353,6 +372,40 @@ class MyAvatar : public Avatar { using TimePoint = Clock::time_point; public: + + /**jsdoc + *

Logical keys that drive your avatar and camera.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0TRANSLATE_XMove the user's avatar in the direction of its x-axis, if the + * camera isn't in independent or mirror modes.
1TRANSLATE_YMove the user's avatar in the direction of its y-axis, if the + * camera isn't in independent or mirror modes.
2TRANSLATE_ZMove the user's avatar in the direction of its z-axis, if the + * camera isn't in independent or mirror modes
3YAWRotate the user's avatar about its y-axis at a rate proportional to the + * control value, if the camera isn't in independent or mirror modes.
4STEP_TRANSLATE_XNo action.
5STEP_TRANSLATE_YNo action.
6STEP_TRANSLATE_ZNo action.
7STEP_YAWRotate the user's avatar about its y-axis in a step increment, if + * the camera isn't in independent or mirror modes.
8PITCHRotate the user's avatar head and attached camera about its negative + * x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the camera isn't in HMD, + * independent, or mirror modes.
9ZOOMZooms the camera in or out.
10DELTA_YAWRotate the user's avatar about its y-axis by an amount proportional + * to the control value, if the camera isn't in independent or mirror modes.
11DELTA_PITCHRotate the user's avatar head and attached camera about its + * negative x-axis (i.e., positive values pitch down) by an amount proportional to the control value, if the camera + * isn't in HMD, independent, or mirror modes.
+ * @typedef {number} MyAvatar.DriveKeys + */ enum DriveKeys { TRANSLATE_X = 0, TRANSLATE_Y, @@ -371,6 +424,25 @@ public: Q_ENUM(DriveKeys) /**jsdoc + *

Specifies different avatar leaning and recentering behaviors.

+ * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0ForceSitAssumes the user is seated in the real world. Disables avatar + * leaning regardless of what the avatar is doing in the virtual world (i.e., avatar always recenters).
1ForceStandAssumes the user is standing in the real world. Enables avatar + * leaning regardless of what the avatar is doing in the virtual world (i.e. avatar leans, then if leans too far it + * recenters).
2AutoInterface detects when the user is standing or seated in the real world. + * Avatar leaning is disabled when the user is sitting (i.e., avatar always recenters), and avatar leaning is enabled + * when the user is standing (i.e., avatar leans, then if leans too far it recenters).
3DisableHMDLeanBoth avatar leaning and recentering are disabled regardless of + * what the user is doing in the real world and no matter what their avatar is doing in the virtual world. Enables + * the avatar to sit on the floor when the user sits on the floor.
Note: Experimental.
* @typedef {number} MyAvatar.SitStandModelType */ enum SitStandModelType { @@ -399,6 +471,7 @@ public: void setCollisionWithOtherAvatarsFlags() override; /**jsdoc + * Resets the sensor positioning of your HMD (if used) and recenters your avatar body and head. * @function MyAvatar.resetSensorsAndBody */ Q_INVOKABLE void resetSensorsAndBody(); @@ -427,14 +500,16 @@ public: const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } /**jsdoc + * Gets the avatar orientation. Suitable for use in QML. * @function MyAvatar.setOrientationVar - * @param {object} newOrientationVar + * @param {object} newOrientationVar - The avatar orientation. */ Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); /**jsdoc + * Gets the avatar orientation. Suitable for use in QML. * @function MyAvatar.getOrientationVar - * @returns {object} + * @returns {object} The avatar orientation. */ Q_INVOKABLE QVariant getOrientationVar() const; @@ -598,74 +673,124 @@ public: // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. // It is not specified in what order multiple handlers are called. /**jsdoc + * Adds an animation state handler function that is invoked just before each animation graph update. More than one + * animation state handler function may be added by calling addAnimationStateHandler multiple times. It is not + * specified in what order multiple handlers are called. + *

The animation state handler function is called with an {@link MyAvatar.AnimStateDictionary|AnimStateDictionary} + * "animStateDictionaryIn" parameter and is expected to return an + * {@link MyAvatar.AnimStateDictionary|AnimStateDictionary} "animStateDictionaryOut" object. The + * animStateDictionaryOut object can be the same object as animStateDictionaryIn, or it can be a + * different object. The animStateDictionaryIn may be shared among multiple handlers and thus may contain + * additional properties specified when adding the different handlers.

+ *

A handler may change a value from animStateDictionaryIn or add different values in the + * animStateDictionaryOut returned. Any property values set in animStateDictionaryOut will + * override those of the internal animation machinery.|null} propertiesList - The list of {@link MyAvatar.AnimStateDictionary|AnimStateDictionary} + * properties that should be included the in parameter that the handler function is called with. If null + * then all properties are included in the call parameter. + * @returns {number} The ID of the animation state handler function if successfully added, undefined if not + * successfully added. + * @example Log all the animation state dictionary parameters for a short while. + * function animStateHandler(dictionary) { + * print("Anim state dictionary: " + JSON.stringify(dictionary)); + * } + * + * var handler = MyAvatar.addAnimationStateHandler(animStateHandler, null); + * + * Script.setTimeout(function () { + * MyAvatar.removeAnimationStateHandler(handler); + * }, 100); */ Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _skeletonModel->getRig().addAnimationStateHandler(handler, propertiesList); } /**jsdoc + * Removes an animation state handler function. * @function MyAvatar.removeAnimationStateHandler - * @param {number} handler + * @param {number} handler - The ID of the animation state handler function to remove. */ // Removes a handler previously added by addAnimationStateHandler. Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _skeletonModel->getRig().removeAnimationStateHandler(handler); } /**jsdoc + * Gets whether or not you do snap turns in HMD mode. * @function MyAvatar.getSnapTurn - * @returns {boolean} + * @returns {boolean} true if you do snap turns in HMD mode; false if you do smooth turns in HMD + * mode. */ Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; } /**jsdoc + * Sets whether your should do snap turns or smooth turns in HMD mode. * @function MyAvatar.setSnapTurn - * @param {boolean} on + * @param {boolean} on - true to do snap turns in HMD mode; false to do smooth turns in HMD mode. */ Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; } /**jsdoc + * Sets the avatar's dominant hand. * @function MyAvatar.setDominantHand - * @param {string} hand + * @param {string} hand - The dominant hand: "left" for the left hand or "right" for the right + * hand. Any other value has no effect. */ Q_INVOKABLE void setDominantHand(const QString& hand); /**jsdoc + * Gets the avatar's dominant hand. * @function MyAvatar.getDominantHand - * @returns {string} + * @returns {string} "left" for the left hand, "right" for the right hand. */ Q_INVOKABLE QString getDominantHand() const; /**jsdoc * @function MyAvatar.setHmdAvatarAlignmentType - * @param {string} hand + * @param {string} type - "head" to align your head and your avatar's head, "eyes" to align your + * eyes and your avatar's eyes. + * */ - Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand); + Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& type); /**jsdoc + * Gets the HMD alignment for your avatar. * @function MyAvatar.getHmdAvatarAlignmentType - * @returns {string} + * @returns {string} "head" if aligning your head and your avatar's head, "eyes" if aligning your + * eyes and your avatar's eyes. */ Q_INVOKABLE QString getHmdAvatarAlignmentType() const; /**jsdoc + * Sets whether the avatar hips are balanced over the feet or positioned under the head. * @function MyAvatar.setCenterOfGravityModelEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to balance the hips over the feet, false to position the hips + * under the head. */ Q_INVOKABLE void setCenterOfGravityModelEnabled(bool value) { _centerOfGravityModelEnabled = value; } /**jsdoc + * Gets whether the avatar hips are being balanced over the feet or placed under the head. * @function MyAvatar.getCenterOfGravityModelEnabled - * @returns {boolean} + * @returns {boolean} true if the hips are being balanced over the feet, false if the hips are + * being positioned under the head. */ Q_INVOKABLE bool getCenterOfGravityModelEnabled() const { return _centerOfGravityModelEnabled; } + /**jsdoc + * Sets whether or not the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering + * causes your avatar to follow your HMD as you walk around the room. Disabling recentering is useful if you want to pin + * the avatar to a fixed position. * @function MyAvatar.setHMDLeanRecenterEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to recenter the avatar under the head as it moves, false to + * disable recentering. */ Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } /**jsdoc + * Gets whether or not the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering + * causes your avatar to follow your HMD as you walk around the room. * @function MyAvatar.getHMDLeanRecenterEnabled - * @returns {boolean} + * @returns {boolean} true if recentering is enabled, false if not. */ Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } @@ -739,30 +864,42 @@ public: float getDriveKey(DriveKeys key) const; /**jsdoc + * Gets the value of a drive key, regardless of whether or not it is disabled. * @function MyAvatar.getRawDriveKey - * @param {DriveKeys} key - * @returns {number} + * @param {MyAvatar.DriveKeys} key - The drive key. + * @returns {number} The value of the drive key. */ Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; void relayDriveKeysToCharacterController(); /**jsdoc + * Disables the action associated with a drive key. * @function MyAvatar.disableDriveKey - * @param {DriveKeys} key + * @param {MyAvatar.DriveKeys} key - The drive key to disable. + * @example Disable rotating your avatar using the keyboard for a couple of seconds. + * var YAW = 3; + * print("Disable"); + * MyAvatar.disableDriveKey(YAW); + * Script.setTimeout(function () { + * print("Enable"); + * MyAvatar.enableDriveKey(YAW); + * }, 5000); */ Q_INVOKABLE void disableDriveKey(DriveKeys key); /**jsdoc + * Enables the acction associated with a drive key. * @function MyAvatar.enableDriveKey - * @param {DriveKeys} key + * @param {MyAvatar.DriveKeys} key - The drive key to enable. */ Q_INVOKABLE void enableDriveKey(DriveKeys key); /**jsdoc + * Checks whether or not a drive key is disabled. * @function MyAvatar.isDriveKeyDisabled - * @param {DriveKeys} key - * @returns {boolean} + * @param {DriveKeys} key - The drive key to check. + * @returns {boolean} true if the drive key is disabled, false if it isn't. */ Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; @@ -809,26 +946,32 @@ public: Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } /**jsdoc + * Gets the yaw of the avatar's head relative to its body. * @function MyAvatar.getHeadFinalYaw - * @returns {number} + * @returns {number} The yaw of the avatar's head, in degrees. */ Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } /**jsdoc + * Gets the roll of the avatar's head relative to its body. * @function MyAvatar.getHeadFinalRoll - * @returns {number} + * @returns {number} The roll of the avatar's head, in degrees. */ Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } /**jsdoc + * Gets the pitch of the avatar's head relative to its body. * @function MyAvatar.getHeadFinalPitch - * @returns {number} + * @returns {number} The pitch of the avatar's head, in degrees. */ Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); } /**jsdoc + * If a face tracker is connected and being used, gets the estimated pitch of the user's head scaled such that the avatar + * looks at the edge of the view frustum when the user looks at the edge of their screen. * @function MyAvatar.getHeadDeltaPitch - * @returns {number} + * @returns {number} The pitch that the avatar's head should be if a face tracker is connected and being used, otherwise + * 0, in degrees. */ Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); } @@ -845,21 +988,27 @@ public: /**jsdoc * Gets the position of the avatar your avatar is currently looking at. * @function MyAvatar.getTargetAvatarPosition - * @returns {Vec3} The position of the avatar your avatar is currently looking at. + * @returns {Vec3} The position of the avatar beeing looked at. * @example Report the position of the avatar you're currently looking at. * print(JSON.stringify(MyAvatar.getTargetAvatarPosition())); */ + // FIXME: If not looking at an avatar, the most recently looked-at position is returned. This should be fixed to return + // undefined or {NaN, NaN, NaN} or similar. Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } /**jsdoc + * Gets information on the avatar your avatar is currently looking at. * @function MyAvatar.getTargetAvatar - * @returns {AvatarData} + * @returns {AvatarData} Information on the avatar being looked at. */ + // FIXME: The return type doesn't have a conversion to a script value so the function always returns undefined in + // JavaScript. Note: When fixed, JSDoc is needed for the return type. Q_INVOKABLE ScriptAvatarData* getTargetAvatar() const; /**jsdoc - * Gets the position of the avatar's left hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
+ * Gets the position of the avatar's left hand, relative to the avatar, as positioned by a hand controller (e.g., Oculus + * Touch or Vive). *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.)

* @function MyAvatar.getLeftHandPosition @@ -871,7 +1020,8 @@ public: Q_INVOKABLE glm::vec3 getLeftHandPosition() const; /**jsdoc - * Gets the position of the avatar's right hand as positioned by a hand controller (e.g., Oculus Touch or Vive).
+ * Gets the position of the avatar's right hand, relative to the avatar, as positioned by a hand controller (e.g., Oculus + * Touch or Vive). *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.)

* @function MyAvatar.getRightHandPosition @@ -883,26 +1033,32 @@ public: Q_INVOKABLE glm::vec3 getRightHandPosition() const; /**jsdoc + * Gets the position 0.3m in front of the left hand's position in the direction along the palm, in avatar coordinates, as + * positioned by a hand controller. * @function MyAvatar.getLeftHandTipPosition - * @returns {Vec3} + * @returns {Vec3} The position 0.3m in front of the left hand's position in the direction along the palm, in avatar + * coordinates. If the hand isn't being positioned by a controller, {@link Vec3(0)|Vec3.ZERO} is returned. */ Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const; /**jsdoc + * Gets the position 0.3m in front of the right hand's position in the direction along the palm, in avatar coordinates, as + * positioned by a hand controller. * @function MyAvatar.getRightHandTipPosition - * @returns {Vec3} + * @returns {Vec3} The position 0.3m in front of the right hand's position in the direction along the palm, in avatar + * coordinates. If the hand isn't being positioned by a controller, {@link Vec3(0)|Vec3.ZERO} is returned. */ Q_INVOKABLE glm::vec3 getRightHandTipPosition() const; /**jsdoc * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a - * hand controller (e.g., Oculus Touch or Vive).
+ * hand controller (e.g., Oculus Touch or Vive). *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.) If you are using the Leap Motion, the return value's valid property will be * false and any pose values returned will not be meaningful.

* @function MyAvatar.getLeftHandPose - * @returns {Pose} + * @returns {Pose} The pose of the avatar's left hand, relative to the avatar, as positioned by a hand controller. * @example Report the pose of your avatar's left hand. * print(JSON.stringify(MyAvatar.getLeftHandPose())); */ @@ -910,26 +1066,38 @@ public: /**jsdoc * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand as positioned by a - * hand controller (e.g., Oculus Touch or Vive).
+ * hand controller (e.g., Oculus Touch or Vive). *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints * for hand animation.) If you are using the Leap Motion, the return value's valid property will be * false and any pose values returned will not be meaningful.

* @function MyAvatar.getRightHandPose - * @returns {Pose} + * @returns {Pose} The pose of the avatar's right hand, relative to the avatar, as positioned by a hand controller. * @example Report the pose of your avatar's right hand. * print(JSON.stringify(MyAvatar.getRightHandPose())); */ Q_INVOKABLE controller::Pose getRightHandPose() const; /**jsdoc + * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand, relative to the avatar, as + * positioned by a hand controller (e.g., Oculus Touch or Vive), and translated 0.3m along the palm. + *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints + * for hand animation.) If you are using the Leap Motion, the return value's valid property will be + * false and any pose values returned will not be meaningful.

* @function MyAvatar.getLeftHandTipPose - * @returns {Pose} + * @returns {Pose} The pose of the avatar's left hand, relative to the avatar, as positioned by a hand controller, and + * translated 0.3m along the palm. */ Q_INVOKABLE controller::Pose getLeftHandTipPose() const; /**jsdoc + * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's right hand, relative to the avatar, as + * positioned by a hand controller (e.g., Oculus Touch or Vive), and translated 0.3m along the palm. + *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints + * for hand animation.) If you are using the Leap Motion, the return value's valid property will be + * false and any pose values returned will not be meaningful.

* @function MyAvatar.getRightHandTipPose - * @returns {Pose} + * @returns {Pose} The pose of the avatar's right hand, relative to the avatar, as positioned by a hand controller, and + * translated 0.3m along the palm. */ Q_INVOKABLE controller::Pose getRightHandTipPose() const; @@ -952,33 +1120,39 @@ public: virtual void clearJointsData() override; /**jsdoc + * Sets and locks a joint's position and orientation. + *

Note: Only works on the hips joint.

* @function MyAvatar.pinJoint - * @param {number} index - * @param {Vec3} position - * @param {Quat} orientation - * @returns {boolean} + * @param {number} index - The index of the joint. + * @param {Vec3} position - The position of the joint in world coordinates. + * @param {Quat} orientation - The orientation of the joint in world coordinates. + * @returns {boolean} true if the joint was pinned, false if it wasn't. */ Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation); bool isJointPinned(int index); /**jsdoc + * Clears a lock on a joint's position and orientation, as set by {@link MyAvatar.pinJoint|pinJoint}. + *

Note: Only works on the hips joint.

* @function MyAvatar.clearPinOnJoint - * @param {number} index - * @returns {boolean} + * @param {number} index - The index of the joint. + * @returns {boolean} true if the joint was unpinned, false if it wasn't. */ Q_INVOKABLE bool clearPinOnJoint(int index); /**jsdoc + * Gets the maximum error distance from the most recent inverse kinematics (IK) solution. * @function MyAvatar.getIKErrorOnLastSolve - * @returns {number} + * @returns {number} The maximum IK error distance. */ Q_INVOKABLE float getIKErrorOnLastSolve() const; /**jsdoc + * Changes the user's avatar and associated descriptive name. * @function MyAvatar.useFullAvatarURL - * @param {string} fullAvatarURL - * @param {string} [modelName=""] + * @param {string} fullAvatarURL - The URL of the avatar's .fst file. + * @param {string} [modelName=""] - Descriptive name of the avatar. */ Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString()); @@ -1065,7 +1239,7 @@ public: /**jsdoc * Gets the list of avatar entities and their properties. * @function MyAvatar.getAvatarEntitiesVariant - * @returns {MyAvatar.AvatarEntityData[]} + * @returns {MyAvatar.AvatarEntityData[]} The list of avatar entities and their properties. */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); @@ -1141,60 +1315,76 @@ public: */ Q_INVOKABLE bool getFlyingHMDPref(); - /**jsdoc + * Gets the target scale of the avatar. The target scale is the desired scale of the avatar without any restrictions on + * permissible scale values imposed by the domain. * @function MyAvatar.getAvatarScale - * @returns {number} + * @returns {number} The target scale for the avatar, range 0.0051000.0. */ Q_INVOKABLE float getAvatarScale(); /**jsdoc + * Sets the target scale of the avatar. The target scale is the desired scale of the avatar without any restrictions on + * permissible scale values imposed by the domain. * @function MyAvatar.setAvatarScale - * @param {number} scale + * @param {number} scale - The target scale for the avatar, range 0.0051000.0. */ Q_INVOKABLE void setAvatarScale(float scale); - /**jsdoc + * Sets whether or not the avatar should collide with entities. + *

Note: A false value won't disable collisions if the avatar is in a zone that disallows + * collisionless avatars, however the false value will be set so that collisions are disabled as soon as the + * avatar moves to a position where collisionless avatars are allowed. * @function MyAvatar.setCollisionsEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to enable the avatar to collide with entities, false to + * disable. */ Q_INVOKABLE void setCollisionsEnabled(bool enabled); /**jsdoc + * Gets whether or not the avatar will currently collide with entities. + *

Note: The avatar will always collide with entities if in a zone that disallows collisionless avatars. * @function MyAvatar.getCollisionsEnabled - * @returns {boolean} + * @returns {boolean} true if the avatar will currently collide with entities, false if it won't. */ Q_INVOKABLE bool getCollisionsEnabled(); /**jsdoc + * Sets whether or not the avatar should collide with other avatars. * @function MyAvatar.setOtherAvatarsCollisionsEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to enable the avatar to collide with other avatars, false + * to disable. */ Q_INVOKABLE void setOtherAvatarsCollisionsEnabled(bool enabled); /**jsdoc + * Gets whether or not the avatar will collide with other avatars. * @function MyAvatar.getOtherAvatarsCollisionsEnabled - * @returns {boolean} + * @returns {boolean} true if the avatar will collide with other avatars, false if it won't. */ Q_INVOKABLE bool getOtherAvatarsCollisionsEnabled(); /**jsdoc + * Gets the avatar's collision capsule: a cylinder with hemispherical ends that approximates the extents or the avatar. + *

Warning: The values returned are in world coordinates but aren't necessarily up to date with the + * avatar's current position.

* @function MyAvatar.getCollisionCapsule - * @returns {object} + * @returns {MyAvatar.CollisionCapsule} The avatar's collision capsule. */ Q_INVOKABLE QVariantMap getCollisionCapsule() const; /**jsdoc * @function MyAvatar.setCharacterControllerEnabled - * @param {boolean} enabled + * @param {boolean} enabled - true to enable the avatar to collide with entities, false to + * disable. * @deprecated Use {@link MyAvatar.setCollisionsEnabled} instead. */ Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated /**jsdoc * @function MyAvatar.getCharacterControllerEnabled - * @returns {boolean} + * @returns {boolean} true if the avatar will currently collide with entities, false if it won't. * @deprecated Use {@link MyAvatar.getCollisionsEnabled} instead. */ Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated @@ -1248,16 +1438,22 @@ public: glm::mat4 deriveBodyUsingCgModel(); /**jsdoc + * Tests whether a vector is pointing in the general direction of the avatar's "up" direction (i.e., dot product of vectors + * is > 0). * @function MyAvatar.isUp - * @param {Vec3} direction - * @returns {boolean} + * @param {Vec3} direction - The vector to test. + * @returns {boolean} true if the direction vector is pointing generally in the direction of the avatar's "up" + * direction. */ Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. /**jsdoc + * Tests whether a vector is pointing in the general direction of the avatar's "down" direction (i.e., dot product of + * vectors is < 0). * @function MyAvatar.isDown - * @param {Vec3} direction - * @returns {boolean} + * @param {Vec3} direction - The vector to test. + * @returns {boolean} true if the direction vector is pointing generally in the direction of the avatar's + * "down" direction. */ Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; @@ -1326,14 +1522,14 @@ public: * Gets the avatar entities as binary data. * @function MyAvatar.getAvatarEntityData * @override - * @returns {AvatarEntityMap} + * @returns {AvatarEntityMap} The avatar entities as binary data. */ AvatarEntityMap getAvatarEntityData() const override; /**jsdoc * Sets the avatar entities from binary data. * @function MyAvatar.setAvatarEntityData - * @param {AvatarEntityMap} avatarEntityData + * @param {AvatarEntityMap} avatarEntityData - The avatar entities as binary data. */ void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; @@ -1345,8 +1541,7 @@ public: void avatarEntityDataToJson(QJsonObject& root) const override; /**jsdoc - * @function MyAvatar.sendAvatarDataPacket - * @param {boolean} sendAll + * @comment Uses the base class's JSDoc. */ int sendAvatarDataPacket(bool sendAll = false) override; @@ -1386,18 +1581,22 @@ public slots: /**jsdoc * @function MyAvatar.animGraphLoaded + * @deprecated This function is deprecated and will be removed. */ void animGraphLoaded(); /**jsdoc + * Sets the amount of gravity applied to the avatar in the y-axis direction. (Negative values are downward.) * @function MyAvatar.setGravity - * @param {number} gravity + * @param {number} gravity - The amount of gravity to be applied to the avatar, in m/s2. */ void setGravity(float gravity); /**jsdoc + * Sets the amount of gravity applied to the avatar in the y-axis direction. (Negative values are downward.) The default + * value is -5 m/s2. * @function MyAvatar.getGravity - * @returns {number} + * @returns {number} The amount of gravity currently applied to the avatar, in m/s2. */ float getGravity(); @@ -1428,125 +1627,149 @@ public slots: bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false, bool withSafeLanding = true); /**jsdoc + * Moves the avatar to a new position and (optional) orientation in the domain. * @function MyAvatar.goToLocation - * @param {object} properties + * @param {MyAvatar.GoToProperties} target - The goto target. */ void goToLocation(const QVariant& properties); /**jsdoc + * Moves the avatar to a new position and then enables collisions. * @function MyAvatar.goToLocationAndEnableCollisions - * @param {Vec3} position + * @param {Vec3} position - The new position for the avatar, in world coordinates. */ void goToLocationAndEnableCollisions(const glm::vec3& newPosition); /**jsdoc * @function MyAvatar.safeLanding - * @param {Vec3} position - * @returns {boolean} + * @param {Vec3} position -The new position for the avatar, in world coordinates. + * @returns {boolean} true if the avatar was moved, false if it wasn't. + * @deprecated This function is deprecated and will be removed. */ bool safeLanding(const glm::vec3& position); /**jsdoc * @function MyAvatar.restrictScaleFromDomainSettings - * @param {object} domainSettingsObject + * @param {object} domainSettings - Domain settings. + * @deprecated This function is deprecated and will be removed. */ void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject); /**jsdoc * @function MyAvatar.clearScaleRestriction + * @deprecated This function is deprecated and will be removed from the API. */ void clearScaleRestriction(); /**jsdoc + * Adds a thrust to your avatar's current thrust, to be applied for a short while. * @function MyAvatar.addThrust - * @param {Vec3} thrust + * @param {Vec3} thrust - The thrust direction and magnitude. + * @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead. */ // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; /**jsdoc + * Gets the thrust currently being applied to your avatar. * @function MyAvatar.getThrust - * @returns {vec3} + * @returns {Vec3} The thrust currently being applied to your avatar. + * @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead. */ glm::vec3 getThrust() { return _thrust; }; /**jsdoc + * Sets the thrust to be applied to your avatar for a short while. * @function MyAvatar.setThrust - * @param {Vec3} thrust + * @param {Vec3} thrust - The thrust direction and magnitude. + * @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead. */ void setThrust(glm::vec3 newThrust) { _thrust = newThrust; } /**jsdoc + * Updates avatar motion behavior from the Developer > Avatar > Enable Default Motor Control and Enable Scripted + * Motor Control menu items. * @function MyAvatar.updateMotionBehaviorFromMenu */ Q_INVOKABLE void updateMotionBehaviorFromMenu(); /**jsdoc * @function MyAvatar.setToggleHips - * @param {boolean} enabled + * @param {boolean} enabled - Enabled. + * @deprecated This function is deprecated and will be removed. */ void setToggleHips(bool followHead); /**jsdoc + * Displays base of support of feet debug graphics. * @function MyAvatar.setEnableDebugDrawBaseOfSupport - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawBaseOfSupport(bool isEnabled); /**jsdoc + * Displays default pose debug graphics. * @function MyAvatar.setEnableDebugDrawDefaultPose - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawDefaultPose(bool isEnabled); /**jsdoc + * Displays animation debug graphics. * @function MyAvatar.setEnableDebugDrawAnimPose - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawAnimPose(bool isEnabled); /**jsdoc + * Displays position debug graphics. * @function MyAvatar.setEnableDebugDrawPosition - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawPosition(bool isEnabled); /**jsdoc + * Displays controller hand target debug graphics. * @function MyAvatar.setEnableDebugDrawHandControllers - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawHandControllers(bool isEnabled); /**jsdoc + * Displays sensor-to-world matrix debug graphics. * @function MyAvatar.setEnableDebugDrawSensorToWorldMatrix - * @param {boolean} enabled + * @param {boolean} enable - true to show the debug graphics, false to hide. */ void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); /**jsdoc + * Displays inverse kinematics targets debug graphics. * @function MyAvatar.setEnableDebugDrawIKTargets - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawIKTargets(bool isEnabled); /**jsdoc + * Displays inverse kinematics constraints debug graphics. * @function MyAvatar.setEnableDebugDrawIKConstraints - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawIKConstraints(bool isEnabled); /**jsdoc + * Displays inverse kinematics chains debug graphics. * @function MyAvatar.setEnableDebugDrawIKChains - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawIKChains(bool isEnabled); /**jsdoc + * Displays detailed collision debug graphics. * @function MyAvatar.setEnableDebugDrawDetailedCollision - * @param {boolean} enabled + * @param {boolean} enabled - true to show the debug graphics, false to hide. */ void setEnableDebugDrawDetailedCollision(bool isEnabled); @@ -1570,13 +1793,15 @@ public slots: /**jsdoc * @function MyAvatar.sanitizeAvatarEntityProperties + * @param {EntityItemProperties} properties - Properties. + * @deprecated This function is deprecated and will be removed. */ void sanitizeAvatarEntityProperties(EntityItemProperties& properties) const; /**jsdoc - * Sets whether or not your avatar mesh is visible. + * Sets whether or not your avatar mesh is visible to you. * @function MyAvatar.setEnableMeshVisible - * @param {boolean} visible - true to set your avatar mesh visible; false to set it invisible. + * @param {boolean} enabled - true to show your avatar mesh, false to hide. * @example Make your avatar invisible for 10s. * MyAvatar.setEnableMeshVisible(false); * Script.setTimeout(function () { @@ -1586,57 +1811,76 @@ public slots: virtual void setEnableMeshVisible(bool isEnabled) override; /**jsdoc + * Sets whether or not inverse kinematics (IK) for your avatar. * @function MyAvatar.setEnableInverseKinematics - * @param {boolean} enabled + * @param {boolean} enabled - true to enable IK, false to disable. */ void setEnableInverseKinematics(bool isEnabled); /**jsdoc + * Gets the URL of the override animation graph. + *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for + * information on animation graphs.

* @function MyAvatar.getAnimGraphOverrideUrl - * @returns {string} + * @returns {string} The URL of the override animation graph. "" if there is no override animation graph. */ QUrl getAnimGraphOverrideUrl() const; // thread-safe /**jsdoc + * Sets the animation graph to use in preference to the default animation graph. + *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for + * information on animation graphs.

* @function MyAvatar.setAnimGraphOverrideUrl - * @param {string} url + * @param {string} url - The URL of the animation graph to use. Set to "" to clear an override. */ void setAnimGraphOverrideUrl(QUrl value); // thread-safe /**jsdoc + * Gets the URL of animation graph that's currently being used for avatar animations. + *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for + * information on animation graphs.

* @function MyAvatar.getAnimGraphUrl - * @returns {string} + * @returns {string} The URL of the current animation graph. */ QUrl getAnimGraphUrl() const; // thread-safe /**jsdoc + * Sets the current animation graph to use for avatar animations and makes it the default. + *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for + * information on animation graphs.

* @function MyAvatar.setAnimGraphUrl - * @param {string} url + * @param {string} url - The URL of the animation graph to use. */ void setAnimGraphUrl(const QUrl& url); // thread-safe /**jsdoc + * Gets your listening position for spatialized audio. The position depends on the value of the + * {@link Myavatar|audioListenerMode} property. * @function MyAvatar.getPositionForAudio - * @returns {Vec3} + * @returns {Vec3} Your listening position. */ glm::vec3 getPositionForAudio(); /**jsdoc + * Gets the orientation of your listening position for spatialized audio. The orientation depends on the value of the + * {@link Myavatar|audioListenerMode} property. * @function MyAvatar.getOrientationForAudio - * @returns {Quat} + * @returns {Quat} The orientation of your listening position. */ glm::quat getOrientationForAudio(); /**jsdoc * @function MyAvatar.setModelScale - * @param {number} scale + * @param {number} scale - The scale. + * @deprecated This function is deprecated and will be removed. */ virtual void setModelScale(float scale) override; signals: /**jsdoc + * Triggered when the {@link MyAvatar|audioListenerMode} property value changes. * @function MyAvatar.audioListenerModeChanged * @returns {Signal} */ @@ -1645,12 +1889,14 @@ signals: /**jsdoc * @function MyAvatar.transformChanged * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ void transformChanged(); /**jsdoc + * Triggered when the {@link MyAvatar|collisionSoundURL} property value changes. * @function MyAvatar.newCollisionSoundURL - * @param {string} url + * @param {string} url - The URL of the new collision sound. * @returns {Signal} */ void newCollisionSoundURL(const QUrl& url); @@ -1658,7 +1904,7 @@ signals: /**jsdoc * Triggered when the avatar collides with an entity. * @function MyAvatar.collisionWithEntity - * @param {Collision} collision + * @param {Collision} collision - Details of the collision. * @returns {Signal} * @example Report each time your avatar collides with an entity. * MyAvatar.collisionWithEntity.connect(function (collision) { @@ -1686,21 +1932,23 @@ signals: void otherAvatarsCollisionsEnabledChanged(bool enabled); /**jsdoc - * Triggered when the avatar's animation changes. + * Triggered when the avatar's animation graph URL changes. * @function MyAvatar.animGraphUrlChanged - * @param {url} url - The URL of the new animation. + * @param {string} url - The URL of the new animation graph. * @returns {Signal} */ void animGraphUrlChanged(const QUrl& url); /**jsdoc * @function MyAvatar.energyChanged - * @param {number} energy + * @param {number} energy - Avatar energy. * @returns {Signal} + * @deprecated This signal is deprecated and will be removed. */ void energyChanged(float newEnergy); /**jsdoc + * Triggered when the avatar has been moved to a new position by one of the MyAvatar "goTo" functions. * @function MyAvatar.positionGoneTo * @returns {Signal} */ @@ -1708,57 +1956,78 @@ signals: void positionGoneTo(); /**jsdoc + * Triggered when the avatar's model finishes loading. * @function MyAvatar.onLoadComplete * @returns {Signal} */ void onLoadComplete(); /**jsdoc + * Triggered when your avatar changes from being active to being away. * @function MyAvatar.wentAway * @returns {Signal} + * @example Report when your avatar goes away. + * MyAvatar.wentAway.connect(function () { + * print("My avatar went away"); + * }); + * // In desktop mode, pres the Esc key to go away. */ void wentAway(); /**jsdoc + * Triggered when your avatar changes from being away to being active. * @function MyAvatar.wentActive * @returns {Signal} */ void wentActive(); /**jsdoc + * Triggered when the avatar's model (i.e., {@link MyAvatar|skeletonModelURL} property value) is changed. + *

Synonym of {@link MyAvatar.skeletonModelURLChanged|skeletonModelURLChanged}.

* @function MyAvatar.skeletonChanged * @returns {Signal} */ void skeletonChanged(); /**jsdoc + * Triggered when the avatar's dominant hand changes. * @function MyAvatar.dominantHandChanged - * @param {string} hand + * @param {string} hand - The dominant hand: "left" for the left hand, "right" for the right hand. * @returns {Signal} */ void dominantHandChanged(const QString& hand); /**jsdoc + * Triggered when the HMD alignment for your avatar changes. * @function MyAvatar.hmdAvatarAlignmentTypeChanged - * @param {string} type + * @param {string} type - "head" if aligning your head and your avatar's head, "eyes" if aligning + * your eyes and your avatar's eyes. * @returns {Signal} */ void hmdAvatarAlignmentTypeChanged(const QString& type); /**jsdoc + * Triggered when the avatar's sensorToWorldScale property value changes. * @function MyAvatar.sensorToWorldScaleChanged - * @param {number} scale + * @param {number} scale - The scale that transforms dimensions in the user's real world to the avatar's size in the virtual + * world. * @returns {Signal} */ void sensorToWorldScaleChanged(float sensorToWorldScale); /**jsdoc + * Triggered when the a model is attached to or detached from one of the avatar's joints using one of + * {@link MyAvatar.attach|attach}, {@link MyAvatar.detachOne|detachOne}, {@link MyAvatar.detachAll|detachAll}, or + * {@link MyAvatar.setAttachmentData|setAttachmentData}. * @function MyAvatar.attachmentsChanged * @returns {Signal} + * @deprecated Use avatar entities instead. */ void attachmentsChanged(); /**jsdoc + * Triggered when the avatar's size changes. This can be due to the user changing the size of their avatar or the domain + * limiting the size of their avatar. * @function MyAvatar.scaleChanged * @returns {Signal} */ diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 11940ad76a..aedfcedf89 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -201,16 +201,24 @@ public: virtual QStringList getJointNames() const override; /**jsdoc + * Gets the default rotation of a joint (in the current avatar) relative to its parent. + *

For information on the joint hierarchy used, see + * Avatar Standards.

* @function MyAvatar.getDefaultJointRotation - * @param {number} index - * @returns {Quat} + * @param {number} index - The joint index. + * @returns {Quat} The default rotation of the joint if the joint index is valid, otherwise {@link Quat(0)|Quat.IDENTITY}. */ Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const; /**jsdoc + * Gets the default translation of a joint (in the current avatar) relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

* @function MyAvatar.getDefaultJointTranslation - * @param {number} index - * @returns {Vec3} + * @param {number} index - The joint index. + * @returns {Vec3} The default translation of the joint (in model coordinates) if the joint index is valid, otherwise + * {@link Vec3(0)|Vec3.ZERO}. */ Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const; @@ -261,50 +269,62 @@ public: // world-space to avatar-space rigconversion functions /**jsdoc + * Transforms a position in world coordinates to a position in a joint's coordinates, or avatar coordinates if no joint is + * specified. * @function MyAvatar.worldToJointPoint - * @param {Vec3} position - * @param {number} [jointIndex=-1] - * @returns {Vec3} + * @param {Vec3} position - The position in world coordinates. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Vec3} The position in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const; /**jsdoc + * Transforms a direction in world coordinates to a direction in a joint's coordinates, or avatar coordinates if no joint + * is specified. * @function MyAvatar.worldToJointDirection - * @param {Vec3} direction - * @param {number} [jointIndex=-1] - * @returns {Vec3} + * @param {Vec3} direction - The direction in world coordinates. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Vec3} The direction in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const; /**jsdoc + * Transforms a rotation in world coordinates to a rotation in a joint's coordinates, or avatar coordinates if no joint is + * specified. * @function MyAvatar.worldToJointRotation - * @param {Quat} rotation - * @param {number} [jointIndex=-1] - * @returns {Quat} + * @param {Quat} rotation - The rotation in world coordinates. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Quat} The rotation in the joint's coordinate system, or avatar coordinate system if no joint is specified. */ Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const; /**jsdoc + * Transforms a position in a joint's coordinates, or avatar coordinates if no joint is specified, to a position in world + * coordinates. * @function MyAvatar.jointToWorldPoint - * @param {vec3} position - * @param {number} [jointIndex=-1] - * @returns {Vec3} + * @param {Vec3} position - The position in joint coordinates, or avatar coordinates if no joint is specified. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Vec3} The position in world coordinates. */ Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const; /**jsdoc + * Transforms a direction in a joint's coordinates, or avatar coordinates if no joint is specified, to a direction in world + * coordinates. * @function MyAvatar.jointToWorldDirection - * @param {Vec3} direction - * @param {number} [jointIndex=-1] - * @returns {Vec3} + * @param {Vec3} direction - The direction in joint coordinates, or avatar coordinates if no joint is specified. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Vec3} The direction in world coordinates. */ Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const; /**jsdoc + * Transforms a rotation in a joint's coordinates, or avatar coordinates if no joint is specified, to a rotation in world + * coordinates. * @function MyAvatar.jointToWorldRotation - * @param {Quat} rotation - * @param {number} [jointIndex=-1] - * @returns {Quat} + * @param {Quat} rotation - The rotation in joint coordinates, or avatar coordinates if no joint is specified. + * @param {number} [jointIndex=-1] - The index of the joint. + * @returns {Quat} The rotation in world coordinates. */ Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const; @@ -375,8 +395,9 @@ public: Q_INVOKABLE glm::vec3 getNeckPosition() const; /**jsdoc + * Gets the current acceleration of the avatar. * @function MyAvatar.getAcceleration - * @returns {Vec3} + * @returns {Vec3} The current acceleration of the avatar. */ Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } @@ -410,29 +431,37 @@ public: void setOrientationViaScript(const glm::quat& orientation) override; /**jsdoc + * Gets the ID of the entity of avatar that the avatar is parented to. * @function MyAvatar.getParentID - * @returns {Uuid} + * @returns {Uuid} The ID of the entity or avatar that the avatar is parented to. {@link Uuid|Uuid.NULL} if not parented. */ // This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript. Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } /**jsdoc + * Sets the ID of the entity of avatar that the avatar is parented to. * @function MyAvatar.setParentID - * @param {Uuid} parentID + * @param {Uuid} parentID - The ID of the entity or avatar that the avatar should be parented to. Set to + * {@link Uuid|Uuid.NULL} to unparent. */ // This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript. Q_INVOKABLE virtual void setParentID(const QUuid& parentID) override; /**jsdoc + * Gets the joint of the entity or avatar that the avatar is parented to. * @function MyAvatar.getParentJointIndex - * @returns {number} + * @returns {number} The joint of the entity or avatar that the avatar is parented to. 65535 or + * -1 if parented to the entity or avatar's position and orientation rather than a joint. */ // This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript. Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); } /**jsdoc + * sets the joint of the entity or avatar that the avatar is parented to. * @function MyAvatar.setParentJointIndex - * @param {number} parentJointIndex + * @param {number} parentJointIndex - he joint of the entity or avatar that the avatar should be parented to. Use + * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a + * joint. */ // This calls through to the SpatiallyNestable versions, but is here to expose these to JavaScript. Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override; @@ -466,8 +495,9 @@ public: /**jsdoc * @function MyAvatar.getSimulationRate - * @param {string} [rateName=""] - * @returns {number} + * @param {string} [rateName=""] - Rate name. + * @returns {number} Simulation rate. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const; @@ -522,8 +552,10 @@ public: signals: /**jsdoc + * Triggered when the avatar's target scale is changed. The target scale is the desired scale of the avatar without any + * restrictions on permissible scale values imposed by the domain. * @function MyAvatar.targetScaleChanged - * @param {number} targetScale + * @param {number} targetScale - The avatar's target scale. * @returns Signal */ void targetScaleChanged(float targetScale); @@ -572,6 +604,7 @@ public slots: /**jsdoc * @function MyAvatar.setModelURLFinished * @param {boolean} success + * @deprecated This function is deprecated and will be removed. */ // hooked up to Model::setURLFinished signal void setModelURLFinished(bool success); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 614858a77d..79085e05ba 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -1069,13 +1069,14 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { } /**jsdoc + * Details of a collision between avatars and entities. * @typedef {object} Collision * @property {ContactEventType} type - The contact type of the collision event. - * @property {Uuid} idA - The ID of one of the entities in the collision. - * @property {Uuid} idB - The ID of the other of the entities in the collision. - * @property {Vec3} penetration - The amount of penetration between the two entities. + * @property {Uuid} idA - The ID of one of the avatars or entities in the collision. + * @property {Uuid} idB - The ID of the other of the avatars or entities in the collision. + * @property {Vec3} penetration - The amount of penetration between the two items. * @property {Vec3} contactPoint - The point of contact. - * @property {Vec3} velocityChange - The change in relative velocity of the two entities, in m/s. + * @property {Vec3} velocityChange - The change in relative velocity of the two items, in m/s. */ QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) { QScriptValue obj = engine->newObject(); From eba89e8a80ff0d49a3ef3350b079ee486ec42f69 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 8 Mar 2019 13:50:03 +1300 Subject: [PATCH 11/88] Revise Avatar JSDoc as a result of MyAvatar JSDoc work --- .../src/avatars/ScriptableAvatar.h | 4 +- libraries/avatars/src/AvatarData.h | 84 +++++++++++-------- 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 2d8dce23de..37e82947ae 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -149,7 +149,7 @@ public: * Gets the avatar entities as binary data. *

Warning: Potentially a very expensive call. Do not use if possible.

* @function Avatar.getAvatarEntityData - * @returns {AvatarEntityMap} + * @returns {AvatarEntityMap} The avatar entities as binary data. */ Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; @@ -157,7 +157,7 @@ public: * Sets the avatar entities from binary data. *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.setAvatarEntityData - * @param {AvatarEntityMap} avatarEntityData + * @param {AvatarEntityMap} avatarEntityData - The avatar entities as binary data. */ Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a20518076f..858af7ba3c 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -131,7 +131,7 @@ const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit * * *

The values for the hand states are added together to give the HandState value. For example, if the left - * hand's finger is pointing, the value is 1 + 4 == 5. + * hand's finger is pointing, the value is 1 + 4 == 5. * @typedef {number} HandState */ const char HAND_STATE_NULL = 0; @@ -705,8 +705,9 @@ public: Q_INVOKABLE void setRawJointData(QVector data); /**jsdoc - * Sets a specific joint's rotation and position relative to its parent. - *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse + * Sets a specific joint's rotation and position relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set @@ -714,7 +715,7 @@ public: * @function Avatar.setJointData * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. - * @param {Vec3} translation - The translation of the joint relative to its parent. + * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Set your avatar to it's default T-pose for a while.
* Avatar in T-pose * // Set all joint translations and rotations to defaults. @@ -748,15 +749,16 @@ public: Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); /**jsdoc - * Sets a specific joint's translation relative to its parent. - *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse + * Sets a specific joint's translation relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslation * @param {number} index - The index of the joint. - * @param {Vec3} translation - The translation of the joint relative to its parent. + * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation); @@ -773,7 +775,7 @@ public: * Checks that the data for a joint are valid. * @function Avatar.isJointDataValid * @param {number} index - The index of the joint. - * @returns {boolean} true if the joint data is valid, false if not. + * @returns {boolean} true if the joint data are valid, false if not. */ Q_INVOKABLE bool isJointDataValid(int index) const; @@ -787,17 +789,20 @@ public: Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; /**jsdoc - * Gets the translation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Gets the translation of a joint relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} index - The index of the joint. - * @returns {Vec3} The translation of the joint relative to its parent. + * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; /**jsdoc - * Sets a specific joint's rotation and position relative to its parent. - *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse + * Sets a specific joint's rotation and position relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set @@ -805,7 +810,7 @@ public: * @function Avatar.setJointData * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. - * @param {Vec3} translation - The translation of the joint relative to its parent. + * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); @@ -843,20 +848,21 @@ public: Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation); /**jsdoc - * Sets a specific joint's translation relative to its parent. - *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse + * Sets a specific joint's translation relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslation * @param {string} name - The name of the joint. - * @param {Vec3} translation - The translation of the joint relative to its parent. + * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between * the head and body or you will see the neck stretched.
* Avatar with neck stretched * // Stretch your avatar's neck. - * MyAvatar.setJointTranslation("Neck", { x: 0, y: 25, z: 0 }); + * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * * // Restore your avatar's neck after 5s. * Script.setTimeout(function () { @@ -874,10 +880,10 @@ public: * @function Avatar.clearJointData * @param {string} name - The name of the joint. * @example Offset and restore the position of your avatar's head. - * // Move your avatar's head up by 25cm from where it should be. - * MyAvatar.setJointTranslation("Neck", { x: 0, y: 0.25, z: 0 }); + * // Stretch your avatar's neck. + * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * - * // Restore your avatar's head to its default position after 5s. + * // Restore your avatar's neck after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointData("Neck"); * }, 5000); @@ -890,7 +896,7 @@ public: * Checks that the data for a joint are valid. * @function Avatar.isJointDataValid * @param {string} name - The name of the joint. - * @returns {boolean} true if the joint data is valid, false if not. + * @returns {boolean} true if the joint data are valid, false if not. */ Q_INVOKABLE virtual bool isJointDataValid(const QString& name) const; @@ -908,11 +914,13 @@ public: Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const; /**jsdoc - * Gets the translation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Gets the translation of a joint relative to its parent, in model coordinates. + *

Warning: These coordinates are not necessarily in meters.

+ *

For information on the joint hierarchy used, see + * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} name - The name of the joint. - * @returns {Vec3} The translation of the joint relative to its parent. + * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. * @example Report the translation of your avatar's hips joint. * print(JSON.stringify(MyAvatar.getJointRotation("Hips"))); * @@ -933,10 +941,13 @@ public: Q_INVOKABLE virtual QVector getJointRotations() const; /**jsdoc - * Gets the translations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. + * Gets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in + * model coordinates. + *

Warning: These coordinates are not necessarily in meters.

* @function Avatar.getJointTranslations - * @returns {Vec3[]} The translations of all joints relative to each's parent. The values are in the same order as the array - * returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. + * @returns {Vec3[]} The translations of all joints relative to each's parent, in model coordinates. The values are in the + * same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the + * Avatar API. */ Q_INVOKABLE virtual QVector getJointTranslations() const; @@ -979,15 +990,18 @@ public: Q_INVOKABLE virtual void setJointRotations(const QVector& jointRotations); /**jsdoc - * Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint. + * Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in + * model coordinates. + *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslations - * @param {Vec3[]} translations - The translations for all joints in the avatar. The values are in the same order as the - * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. + * @param {Vec3[]} translations - The translations for all joints in the avatar, in model coordinates. The values are in + * the same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the + * Avatar API. */ Q_INVOKABLE virtual void setJointTranslations(const QVector& jointTranslations); @@ -1271,12 +1285,12 @@ public: AABox getDefaultBubbleBox() const; /**jsdoc - * @comment Documented in derived classes' JSDoc. + * @comment Documented in derived classes' JSDoc because implementations are different. */ Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; /**jsdoc - * @comment Documented in derived classes' JSDoc. + * @comment Documented in derived classes' JSDoc because implementations are different. */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); @@ -1383,14 +1397,14 @@ signals: void displayNameChanged(); /**jsdoc - * Triggered when the avattr's sessionDisplayName property value changes. + * Triggered when the avatar's sessionDisplayName property value changes. * @function Avatar.sessionDisplayNameChanged * @returns {Signal} */ void sessionDisplayNameChanged(); /**jsdoc - * Triggered when the avatar's skeletonModelURL property value changes. + * Triggered when the avatar's model (i.e., skeletonModelURL property value) is changed. * @function Avatar.skeletonModelURLChanged * @returns {Signal} */ From c8648c70161d00e5bb0e2e150bfd467295834046 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 08:53:48 -0800 Subject: [PATCH 12/88] Added worst tile value to test. --- tools/nitpick/src/ImageComparer.cpp | 11 +++++++++++ tools/nitpick/src/ImageComparer.h | 2 ++ tools/nitpick/src/MismatchWindow.cpp | 2 +- tools/nitpick/src/Nitpick.cpp | 2 +- tools/nitpick/src/TestCreator.cpp | 14 +++++++++----- tools/nitpick/src/TestCreator.h | 3 ++- tools/nitpick/src/common.h | 10 +++++++--- tools/nitpick/ui/MismatchWindow.ui | 6 +++--- 8 files changed, 36 insertions(+), 14 deletions(-) diff --git a/tools/nitpick/src/ImageComparer.cpp b/tools/nitpick/src/ImageComparer.cpp index 7e3e6eaf63..b35c5d639d 100644 --- a/tools/nitpick/src/ImageComparer.cpp +++ b/tools/nitpick/src/ImageComparer.cpp @@ -43,6 +43,8 @@ void ImageComparer::compareImages(const QImage& resultImage, const QImage& expec int windowCounter{ 0 }; double ssim{ 0.0 }; + double worstTileValue{ 1.0 }; + double min { 1.0 }; double max { -1.0 }; @@ -108,6 +110,10 @@ void ImageComparer::compareImages(const QImage& resultImage, const QImage& expec if (value < min) min = value; if (value > max) max = value; + if (value < worstTileValue) { + worstTileValue = value; + } + ++windowCounter; y += WIN_SIZE; @@ -122,12 +128,17 @@ void ImageComparer::compareImages(const QImage& resultImage, const QImage& expec _ssimResults.min = min; _ssimResults.max = max; _ssimResults.ssim = ssim / windowCounter; + _ssimResults.worstTileValue = worstTileValue; }; double ImageComparer::getSSIMValue() { return _ssimResults.ssim; } +double ImageComparer::getWorstTileValue() { + return _ssimResults.worstTileValue; +} + SSIMResults ImageComparer::getSSIMResults() { return _ssimResults; } diff --git a/tools/nitpick/src/ImageComparer.h b/tools/nitpick/src/ImageComparer.h index fc14dab94d..a18e432a01 100644 --- a/tools/nitpick/src/ImageComparer.h +++ b/tools/nitpick/src/ImageComparer.h @@ -18,7 +18,9 @@ class ImageComparer { public: void compareImages(const QImage& resultImage, const QImage& expectedImage); + double getSSIMValue(); + double getWorstTileValue(); SSIMResults getSSIMResults(); diff --git a/tools/nitpick/src/MismatchWindow.cpp b/tools/nitpick/src/MismatchWindow.cpp index fd5df0dd4e..2a7aca9f2e 100644 --- a/tools/nitpick/src/MismatchWindow.cpp +++ b/tools/nitpick/src/MismatchWindow.cpp @@ -61,7 +61,7 @@ QPixmap MismatchWindow::computeDiffPixmap(const QImage& expectedImage, const QIm } void MismatchWindow::setTestResult(const TestResult& testResult) { - errorLabel->setText("Similarity: " + QString::number(testResult._error)); + errorLabel->setText("Similarity: " + QString::number(testResult._errorGlobal) + " (worst tile: " + QString::number(testResult._errorLocal) + ")"); imagePath->setText("Path to test: " + testResult._pathname); diff --git a/tools/nitpick/src/Nitpick.cpp b/tools/nitpick/src/Nitpick.cpp index cf50774617..e72de9d1ad 100644 --- a/tools/nitpick/src/Nitpick.cpp +++ b/tools/nitpick/src/Nitpick.cpp @@ -38,7 +38,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) { _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Nitpick - v3.1.2"); + setWindowTitle("Nitpick - v3.1.3"); clientProfiles << "VR-High" << "Desktop-High" << "Desktop-Low" << "Mobile-Touch" << "VR-Standalone"; _ui.clientProfileComboBox->insertItems(0, clientProfiles); diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index 089e84904a..a79a2b3b09 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -83,6 +83,7 @@ int TestCreator::compareImageLists() { QImage expectedImage(_expectedImagesFullFilenames[i]); double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical + double worstTileValue; // in [-1.0 .. 1.0], where 1.0 means images are identical bool isInteractiveMode = (!_isRunningFromCommandLine && _checkBoxInteractiveMode->isChecked() && !_isRunningInAutomaticTestRun); @@ -93,10 +94,12 @@ int TestCreator::compareImageLists() { } else { _imageComparer.compareImages(resultImage, expectedImage); similarityIndex = _imageComparer.getSSIMValue(); + worstTileValue = _imageComparer.getWorstTileValue(); } TestResult testResult = TestResult{ - (float)similarityIndex, + similarityIndex, + worstTileValue, _expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /) QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of result image @@ -105,10 +108,9 @@ int TestCreator::compareImageLists() { _mismatchWindow.setTestResult(testResult); - if (similarityIndex < THRESHOLD) { - ++numberOfFailures; - + if (similarityIndex < THRESHOLD_GLOBAL || worstTileValue < THRESHOLD_LOCAL) { if (!isInteractiveMode) { + ++numberOfFailures; appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), _mismatchWindow.getSSIMResultsImage(testResult._ssimResults), true); } else { _mismatchWindow.exec(); @@ -117,6 +119,7 @@ int TestCreator::compareImageLists() { case USER_RESPONSE_PASS: break; case USE_RESPONSE_FAIL: + ++numberOfFailures; appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), _mismatchWindow.getSSIMResultsImage(testResult._ssimResults), true); break; case USER_RESPONSE_ABORT: @@ -198,7 +201,8 @@ void TestCreator::appendTestResultsToFile(const TestResult& testResult, const QP stream << "TestCreator in folder " << testResult._pathname.left(testResult._pathname.length() - 1) << endl; // remove trailing '/' stream << "Expected image was " << testResult._expectedImageFilename << endl; stream << "Actual image was " << testResult._actualImageFilename << endl; - stream << "Similarity index was " << testResult._error << endl; + stream << "Similarity index was " << testResult._errorGlobal << endl; + stream << "Worst tile was " << testResult._errorLocal << endl; descriptionFile.close(); diff --git a/tools/nitpick/src/TestCreator.h b/tools/nitpick/src/TestCreator.h index 7cd38b42d4..6491d6fe6c 100644 --- a/tools/nitpick/src/TestCreator.h +++ b/tools/nitpick/src/TestCreator.h @@ -121,7 +121,8 @@ private: const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD{ 0.9999 }; + const double THRESHOLD_GLOBAL{ 0.9999 }; + const double THRESHOLD_LOCAL { 0.7770 }; QDir _imageDirectory; diff --git a/tools/nitpick/src/common.h b/tools/nitpick/src/common.h index eb228ff2b3..17090c46db 100644 --- a/tools/nitpick/src/common.h +++ b/tools/nitpick/src/common.h @@ -18,7 +18,9 @@ public: int width; int height; std::vector results; + double ssim; + double worstTileValue; // Used for scaling double min; @@ -27,15 +29,17 @@ public: class TestResult { public: - TestResult(float error, const QString& pathname, const QString& expectedImageFilename, const QString& actualImageFilename, const SSIMResults& ssimResults) : - _error(error), + TestResult(double errorGlobal, double errorLocal, const QString& pathname, const QString& expectedImageFilename, const QString& actualImageFilename, const SSIMResults& ssimResults) : + _errorGlobal(errorGlobal), + _errorLocal(errorLocal), _pathname(pathname), _expectedImageFilename(expectedImageFilename), _actualImageFilename(actualImageFilename), _ssimResults(ssimResults) {} - double _error; + double _errorGlobal; + double _errorLocal; QString _pathname; QString _expectedImageFilename; diff --git a/tools/nitpick/ui/MismatchWindow.ui b/tools/nitpick/ui/MismatchWindow.ui index 8a174989d4..fa3e21957f 100644 --- a/tools/nitpick/ui/MismatchWindow.ui +++ b/tools/nitpick/ui/MismatchWindow.ui @@ -45,7 +45,7 @@ - 540 + 900 480 800 450 @@ -78,7 +78,7 @@ 60 630 - 480 + 540 28 @@ -145,7 +145,7 @@ - Abort current test + Abort evaluation From 20e1753605afc1d6dcafd8e1aa306f4bf663c869 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 09:22:52 -0800 Subject: [PATCH 13/88] Reduced threshold a wee bit after testing on laptop. --- tools/nitpick/src/TestCreator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/src/TestCreator.h b/tools/nitpick/src/TestCreator.h index 6491d6fe6c..c2e7ba14f2 100644 --- a/tools/nitpick/src/TestCreator.h +++ b/tools/nitpick/src/TestCreator.h @@ -121,7 +121,7 @@ private: const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD_GLOBAL{ 0.9999 }; + const double THRESHOLD_GLOBAL{ 0.9998 }; const double THRESHOLD_LOCAL { 0.7770 }; QDir _imageDirectory; From 19c7c26c6369b5df000f68e794c2e5f5834427b6 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 10:04:23 -0800 Subject: [PATCH 14/88] gcc / Mac compilation error. --- tools/nitpick/src/TestCreator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index a79a2b3b09..f87134ce5b 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -91,6 +91,7 @@ int TestCreator::compareImageLists() { if (isInteractiveMode && (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height())) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Images are not the same size"); similarityIndex = -100.0; + worstTileValue = 0.0; } else { _imageComparer.compareImages(resultImage, expectedImage); similarityIndex = _imageComparer.getSSIMValue(); From 5068075645c8c1511d2cc99c94c78dec68f0f325 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 9 Mar 2019 07:42:55 +1300 Subject: [PATCH 15/88] Fill in MyAvatar animation JSDoc --- .../animation/src/AnimInverseKinematics.h | 41 ++++ libraries/animation/src/AnimOverlay.h | 27 +++ libraries/animation/src/IKTarget.h | 21 ++ libraries/animation/src/Rig.cpp | 211 ++++++++++++++++++ libraries/script-engine/src/ScriptEngine.h | 11 +- 5 files changed, 306 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 0136b7d125..d5a110ea76 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -59,6 +59,47 @@ public: float getMaxErrorOnLastSolve() { return _maxErrorOnLastSolve; } + /**jsdoc + *

Specifies the initial conditions of the IK solver.

+ * + * + * + * + * + * + * + * + * + * + * + *
ValueName

Description
0RelaxToUnderPosesThis is a blend between PreviousSolution and + * UnderPoses: it is 15/16 PreviousSolution and 1/16 UnderPoses. This + * provides some of the benefits of using UnderPoses so that the underlying animation is still visible, + * while at the same time converging faster then using the UnderPoses only initial solution.
1RelaxToLimitCenterPosesThis is a blend between + * Previous Solution and LimitCenterPoses: it is 15/16 PreviousSolution and + * 1/16 LimitCenterPoses. This should converge quickly because it is close to the previous solution, but + * still provides the benefits of avoiding limb locking.
2PreviousSolutionThe IK system will begin to solve from the same position and + * orientations for each joint that was the result from the previous frame.
+ * Pros: because the end effectors typically do not move much from frame to frame, this is likely to converge quickly + * to a valid solution.
+ * Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be + * awkward and uncomfortable. It can also result in locked elbows and knees.
3UnderPosesThe IK occurs at one of the top-most layers, it has access to the + * full posture that was computed via canned animations and blends. We call this animated set of poses the "under + * pose". The under poses are what would be visible if IK was completely disabled. Using the under poses as the + * initial conditions of the CCD solve will cause some of the animated motion to be blended in to the result of the + * IK. This can result in very natural results, especially if there are only a few IK targets enabled. On the other + * hand, because the under poses might be quite far from the desired end effector, it can converge slowly in some + * cases, causing it to never reach the IK target in the allotted number of iterations. Also, in situations where all + * of the IK targets are being controlled by external sensors, sometimes starting from the under poses can cause + * awkward motions from the underlying animations to leak into the IK result.
4LimitCenterPosesThis pose is taken to be the center of all the joint + * constraints. This can prevent the IK solution from getting locked or stuck at a particular constraint. For + * example, if the arm is pointing straight outward from the body, as the end effector moves towards the body, at + * some point the elbow should bend to accommodate. However, because the CCD solver is stuck at a local maximum, it + * will not rotate the elbow, unless the initial conditions already has the elbow bent, which is the case for + * LimitCenterPoses. When all the IK targets are enabled, this result will provide a consistent starting + * point for each IK solve, hopefully resulting in a consistent, natural result.
+ * @typedef {number} MyAvatar.AnimIKSolutionSource + */ enum class SolutionSource { RelaxToUnderPoses = 0, RelaxToLimitCenterPoses, diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 70929bd4e4..623672143e 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -24,6 +24,33 @@ class AnimOverlay : public AnimNode { public: friend class AnimTests; + /**jsdoc + *

Specifies sets of joints.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueName

Description
0FullBodyBoneSetAll joints.
1UpperBodyBoneSetOnly the "Spine" joint and its children.
2LowerBodyBoneSetOnly the leg joints and their children.
3LeftArmBoneSetJoints that are children of the "LeftShoulder" joint.
4RightArmBoneSetJoints that are children of the "RightShoulder" + * joint.
5AboveTheHeadBoneSetJoints that are children of the "Head" joint.
6BelowTheHeadBoneSetJoints that are NOT children of the "head" + * joint.
7HeadOnlyBoneSetThe "Head" joint.
8SpineOnlyBoneSetThe "Spine" joint.
9EmptyBoneSetNo joints.
10LeftHandBoneSetjoints that are children of the "LeftHand" joint.
11RightHandBoneSetJoints that are children of the "RightHand" joint.
12HipsOnlyBoneSetThe "Hips" joint.
13BothFeetBoneSetThe "LeftFoot" and "RightFoot" joints.
+ * @typedef {number} MyAvatar.AnimOverlayBoneSet + */ enum BoneSet { FullBodyBoneSet = 0, UpperBodyBoneSet, diff --git a/libraries/animation/src/IKTarget.h b/libraries/animation/src/IKTarget.h index 325a1b40b6..7e53e6a7ea 100644 --- a/libraries/animation/src/IKTarget.h +++ b/libraries/animation/src/IKTarget.h @@ -16,6 +16,27 @@ const float HACK_HMD_TARGET_WEIGHT = 8.0f; class IKTarget { public: + /**jsdoc + *

An IK target type.

+ * + * + * + * + * + * + * + * + * + * + * + * + *
ValueName

Description
0RotationAndPositionAttempt to reach the rotation and position end + * effector.
1RotationOnlyAttempt to reach the end effector rotation only.
2HmdHeadDeprecated: A special mode of IK that would attempt + * to prevent unnecessary bending of the spine.
3HipsRelativeRotationAndPositionAttempt to reach a rotation and position end + * effector that is not in absolute rig coordinates but is offset by the avatar hips translation.
4SplineUse a cubic Hermite spline to model the human spine. This prevents + * kinks in the spine and allows for a small amount of stretch and squash.
5UnknownIK is disabled.
+ * @typedef {number} MyAvatar.IKTargetType + */ enum class Type { RotationAndPosition, RotationOnly, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a9c57a4a15..1ab680fba2 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -75,6 +75,217 @@ static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRig static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition"); +/**jsdoc + *

An AnimStateDictionary object may have the following properties. It may also have other properties, set by + * scripts.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameType

Description
userAnimNonebooleantrue when no user overrideAnimation is + * playing.
userAnimAbooleantrue when a user overrideAnimation is + * playing.
userAnimBbooleantrue when a user overrideAnimation is + * playing.
sinenumberOscillating sine wave.
moveForwardSpeednumberControls the blend between the various forward walking + * & running animations.
moveBackwardSpeednumberControls the blend between the various backward walking + * & running animations.
moveLateralSpeednumberControls the blend between the various sidestep walking + * & running animations.
isMovingForwardbooleantrue if the avatar is moving + * forward.
isMovingBackwardbooleantrue if the avatar is moving + * backward.
isMovingRightbooleantrue if the avatar is moving to the + * right.
isMovingLeftbooleantrue if the avatar is moving to the + * left.
isMovingRightHmdbooleantrue if the avatar is moving to the right + * while the user is in HMD mode.
isMovingLeftHmdbooleantrue if the avatar is moving to the left while + * the user is in HMD mode.
isNotMovingbooleantrue if the avatar is stationary.
isTurningRightbooleantrue if the avatar is turning + * clockwise.
isTurningLeftbooleantrue if the avatar is turning + * counter-clockwise.
isNotTurningbooleantrue if the avatar is not turning.
isFlyingbooleantrue if the avatar is flying.
isNotFlyingbooleantrue if the avatar is not flying.
isTakeoffStandbooleantrue if the avatar is about to execute a + * standing jump.
isTakeoffRunbooleantrue if the avatar is about to execute a running + * jump.
isNotTakeoffbooleantrue if the avatar is not jumping.
isInAirStandbooleantrue if the avatar is in the air after a standing + * jump.
isInAirRunbooleantrue if the avatar is in the air after a running + * jump.
isNotInAirbooleantrue if the avatar on the ground.
inAirAlphanumberUsed to interpolate between the up, apex, and down in-air + * animations.
ikOverlayAlphanumberThe blend between upper body and spline IK versus the + * underlying animation
headPosition{@link Vec3}The desired position of the Head joint in + * rig coordinates.
headRotation{@link Quat}The desired orientation of the Head joint in + * rig coordinates.
headType{@link MyAvatar.IKTargetType|IKTargetType}The type of IK used for the + * head.
headWeightnumberHow strongly the head chain blends with the other IK + * chains.
leftHandPosition{@link Vec3}The desired position of the LeftHand + * joint in rig coordinates.
leftHandRotation{@link Quat}The desired orientation of the LeftHand + * joint in rig coordinates.
leftHandType{@link MyAvatar.IKTargetType|IKTargetType}The type of IK used for the + * left arm.
leftHandPoleVectorEnabledbooleanWhen true, the elbow angle is + * controlled by the rightHandPoleVector property value. Otherwise the elbow direction comes from the + * underlying animation.
leftHandPoleReferenceVector{@link Vec3}The direction of the elbow in the local + * coordinate system of the elbow.
leftHandPoleVector{@link Vec3}The direction the elbow should point in rig + * coordinates.
rightHandPosition{@link Vec3}The desired position of the RightHand + * joint in rig coordinates.
rightHandRotation{@link Quat}The desired orientation of the + * RightHand joint in rig coordinates.
rightHandType{@link MyAvatar.IKTargetType|IKTargetType}The type of IK used for + * the right arm.
rightHandPoleVectorEnabledbooleanWhen true, the elbow angle is + * controlled by the rightHandPoleVector property value. Otherwise the elbow direction comes from the + * underlying animation.
rightHandPoleReferenceVector{@link Vec3}The direction of the elbow in the local + * coordinate system of the elbow.
rightHandPoleVector{@link Vec3}The direction the elbow should point in rig + * coordinates.
leftFootIKEnabledbooleantrue if IK is enabled for the left + * foot.
rightFootIKEnabledbooleantrue if IK is enabled for the right + * foot.
leftFootIKPositionVarstringThe name of the source for the desired position + * of the LeftFoot joint. If not set, the foot rotation of the underlying animation will be used.
leftFootIKRotationVarstringThe name of the source for the desired rotation + * of the LeftFoot joint. If not set, the foot rotation of the underlying animation will be used.
leftFootPoleVectorEnabledbooleanWhen true, the knee angle is + * controlled by the leftFootPoleVector property value. Otherwise the knee direction comes from the + * underlying animation.
leftFootPoleVector{@link Vec3}The direction the knee should face in rig + * coordinates.
rightFootIKPositionVarstringThe name of the source for the desired position + * of the RightFoot joint. If not set, the foot rotation of the underlying animation will be used.
rightFootIKRotationVarstringThe name of the source for the desired rotation + * of the RightFoot joint. If not set, the foot rotation of the underlying animation will be used.
rightFootPoleVectorEnabledbooleanWhen true, the knee angle is + * controlled by the rightFootPoleVector property value. Otherwise the knee direction comes from the + * underlying animation.
rightFootPoleVector{@link Vec3}The direction the knee should face in rig + * coordinates.
isTalkingbooleantrue if the avatar is talking.
notIsTalkingbooleantrue if the avatar is not talking.
solutionSource{@link MyAvatar.AnimIKSolutionSource|AnimIKSolutionSource}Determines the initial conditions of the IK solver.
defaultPoseOverlayAlphanumberControls the blend between the main animation state + * machine and the default pose. Mostly used during full body tracking so that walking & jumping animations do not + * affect the IK of the figure.
defaultPoseOverlayBoneSet{@link MyAvatar.AnimOverlayBoneSet|AnimOverlayBoneSet}Specifies which bones will be replace by the source overlay.
hipsType{@link MyAvatar.IKTargetType|IKTargetType}The type of IK used for the + * hips.
hipsPosition{@link Vec3}The desired position of Hips joint in rig + * coordinates.
hipsRotation{@link Quat}the desired orientation of the Hips joint in + * rig coordinates.
spine2Type{@link MyAvatar.IKTargetType|IKTargetType}The type of IK used for the + * Spine2 joint.
spine2Position{@link Vec3}The desired position of the Spine2 joint + * in rig coordinates.
spine2Rotation{@link Quat}The desired orientation of the Spine2 + * joint in rig coordinates.
leftFootIKAlphanumberBlends between full IK for the leg and the underlying + * animation.
rightFootIKAlphanumberBlends between full IK for the leg and the underlying + * animation.
hipsWeightnumberHow strongly the hips target blends with the IK solution for + * other IK chains.
leftHandWeightnumberHow strongly the left hand blends with IK solution of other + * IK chains.
rightHandWeightnumberHow strongly the right hand blends with IK solution of other + * IK chains.
spine2WeightnumberHow strongly the spine2 chain blends with the rest of the IK + * solution.
leftHandOverlayAlphanumberUsed to blend in the animated hand gesture poses, such + * as point and thumbs up.
leftHandGraspAlphanumberUsed to blend between an open hand and a closed hand. + * Usually changed as you squeeze the trigger of the hand controller.
rightHandOverlayAlphanumberUsed to blend in the animated hand gesture poses, + * such as point and thumbs up.
rightHandGraspAlphanumberUsed to blend between an open hand and a closed hand. + * Usually changed as you squeeze the trigger of the hand controller.
isLeftIndexPointbooleantrue if the left hand should be + * pointing.
isLeftThumbRaisebooleantrue if the left hand should be + * thumbs-up.
isLeftIndexPointAndThumbRaisebooleantrue if the left hand should be + * pointing and thumbs-up.
isLeftHandGraspbooleantrue if the left hand should be at rest, + * grasping the controller.
isRightIndexPointbooleantrue if the right hand should be + * pointing.
isRightThumbRaisebooleantrue if the right hand should be + * thumbs-up.
isRightIndexPointAndThumbRaisebooleantrue if the right hand should + * be pointing and thumbs-up.
isRightHandGraspbooleantrue if the right hand should be at rest, + * grasping the controller.
+ *

Note: Rig coordinates are +z forward and +y up.

+ * @typedef {object} MyAvatar.AnimStateDictionary + */ +// Note: The following animVars are intentionally not documented: +// - leftFootPosition +// - leftFootRotation +// - rightFooKPosition +// - rightFooKRotation +// Note: The following items aren't set in the code below but are still intentionally documented: +// - leftFootIKAlpha +// - rightFootIKAlpha +// - hipsWeight +// - leftHandWeight +// - rightHandWeight +// - spine2Weight +// - rightHandOverlayAlpha +// - rightHandGraspAlpha +// - leftHandOverlayAlpha +// - leftHandGraspAlpha +// - isRightIndexPoint +// - isRightThumbRaise +// - isRightIndexPointAndThumbRaise +// - isRightHandGrasp +// - isLeftIndexPoint +// - isLeftThumbRaise +// - isLeftIndexPointAndThumbRaise +// - isLeftHandGrasp Rig::Rig() { // Ensure thread-safe access to the rigRegistry. std::lock_guard guard(rigRegistryMutex); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 48fb4f0b83..3549578ed5 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -573,11 +573,12 @@ public slots: /**jsdoc * @function Script.callAnimationStateHandler - * @param {function} callback - * @param {object} parameters - * @param {string[]} names - * @param {boolean} useNames - * @param {object} resultHandler + * @param {function} callback - Callback. + * @param {object} parameters - Parameters. + * @param {string[]} names - Names. + * @param {boolean} useNames - Use names. + * @param {function} resultHandler - Result handler. + * @deprecated This function is deprecated and will be removed. */ void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); From 1e5837f25f5a5a1bffdf36da761040627ce2ceec Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 11:10:54 -0800 Subject: [PATCH 16/88] Enable Android buttons as needed. --- tools/nitpick/src/TestRunnerMobile.cpp | 3 +-- tools/nitpick/ui/Nitpick.ui | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/nitpick/src/TestRunnerMobile.cpp b/tools/nitpick/src/TestRunnerMobile.cpp index 4d0d18ef3d..d7800f35b4 100644 --- a/tools/nitpick/src/TestRunnerMobile.cpp +++ b/tools/nitpick/src/TestRunnerMobile.cpp @@ -60,6 +60,7 @@ void TestRunnerMobile::setWorkingFolderAndEnableControls() { setWorkingFolder(_workingFolderLabel); _connectDeviceButton->setEnabled(true); + _downloadAPKPushbutton->setEnabled(true); } void TestRunnerMobile::connectDevice() { @@ -154,8 +155,6 @@ void TestRunnerMobile::downloadComplete() { } else { _statusLabel->setText("Installer download complete"); } - - _installAPKPushbutton->setEnabled(true); } void TestRunnerMobile::installAPK() { diff --git a/tools/nitpick/ui/Nitpick.ui b/tools/nitpick/ui/Nitpick.ui index a0f368863d..c85311d86b 100644 --- a/tools/nitpick/ui/Nitpick.ui +++ b/tools/nitpick/ui/Nitpick.ui @@ -46,7 +46,7 @@ - 5 + 0 From dbdf5fdd1f73e1799a9ecd3351ac6dc774ff3ceb Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 13:53:07 -0800 Subject: [PATCH 17/88] Decreased threshold after additional testing. --- tools/nitpick/src/TestCreator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nitpick/src/TestCreator.h b/tools/nitpick/src/TestCreator.h index c2e7ba14f2..f2bd520574 100644 --- a/tools/nitpick/src/TestCreator.h +++ b/tools/nitpick/src/TestCreator.h @@ -122,7 +122,7 @@ private: const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; const double THRESHOLD_GLOBAL{ 0.9998 }; - const double THRESHOLD_LOCAL { 0.7770 }; + const double THRESHOLD_LOCAL { 0.7500 }; QDir _imageDirectory; From 24c7c8be190cd605f2531a6f2353e44aefb747e6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 9 Mar 2019 12:03:59 +1300 Subject: [PATCH 18/88] Update JSDoc per merge from master --- interface/src/avatar/MyAvatar.cpp | 18 ++++++++++++++++++ interface/src/avatar/MyAvatar.h | 21 ++++++++++++--------- libraries/animation/src/Rig.cpp | 15 +++++++++++---- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3db1228796..710a0550c5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5358,6 +5358,24 @@ void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr& otherAvatar) } } +/**jsdoc + * Physics options to use in the flow simulation of a joint. + * @typedef {object} MyAvatar.FlowPhysicsOptions + * @property {boolean} [active=true] - true to enable flow on the joint, false if it isn't., + * @property {number} [radius=0.01] - The thickness of segments and knots. (Needed for collisions.) + * @property {number} [gravity=-0.0096] - Y-value of the gravity vector. + * @property {number} [inertia=0.8] - Rotational inertia multiplier. + * @property {number} [damping=0.85] - The amount of damping on joint oscillation. + * @property {number} [stiffness=0.0] - How stiff each thread is. + * @property {number} [delta=0.55] - Delta time for every integration step. + */ +/**jsdoc + * Collision options to use in the flow simulation of a joint. + * @typedef {object} MyAvatar.FlowCollisionsOptions + * @property {string} [type="sphere"] - Currently, only "sphere" is supported. + * @property {number} [radius=0.05] - Collision sphere radius. + * @property {number} [offset=Vec3.ZERO] - Offset of the collision sphere from the joint. + */ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig, const QVariantMap& collisionsConfig) { if (_skeletonModel->isLoaded()) { _skeletonModel->getRig().initFlow(isActive); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5e049c7a02..1c44db703f 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1548,15 +1548,18 @@ public: void addAvatarHandsToFlow(const std::shared_ptr& otherAvatar); /**jsdoc - * Init flow simulation on avatar. - * @function MyAvatar.useFlow - * @param {boolean} - Set to true to activate flow simulation. - * @param {boolean} - Set to true to activate collisions. - * @param {Object} physicsConfig - object with the customized physic parameters - * i.e. {"hair": {"active": true, "stiffness": 0.0, "radius": 0.04, "gravity": -0.035, "damping": 0.8, "inertia": 0.8, "delta": 0.35}} - * @param {Object} collisionsConfig - object with the customized collision parameters - * i.e. {"Spine2": {"type": "sphere", "radius": 0.14, "offset": {"x": 0.0, "y": 0.2, "z": 0.0}}} - */ + * Enables and disables flow simulation of physics on the avatar's hair, clothes, and body parts. See + * {@link https://docs.highfidelity.com/create/avatars/create-avatars/add-flow.html|Add Flow to Your Avatar} for more + * information. + * @function MyAvatar.useFlow + * @param {boolean} isActive - true if flow simulation is enabled on the joint, false if it isn't. + * @param {boolean} isCollidable - true to enable collisions in the flow simulation, false to + * disable. + * @param {Object} [physicsConfig>] - Physic configurations for particular entity + * and avatar joints. + * @param {Object} [collisionsConfig] - Collision configurations for particular + * entity and avatar joints. + */ Q_INVOKABLE void useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig = QVariantMap(), const QVariantMap& collisionsConfig = QVariantMap()); public slots: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2a4c2326db..e1ee134530 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -170,8 +170,13 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig * coordinate system of the elbow. * leftHandPoleVector{@link Vec3}The direction the elbow should point in rig * coordinates. - - * rightHandPosition{@link Vec3}The desired position of the RightHand + * + * leftHandIKEnabledbooleantrue if IK is enabled for the left + * hand. + * rightHandIKEnabledbooleantrue if IK is enabled for the right + * hand. + * + * rightHandPosition{@link Vec3}The desired position of the RightHand * joint in rig coordinates. * rightHandRotation{@link Quat}The desired orientation of the * RightHand joint in rig coordinates. @@ -189,7 +194,7 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig * foot. * rightFootIKEnabledbooleantrue if IK is enabled for the right * foot. - + * * leftFootIKPositionVarstringThe name of the source for the desired position * of the LeftFoot joint. If not set, the foot rotation of the underlying animation will be used. * leftFootIKRotationVarstringThe name of the source for the desired rotation @@ -199,7 +204,7 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig * underlying animation. * leftFootPoleVector{@link Vec3}The direction the knee should face in rig * coordinates. - * rightFootIKPositionVarstringThe name of the source for the desired position + * rightFootIKPositionVarstringThe name of the source for the desired position * of the RightFoot joint. If not set, the foot rotation of the underlying animation will be used. * rightFootIKRotationVarstringThe name of the source for the desired rotation * of the RightFoot joint. If not set, the foot rotation of the underlying animation will be used. @@ -209,6 +214,8 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig * rightFootPoleVector{@link Vec3}The direction the knee should face in rig * coordinates. * + * splineIKEnabledbooleantrue if IK is enabled for the spline. + * * isTalkingbooleantrue if the avatar is talking. * notIsTalkingbooleantrue if the avatar is not talking. * From 687745dc7e3f3aa44a69db36066840ff0b11ad8f Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Fri, 8 Mar 2019 17:17:10 -0800 Subject: [PATCH 19/88] Remove old recursive scripts that are empty. Don't add unneeded delays. --- tools/nitpick/src/TestCreator.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index f87134ce5b..587490bb64 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -824,6 +824,10 @@ void TestCreator::createRecursiveScript(const QString& directory, bool interacti // If 'directories' is empty, this means that this recursive script has no tests to call, so it is redundant if (directories.length() == 0) { + QString testRecursivePathname = directory + "/" + TEST_RECURSIVE_FILENAME; + if (QFile::exists(testRecursivePathname)) { + QFile::remove(testRecursivePathname); + } return; } @@ -856,10 +860,7 @@ void TestCreator::createRecursiveScript(const QString& directory, bool interacti textStream << " nitpick = createNitpick(Script.resolvePath(\".\"));" << endl; textStream << " testsRootPath = nitpick.getTestsRootPath();" << endl << endl; textStream << " nitpick.enableRecursive();" << endl; - textStream << " nitpick.enableAuto();" << endl << endl; - textStream << " if (typeof Test !== 'undefined') {" << endl; - textStream << " Test.wait(10000);" << endl; - textStream << " }" << endl; + textStream << " nitpick.enableAuto();" << endl; textStream << "} else {" << endl; textStream << " depth++" << endl; textStream << "}" << endl << endl; From 24286273b402d83009fa7387900514d68e6e73ef Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 9 Mar 2019 19:22:07 +1300 Subject: [PATCH 20/88] Miscellaneous JSDoc fixes --- interface/src/ui/overlays/Overlays.cpp | 48 +++++++++++++------------- libraries/avatars/src/AvatarData.h | 2 +- libraries/shared/src/BillboardMode.h | 8 +++-- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 1bcb040a77..eec6eddf44 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -1711,9 +1711,9 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * parentID is an avatar skeleton. A value of 65535 means "no joint". * - * @property {boolean} isFacingAvatar - If true< / code>, the overlay is rotated to face the user's camera about an axis + * @property {boolean} isFacingAvatar - If true, the overlay is rotated to face the user's camera about an axis * parallel to the user's avatar's "up" direction. - * @property {string} text="" - The text to display.Text does not automatically wrap; use \n< / code> for a line break. + * @property {string} text="" - The text to display.Text does not automatically wrap; use \n for a line break. * @property {number} textAlpha=1 - The text alpha value. * @property {Color} backgroundColor=0,0,0 - The background color. * @property {number} backgroundAlpha=0.7 - The background alpha value. @@ -1929,39 +1929,39 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * * @property {number} startAt = 0 - The counter - clockwise angle from the overlay's x-axis that drawing starts at, in degrees. * @property {number} endAt = 360 - The counter - clockwise angle from the overlay's x-axis that drawing ends at, in degrees. - * @property {number} outerRadius = 1 - The outer radius of the overlay, in meters.Synonym: radius< / code>. + * @property {number} outerRadius = 1 - The outer radius of the overlay, in meters.Synonym: radius. * @property {number} innerRadius = 0 - The inner radius of the overlay, in meters. * @property {Color} color = 255, 255, 255 - The color of the overlay.Setting this value also sets the values of - * innerStartColor< / code>, innerEndColor< / code>, outerStartColor< / code>, and outerEndColor< / code>. - * @property {Color} startColor - Sets the values of innerStartColor< / code> and outerStartColor< / code>. - * Write - only.< / em> - * @property {Color} endColor - Sets the values of innerEndColor< / code> and outerEndColor< / code>. - * Write - only.< / em> - * @property {Color} innerColor - Sets the values of innerStartColor< / code> and innerEndColor< / code>. - * Write - only.< / em> - * @property {Color} outerColor - Sets the values of outerStartColor< / code> and outerEndColor< / code>. - * Write - only.< / em> + * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. + * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. + * Write - only. + * @property {Color} endColor - Sets the values of innerEndColor and outerEndColor. + * Write - only. + * @property {Color} innerColor - Sets the values of innerStartColor and innerEndColor. + * Write - only. + * @property {Color} outerColor - Sets the values of outerStartColor and outerEndColor. + * Write - only. * @property {Color} innerStartcolor - The color at the inner start point of the overlay. * @property {Color} innerEndColor - The color at the inner end point of the overlay. * @property {Color} outerStartColor - The color at the outer start point of the overlay. * @property {Color} outerEndColor - The color at the outer end point of the overlay. - * @property {number} alpha = 0.5 - The opacity of the overlay, 0.0< / code> -1.0< / code>.Setting this value also sets - * the values of innerStartAlpha< / code>, innerEndAlpha< / code>, outerStartAlpha< / code>, and - * outerEndAlpha< / code>.Synonym: Alpha< / code>; write - only< / em>. - * @property {number} startAlpha - Sets the values of innerStartAlpha< / code> and outerStartAlpha< / code>. - * Write - only.< / em> - * @property {number} endAlpha - Sets the values of innerEndAlpha< / code> and outerEndAlpha< / code>. - * Write - only.< / em> - * @property {number} innerAlpha - Sets the values of innerStartAlpha< / code> and innerEndAlpha< / code>. - * Write - only.< / em> - * @property {number} outerAlpha - Sets the values of outerStartAlpha< / code> and outerEndAlpha< / code>. - * Write - only.< / em> + * @property {number} alpha = 0.5 - The opacity of the overlay, 0.0 -1.0.Setting this value also sets + * the values of innerStartAlpha, innerEndAlpha, outerStartAlpha, and + * outerEndAlpha.Synonym: Alpha; write - only. + * @property {number} startAlpha - Sets the values of innerStartAlpha and outerStartAlpha. + * Write - only. + * @property {number} endAlpha - Sets the values of innerEndAlpha and outerEndAlpha. + * Write - only. + * @property {number} innerAlpha - Sets the values of innerStartAlpha and innerEndAlpha. + * Write - only. + * @property {number} outerAlpha - Sets the values of outerStartAlpha and outerEndAlpha. + * Write - only. * @property {number} innerStartAlpha = 0 - The alpha at the inner start point of the overlay. * @property {number} innerEndAlpha = 0 - The alpha at the inner end point of the overlay. * @property {number} outerStartAlpha = 0 - The alpha at the outer start point of the overlay. * @property {number} outerEndAlpha = 0 - The alpha at the outer end point of the overlay. * - * @property {boolean} hasTickMarks = false - If true< / code>, tick marks are drawn. + * @property {boolean} hasTickMarks = false - If true, tick marks are drawn. * @property {number} majorTickMarksAngle = 0 - The angle between major tick marks, in degrees. * @property {number} minorTickMarksAngle = 0 - The angle between minor tick marks, in degrees. * @property {number} majorTickMarksLength = 0 - The length of the major tick marks, in meters.A positive value draws tick marks diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 858af7ba3c..e0437cfeb1 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1034,7 +1034,7 @@ public: * @param {string} name - The name of the joint. * @returns {number} The index of the joint if valid, otherwise -1. * @example Report the index of your avatar's left arm joint. - * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm")); + * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"))); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ diff --git a/libraries/shared/src/BillboardMode.h b/libraries/shared/src/BillboardMode.h index 050f939941..700127aff1 100644 --- a/libraries/shared/src/BillboardMode.h +++ b/libraries/shared/src/BillboardMode.h @@ -18,9 +18,11 @@ * ValueDescription * * - * noneThe entity will not be billboarded. - * yawThe entity will yaw, but not pitch, to face the camera. Its actual rotation will be ignored. - * fullThe entity will be billboarded to face the camera. Its actual rotation will be ignored. + * "none"The entity will not be billboarded. + * "yaw"The entity will yaw, but not pitch, to face the camera. Its actual rotation will be + * ignored. + * "full"The entity will be billboarded to face the camera. Its actual rotation will be + * ignored. * * * @typedef {string} BillboardMode From 7419f9899e4af5b753966ea30b259359698d1d0a Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Sat, 9 Mar 2019 11:54:04 -0800 Subject: [PATCH 21/88] Modified thresholds to reduce false positives. --- tools/nitpick/src/TestCreator.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/nitpick/src/TestCreator.h b/tools/nitpick/src/TestCreator.h index f2bd520574..b4ce56a7d5 100644 --- a/tools/nitpick/src/TestCreator.h +++ b/tools/nitpick/src/TestCreator.h @@ -121,8 +121,8 @@ private: const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD_GLOBAL{ 0.9998 }; - const double THRESHOLD_LOCAL { 0.7500 }; + const double THRESHOLD_GLOBAL{ 0.9995 }; + const double THRESHOLD_LOCAL { 0.6 }; QDir _imageDirectory; From 97b01bad70d081f3245028618913c4f3e0c5f63a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 11 Mar 2019 12:23:26 -0700 Subject: [PATCH 22/88] tellPhysics to children when animating model --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 03c50008a0..54254ef26c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1032,7 +1032,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { }); if (changed) { - locationChanged(false, true); + locationChanged(true, true); } } From c9cb284c19534899224ac9162316b9d811446059 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 11 Mar 2019 14:33:00 -0700 Subject: [PATCH 23/88] Case 21467 - only update search when search field content has changed --- .../qml/hifi/commerce/marketplace/Marketplace.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index 07ded49956..fdeca07561 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -359,9 +359,11 @@ Rectangle { } onAccepted: { - root.searchString = searchField.text; - getMarketplaceItems(); - searchField.forceActiveFocus(); + if(root.searchString !== searchField.text) { + root.searchString = searchField.text; + getMarketplaceItems(); + searchField.forceActiveFocus(); + } } onActiveFocusChanged: { From d4b77d15cc4a84ea2eea91cd3eb13ea7da9b5ac8 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 11 Mar 2019 17:11:09 -0700 Subject: [PATCH 24/88] Use correct snapshots folder. Fix bug in APK installer. --- tools/nitpick/src/TestRunnerMobile.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tools/nitpick/src/TestRunnerMobile.cpp b/tools/nitpick/src/TestRunnerMobile.cpp index d7800f35b4..62630cc7b3 100644 --- a/tools/nitpick/src/TestRunnerMobile.cpp +++ b/tools/nitpick/src/TestRunnerMobile.cpp @@ -43,7 +43,7 @@ TestRunnerMobile::TestRunnerMobile( _installAPKPushbutton = installAPKPushbutton; _runInterfacePushbutton = runInterfacePushbutton; - folderLineEdit->setText("/sdcard/DCIM/TEST"); + folderLineEdit->setText("/sdcard/snapshots"); modelNames["SM_G955U1"] = "Samsung S8+ unlocked"; modelNames["SM_N960U1"] = "Samsung Note 9 unlocked"; @@ -163,22 +163,16 @@ void TestRunnerMobile::installAPK() { _adbInterface = new AdbInterface(); } - if (_installerFilename.isNull()) { - QString installerPathname = QFileDialog::getOpenFileName(nullptr, "Please select the APK", _workingFolder, - "Available APKs (*.apk)" - ); + QString installerPathname = QFileDialog::getOpenFileName(nullptr, "Please select the APK", _workingFolder, + "Available APKs (*.apk)" + ); - if (installerPathname.isNull()) { - return; - } - - // Remove the path - QStringList parts = installerPathname.split('/'); - _installerFilename = parts[parts.length() - 1]; + if (installerPathname.isNull()) { + return; } _statusLabel->setText("Installing"); - QString command = _adbInterface->getAdbCommand() + " install -r -d " + _workingFolder + "/" + _installerFilename + " >" + _workingFolder + "/installOutput.txt"; + QString command = _adbInterface->getAdbCommand() + " install -r -d " + installerPathname + " >" + _workingFolder + "/installOutput.txt"; appendLog(command); system(command.toStdString().c_str()); _statusLabel->setText("Installation complete"); From 41662b183bf82e26fb6b0fdf4fc8ff8015893e07 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 12 Mar 2019 10:45:04 -0700 Subject: [PATCH 25/88] Removed redundant method. --- interface/src/scripting/TestScriptingInterface.cpp | 10 ---------- interface/src/scripting/TestScriptingInterface.h | 7 ------- 2 files changed, 17 deletions(-) diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index a9ba165037..c3aeb2643b 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -199,13 +199,3 @@ void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { int TestScriptingInterface::getOtherAvatarsReplicaCount() { return qApp->getOtherAvatarsReplicaCount(); } - -QString TestScriptingInterface::getOperatingSystemType() { -#ifdef Q_OS_WIN - return "WINDOWS"; -#elif defined Q_OS_MAC - return "MACOS"; -#else - return "UNKNOWN"; -#endif -} diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index 26e967c9b5..4a1d1a3eeb 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -163,13 +163,6 @@ public slots: */ Q_INVOKABLE int getOtherAvatarsReplicaCount(); - /**jsdoc - * Returns the Operating Sytem type - * @function Test.getOperatingSystemType - * @returns {string} "WINDOWS", "MACOS" or "UNKNOWN" - */ - QString getOperatingSystemType(); - private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; From cb311408c68f2d465a80d76e0fdfe979cde96e13 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 11 Mar 2019 13:15:47 -0700 Subject: [PATCH 26/88] Remove _compositeFramebuffer from display plugins --- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 6 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 175 ++++++++---------- .../src/display-plugins/OpenGLDisplayPlugin.h | 28 ++- .../hmd/DebugHmdDisplayPlugin.h | 2 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 64 +++---- .../display-plugins/hmd/HmdDisplayPlugin.h | 11 +- .../stereo/InterleavedStereoDisplayPlugin.cpp | 4 +- .../stereo/InterleavedStereoDisplayPlugin.h | 2 +- .../src/OculusMobileDisplayPlugin.cpp | 10 +- .../src/OculusMobileDisplayPlugin.h | 4 +- .../plugins/src/plugins/DisplayPlugin.cpp | 12 +- libraries/plugins/src/plugins/DisplayPlugin.h | 8 +- .../render-utils/src/RenderCommonTask.cpp | 4 +- libraries/render/src/render/Args.h | 2 +- plugins/oculus/src/OculusDebugDisplayPlugin.h | 2 +- plugins/oculus/src/OculusDisplayPlugin.cpp | 28 ++- plugins/oculus/src/OculusDisplayPlugin.h | 2 +- .../src/OculusLegacyDisplayPlugin.cpp | 4 +- .../src/OculusLegacyDisplayPlugin.h | 2 +- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 16 +- plugins/openvr/src/OpenVrDisplayPlugin.h | 4 +- 22 files changed, 181 insertions(+), 211 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 9828a8beda..f55f5919f5 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -109,7 +109,7 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { +void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPointer& compositeFramebuffer) { #if defined(Q_OS_ANDROID) auto& virtualPadManager = VirtualPad::Manager::instance(); if(virtualPadManager.getLeftVirtualPad()->isShown()) { @@ -121,7 +121,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { render([&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.resetViewTransform(); batch.setProjectionTransform(mat4()); batch.setPipeline(_cursorPipeline); @@ -140,7 +140,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { }); } #endif - Parent::compositeExtra(); + Parent::compositeExtra(compositeFramebuffer); } static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60; diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index cc304c19c2..d4c321a571 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -33,7 +33,7 @@ public: virtual bool isThrottled() const override; - virtual void compositeExtra() override; + virtual void compositeExtra(const gpu::FramebufferPointer&) override; virtual void pluginUpdate() override {}; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index c536e6b6e2..5bc84acc6a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -379,14 +379,6 @@ void OpenGLDisplayPlugin::customizeContext() { scissorState->setDepthTest(gpu::State::DepthTest(false)); scissorState->setScissorEnable(true); - { -#ifdef Q_OS_ANDROID - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); -#else - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); -#endif - _simplePipeline = gpu::Pipeline::create(program, scissorState); - } { #ifdef Q_OS_ANDROID gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB); @@ -396,29 +388,59 @@ void OpenGLDisplayPlugin::customizeContext() { _presentPipeline = gpu::Pipeline::create(program, scissorState); } + + // HUD operator { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); - _hudPipeline = gpu::Pipeline::create(program, blendState); - } - { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); - _mirrorHUDPipeline = gpu::Pipeline::create(program, blendState); + gpu::PipelinePointer hudPipeline; + { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture); + hudPipeline = gpu::Pipeline::create(program, blendState); + } + + gpu::PipelinePointer hudMirrorPipeline; + { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX); + hudMirrorPipeline = gpu::Pipeline::create(program, blendState); + } + + + _hudOperator = [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror) { + auto hudStereo = isStereo(); + auto hudCompositeFramebufferSize = compositeFramebuffer->getSize(); + std::array hudEyeViewports; + for_each_eye([&](Eye eye) { + hudEyeViewports[eye] = eyeViewport(eye); + }); + if (hudPipeline && hudTexture) { + batch.enableStereo(false); + batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); + batch.setResourceTexture(0, hudTexture); + if (hudStereo) { + for_each_eye([&](Eye eye) { + batch.setViewportTransform(hudEyeViewports[eye]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } else { + batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + } + }; + } + { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture); _cursorPipeline = gpu::Pipeline::create(program, blendState); } } - updateCompositeFramebuffer(); } void OpenGLDisplayPlugin::uncustomizeContext() { _presentPipeline.reset(); _cursorPipeline.reset(); - _hudPipeline.reset(); - _mirrorHUDPipeline.reset(); - _compositeFramebuffer.reset(); + _hudOperator = DEFAULT_HUD_OPERATOR; withPresentThreadLock([&] { _currentFrame.reset(); _lastFrame = nullptr; @@ -510,24 +532,16 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const { }); } -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) { - renderFromTexture(batch, texture, viewport, scissor, nullptr); -} -void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { - auto fbo = gpu::FramebufferPointer(); +void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) { batch.enableStereo(false); batch.resetViewTransform(); - batch.setFramebuffer(fbo); + batch.setFramebuffer(destFbo); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); batch.setStateScissorRect(scissor); batch.setViewportTransform(viewport); batch.setResourceTexture(0, texture); -#ifndef USE_GLES batch.setPipeline(_presentPipeline); -#else - batch.setPipeline(_simplePipeline); -#endif batch.draw(gpu::TRIANGLE_STRIP, 4); if (copyFbo) { gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight()); @@ -553,7 +567,7 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur batch.setViewportTransform(copyFboRect); batch.setStateScissorRect(copyFboRect); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, {0.0f, 0.0f, 0.0f, 1.0f}); - batch.blit(fbo, sourceRect, copyFbo, copyRect); + batch.blit(destFbo, sourceRect, copyFbo, copyRect); } } @@ -581,41 +595,14 @@ void OpenGLDisplayPlugin::updateFrameData() { }); } -std::function OpenGLDisplayPlugin::getHUDOperator() { - auto hudPipeline = _hudPipeline; - auto hudMirrorPipeline = _mirrorHUDPipeline; - auto hudStereo = isStereo(); - auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize(); - std::array hudEyeViewports; - for_each_eye([&](Eye eye) { - hudEyeViewports[eye] = eyeViewport(eye); - }); - return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { - if (hudPipeline && hudTexture) { - batch.enableStereo(false); - batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline); - batch.setResourceTexture(0, hudTexture); - if (hudStereo) { - for_each_eye([&](Eye eye) { - batch.setViewportTransform(hudEyeViewports[eye]); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - } else { - batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } - } - }; -} - -void OpenGLDisplayPlugin::compositePointer() { +void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4()); render([&](gpu::Batch& batch) { batch.enableStereo(false); batch.setProjectionTransform(mat4()); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -626,34 +613,13 @@ void OpenGLDisplayPlugin::compositePointer() { batch.draw(gpu::TRIANGLE_STRIP, 4); }); } else { - batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize())); + batch.setViewportTransform(ivec4(uvec2(0), compositeFramebuffer->getSize())); batch.draw(gpu::TRIANGLE_STRIP, 4); } }); } -void OpenGLDisplayPlugin::compositeScene() { - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(_compositeFramebuffer); - batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize())); - batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize())); - batch.resetViewTransform(); - batch.setProjectionTransform(mat4()); - batch.setPipeline(_simplePipeline); - batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); -} - -void OpenGLDisplayPlugin::compositeLayers() { - updateCompositeFramebuffer(); - - { - PROFILE_RANGE_EX(render_detail, "compositeScene", 0xff0077ff, (uint64_t)presentCount()) - compositeScene(); - } - +void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { #ifdef HIFI_ENABLE_NSIGHT_DEBUG if (false) // do not draw the HUD if running nsight debug #endif @@ -667,23 +633,35 @@ void OpenGLDisplayPlugin::compositeLayers() { { PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount()) - compositeExtra(); + compositeExtra(compositeFramebuffer); } // Draw the pointer last so it's on top of everything auto compositorHelper = DependencyManager::get(); if (compositorHelper->getReticleVisible()) { PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount()) - compositePointer(); + compositePointer(compositeFramebuffer); } } -void OpenGLDisplayPlugin::internalPresent() { +void OpenGLDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { render([&](gpu::Batch& batch) { // Note: _displayTexture must currently be the same size as the display. uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels(); auto viewport = ivec4(uvec2(0), dims); - renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport); + + gpu::TexturePointer finalTexture; + if (_displayTexture) { + finalTexture = _displayTexture; + } else if (compositeFramebuffer) { + finalTexture = compositeFramebuffer->getRenderBuffer(0); + } else { + qCWarning(displayPlugins) << "No valid texture for output"; + } + + if (finalTexture) { + renderFromTexture(batch, finalTexture, viewport, viewport); + } }); swapBuffers(); _presentRate.increment(); @@ -700,7 +678,7 @@ void OpenGLDisplayPlugin::present() { } incrementPresentCount(); - if (_currentFrame) { + if (_currentFrame && _currentFrame->framebuffer) { auto correction = getViewCorrection(); getGLBackend()->setCameraCorrection(correction, _prevRenderView); _prevRenderView = correction * _currentFrame->view; @@ -720,18 +698,18 @@ void OpenGLDisplayPlugin::present() { // Write all layers to a local framebuffer { PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId) - compositeLayers(); + compositeLayers(_currentFrame->framebuffer); } // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) - internalPresent(); + internalPresent(_currentFrame->framebuffer); } gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory()); } else if (alwaysPresent()) { - internalPresent(); + internalPresent(nullptr); } _movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent)); } @@ -788,7 +766,12 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { } QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { - auto size = _compositeFramebuffer->getSize(); + if (!_currentFrame || !_currentFrame->framebuffer) { + return QImage(); + } + + auto compositeFramebuffer = _currentFrame->framebuffer; + auto size = compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; } @@ -806,7 +789,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); withOtherThreadContext([&] { - glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); + glBackend->downloadFramebuffer(compositeFramebuffer, ivec4(corner, bestSize), screenshot); }); return screenshot.mirrored(false, true); } @@ -858,7 +841,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const { - uvec2 vpSize = _compositeFramebuffer->getSize(); + auto vpSize = glm::uvec2(getRecommendedRenderSize()); vpSize.x /= 2; uvec2 vpPos; if (eye == Eye::Right) { @@ -891,14 +874,6 @@ void OpenGLDisplayPlugin::render(std::function f) { OpenGLDisplayPlugin::~OpenGLDisplayPlugin() { } -void OpenGLDisplayPlugin::updateCompositeFramebuffer() { - auto renderSize = glm::uvec2(getRecommendedRenderSize()); - if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) { - _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); - // _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y)); - } -} - void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { #if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 49a38ecb4c..3c48e8fc48 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -94,14 +94,10 @@ protected: // is not populated virtual bool alwaysPresent() const { return false; } - void updateCompositeFramebuffer(); - virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; } - virtual void compositeLayers(); - virtual void compositeScene(); - virtual std::function getHUDOperator(); - virtual void compositePointer(); - virtual void compositeExtra() {}; + virtual void compositeLayers(const gpu::FramebufferPointer&); + virtual void compositePointer(const gpu::FramebufferPointer&); + virtual void compositeExtra(const gpu::FramebufferPointer&) {}; // These functions must only be called on the presentation thread virtual void customizeContext(); @@ -116,10 +112,10 @@ protected: virtual void deactivateSession() {} // Plugin specific functionality to send the composed scene to the output window or device - virtual void internalPresent(); + virtual void internalPresent(const gpu::FramebufferPointer&); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo); - void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor); + + void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo = nullptr, const gpu::FramebufferPointer& copyFbo = nullptr); virtual void updateFrameData(); virtual glm::mat4 getViewCorrection() { return glm::mat4(); } @@ -142,14 +138,8 @@ protected: gpu::FramePointer _currentFrame; gpu::Frame* _lastFrame { nullptr }; mat4 _prevRenderView; - gpu::FramebufferPointer _compositeFramebuffer; - gpu::PipelinePointer _hudPipeline; - gpu::PipelinePointer _mirrorHUDPipeline; - gpu::ShaderPointer _mirrorHUDPS; - gpu::PipelinePointer _simplePipeline; - gpu::PipelinePointer _presentPipeline; gpu::PipelinePointer _cursorPipeline; - gpu::TexturePointer _displayTexture{}; + gpu::TexturePointer _displayTexture; float _compositeHUDAlpha { 1.0f }; struct CursorData { @@ -185,5 +175,9 @@ protected: // be serialized through this mutex mutable Mutex _presentMutex; float _hudAlpha{ 1.0f }; + +private: + gpu::PipelinePointer _presentPipeline; + }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index f2b1f36419..95592cc490 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -24,7 +24,7 @@ public: protected: void updatePresentPose() override; - void hmdPresent() override {} + void hmdPresent(const gpu::FramebufferPointer&) override {} bool isHmdMounted() const override { return true; } bool internalActivate() override; private: diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 321bcc3fd2..a515232b3f 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -114,20 +114,23 @@ void HmdDisplayPlugin::internalDeactivate() { void HmdDisplayPlugin::customizeContext() { Parent::customizeContext(); - _hudRenderer.build(); + _hudOperator = _hudRenderer.build(); } void HmdDisplayPlugin::uncustomizeContext() { // This stops the weirdness where if the preview was disabled, on switching back to 2D, // the vsync was stuck in the disabled state. No idea why that happens though. _disablePreview = false; - render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.resetViewTransform(); - batch.setFramebuffer(_compositeFramebuffer); - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); - }); - _hudRenderer = HUDRenderer(); + if (_currentFrame && _currentFrame->framebuffer) { + render([&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.resetViewTransform(); + batch.setFramebuffer(_currentFrame->framebuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0)); + }); + + } + _hudRenderer = {}; _previewTexture.reset(); Parent::uncustomizeContext(); } @@ -174,11 +177,11 @@ float HmdDisplayPlugin::getLeftCenterPixel() const { return leftCenterPixel; } -void HmdDisplayPlugin::internalPresent() { +void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) // Composite together the scene, hud and mouse cursor - hmdPresent(); + hmdPresent(compositeFramebuffer); if (_displayTexture) { // Note: _displayTexture must currently be the same size as the display. @@ -260,7 +263,7 @@ void HmdDisplayPlugin::internalPresent() { viewport.z *= 2; } - renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo); + renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, scissor, nullptr, fbo); }); swapBuffers(); @@ -345,7 +348,7 @@ glm::mat4 HmdDisplayPlugin::getViewCorrection() { } } -void HmdDisplayPlugin::HUDRenderer::build() { +DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() { vertices = std::make_shared(); indices = std::make_shared(); @@ -380,7 +383,7 @@ void HmdDisplayPlugin::HUDRenderer::build() { indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - std::vector indices; + std::vector indexData; for (int i = 0; i < stacks - 1; i++) { for (int j = 0; j < slices - 1; j++) { GLushort bottomLeftIndex = i * slices + j; @@ -388,24 +391,21 @@ void HmdDisplayPlugin::HUDRenderer::build() { GLushort topLeftIndex = bottomLeftIndex + slices; GLushort topRightIndex = topLeftIndex + 1; // FIXME make a z-order curve for better vertex cache locality - indices.push_back(topLeftIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(topRightIndex); + indexData.push_back(topLeftIndex); + indexData.push_back(bottomLeftIndex); + indexData.push_back(topRightIndex); - indices.push_back(topRightIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(bottomRightIndex); + indexData.push_back(topRightIndex); + indexData.push_back(bottomLeftIndex); + indexData.push_back(bottomRightIndex); } } - this->indices->append(indices); + indices->append(indexData); format = std::make_shared(); // 1 for everyone format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); uniformsBuffer = std::make_shared(sizeof(Uniforms), nullptr); - updatePipeline(); -} -void HmdDisplayPlugin::HUDRenderer::updatePipeline() { if (!pipeline) { auto program = gpu::Shader::createProgram(shader::render_utils::program::hmd_ui); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -416,10 +416,6 @@ void HmdDisplayPlugin::HUDRenderer::updatePipeline() { pipeline = gpu::Pipeline::create(program, state); } -} - -std::function HmdDisplayPlugin::HUDRenderer::render(HmdDisplayPlugin& plugin) { - updatePipeline(); auto hudPipeline = pipeline; auto hudFormat = format; @@ -428,9 +424,9 @@ std::function HmdDis auto hudUniformBuffer = uniformsBuffer; auto hudUniforms = uniforms; auto hudIndexCount = indexCount; - return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) { - if (hudPipeline && hudTexture) { - batch.setPipeline(hudPipeline); + return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer&, const bool mirror) { + if (pipeline && hudTexture) { + batch.setPipeline(pipeline); batch.setInputFormat(hudFormat); gpu::BufferView posView(hudVertices, VERTEX_OFFSET, hudVertices->getSize(), VERTEX_STRIDE, hudFormat->getAttributes().at(gpu::Stream::POSITION)._element); @@ -454,7 +450,7 @@ std::function HmdDis }; } -void HmdDisplayPlugin::compositePointer() { +void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) { auto& cursorManager = Cursor::Manager::instance(); const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto compositorHelper = DependencyManager::get(); @@ -463,7 +459,7 @@ void HmdDisplayPlugin::compositePointer() { render([&](gpu::Batch& batch) { // FIXME use standard gpu stereo rendering for this. batch.enableStereo(false); - batch.setFramebuffer(_compositeFramebuffer); + batch.setFramebuffer(compositeFramebuffer); batch.setPipeline(_cursorPipeline); batch.setResourceTexture(0, cursorData.texture); batch.resetViewTransform(); @@ -478,10 +474,6 @@ void HmdDisplayPlugin::compositePointer() { }); } -std::function HmdDisplayPlugin::getHUDOperator() { - return _hudRenderer.render(*this); -} - HmdDisplayPlugin::~HmdDisplayPlugin() { } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index d8c0ce8e1d..6755c5b7e0 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -53,16 +53,15 @@ signals: void hmdVisibleChanged(bool visible); protected: - virtual void hmdPresent() = 0; + virtual void hmdPresent(const gpu::FramebufferPointer&) = 0; virtual bool isHmdMounted() const = 0; virtual void postPreview() {}; virtual void updatePresentPose(); bool internalActivate() override; void internalDeactivate() override; - std::function getHUDOperator() override; - void compositePointer() override; - void internalPresent() override; + void compositePointer(const gpu::FramebufferPointer&) override; + void internalPresent(const gpu::FramebufferPointer&) override; void customizeContext() override; void uncustomizeContext() override; void updateFrameData() override; @@ -120,8 +119,6 @@ private: static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) }; static const int VERTEX_STRIDE { sizeof(Vertex) }; - void build(); - void updatePipeline(); - std::function render(HmdDisplayPlugin& plugin); + HUDOperator build(); } _hudRenderer; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 0ae0f9b1b6..69aa7fc344 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -37,13 +37,13 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { return result; } -void InterleavedStereoDisplayPlugin::internalPresent() { +void InterleavedStereoDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) { render([&](gpu::Batch& batch) { batch.enableStereo(false); batch.resetViewTransform(); batch.setFramebuffer(gpu::FramebufferPointer()); batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); - batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + batch.setResourceTexture(0, compositeFramebuffer->getRenderBuffer(0)); batch.setPipeline(_interleavedPresentPipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); }); diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index debd340f24..52dfa8f402 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -21,7 +21,7 @@ protected: // initialize OpenGL context settings needed by the plugin void customizeContext() override; void uncustomizeContext() override; - void internalPresent() override; + void internalPresent(const gpu::FramebufferPointer&) override; private: static const QString NAME; diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp index 9809d02866..12a9b12adc 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.cpp @@ -245,7 +245,7 @@ void OculusMobileDisplayPlugin::updatePresentPose() { }); } -void OculusMobileDisplayPlugin::internalPresent() { +void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compsiteFramebuffer) { VrHandler::pollTask(); if (!vrActive()) { @@ -253,8 +253,12 @@ void OculusMobileDisplayPlugin::internalPresent() { return; } - auto sourceTexture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); - glm::uvec2 sourceSize{ _compositeFramebuffer->getWidth(), _compositeFramebuffer->getHeight() }; + GLuint sourceTexture = 0; + glm::uvec2 sourceSize; + if (compsiteFramebuffer) { + sourceTexture = getGLBackend()->getTextureID(compsiteFramebuffer->getRenderBuffer(0)); + sourceSize = { compsiteFramebuffer->getWidth(), compsiteFramebuffer->getHeight() }; + } VrHandler::presentFrame(sourceTexture, sourceSize, presentTracking); _presentRate.increment(); } diff --git a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h index a98989655e..b5f7aa57b0 100644 --- a/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h +++ b/libraries/oculusMobilePlugin/src/OculusMobileDisplayPlugin.h @@ -54,8 +54,8 @@ protected: void uncustomizeContext() override; void updatePresentPose() override; - void internalPresent() override; - void hmdPresent() override { throw std::runtime_error("Unused"); } + void internalPresent(const gpu::FramebufferPointer&) override; + void hmdPresent(const gpu::FramebufferPointer&) override { throw std::runtime_error("Unused"); } bool isHmdMounted() const override; bool alwaysPresent() const override { return true; } diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 47503e8f85..71db87557c 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -2,6 +2,12 @@ #include + +const DisplayPlugin::HUDOperator DisplayPlugin::DEFAULT_HUD_OPERATOR{ std::function() }; + +DisplayPlugin::DisplayPlugin() : _hudOperator{ DEFAULT_HUD_OPERATOR } { +} + int64_t DisplayPlugin::getPaintDelayUsecs() const { std::lock_guard lock(_paintDelayMutex); return _paintDelayTimer.isValid() ? _paintDelayTimer.nsecsElapsed() / NSECS_PER_USEC : 0; @@ -35,8 +41,8 @@ void DisplayPlugin::waitForPresent() { } } -std::function DisplayPlugin::getHUDOperator() { - std::function hudOperator; +std::function DisplayPlugin::getHUDOperator() { + HUDOperator hudOperator; { QMutexLocker locker(&_presentMutex); hudOperator = _hudOperator; @@ -48,3 +54,5 @@ glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const { static const glm::mat4 xform; return xform; } + + diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index aa52e57c3f..9194fde3ac 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -121,6 +121,8 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: + DisplayPlugin(); + virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -214,7 +216,8 @@ public: void waitForPresent(); float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec - std::function getHUDOperator(); + using HUDOperator = std::function; + virtual HUDOperator getHUDOperator() final; static const QString& MENU_PATH(); @@ -231,7 +234,8 @@ protected: gpu::ContextPointer _gpuContext; - std::function _hudOperator { std::function() }; + static const HUDOperator DEFAULT_HUD_OPERATOR; + HUDOperator _hudOperator; MovingAverage _movingAveragePresent; diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 385e384efe..e77ccb74a5 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -122,8 +122,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra if (inputs) { batch.setFramebuffer(inputs); } - if (renderContext->args->_hudOperator) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + if (renderContext->args->_hudOperator && renderContext->args->_blitFramebuffer) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_blitFramebuffer, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); } }); #endif diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index b5c98e3428..8b2fff68c6 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -131,7 +131,7 @@ namespace render { render::ScenePointer _scene; int8_t _cameraMode { -1 }; - std::function _hudOperator; + std::function _hudOperator; gpu::TexturePointer _hudTexture; }; diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h index ec05cd92e2..690a488b34 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.h +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h @@ -16,7 +16,7 @@ public: bool isSupported() const override; protected: - void hmdPresent() override {} + void hmdPresent(const gpu::FramebufferPointer&) override {} bool isHmdMounted() const override { return true; } private: diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index df01591639..48440ac80f 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -108,13 +108,16 @@ void OculusDisplayPlugin::customizeContext() { } void OculusDisplayPlugin::uncustomizeContext() { + #if 0 - // Present a final black frame to the HMD - _compositeFramebuffer->Bound(FramebufferTarget::Draw, [] { - Context::ClearColor(0, 0, 0, 1); - Context::Clear().ColorBuffer(); - }); - hmdPresent(); + if (_currentFrame && _currentFrame->framebuffer) { + // Present a final black frame to the HMD + _currentFrame->framebuffer->Bound(FramebufferTarget::Draw, [] { + Context::ClearColor(0, 0, 0, 1); + Context::Clear().ColorBuffer(); + }); + hmdPresent(); + } #endif ovr_DestroyTextureSwapChain(_session, _textureSwapChain); @@ -127,7 +130,7 @@ void OculusDisplayPlugin::uncustomizeContext() { static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC); static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC); -void OculusDisplayPlugin::hmdPresent() { +void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { static uint64_t lastSubmitEnd = 0; if (!_customized) { @@ -157,15 +160,8 @@ void OculusDisplayPlugin::hmdPresent() { auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0); render([&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(_outputFramebuffer); - batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize())); - batch.resetViewTransform(); - batch.setProjectionTransform(mat4()); - batch.setPipeline(_presentPipeline); - batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); + auto viewport = ivec4(uvec2(), _outputFramebuffer->getSize()); + renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, viewport, _outputFramebuffer); }); glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0); } diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 9209fd373e..a0126d2e58 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -28,7 +28,7 @@ protected: QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; } bool internalActivate() override; - void hmdPresent() override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override; void customizeContext() override; void uncustomizeContext() override; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index e6b555443f..a928887866 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -237,7 +237,7 @@ void OculusLegacyDisplayPlugin::uncustomizeContext() { Parent::uncustomizeContext(); } -void OculusLegacyDisplayPlugin::hmdPresent() { +void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { if (!_hswDismissed) { ovrHSWDisplayState hswState; ovrHmd_GetHSWDisplayState(_hmd, &hswState); @@ -252,7 +252,7 @@ void OculusLegacyDisplayPlugin::hmdPresent() { memset(eyePoses, 0, sizeof(ovrPosef) * 2); eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation; - GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLint texture = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); if (_hmdWindow->makeCurrent()) { diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 36bdd1c792..241d626f0c 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -39,7 +39,7 @@ protected: void customizeContext() override; void uncustomizeContext() override; - void hmdPresent() override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override { return true; } private: diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 11d941dcd0..3d22268472 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -511,13 +511,13 @@ void OpenVrDisplayPlugin::customizeContext() { Parent::customizeContext(); if (_threadedSubmit) { - _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); +// _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { - if (0 != i) { +// if (0 != i) { _compositeInfos[i].texture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT)); - } +// } _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } _submitThread->_canvas = _submitCanvas; @@ -613,17 +613,17 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { return Parent::beginFrameRender(frameIndex); } -void OpenVrDisplayPlugin::compositeLayers() { +void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) { if (_threadedSubmit) { ++_renderingIndex; _renderingIndex %= COMPOSITING_BUFFER_SIZE; auto& newComposite = _compositeInfos[_renderingIndex]; newComposite.pose = _currentPresentFrameInfo.presentPose; - _compositeFramebuffer->setRenderBuffer(0, newComposite.texture); + compositeFramebuffer->setRenderBuffer(0, newComposite.texture); } - Parent::compositeLayers(); + Parent::compositeLayers(compositeFramebuffer); if (_threadedSubmit) { auto& newComposite = _compositeInfos[_renderingIndex]; @@ -645,13 +645,13 @@ void OpenVrDisplayPlugin::compositeLayers() { } } -void OpenVrDisplayPlugin::hmdPresent() { +void OpenVrDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) { PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex) if (_threadedSubmit) { _submitThread->waitForPresent(); } else { - GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); + GLuint glTexId = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0)); vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 265f328920..923a0f7a8f 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -72,8 +72,8 @@ protected: void internalDeactivate() override; void updatePresentPose() override; - void compositeLayers() override; - void hmdPresent() override; + void compositeLayers(const gpu::FramebufferPointer&) override; + void hmdPresent(const gpu::FramebufferPointer&) override; bool isHmdMounted() const override; void postPreview() override; From 6303f61cc32b010e3a73278e73ccd37cfcb39b64 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Tue, 12 Mar 2019 14:26:59 -0700 Subject: [PATCH 27/88] fix lasers scale issue --- interface/src/avatar/MyAvatar.h | 1 + libraries/shared/src/NestableTransformNode.h | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e516364f61..917da1a852 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1122,6 +1122,7 @@ public: float getUserEyeHeight() const; virtual SpatialParentTree* getParentTree() const override; + virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getSensorToWorldScale()); } const QUuid& getSelfID() const { return AVATAR_SELF_ID; } diff --git a/libraries/shared/src/NestableTransformNode.h b/libraries/shared/src/NestableTransformNode.h index a584bcd308..f70d158c91 100644 --- a/libraries/shared/src/NestableTransformNode.h +++ b/libraries/shared/src/NestableTransformNode.h @@ -20,8 +20,10 @@ public: _jointIndex(jointIndex) { auto nestablePointer = _spatiallyNestable.lock(); if (nestablePointer) { - glm::vec3 nestableDimensions = getActualScale(nestablePointer); - _baseScale = glm::max(glm::vec3(0.001f), nestableDimensions); + if (nestablePointer->getNestableType() != NestableType::Avatar) { + glm::vec3 nestableDimensions = getActualScale(nestablePointer); + _baseScale = glm::max(glm::vec3(0.001f), nestableDimensions); + } } } From 186588ddc4974069769b082ccc55b2abdf32d165 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 12 Mar 2019 15:09:50 -0700 Subject: [PATCH 28/88] set audio client muted when loaded data --- interface/src/scripting/Audio.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index bf43db3044..b1b5077e60 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -228,6 +228,9 @@ void Audio::loadData() { _hmdMuted = _hmdMutedSetting.get(); _pttDesktop = _pttDesktopSetting.get(); _pttHMD = _pttHMDSetting.get(); + + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false)); } bool Audio::getPTTHMD() const { From 1e354fb280b3e42f7698edddf16ebd003139b5b6 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Tue, 12 Mar 2019 17:18:15 -0700 Subject: [PATCH 29/88] fix notications scaling --- scripts/system/notifications.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 1d6b4dada3..469f30cd23 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -203,7 +203,7 @@ // Notification plane positions noticeY = -sensorScaleFactor * (y * NOTIFICATION_3D_SCALE + 0.5 * noticeHeight); notificationPosition = { x: 0, y: noticeY, z: 0 }; - buttonPosition = { x: 0.5 * sensorScaleFactor * (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH), y: noticeY, z: 0.001 }; + buttonPosition = { x: sensorScaleFactor * (noticeWidth - NOTIFICATION_3D_BUTTON_WIDTH), y: noticeY, z: 0.001 }; // Rotate plane notificationOrientation = Quat.fromPitchYawRollDegrees(NOTIFICATIONS_3D_PITCH, @@ -241,7 +241,7 @@ noticeWidth = notice.width * NOTIFICATION_3D_SCALE + NOTIFICATION_3D_BUTTON_WIDTH; noticeHeight = notice.height * NOTIFICATION_3D_SCALE; - notice.size = { x: noticeWidth, y: noticeHeight }; + notice.size = { x: noticeWidth * sensorScaleFactor, y: noticeHeight * sensorScaleFactor }; positions = calculate3DOverlayPositions(noticeWidth, noticeHeight, notice.y); @@ -249,15 +249,15 @@ notice.parentJointIndex = -2; if (!image) { - notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE; - notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE; + notice.topMargin = 0.75 * notice.topMargin * NOTIFICATION_3D_SCALE * sensorScaleFactor; + notice.leftMargin = 2 * notice.leftMargin * NOTIFICATION_3D_SCALE * sensorScaleFactor; notice.bottomMargin = 0; notice.rightMargin = 0; notice.lineHeight = 10.0 * (fontSize * sensorScaleFactor / 12.0) * NOTIFICATION_3D_SCALE; notice.isFacingAvatar = false; notificationText = Overlays.addOverlay("text3d", notice); - notifications.push(notificationText); + notifications.push(notificationText); } else { notifications.push(Overlays.addOverlay("image3d", notice)); } @@ -267,14 +267,15 @@ button.isFacingAvatar = false; button.parentID = MyAvatar.sessionUUID; button.parentJointIndex = -2; + button.visible = false; buttons.push((Overlays.addOverlay("image3d", button))); overlay3DDetails.push({ notificationOrientation: positions.notificationOrientation, notificationPosition: positions.notificationPosition, buttonPosition: positions.buttonPosition, - width: noticeWidth, - height: noticeHeight + width: noticeWidth * sensorScaleFactor, + height: noticeHeight * sensorScaleFactor }); From b35b7687b727d1c69cc9cc943b666cc66d9276df Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 12 Mar 2019 18:32:17 -0700 Subject: [PATCH 30/88] Ready. --- tools/nitpick/src/AWSInterface.cpp | 33 ++++++++++++++++++++++++------ tools/nitpick/src/AWSInterface.h | 11 ++++++++-- tools/nitpick/src/Nitpick.cpp | 4 ++-- tools/nitpick/src/TestCreator.cpp | 9 ++++++-- tools/nitpick/src/TestCreator.h | 5 ++++- tools/nitpick/src/common.h | 1 + 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/tools/nitpick/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp index a098d17917..16c0a220d8 100644 --- a/tools/nitpick/src/AWSInterface.cpp +++ b/tools/nitpick/src/AWSInterface.cpp @@ -8,6 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "AWSInterface.h" +#include "common.h" #include #include @@ -29,7 +30,9 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, QCheckBox* updateAWSCheckBox, QRadioButton* diffImageRadioButton, QRadioButton* ssimImageRadionButton, - QLineEdit* urlLineEdit + QLineEdit* urlLineEdit, + const QString& branch, + const QString& user ) { _workingDirectory = workingDirectory; @@ -53,6 +56,13 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, _urlLineEdit = urlLineEdit; _urlLineEdit->setEnabled(false); + + _urlLineEdit = urlLineEdit; + _urlLineEdit->setEnabled(false); + + _branch = branch; + _user = user; + QString zipFilenameWithoutExtension = zipFilename.split('.')[0]; extractTestFailuresFromZippedFolder(_workingDirectory + "/" + zipFilenameWithoutExtension); @@ -202,13 +212,21 @@ void AWSInterface::writeTitle(QTextStream& stream, const QStringList& originalNa stream << "run on " << hostName << "\n"; - int numberOfFailures = originalNamesFailures.length(); - int numberOfSuccesses = originalNamesSuccesses.length(); + stream << "

"; + stream << "nitpick " << nitpickVersion; + stream << ", tests from GitHub: " << _user << "/" << _branch; + stream << "

"; - stream << "

" << QString::number(numberOfFailures) << " failed, out of a total of " << QString::number(numberOfSuccesses) << " tests

\n"; + _numberOfFailures = originalNamesFailures.length(); + _numberOfSuccesses = originalNamesSuccesses.length(); + + stream << "

" << QString::number(_numberOfFailures) << " failed, out of a total of " << QString::number(_numberOfFailures + _numberOfSuccesses) << " tests

\n"; stream << "\t" << "\t" << "\n"; - stream << "\t" << "\t" << "

The following tests failed:

"; + + if (_numberOfFailures > 0) { + stream << "\t" << "\t" << "

The following tests failed:

"; + } } void AWSInterface::writeTable(QTextStream& stream, const QStringList& originalNamesFailures, const QStringList& originalNamesSuccesses) { @@ -289,7 +307,10 @@ void AWSInterface::writeTable(QTextStream& stream, const QStringList& originalNa closeTable(stream); stream << "\t" << "\t" << "\n"; - stream << "\t" << "\t" << "

The following tests passed:

"; + + if (_numberOfSuccesses > 0) { + stream << "\t" << "\t" << "

The following tests passed:

"; + } // Now do the same for passes folderNames.clear(); diff --git a/tools/nitpick/src/AWSInterface.h b/tools/nitpick/src/AWSInterface.h index 77d500fa7c..a2e4e36c37 100644 --- a/tools/nitpick/src/AWSInterface.h +++ b/tools/nitpick/src/AWSInterface.h @@ -31,7 +31,10 @@ public: QCheckBox* updateAWSCheckBox, QRadioButton* diffImageRadioButton, QRadioButton* ssimImageRadionButton, - QLineEdit* urlLineEdit); + QLineEdit* urlLineEdit, + const QString& branch, + const QString& user + ); void extractTestFailuresFromZippedFolder(const QString& folderName); void createHTMLFile(); @@ -70,9 +73,13 @@ private: QString AWS_BUCKET{ "hifi-qa" }; QLineEdit* _urlLineEdit; - + QString _user; + QString _branch; QString _comparisonImageFilename; + + int _numberOfFailures; + int _numberOfSuccesses; }; #endif // hifi_AWSInterface_h \ No newline at end of file diff --git a/tools/nitpick/src/Nitpick.cpp b/tools/nitpick/src/Nitpick.cpp index cf50774617..02ed120350 100644 --- a/tools/nitpick/src/Nitpick.cpp +++ b/tools/nitpick/src/Nitpick.cpp @@ -38,7 +38,7 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) { _ui.plainTextEdit->setReadOnly(true); - setWindowTitle("Nitpick - v3.1.2"); + setWindowTitle("Nitpick - " + nitpickVersion); clientProfiles << "VR-High" << "Desktop-High" << "Desktop-Low" << "Mobile-Touch" << "VR-Standalone"; _ui.clientProfileComboBox->insertItems(0, clientProfiles); @@ -266,7 +266,7 @@ void Nitpick::on_createXMLScriptRadioButton_clicked() { } void Nitpick::on_createWebPagePushbutton_clicked() { - _testCreator->createWebPage(_ui.updateAWSCheckBox, _ui.diffImageRadioButton, _ui.ssimImageRadioButton, _ui.awsURLLineEdit); + _testCreator->createWebPage(_ui.updateAWSCheckBox, _ui.diffImageRadioButton, _ui.ssimImageRadioButton, _ui.awsURLLineEdit, _ui.branchLineEdit->text(), _ui.userLineEdit->text()); } void Nitpick::about() { diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index 089e84904a..f45a23e459 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -1112,7 +1112,10 @@ void TestCreator::createWebPage( QCheckBox* updateAWSCheckBox, QRadioButton* diffImageRadioButton, QRadioButton* ssimImageRadionButton, - QLineEdit* urlLineEdit + QLineEdit* urlLineEdit, + const QString& branch, + const QString& user + ) { QString testResults = QFileDialog::getOpenFileName(nullptr, "Please select the zipped test results to update from", nullptr, "Zipped TestCreator Results (TestResults--*.zip)"); @@ -1136,6 +1139,8 @@ void TestCreator::createWebPage( updateAWSCheckBox, diffImageRadioButton, ssimImageRadionButton, - urlLineEdit + urlLineEdit, + branch, + user ); } \ No newline at end of file diff --git a/tools/nitpick/src/TestCreator.h b/tools/nitpick/src/TestCreator.h index 7cd38b42d4..50aa06e944 100644 --- a/tools/nitpick/src/TestCreator.h +++ b/tools/nitpick/src/TestCreator.h @@ -107,7 +107,10 @@ public: QCheckBox* updateAWSCheckBox, QRadioButton* diffImageRadioButton, QRadioButton* ssimImageRadionButton, - QLineEdit* urlLineEdit); + QLineEdit* urlLineEdit, + const QString& branch, + const QString& user + ); private: QProgressBar* _progressBar; diff --git a/tools/nitpick/src/common.h b/tools/nitpick/src/common.h index eb228ff2b3..b0a58747c1 100644 --- a/tools/nitpick/src/common.h +++ b/tools/nitpick/src/common.h @@ -56,4 +56,5 @@ const double R_Y = 0.212655f; const double G_Y = 0.715158f; const double B_Y = 0.072187f; +const QString nitpickVersion { "v3.1.4" }; #endif // hifi_common_h \ No newline at end of file From 609c4ab52ea5b138f0e41e99212cc63c6e9fcec2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 12 Mar 2019 18:41:43 -0700 Subject: [PATCH 31/88] try to fix audio injector threading issues --- interface/resources/qml/Stats.qml | 4 + interface/src/Application.cpp | 9 +- interface/src/avatar/AvatarManager.cpp | 5 +- interface/src/avatar/AvatarManager.h | 4 +- .../src/scripting/TTSScriptingInterface.cpp | 10 +- interface/src/ui/Keyboard.cpp | 4 +- interface/src/ui/Keyboard.h | 2 +- interface/src/ui/Stats.cpp | 5 + interface/src/ui/Stats.h | 9 + libraries/audio-client/src/AudioClient.cpp | 43 +-- libraries/audio-client/src/AudioClient.h | 2 + libraries/audio/src/AudioInjector.cpp | 267 +++++------------- libraries/audio/src/AudioInjector.h | 47 ++- .../audio/src/AudioInjectorLocalBuffer.cpp | 7 +- .../audio/src/AudioInjectorLocalBuffer.h | 1 + libraries/audio/src/AudioInjectorManager.cpp | 229 +++++++++++++-- libraries/audio/src/AudioInjectorManager.h | 22 +- .../src/EntityTreeRenderer.cpp | 2 +- .../src/EntityTreeRenderer.h | 2 +- .../src/AudioScriptingInterface.cpp | 2 +- .../script-engine/src/ScriptAudioInjector.cpp | 19 +- .../script-engine/src/ScriptAudioInjector.h | 21 +- .../ui/src/ui/TabletScriptingInterface.cpp | 4 +- libraries/ui/src/ui/types/SoundEffect.cpp | 11 +- libraries/ui/src/ui/types/SoundEffect.h | 6 +- 25 files changed, 403 insertions(+), 334 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 6748418d19..e10f86a947 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -232,6 +232,10 @@ Item { text: "Audio Codec: " + root.audioCodec + " Noise Gate: " + root.audioNoiseGate; } + StatText { + visible: root.expanded; + text: "Injectors (Local/NonLocal): " + root.audioInjectors.x + "/" + root.audioInjectors.y; + } StatText { visible: root.expanded; text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps"; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d9a1823a1..fd8f8dd4b0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2695,9 +2695,7 @@ void Application::cleanupBeforeQuit() { DependencyManager::destroy(); - if (_snapshotSoundInjector != nullptr) { - _snapshotSoundInjector->stop(); - } + _snapshotSoundInjector = nullptr; // destroy Audio so it and its threads have a chance to go down safely // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine @@ -4216,10 +4214,9 @@ void Application::keyPressEvent(QKeyEvent* event) { Setting::Handle notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true }; if (notificationSounds.get() && notificationSoundSnapshot.get()) { if (_snapshotSoundInjector) { - _snapshotSoundInjector->setOptions(options); - _snapshotSoundInjector->restart(); + DependencyManager::get()->setOptionsAndRestart(_snapshotSoundInjector, options); } else { - _snapshotSoundInjector = AudioInjector::playSound(_snapshotSound, options); + _snapshotSoundInjector = DependencyManager::get()->playSound(_snapshotSound, options); } } takeSnapshot(true); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c66c0a30cb..69f7054953 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -629,8 +629,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents // but most avatars are roughly the same size, so let's not be so fancy yet. const float AVATAR_STRETCH_FACTOR = 1.0f; - _collisionInjectors.remove_if( - [](const AudioInjectorPointer& injector) { return !injector || injector->isFinished(); }); + _collisionInjectors.remove_if([](const AudioInjectorPointer& injector) { return !injector; }); static const int MAX_INJECTOR_COUNT = 3; if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) { @@ -640,7 +639,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents options.volume = energyFactorOfFull; options.pitch = 1.0f / AVATAR_STRETCH_FACTOR; - auto injector = AudioInjector::playSoundAndDelete(collisionSound, options); + auto injector = DependencyManager::get()->playSound(collisionSound, options, true); _collisionInjectors.emplace_back(injector); } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 2b58b14d11..0468fbd809 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include // for SetOfEntities @@ -239,7 +239,7 @@ private: std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. - std::list _collisionInjectors; + std::list> _collisionInjectors; RateCounter<> _myAvatarSendRate; int _numAvatarsUpdated { 0 }; diff --git a/interface/src/scripting/TTSScriptingInterface.cpp b/interface/src/scripting/TTSScriptingInterface.cpp index 6589769ece..325e1ff649 100644 --- a/interface/src/scripting/TTSScriptingInterface.cpp +++ b/interface/src/scripting/TTSScriptingInterface.cpp @@ -66,7 +66,7 @@ void TTSScriptingInterface::updateLastSoundAudioInjector() { if (_lastSoundAudioInjector) { AudioInjectorOptions options; options.position = DependencyManager::get()->getMyAvatarPosition(); - _lastSoundAudioInjector->setOptions(options); + DependencyManager::get()->setOptions(_lastSoundAudioInjector, options); _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); } } @@ -143,7 +143,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { options.position = DependencyManager::get()->getMyAvatarPosition(); if (_lastSoundAudioInjector) { - _lastSoundAudioInjector->stop(); + DependencyManager::get()->stop(_lastSoundAudioInjector); _lastSoundAudioInjectorUpdateTimer.stop(); } @@ -151,7 +151,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { uint32_t numSamples = (uint32_t)_lastSoundByteArray.size() / sizeof(AudioData::AudioSample); auto samples = reinterpret_cast(_lastSoundByteArray.data()); auto newAudioData = AudioData::make(numSamples, numChannels, samples); - _lastSoundAudioInjector = AudioInjector::playSoundAndDelete(newAudioData, options); + _lastSoundAudioInjector = DependencyManager::get()->playSound(newAudioData, options, true); _lastSoundAudioInjectorUpdateTimer.start(INJECTOR_INTERVAL_MS); #else @@ -161,7 +161,7 @@ void TTSScriptingInterface::speakText(const QString& textToSpeak) { void TTSScriptingInterface::stopLastSpeech() { if (_lastSoundAudioInjector) { - _lastSoundAudioInjector->stop(); - _lastSoundAudioInjector = NULL; + DependencyManager::get()->stop(_lastSoundAudioInjector); + _lastSoundAudioInjector = nullptr; } } diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 9b75f78e67..1cbe31f1eb 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include @@ -537,7 +537,7 @@ void Keyboard::handleTriggerBegin(const QUuid& id, const PointerEvent& event) { audioOptions.position = keyWorldPosition; audioOptions.volume = 0.05f; - AudioInjector::playSoundAndDelete(_keySound, audioOptions); + DependencyManager::get()->playSound(_keySound, audioOptions, true); int scanCode = key.getScanCode(_capsEnabled); QString keyString = key.getKeyString(_capsEnabled); diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index b3358e486d..51e5e0571f 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -19,9 +19,9 @@ #include #include #include +#include #include #include -#include #include #include diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ecdae0b375..3c943028f5 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -266,6 +266,11 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(audioCodec, audioClient->getSelectedAudioFormat()); STAT_UPDATE(audioNoiseGate, audioClient->getNoiseGateOpen() ? "Open" : "Closed"); + { + int localInjectors = audioClient->getNumLocalInjectors(); + int nonLocalInjectors = DependencyManager::get()->getNumInjectors(); + STAT_UPDATE(audioInjectors, QVector2D(localInjectors, nonLocalInjectors)); + } STAT_UPDATE(entityPacketsInKbps, octreeServerCount ? totalEntityKbps / octreeServerCount : -1); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 0f563a6935..3134b223d6 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -87,6 +87,7 @@ private: \ * @property {number} audioPacketLoss - Read-only. * @property {string} audioCodec - Read-only. * @property {string} audioNoiseGate - Read-only. + * @property {Vec2} audioInjectors - Read-only. * @property {number} entityPacketsInKbps - Read-only. * * @property {number} downloads - Read-only. @@ -243,6 +244,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, audioPacketLoss, 0) STATS_PROPERTY(QString, audioCodec, QString()) STATS_PROPERTY(QString, audioNoiseGate, QString()) + STATS_PROPERTY(QVector2D, audioInjectors, QVector2D()); STATS_PROPERTY(int, entityPacketsInKbps, 0) STATS_PROPERTY(int, downloads, 0) @@ -692,6 +694,13 @@ signals: */ void audioNoiseGateChanged(); + /**jsdoc + * Triggered when the value of the audioInjectors property changes. + * @function Stats.audioInjectorsChanged + * @returns {Signal} + */ + void audioInjectorsChanged(); + /**jsdoc * Triggered when the value of the entityPacketsInKbps property changes. * @function Stats.entityPacketsInKbpsChanged diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index b2e6167ffa..afe57647f3 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1354,26 +1354,28 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { for (const AudioInjectorPointer& injector : _activeLocalAudioInjectors) { // the lock guarantees that injectorBuffer, if found, is invariant - AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + auto injectorBuffer = injector->getLocalBuffer(); if (injectorBuffer) { + auto options = injector->getOptions(); + static const int HRTF_DATASET_INDEX = 1; - int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + int numChannels = options.ambisonic ? AudioConstants::AMBISONIC : (options.stereo ? AudioConstants::STEREO : AudioConstants::MONO); size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; // get one frame from the injector memset(_localScratchBuffer, 0, bytesToRead); if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { - float gain = injector->getVolume(); + float gain = options.volume; - if (injector->isAmbisonic()) { + if (options.ambisonic) { - if (injector->isPositionSet()) { + if (options.positionSet) { // distance attenuation - glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); + glm::vec3 relativePosition = options.position - _positionGetter(); float distance = glm::max(glm::length(relativePosition), EPSILON); gain = gainForSource(distance, gain); } @@ -1382,7 +1384,7 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { // Calculate the soundfield orientation relative to the listener. // Injector orientation can be used to align a recording to our world coordinates. // - glm::quat relativeOrientation = injector->getOrientation() * glm::inverse(_orientationGetter()); + glm::quat relativeOrientation = options.orientation * glm::inverse(_orientationGetter()); // convert from Y-up (OpenGL) to Z-up (Ambisonic) coordinate system float qw = relativeOrientation.w; @@ -1394,12 +1396,12 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - } else if (injector->isStereo()) { + } else if (options.stereo) { - if (injector->isPositionSet()) { + if (options.positionSet) { // distance attenuation - glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); + glm::vec3 relativePosition = options.position - _positionGetter(); float distance = glm::max(glm::length(relativePosition), EPSILON); gain = gainForSource(distance, gain); } @@ -1412,10 +1414,10 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } else { // injector is mono - if (injector->isPositionSet()) { + if (options.positionSet) { // distance attenuation - glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); + glm::vec3 relativePosition = options.position - _positionGetter(); float distance = glm::max(glm::length(relativePosition), EPSILON); gain = gainForSource(distance, gain); @@ -1437,21 +1439,21 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } else { - qCDebug(audioclient) << "injector has no more data, marking finished for removal"; + //qCDebug(audioclient) << "injector has no more data, marking finished for removal"; injector->finishLocalInjection(); injectorsToRemove.append(injector); } } else { - qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; + //qCDebug(audioclient) << "injector has no local buffer, marking as finished for removal"; injector->finishLocalInjection(); injectorsToRemove.append(injector); } } for (const AudioInjectorPointer& injector : injectorsToRemove) { - qCDebug(audioclient) << "removing injector"; + //qCDebug(audioclient) << "removing injector"; _activeLocalAudioInjectors.removeOne(injector); } @@ -1562,15 +1564,13 @@ bool AudioClient::setIsStereoInput(bool isStereoInput) { } bool AudioClient::outputLocalInjector(const AudioInjectorPointer& injector) { - AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer(); + auto injectorBuffer = injector->getLocalBuffer(); if (injectorBuffer) { // local injectors are on the AudioInjectorsThread, so we must guard access Lock lock(_injectorsMutex); if (!_activeLocalAudioInjectors.contains(injector)) { - qCDebug(audioclient) << "adding new injector"; + //qCDebug(audioclient) << "adding new injector"; _activeLocalAudioInjectors.append(injector); - // move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop()) - injectorBuffer->setParent(nullptr); // update the flag _localInjectorsAvailable.exchange(true, std::memory_order_release); @@ -1586,6 +1586,11 @@ bool AudioClient::outputLocalInjector(const AudioInjectorPointer& injector) { } } +int AudioClient::getNumLocalInjectors() { + Lock lock(_injectorsMutex); + return _activeLocalAudioInjectors.size(); +} + void AudioClient::outputFormatChanged() { _outputFrameSize = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * OUTPUT_CHANNEL_COUNT * _outputFormat.sampleRate()) / _desiredOutputFormat.sampleRate(); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 87e0f68e72..2cfe83d445 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -181,6 +181,8 @@ public: bool isHeadsetPluggedIn() { return _isHeadsetPluggedIn; } #endif + int getNumLocalInjectors(); + public slots: void start(); void stop(); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1581990e0c..4911917bf0 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -24,9 +24,10 @@ #include "AudioRingBuffer.h" #include "AudioLogging.h" #include "SoundCache.h" -#include "AudioSRC.h" #include "AudioHelpers.h" +int metaType = qRegisterMetaType("AudioInjectorPointer"); + AbstractAudioInterface* AudioInjector::_localAudioInterface{ nullptr }; AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs) { @@ -51,26 +52,30 @@ AudioInjector::AudioInjector(AudioDataPointer audioData, const AudioInjectorOpti { } -AudioInjector::~AudioInjector() { - deleteLocalBuffer(); -} +AudioInjector::~AudioInjector() {} bool AudioInjector::stateHas(AudioInjectorState state) const { - return (_state & state) == state; + return resultWithReadLock([&] { + return (_state & state) == state; + }); } void AudioInjector::setOptions(const AudioInjectorOptions& options) { // since options.stereo is computed from the audio stream, // we need to copy it from existing options just in case. - bool currentlyStereo = _options.stereo; - bool currentlyAmbisonic = _options.ambisonic; - _options = options; - _options.stereo = currentlyStereo; - _options.ambisonic = currentlyAmbisonic; + withWriteLock([&] { + bool currentlyStereo = _options.stereo; + bool currentlyAmbisonic = _options.ambisonic; + _options = options; + _options.stereo = currentlyStereo; + _options.ambisonic = currentlyAmbisonic; + }); } void AudioInjector::finishNetworkInjection() { - _state |= AudioInjectorState::NetworkInjectionFinished; + withWriteLock([&] { + _state |= AudioInjectorState::NetworkInjectionFinished; + }); // if we are already finished with local // injection, then we are finished @@ -80,35 +85,31 @@ void AudioInjector::finishNetworkInjection() { } void AudioInjector::finishLocalInjection() { - _state |= AudioInjectorState::LocalInjectionFinished; - if(_options.localOnly || stateHas(AudioInjectorState::NetworkInjectionFinished)) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "finishLocalInjection"); + return; + } + + bool localOnly = false; + withWriteLock([&] { + _state |= AudioInjectorState::LocalInjectionFinished; + localOnly = _options.localOnly; + }); + + if(localOnly || stateHas(AudioInjectorState::NetworkInjectionFinished)) { finish(); } } void AudioInjector::finish() { - _state |= AudioInjectorState::Finished; - + withWriteLock([&] { + _state |= AudioInjectorState::Finished; + }); emit finished(); - - deleteLocalBuffer(); + _localBuffer = nullptr; } void AudioInjector::restart() { - // grab the AudioInjectorManager - auto injectorManager = DependencyManager::get(); - - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "restart"); - - if (!_options.localOnly) { - // notify the AudioInjectorManager to wake up in case it's waiting for new injectors - injectorManager->notifyInjectorReadyCondition(); - } - - return; - } - // reset the current send offset to zero _currentSendOffset = 0; @@ -121,19 +122,23 @@ void AudioInjector::restart() { // check our state to decide if we need extra handling for the restart request if (stateHas(AudioInjectorState::Finished)) { - if (!inject(&AudioInjectorManager::restartFinishedInjector)) { + if (!inject(&AudioInjectorManager::threadInjector)) { qWarning() << "AudioInjector::restart failed to thread injector"; } } } bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInjectorPointer&)) { - _state = AudioInjectorState::NotFinished; + AudioInjectorOptions options; + withWriteLock([&] { + _state = AudioInjectorState::NotFinished; + options = _options; + }); int byteOffset = 0; - if (_options.secondOffset > 0.0f) { - int numChannels = _options.ambisonic ? 4 : (_options.stereo ? 2 : 1); - byteOffset = (int)(AudioConstants::SAMPLE_RATE * _options.secondOffset * numChannels); + if (options.secondOffset > 0.0f) { + int numChannels = options.ambisonic ? 4 : (options.stereo ? 2 : 1); + byteOffset = (int)(AudioConstants::SAMPLE_RATE * options.secondOffset * numChannels); byteOffset *= AudioConstants::SAMPLE_SIZE; } _currentSendOffset = byteOffset; @@ -143,7 +148,7 @@ bool AudioInjector::inject(bool(AudioInjectorManager::*injection)(const AudioInj } bool success = true; - if (!_options.localOnly) { + if (!options.localOnly) { auto injectorManager = DependencyManager::get(); if (!(*injectorManager.*injection)(sharedFromThis())) { success = false; @@ -158,7 +163,8 @@ bool AudioInjector::injectLocally() { if (_localAudioInterface) { if (_audioData->getNumBytes() > 0) { - _localBuffer = new AudioInjectorLocalBuffer(_audioData); + _localBuffer = QSharedPointer(new AudioInjectorLocalBuffer(_audioData), &AudioInjectorLocalBuffer::deleteLater); + _localBuffer->moveToThread(thread()); _localBuffer->open(QIODevice::ReadOnly); _localBuffer->setShouldLoop(_options.loop); @@ -181,14 +187,6 @@ bool AudioInjector::injectLocally() { return success; } -void AudioInjector::deleteLocalBuffer() { - if (_localBuffer) { - _localBuffer->stop(); - _localBuffer->deleteLater(); - _localBuffer = nullptr; - } -} - const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f); static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; @@ -220,6 +218,10 @@ int64_t AudioInjector::injectNextFrame() { static int volumeOptionOffset = -1; static int audioDataOffset = -1; + AudioInjectorOptions options = resultWithReadLock([&] { + return _options; + }); + if (!_currentPacket) { if (_currentSendOffset < 0 || _currentSendOffset >= (int)_audioData->getNumBytes()) { @@ -253,7 +255,7 @@ int64_t AudioInjector::injectNextFrame() { audioPacketStream << QUuid::createUuid(); // pack the stereo/mono type of the stream - audioPacketStream << _options.stereo; + audioPacketStream << options.stereo; // pack the flag for loopback, if requested loopbackOptionOffset = _currentPacket->pos(); @@ -262,15 +264,16 @@ int64_t AudioInjector::injectNextFrame() { // pack the position for injected audio positionOptionOffset = _currentPacket->pos(); - audioPacketStream.writeRawData(reinterpret_cast(&_options.position), - sizeof(_options.position)); + audioPacketStream.writeRawData(reinterpret_cast(&options.position), + sizeof(options.position)); // pack our orientation for injected audio - audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation), - sizeof(_options.orientation)); + audioPacketStream.writeRawData(reinterpret_cast(&options.orientation), + sizeof(options.orientation)); + + audioPacketStream.writeRawData(reinterpret_cast(&options.position), + sizeof(options.position)); - audioPacketStream.writeRawData(reinterpret_cast(&_options.position), - sizeof(_options.position)); glm::vec3 boxCorner = glm::vec3(0); audioPacketStream.writeRawData(reinterpret_cast(&boxCorner), sizeof(glm::vec3)); @@ -283,7 +286,7 @@ int64_t AudioInjector::injectNextFrame() { volumeOptionOffset = _currentPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME; audioPacketStream << volume; - audioPacketStream << _options.ignorePenumbra; + audioPacketStream << options.ignorePenumbra; audioDataOffset = _currentPacket->pos(); @@ -313,10 +316,10 @@ int64_t AudioInjector::injectNextFrame() { _currentPacket->writePrimitive((uchar)(_localAudioInterface && _localAudioInterface->shouldLoopbackInjectors())); _currentPacket->seek(positionOptionOffset); - _currentPacket->writePrimitive(_options.position); - _currentPacket->writePrimitive(_options.orientation); + _currentPacket->writePrimitive(options.position); + _currentPacket->writePrimitive(options.orientation); - quint8 volume = packFloatGainToByte(_options.volume); + quint8 volume = packFloatGainToByte(options.volume); _currentPacket->seek(volumeOptionOffset); _currentPacket->writePrimitive(volume); @@ -326,8 +329,8 @@ int64_t AudioInjector::injectNextFrame() { // Might be a reasonable place to do the encode step here. QByteArray decodedAudio; - int totalBytesLeftToCopy = (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - if (!_options.loop) { + int totalBytesLeftToCopy = (options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + if (!options.loop) { // If we aren't looping, let's make sure we don't read past the end int bytesLeftToRead = _audioData->getNumBytes() - _currentSendOffset; totalBytesLeftToCopy = std::min(totalBytesLeftToCopy, bytesLeftToRead); @@ -342,14 +345,16 @@ int64_t AudioInjector::injectNextFrame() { auto samplesOut = reinterpret_cast(decodedAudio.data()); // Copy and Measure the loudness of this frame - _loudness = 0.0f; - for (int i = 0; i < samplesLeftToCopy; ++i) { - auto index = (currentSample + i) % _audioData->getNumSamples(); - auto sample = samples[index]; - samplesOut[i] = sample; - _loudness += abs(sample) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); - } - _loudness /= (float)samplesLeftToCopy; + withWriteLock([&] { + _loudness = 0.0f; + for (int i = 0; i < samplesLeftToCopy; ++i) { + auto index = (currentSample + i) % _audioData->getNumSamples(); + auto sample = samples[index]; + samplesOut[i] = sample; + _loudness += abs(sample) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); + } + _loudness /= (float)samplesLeftToCopy; + }); _currentSendOffset = (_currentSendOffset + totalBytesLeftToCopy) % _audioData->getNumBytes(); @@ -371,7 +376,7 @@ int64_t AudioInjector::injectNextFrame() { _outgoingSequenceNumber++; } - if (_currentSendOffset == 0 && !_options.loop) { + if (_currentSendOffset == 0 && !options.loop) { finishNetworkInjection(); return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } @@ -391,134 +396,10 @@ int64_t AudioInjector::injectNextFrame() { // If we are falling behind by more frames than our threshold, let's skip the frames ahead qCDebug(audio) << this << "injectNextFrame() skipping ahead, fell behind by " << (currentFrameBasedOnElapsedTime - _nextFrame) << " frames"; _nextFrame = currentFrameBasedOnElapsedTime; - _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (_options.stereo ? 2 : 1) % _audioData->getNumBytes(); + _currentSendOffset = _nextFrame * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL * (options.stereo ? 2 : 1) % _audioData->getNumBytes(); } int64_t playNextFrameAt = ++_nextFrame * AudioConstants::NETWORK_FRAME_USECS; return std::max(INT64_C(0), playNextFrameAt - currentTime); -} - -void AudioInjector::stop() { - // trigger a call on the injector's thread to change state to finished - QMetaObject::invokeMethod(this, "finish"); -} - -void AudioInjector::triggerDeleteAfterFinish() { - // make sure this fires on the AudioInjector thread - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection); - return; - } - - if (stateHas(AudioInjectorState::Finished)) { - stop(); - } else { - _state |= AudioInjectorState::PendingDelete; - } -} - -AudioInjectorPointer AudioInjector::playSoundAndDelete(SharedSoundPointer sound, const AudioInjectorOptions& options) { - AudioInjectorPointer injector = playSound(sound, options); - - if (injector) { - injector->_state |= AudioInjectorState::PendingDelete; - } - - return injector; -} - - -AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const AudioInjectorOptions& options) { - if (!sound || !sound->isReady()) { - return AudioInjectorPointer(); - } - - if (options.pitch == 1.0f) { - - AudioInjectorPointer injector = AudioInjectorPointer::create(sound, options); - - if (!injector->inject(&AudioInjectorManager::threadInjector)) { - qWarning() << "AudioInjector::playSound failed to thread injector"; - } - return injector; - - } else { - using AudioConstants::AudioSample; - using AudioConstants::SAMPLE_RATE; - const int standardRate = SAMPLE_RATE; - // limit pitch to 4 octaves - const float pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); - const int resampledRate = glm::round(SAMPLE_RATE / pitch); - - auto audioData = sound->getAudioData(); - auto numChannels = audioData->getNumChannels(); - auto numFrames = audioData->getNumFrames(); - - AudioSRC resampler(standardRate, resampledRate, numChannels); - - // create a resampled buffer that is guaranteed to be large enough - const int maxOutputFrames = resampler.getMaxOutput(numFrames); - const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); - QByteArray resampledBuffer(maxOutputSize, '\0'); - auto bufferPtr = reinterpret_cast(resampledBuffer.data()); - - resampler.render(audioData->data(), bufferPtr, numFrames); - - int numSamples = maxOutputFrames * numChannels; - auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); - - AudioInjectorPointer injector = AudioInjectorPointer::create(newAudioData, options); - - if (!injector->inject(&AudioInjectorManager::threadInjector)) { - qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; - } - return injector; - } -} - -AudioInjectorPointer AudioInjector::playSoundAndDelete(AudioDataPointer audioData, const AudioInjectorOptions& options) { - AudioInjectorPointer injector = playSound(audioData, options); - - if (injector) { - injector->_state |= AudioInjectorState::PendingDelete; - } - - return injector; -} - -AudioInjectorPointer AudioInjector::playSound(AudioDataPointer audioData, const AudioInjectorOptions& options) { - if (options.pitch == 1.0f) { - AudioInjectorPointer injector = AudioInjectorPointer::create(audioData, options); - - if (!injector->inject(&AudioInjectorManager::threadInjector)) { - qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; - } - return injector; - } else { - using AudioConstants::AudioSample; - using AudioConstants::SAMPLE_RATE; - const int standardRate = SAMPLE_RATE; - // limit pitch to 4 octaves - const float pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); - const int resampledRate = glm::round(SAMPLE_RATE / pitch); - - auto numChannels = audioData->getNumChannels(); - auto numFrames = audioData->getNumFrames(); - - AudioSRC resampler(standardRate, resampledRate, numChannels); - - // create a resampled buffer that is guaranteed to be large enough - const int maxOutputFrames = resampler.getMaxOutput(numFrames); - const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); - QByteArray resampledBuffer(maxOutputSize, '\0'); - auto bufferPtr = reinterpret_cast(resampledBuffer.data()); - - resampler.render(audioData->data(), bufferPtr, numFrames); - - int numSamples = maxOutputFrames * numChannels; - auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); - - return AudioInjector::playSound(newAudioData, options); - } } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 3c21d2eccf..1d5cf50033 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -49,7 +51,7 @@ AudioInjectorState& operator|= (AudioInjectorState& lhs, AudioInjectorState rhs) // In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object // until it dies. -class AudioInjector : public QObject, public QEnableSharedFromThis { +class AudioInjector : public QObject, public QEnableSharedFromThis, public ReadWriteLockable { Q_OBJECT public: AudioInjector(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions); @@ -61,38 +63,30 @@ public: int getCurrentSendOffset() const { return _currentSendOffset; } void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } - AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } + QSharedPointer getLocalBuffer() const { return _localBuffer; } AudioHRTF& getLocalHRTF() { return _localHRTF; } AudioFOA& getLocalFOA() { return _localFOA; } - bool isLocalOnly() const { return _options.localOnly; } - float getVolume() const { return _options.volume; } - bool isPositionSet() const { return _options.positionSet; } - glm::vec3 getPosition() const { return _options.position; } - glm::quat getOrientation() const { return _options.orientation; } - bool isStereo() const { return _options.stereo; } - bool isAmbisonic() const { return _options.ambisonic; } + float getLoudness() const { return resultWithReadLock([&] { return _loudness; }); } + bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); } + + bool isLocalOnly() const { return resultWithReadLock([&] { return _options.localOnly; }); } + float getVolume() const { return resultWithReadLock([&] { return _options.volume; }); } + bool isPositionSet() const { return resultWithReadLock([&] { return _options.positionSet; }); } + glm::vec3 getPosition() const { return resultWithReadLock([&] { return _options.position; }); } + glm::quat getOrientation() const { return resultWithReadLock([&] { return _options.orientation; }); } + bool isStereo() const { return resultWithReadLock([&] { return _options.stereo; }); } + bool isAmbisonic() const { return resultWithReadLock([&] { return _options.ambisonic; }); } + + const AudioInjectorOptions& getOptions() const { return resultWithReadLock([&] { return _options; }); } + void setOptions(const AudioInjectorOptions& options); bool stateHas(AudioInjectorState state) const ; static void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } - static AudioInjectorPointer playSoundAndDelete(SharedSoundPointer sound, const AudioInjectorOptions& options); - static AudioInjectorPointer playSound(SharedSoundPointer sound, const AudioInjectorOptions& options); - static AudioInjectorPointer playSoundAndDelete(AudioDataPointer audioData, const AudioInjectorOptions& options); - static AudioInjectorPointer playSound(AudioDataPointer audioData, const AudioInjectorOptions& options); - -public slots: void restart(); - - void stop(); - void triggerDeleteAfterFinish(); - - const AudioInjectorOptions& getOptions() const { return _options; } - void setOptions(const AudioInjectorOptions& options); - - float getLoudness() const { return _loudness; } - bool isPlaying() const { return !stateHas(AudioInjectorState::Finished); } void finish(); + void finishLocalInjection(); void finishNetworkInjection(); @@ -104,7 +98,6 @@ private: int64_t injectNextFrame(); bool inject(bool(AudioInjectorManager::*injection)(const AudioInjectorPointer&)); bool injectLocally(); - void deleteLocalBuffer(); static AbstractAudioInterface* _localAudioInterface; @@ -116,7 +109,7 @@ private: float _loudness { 0.0f }; int _currentSendOffset { 0 }; std::unique_ptr _currentPacket { nullptr }; - AudioInjectorLocalBuffer* _localBuffer { nullptr }; + QSharedPointer _localBuffer { nullptr }; int64_t _nextFrame { 0 }; std::unique_ptr _frameTimer { nullptr }; @@ -128,4 +121,6 @@ private: friend class AudioInjectorManager; }; +Q_DECLARE_METATYPE(AudioInjectorPointer) + #endif // hifi_AudioInjector_h diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index 015d87e03b..680513abf5 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -16,6 +16,10 @@ AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(AudioDataPointer audioData) : { } +AudioInjectorLocalBuffer::~AudioInjectorLocalBuffer() { + stop(); +} + void AudioInjectorLocalBuffer::stop() { _isStopped = true; @@ -30,9 +34,8 @@ bool AudioInjectorLocalBuffer::seek(qint64 pos) { } } - qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { - if (!_isStopped) { + if (!_isStopped && _audioData) { // first copy to the end of the raw audio int bytesToEnd = (int)_audioData->getNumBytes() - _currentOffset; diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index e0f8847883..2f73e5b313 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -22,6 +22,7 @@ class AudioInjectorLocalBuffer : public QIODevice { Q_OBJECT public: AudioInjectorLocalBuffer(AudioDataPointer audioData); + ~AudioInjectorLocalBuffer(); void stop(); diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index f30d3093ec..e5ffc77798 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -14,11 +14,14 @@ #include #include +#include #include "AudioConstants.h" #include "AudioInjector.h" #include "AudioLogging.h" +#include "AudioSRC.h" + AudioInjectorManager::~AudioInjectorManager() { _shouldStop = true; @@ -30,7 +33,7 @@ AudioInjectorManager::~AudioInjectorManager() { auto& timePointerPair = _injectors.top(); // ask it to stop and be deleted - timePointerPair.second->stop(); + timePointerPair.second->finish(); _injectors.pop(); } @@ -46,6 +49,8 @@ AudioInjectorManager::~AudioInjectorManager() { _thread->quit(); _thread->wait(); } + + moveToThread(qApp->thread()); } void AudioInjectorManager::createThread() { @@ -55,6 +60,8 @@ void AudioInjectorManager::createThread() { // when the thread is started, have it call our run to handle injection of audio connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection); + moveToThread(_thread); + // start the thread _thread->start(); } @@ -141,36 +148,7 @@ bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a bool AudioInjectorManager::threadInjector(const AudioInjectorPointer& injector) { if (_shouldStop) { - qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; - return false; - } - - // guard the injectors vector with a mutex - Lock lock(_injectorsMutex); - - if (wouldExceedLimits()) { - return false; - } else { - if (!_thread) { - createThread(); - } - - // move the injector to the QThread - injector->moveToThread(_thread); - - // add the injector to the queue with a send timestamp of now - _injectors.emplace(usecTimestampNow(), injector); - - // notify our wait condition so we can inject two frames for this injector immediately - _injectorReady.notify_one(); - - return true; - } -} - -bool AudioInjectorManager::restartFinishedInjector(const AudioInjectorPointer& injector) { - if (_shouldStop) { - qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; return false; } @@ -188,3 +166,192 @@ bool AudioInjectorManager::restartFinishedInjector(const AudioInjectorPointer& i } return true; } + +AudioInjectorPointer AudioInjectorManager::playSound(const SharedSoundPointer& sound, const AudioInjectorOptions& options, bool setPendingDelete) { + if (_shouldStop) { + qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + return nullptr; + } + + AudioInjectorPointer injector = nullptr; + if (sound && sound->isReady()) { + if (options.pitch == 1.0f) { + injector = QSharedPointer(new AudioInjector(sound, options), &AudioInjector::deleteLater); + } else { + using AudioConstants::AudioSample; + using AudioConstants::SAMPLE_RATE; + const int standardRate = SAMPLE_RATE; + // limit pitch to 4 octaves + const float pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); + const int resampledRate = glm::round(SAMPLE_RATE / pitch); + + auto audioData = sound->getAudioData(); + auto numChannels = audioData->getNumChannels(); + auto numFrames = audioData->getNumFrames(); + + AudioSRC resampler(standardRate, resampledRate, numChannels); + + // create a resampled buffer that is guaranteed to be large enough + const int maxOutputFrames = resampler.getMaxOutput(numFrames); + const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); + QByteArray resampledBuffer(maxOutputSize, '\0'); + auto bufferPtr = reinterpret_cast(resampledBuffer.data()); + + resampler.render(audioData->data(), bufferPtr, numFrames); + + int numSamples = maxOutputFrames * numChannels; + auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); + + injector = QSharedPointer(new AudioInjector(newAudioData, options), &AudioInjector::deleteLater); + } + } + + if (!injector) { + return nullptr; + } + + if (setPendingDelete) { + injector->_state |= AudioInjectorState::PendingDelete; + } + + injector->moveToThread(_thread); + injector->inject(&AudioInjectorManager::threadInjector); + + return injector; +} + +AudioInjectorPointer AudioInjectorManager::playSound(const AudioDataPointer& audioData, const AudioInjectorOptions& options, bool setPendingDelete) { + if (_shouldStop) { + qCDebug(audio) << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + return nullptr; + } + + AudioInjectorPointer injector = nullptr; + if (options.pitch == 1.0f) { + injector = QSharedPointer(new AudioInjector(audioData, options), &AudioInjector::deleteLater); + } else { + using AudioConstants::AudioSample; + using AudioConstants::SAMPLE_RATE; + const int standardRate = SAMPLE_RATE; + // limit pitch to 4 octaves + const float pitch = glm::clamp(options.pitch, 1 / 16.0f, 16.0f); + const int resampledRate = glm::round(SAMPLE_RATE / pitch); + + auto numChannels = audioData->getNumChannels(); + auto numFrames = audioData->getNumFrames(); + + AudioSRC resampler(standardRate, resampledRate, numChannels); + + // create a resampled buffer that is guaranteed to be large enough + const int maxOutputFrames = resampler.getMaxOutput(numFrames); + const int maxOutputSize = maxOutputFrames * numChannels * sizeof(AudioSample); + QByteArray resampledBuffer(maxOutputSize, '\0'); + auto bufferPtr = reinterpret_cast(resampledBuffer.data()); + + resampler.render(audioData->data(), bufferPtr, numFrames); + + int numSamples = maxOutputFrames * numChannels; + auto newAudioData = AudioData::make(numSamples, numChannels, bufferPtr); + + injector = QSharedPointer(new AudioInjector(newAudioData, options), &AudioInjector::deleteLater); + } + + if (!injector) { + return nullptr; + } + + if (setPendingDelete) { + injector->_state |= AudioInjectorState::PendingDelete; + } + + injector->moveToThread(_thread); + injector->inject(&AudioInjectorManager::threadInjector); + + return injector; +} + +void AudioInjectorManager::setOptionsAndRestart(const AudioInjectorPointer& injector, const AudioInjectorOptions& options) { + if (!injector) { + return; + } + + if (QThread::currentThread() != _thread) { + QMetaObject::invokeMethod(this, "setOptionsAndRestart", Q_ARG(const AudioInjectorPointer&, injector), Q_ARG(const AudioInjectorOptions&, options)); + _injectorReady.notify_one(); + return; + } + + injector->setOptions(options); + injector->restart(); +} + +void AudioInjectorManager::restart(const AudioInjectorPointer& injector) { + if (!injector) { + return; + } + + if (QThread::currentThread() != _thread) { + QMetaObject::invokeMethod(this, "restart", Q_ARG(const AudioInjectorPointer&, injector)); + _injectorReady.notify_one(); + return; + } + + injector->restart(); +} + +void AudioInjectorManager::setOptions(const AudioInjectorPointer& injector, const AudioInjectorOptions& options) { + if (!injector) { + return; + } + + if (QThread::currentThread() != _thread) { + QMetaObject::invokeMethod(this, "setOptions", Q_ARG(const AudioInjectorPointer&, injector), Q_ARG(const AudioInjectorOptions&, options)); + _injectorReady.notify_one(); + return; + } + + injector->setOptions(options); +} + +AudioInjectorOptions AudioInjectorManager::getOptions(const AudioInjectorPointer& injector) { + if (!injector) { + return AudioInjectorOptions(); + } + + return injector->getOptions(); +} + +float AudioInjectorManager::getLoudness(const AudioInjectorPointer& injector) { + if (!injector) { + return 0.0f; + } + + return injector->getLoudness(); +} + +bool AudioInjectorManager::isPlaying(const AudioInjectorPointer& injector) { + if (!injector) { + return false; + } + + return injector->isPlaying(); +} + +void AudioInjectorManager::stop(const AudioInjectorPointer& injector) { + if (!injector) { + return; + } + + if (QThread::currentThread() != _thread) { + QMetaObject::invokeMethod(this, "stop", Q_ARG(const AudioInjectorPointer&, injector)); + _injectorReady.notify_one(); + return; + } + + injector->finish(); +} + +size_t AudioInjectorManager::getNumInjectors() { + Lock lock(_injectorsMutex); + return _injectors.size(); +} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 9aca3014e3..cb243f23de 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -30,8 +30,27 @@ class AudioInjectorManager : public QObject, public Dependency { SINGLETON_DEPENDENCY public: ~AudioInjectorManager(); + + AudioInjectorPointer playSound(const SharedSoundPointer& sound, const AudioInjectorOptions& options, bool setPendingDelete = false); + AudioInjectorPointer playSound(const AudioDataPointer& audioData, const AudioInjectorOptions& options, bool setPendingDelete = false); + + size_t getNumInjectors(); + +public slots: + void setOptionsAndRestart(const AudioInjectorPointer& injector, const AudioInjectorOptions& options); + void restart(const AudioInjectorPointer& injector); + + void setOptions(const AudioInjectorPointer& injector, const AudioInjectorOptions& options); + AudioInjectorOptions getOptions(const AudioInjectorPointer& injector); + + float getLoudness(const AudioInjectorPointer& injector); + bool isPlaying(const AudioInjectorPointer& injector); + + void stop(const AudioInjectorPointer& injector); + private slots: void run(); + private: using TimeInjectorPointerPair = std::pair; @@ -49,11 +68,10 @@ private: using Lock = std::unique_lock; bool threadInjector(const AudioInjectorPointer& injector); - bool restartFinishedInjector(const AudioInjectorPointer& injector); void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } bool wouldExceedLimits(); - AudioInjectorManager() {}; + AudioInjectorManager() { createThread(); } AudioInjectorManager(const AudioInjectorManager&) = delete; AudioInjectorManager& operator=(const AudioInjectorManager&) = delete; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 143c7fa377..c235460404 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1105,7 +1105,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const EntityItemPointer& entit options.volume = volume; options.pitch = 1.0f / stretchFactor; - AudioInjector::playSoundAndDelete(collisionSound, options); + DependencyManager::get()->playSound(collisionSound, options, true); } void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index a257951ba8..a511d73210 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include // for RayToEntityIntersectionResult #include #include diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 65d71e46e6..395571c51f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -63,7 +63,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound optionsCopy.ambisonic = sound->isAmbisonic(); optionsCopy.localOnly = optionsCopy.localOnly || sound->isAmbisonic(); // force localOnly when Ambisonic - auto injector = AudioInjector::playSound(sound, optionsCopy); + auto injector = DependencyManager::get()->playSound(sound, optionsCopy); if (!injector) { return nullptr; } diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index 8b51377bff..822aa0a9c1 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -20,8 +20,11 @@ QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* c } // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately, - Qt::DirectConnection); + QObject::connect(engine, &QScriptEngine::destroyed, DependencyManager::get().data(), [&] { + qCDebug(scriptengine) << "Script was shutdown, stopping an injector"; + // FIXME: this doesn't work and leaves the injectors lying around + //DependencyManager::get()->stop(in->_injector); + }); return engine->newQObject(in, QScriptEngine::ScriptOwnership); } @@ -37,13 +40,5 @@ ScriptAudioInjector::ScriptAudioInjector(const AudioInjectorPointer& injector) : } ScriptAudioInjector::~ScriptAudioInjector() { - if (!_injector.isNull()) { - // we've been asked to delete after finishing, trigger a queued deleteLater here - _injector->triggerDeleteAfterFinish(); - } -} - -void ScriptAudioInjector::stopInjectorImmediately() { - qCDebug(scriptengine) << "ScriptAudioInjector::stopInjectorImmediately called to stop audio injector immediately."; - _injector->stop(); -} + DependencyManager::get()->stop(_injector); +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptAudioInjector.h b/libraries/script-engine/src/ScriptAudioInjector.h index c7fb2f8a9a..d77291b92c 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.h +++ b/libraries/script-engine/src/ScriptAudioInjector.h @@ -14,7 +14,7 @@ #include -#include +#include /**jsdoc * Plays — "injects" — the content of an audio file. Used in the {@link Audio} API. @@ -48,7 +48,7 @@ public slots: * Stop current playback, if any, and start playing from the beginning. * @function AudioInjector.restart */ - void restart() { _injector->restart(); } + void restart() { DependencyManager::get()->restart(_injector); } /**jsdoc * Stop audio playback. @@ -68,28 +68,28 @@ public slots: * injector.stop(); * }, 2000); */ - void stop() { _injector->stop(); } + void stop() { DependencyManager::get()->stop(_injector); } /**jsdoc * Get the current configuration of the audio injector. * @function AudioInjector.getOptions * @returns {AudioInjector.AudioInjectorOptions} Configuration of how the injector plays the audio. */ - const AudioInjectorOptions& getOptions() const { return _injector->getOptions(); } + AudioInjectorOptions getOptions() const { return DependencyManager::get()->getOptions(_injector); } /**jsdoc * Configure how the injector plays the audio. * @function AudioInjector.setOptions * @param {AudioInjector.AudioInjectorOptions} options - Configuration of how the injector plays the audio. */ - void setOptions(const AudioInjectorOptions& options) { _injector->setOptions(options); } + void setOptions(const AudioInjectorOptions& options) { DependencyManager::get()->setOptions(_injector, options); } /**jsdoc * Get the loudness of the most recent frame of audio played. * @function AudioInjector.getLoudness * @returns {number} The loudness of the most recent frame of audio played, range 0.01.0. */ - float getLoudness() const { return _injector->getLoudness(); } + float getLoudness() const { return DependencyManager::get()->getLoudness(_injector); } /**jsdoc * Get whether or not the audio is currently playing. @@ -110,7 +110,7 @@ public slots: * print("Sound is playing: " + injector.isPlaying()); * }, 2000); */ - bool isPlaying() const { return _injector->isPlaying(); } + bool isPlaying() const { return DependencyManager::get()->isPlaying(_injector); } signals: @@ -134,13 +134,6 @@ signals: */ void finished(); -protected slots: - - /**jsdoc - * Stop audio playback. (Synonym of {@link AudioInjector.stop|stop}.) - * @function AudioInjector.stopInjectorImmediately - */ - void stopInjectorImmediately(); private: AudioInjectorPointer _injector; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 7a1c37af33..963e0d87c1 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -23,7 +23,7 @@ #include "ToolbarScriptingInterface.h" #include "Logging.h" -#include +#include #include "SettingHandle.h" @@ -212,7 +212,7 @@ void TabletScriptingInterface::playSound(TabletAudioEvents aEvent) { options.localOnly = true; options.positionSet = false; // system sound - AudioInjectorPointer injector = AudioInjector::playSoundAndDelete(sound, options); + DependencyManager::get()->playSound(sound, options, true); } } diff --git a/libraries/ui/src/ui/types/SoundEffect.cpp b/libraries/ui/src/ui/types/SoundEffect.cpp index dc2328b33e..38114ecef1 100644 --- a/libraries/ui/src/ui/types/SoundEffect.cpp +++ b/libraries/ui/src/ui/types/SoundEffect.cpp @@ -2,12 +2,10 @@ #include "SoundEffect.h" #include -#include SoundEffect::~SoundEffect() { if (_injector) { - // stop will cause the AudioInjector to delete itself. - _injector->stop(); + DependencyManager::get()->stop(_injector); } } @@ -28,15 +26,14 @@ void SoundEffect::setVolume(float volume) { _volume = volume; } -void SoundEffect::play(QVariant position) { +void SoundEffect::play(const QVariant& position) { AudioInjectorOptions options; options.position = vec3FromVariant(position); options.localOnly = true; options.volume = _volume; if (_injector) { - _injector->setOptions(options); - _injector->restart(); + DependencyManager::get()->setOptionsAndRestart(_injector, options); } else { - _injector = AudioInjector::playSound(_sound, options); + _injector = DependencyManager::get()->playSound(_sound, options); } } diff --git a/libraries/ui/src/ui/types/SoundEffect.h b/libraries/ui/src/ui/types/SoundEffect.h index a7e29d86f9..cb8a5cd67f 100644 --- a/libraries/ui/src/ui/types/SoundEffect.h +++ b/libraries/ui/src/ui/types/SoundEffect.h @@ -13,9 +13,7 @@ #include #include - -class AudioInjector; -using AudioInjectorPointer = QSharedPointer; +#include // SoundEffect object, exposed to qml only, not interface JavaScript. // This is used to play spatial sound effects on tablets/web entities from within QML. @@ -34,7 +32,7 @@ public: float getVolume() const; void setVolume(float volume); - Q_INVOKABLE void play(QVariant position); + Q_INVOKABLE void play(const QVariant& position); protected: QUrl _url; float _volume { 1.0f }; From ea84847950e61db2534e86fd7487004b45e78559 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 13 Mar 2019 16:20:38 +1300 Subject: [PATCH 32/88] Update AnimStateDictionary JSDoc per feedback --- libraries/animation/src/Rig.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 74fd5d600b..82ab067472 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -91,6 +91,7 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig /**jsdoc *

An AnimStateDictionary object may have the following properties. It may also have other properties, set by * scripts.

+ *

Warning: These properties are subject to change. * * * @@ -171,11 +172,6 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig * * - * - * - * * * * - * - * * * * From f013b9af2b6691a85da36f153be482e74b6574e9 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Wed, 13 Mar 2019 00:24:19 -0700 Subject: [PATCH 33/88] fix warnings --- libraries/audio/src/AudioInjector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 1d5cf50033..94527cfdd0 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -78,7 +78,7 @@ public: bool isStereo() const { return resultWithReadLock([&] { return _options.stereo; }); } bool isAmbisonic() const { return resultWithReadLock([&] { return _options.ambisonic; }); } - const AudioInjectorOptions& getOptions() const { return resultWithReadLock([&] { return _options; }); } + AudioInjectorOptions getOptions() const { return resultWithReadLock([&] { return _options; }); } void setOptions(const AudioInjectorOptions& options); bool stateHas(AudioInjectorState state) const ; From 300dd39abf76459eddb7b7da2f58e9bb665df433 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 13 Mar 2019 12:23:31 -0700 Subject: [PATCH 34/88] fix script engine shutdown --- interface/src/ui/Stats.cpp | 2 +- libraries/audio/src/AudioInjector.h | 4 +++- .../script-engine/src/AudioScriptingInterface.cpp | 10 ---------- libraries/script-engine/src/ScriptAudioInjector.cpp | 7 ------- libraries/script-engine/src/ScriptEngine.cpp | 7 +------ libraries/script-engine/src/ScriptEngines.cpp | 2 ++ 6 files changed, 7 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3c943028f5..022b57c0d9 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -268,7 +268,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(audioNoiseGate, audioClient->getNoiseGateOpen() ? "Open" : "Closed"); { int localInjectors = audioClient->getNumLocalInjectors(); - int nonLocalInjectors = DependencyManager::get()->getNumInjectors(); + size_t nonLocalInjectors = DependencyManager::get()->getNumInjectors(); STAT_UPDATE(audioInjectors, QVector2D(localInjectors, nonLocalInjectors)); } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 94527cfdd0..555af84025 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -87,9 +87,11 @@ public: void restart(); void finish(); - void finishLocalInjection(); void finishNetworkInjection(); +public slots: + void finishLocalInjection(); + signals: void finished(); void restarting(); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 395571c51f..a55cac292f 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -46,16 +46,6 @@ ScriptAudioInjector* AudioScriptingInterface::playSystemSound(SharedSoundPointer } ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) { - if (QThread::currentThread() != thread()) { - ScriptAudioInjector* injector = NULL; - - BLOCKING_INVOKE_METHOD(this, "playSound", - Q_RETURN_ARG(ScriptAudioInjector*, injector), - Q_ARG(SharedSoundPointer, sound), - Q_ARG(const AudioInjectorOptions&, injectorOptions)); - return injector; - } - if (sound) { // stereo option isn't set from script, this comes from sound metadata or filename AudioInjectorOptions optionsCopy = injectorOptions; diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index 822aa0a9c1..267ac3339d 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -19,13 +19,6 @@ QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* c return QScriptValue(QScriptValue::NullValue); } - // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, DependencyManager::get().data(), [&] { - qCDebug(scriptengine) << "Script was shutdown, stopping an injector"; - // FIXME: this doesn't work and leaves the injectors lying around - //DependencyManager::get()->stop(in->_injector); - }); - return engine->newQObject(in, QScriptEngine::ScriptOwnership); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 825017b1fe..a4fd2540d4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -260,12 +260,7 @@ bool ScriptEngine::isDebugMode() const { #endif } -ScriptEngine::~ScriptEngine() { - QSharedPointer scriptEngines(_scriptEngines); - if (scriptEngines) { - scriptEngines->removeScriptEngine(qSharedPointerCast(sharedFromThis())); - } -} +ScriptEngine::~ScriptEngine() {} void ScriptEngine::disconnectNonEssentialSignals() { disconnect(); diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 3963ad5593..25c330e3fe 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -591,6 +591,8 @@ void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEnginePo } } + removeScriptEngine(engine); + if (removed && !_isReloading) { // Update settings with removed script saveScripts(); From 277ef56f4941e9d7d9742be7a23406b3b05fd516 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 14 Mar 2019 10:24:19 +1300 Subject: [PATCH 35/88] Fill in JSDoc for new flow functions --- interface/src/avatar/MyAvatar.cpp | 33 +++++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 15 +++++++------- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e0353da1b4..e0e9b5b648 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5443,6 +5443,39 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys } } +/**jsdoc + * Flow options currently used in flow simulation. + * @typedef {object} MyAvatar.FlowData + * @property {boolean} initialized - true if flow has been initialized for the current avatar, false + * if it hasn't. + * @property {boolean} active - true if flow is enabled, false if it isn't. + * @property {boolean} colliding - true if collisions are enabled, false if they aren't. + * @property {Object} physicsData - The physics configuration for each group of joints + * that has been configured. + * @property {Object} collisions - The collisions configuration for each joint that + * has collisions configured. + * @property {Object} threads - The threads hat have been configured, with the name of the first joint as + * the ThreadName and an array of the indexes of all the joints in the thread as the value. + */ +/**jsdoc + * A set of physics options currently used in flow simulation. + * @typedef {object} MyAvatar.FlowPhysicsData + * @property {boolean} active - true to enable flow on the joint, false if it isn't., + * @property {number} radius - The thickness of segments and knots. (Needed for collisions.) + * @property {number} gravity - Y-value of the gravity vector. + * @property {number} inertia - Rotational inertia multiplier. + * @property {number} damping - The amount of damping on joint oscillation. + * @property {number} stiffness - How stiff each thread is. + * @property {number} delta - Delta time for every integration step. + * @property {number[]} jointIndices - The indexes of the joints the options are applied to. + */ +/**jsdoc + * A set of collision options currently used in flow simulation. + * @typedef {object} MyAvatar.FlowCollisionsData + * @property {number} radius - Collision sphere radius. + * @property {number} offset - Offset of the collision sphere from the joint. + * @property {number} jointIndex - The index of the joint the options are applied to. + */ QVariantMap MyAvatar::getFlowData() { QVariantMap result; if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c7585311b8..bd112bfacc 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1563,16 +1563,17 @@ public: Q_INVOKABLE void useFlow(bool isActive, bool isCollidable, const QVariantMap& physicsConfig = QVariantMap(), const QVariantMap& collisionsConfig = QVariantMap()); /**jsdoc - * @function MyAvatar.getFlowData - * @returns {object} - */ + * Gets the current flow configuration. + * @function MyAvatar.getFlowData + * @returns {MyAvatar.FlowData} + */ Q_INVOKABLE QVariantMap getFlowData(); /**jsdoc - * returns the indices of every colliding flow joint - * @function MyAvatar.getCollidingFlowJoints - * @returns {int[]} - */ + * Gets the indexes of currently colliding flow joints. + * @function MyAvatar.getCollidingFlowJoints + * @returns {number[]} The indexes of currently colliding flow joints. + */ Q_INVOKABLE QVariantList getCollidingFlowJoints(); public slots: From 74edea80346209b6d7c7665360e78d2a212bef25 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 14 Mar 2019 10:25:31 +1300 Subject: [PATCH 36/88] Miscellaneous JSDoc fixes --- interface/src/avatar/MyAvatar.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bd112bfacc..8951bc7fed 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -652,7 +652,7 @@ public: * Restores a default role animation. *

Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily * understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or - * "walkFwd". To get the full list of roles, use {#link MyAvatar.getAnimationRoles}. For each role, + * "walkFwd". To get the full list of roles, use {@link MyAvatar.getAnimationRoles}. For each role, * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are * blended together with procedural data (such as look-at vectors, hand sensors etc.). You can change the animation clip * (.FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}. @@ -1555,7 +1555,7 @@ public: * @param {boolean} isActive - true if flow simulation is enabled on the joint, false if it isn't. * @param {boolean} isCollidable - true to enable collisions in the flow simulation, false to * disable. - * @param {Object} [physicsConfig>] - Physic configurations for particular entity + * @param {Object} [physicsConfig>] - Physics configurations for particular entity * and avatar joints. * @param {Object} [collisionsConfig] - Collision configurations for particular * entity and avatar joints. From a3dfd09e26335f41c43664a58f47ef558d7a222f Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 13 Mar 2019 17:03:53 -0700 Subject: [PATCH 37/88] adding scrolling in audio settings window --- interface/resources/qml/hifi/audio/Audio.qml | 165 ++++++++++++++---- .../qml/hifi/audio/AudioTabButton.qml | 2 +- 2 files changed, 133 insertions(+), 34 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index da306f911b..50329f9fa4 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 @@ -31,6 +31,8 @@ Rectangle { property string title: "Audio Settings" property int switchHeight: 16 property int switchWidth: 40 + readonly property real verticalScrollWidth: 10 + readonly property real verticalScrollShaft: 8 signal sendToScript(var message); color: hifi.colors.baseGray; @@ -42,7 +44,7 @@ Rectangle { property bool isVR: AudioScriptingInterface.context === "VR" - property real rightMostInputLevelPos: 450 + property real rightMostInputLevelPos: 440 //placeholder for control sizes and paddings //recalculates dynamically in case of UI size is changed QtObject { @@ -60,8 +62,8 @@ Rectangle { id: bar spacing: 0 width: parent.width - height: 42 - currentIndex: isVR ? 1 : 0 + height: 28; + currentIndex: isVR ? 1 : 0; AudioControls.AudioTabButton { height: parent.height @@ -92,25 +94,74 @@ Rectangle { Component.onCompleted: enablePeakValues(); - Column { - id: column - spacing: 12; - anchors.top: bar.bottom - anchors.bottom: parent.bottom - anchors.bottomMargin: 5 + Flickable { + id: flickView; + anchors.top: bar.bottom; + anchors.left: parent.left; + anchors.bottom: parent.bottom; width: parent.width; + contentWidth: parent.width; + contentHeight: contentItem.childrenRect.height; + boundsBehavior: Flickable.DragOverBounds; + flickableDirection: Flickable.VerticalFlick; + property bool isScrolling: (contentHeight - height) > 10 ? true : false; + clip: true; - Separator { } + ScrollBar.vertical: ScrollBar { + policy: flickView.isScrolling ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff; + parent: flickView.parent; + anchors.top: flickView.top; + anchors.right: flickView.right; + anchors.bottom: flickView.bottom; + anchors.rightMargin: -verticalScrollWidth; //compensate flickView's right margin + background: Item { + implicitWidth: verticalScrollWidth; + Rectangle { + color: hifi.colors.darkGray30; + radius: 4; + anchors { + fill: parent; + topMargin: -1; // Finesse size + bottomMargin: -2; + } + } + } + contentItem: Item { + implicitWidth: verticalScrollShaft; + Rectangle { + radius: verticalScrollShaft/2; + color: hifi.colors.white30; + anchors { + fill: parent; + leftMargin: 2; // Finesse size and position. + topMargin: 1; + bottomMargin: 1; + } + } + } + } - RowLayout { + Separator { + id: firstSeparator; + anchors.top: parent.top; + } + + Item { + id: switchesContainer; x: 2 * margins.paddings; width: parent.width; + // switch heights + 2 * top margins + height: (root.switchHeight) * 3 + 48; + anchors.top: firstSeparator.bottom; + anchors.topMargin: 10; // mute is in its own row - ColumnLayout { - id: columnOne - spacing: 24; - x: margins.paddings + Item { + id: switchContainer; + x: margins.paddings; + width: parent.width / 2; + height: parent.height; + anchors.left: parent.left; HifiControlsUit.Switch { id: muteMic; height: root.switchHeight; @@ -129,8 +180,12 @@ Rectangle { } HifiControlsUit.Switch { + id: noiseReductionSwitch; height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: muteMic.bottom; + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: "Noise Reduction"; backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.noiseReduction; @@ -144,6 +199,9 @@ Rectangle { id: pttSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: noiseReductionSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Push To Talk (T)"); backgroundOnColor: "#E3E3E3"; checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; @@ -164,12 +222,18 @@ Rectangle { } } - ColumnLayout { - spacing: 24; + Item { + id: additionalSwitchContainer + width: switchContainer.width - margins.paddings; + height: parent.height; + anchors.top: parent.top + anchors.left: switchContainer.right; HifiControlsUit.Switch { id: warnMutedSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: parent.top + anchors.left: parent.left labelTextOn: qsTr("Warn when muted"); backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.warnWhenMuted; @@ -184,6 +248,9 @@ Rectangle { id: audioLevelSwitch height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: warnMutedSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Audio Level Meter"); backgroundOnColor: "#E3E3E3"; checked: AvatarInputs.showAudioTools; @@ -197,6 +264,9 @@ Rectangle { id: stereoInput; height: root.switchHeight; switchWidth: root.switchWidth; + anchors.top: audioLevelSwitch.bottom + anchors.topMargin: 24 + anchors.left: parent.left labelTextOn: qsTr("Stereo input"); backgroundOnColor: "#E3E3E3"; checked: AudioScriptingInterface.isStereoInput; @@ -210,17 +280,20 @@ Rectangle { } Item { - anchors.left: parent.left + id: pttTextContainer + anchors.top: switchesContainer.bottom; + anchors.topMargin: 10; + anchors.left: parent.left; width: rightMostInputLevelPos; height: pttText.height; RalewayRegular { - id: pttText + id: pttText; x: margins.paddings; color: hifi.colors.white; width: rightMostInputLevelPos; height: paintedHeight; wrapMode: Text.WordWrap; - font.italic: true + font.italic: true; size: 16; text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") : @@ -228,28 +301,35 @@ Rectangle { } } - Separator { } + Separator { + id: secondSeparator; + anchors.top: pttTextContainer.bottom; + anchors.topMargin: 10; + } Item { + id: inputDeviceHeader x: margins.paddings; - width: parent.width - margins.paddings*2 - height: 36 + width: parent.width - margins.paddings*2; + height: 36; + anchors.top: secondSeparator.bottom; + anchors.topMargin: 10; HiFiGlyphs { - width: margins.sizeCheckBox + width: margins.sizeCheckBox; text: hifi.glyphs.mic; color: hifi.colors.white; - anchors.left: parent.left - anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% + anchors.left: parent.left; + anchors.leftMargin: -size/4; //the glyph has empty space at left about 25% anchors.verticalCenter: parent.verticalCenter; size: 30; } RalewayRegular { anchors.verticalCenter: parent.verticalCenter; - width: margins.sizeText + margins.sizeLevel - anchors.left: parent.left - anchors.leftMargin: margins.sizeCheckBox + width: margins.sizeText + margins.sizeLevel; + anchors.left: parent.left; + anchors.leftMargin: margins.sizeCheckBox; size: 16; color: hifi.colors.white; text: qsTr("Choose input device"); @@ -257,8 +337,10 @@ Rectangle { } ListView { - id: inputView - width: parent.width - margins.paddings*2 + id: inputView; + width: parent.width - margins.paddings*2; + anchors.top: inputDeviceHeader.bottom; + anchors.topMargin: 10; x: margins.paddings height: Math.min(150, contentHeight); spacing: 4; @@ -302,16 +384,26 @@ Rectangle { } } AudioControls.LoopbackAudio { + id: loopbackAudio x: margins.paddings + anchors.top: inputView.bottom; + anchors.topMargin: 10; visible: (bar.currentIndex === 1 && isVR) || (bar.currentIndex === 0 && !isVR); anchors { left: parent.left; leftMargin: margins.paddings } } - Separator {} + Separator { + id: thirdSeparator; + anchors.top: loopbackAudio.bottom; + anchors.topMargin: 10; + } Item { + id: outputDeviceHeader; + anchors.topMargin: 10; + anchors.top: thirdSeparator.bottom; x: margins.paddings; width: parent.width - margins.paddings*2 height: 36 @@ -342,6 +434,8 @@ Rectangle { width: parent.width - margins.paddings*2 x: margins.paddings height: Math.min(360 - inputView.height, contentHeight); + anchors.top: outputDeviceHeader.bottom; + anchors.topMargin: 10; spacing: 4; snapMode: ListView.SnapToItem; clip: true; @@ -372,6 +466,8 @@ Rectangle { Item { id: gainContainer x: margins.paddings; + anchors.top: outputView.bottom; + anchors.topMargin: 10; width: parent.width - margins.paddings*2 height: gainSliderTextMetrics.height @@ -430,7 +526,10 @@ Rectangle { } AudioControls.PlaySampleSound { + id: playSampleSound x: margins.paddings + anchors.top: gainContainer.bottom; + anchors.topMargin: 10; visible: (bar.currentIndex === 1 && isVR) || (bar.currentIndex === 0 && !isVR); diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index 32331ccb6e..c81377e524 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -16,7 +16,7 @@ import stylesUit 1.0 TabButton { id: control - font.pixelSize: height / 2 + font.pixelSize: 14 HifiConstants { id: hifi; } From 9b3b109d2222058ae686f8b07d7620eccfb7eec6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 13 Mar 2019 17:09:54 -0700 Subject: [PATCH 38/88] make placename consistent with hostname after domain reset --- libraries/networking/src/AddressManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index f4221e3d49..517daf8ce5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -834,6 +834,7 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) } _domainURL = domainURL; + _shareablePlaceName.clear(); // clear any current place information _rootPlaceID = QUuid(); From 6c9c58c657edc0c5ac83d8aaf59aa3f9f870c32a Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 14 Mar 2019 11:36:40 -0700 Subject: [PATCH 39/88] Remove unneeded wait. --- tools/nitpick/src/TestCreator.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/nitpick/src/TestCreator.cpp b/tools/nitpick/src/TestCreator.cpp index f45a23e459..bbeef11a1f 100644 --- a/tools/nitpick/src/TestCreator.cpp +++ b/tools/nitpick/src/TestCreator.cpp @@ -851,10 +851,7 @@ void TestCreator::createRecursiveScript(const QString& directory, bool interacti textStream << " nitpick = createNitpick(Script.resolvePath(\".\"));" << endl; textStream << " testsRootPath = nitpick.getTestsRootPath();" << endl << endl; textStream << " nitpick.enableRecursive();" << endl; - textStream << " nitpick.enableAuto();" << endl << endl; - textStream << " if (typeof Test !== 'undefined') {" << endl; - textStream << " Test.wait(10000);" << endl; - textStream << " }" << endl; + textStream << " nitpick.enableAuto();" << endl; textStream << "} else {" << endl; textStream << " depth++" << endl; textStream << "}" << endl << endl; From 10eae54d8c44358e81b673a7cf50cb0a317782cb Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 14 Mar 2019 12:06:43 -0700 Subject: [PATCH 40/88] changing content height and snap mode to allow scroll --- interface/resources/qml/hifi/audio/Audio.qml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 50329f9fa4..cd0f290da4 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -342,9 +342,8 @@ Rectangle { anchors.top: inputDeviceHeader.bottom; anchors.topMargin: 10; x: margins.paddings - height: Math.min(150, contentHeight); + height: contentHeight; spacing: 4; - snapMode: ListView.SnapToItem; clip: true; model: AudioScriptingInterface.devices.input; delegate: Item { @@ -433,11 +432,10 @@ Rectangle { id: outputView width: parent.width - margins.paddings*2 x: margins.paddings - height: Math.min(360 - inputView.height, contentHeight); + height: contentHeight; anchors.top: outputDeviceHeader.bottom; anchors.topMargin: 10; spacing: 4; - snapMode: ListView.SnapToItem; clip: true; model: AudioScriptingInterface.devices.output; delegate: Item { From 19f856b760bbf71ca032eccc45f797c2f47b8faa Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 12 Mar 2019 15:01:11 -0700 Subject: [PATCH 41/88] Switch Oculus mobile to single draw FBO with multiple color attachments --- .../src/main/assets/shaders/present.frag | 37 +++ .../src/main/assets/shaders/present.vert | 21 ++ .../oculus/OculusMobileActivity.java | 7 +- .../oculusMobile/src/ovr/Framebuffer.cpp | 141 +++++++---- libraries/oculusMobile/src/ovr/Framebuffer.h | 26 +- libraries/oculusMobile/src/ovr/VrHandler.cpp | 232 +++++++++++++++--- 6 files changed, 372 insertions(+), 92 deletions(-) create mode 100644 android/libraries/oculus/src/main/assets/shaders/present.frag create mode 100644 android/libraries/oculus/src/main/assets/shaders/present.vert diff --git a/android/libraries/oculus/src/main/assets/shaders/present.frag b/android/libraries/oculus/src/main/assets/shaders/present.frag new file mode 100644 index 0000000000..4fbec70f57 --- /dev/null +++ b/android/libraries/oculus/src/main/assets/shaders/present.frag @@ -0,0 +1,37 @@ +#version 320 es + +precision highp float; +precision highp sampler2D; + +layout(location = 0) in vec4 vTexCoordLR; + +layout(location = 0) out vec4 FragColorL; +layout(location = 1) out vec4 FragColorR; + +uniform sampler2D sampler; + +// https://software.intel.com/en-us/node/503873 + +// sRGB ====> Linear +vec3 color_sRGBToLinear(vec3 srgb) { + return mix(pow((srgb + vec3(0.055)) / vec3(1.055), vec3(2.4)), srgb / vec3(12.92), vec3(lessThanEqual(srgb, vec3(0.04045)))); +} + +vec4 color_sRGBAToLinear(vec4 srgba) { + return vec4(color_sRGBToLinear(srgba.xyz), srgba.w); +} + +// Linear ====> sRGB +vec3 color_LinearTosRGB(vec3 lrgb) { + return mix(vec3(1.055) * pow(vec3(lrgb), vec3(0.41666)) - vec3(0.055), vec3(lrgb) * vec3(12.92), vec3(lessThan(lrgb, vec3(0.0031308)))); +} + +vec4 color_LinearTosRGBA(vec4 lrgba) { + return vec4(color_LinearTosRGB(lrgba.xyz), lrgba.w); +} + +// FIXME switch to texelfetch for getting from the source texture +void main() { + FragColorL = color_LinearTosRGBA(texture(sampler, vTexCoordLR.xy)); + FragColorR = color_LinearTosRGBA(texture(sampler, vTexCoordLR.zw)); +} diff --git a/android/libraries/oculus/src/main/assets/shaders/present.vert b/android/libraries/oculus/src/main/assets/shaders/present.vert new file mode 100644 index 0000000000..dfd6b1412f --- /dev/null +++ b/android/libraries/oculus/src/main/assets/shaders/present.vert @@ -0,0 +1,21 @@ +#version 320 es + +layout(location = 0) out vec4 vTexCoordLR; + +void main(void) { + const float depth = 0.0; + const vec4 UNIT_QUAD[4] = vec4[4]( + vec4(-1.0, -1.0, depth, 1.0), + vec4(1.0, -1.0, depth, 1.0), + vec4(-1.0, 1.0, depth, 1.0), + vec4(1.0, 1.0, depth, 1.0) + ); + vec4 pos = UNIT_QUAD[gl_VertexID]; + gl_Position = pos; + vTexCoordLR.xy = pos.xy; + vTexCoordLR.xy += 1.0; + vTexCoordLR.y *= 0.5; + vTexCoordLR.x *= 0.25; + vTexCoordLR.zw = vTexCoordLR.xy; + vTexCoordLR.z += 0.5; +} diff --git a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java index 8ee22749c9..19865e7751 100644 --- a/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java +++ b/android/libraries/oculus/src/main/java/io/highfidelity/oculus/OculusMobileActivity.java @@ -7,6 +7,7 @@ // package io.highfidelity.oculus; +import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import android.view.Surface; @@ -24,7 +25,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca private static final String TAG = OculusMobileActivity.class.getSimpleName(); static { System.loadLibrary("oculusMobile"); } - private native void nativeOnCreate(); + private native void nativeOnCreate(AssetManager assetManager); private native static void nativeOnResume(); private native static void nativeOnPause(); private native static void nativeOnSurfaceChanged(Surface s); @@ -53,7 +54,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca mView = new SurfaceView(this); mView.getHolder().addCallback(this); - nativeOnCreate(); + nativeOnCreate(getAssets()); questNativeOnCreate(); } @@ -81,7 +82,7 @@ public class OculusMobileActivity extends QtActivity implements SurfaceHolder.Ca Log.w(TAG, "QQQ onResume"); super.onResume(); //Reconnect the global reference back to handler - nativeOnCreate(); + nativeOnCreate(getAssets()); questNativeOnResume(); nativeOnResume(); diff --git a/libraries/oculusMobile/src/ovr/Framebuffer.cpp b/libraries/oculusMobile/src/ovr/Framebuffer.cpp index 0f59eef614..57c45d3159 100644 --- a/libraries/oculusMobile/src/ovr/Framebuffer.cpp +++ b/libraries/oculusMobile/src/ovr/Framebuffer.cpp @@ -7,59 +7,44 @@ // #include "Framebuffer.h" +#include + #include -#include #include #include #include +#include "Helpers.h" + using namespace ovr; void Framebuffer::updateLayer(int eye, ovrLayerProjection2& layer, const ovrMatrix4f* projectionMatrix ) const { auto& layerTexture = layer.Textures[eye]; - layerTexture.ColorSwapChain = _swapChain; - layerTexture.SwapChainIndex = _index; + layerTexture.ColorSwapChain = _swapChainInfos[eye].swapChain; + layerTexture.SwapChainIndex = _swapChainInfos[eye].index; if (projectionMatrix) { layerTexture.TexCoordsFromTanAngles = ovrMatrix4f_TanAngleMatrixFromProjection( projectionMatrix ); } layerTexture.TextureRect = { 0, 0, 1, 1 }; } +void Framebuffer::SwapChainInfo::destroy() { + if (swapChain != nullptr) { + vrapi_DestroyTextureSwapChain(swapChain); + swapChain = nullptr; + } + index = -1; + length = -1; +} + void Framebuffer::create(const glm::uvec2& size) { _size = size; - _index = 0; - _validTexture = false; - - // Depth renderbuffer - /* glGenRenderbuffers(1, &_depth); - glBindRenderbuffer(GL_RENDERBUFFER, _depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, _size.x, _size.y); - glBindRenderbuffer(GL_RENDERBUFFER, 0); -*/ - // Framebuffer - glGenFramebuffers(1, &_fbo); - // glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - // glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depth); - // glBindFramebuffer(GL_FRAMEBUFFER, 0); - - _swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8, _size.x, _size.y, 1, 3); - - _length = vrapi_GetTextureSwapChainLength(_swapChain); - if (!_length) { - __android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures"); - return; - } - - for (int i = 0; i < _length; ++i) { - GLuint chainTexId = vrapi_GetTextureSwapChainHandle(_swapChain, i); - glBindTexture(GL_TEXTURE_2D, chainTexId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].create(size); + }); glBindTexture(GL_TEXTURE_2D, 0); + glGenFramebuffers(1, &_fbo); } void Framebuffer::destroy() { @@ -67,28 +52,80 @@ void Framebuffer::destroy() { glDeleteFramebuffers(1, &_fbo); _fbo = 0; } - if (0 != _depth) { - glDeleteRenderbuffers(1, &_depth); - _depth = 0; - } - if (_swapChain != nullptr) { - vrapi_DestroyTextureSwapChain(_swapChain); - _swapChain = nullptr; - } - _index = -1; - _length = -1; + + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].destroy(); + }); } void Framebuffer::advance() { - _index = (_index + 1) % _length; - _validTexture = false; + ovr::for_each_eye([&](ovrEye eye) { + _swapChainInfos[eye].advance(); + }); } -void Framebuffer::bind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - if (!_validTexture) { - GLuint chainTexId = vrapi_GetTextureSwapChainHandle(_swapChain, _index); - glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, chainTexId, 0); - _validTexture = true; +void Framebuffer::bind(GLenum target) { + glBindFramebuffer(target, _fbo); + _swapChainInfos[0].bind(target, GL_COLOR_ATTACHMENT0); + _swapChainInfos[1].bind(target, GL_COLOR_ATTACHMENT1); +} + +void Framebuffer::invalidate(GLenum target) { + static const std::array INVALIDATE_ATTACHMENTS {{ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }}; + glInvalidateFramebuffer(target, static_cast(INVALIDATE_ATTACHMENTS.size()), INVALIDATE_ATTACHMENTS.data()); +} + + +void Framebuffer::drawBuffers(ovrEye eye) const { + static const std::array, 3> EYE_DRAW_BUFFERS { { + {GL_COLOR_ATTACHMENT0, GL_NONE}, + {GL_NONE, GL_COLOR_ATTACHMENT1}, + {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1} + } }; + + switch(eye) { + case VRAPI_EYE_LEFT: + case VRAPI_EYE_RIGHT: + case VRAPI_EYE_COUNT: { + const auto& eyeDrawBuffers = EYE_DRAW_BUFFERS[eye]; + glDrawBuffers(static_cast(eyeDrawBuffers.size()), eyeDrawBuffers.data()); + } + break; + + default: + throw std::runtime_error("Invalid eye for drawBuffers"); + } +} + +void Framebuffer::SwapChainInfo::create(const glm::uvec2 &size) { + index = 0; + validTexture = false; + swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8, size.x, size.y, 1, 3); + length = vrapi_GetTextureSwapChainLength(swapChain); + if (!length) { + __android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures"); + throw std::runtime_error("Unable to create Oculus texture swap chain"); + } + + for (int i = 0; i < length; ++i) { + GLuint chainTexId = vrapi_GetTextureSwapChainHandle(swapChain, i); + glBindTexture(GL_TEXTURE_2D, chainTexId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + +void Framebuffer::SwapChainInfo::advance() { + index = (index + 1) % length; + validTexture = false; +} + +void Framebuffer::SwapChainInfo::bind(uint32_t target, uint32_t attachment) { + if (!validTexture) { + GLuint chainTexId = vrapi_GetTextureSwapChainHandle(swapChain, index); + glFramebufferTexture(target, attachment, chainTexId, 0); + validTexture = true; } } diff --git a/libraries/oculusMobile/src/ovr/Framebuffer.h b/libraries/oculusMobile/src/ovr/Framebuffer.h index 5127574462..4600d91534 100644 --- a/libraries/oculusMobile/src/ovr/Framebuffer.h +++ b/libraries/oculusMobile/src/ovr/Framebuffer.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -20,15 +21,28 @@ public: void create(const glm::uvec2& size); void advance(); void destroy(); - void bind(); + void bind(GLenum target = GL_DRAW_FRAMEBUFFER); + void invalidate(GLenum target = GL_DRAW_FRAMEBUFFER); + void drawBuffers(ovrEye eye) const; - uint32_t _depth { 0 }; + const glm::uvec2& size() const { return _size; } + +private: uint32_t _fbo{ 0 }; - int _length{ -1 }; - int _index{ -1 }; - bool _validTexture{ false }; glm::uvec2 _size; - ovrTextureSwapChain* _swapChain{ nullptr }; + struct SwapChainInfo { + int length{ -1 }; + int index{ -1 }; + bool validTexture{ false }; + ovrTextureSwapChain* swapChain{ nullptr }; + + void create(const glm::uvec2& size); + void destroy(); + void advance(); + void bind(GLenum target, GLenum attachment); + }; + + SwapChainInfo _swapChainInfos[VRAPI_FRAME_LAYER_EYE_MAX]; }; } // namespace ovr \ No newline at end of file diff --git a/libraries/oculusMobile/src/ovr/VrHandler.cpp b/libraries/oculusMobile/src/ovr/VrHandler.cpp index 6cc2ec9526..8748ec83cb 100644 --- a/libraries/oculusMobile/src/ovr/VrHandler.cpp +++ b/libraries/oculusMobile/src/ovr/VrHandler.cpp @@ -9,37 +9,186 @@ #include #include +#include +#include #include +#include +#include #include #include #include //#include + #include "GLContext.h" #include "Helpers.h" #include "Framebuffer.h" +static AAssetManager* ASSET_MANAGER = nullptr; +#define USE_BLIT_PRESENT 0 + +#if !USE_BLIT_PRESENT + + + +static std::string getTextAsset(const char* assetPath) { + if (!ASSET_MANAGER || !assetPath) { + return nullptr; + } + AAsset* asset = AAssetManager_open(ASSET_MANAGER, assetPath, AASSET_MODE_BUFFER); + if (!asset) { + return {}; + } + + auto length = AAsset_getLength(asset); + if (0 == length) { + AAsset_close(asset); + return {}; + } + + auto buffer = AAsset_getBuffer(asset); + if (!buffer) { + AAsset_close(asset); + return {}; + } + + std::string result { static_cast(buffer), static_cast(length) }; + AAsset_close(asset); + return result; +} + +static std::string getShaderInfoLog(GLuint glshader) { + std::string result; + GLint infoLength = 0; + glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength); + if (infoLength > 0) { + char* temp = new char[infoLength]; + glGetShaderInfoLog(glshader, infoLength, NULL, temp); + result = std::string(temp); + delete[] temp; + } + return result; +} + +static GLuint buildShader(GLenum shaderDomain, const char* shader) { + GLuint glshader = glCreateShader(shaderDomain); + if (!glshader) { + throw std::runtime_error("Bad shader"); + } + + glShaderSource(glshader, 1, &shader, NULL); + glCompileShader(glshader); + + GLint compiled = 0; + glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled); + + // if compilation fails + if (!compiled) { + std::string compileError = getShaderInfoLog(glshader); + glDeleteShader(glshader); + __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "Shader compile error: %s", compileError.c_str()); + return 0; + } + + return glshader; +} + +static std::string getProgramInfoLog(GLuint glprogram) { + std::string result; + GLint infoLength = 0; + glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength); + if (infoLength > 0) { + char* temp = new char[infoLength]; + glGetProgramInfoLog(glprogram, infoLength, NULL, temp); + result = std::string(temp); + delete[] temp; + } + return result; +} + +static GLuint buildProgram(const char* vertex, const char* fragment) { + // A brand new program: + GLuint glprogram { 0 }, glvertex { 0 }, glfragment { 0 }; + + try { + glprogram = glCreateProgram(); + if (0 == glprogram) { + throw std::runtime_error("Failed to create program, is GL context current?"); + } + + glvertex = buildShader(GL_VERTEX_SHADER, vertex); + if (0 == glvertex) { + throw std::runtime_error("Failed to create or compile vertex shader"); + } + glAttachShader(glprogram, glvertex); + + glfragment = buildShader(GL_FRAGMENT_SHADER, fragment); + if (0 == glfragment) { + throw std::runtime_error("Failed to create or compile fragment shader"); + } + glAttachShader(glprogram, glfragment); + + GLint linked { 0 }; + glLinkProgram(glprogram); + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + + if (!linked) { + std::string linkErrorLog = getProgramInfoLog(glprogram); + __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "Program link error: %s", linkErrorLog.c_str()); + throw std::runtime_error("Failed to link program, is the interface between the fragment and vertex shaders correct?"); + } + + + } catch(const std::runtime_error& error) { + if (0 != glprogram) { + glDeleteProgram(glprogram); + glprogram = 0; + } + } + + if (0 != glvertex) { + glDeleteShader(glvertex); + } + + if (0 != glfragment) { + glDeleteShader(glfragment); + } + + if (0 == glprogram) { + throw std::runtime_error("Failed to build program"); + } + + return glprogram; +} + +#endif using namespace ovr; static thread_local bool isRenderThread { false }; struct VrSurface : public TaskQueue { - using HandlerTask = VrHandler::HandlerTask; + using HandlerTask = ovr::VrHandler::HandlerTask; JavaVM* vm{nullptr}; jobject oculusActivity{ nullptr }; ANativeWindow* nativeWindow{ nullptr }; - VrHandler* handler{nullptr}; + ovr::VrHandler* handler{nullptr}; ovrMobile* session{nullptr}; bool resumed { false }; - GLContext vrglContext; - Framebuffer eyeFbos[2]; - uint32_t readFbo{0}; + ovr::GLContext vrglContext; + ovr::Framebuffer eyesFbo; + +#if USE_BLIT_PRESENT + GLuint readFbo { 0 }; +#else + GLuint renderProgram { 0 }; + GLuint renderVao { 0 }; +#endif std::atomic presentIndex{1}; double displayTime{0}; // Not currently set by anything @@ -76,6 +225,16 @@ struct VrSurface : public TaskQueue { vrglContext.create(currentDisplay, currentContext, noErrorContext); vrglContext.makeCurrent(); +#if USE_BLIT_PRESENT + glGenFramebuffers(1, &readFbo); +#else + glGenVertexArrays(1, &renderVao); + const char* vertex = nullptr; + auto vertexShader = getTextAsset("shaders/present.vert"); + auto fragmentShader = getTextAsset("shaders/present.frag"); + renderProgram = buildProgram(vertexShader.c_str(), fragmentShader.c_str()); +#endif + glm::uvec2 eyeTargetSize; withEnv([&](JNIEnv* env){ ovrJava java{ vm, env, oculusActivity }; @@ -85,10 +244,7 @@ struct VrSurface : public TaskQueue { }; }); __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "QQQ Eye Size %d, %d", eyeTargetSize.x, eyeTargetSize.y); - ovr::for_each_eye([&](ovrEye eye) { - eyeFbos[eye].create(eyeTargetSize); - }); - glGenFramebuffers(1, &readFbo); + eyesFbo.create(eyeTargetSize); vrglContext.doneCurrent(); } @@ -178,38 +334,51 @@ struct VrSurface : public TaskQueue { void presentFrame(uint32_t sourceTexture, const glm::uvec2 &sourceSize, const ovrTracking2& tracking) { ovrLayerProjection2 layer = vrapi_DefaultLayerProjection2(); layer.HeadPose = tracking.HeadPose; + + eyesFbo.bind(); if (sourceTexture) { + eyesFbo.invalidate(); +#if USE_BLIT_PRESENT glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo); glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, sourceTexture, 0); - GLenum framebufferStatus = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != framebufferStatus) { - __android_log_print(ANDROID_LOG_WARN, "QQQ_OVR", "incomplete framebuffer"); - } - } - GLenum invalidateAttachment = GL_COLOR_ATTACHMENT0; - - ovr::for_each_eye([&](ovrEye eye) { - const auto &eyeTracking = tracking.Eye[eye]; - auto &eyeFbo = eyeFbos[eye]; - const auto &destSize = eyeFbo._size; - eyeFbo.bind(); - glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &invalidateAttachment); - if (sourceTexture) { + const auto &destSize = eyesFbo.size(); + ovr::for_each_eye([&](ovrEye eye) { auto sourceWidth = sourceSize.x / 2; auto sourceX = (eye == VRAPI_EYE_LEFT) ? 0 : sourceWidth; + // Each eye blit uses a different draw buffer + eyesFbo.drawBuffers(eye); glBlitFramebuffer( sourceX, 0, sourceX + sourceWidth, sourceSize.y, 0, 0, destSize.x, destSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - eyeFbo.updateLayer(eye, layer, &eyeTracking.ProjectionMatrix); - eyeFbo.advance(); - }); - if (sourceTexture) { - glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &invalidateAttachment); + }); + static const std::array READ_INVALIDATE_ATTACHMENTS {{ GL_COLOR_ATTACHMENT0 }}; + glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, (GLuint)READ_INVALIDATE_ATTACHMENTS.size(), READ_INVALIDATE_ATTACHMENTS.data()); glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0); +#else + eyesFbo.drawBuffers(VRAPI_EYE_COUNT); + const auto &destSize = eyesFbo.size(); + glViewport(0, 0, destSize.x, destSize.y); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sourceTexture); + glBindVertexArray(renderVao); + glUseProgram(renderProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glUseProgram(0); + glBindVertexArray(0); +#endif + } else { + eyesFbo.drawBuffers(VRAPI_EYE_COUNT); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); } - glFlush(); + + ovr::for_each_eye([&](ovrEye eye) { + const auto &eyeTracking = tracking.Eye[eye]; + eyesFbo.updateLayer(eye, layer, &eyeTracking.ProjectionMatrix); + }); + + eyesFbo.advance(); ovrLayerHeader2 *layerHeader = &layer.Header; ovrSubmitFrameDescription2 frameDesc = {}; @@ -321,8 +490,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) { return JNI_VERSION_1_6; } -JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnCreate(JNIEnv* env, jobject obj) { +JNIEXPORT void JNICALL Java_io_highfidelity_oculus_OculusMobileActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject assetManager) { __android_log_write(ANDROID_LOG_WARN, "QQQ_JNI", __FUNCTION__); + ASSET_MANAGER = AAssetManager_fromJava(env, assetManager); SURFACE.onCreate(env, obj); } From 53b5a599b1c7600908ed5d41ddb27886c2eb814a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 15 Mar 2019 09:12:16 +1300 Subject: [PATCH 42/88] Reinstate avatar script type tag --- tools/jsdoc/plugins/hifi.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index b4350ddbdb..184da9b618 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -107,6 +107,9 @@ exports.handlers = { if (e.doclet.hifiClientEntity) { rows.push("Client Entity Scripts"); } + if (e.doclet.hifiAvatar) { + rows.push("Avatar Scripts"); + } if (e.doclet.hifiServerEntity) { rows.push("Server Entity Scripts"); } @@ -155,6 +158,13 @@ exports.defineTags = function (dictionary) { } }); + // @hifi-avatar-script + dictionary.defineTag("hifi-avatar", { + onTagged: function (doclet, tag) { + doclet.hifiAvatar = true; + } + }); + // @hifi-client-entity dictionary.defineTag("hifi-client-entity", { onTagged: function (doclet, tag) { From fd65f511408c55d616671de94a7ad60c9269f1d0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 14 Mar 2019 14:09:06 -0700 Subject: [PATCH 43/88] Fix gamma correction on Quest --- .../libraries/oculus/src/main/assets/shaders/present.frag | 8 +++++--- libraries/oculusMobile/src/ovr/Framebuffer.cpp | 4 +++- libraries/oculusMobile/src/ovr/VrHandler.cpp | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/android/libraries/oculus/src/main/assets/shaders/present.frag b/android/libraries/oculus/src/main/assets/shaders/present.frag index 4fbec70f57..f5d96932f8 100644 --- a/android/libraries/oculus/src/main/assets/shaders/present.frag +++ b/android/libraries/oculus/src/main/assets/shaders/present.frag @@ -30,8 +30,10 @@ vec4 color_LinearTosRGBA(vec4 lrgba) { return vec4(color_LinearTosRGB(lrgba.xyz), lrgba.w); } -// FIXME switch to texelfetch for getting from the source texture +// FIXME switch to texelfetch for getting from the source texture? void main() { - FragColorL = color_LinearTosRGBA(texture(sampler, vTexCoordLR.xy)); - FragColorR = color_LinearTosRGBA(texture(sampler, vTexCoordLR.zw)); + //FragColorL = color_LinearTosRGBA(texture(sampler, vTexCoordLR.xy)); + //FragColorR = color_LinearTosRGBA(texture(sampler, vTexCoordLR.zw)); + FragColorL = texture(sampler, vTexCoordLR.xy); + FragColorR = texture(sampler, vTexCoordLR.zw); } diff --git a/libraries/oculusMobile/src/ovr/Framebuffer.cpp b/libraries/oculusMobile/src/ovr/Framebuffer.cpp index 57c45d3159..a1dc1841de 100644 --- a/libraries/oculusMobile/src/ovr/Framebuffer.cpp +++ b/libraries/oculusMobile/src/ovr/Framebuffer.cpp @@ -100,7 +100,9 @@ void Framebuffer::drawBuffers(ovrEye eye) const { void Framebuffer::SwapChainInfo::create(const glm::uvec2 &size) { index = 0; validTexture = false; - swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_RGBA8, size.x, size.y, 1, 3); + // GL_SRGB8_ALPHA8 and GL_RGBA8 appear to behave the same here. The only thing that changes the + // output gamma behavior is VRAPI_MODE_FLAG_FRONT_BUFFER_SRGB passed to vrapi_EnterVrMode + swapChain = vrapi_CreateTextureSwapChain3(VRAPI_TEXTURE_TYPE_2D, GL_SRGB8_ALPHA8, size.x, size.y, 1, 3); length = vrapi_GetTextureSwapChainLength(swapChain); if (!length) { __android_log_write(ANDROID_LOG_WARN, "QQQ_OVR", "Unable to count swap chain textures"); diff --git a/libraries/oculusMobile/src/ovr/VrHandler.cpp b/libraries/oculusMobile/src/ovr/VrHandler.cpp index 8748ec83cb..a7b0f9f8ee 100644 --- a/libraries/oculusMobile/src/ovr/VrHandler.cpp +++ b/libraries/oculusMobile/src/ovr/VrHandler.cpp @@ -313,6 +313,7 @@ struct VrSurface : public TaskQueue { ovrJava java{ vm, env, oculusActivity }; ovrModeParms modeParms = vrapi_DefaultModeParms(&java); modeParms.Flags |= VRAPI_MODE_FLAG_NATIVE_WINDOW; + modeParms.Flags |= VRAPI_MODE_FLAG_FRONT_BUFFER_SRGB; if (noErrorContext) { modeParms.Flags |= VRAPI_MODE_FLAG_CREATE_CONTEXT_NO_ERROR; } From f9f2b6f8ac5220068c3ec68d66f53e1cb1b981f2 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 14 Mar 2019 15:03:33 -0700 Subject: [PATCH 44/88] avatar exporter 0.3.4/0.3.5 changes to master --- .../{ => AvatarExporter}/AvatarExporter.cs | 767 ++++++--- .../Assets/Editor/AvatarExporter/Average.mat | 76 + .../Assets/Editor/AvatarExporter/Floor.mat | 76 + .../AvatarExporter/HeightReference.prefab | 1393 +++++++++++++++++ .../Assets/Editor/AvatarExporter/Line.mat | 76 + .../Editor/AvatarExporter/ShortOrTall.mat | 76 + .../Editor/AvatarExporter/TooShortOrTall.mat | 76 + tools/unity-avatar-exporter/Assets/README.txt | 13 +- .../avatarExporter.unitypackage | Bin 16045 -> 74582 bytes 9 files changed, 2296 insertions(+), 257 deletions(-) rename tools/unity-avatar-exporter/Assets/Editor/{ => AvatarExporter}/AvatarExporter.cs (67%) create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Average.mat create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Floor.mat create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Line.mat create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/ShortOrTall.mat create mode 100644 tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/TooShortOrTall.mat diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs similarity index 67% rename from tools/unity-avatar-exporter/Assets/Editor/AvatarExporter.cs rename to tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index c25a962824..142e4ae35a 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -6,15 +6,18 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -using UnityEngine; using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; using System; -using System.IO; using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; class AvatarExporter : MonoBehaviour { // update version number for every PR that changes this file, also set updated version in README file - static readonly string AVATAR_EXPORTER_VERSION = "0.3.3"; + static readonly string AVATAR_EXPORTER_VERSION = "0.3.5"; static readonly float HIPS_GROUND_MIN_Y = 0.01f; static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f; @@ -22,6 +25,9 @@ class AvatarExporter : MonoBehaviour { static readonly string EMPTY_WARNING_TEXT = "None"; static readonly string TEXTURES_DIRECTORY = "textures"; static readonly string DEFAULT_MATERIAL_NAME = "No Name"; + static readonly string HEIGHT_REFERENCE_PREFAB = "Assets/Editor/AvatarExporter/HeightReference.prefab"; + static readonly Vector3 PREVIEW_CAMERA_PIVOT = new Vector3(0.0f, 1.755f, 0.0f); + static readonly Vector3 PREVIEW_CAMERA_DIRECTION = new Vector3(0.0f, 0.0f, -1.0f); // TODO: use regex static readonly string[] RECOMMENDED_UNITY_VERSIONS = new string[] { @@ -298,18 +304,17 @@ class AvatarExporter : MonoBehaviour { if (!string.IsNullOrEmpty(occlusionMap)) { json += "\"occlusionMap\": \"" + occlusionMap + "\", "; } - json += "\"emissive\": [" + emissive.r + ", " + emissive.g + ", " + emissive.b + "] "; + json += "\"emissive\": [" + emissive.r + ", " + emissive.g + ", " + emissive.b + "]"; if (!string.IsNullOrEmpty(emissiveMap)) { - json += "\", emissiveMap\": \"" + emissiveMap + "\""; + json += ", \"emissiveMap\": \"" + emissiveMap + "\""; } - json += "} }"; + json += " } }"; return json; } } static string assetPath = ""; - static string assetName = ""; - + static string assetName = ""; static ModelImporter modelImporter; static HumanDescription humanDescription; @@ -317,12 +322,23 @@ class AvatarExporter : MonoBehaviour { static Dictionary humanoidToUserBoneMappings = new Dictionary(); static BoneTreeNode userBoneTree = new BoneTreeNode(); static Dictionary failedAvatarRules = new Dictionary(); + static string warnings = ""; static Dictionary textureDependencies = new Dictionary(); static Dictionary materialMappings = new Dictionary(); static Dictionary materialDatas = new Dictionary(); - static List materialAlternateStandardShader = new List(); - static Dictionary materialUnsupportedShader = new Dictionary(); + static List alternateStandardShaderMaterials = new List(); + static List unsupportedShaderMaterials = new List(); + + static Scene previewScene; + static string previousScene = ""; + static Vector3 previousScenePivot = Vector3.zero; + static Quaternion previousSceneRotation = Quaternion.identity; + static float previousSceneSize = 0.0f; + static bool previousSceneOrthographic = false; + static UnityEngine.Object avatarResource; + static GameObject avatarPreviewObject; + static GameObject heightReferenceObject; [MenuItem("High Fidelity/Export New Avatar")] static void ExportNewAvatar() { @@ -339,8 +355,8 @@ class AvatarExporter : MonoBehaviour { EditorUtility.DisplayDialog("About", "High Fidelity, Inc.\nAvatar Exporter\nVersion " + AVATAR_EXPORTER_VERSION, "Ok"); } - static void ExportSelectedAvatar(bool updateAvatar) { - // ensure everything is saved to file before exporting + static void ExportSelectedAvatar(bool updateExistingAvatar) { + // ensure everything is saved to file before doing anything AssetDatabase.SaveAssets(); string[] guids = Selection.assetGUIDs; @@ -364,6 +380,11 @@ class AvatarExporter : MonoBehaviour { " the Rig section of it's Inspector window.", "Ok"); return; } + + avatarResource = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); + humanDescription = modelImporter.humanDescription; + + string textureWarnings = SetTextureDependencies(); // if the rig is optimized we should de-optimize it during the export process bool shouldDeoptimizeGameObjects = modelImporter.optimizeGameObjects; @@ -371,28 +392,23 @@ class AvatarExporter : MonoBehaviour { modelImporter.optimizeGameObjects = false; modelImporter.SaveAndReimport(); } - - humanDescription = modelImporter.humanDescription; - string textureWarnings = SetTextureDependencies(); + SetBoneAndMaterialInformation(); + if (shouldDeoptimizeGameObjects) { + // switch back to optimized game object in case it was originally optimized + modelImporter.optimizeGameObjects = true; + modelImporter.SaveAndReimport(); + } + // check if we should be substituting a bone for a missing UpperChest mapping AdjustUpperChestMapping(); // format resulting avatar rule failure strings // consider export-blocking avatar rules to be errors and show them in an error dialog, // and also include any other avatar rule failures plus texture warnings as warnings in the dialog - if (shouldDeoptimizeGameObjects) { - // switch back to optimized game object in case it was originally optimized - modelImporter.optimizeGameObjects = true; - modelImporter.SaveAndReimport(); - } - - // format resulting bone rule failure strings - // consider export-blocking bone rules to be errors and show them in an error dialog, - // and also include any other bone rule failures plus texture warnings as warnings in the dialog string boneErrors = ""; - string warnings = ""; + warnings = ""; foreach (var failedAvatarRule in failedAvatarRules) { if (Array.IndexOf(EXPORT_BLOCKING_AVATAR_RULES, failedAvatarRule.Key) >= 0) { boneErrors += failedAvatarRule.Value + "\n\n"; @@ -400,15 +416,16 @@ class AvatarExporter : MonoBehaviour { warnings += failedAvatarRule.Value + "\n\n"; } } - foreach (string materialName in materialAlternateStandardShader) { - warnings += "The material " + materialName + " is not using the recommended variation of the Standard shader. " + - "We recommend you change it to Standard (Roughness setup) shader for improved performance.\n\n"; - } - foreach (var material in materialUnsupportedShader) { - warnings += "The material " + material.Key + " is using an unsupported shader " + material.Value + - ". Please change it to a Standard shader type.\n\n"; - } + + // add material and texture warnings after bone-related warnings + AddMaterialWarnings(); warnings += textureWarnings; + + // remove trailing newlines at the end of the warnings + if (!string.IsNullOrEmpty(warnings)) { + warnings = warnings.Substring(0, warnings.LastIndexOf("\n\n")); + } + if (!string.IsNullOrEmpty(boneErrors)) { // if there are both errors and warnings then warnings will be displayed with errors in the error dialog if (!string.IsNullOrEmpty(warnings)) { @@ -421,150 +438,157 @@ class AvatarExporter : MonoBehaviour { return; } + // since there are no errors we can now open the preview scene in place of the user's scene + if (!OpenPreviewScene()) { + return; + } + + // show None instead of blank warnings if there are no warnings in the export windows + if (string.IsNullOrEmpty(warnings)) { + warnings = EMPTY_WARNING_TEXT; + } + string documentsFolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); string hifiFolder = documentsFolder + "\\High Fidelity Projects"; - if (updateAvatar) { // Update Existing Avatar menu option - bool copyModelToExport = false; + if (updateExistingAvatar) { // Update Existing Avatar menu option + // open update existing project popup window including project to update, scale, and warnings + // default the initial file chooser location to HiFi projects folder in user documents folder + ExportProjectWindow window = ScriptableObject.CreateInstance(); string initialPath = Directory.Exists(hifiFolder) ? hifiFolder : documentsFolder; - - // open file explorer defaulting to hifi projects folder in user documents to select target fst to update - string exportFstPath = EditorUtility.OpenFilePanel("Select .fst to update", initialPath, "fst"); - if (exportFstPath.Length == 0) { // file selection cancelled - return; - } - exportFstPath = exportFstPath.Replace('/', '\\'); - - // lookup the project name field from the fst file to update - string projectName = ""; - try { - string[] lines = File.ReadAllLines(exportFstPath); - foreach (string line in lines) { - int separatorIndex = line.IndexOf("="); - if (separatorIndex >= 0) { - string key = line.Substring(0, separatorIndex).Trim(); - if (key == "name") { - projectName = line.Substring(separatorIndex + 1).Trim(); - break; - } - } - } - } catch { - EditorUtility.DisplayDialog("Error", "Failed to read from existing file " + exportFstPath + - ". Please check the file and try again.", "Ok"); - return; - } - - string exportModelPath = Path.GetDirectoryName(exportFstPath) + "\\" + assetName + ".fbx"; - if (File.Exists(exportModelPath)) { - // if the fbx in Unity Assets is newer than the fbx in the target export - // folder or vice-versa then ask to replace the older fbx with the newer fbx - DateTime assetModelWriteTime = File.GetLastWriteTime(assetPath); - DateTime targetModelWriteTime = File.GetLastWriteTime(exportModelPath); - if (assetModelWriteTime > targetModelWriteTime) { - int option = EditorUtility.DisplayDialogComplex("Error", "The " + assetName + - ".fbx model in the Unity Assets folder is newer than the " + exportModelPath + - " model.\n\nDo you want to replace the older .fbx with the newer .fbx?", - "Yes", "No", "Cancel"); - if (option == 2) { // Cancel - return; - } - copyModelToExport = option == 0; // Yes - } else if (assetModelWriteTime < targetModelWriteTime) { - int option = EditorUtility.DisplayDialogComplex("Error", "The " + exportModelPath + - " model is newer than the " + assetName + ".fbx model in the Unity Assets folder." + - "\n\nDo you want to replace the older .fbx with the newer .fbx and re-import it?", - "Yes", "No" , "Cancel"); - if (option == 2) { // Cancel - return; - } else if (option == 0) { // Yes - copy model to Unity project - // copy the fbx from the project folder to Unity Assets, overwriting the existing fbx, and re-import it - try { - File.Copy(exportModelPath, assetPath, true); - } catch { - EditorUtility.DisplayDialog("Error", "Failed to copy existing file " + exportModelPath + " to " + assetPath + - ". Please check the location and try again.", "Ok"); - return; - } - AssetDatabase.ImportAsset(assetPath); - - // set model to Humanoid animation type and force another refresh on it to process Humanoid - modelImporter = ModelImporter.GetAtPath(assetPath) as ModelImporter; - modelImporter.animationType = ModelImporterAnimationType.Human; - EditorUtility.SetDirty(modelImporter); - modelImporter.SaveAndReimport(); - - // redo parent names, joint mappings, and user bone positions due to the fbx change - // as well as re-check the avatar rules for failures - humanDescription = modelImporter.humanDescription; - SetBoneAndMaterialInformation(); - } - } - } else { - // if no matching fbx exists in the target export folder then ask to copy fbx over - int option = EditorUtility.DisplayDialogComplex("Error", "There is no existing " + exportModelPath + - " model.\n\nDo you want to copy over the " + assetName + - ".fbx model from the Unity Assets folder?", "Yes", "No", "Cancel"); - if (option == 2) { // Cancel - return; - } - copyModelToExport = option == 0; // Yes - } - - // copy asset fbx over deleting any existing fbx if we agreed to overwrite it - if (copyModelToExport) { - try { - File.Copy(assetPath, exportModelPath, true); - } catch { - EditorUtility.DisplayDialog("Error", "Failed to copy existing file " + assetPath + " to " + exportModelPath + - ". Please check the location and try again.", "Ok"); - return; - } - } - - // delete existing fst file since we will write a new file - // TODO: updating fst should only rewrite joint mappings and joint rotation offsets to existing file - try { - File.Delete(exportFstPath); - } catch { - EditorUtility.DisplayDialog("Error", "Failed to overwrite existing file " + exportFstPath + - ". Please check the file and try again.", "Ok"); - return; - } - - // write out a new fst file in place of the old file - if (!WriteFST(exportFstPath, projectName)) { - return; - } - - // copy any external texture files to the project's texture directory that are considered dependencies of the model - string texturesDirectory = GetTextureDirectory(exportFstPath); - if (!CopyExternalTextures(texturesDirectory)) { - return; - } - - // display success dialog with any avatar rule warnings - string successDialog = "Avatar successfully updated!"; - if (!string.IsNullOrEmpty(warnings)) { - successDialog += "\n\nWarnings:\n" + warnings; - } - EditorUtility.DisplayDialog("Success!", successDialog, "Ok"); + window.Init(initialPath, warnings, updateExistingAvatar, avatarPreviewObject, OnUpdateExistingProject, OnExportWindowClose); } else { // Export New Avatar menu option // create High Fidelity Projects folder in user documents folder if it doesn't exist if (!Directory.Exists(hifiFolder)) { Directory.CreateDirectory(hifiFolder); } - if (string.IsNullOrEmpty(warnings)) { - warnings = EMPTY_WARNING_TEXT; - } - - // open a popup window to enter new export project name and project location + // open export new project popup window including project name, project location, scale, and warnings + // default the initial project location path to the High Fidelity Projects folder above ExportProjectWindow window = ScriptableObject.CreateInstance(); - window.Init(hifiFolder, warnings, OnExportProjectWindowClose); + window.Init(hifiFolder, warnings, updateExistingAvatar, avatarPreviewObject, OnExportNewProject, OnExportWindowClose); } } - static void OnExportProjectWindowClose(string projectDirectory, string projectName, string warnings) { + static void OnUpdateExistingProject(string exportFstPath, string projectName, float scale) { + bool copyModelToExport = false; + + // lookup the project name field from the fst file to update + projectName = ""; + try { + string[] lines = File.ReadAllLines(exportFstPath); + foreach (string line in lines) { + int separatorIndex = line.IndexOf("="); + if (separatorIndex >= 0) { + string key = line.Substring(0, separatorIndex).Trim(); + if (key == "name") { + projectName = line.Substring(separatorIndex + 1).Trim(); + break; + } + } + } + } catch { + EditorUtility.DisplayDialog("Error", "Failed to read from existing file " + exportFstPath + + ". Please check the file and try again.", "Ok"); + return; + } + + string exportModelPath = Path.GetDirectoryName(exportFstPath) + "\\" + assetName + ".fbx"; + if (File.Exists(exportModelPath)) { + // if the fbx in Unity Assets is newer than the fbx in the target export + // folder or vice-versa then ask to replace the older fbx with the newer fbx + DateTime assetModelWriteTime = File.GetLastWriteTime(assetPath); + DateTime targetModelWriteTime = File.GetLastWriteTime(exportModelPath); + if (assetModelWriteTime > targetModelWriteTime) { + int option = EditorUtility.DisplayDialogComplex("Error", "The " + assetName + + ".fbx model in the Unity Assets folder is newer than the " + exportModelPath + + " model.\n\nDo you want to replace the older .fbx with the newer .fbx?", + "Yes", "No", "Cancel"); + if (option == 2) { // Cancel + return; + } + copyModelToExport = option == 0; // Yes + } else if (assetModelWriteTime < targetModelWriteTime) { + int option = EditorUtility.DisplayDialogComplex("Error", "The " + exportModelPath + + " model is newer than the " + assetName + ".fbx model in the Unity Assets folder." + + "\n\nDo you want to replace the older .fbx with the newer .fbx and re-import it?", + "Yes", "No" , "Cancel"); + if (option == 2) { // Cancel + return; + } else if (option == 0) { // Yes - copy model to Unity project + // copy the fbx from the project folder to Unity Assets, overwriting the existing fbx, and re-import it + try { + File.Copy(exportModelPath, assetPath, true); + } catch { + EditorUtility.DisplayDialog("Error", "Failed to copy existing file " + exportModelPath + " to " + assetPath + + ". Please check the location and try again.", "Ok"); + return; + } + AssetDatabase.ImportAsset(assetPath); + + // set model to Humanoid animation type and force another refresh on it to process Humanoid + modelImporter = ModelImporter.GetAtPath(assetPath) as ModelImporter; + modelImporter.animationType = ModelImporterAnimationType.Human; + EditorUtility.SetDirty(modelImporter); + modelImporter.SaveAndReimport(); + + // redo parent names, joint mappings, and user bone positions due to the fbx change + // as well as re-check the avatar rules for failures + humanDescription = modelImporter.humanDescription; + SetBoneAndMaterialInformation(); + } + } + } else { + // if no matching fbx exists in the target export folder then ask to copy fbx over + int option = EditorUtility.DisplayDialogComplex("Error", "There is no existing " + exportModelPath + + " model.\n\nDo you want to copy over the " + assetName + + ".fbx model from the Unity Assets folder?", "Yes", "No", "Cancel"); + if (option == 2) { // Cancel + return; + } + copyModelToExport = option == 0; // Yes + } + + // copy asset fbx over deleting any existing fbx if we agreed to overwrite it + if (copyModelToExport) { + try { + File.Copy(assetPath, exportModelPath, true); + } catch { + EditorUtility.DisplayDialog("Error", "Failed to copy existing file " + assetPath + " to " + exportModelPath + + ". Please check the location and try again.", "Ok"); + return; + } + } + + // delete existing fst file since we will write a new file + // TODO: updating fst should only rewrite joint mappings and joint rotation offsets to existing file + try { + File.Delete(exportFstPath); + } catch { + EditorUtility.DisplayDialog("Error", "Failed to overwrite existing file " + exportFstPath + + ". Please check the file and try again.", "Ok"); + return; + } + + // write out a new fst file in place of the old file + if (!WriteFST(exportFstPath, projectName, scale)) { + return; + } + + // copy any external texture files to the project's texture directory that are considered dependencies of the model + string texturesDirectory = GetTextureDirectory(exportFstPath); + if (!CopyExternalTextures(texturesDirectory)) { + return; + } + + // display success dialog with any avatar rule warnings + string successDialog = "Avatar successfully updated!"; + if (!string.IsNullOrEmpty(warnings)) { + successDialog += "\n\nWarnings:\n" + warnings; + } + EditorUtility.DisplayDialog("Success!", successDialog, "Ok"); + } + + static void OnExportNewProject(string projectDirectory, string projectName, float scale) { // copy the fbx from the Unity Assets folder to the project directory string exportModelPath = projectDirectory + assetName + ".fbx"; File.Copy(assetPath, exportModelPath); @@ -577,7 +601,7 @@ class AvatarExporter : MonoBehaviour { // write out the avatar.fst file to the project directory string exportFstPath = projectDirectory + "avatar.fst"; - if (!WriteFST(exportFstPath, projectName)) { + if (!WriteFST(exportFstPath, projectName, scale)) { return; } @@ -592,16 +616,27 @@ class AvatarExporter : MonoBehaviour { if (warnings != EMPTY_WARNING_TEXT) { successDialog += "Warnings:\n" + warnings; } - successDialog += "Note: If you are using any external textures with your model, " + + successDialog += "\n\nNote: If you are using any external textures with your model, " + "please ensure those textures are copied to " + texturesDirectory; EditorUtility.DisplayDialog("Success!", successDialog, "Ok"); } + + static void OnExportWindowClose() { + // close the preview avatar scene and go back to user's previous scene when export project windows close + ClosePreviewScene(); + } - static bool WriteFST(string exportFstPath, string projectName) { + // The High Fidelity FBX Serializer omits the colon based prefixes. This will make the jointnames compatible. + static string removeTypeFromJointname(string jointName) { + return jointName.Substring(jointName.IndexOf(':') + 1); + } + + static bool WriteFST(string exportFstPath, string projectName, float scale) { // write out core fields to top of fst file try { - File.WriteAllText(exportFstPath, "name = " + projectName + "\ntype = body+head\nscale = 1\nfilename = " + - assetName + ".fbx\n" + "texdir = textures\n"); + File.WriteAllText(exportFstPath, "exporterVersion = " + AVATAR_EXPORTER_VERSION + "\nname = " + projectName + + "\ntype = body+head\nscale = " + scale + "\nfilename = " + assetName + + ".fbx\n" + "texdir = textures\n"); } catch { EditorUtility.DisplayDialog("Error", "Failed to write file " + exportFstPath + ". Please check the location and try again.", "Ok"); @@ -612,7 +647,7 @@ class AvatarExporter : MonoBehaviour { foreach (var userBoneInfo in userBoneInfos) { if (userBoneInfo.Value.HasHumanMapping()) { string hifiJointName = HUMANOID_TO_HIFI_JOINT_NAME[userBoneInfo.Value.humanName]; - File.AppendAllText(exportFstPath, "jointMap = " + hifiJointName + " = " + userBoneInfo.Key + "\n"); + File.AppendAllText(exportFstPath, "jointMap = " + hifiJointName + " = " + removeTypeFromJointname(userBoneInfo.Key) + "\n"); } } @@ -653,7 +688,7 @@ class AvatarExporter : MonoBehaviour { // swap from left-handed (Unity) to right-handed (HiFi) coordinates and write out joint rotation offset to fst jointOffset = new Quaternion(-jointOffset.x, jointOffset.y, jointOffset.z, -jointOffset.w); - File.AppendAllText(exportFstPath, "jointRotationOffset2 = " + userBoneName + " = (" + jointOffset.x + ", " + + File.AppendAllText(exportFstPath, "jointRotationOffset2 = " + removeTypeFromJointname(userBoneName) + " = (" + jointOffset.x + ", " + jointOffset.y + ", " + jointOffset.z + ", " + jointOffset.w + ")\n"); } @@ -690,14 +725,13 @@ class AvatarExporter : MonoBehaviour { userBoneTree = new BoneTreeNode(); materialDatas.Clear(); - materialAlternateStandardShader.Clear(); - materialUnsupportedShader.Clear(); - + alternateStandardShaderMaterials.Clear(); + unsupportedShaderMaterials.Clear(); + SetMaterialMappings(); - - // instantiate a game object of the user avatar to traverse the bone tree to gather - // bone parents and positions as well as build a bone tree, then destroy it - UnityEngine.Object avatarResource = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); + + // instantiate a game object of the user avatar to traverse the bone tree to gather + // bone parents and positions as well as build a bone tree, then destroy it GameObject assetGameObject = (GameObject)Instantiate(avatarResource); TraverseUserBoneTree(assetGameObject.transform); DestroyImmediate(assetGameObject); @@ -732,8 +766,8 @@ class AvatarExporter : MonoBehaviour { bool light = gameObject.GetComponent() != null; bool camera = gameObject.GetComponent() != null; - // if this is a mesh and the model is using external materials then store its material data to be exported - if (mesh && modelImporter.materialLocation == ModelImporterMaterialLocation.External) { + // if this is a mesh then store its material data to be exported if the material is mapped to an fbx material name + if (mesh) { Material[] materials = skinnedMeshRenderer != null ? skinnedMeshRenderer.sharedMaterials : meshRenderer.sharedMaterials; StoreMaterialData(materials); } else if (!light && !camera) { @@ -959,7 +993,8 @@ class AvatarExporter : MonoBehaviour { string userBoneName = ""; // avatar rule fails if bone is not mapped in Humanoid if (!humanoidToUserBoneMappings.TryGetValue(humanBoneName, out userBoneName)) { - failedAvatarRules.Add(avatarRule, "There is no " + humanBoneName + " bone mapped in Humanoid for the selected avatar."); + failedAvatarRules.Add(avatarRule, "There is no " + humanBoneName + + " bone mapped in Humanoid for the selected avatar."); } return userBoneName; } @@ -1072,11 +1107,11 @@ class AvatarExporter : MonoBehaviour { // don't store any material data for unsupported shader types if (Array.IndexOf(SUPPORTED_SHADERS, shaderName) == -1) { - if (!materialUnsupportedShader.ContainsKey(materialName)) { - materialUnsupportedShader.Add(materialName, shaderName); + if (!unsupportedShaderMaterials.Contains(materialName)) { + unsupportedShaderMaterials.Add(materialName); } continue; - } + } MaterialData materialData = new MaterialData(); materialData.albedo = material.GetColor("_Color"); @@ -1100,18 +1135,19 @@ class AvatarExporter : MonoBehaviour { // for non-roughness Standard shaders give a warning that is not the recommended Standard shader, // and invert smoothness for roughness if (shaderName == STANDARD_SHADER || shaderName == STANDARD_SPECULAR_SHADER) { - if (!materialAlternateStandardShader.Contains(materialName)) { - materialAlternateStandardShader.Add(materialName); + if (!alternateStandardShaderMaterials.Contains(materialName)) { + alternateStandardShaderMaterials.Add(materialName); } materialData.roughness = 1.0f - materialData.roughness; } - - // remap the material name from the Unity material name to the fbx material name that it overrides - if (materialMappings.ContainsKey(materialName)) { - materialName = materialMappings[materialName]; - } - if (!materialDatas.ContainsKey(materialName)) { - materialDatas.Add(materialName, materialData); + + // store the material data under each fbx material name that it overrides from the material mapping + foreach (var materialMapping in materialMappings) { + string fbxMaterialName = materialMapping.Key; + string unityMaterialName = materialMapping.Value; + if (unityMaterialName == materialName && !materialDatas.ContainsKey(fbxMaterialName)) { + materialDatas.Add(fbxMaterialName, materialData); + } } } } @@ -1136,20 +1172,110 @@ class AvatarExporter : MonoBehaviour { static void SetMaterialMappings() { materialMappings.Clear(); - // store the mappings from fbx material name to the Unity material name overriding it using external fbx mapping + // store the mappings from fbx material name to the Unity Material name that overrides it using external fbx mapping var objectMap = modelImporter.GetExternalObjectMap(); foreach (var mapping in objectMap) { var material = mapping.Value as UnityEngine.Material; if (material != null) { - materialMappings.Add(material.name, mapping.Key.name); + materialMappings.Add(mapping.Key.name, material.name); } } } + + static void AddMaterialWarnings() { + string alternateStandardShaders = ""; + string unsupportedShaders = ""; + // combine all material names for each material warning into a comma-separated string + foreach (string materialName in alternateStandardShaderMaterials) { + if (!string.IsNullOrEmpty(alternateStandardShaders)) { + alternateStandardShaders += ", "; + } + alternateStandardShaders += materialName; + } + foreach (string materialName in unsupportedShaderMaterials) { + if (!string.IsNullOrEmpty(unsupportedShaders)) { + unsupportedShaders += ", "; + } + unsupportedShaders += materialName; + } + if (alternateStandardShaderMaterials.Count > 1) { + warnings += "The materials " + alternateStandardShaders + " are not using the " + + "recommended variation of the Standard shader. We recommend you change " + + "them to Standard (Roughness setup) shader for improved performance.\n\n"; + } else if (alternateStandardShaderMaterials.Count == 1) { + warnings += "The material " + alternateStandardShaders + " is not using the " + + "recommended variation of the Standard shader. We recommend you change " + + "it to Standard (Roughness setup) shader for improved performance.\n\n"; + } + if (unsupportedShaderMaterials.Count > 1) { + warnings += "The materials " + unsupportedShaders + " are using an unsupported shader. " + + "Please change them to a Standard shader type.\n\n"; + } else if (unsupportedShaderMaterials.Count == 1) { + warnings += "The material " + unsupportedShaders + " is using an unsupported shader. " + + "Please change it to a Standard shader type.\n\n"; + } + } + + static bool OpenPreviewScene() { + // see if the user wants to save their current scene before opening preview avatar scene in place of user's scene + if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { + return false; + } + + // store the user's current scene to re-open when done and open a new default scene in place of the user's scene + previousScene = EditorSceneManager.GetActiveScene().path; + previewScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene); + + // instantiate a game object to preview the avatar and a game object for the height reference prefab at 0, 0, 0 + UnityEngine.Object heightReferenceResource = AssetDatabase.LoadAssetAtPath(HEIGHT_REFERENCE_PREFAB, typeof(UnityEngine.Object)); + avatarPreviewObject = (GameObject)Instantiate(avatarResource, Vector3.zero, Quaternion.identity); + heightReferenceObject = (GameObject)Instantiate(heightReferenceResource, Vector3.zero, Quaternion.identity); + + // store the camera pivot and rotation from the user's last scene to be restored later + // replace the camera pivot and rotation to point at the preview avatar object in the -Z direction (facing front of it) + var sceneView = SceneView.lastActiveSceneView; + if (sceneView != null) { + previousScenePivot = sceneView.pivot; + previousSceneRotation = sceneView.rotation; + previousSceneSize = sceneView.size; + previousSceneOrthographic = sceneView.orthographic; + sceneView.pivot = PREVIEW_CAMERA_PIVOT; + sceneView.rotation = Quaternion.LookRotation(PREVIEW_CAMERA_DIRECTION); + sceneView.orthographic = true; + sceneView.size = 5.0f; + } + + return true; + } + + static void ClosePreviewScene() { + // destroy the avatar and height reference game objects closing the scene + DestroyImmediate(avatarPreviewObject); + DestroyImmediate(heightReferenceObject); + + // re-open the scene the user had open before switching to the preview scene + if (!string.IsNullOrEmpty(previousScene)) { + EditorSceneManager.OpenScene(previousScene); + } + + // close the preview scene and flag it to be removed + EditorSceneManager.CloseScene(previewScene, true); + + // restore the camera pivot and rotation to the user's previous scene settings + var sceneView = SceneView.lastActiveSceneView; + if (sceneView != null) { + sceneView.pivot = previousScenePivot; + sceneView.rotation = previousSceneRotation; + sceneView.size = previousSceneSize; + sceneView.orthographic = previousSceneOrthographic; + } + } } class ExportProjectWindow : EditorWindow { const int WINDOW_WIDTH = 500; - const int WINDOW_HEIGHT = 460; + const int EXPORT_NEW_WINDOW_HEIGHT = 520; + const int UPDATE_EXISTING_WINDOW_HEIGHT = 465; const int BUTTON_FONT_SIZE = 16; const int LABEL_FONT_SIZE = 16; const int TEXT_FIELD_FONT_SIZE = 14; @@ -1157,28 +1283,62 @@ class ExportProjectWindow : EditorWindow { const int ERROR_FONT_SIZE = 12; const int WARNING_SCROLL_HEIGHT = 170; const string EMPTY_ERROR_TEXT = "None\n"; - + const int SLIDER_WIDTH = 340; + const int SCALE_TEXT_WIDTH = 60; + const float MIN_SCALE_SLIDER = 0.0f; + const float MAX_SCALE_SLIDER = 2.0f; + const int SLIDER_SCALE_EXPONENT = 10; + const float ACTUAL_SCALE_OFFSET = 1.0f; + const float DEFAULT_AVATAR_HEIGHT = 1.755f; + const float MAXIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; + const float MINIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f; + readonly Color COLOR_YELLOW = Color.yellow; //new Color(0.9176f, 0.8274f, 0.0f); + readonly Color COLOR_BACKGROUND = new Color(0.5f, 0.5f, 0.5f); + + GameObject avatarPreviewObject; + bool updateExistingAvatar = false; string projectName = ""; string projectLocation = ""; + string initialProjectLocation = ""; string projectDirectory = ""; string errorText = EMPTY_ERROR_TEXT; - string warningText = ""; + string warningText = "\n"; Vector2 warningScrollPosition = new Vector2(0, 0); + string scaleWarningText = ""; + float sliderScale = 0.30103f; - public delegate void OnCloseDelegate(string projectDirectory, string projectName, string warnings); + public delegate void OnExportDelegate(string projectDirectory, string projectName, float scale); + OnExportDelegate onExportCallback; + + public delegate void OnCloseDelegate(); OnCloseDelegate onCloseCallback; - public void Init(string initialPath, string warnings, OnCloseDelegate closeCallback) { - minSize = new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT); - maxSize = new Vector2(WINDOW_WIDTH, WINDOW_HEIGHT); - titleContent.text = "Export New Avatar"; - projectLocation = initialPath; + public void Init(string initialPath, string warnings, bool updateExisting, GameObject avatarObject, + OnExportDelegate exportCallback, OnCloseDelegate closeCallback) { + updateExistingAvatar = updateExisting; + float windowHeight = updateExistingAvatar ? UPDATE_EXISTING_WINDOW_HEIGHT : EXPORT_NEW_WINDOW_HEIGHT; + minSize = new Vector2(WINDOW_WIDTH, windowHeight); + maxSize = new Vector2(WINDOW_WIDTH, windowHeight); + avatarPreviewObject = avatarObject; + titleContent.text = updateExistingAvatar ? "Update Existing Avatar" : "Export New Avatar"; + initialProjectLocation = initialPath; + projectLocation = updateExistingAvatar ? "" : initialProjectLocation; warningText = warnings; + onExportCallback = exportCallback; onCloseCallback = closeCallback; + ShowUtility(); + + // if the avatar's starting height is outside of the recommended ranges, auto-adjust the scale to default height + float height = GetAvatarHeight(); + if (height < MINIMUM_RECOMMENDED_HEIGHT || height > MAXIMUM_RECOMMENDED_HEIGHT) { + float newScale = DEFAULT_AVATAR_HEIGHT / height; + SetAvatarScale(newScale); + scaleWarningText = "Avatar's scale automatically adjusted to be within the recommended range."; + } } - void OnGUI() { + void OnGUI() { // define UI styles for all GUI elements to be created GUIStyle buttonStyle = new GUIStyle(GUI.skin.button); buttonStyle.fontSize = BUTTON_FONT_SIZE; @@ -1192,35 +1352,82 @@ class ExportProjectWindow : EditorWindow { errorStyle.normal.textColor = Color.red; errorStyle.wordWrap = true; GUIStyle warningStyle = new GUIStyle(errorStyle); - warningStyle.normal.textColor = Color.yellow; + warningStyle.normal.textColor = COLOR_YELLOW; + GUIStyle sliderStyle = new GUIStyle(GUI.skin.horizontalSlider); + sliderStyle.fixedWidth = SLIDER_WIDTH; + GUIStyle sliderThumbStyle = new GUIStyle(GUI.skin.horizontalSliderThumb); + + // set the background for the window to a darker gray + Texture2D backgroundTexture = new Texture2D(1, 1, TextureFormat.RGBA32, false); + backgroundTexture.SetPixel(0, 0, COLOR_BACKGROUND); + backgroundTexture.Apply(); + GUI.DrawTexture(new Rect(0, 0, maxSize.x, maxSize.y), backgroundTexture, ScaleMode.StretchToFill); GUILayout.Space(10); - // Project name label and input text field - GUILayout.Label("Export project name:", labelStyle); - projectName = GUILayout.TextField(projectName, textStyle); + if (updateExistingAvatar) { + // Project file to update label and input text field + GUILayout.Label("Project file to update:", labelStyle); + projectLocation = GUILayout.TextField(projectLocation, textStyle); + } else { + // Project name label and input text field + GUILayout.Label("Export project name:", labelStyle); + projectName = GUILayout.TextField(projectName, textStyle); + + GUILayout.Space(10); + + // Project location label and input text field + GUILayout.Label("Export project location:", labelStyle); + projectLocation = GUILayout.TextField(projectLocation, textStyle); + } - GUILayout.Space(10); - - // Project location label and input text field - GUILayout.Label("Export project location:", labelStyle); - projectLocation = GUILayout.TextField(projectLocation, textStyle); - - // Browse button to open folder explorer that starts at project location path and then updates project location + // Browse button to open file/folder explorer and set project location if (GUILayout.Button("Browse", buttonStyle)) { - string result = EditorUtility.OpenFolderPanel("Select export location", projectLocation, ""); - if (result.Length > 0) { // folder selection not cancelled + string result = ""; + if (updateExistingAvatar) { + // open file explorer starting at hifi projects folder in user documents and select target fst to update + string initialPath = string.IsNullOrEmpty(projectLocation) ? initialProjectLocation : projectLocation; + result = EditorUtility.OpenFilePanel("Select .fst to update", initialPath, "fst"); + } else { + // open folder explorer starting at project location path and select folder to create project folder in + result = EditorUtility.OpenFolderPanel("Select export location", projectLocation, ""); + } + if (!string.IsNullOrEmpty(result)) { // file/folder selection not cancelled projectLocation = result.Replace('/', '\\'); } } - // Red error label text to display any file-related errors + // warning if scale is above/below recommended range or if scale was auto-adjusted initially + GUILayout.Label(scaleWarningText, warningStyle); + + // from left to right show scale label, scale slider itself, and scale value input with % value + // slider value itself is from 0.0 to 2.0, and actual scale is an exponent of it with an offset of 1 + // displayed scale is the actual scale value with 2 decimal places, and changing the displayed + // scale via keyboard does the inverse calculation to get the slider value via logarithm + GUILayout.BeginHorizontal(); + GUILayout.Label("Scale:", labelStyle); + sliderScale = GUILayout.HorizontalSlider(sliderScale, MIN_SCALE_SLIDER, MAX_SCALE_SLIDER, sliderStyle, sliderThumbStyle); + float actualScale = (Mathf.Pow(SLIDER_SCALE_EXPONENT, sliderScale) - ACTUAL_SCALE_OFFSET); + GUIStyle scaleInputStyle = new GUIStyle(textStyle); + scaleInputStyle.fixedWidth = SCALE_TEXT_WIDTH; + actualScale *= 100.0f; // convert to 100-based percentage for display purposes + string actualScaleStr = GUILayout.TextField(String.Format("{0:0.00}", actualScale), scaleInputStyle); + actualScaleStr = Regex.Replace(actualScaleStr, @"[^0-9.]", ""); + actualScale = float.Parse(actualScaleStr); + actualScale /= 100.0f; // convert back to 1.0-based percentage + SetAvatarScale(actualScale); + GUILayout.Label("%", labelStyle); + GUILayout.EndHorizontal(); + + GUILayout.Space(15); + + // red error label text to display any file-related errors GUILayout.Label("Error:", errorStyle); GUILayout.Label(errorText, errorStyle); GUILayout.Space(10); - // Yellow warning label text to display scrollable list of any bone-related warnings + // yellow warning label text to display scrollable list of any bone-related warnings GUILayout.Label("Warnings:", warningStyle); warningScrollPosition = GUILayout.BeginScrollView(warningScrollPosition, GUILayout.Width(WINDOW_WIDTH), GUILayout.Height(WARNING_SCROLL_HEIGHT)); @@ -1229,64 +1436,122 @@ class ExportProjectWindow : EditorWindow { GUILayout.Space(10); - // Export button which will verify project folder can actually be created + // export button will verify target project folder can actually be created (or target fst file is valid) // before closing popup window and calling back to initiate the export bool export = false; if (GUILayout.Button("Export", buttonStyle)) { export = true; if (!CheckForErrors(true)) { Close(); - onCloseCallback(projectDirectory, projectName, warningText); + onExportCallback(updateExistingAvatar ? projectLocation : projectDirectory, projectName, actualScale); } } - // Cancel button just closes the popup window without callback + // cancel button closes the popup window triggering the close callback to close the preview scene if (GUILayout.Button("Cancel", buttonStyle)) { Close(); } - // When either text field changes check for any errors if we didn't just check errors from clicking Export above + // when any value changes check for any errors and update scale warning if we are not exporting if (GUI.changed && !export) { CheckForErrors(false); + UpdateScaleWarning(); } } bool CheckForErrors(bool exporting) { errorText = EMPTY_ERROR_TEXT; // default to None if no errors found - projectDirectory = projectLocation + "\\" + projectName + "\\"; - if (projectName.Length > 0) { - // new project must have a unique folder name since the folder will be created for it - if (Directory.Exists(projectDirectory)) { - errorText = "A folder with the name " + projectName + - " already exists at that location.\nPlease choose a different project name or location."; + if (updateExistingAvatar) { + // if any text is set in the project file to update field verify that the file actually exists + if (projectLocation.Length > 0) { + if (!File.Exists(projectLocation)) { + errorText = "Please select a valid project file to update.\n"; + return true; + } + } else if (exporting) { + errorText = "Please select a project file to update.\n"; return true; } - } - if (projectLocation.Length > 0) { - // before clicking Export we can verify that the project location at least starts with a drive - if (!Char.IsLetter(projectLocation[0]) || projectLocation.Length == 1 || projectLocation[1] != ':') { - errorText = "Project location is invalid. Please choose a different project location.\n"; - return true; + } else { + projectDirectory = projectLocation + "\\" + projectName + "\\"; + if (projectName.Length > 0) { + // new project must have a unique folder name since the folder will be created for it + if (Directory.Exists(projectDirectory)) { + errorText = "A folder with the name " + projectName + + " already exists at that location.\nPlease choose a different project name or location."; + return true; + } } - } - if (exporting) { - // when exporting, project name and location must both be defined, and project location must - // be valid and accessible (we attempt to create the project folder at this time to verify this) - if (projectName.Length == 0) { - errorText = "Please define a project name.\n"; - return true; - } else if (projectLocation.Length == 0) { - errorText = "Please define a project location.\n"; - return true; - } else { - try { - Directory.CreateDirectory(projectDirectory); - } catch { + if (projectLocation.Length > 0) { + // before clicking Export we can verify that the project location at least starts with a drive + if (!Char.IsLetter(projectLocation[0]) || projectLocation.Length == 1 || projectLocation[1] != ':') { errorText = "Project location is invalid. Please choose a different project location.\n"; return true; } } - } + if (exporting) { + // when exporting, project name and location must both be defined, and project location must + // be valid and accessible (we attempt to create the project folder at this time to verify this) + if (projectName.Length == 0) { + errorText = "Please define a project name.\n"; + return true; + } else if (projectLocation.Length == 0) { + errorText = "Please define a project location.\n"; + return true; + } else { + try { + Directory.CreateDirectory(projectDirectory); + } catch { + errorText = "Project location is invalid. Please choose a different project location.\n"; + return true; + } + } + } + } + return false; } + + void UpdateScaleWarning() { + // called on any input changes + float height = GetAvatarHeight(); + if (height < MINIMUM_RECOMMENDED_HEIGHT) { + scaleWarningText = "The height of the avatar is below the recommended minimum."; + } else if (height > MAXIMUM_RECOMMENDED_HEIGHT) { + scaleWarningText = "The height of the avatar is above the recommended maximum."; + } else { + scaleWarningText = ""; + } + } + + float GetAvatarHeight() { + // height of an avatar model can be determined to be the max Y extents of the combined bounds for all its mesh renderers + Bounds bounds = new Bounds(); + var meshRenderers = avatarPreviewObject.GetComponentsInChildren(); + var skinnedMeshRenderers = avatarPreviewObject.GetComponentsInChildren(); + foreach (var renderer in meshRenderers) { + bounds.Encapsulate(renderer.bounds); + } + foreach (var renderer in skinnedMeshRenderers) { + bounds.Encapsulate(renderer.bounds); + } + return bounds.max.y; + } + + void SetAvatarScale(float actualScale) { + // set the new scale uniformly on the preview avatar's transform to show the resulting avatar size + avatarPreviewObject.transform.localScale = new Vector3(actualScale, actualScale, actualScale); + + // adjust slider scale value to match the new actual scale value + sliderScale = GetSliderScaleFromActualScale(actualScale); + } + + float GetSliderScaleFromActualScale(float actualScale) { + // since actual scale is an exponent of slider scale with an offset, do the logarithm operation to convert it back + return Mathf.Log(actualScale + ACTUAL_SCALE_OFFSET, SLIDER_SCALE_EXPONENT); + } + + void OnDestroy() { + onCloseCallback(); + } } diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Average.mat b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Average.mat new file mode 100644 index 0000000000..69421ca8e2 --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Average.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Average + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.53309965, g: 0.8773585, b: 0.27727836, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Floor.mat b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Floor.mat new file mode 100644 index 0000000000..4c63832593 --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Floor.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Floor + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab new file mode 100644 index 0000000000..3a6b6b21fa --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab @@ -0,0 +1,1393 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &100100000 +Prefab: + m_ObjectHideFlags: 1 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: [] + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 0} + m_RootGameObject: {fileID: 1663253797283788} + m_IsPrefabAsset: 1 +--- !u!1 &1046656866020106 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224386929081752724} + - component: {fileID: 222160789105267064} + - component: {fileID: 114930405832365464} + m_Layer: 5 + m_Name: TwoAndHalfMText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1098451480288840 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4735851023856772} + - component: {fileID: 33008877752475126} + - component: {fileID: 23983268565997994} + m_Layer: 0 + m_Name: HalfMLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1107359137501064 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224352215517075892} + - component: {fileID: 222924084127982026} + - component: {fileID: 114523909969846714} + m_Layer: 5 + m_Name: TwoMText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1108041172082256 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224494569551489322} + - component: {fileID: 223961774962398002} + - component: {fileID: 114011556853048752} + - component: {fileID: 114521005238033952} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1165326825168616 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224593141416602104} + - component: {fileID: 222331762946337184} + - component: {fileID: 114101794169638918} + m_Layer: 5 + m_Name: OneAndHalfMText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1182485492886750 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4302978871272126} + - component: {fileID: 33686989621546016} + - component: {fileID: 23982106336197490} + m_Layer: 0 + m_Name: TwoAndHalfMLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1365616260555366 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224613908675679132} + - component: {fileID: 222421911825862480} + - component: {fileID: 114276838631099888} + m_Layer: 5 + m_Name: OneMText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1398639835840810 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4460037940915778} + - component: {fileID: 33999849812690240} + - component: {fileID: 23416265009837404} + m_Layer: 0 + m_Name: Floor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1534720920953066 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4413776654278098} + - component: {fileID: 33291071156168694} + - component: {fileID: 23550720950256080} + m_Layer: 0 + m_Name: Average + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1594624973687270 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4908828994703896} + - component: {fileID: 33726300519449444} + - component: {fileID: 23824769923661608} + m_Layer: 0 + m_Name: Tall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1663253797283788 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4466308008297536} + m_Layer: 0 + m_Name: HeightReference + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1684603522306818 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4359301733271006} + - component: {fileID: 33170278100239952} + - component: {fileID: 23463284742561382} + m_Layer: 0 + m_Name: TwoMLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1758516477546936 +GameObject: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 224093314116541246} + - component: {fileID: 222104353024021134} + - component: {fileID: 114198955202599194} + m_Layer: 5 + m_Name: HalfMText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1843086377652878 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967607462495426} + - component: {fileID: 33458427168817864} + - component: {fileID: 23807848267690204} + m_Layer: 0 + m_Name: Short + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1845490813592506 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4990347338131576} + - component: {fileID: 108630196659418708} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1883639722740524 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4177433262325602} + - component: {fileID: 33418961761515394} + - component: {fileID: 23536779434871182} + m_Layer: 0 + m_Name: TooShort + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1885741171197356 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4718462335765420} + - component: {fileID: 33030310456480364} + - component: {fileID: 23105277758912132} + m_Layer: 0 + m_Name: TooTall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1919147340747728 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4440944676647488} + - component: {fileID: 33820823812379558} + - component: {fileID: 23886085173153614} + m_Layer: 0 + m_Name: OneAndHalfMLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &1985295559338180 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 6 + m_Component: + - component: {fileID: 4498194399146796} + - component: {fileID: 33041053251399642} + - component: {fileID: 23936786851965954} + m_Layer: 0 + m_Name: OneMLine + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4177433262325602 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1883639722740524} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.219375, z: -1} + m_LocalScale: {x: 200, y: 0.43875, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4302978871272126 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1182485492886750} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 2.5, z: -0.94} + m_LocalScale: {x: 200, y: 1.0000005, z: 0.0100000035} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 12 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!4 &4359301733271006 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1684603522306818} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 2, z: -0.94} + m_LocalScale: {x: 200, y: 1.0000005, z: 0.0100000035} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!4 &4413776654278098 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1534720920953066} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1.535625, z: -1} + m_LocalScale: {x: 200, y: 1.19375, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4440944676647488 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1919147340747728} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 1.5, z: -0.94} + m_LocalScale: {x: 200, y: 1.0000005, z: 0.0100000035} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!4 &4460037940915778 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1398639835840810} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -50, z: -0.5} + m_LocalScale: {x: 200, y: 100, z: 2} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4466308008297536 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1663253797283788} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4990347338131576} + - {fileID: 4460037940915778} + - {fileID: 4177433262325602} + - {fileID: 4967607462495426} + - {fileID: 4413776654278098} + - {fileID: 4908828994703896} + - {fileID: 4718462335765420} + - {fileID: 224494569551489322} + - {fileID: 4735851023856772} + - {fileID: 4498194399146796} + - {fileID: 4440944676647488} + - {fileID: 4359301733271006} + - {fileID: 4302978871272126} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4498194399146796 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1985295559338180} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 1, z: -0.94} + m_LocalScale: {x: 200, y: 1.0000005, z: 0.0100000035} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!4 &4718462335765420 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1885741171197356} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 502.6325, z: -1} + m_LocalScale: {x: 200, y: 1000, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4735851023856772 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1098451480288840} + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0.5, z: -0.94} + m_LocalScale: {x: 200, y: 1, z: 0.01} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!4 &4908828994703896 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1594624973687270} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 2.3825, z: -1} + m_LocalScale: {x: 200, y: 0.5, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4967607462495426 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1843086377652878} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.68875, z: -1} + m_LocalScale: {x: 200, y: 0.5, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &4990347338131576 +Transform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1845490813592506} + m_LocalRotation: {x: -0.11086535, y: -0.8745676, z: 0.40781754, w: -0.23775047} + m_LocalPosition: {x: 0, y: 3, z: 77.17} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4466308008297536} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 50.000004, y: -210.41699, z: 0} +--- !u!23 &23105277758912132 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1885741171197356} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d07e04b46b88ae54e9f418c8645f1580, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23416265009837404 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1398639835840810} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 320b570da434d374985fe89d653ae75b, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23463284742561382 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1684603522306818} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23536779434871182 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1883639722740524} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d07e04b46b88ae54e9f418c8645f1580, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23550720950256080 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1534720920953066} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 722779087c41d074eb632820263fc661, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23807848267690204 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1843086377652878} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 1cd16d030e4890a4cab22d897ccfc8d8, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23824769923661608 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1594624973687270} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: 1cd16d030e4890a4cab22d897ccfc8d8, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23886085173153614 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1919147340747728} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23936786851965954 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1985295559338180} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23982106336197490 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1182485492886750} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!23 &23983268565997994 +MeshRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1098451480288840} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 4294967295 + m_Materials: + - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &33008877752475126 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1098451480288840} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33030310456480364 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1885741171197356} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33041053251399642 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1985295559338180} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33170278100239952 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1684603522306818} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33291071156168694 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1534720920953066} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33418961761515394 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1883639722740524} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33458427168817864 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1843086377652878} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33686989621546016 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1182485492886750} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33726300519449444 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1594624973687270} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33820823812379558 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1919147340747728} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!33 &33999849812690240 +MeshFilter: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1398639835840810} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!108 &108630196659418708 +Light: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1845490813592506} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Intensity: 2 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!114 &114011556853048752 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1108041172082256} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!114 &114101794169638918 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1165326825168616} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 128 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 256 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: '1.5m + +' +--- !u!114 &114198955202599194 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1758516477546936} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 128 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 256 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 0.5m +--- !u!114 &114276838631099888 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1365616260555366} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 128 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 256 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: '1.0m + +' +--- !u!114 &114521005238033952 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1108041172082256} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &114523909969846714 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1107359137501064} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 128 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 256 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: '2.0m + +' +--- !u!114 &114930405832365464 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1046656866020106} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 128 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 256 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: '2.5m + +' +--- !u!222 &222104353024021134 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1758516477546936} + m_CullTransparentMesh: 0 +--- !u!222 &222160789105267064 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1046656866020106} + m_CullTransparentMesh: 0 +--- !u!222 &222331762946337184 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1165326825168616} + m_CullTransparentMesh: 0 +--- !u!222 &222421911825862480 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1365616260555366} + m_CullTransparentMesh: 0 +--- !u!222 &222924084127982026 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1107359137501064} + m_CullTransparentMesh: 0 +--- !u!223 &223961774962398002 +Canvas: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1108041172082256} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 2 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &224093314116541246 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1758516477546936} + m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: -0.395} + m_LocalScale: {x: 0.001, y: 0.001, z: 0.001} + m_Children: [] + m_Father: {fileID: 224494569551489322} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -2, y: 0.55} + m_SizeDelta: {x: 300, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &224352215517075892 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1107359137501064} + m_LocalRotation: {x: -0, y: 1, z: -0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: -0.395} + m_LocalScale: {x: 0.0009999999, y: 0.0009999999, z: 0.0009999999} + m_Children: [] + m_Father: {fileID: 224494569551489322} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -2, y: 2.05} + m_SizeDelta: {x: 300, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &224386929081752724 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1046656866020106} + m_LocalRotation: {x: -0, y: 1, z: -0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: -0.395} + m_LocalScale: {x: 0.0009999999, y: 0.0009999999, z: 0.0009999999} + m_Children: [] + m_Father: {fileID: 224494569551489322} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -2, y: 2.55} + m_SizeDelta: {x: 300, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &224494569551489322 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1108041172082256} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 224093314116541246} + - {fileID: 224613908675679132} + - {fileID: 224593141416602104} + - {fileID: 224352215517075892} + - {fileID: 224386929081752724} + m_Father: {fileID: 4466308008297536} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 1000, y: 1000} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &224593141416602104 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1165326825168616} + m_LocalRotation: {x: -0, y: 1, z: -0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: -0.395} + m_LocalScale: {x: 0.0009999999, y: 0.0009999999, z: 0.0009999999} + m_Children: [] + m_Father: {fileID: 224494569551489322} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -2, y: 1.55} + m_SizeDelta: {x: 300, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!224 &224613908675679132 +RectTransform: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1365616260555366} + m_LocalRotation: {x: -0, y: 1, z: -0, w: 0} + m_LocalPosition: {x: 0, y: 0, z: -0.395} + m_LocalScale: {x: 0.0009999999, y: 0.0009999999, z: 0.0009999999} + m_Children: [] + m_Father: {fileID: 224494569551489322} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -2, y: 1.05} + m_SizeDelta: {x: 300, y: 200} + m_Pivot: {x: 0.5, y: 0.5} diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Line.mat b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Line.mat new file mode 100644 index 0000000000..2f9a048c63 --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/Line.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: Line + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/ShortOrTall.mat b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/ShortOrTall.mat new file mode 100644 index 0000000000..5543fef85e --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/ShortOrTall.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: ShortOrTall + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.91758025, g: 0.9622642, b: 0.28595585, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/TooShortOrTall.mat b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/TooShortOrTall.mat new file mode 100644 index 0000000000..4851a64056 --- /dev/null +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/TooShortOrTall.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: TooShortOrTall + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.9056604, g: 0.19223925, b: 0.19223925, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/tools/unity-avatar-exporter/Assets/README.txt b/tools/unity-avatar-exporter/Assets/README.txt index 402719b497..767c093800 100644 --- a/tools/unity-avatar-exporter/Assets/README.txt +++ b/tools/unity-avatar-exporter/Assets/README.txt @@ -1,6 +1,6 @@ High Fidelity, Inc. Avatar Exporter -Version 0.3.3 +Version 0.3.5 Note: It is recommended to use Unity versions between 2017.4.17f1 and 2018.2.12f1 for this Avatar Exporter. @@ -9,15 +9,16 @@ To create a new avatar project: 2. Select the .fbx avatar that you imported in step 1 in the Assets window, and in the Rig section of the Inspector window set the Animation Type to Humanoid and choose Apply. 3. With the .fbx avatar still selected in the Assets window, choose High Fidelity menu > Export New Avatar. 4. Select a name for your avatar project (this will be used to create a directory with that name), as well as the target location for your project folder. -5. Once it is exported, your project directory will open in File Explorer. +5. If necessary, adjust the scale for your avatar so that it's height is within the recommended range. +6. Once it is exported, you will receive a successfully exported dialog with any warnings, and your project directory will open in File Explorer. To update an existing avatar project: -1. Select the existing .fbx avatar in the Assets window that you would like to re-export. -2. Choose High Fidelity menu > Update Existing Avatar and browse to the .fst file you would like to update. +1. Select the existing .fbx avatar in the Assets window that you would like to re-export and choose High Fidelity menu > Update Existing Avatar +2. Select the .fst project file that you wish to update. 3. If the .fbx file in your Unity Assets folder is newer than the existing .fbx file in your selected avatar project or vice-versa, you will be prompted if you wish to replace the older file with the newer file before performing the update. -4. Once it is updated, your project directory will open in File Explorer. +4. Once it is updated, you will receive a successfully exported dialog with any warnings, and your project directory will open in File Explorer. * WARNING * If you are using any external textures as part of your .fbx model, be sure they are copied into the textures folder that is created in the project folder after exporting a new avatar. -For further details including troubleshooting tips, see the full documentation at https://docs.highfidelity.com/create-and-explore/avatars/create-avatars/unity-extension +For further details including troubleshooting tips, see the full documentation at https://docs.highfidelity.com/create-and-explore/avatars/create-avatars/unity-extension \ No newline at end of file diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index f7385e38311e93788a17308f7cb2a174c7f7f138..ee3f6abe01b509bba63360b2834400660f9f3901 100644 GIT binary patch literal 74582 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 Date: Fri, 15 Mar 2019 11:05:58 +1300 Subject: [PATCH 45/88] Update JSDoc README with solution for out-of-memory problem --- tools/jsdoc/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/jsdoc/README.md b/tools/jsdoc/README.md index f9864a21e4..f3dda84291 100644 --- a/tools/jsdoc/README.md +++ b/tools/jsdoc/README.md @@ -15,6 +15,13 @@ To generate html documentation for the High Fidelity JavaScript API: The out folder should contain index.html. +If you get a "JavaScript heap out of memory" error when running the `jsdoc` command you need to increase the amount of memory +available to it. For example, to increase the memory available to 2GB on Windows: +* `where jsdoc` to find the `jsdoc.cmd` file. +* Edit the `jsdoc.cmd` file to add `--max-old-space-size=2048` after the `node` and/or `node.exe` commands. + +Reference: https://medium.com/@vuongtran/how-to-solve-process-out-of-memory-in-node-js-5f0de8f8464c + To generate the grav automation files, run node gravPrep.js after you have made a JSdoc output folder. This will create files that are needed for hifi-grav and hifi-grav-content repos From 3c78e48f88ffe74868340a389ca0597b65ed0d27 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 14 Mar 2019 17:05:29 -0700 Subject: [PATCH 46/88] prevent warnings on embedded materials --- .../Editor/AvatarExporter/AvatarExporter.cs | 5 +++++ .../avatarExporter.unitypackage | Bin 74582 -> 74600 bytes 2 files changed, 5 insertions(+) diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index 142e4ae35a..f0d970031c 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -1105,6 +1105,11 @@ class AvatarExporter : MonoBehaviour { string materialName = material.name; string shaderName = material.shader.name; + // if this material isn't mapped externally then ignore it + if (!materialMappings.ContainsValue(materialName)) { + continue; + } + // don't store any material data for unsupported shader types if (Array.IndexOf(SUPPORTED_SHADERS, shaderName) == -1) { if (!unsupportedShaderMaterials.Contains(materialName)) { diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index ee3f6abe01b509bba63360b2834400660f9f3901..48a9502079839c3aee59ead39531198dd5d4bf86 100644 GIT binary patch delta 72937 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 L^b|O508|42fq(tw delta 72877 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 z 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%0Dyirf01r8FqLONO!VuD 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 Date: Thu, 14 Mar 2019 17:19:21 -0700 Subject: [PATCH 47/88] fix null object error on cancel export --- .../Editor/AvatarExporter/AvatarExporter.cs | 21 ++++++++++-------- .../avatarExporter.unitypackage | Bin 74600 -> 74623 bytes 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index f0d970031c..c5bc5eb84e 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -1531,16 +1531,19 @@ class ExportProjectWindow : EditorWindow { float GetAvatarHeight() { // height of an avatar model can be determined to be the max Y extents of the combined bounds for all its mesh renderers - Bounds bounds = new Bounds(); - var meshRenderers = avatarPreviewObject.GetComponentsInChildren(); - var skinnedMeshRenderers = avatarPreviewObject.GetComponentsInChildren(); - foreach (var renderer in meshRenderers) { - bounds.Encapsulate(renderer.bounds); + if (avatarPreviewObject != null) { + Bounds bounds = new Bounds(); + var meshRenderers = avatarPreviewObject.GetComponentsInChildren(); + var skinnedMeshRenderers = avatarPreviewObject.GetComponentsInChildren(); + foreach (var renderer in meshRenderers) { + bounds.Encapsulate(renderer.bounds); + } + foreach (var renderer in skinnedMeshRenderers) { + bounds.Encapsulate(renderer.bounds); + } + return bounds.max.y; } - foreach (var renderer in skinnedMeshRenderers) { - bounds.Encapsulate(renderer.bounds); - } - return bounds.max.y; + return 0.0f; } void SetAvatarScale(float actualScale) { diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index 48a9502079839c3aee59ead39531198dd5d4bf86..3e2d6f2aed318abc6382f1a914ec3fb5e87dee69 100644 GIT binary patch delta 73664 zcmV(mK=Z%o#svSy1b-ik2nb5=id+N$VRB<=bY*RDE_7jX0PI=^G@M@+AB^5fL=A}^ zZS>BJ8eO#L3^N#QWDF9bmxvP4qB9~0q9h0+T0{xKD2W;+dKV;wU?=O8naXqLLC~($e4UFM(SOKmGrYfD^(U zu7N~(K+$G!ls5wDDaRp3=In!j$#MLxr9tL|bcexFzdk|aIBvnc$<*LZP#<@+mWLM- zg@&W#$T&FQH*pW1Pk|4WEUN&VdaKLWq-KM{2p0*yoge&YWPqT-^` z;*wAqkc_mGl(e*j6ch%P6@`JI4mg+K=;-*L;(s8}PyY8K@Hg%MpW%Pv(x9LI|3~1j z@V~#cG=F~bzyAUJEhGOB{wFT+XZ%l8^5_2l5%?qiCj#+>;$}nr=CAkfPdfO208vMn zs1yt&4uVU_$bz5}j!*|NF_?@jZtpue%D`m)L;O!t;%EHthv0A8|JNrm;Ge?Z@c-h{ zQopmmIPT_;D=#i3`t$wI4*|CYL{FDPR9KXZ+kXV2$#Kc&5(gUUEa&5iK;IIF2|FS^ z1jTUgW97(%goM85iivXYi2d@CkW3GXJ0uWL_wPK<``1qhe>m)SJW>iL_OSbf_+3T7 z;2Q2wXB+_p{aUPwM4{l`UPw4S2F|56gi5WjO$Ej3)pz+V?NMB!+qgO=wn zM1S(n^M4_Y#xA%s%K&8pb$9=@)Yt`zBd32*T0%;Y<2QlLKY`Pqqy;(9x4huLZvXc! z>A-LKAyF{g0DdjhML4^lJ)mB{8I;7Yh3cMA2b?lb+>-Ki#Et7W)b)3RRzv#W@{MtG z0n^m_-Av$CAkxDK?uk=!%?Iv-Q!FIE9JW z`=H=|JmtU7Hg<%%f6u&$yDJEC+`@hTVXlFb6V7w|k`2OTf3N&oU23@NB*Goy?tcJ> zA%Cn+J*cOZ2xgRUb6c$q83SSn@9~?LVub=8aZy z$GJ0Ho!^RnG1~8o{*fHv3HSE?t$%{wi*Fgho!oH;0&ezkxuSo%4*sBZJtXYAxBfGu z8KY5fPiM5t?`r>ke!r^&iTze;?14n0UA}kYHxsSu0`>HSyZ=F>#$IqoA9pC~D$eh_ z<38v=?2|Fd@s|<*USw)!4FAJET3$yX(7ze_FXx{-67{>8|0Rht5$b!#WPe4aC1r3? zG2D6e>!g&Jn3RN=Acw>El$ea9tfZt2E)$At?a#LUA2#$${a@<+w)j8B|NhPKKdImR z{|EOXNJ>KT`~6>1{Ac{{hv2Wo|Nhp}Ak&AU5x&3F{ySoR9=OBe7m3Dieb~=P;C~!{ z%g8?@{wMio@jo%CpZC8Xf`7jj{}cIxJC(2p^q-6Z{KS7F{`USy!F>^MKVdIVXTU#* zzrO#)BqhZ~|8)OLi%b99|33nE4fQoC$(X;NsFYe))r@ezA>ThF__%Lf>o3^=00%%z z4PtT=Z!-sx1@G`F^ezo$7OU3aD#E|S2R1j0#TT5cIbO|co!Lw{`hU1(R>x%(ZiVXQ z2#uf|(Y>Zh)ihn}`&#y3I$J;>Ht6DIgU<1%j$JkH?*;ZN9>CsxyTcwNbKv3cPa>$$ zl;6U}1Z?J>6#hX5wUKlZICT?)XyNyTY3c6y7X!)EiZ4z zpvz$ROO{S+Z{;-YqkrBq>i&wUnW?dPb|smnFDv`u zm3fM;FP%FBGJ)3hhc2IAg>N_c7Sq%rS-e}%f2)@kr#g~6h$QS_VCi;%8sDG18DuMs zW_e}f+zt2^segxN!2;MnYOd)m6wdoyVAvLBJKLB$aULD*_k5yUtM~~`Qt!1oSXMcc zI4aOD6G2dceN)2e=^}q*lWql=Z=e)^1`@fZN2>5m(7iuY=G7~rjdMpd4i*yb{UXse ziFLAm8?T+ob@Iw9b1Z`QbNb@Nw|1{q7x2ukSsmCm`F}eH+DzYT=LNfTbWU?&7)(L3{5vcN>w@eL7;;iwnHlT6Gqm^V`)^Bod)}HgEce1fbOtK$P8x_`C<@h)juRs?KXL$JtBT62e2+)o)IDIxBOx}32x&PMksioN znGqB0(m`pGdDwRJC!KVVRf+f0h5KBa`jmO$F zVjjq4BZ$y?2#q4A=W}gQ{zlYm+q!B>_c~TM8rr9g~0xBtWFYfPW%Q3Cvvz$t)JOgZKJPJtLqDK~8ewrJ>Y|kPlQam+r9X$By7w3bxWJKRCH& zU>zczMLLt@3N0Na#3z2gXq8el@_Eu*eLjog?zOI)Dy}T?8E{J_lP(Jve-4siWu?MH zHkS|7`2}WGW;`>JPrdfI_8Aq!{Myw-9d5-H7XR(A=LAci-+~8jqEFz|hnK~US&>u$dzy*<<7v%3(}N1cw~CpL&Vp4uQVDSt(;XSU+j zb2OU?gF@bDlf#C!DE(EwY^y21vr}ezNko_Jg$|iqC}Lnvcf1l%ELS{ogmFqDn*4Y% z_;e$$xjDGEgTRTHe;2TSb{e&9ECw-rEb9l$}UJEjef{Zr1LG9n?J{g<#A0n2x6% zsjF)h$Lz{?-*d`5PrT??%7i~ZTrMVjd8?26Q=+>-bq5tz{<@@1M7wLQXgO&HJ^SePt;3Gu{j! zUWuL3Nn>Ai1DmbAB?0A-a5pgS-!MC~9DPV4U`QBve|1-(iF6F_W^?Y6x0?W)fpHTx ztPnZl6YFx|htyC|rF+1ALw@0w&p{eKQ$mKe-u#-`Xo~Rl$q_b&Q=ticEYXfF zGh>7~e>EHscBjFe6)5e>YZ(8cWTGKXQ{8X)n2}ua!CfK>s@0&g%HwI^G3RVkOJno3 z1pgy+9Hh-KMB*Dk>dDN<);2)%ob*0hP`a0(wn7+Ts~t>v&_EeT!<8(~=JXN4tvUbj z12+pNd@)M4JKsCeoBq+NXH=Ve&;7zKGqR?qf1gComV*Z-wlL$GByz!@=GMO6Q6rvz zqA$rc>{5JBom8&ljO7S&-^z#}V`5E+(SI<4MV+o`jQ|9c@3}o+rdG_AnP@`3MS>@y zBI3WvW|qq3d~Wp7SO>}QE#x$zpadU0euEF_m$V(^Gyi3irMGvvNPfKNWdfVmChsW3 ze|{ypBVdt)WA>T4qj(SC!)Yw_{B%!X%(h7Fuo2f{Vb8mCv+()qLNQPMn_wa&UGkH= zF)v5t*j1IsBp6;4_PJo+XJy)MH%Gm7D~_kw5A3fD-fM|^@!?Su-OkpG?ecp0;5pZK z7hA;vsdLWY8Cn980J_upZh|Qt*7zv8e;$fX%iVj-FNnN4D~zq&i!_vSs}`U~N!I)0 z?zJ!4o@`_x{E<#f;fXm@WIB4zH*RdDo$Xhbj&0Ty(>$7OpO@MU7O73h>@R@ve9=Xb zJ8?!XP%Z9J_5mNptn!FhSsp08JiyeIFR)InR!g-Y%CZ7m_A%;9Zt(c=gl0noe=~|w z@BmA}oNK!3P{u<0Kq>P^@ng#dJe`ZYQJ1bDL}OIyJU^IQj4RS?B8Xq^p2$zpQiWG< z9O({?x6-%c@j8hTDPL=meb#>e7I^%zjxPJJr&E_96fS$~jJQ3h$-+czq6?@X?s zfPLo#({f8qP$h0oXieg8au$4g*UeHV5YaVJ;R#Bosj6}0jO6R-Lv@lOAxbihhT?8Eu<tK^)}1KJ}XCAIIsDiuwt_tNGgt4f+l`_Kn_oO4 z8180CfvN7ip}aQWu`-rpjJOmO>aO54&HNmC; z66)kO|E*@lqQbE|1YaDeQXAc2H{6g!Kp2@UYkkbM#}StA^A<=GE$qiHipr{MmH1o~ zUsNH^VjO#Mp`fPXe@NT36O-Rl@y3fEdqund{kT{s>O+=$SWdIh*xe9;nY}zR_~ncI z1)?TVu|plqGs$CDdY9}C7IvnuLPQj%bq|P<-#;SPoN~Z3ci4l9Ib^fgPBSfNi zU0o7=+bu2YJn%DJBx_PakL;fo8Hi9jfc)qszM0PTSxFl;z?+5ejyTm7{r1MTaw^{C zC-k^7Qg^c&e_1pb&}UA|nF<}yY{fJjoTF4%3SDs;k?gx~`l-XWrSjVzC@q4;{ePc42W=j~f$ugZrA9 zsjH552BAbXWAf-T1y#EbZP&HrO-X|hU6>o(zNOE~eIE<>@4I{k)8> zm8MgQR3Sfsld_mSe;=lnu-E8CsZbiV@RoIy`Q!TbChT+e*Zsn=fKJ0lhMHu2-}v82Vd6_mYbWqmBs3PVaT(m9A*bG+r%n6R~-1S@~e?{`>+~dH`Bb z|0#*ge^X{>X%^Y{t(?*Zc%hUJaXx(LatGtn=bp$~ZJYUtD@B$0Hb}mFI-w7b-0#+1 zo0>RvLa(#!3<=cTQJ&Wp$rV@hZ}u3HyuQvkdZp)zF`_@C`mzJ(sby zTkXTpiHA^nv$vsu2dyH-T+NE>&mzxtdB;wne?q&sx-_#IQ&l}Ij3!gx7kV6lqr@h7 z3WCcIst1%zJKQJyiEq*m(vwnO=bd_l4OlFE+h2tYFuN)0L?IF~A{BhaM0mT!@{vGA z>(h+{hSGOSMQ>>aA$Rk-P^53GC-d61l`VpXLfzlqHMhQv9WhiN^WhT`o ze+K!iHe9Q=kf|pwoeg88*Xg3~Y(>8_6zlcha6d<-c&@{WxjQq!|shAt-|kS!;rS6x9ql06tE{ z%=z~Ko%cK_;^cg4hh`NdiZ?ikYy+bRf21r2`c4+B4VsTXTxfL{^Uw=nc;h1R+8{?E z$Z3}O3s8NMf0XQmLC7vEz|#mDK7Dx(dF9;QPyWgd1qMxfk{kj{1j$qNv$(Auu0fxQppzWR+gh%4#tYNI`vU99!R;ucwa z6z5$Es`-kSF=k?UJg>)YJv3XIde=?|F8G@Ey16F5oTuH6)^!|Wzg1TGe?ux|Y;xPqr4`Yqinj+sVzw|wf1#(*&)NK7q8rI!FY#aWFmR~uC59L5s+neulUg^M0dK!U zoxol7YgMp!0JD0pw^#1OMZI{7uC#V%LcDpj59-Vbyw6#EbvjktwE9c9le@x$cgt#b za^uoERV>z)?YF6Ne=2r1TK5+oKN^hm zZR9JPp!7z@Jbf|54+NbfuNsh|qH~QyO%g*pQqrSo08pn_9c$`=gL5!uM~MoAza*2T zWFhbSC(fzwErwOvms5dvNUxk2S7!vN*4^-BmzY$WZu?=iPcJUP4^I+{g2H-OEj?*e$h9etsf-niRA`E8!4vF z)tY3uwo|U~i!~S1rWD77CoN@qgerLzJ#{_5@7yvvgjyH6PSCJga~`ib^1^+8dB_&| z{^k`7UWB#RbpzqB`ijT%mp`dzLC&n>T%ZD9-|j_`xaQ~Wf1YfF)CW6J+Y;uT_>(iE zE{rralT&s9m;rCXd(|pZ@f-_pn3~uO>lVqc9%%*0QC2oXU^%uW7nCW_rlutDc=Nt8 zJKPBLnLi{T-sj_)x>$#gPJKzhlYbD>T6UM@29Ua3_t|iS^W)kB^4`4>f+n?>lHEzi zNi^}Vu9c|>f5_hHx0$x&GdQ@~{~8jqRa)*>)Mzwe{%CH>ROn`1e^lA8DPn0`98cw zA)ZYMUj-~q0$d*Od6|pi&m1b=KJ>bP?<(hOFL`eN1+nC`J6T^3hTxK4{Z>v4Fp9I z5xVtkfAxGJ2+lD5sx}@_dedJoU7y1=5UVBb*}L!Gpgu}YoUUKA$~J2Y(9d&UtZt?_ z)Gl?pOz^-cQjjR*~5igbW99J#* zhoJ73yeU(+d|o|lQh|6LTraxNmr}r1e`tZv7~(b1k!EnsgLrIsRT!V(&~Tjj($#YC z`6|c^pulZG1{NAXYv1M9#tJ5^uTmkoZx{;??8bAU0rX4JW)D}K|Dbu_r%uW@@0{b( zQw3I4Z5ylCjzs?J2qJ;p{5>egevJ^-hS}^a7bAVx{%J;Qy6!NLM1b;9bbEOde-VA) znswEX#3hn%6iU|(G z8J5Y@O8+tZ-O8X!A{0%@Ps2HTe;ieZ6~b3g@d)c|3u&}8bI1+jkTo}#z}gx?=Q^s@ z9xN%Ozi>HxD54h9%Eu?NPo9R0l%~49d$15R1j1^1L}|lTO4zVgbF6RU3xq@rBS~#S zPw=07yZQKe4L6=fJh8mLfAaE)LvopRtg;QDc>>5Fq5Amx=zRz`V*s(*f1S`I@qYeW zIl^I)7pzZh)ftf8-JE_YrZu7rp&YLyjxeh6$87~dD&cbluW!e31)>0a>8P6yxt*_p zQ#$VZgAqJ}psEnUlAYy&DPP{!y?K61PkAd6It;5e{b&d?;?)7mzL~#RrZJIctAK*~ zNNEVZK&3KY+^$ObCp>_le^JRY*zu9J7X4(skW=e&gvFa7zZDN8v-`$jff>Tsq+w6j z=)qk3YjaLLQCxZ2&%Oc~j1|g?p;&g+tiw2sVaSbJoj=^t#HA zY5O)<5>hjs3WPmRVe{*?#7I7obMt&mXa@jO3r5KRo}#X6&FVIve?Mv!(m9Bu@Esd| zjny=pq+2O=3M<`shlzK&)X{^O4C%8dlP35|!$F~meeIALqDiff%wD>5&nZ$e-#QJb z{J`s4w%mSMQaK3k1c+A@31Qw3cjs5A$;81td8el1~!c4IpBHx5gcM; zYDocZxul-OD9vm?f0_e&2oLe3RO~iTz+<} z5V2z!%T(q@e+4;?p%kx$bnzb1XY-BA`^w@mJd)y7hC8gl2XwOa>R^-nMOrN5$+bkW z4$$0fB0qPYdOS67VI8Ek+bhit$z7axnJ9XMtEnW9)z%muCBW2Q@+b!+wX%yQG3uCT zAJ*yGLN%Dkqc~C|0=sJ%UvY>=LyCyWCiEt;yd&M_f3fTs&1F=`#ob-V_1?=Q_efo~ zEE-`Or|I*(k)skva|8t3@#&B(U7_~RW^I{sGP>tQyzG_rH70>1Qo{Hgg$DSJS>0AN zm@@}>(A+&GlJ&cMGw81kE8Qo35&Wyb$0F2-9hQ;g@DlkauDL0@oQEY)JU0ACfY6JQ zJ7`J)e@%WpV&O0w`A+<+ME$)N$BBkjkmN@CaLWO#JYn| zU?vK1Xqb5;_=OmZTNlfrTjCjAHx|ZF__5F$IgsE_SbP|H~Sm~ zdaX7?hzEoPPt-#If-}0$sHeCOa!vq&@&Pp05%ysk6)f@BhxhLay5B$69FGt*c{0cZ zO(C!tj-yG-Ff=2>QyrY-&W3UTDP2PK!(G!uA1@|37LrSW=x^ht6>Qyh3QsU&IFu(< zf4Lm1bn9aV?L}@+NQkSwDQ3N&lo@?6l7GF9n?R-@9(BR0$h$DiwvhHHg)t)8`vNf& zf3)rb#5*h`=2(p08b6kTzv?*%J{2lfOQiTk-AV$O@ygP_cizF*sF}6AF3B~qU^qRb z{*uf+iq4Pkflm~V`aeExS!v68kjOjsf8mo5I6S{=T{T=Theo0M^t=)7XWXdCSn~&M7Cu8{0*+6aF)5R*;FZ zvY-D_IaXpaGo&xRdVM{3@NHOguHK=kz8`NoVIy0M3QcQWxqX{;9M9-$xnq;=e^DM9 zTi5ZqEz~H~Ow92~eY#ffEU ztc>bc75B3#2RXbq&nzd!y<<&wi^Yt}JcNWdv|NlWy;^+#65Zp{b5N%sBBo>#3&?}~ zmlwD(8+_FGMTz0HIA-1M0zJ>BShYYlfV$73~N85$XN zuplYKv>@BTBkZ}*$3b(+*Lax@;#8SyJz@Uh(G}{(`(u6NGmQ6%Z!D|if35E@_$k#& z6#%~}5PYP{Men|TQ<^L5QB44=yW!+<^5vn^6fz=)z-PLmJsyyeb{rt`HtwMh)e9c- zT&gBlekr{-O8F_E9)5k=5FQxt0FYy*QWbNo(&8zt4&IUpm)~k3-j}Cz0IOvyG<2L8 z(2PF_anu=@3Ep3BP@g__fBv+l@#*T}`)d-9FKC4&2;SW#L1H{im5NComXi&_$)n9~ zTHnEr&^S3Lz`*0SJwMXd>TE5ChnYj za+BS~=kqIHz31FuN`~b;D?45B;&A|g{M`5d0-Tx5f4Y_K7UHQZe~-uAO|cMv+S=w@ zfakT%p1y2hADKcj-Xmnx=2e;@nQR1K5Z}hH%hEImaU0A z@*JbKluuQ6yKF@{yw=347Gf_5Z)0J+_%);#r_WxO;q&Jf_Y7Kn!}CnwYL(lk@HDAo zG+L1;PlGVkeDygGe|YYY>?2G-&aX}2~lBOJFSEV4vr{9(D%7esiLIirlkZsT(t%zS8{= zgyrlL+!uQ`#37CUakuFTQBPJiXRC!HAr%(5GWTi{n-&zXe^Y{&{oru7V{&dhqoq52 z4OYw+vRb4>BSFHz)SkdC#yJ0O{!RpkFR&VoV03Z#7_t#@pM5*T3`Fi8ftV_mOeSi( z{z7Z&TFtCu1HIve1z=u`3gg-VYuRvlV+u31_n;z|LDbpQIbXc#-Sz}uRx8UOL z+?{x*%csqQl~u{ZFHhgS9#@Wqc@*HHvdJv!27ySA) zw+!sPNG!*8MXHOB#&bd(yB=IvPWxpH49-le7$j4D zdBK$We|i3&db6Z!DQ?UL-nUa%#2vWDimJws3ou#m1&OAe^6}L!89+b(jxHv#GlnV0h(rs;K4Kk zD|`9w_;4{V^m!uuwb;&c}I?39j&`mESV zY4B;cwE@zNZ6aAjM;Qtc=XwhIX;`KbDfcm~F=4biU`>~=HHNvKS)`R|JQ6b9B&M#P ze~$1DrULJUH@g-)HL8l{Rw`tHE7{iyw)X~FYE+YNyvy|kGiNJ$FMi2M*f)G7@`OM! z4R4!>=`BY2VqveKAl9!sM+XNJf>=U9?j5XPLoWShjgIh8O zkPGjy_p#!p!G=gUEge+ia0 zR*OJlGs|!omtdR>NXZCwao6nJy=zjHY1dr3Jwa3MiFCU)#QS>fZ_Buclk@o93S+(z z5c+Szw6@NA;^y>YPH4T>GTyyQO+!w7b3tgO1x(vjDignFV5GfInGfZ5-@FE1zruM> z-dI>@wul5{Vl+qJVF4xcXHpZte~r&LcM%`(AweO&nGYHr$b&UlIf%Tz6};GU@F4Br zK$&GL0noRzTX=nIy6F@fdUYmc6{~%?lq7OzFPU=gqm<6BL!@!YR)b`TP9cg4%S3KQ znd!l1w|SczOF`VVEo_HxI;rc;E9s|r{k-{X1dq%_{ze?maz%e_6j zZ*}y%KD7oLduH~^@0C_wnQ>CRAbQqhEFh%2QO6Q@9e=6yNs%>Uc(tRT$thJ9GkvzB~G=%J+_7dYUxI{gd45Fi> z6MWLn&i=S~vpHp^yn3;E**4&F&dkZFbJxA~P61Y+f3v9Jy1r_${R#?$hlo(Bx+kg8 z)aKDEx>c0#2_{E7QgWki`?g1ifM-4$5U~~2uxpWx**tqoS(lPH`Bd4*S)dE>hS%rMe!+WLXHU^@Syxm! za+SSin+&P!+W6jrf0{ZW>5r!lDKZgL`D(SS+$LjflbeU(#{u*qN_f1G;L6ADj>{hL z+g)>L%G=9b+uMu5YvV=ABMIsu6P1E_!Rvhor(dt1Xvr0iGh9S3YHOOak8 zI-!JqUy|cHcfoi5Awa027g0nPP4wP-6TONiA$sq< z_d2t4d%JtPXP@&z6IlPh*jr|1XJ%(-XWJ`&_NM)>fBr^~c<6rpUI$Zumw4&>x1ZB^ z(OWkD_KQo;y#CF99{cd)ulKiSJ^cxf?B0L(p3l0=Z*Kme7ryz*pZ?ON{(9f1U-i@9 zeE1_i`u_Vp^aVG*)tjfD_nx=D_Kk1c`{`HT)0q5C^-ZU4beqFx-=hDczx?qrumbz8 z;P*Gb+Qsj_vvZp>&v@Q{!mBTSuP==M;q}2&PHb;q^Jo3a)8F*YpI_>sx9Z*h^5I+E z$Nud_w}0B-e)O8Z{pm`d`TW`yzj^J#J$}*r?4@u0*B>swX5;VQ`oq0*FTVQ94W8b= z=r5`ddgZxm^lJCYy>G_d{=vd?p8J9Py`KNEw_WqvkN)7d?sN5jN8IE(hqrd`eAg@e z>GNMZ`1_~d?p^PC-@C%j;U~|%t=GKB;qSiqz^Tc~^2xuSIP<-aHEwm8iFZE!E*Cww z{*m8>XM%6t?`mJX^bf!I?BCc|yvq-MaPyzs{qV1kyTM<+@rXy<@rcOb|J(<@`^_JE*Oh*M#kbFY_uU_P?)K|1zZO30d%wNvyFYdPM}GXn ze?5HF#`en_{n#^{nz(h$A9PZ zpZE7ql)wD$FaPBaZ+OPvfARh+-rzk?J9ocNug~7izSA3C;(hx4pM25BpZ2u}{`{tA z3eUai)gJQV7ys?Om%hRMfBC^{JnC#~{p(N2*^jvNEr0#!#%td5fZzV*kGmV=xBkjy zZ~fjYRjQwVx&9O1_~D;_^&R!~KZlR`%F9pW@9}`&y?*n1SA5_fZglwK#_Ruh-OpU% z2S5JkrGM^!(%bvMbKi6SKmO`-uQ>Dl-(K(Xmp!w1n-6{O{SSHQlk%5)^zFZW+3P)H z@(2BAyz5UNy3W(`_qgTjA5?zF+h20(HZPf7*javm%o8f?28`r z=-H>5B%o)u6*vEf4SK!6Q#zLzT(FhfzNz%Y3|jxz2`$N^55|(wX0j3HQSyl zx1IKsQz_Y%txm})cdA=XajW)ky8o+Os8s%cfB(b3;xknK|Lpr8O66LiB+ma->J@hX zPpR_%{r~@p&(GiSo|7jnOFj6LZwF&1PmW>6sh(qpPRH6ju;%Ow-HvrzyM2M>dsb=6 zTJkTnN`>MS74-KGdU!CFC0S?Tt99D#I9)e9n6&1-cAkTrbMb0}%|3$d^-zqfs|OF)B@QOOesbSK>Hj*ayL1heh|m2?JbT2IQipt`eYd-k@o z>v*AB#M^c~$B>j(t91~B&aV0^f6DKF^0UtQGf=JPwi6}hmlFwiR5`!uZ1=lNa6Kmo zu=@1aSi1|IHX%~$7S^Kg`L}g;K#G39XWehif)6mZzt;f?TNg(B_WHYwh_cu*9W@J&abpK z&a5u4FU@T%&M$4;1?m>^h2qxquu83!`K9K@sk6=28mrlAuFS5^t^s-ikqX20LThpM z&hv}wiyP}Lp!&AU2na}6gPLG}#-;}~xw*Kqc9)Gi&8{xZFP+&~Yu*_v<(7QU$qlTB zCD&J*t&O?))#jvD!S{Tx~9$YHqB+*V)@*{ekBP&pK!jR-G-U=Xh-=zsGKJNx^l0N2d)S$`-)B z<9zc@8>fK6)!B`e`8zHngFI)S7a0fHY)x9le7#zQzxZonB$&iNkh2jk{d=M)!1AuL zwmi3d3p}z67`C1J3}D&a&RKwBd2tcE!RFk?`qKQ`UAS(y2&*UwT`~IP(01la`C@6S zn48p-i{|8lF}Yz(oie6>){Ut(V`|lyS}~@Ujj1WEGo}`{N?K|$-!P>b+Xh-Xp*#5p@P!Ch8X5PRB?cWt3B@3?PrPODPpo z=%XwXfvccF2J$d}xtvlwl{CyOrBpJKg&8KI)^@eII*Y{9P}B&}B?|i3NY(>CN_b1)QD(KT`@WINQ7+eANrJcKkkG1q_9h z-+IY4-$5I|zu(pl3=Lf0+f#^xuPKEDVenfcZOLh00NM0^SIJogzeKk42Zg(hI7@qVx=_HcrNw2BVm}-15(_notSvNm=r9nhezJW^J0_oKZ zWNJ1D6NM2oeFjY52dX;qQ$QY$o)aXk!N0=GeqxidCBDP54 zhh7FzDERb$GKy8g1hZMf9N8{W0({qt$(Bh?(96cQ34fT46XwX)i4x$uxtL0zZlK72 ziXAC6kJVQ_tyrRm>a@h(4E%0Cgjq>6(}Hh)XtQVnP2?dGVx8^p+Me%1Cq3{%aQUg$!(i9p~Q&6{o zkjnXTy-}&uoD;X)g&I>;ZDIZRVy{h5sqTL%E7{qG6<660NGd6w&)&u zDvCm(S|bcapt~AlXjIFEdW2!B3aAHQ7|t$#5KFmUDn(+cLQuOJi={d>Rp(eMm2#<) zj%YByP!847RD`4skYXv+%at0(QK~j-H5G?& zRW47J(@{)mFiQaDGr-md&sfYYK2-O9nmoTs#Y5fuEUjLp<0UNP_ET~ zV+mD3{stXZ+=sMIQY#mV5rRg&R;k9Fq*$M-a|C$9VJQtk24)qepr=$LxdZ9dxX+^+ z$Bn91>VL5-9Sf)QUx8F9CPzxW^+tE7ak_*a;0SR=F80*PtsGiS|ahRIYMMRe-^% zSSy%#f{vTH1JoP!N?|Ip7odwZF2_>6QRA8k;w>BPYwY@&fx}QpAgmK24Mt2F>Tp_DdFX9sF6n!t0K>He8nxH;qV_yXyb0E`; z4eCDhy;zI+TrU+W;zzMjHUl4j$n;tbOnniFq@PnY@au^F27YRhX+yS`%jkhjXtVp{ z?mXzI4|Ba}h@WckA--6w6@}+jsYaF!3V{Ivu4j!RXlN=ZwHO-khD#KF)B#b1zg#Y1 zSIrW0FrWa68+_OTmo!$*LSYIFe1u{O6bk%^3=s+)!&C#tZSHqfE5$N@Z$z;G>dMFc zO1WAG0cVb9m}F8Z);UiQkMO!GCGJ^P@Uu0MDfnSPQESvW#W=mF3$LqChzI|2xd5`6 z$Pg+qFsoLt)k@syYSbFS>6(I1#4PAJkTH=tuA;Z3SgRKbl?Yt93(piXiAnJPqjW(8!6bktQ8Yqa2U^#Rh19F>eP9{*7odR4bQ@ zH8dkx!cvBYz+9kS8d_MhQs#lfzW0j>d^X{ywSU9HNL_J@DU(tT@U+OeqE5xX3*V|*Vu+&eWfSZH zju!b@z}!-;9Q#>+;5t+r!p|y|#E1{QqGXcf08fiKS`hjuaYw5T2!y9qnyLxb;I!Br z0Zbvi2veWf&nh=0KP%Eb_Gn$HYSa>Ng$A)4YBpjw3kECUSutKkUrUt~(1SQ$tK&q3 z`>zEshh^x8l9N@7UE6Aj#w{&CY1Z#*1=Nk%+#<7*)u||d5Le3A>j0}PB5_mb^lnY) z;mzZY@~LrA5%Ju$_x8{d<2~k9(BIl}@8d|3QQX1gZ&^^t@jCXl6Aa$ivCurTu{gW3 zg1dKG>!(l8-?=$xONWq_NN4T5BNi+10s?*4f#)<|;@!*9vXuQN2!XI#b2f<@GaXmzu4X0faTa z>i4(VBQ4lr`+E}^fwoqfr`8u{SB)_Fvo2Xcg0{AQ)>qg*Mc&4Abxv|aFj#MGvPdfJ zu_)MI$LsG#yF6F>T}Q&b>a_jcT?e{Fhb`$udub&EKyBAq1&brnmi)Q?Ue|4dIxLdC zl=EhT1ArLzAioQSYe)$&8beB;9np&5S_&pQ9g)U{AY;WBtBw5w zbR!6VL6A&r0%p9acFzx7WW=<*1|6p+YYG337N@m}m};tsY+vg+&XV78tWD|1bX+n~ z#U!oq2}xe?5mLed0qwr{M65MuD(r;pf|46IKe4lTLMg@D3Z)XhZ=#^mUMj5cLoR-p z%8pIn?^sY(dvv(-~;`{+1dfb0XfguW* zfN*xvf2)7#1$^nr#A!1t9QIb-g-!~HIA4K~Qg+)3Z`WF0N_KCaeXoFZvfTZwyXSTz zDMjAU-E;U_qLdzB%Ka?0yAy<)1BkolNJv=fZaM4bk<9Wv_zH7aDP>zr*~FA{lS$Yx z3XGdOU#t%kykP@rf`UL6v_;^AB=`;`G^~edf9>NsgrE=*|BY})pq*FCKs_(2(9aW( z(1KWlc@yG<5!(>g%g~6ptkQ}&HET1{iJ?B1ff6dHkU>f3Pea_TGmsINRVay5vl1gg znJ|%5iPm6kBX8H4aLt8s(3F?&Q^2E5c+1hFyT8C;$K1ySew2??S;nW&> ze>HK%4tL0bNh={-oFp84Q?<(vy(`e9+QjfTL4YY+O4d< zlo*gRkhSi(au#gRnUjf2$3=l}aBIrXasW`|J zMWvH<}p83dikRcp80`#N+6cS|XXe=`}i z_E@wa>S-j3Ub8e70LrSZy%YEbaRUtb3?PF_W~CwQfAs|k`0rpcW;gwQXu`@e6m)dVcs+EnVje=tFzy@f9=N+lv_%9vy}NtSGyvU%v{UE+Yx#m)+>|7N=GLQ*MhD_KXDLT+ z2n0SH==D8^4%vr0Pzs|jfqfwaUqYWQHrRBwd??cKG1vB}mi1_m`* zN?@8IiwHHo?TmJXsK!poJb^Ro^K&pUlszL>GQYr{P`7S4V-Y^h zS)M;`63g8R=LBBGal!AI3M1P5n$MOI$0tyQa(OsulC$oA|0EGG&Vl!hx^vzV^ejX|2r`N>U2xbu zh19(5x#VcB9qc*Q4HN8}m2>mVIX=%uh18Op238E)VyHu33MZ2pe;cz#{p8q{Dy!}` zU}`hiK1QYi+q@U-u|XdK|6a%6ADMOhOJcaydccBjcOXSqyGbjAcKTc6`Vc-LsL&=H zfIyX4vOubJL2~LgQe}-TIhf1CfRi1NrRpD|3S}XN333EOGSVBdILykSI^lCI9d7V|q zWhp~K)xc<&Bw+>a{3^kFQTM2A0x<>qZrI+jHt`0d(3j-2jn>p>^{^|o?SM&X-v(iV zP+bq=Q3s;@$h-oF=oncAEotvK&<1x)(y&eV*53qoBkYIFe_fyz9pE8+#aIL;#=CB4 zRY>tBI``@ap(qed#*(T|GJ$R%^t*(I{3=`{WRq3sDp7X|6pDQq89Gic()SaaUB7)n zqJDr<*>t3JNm_4&Zn6tEfTeH2MeUN9{U6!}T z2d%75Y5?=McMc}3TbVsHxji?Z(d*vP?)DuP#`5m&RK0n&A0lS1Z2E0DN+~gAmj=}yq_>yn+(*%IwR=O#zc^W z4gYUKPn5c~pcjgG@h{q900y8#P7#XA6&0yNDkdOfG&hxR@JV!K`gRM(8SCa!=Jb$0 zlZzBWf7Inz2Td7-s(?l)in7)7cUgP8ATg79g-n~qDCh*hDti;ajHR_q{7^MRq^Ib{ z=)S9fcvO!O$7-tz72B8APyQA3VGEe=ylKEoY^j?FsvX4m#Ee~`wLEu}f?KqDMM^0rlLFq%>eV_>*y zPH&0s26y~+A8#xSPW#;s&d&IigH7*3x95A9hx0wkW=^}I0B)@V&7ea0#e+Exbz(Y+ zaL3(pIW7%5juCfPLOQrNk-1hm)x%8*x}bo}p)L#X^evyJ4dRpNp`>Pgg5Yondqjh^ zf9LP@_ado9&WD^G`cchE&_5eO^ospUV(K_scE3wQgbTAC;2%YT+B?1<05iaAqA4KG z{@l|p#{>3)xaE3G-8mQI48o*D?IJGTiAaxH0J3KDBzv=V|le`O`OMN$bA=zKyt575UY6k?Q{G{)a1O(ES$ zYuQ^@3Pa0y1~rx1c&h7DU^U_Ppx8tx*PvKQ8Mhfbt%OEr6eDdMgJdW0ZW@v{>Vx1Q zPD6*#bR0jT4#_+IEmzitvLU^O8#Nf!vUV8Ua5Kq&#rl5a-jf7m?V&P;J`@@ve*+p& z|F!)_)@;G{BP({idXQD8rI>v(*qzo-qQNPW^ywf9-9V}&)g=@vE%cBeK?~JTN88^! zV3D*npD5%Z2D5+N_5BO_dniOjallwQ-Hz~`wt|rCXKaQ!5Sg;XTrKP!MAT`)b8^jQ zER472S7Dr=?RFP1ZCq)-DpfK0e7b>Tuf=rRXb|!fhvvnv(#^jXO*sFLc|^3B0k|rfH}hTtE_}$pb>7 zvH+SUqL37!g(L;=9GI3he|MMGji_07>baE0?FH}=XIOE1a&blnh7HLKEy+=RtCNYg zTN#n07$xp1&kX7or~KW$u5%x$cdP;16#W>g`#G_M6A4unKMB#G1%O6Y<4hebM~Lz` zmSb?fAjfj9sLv$}yeQxpPsGuTN;Z{8M-gqV6+e2TG%B1Ck@!kiW zqhOdcHwP$_8aH*^f5O)@f5_0D0@{+sb<&bnDdX)f;*;)E9Bq?=@Gp>4iffV9H*!!q zR`iiQjv?MhJe|tqfn?Du-C~)yxzL&!OG}KO`X@!(#G<;gh8{2Mq1cqcp@zWoE}EpsOp+-HOrEo_yTUmp^8@vm?HxO|@xAA; zQQD6!+sFfcf4UP#H2W7~OGa`;Wv>9oq%^+(hUrY+-AH4w$xa@QosTzn2)%7AG3F8{ zVu4x!%?YE;hRSdn?KQBsdk&35eC!3mg~}vPBMY6u;X|$%YQ@N!PyjL1W*cIUX=o*S z#GpE|YU1agF{Bz*Wbi^230QbaB0xs#ZTk+b!IETce{@b+b__y1f&UO%drgC*siQiJ>_J}K_*tsIB!p=@~&X%%q^?Nc+NKdKpwYmkq~p=i^C74+Na zRYp4;Xrc&=lU)*h1%z73>ylox=eej-l)eSa#|Qkewj1WAGi>rmTV+n5y7>%Ee&bCL zX!@YNUh0wzy8CJ)-rjxH0G=W@}B>o|bH*zAb>5V%ExV?=$$!(wSai^_sLeru` z`#Y0?PZ@vP-q~ug7>0XaTyEQS(bL~S26w^t$5>tm$zoSA<^g~kZU5Z0FTmzS69^Xj zfjYZ;z$EC8dHq~#)M+uabs9$F+wp4Apigj?61TjNIf(P*1!$fo7Ta%ni$p3n||lu<~xqvxx0t_;K12;LW#{Jv?IiDQsX*gobdJBjtgPg zxTiz9X)bA<*JP4O@t-t*94Tx-YMA`3B|k1{gS%-^OzdasfIS zkk@~%t5WLe+edH5d#yDy=%LfQ8BoO+U7edYhoxrO#jt6;hj9nUB)v=!11d)8X;w;U zJDt&hI;e%C1$LmH;6;`pjR3Li(qk^V3|kjqV8!>4n!BB|g6JnTEg8Yi^K8Qz7a342 zd{6`#r1`$4xHL&J{+U;=;ewCxMn0LBhMRw06g|wZ^$uWK!c<^fMmvdoQ$QJY4?I_h z3iX}LTO!3Z2r+PRNrGr;d^ujCzJ3cJ)KZj39FI(bsM8gUoTob7cFl0;aW2b^BYiEkA=I-N{O?k0cu zc1{$!nbw%hN&wB-1}~}ys*u(-L7nNm48NY{M06!1HZ71>tiT%eY^(3d`6((#$d(z3 zp_dLOO>#D4EFlx@G`$9>*x*6dH$k*O0G=7le84w03ULAi)EiC9j)-VFZA8K|JFCvU z`)<$atdL^jwk@x-AZ5&YFfj;ygsTZ!pp^X^2B0_8lOR$SfA@O`n>iL;nkwu&){f2Q z#iVn@FNQEP1I}_eYkf>FdroKtv0{L(>9tn}YhM~^LInqD$IWKaF%I2?HP|o&`}Q6M z6uZt=cwz_S1HGC~zfZ8OL@>h=>3IBO0)`>K*Ks{yN04RYj|P4j^J83DUfA>L;kK#*lZW5SMM*oR*oXFoA#|jGJbR*o*4P42O+T0k3+H7<3J|<6q zBRBLgcYH9+MG+o|^Q;{^v_W9(QWzS5rB$A8H!jGcQ+Nxes7gLj6pE8C*rF+<67W%O z$I8>awzskhDIOXxeY^N(O%XrRrvfr;q*B2($N_Poe^C@L@V&0!Nfd?VlM$x!ZWtAf z4}h>~;*xJMa}$k@q0dt$EfZGcue|kR$EFPlyPFOkuauY#IEg@>BptyATwRJEk%XLJ zCmNFnu#&8z!W*0km`8!cWocYs;MEnao7_Z&35j#f)3fUfYa5HRFo&O?U0|~yC}^H^ zCV}5g9tL+V6&?t3Ah_Gf_my_7liUTxXf+ILc4u z+=Y^(p2-Ca6F|v8Dy+lY$Yxg(J6T%=tZu+eR?G(k3>Th~>iWF_>tE2N+ZYbBBQP;^oH4L*hVH8C$5Hm=@oG%>BpXf0}g(z+zy@dWTZwA%|%#h|uY zeU9jy-=%k2w5ln~kZwpbOdoKo@4geSqO;6m_oR!Fx{zI-(!=;7>25sAwsDA&9!$bI z5?Aw zBtxKz)sQZKx*rmx96aB~G2IJo=sw^Ds1B}aq`mpg)%lcM;apyRuE6Y33<#GhK{8?p zkC3Sinn-g229YMSJcZjCZ0T&LBa=tzcG4Mp1B04>*`8;?-^q|G|Xw^uwr z1ZO^U18~ASG~afEsH|*QPbV(}8c$s^G+UT8Zc%7|RTR@fKqf=CE!j9xI4_c?$4nPb z5#?cw2AcuR>p=K`RF~c)2*A7O;;eNN58Sl@XwR0f$govYvIrGc9q@M0d)SqK%t(ad zrg_{&3^l#*mU12CJay{=*Yli?0yBuxvVo2uq+;0geRA=w8I{@_(shP#&!NDmXRCmS zPXnlbS-^b2pHiKeJD~n4R=@~g%sl(DS*vKo#6T5T(J;`br<+c^p8#qr6Hb64&A)69 z_e_aQ92nG@D4(-{+e6VBpASs4%3u|{kwrV+A*n0VT=<%2q z?Met*YanUDJnM!e81wm7n!D2dU+pmJ4wHC)p${D~Nq9nHoK1|~0U0OU3=um*NQ=+&%+$zO@)a2cv}YVkMLsn@dA+FdR76x1m0fgz zcG$POv;czm$-?Q*E3EGO;)XlCK9hs9gD60oL@c)ly6GyE;iDFCSh^p0pJ76bG96GZ zEZ%zZs=)~(Dx!Nd-1|o8vGGuH@N$Z>Mn&zP9$b@)S15m5 zsyrq~6pCAQ%LGcEijRXm~T)~$7=vTmW*@=Okwq7Fk}pxR9JwFWGu$w2d*`^=M* zb0(%oP_wkvPjH!1X^Jtbt|R6RjoLyiqu&c?jwLxlsB4l&$@3Cx90T$$dov9F+)@$; zD^4ytxbtqpP?>!^Q5%PO1TgMSWVnCKh16;AY7~{>OF>EZk#eIuK&=_M*t9+kWwWKd zeY=NUlD&$f4qnM~ep^6TbTXoceTC?X%A+ei%bNPMuT^$S*RR?{nmG0_%Ub#AX2j zo6umDXxs#;$c7E~n(>(ygiPDGhbi$QsX%30W(VSEU7&l^=--DHC9gpXvuIq}T z9FQh&nFLwt=DBzJmJ}ToHBqOQv{s~#wrmd{159OZF000orspz%Nu7zK%+n2PW{K+La}@MKC2m4& zCmk{>E7i`8Aoc+?uHsT{Oy0yubv1QKG%PW=XC3WyZ|HtwlXY1tf87e_$YDz#Gjh3M zOB;GN^UdSho0zzNGy7f0pvI#>%tM=vaz^$#4X}8-H9WqIF!n8DGYb^3eD#;);WBFkfw+ zT3%diF3mOPHrAKs*Y3hMnYSjb**$uU9D6`tf<1wAoD)S8b?I6RRz+$V-Z>y`bh?lL zqhG@s!5te=w0Nc`+DT=Ou$4sqRJkvnWJR z@JY0df^>Vh9jxim-6v}&pdSHckW1Gmu%8`J+>XERoi_o(ea9K_mb=~Wk;HQoqg)X~ z_x}ymR`KfnEmAhiX|a8NZF4dPv|~e0_Is21S|WdSE_AwFuW9`}Hc>YX&R3!g+l~vy zwAbD_$XhGQc`G!mP=c;h5uz8D1oWKqB|vCufL@GB&p$`4mi)Os*(&bFjHX^Nn1~s% z5;0dr88b}RvhzQNF*xFyJftw4R#$s)K3SQ0uBnTXdY_3(X6hvBA!I-u-yIjX1-|Bn z@|l0~g>A}5zQ!OrlO0rX`Q=d+8ZcD9HDGrW0M13{B>C|~m9&vIRgGp~5Si3{vUekv z^UvsZv%vujTp;ka-Cc0D2X?+wxNs>~h)N?UrI`!OGaHMuD=T>5wY7fw^!%NhEwHX# zXB&*i@zFcm+dUq7XIB?Z^xD1MW2N^jxW0eKL-5(zr8yJDJAmOlNj?jH|03(uMj8`y z6(Tv_Cz3{Q4xyeWVJ(~j4WNwXNTZX+;2sBUINt;p(8le{*xms$CMpwEkkTZ1N~4a6 zkre7oFmjYYO!oMR2433+5&GajIZ`Z2Ss?r!{ji`sndd{Fr9ZEX;qO znEm1>+cP)}M5ZQct3XgS{-wt}Wx$Emo#7*=3|e!GXJ&>E`S4M>V}^*~6Yd5a?=Y5a z{8a9_6R}9cQUu@J$WCnaYcyED*#fc()1X@Gj(jtzj2g~KF# zECQ991ixrRJWpbq2hKn*jCQ*@TOkyra=^>1YbOg-4@gXhjpyW!1bh`L4uF47W=^FX zw)ovp#W%=oApo`hO>Qzu{UeMtyiI9?%@H3CRzA6-3|R7RIgKa(RF;4a{U>wepVC&p z_aCeY$3Fz(RAb2xm%TGRbcE6Za^fN=M{G&Fi`YWRTLT=E9d?9njnk&ED0zZ;|DlEP z&d@qLztY+`v%0*FQ{DNcjk|x0jpjAsL^YG6Zraa)m3f-xs>_hirHUlEf`&h}O`e0N zATk6vI(tiNM2>(BuyFUIy!P_?I?neZFapl#vb>U$+1DRWfFf(=o4H7OW%aU z++{1{vjp`A_J+ zjLOvh%W^YxU=w84u2je@Hv?pOk$uf48P2E%;{-Kg1dG9e($w82g_ubJW1XO;i~t{% zLLxZ7Yj1mQi0_2oa+rUT4U|886q-i?{NHTTr+_N>@#Gm6u4%Wj&026^m04d9H z5|FU|?bzDd|8mp6<^xh}7mJsz2nyVYPT}DlZ0eZ z(J7|;xV!ybzQqf;N*Gfb%`W^n|0U+kd)iXN59Yl>lgdd~ab?Q=#s(j4#aQ#OCG1UK zL>KjmMdtm;+G$huPB!f%?C5G0&x=rwF^VHyBWdN%D}I~YyC=79-~|O)B0K{XK*l5z zJ4ry1b_{=rTmpZ0q${Nj1gvW+cqARt+7#=Oifyz8r1ckTMwMZrTzrxtv5WC|kK~EH zjQr3AocwW+H_b;mS?_R6pv`=vA}2(zKa;9hfPb`y_L3>Go+W<}XFVgAVYI=|P;>A3Lh>Nd zZTCXQixrBi0C{M-?5%OA?%2KVLDcq%vC(XJ((8+z0R8L&jL5n>h-Hsv^>Y)^AeTG| zs8gic({zGHjJc(07xz7e4;=Cqjm$p7-Olkfz*c{YUD+Lv8<30yZL4F27qSRH_c068 zN)|?U^*n#lw@!z}EKjm%B{CB&PP_scF!YcNDHk}}7D9f8Oo8Zi=mHazRMMd|jo!XT zIPo;^SjrpTDm$Qhl0PZ8j384YuA}+KmVySTm8VS=k!5mCWRG5Tq(fBl(uYJ>${i!I zI1~2cs2H(9Lr@Ae8E&)EOqGx~Hb7{r*mT95&2%C8IR!H28tXuZOt)};8 zqAYxQ0Ivu$OnkybmDC5&5k}(eb9@_PeYM%zn44d1o?2U8y^A1LhpQ#!SLvDHaR3JZ zClP-vv^OzF(5i8BZ(u=7ut0iPWjwvv{N0X(TN);T3_KKc@+95zq+XcQ?b2eSRZrc=H=S)tg9KV#W%C(e6mHyjcj$>Nl1s0BZa*FFns!7b@&jD!D4vG=U?K+;9J%IR*544il4R?5>kJ9&&5@SPfFgEyzY-@jgg}uKt zx6wK~JJ(!oO-g)DKMPBoNWIWi@MJhUhTIGbgHQ}3)18Y0SS&MG$&ve?(siPssM0@G z3Y6k`ySwRhd`Um)j=Zkl8_#X9zq#Sn=_XRBNND51F6IN6sEtM7E~F0x$0D&Xp$ZZN zoyK>;fS@y7KL}hbkPS>G!dicOP8$HT(gVSI7+>yYf_dBi?myT&4zQ-0WI#bd(BCfB zhz%r!kkBlsh#>q@lqw=ZNCE_60wke`2QEa#S zmd8tYp?Jk>zVFVTytg|$yE{8OJ2N{I+!8nuha-@}d(Ewa=!lnY2`qnaCSNQD3dknr z)CzotU|ODA98jy!LtqtRhknQ(P{d%&r2;ni1ME7%;)Xoi5}0Nb?FT#wU~X{JflLaI z5C+3OvHkID=mZ$2iQ}OnjVFYD4+?mQEGssrI=g@?R~Alyg-o`xbhWc^s^=w&Ry!Og z0$+uLib#kwpb7^S?-qXmq=N&chXPTd;fKHM4_{rX0hL8(__b1@t+*}4i^;zJbE|P~ z6(QhIi52xT+Xkc!p-8AtFakVScRiOEW6E_Z8$Xz$(=Ad$lOhu|s!Jm^2R;L`0W<}+ zs+l5@1T_hfz!7LYDPa@@-Jkz{TEq6Jmcx|))K1kRxW8+^YLb6z8vNoNIXD9)Q z4sb^0MF(`B9SU^h*s1lzAi<)Rgz~w>8Hs>`t1Vkj(Sa+M`NGDp#7!UhAyW1vK~c6c zI_xx28w>2we0_AvP^>FZP#xAMeq%m6Hs61k=ig>U{CjY-ZnLQJ{nJ+;Rej3iQ z5E_X;Ln=a9G61G{q*vRSC_^utR$Rm8h!;YAP{`SFj!r;qJioF#0Bnt5x=0F@)jQ+8 zBQFmef?|IivEm6pJ2A0I(1mi06#o4AaBmnX0Khn^xZYBM-J>Z{L996b7;yw9mm`#z zGky6&63~30CT*Ex31l&RJO?4H+4!77r@G6WW3cPl9AKXD zZ=neQ35dEP0-BB!E_kD3wr)LqZ0+%90%<1P4f>kR(%N5n%o5NnQ zq2e3QcuDRp1gp5mcp6~X9eBDRcOFWa790ssJk`$>9Uk!-dV`O2GXNa$Nyh(hhU|ZW zj_s@Px2#~-x{PrW5#Frq1rRtI^5t+pqyQfD9wLnKu(luD7v9ul(6IlhH{)7?YCGYT z@$7CbI$79Q*-o@^@g~v^PX>QljBoI^AU7rJc7Zxozes*E@f;1mLPwv)3l-61b68_H zllXGSTKWF9oaC-i`MSsK1@Kf}Oqk)2dN zIRjh=Ej0jX3QLlVbnuNi2%;$5V5>h3sXSm!819b)cn(AnIX3epy0RxYgr*w=22`T> z-*nIeVqu|o^7;;<8u=haUc8gBE}0XJ(XmdBYN`g2uU@VaJ|4}~a|Zces%Rkq3vA7uYq-reKQXJApg}6c-hv@iB&$i`F>RwB&SPDG-Ty&Fz+W0!w*bNaA|3 ztPyN40ue4*BHWwKGpT?Tp>cmwTwEbPQM~l=J&nME*z*I}Xmi82b`+EvLs=xZLJ{^Y zAK~PPlC)PV*tnpSg1wNh+F~{sIJ7)6irA$13E2KfjzUapCX|xZn?VAbeH;sd29U2$ zlcU-imMRg+GE}^a2v;c5OQ5LvOBfk>xszF2D;B##?$-P&C3Ch`GJt<3IUHF61|GQ_ zFh*jk73Oj_8F`$!^MzbdICW(GoTHy;TrjU!33fTc-PU1(qr11e?F1Ja$Ws`MQH@Gi zO@%HY-VT77)fYx`2Q1Abm3Eyx0p#lTwwh||>;gh=lByV)7&opm-qpp$(ZSo=5u^ci zww-1L4P7RU3)xwWx3Yh0p$t?Ndt2LD*-elyXw-B;0x>4J<4#VFPV$hLqZ$vNg_8s9 zrn9A!qn#ZA&Cpa99In-~vY!m1&Lg-G2dL9?0CF}u*T?~OwzCCx%!YYk88JvDEG_J; zFfa|JOymmrK~pppMgc%Y5lB#s0XGhEL5r!43o>Cr#fYJjkT!oDtQ@c&D`?xo(#6%n zuD-0JwY9SqR#IW}c&c{`HwzaFCz+9?n;ILF%Qsw$mWk?5R8kCVH~@w~k%s8!K{4n| z92QUxaZ*fuyDb`Zj)A(Y(ZS3QD0QMVWpv? z1QY-6%n20<1PZfwFm>ShO;--8n8OzE+~q+d!1W|07QlZ?1m{$S{lAm%7U9DG9kTb(AxgQj3QXsBg`8p4(Yk1DEvVF_#ozHClV%Lrl8 zK?6PrF8-PTi+(o=6#=sav#%ph3CO8feyYSYMKnnx6EpguI1UiG>pZ!R(@1^b$o4>1 zeo(wN8ohr;T_)x~9NTSekbrEi7{=HZHp`JsZoNcnGxTz=LiOFaspC*fu>J+OF7pD*l0rzG#Dy&_=UK1sWdwEzv}iYz6-y zuTLTsYYH9MuwvK*G44{v-i^!*m|xeBw-ghH(>SdeU~M5NEgasa|Ih{-_t9@hvWXmX1He~G-> z(5NCxYV3DWUv6|W8!>SG!oiJcX6wh-tsEYZz7Z#GYBx8ihutU*GmbS@SqFzgI>D2- z0*in8(MBJ!(VGd6+n7T+8b);E!55D`rg-G(3e?^3KR9!q=xPg(>ZDnR(*iBSuC`#l zBL&DHjM<4n4Lrk`xE=ux^a>@8h_Hw=tOOIO_T>qhZw)I#G={2>+CqOvh31-qY^eE`U-;M(%fwT)G2N@dMMoECC^6zs9V*nl==WGDkhvLK+!0;&NblXJr*OPR{( zT|H_<2^~Wk)sTk&wuU1vp#yhs3l@`xq<;cEGBi5ikdwhc2`~wnMmnQ3TNh?rliRi6 zFii+$hvRN?5K$+v=OW;^xI&L0{)~UrrI7*Cs2G<9W;B4GB$NkKdM+a1BG$k+WE-?& zM}plCB&C3tp$!@2xj-9~A^|}`(%O0<0bN}K(}SuZE>-Uh9V|sRX+asD0P$QhI(D#v zj<#ZOBeiKVe2>6bv1}s))dgp1I*m#p=SBpMWJ+$blS1~)zrSns(p)eBt5tvfLdQg^TgMA5b0AkV6tQK(}0> z_zJjjeR@hUkK7Nrr9)sj9s{YPjamSGB}4^6S*|$gH*vcD*;R>pMRcM8dqNO~f-11M zUFz7T4mBeiQv%8J zl^8Vn`?t{809|D#vxSfdol!^VvRwePB7ZX124A&xTX{VOA7?avibk4DE6AOj7+9Gb z)<`bJYY?;YQ~kv%$@Lf=zX%LKmr1Keo7Cr4I`JV?AT@*0kX<#Xdk}v*=5xh??n(jy zE_FA%&JoOpkR8JehS8|QrcWQH+w!ernT1U_fcl|R33jj77Z`^O0LKx9lMf$MQD{S9 zP+Kf}2;^~bIuS&wH^t`~pNwd7@||M22Q$N?XfqN~eRvl!IOQTV5PPJdA4u*3I7-0S z`e9L8(Fb&?4e?|YX<>f|dL`Qqs*K*(gMcFfn}=Fh&^pw9BdBbSM9LP(44V)mA|VgA z#-mo)Lh#E^%)?$9${okh)ps635Ks6l2Nz+205QS&jl-V_K4DWX#;`yxQuPK^0-{Va zI3PZo8pMnA6+!`;KrOa;?Bo(J%- zX%J?i5ZB~h2Zgbw_jY+TDN)&`k)*66Q;LO;ZxCGO;jPDOGp+VqMB@fhC|y7X7WK! zVj|eec69vEuCFSExafYp%H^GlaBSNF}X=T+)B~YvFKX+@vX=i;nfEO(euh zL=8m17Di$lnSLk_unoMhxM}pQ;CHy*3@6ZuIM_~<`Oa*NH%W3L$X{`-_G z7DUP@KdCyRP#KvX9ToUuxF7Ply0Q%-U!ex)reIO9G`Ldhov$%ogk&+~7%xVViW9I+`^Md%Bj_kE z7%G27!djkI2%RADAgi6TjI^)QbnHY#^^L*8cLbUM2bn=>-qsI>3^zi-`GejxC}wc| z7@-JXe~&hg?}tbFLunm(4bHj23Kerm^V7h70+5?8R)}CZ=-M-BNj6$2bUQ3L4KQ^v znv7(Ch3m4yz%{N;B0U8oiQl}y&)saq>zaS)8=V|Nj_hOF;yrZYa*FI!tN#?1KZ9SB zxJ5Uw%i;;E@3Dl!KDZ-TOr6daJr=fF=XsF?R>xjq#Z<-v@upVHmBN}Q`*#hYFgf9~0C(nN- zG&Nw78*U4UL}EB7;HnJf1T3dYhMEha(m#Q2|H0)dD>d*rL6ASA?Gw+`+=yk6m1KCP zH>64vqrnEf4+So3k~XuyMbK{Q}Ef-&2aL(ksV&bVUhSX5d1QZowakF#C&)%(Sdt7m_VRfBov}@6oM8XS}9Pe#77|l5J6$@knq|H zjs#|%(YlF6bBDT0Wy=oD-@Q*DF05c_{nN1fN9n2YKGAYKP5dD^ttB>r4ZnZMESG4v zbpba2JqxeJ!TNhcZ>TH2)<_g0d|wpEoQ*@gvY~#RD#C3^M@62PHF=O7p2 zzMx3fISbAcriv%|0?p(LVP+`0;)}k2hg;OoqJ`NB$Y`-66ri_Z#qGUTV1 zK$R`5#}oxGfjcT<#bD~APgpUaX1`O;F;L953jzz9B4`_{iBXpFc71KV`Mn*^m5G=#$A%|tc`v8@nGClAmZ zZ7)-Ij3VIB5e4+s{vT+=nG6Cp)pX1+eEh&Fl<%A(j*bQi=!iJ%V6lG`YT|V8`gHWI zv1Xuk4dWgE7q^dC7Y+r`8$Gg_EL>2ntdPMqRvnYf6cBZ?yAY%-wBF!(0melDXHJn& zR-r+-f=WW!LNP3Z-FpvcU_7ALQ0T!|6}-oUH>HqH8sN~u-ht4Q_bO(|Xujo?JdHIz zumy!m5oC45tyHoGGl_qQ$Q%KiRA5wV%G$(}I1?|df#J^r;X>BcE96j30%+xo!NXe% zrbYP<%NrSKRIUhiny}PC>w73}*=sOV@R6;eG36l>mz~I8wx?7*MW!s8;yXX2VZpsp z>+fqKj;+|Y8Lmje@L5jD@QK zTNYQQ1!}#Dn7Ds44vKqvVL*5sDP)(`1T6)yVJwc3 zklSH|Iv#BDDC%M3cW!Ke2FQm_{mtHfYW+iZFsMkN{QXZ$>z`q4g4VyWiK($66O=bH zHZlIa{(r@TMXwPXAV)m|B;X=lD6jEPI!8?T{rrzTEQWtC)7ON_H1y*#%$O`Ao-f0c zW5hM#8Z(W#EGEx@@{d0ZhLPg+Pdts*Ka&MsQH?3T;Q2pa|1HixwuMsup{E7@Z^U3V z%)hZQ6Uu)qmJ#bW|Nj+_4N%%qt-+ZVdT7(Awn7eFQ@!3@1ZVu3>h+Qvyf#la)>PM2 zcMwT{gk^sV?eKCMNNE2R9b1n_G7B*GbpfSNU)IMck1^6be9~u#5N?FeXy!7=#$Q zD2+aVE-~p;0?RsXCBk8m#P6F(g5PR)`?=xHX)OtYW%g)Ic>J zsK%B}RZf^_@CM}#dk{BcKx%$~3ZYSu4gIAW-NGg#D;A-a`GDMrTi(syGY0LL(5a44 zA;rfE^U!kO(ja8iVNi*Ol7on)n2FL)iX4Ah>gk4y<#ofARFOn92ROg-og>BxLPnQH z0BTtLc`NMJ2(b{6e+SZwA%`7>%aI^&qMQufC?phu_`^j~0Wj(EgYYnzs850Akt5k05_! zCSiOIPakqnw#=0K@?epf!PrXklSNoAV4fCdK=Plpi+0l`d%e2jf5 z&LoNF-9fAcE2z-y?+1_|ycA@2E7X4vYY~_OX(-xfcoLMQTF4Pdx!5#>ilo5JKnw_6 zg8k+PgHaRn(5%8?Zn*o_;D!<)kpzoJ8W?~NVtN2r6Mpqef(}@U0YXRYRh=sj31V@42`rlvjG^2n2ZHYA0 zq3Zy+Hoy;-w5$dGt*ZUcd|I01U!wnI8aCGdG7NvO|6lUd>wlfBEGF1n(IpZ8psMHh z^I!I`m<(TIQwEo9#4_TtOpQjE0mF~k2(F1Si_J4N_Wd{Mf0-tRzxBVr;%RaI|MmJ` zmMODg{#izbCYb)0Y4n@_|B8QSh=+x}9o3L-s6NESVj^{*bRbp2_8*Cz23cHqhNLmU zW;{||Utb@7Wg1e4G7YhLQ^)hyj8tspHvz9_dLuE=1{_MQ1#EvH4l=OUmZDH_x(*fz z;lbA#%O-(J0(lY2q{zmP6`hP_?ywaiv6B29iI5=8uP7AzjWXfLlrw)ap;3`Y%V_lY zHTsPbbwJ26kU(xa7+MXWj=i*l`a6)fuD&})*h?!R+ZQy3927v0>P6Uw?oPlC_Re4w zxD#y~#sJWsXeRP~oFe5(LBsln>iDI87q&l0m50Px&@uhx}j@pP_FW{1`D31}lpM|H^)K z#N6S~X9oC;Dx1{q*b?N(?2XM!yQy|KnQ*>Anzzx`=xl7ET*&&Y(N|6}~t z|NWAuS^rP$Oa-n?{e$QK-_L*U)AIU9H_Pb3LVwEN`7{6(Kl(|Lv8*zjo68&hg9=iU6&lSCFUN_mJ}G(cdaKn`4qH(q`n@gm{_(|*72187SGRAibK7;rz4rQE=Lx;3M}zhi+#UaD#FMX|tDj%m#OcQSR8!)Z zH9GJar|`6YN9?3^^pGc8qTYEj)Tg7rP$&90ubYw6OY?;ct4>sn z{dbk(>*9knsY=r|DXSiQPI-8#d-?mP=WQ;VFSOsDz#N#8-QKsP1M4(>**=fB>*G3` zzhCw2ui@OGtCj_g*B|WT@4wLHVy;wk;nKGglv1Z>y+4u`eIV;0_2h<4S8}YCUHvj` zef21RCEACDoRqpFK{FE1mpOko?l30!RzlC%sE(DlSjBFKrl*hZu+P{!u2+)(7W$|A zacy@FaqJa)&3)+3t|99~wHRs%erDQHe^DvlT>WUNLFnbhfeHJCahneL=TPE<+#e+! zN<06@LU+p1_2LTs{GnrJJFgm1eSFN1uRVT$ggB3s2G(BT8ZJ~`zK^o!$BqHt0&WzF z)-G3POc~JAF6e|rGj+|%Lbt7>4nNU)ze;!CkcYcEyBDoz)jsZcOPus1p+_v1fd8dV5CcIrQ3j?a-XFU55#y-=7_S zwlddmS8Z_Fz@D+sUjw?~IK+?7bUT`0<$iai#x~O)<0jcj78=?Ltd#^(A7=Zt19Oa1 z)Ji%8pBnu2)uEW^x{COP{bwz@wcq|ySx6^V&q{IG-pWIDg0y+6DLo4*2Ryqx@4Tw_ z^}{|%oOSoJ(`+3kyLFka#9Fmh<$`m6b>8{KE5_876?Gar`$6==Dm!?xB9XL`nT~E?*}&EHU6@F`fm>iv0xYJ0ckf%nE8H@_9Q%G*! zn`>U2N?zA1_|E7j4{oK!6nxnakf4`#)aba4l9pc=*gwnT&2Z`i+Pc?g_w>7|I%4$q z9+p!U8TlKj2l!n*Vw$O(9n4uhaiwj)_wP;*sOIKNDz?9w`}%c{*ioI{mOtN^?RM;H z`O4Ha_x-NgsA&ahrVk#_Kc+f=NG<3fZ)BSdGi=tYzK9d7Dlo`Nu88QcVt{_t+Q8Mb z^=gl*mq(9Hx?I;bqtsgEQMOz4N#%ekW7{#PpQ9$1EQ#Mc+Ae$efYE)OYyzv^hCleG z*HdY1_1Qf$3ky4}+)a1(WBIy!%zPR&yWgU_l49#Nr#|r6)5m2W z>_2XnUSv|H!;`Z{!~7{)1^&Od(_HKnxE-jmG?G!#j)Gn&Wu=_ z;&H*uK(y;@#+C<9mYB}nTv}p2>g;*$BR|cROQnLZyWa53bXE_|(g?R7@FQ1yR`tbW zahzMLJ-wB_8YO&Lp?4`TafgSm^y-1^?xPr!F6?a=QTfuVqGadZ*OCcdwBQwYjIZ0=fE$IHx4pbG>KcR zHT4j$$8)bEMd$pVD%GgAyFM_<$3XdJ^|PcOhiqrZm!?w2ek$x_wu*NB$nb>~^Heed zhV__mj`A0`APa$ia@)abHldV5?w+-(VVmZa_byp&?)-RutwVNZ`>7A#&Z)KbHBs`s zJMYtTzwc8Ma{pYZHa&j!toW-3ndA0TmCm;D`Ej`J`Vseht?ix*i(JAN*{<2B*1j}+ zh+q4SA(5RomOs7n;JZm4+f?JtS^ZSX=DhwthEDoO3!tTcYF_&Djr}j%(up%>d32U~ENt>>HSK9jf)oOqGkml`pA>XG&dB4-xu9+K7J-T+qf$DZ{ z53SrjY(wUqzZPm3yM`(sT-INs_yD)K_{EwEdH&snl_AJaRoN!`r{xkNRyRIpV0+j`A zdg%;$5%Or0wvu$K*Nsc;#EKaw&&B-sGWOM9_SWQ za=wcHx!U$}=;FT`UeK-ccowocWNWJ0k)qVi`z==u=O$|Wb+Ol_%eAM5Who2qep)o( z-p=s$ACD`4I(?|LldjvvO|Lo|#nKs0=dLrSE`IjvW$!Ihj6I))noDiJIh7=(#*UnR z&F4UW-i^4lHif(F`G2Uzsr-1@>5F&UFWso8WB$zDpGFm~pF2{yxzbI;?bb)m(>n4OXQhbwi?$3jO^DBNRQlw#c;v2<>)m*d z{!BaBS+XZ*u%Tte!=&8x<{jGHUewm5kGqe5`w+eU9shE;GvBjIiF@L%O(XqhKLY%^ z1N1 z{lSoRw+054hldKQqCJ#nrKeIZkFH+!b_7dv5X^PgYPF$s+=%|z2T)>PdA^Br>tKxYd7Cmb+VU|*@2F8mrWXc zpvO4ch;X_FYwqEPwRSPL9qxT+gohPg(}>-cu`T@m=a*~r)=5(O^^7ePoHe^%sB-$_ zvOI@Ujgzi5l#<@d)~st=R8zTsTeWSC{@x4LvDBmUR38hkRjS5^7hat4VqXHw`k3v= zoJyT{nG{F!58cYn6b^Q|dgSboMJkLXZ?{o=+YMdgd`%_MvPY%A_QcL+sI*8u#u6*{ym+qhiC3kG+^zcFyBT^gPyoxIHs<#oj#oudf`GgV~PW*(N%% zF?|OmaND`}S$EHA@u_QN)7u)m-mDyV!?YlE(Vv>y(Xk5*RX6&aa@*g>leIL3=OgVD zcVjT;`NonZ5$-DQUhr0bC7F(mtSbB%E>Kme`p}6JcY3t1Yk}R7E@4Bc=hYec`yA8r zO7=ZpyL79PiBCvMrkfQr zPf0CqTSlFW;V4@T6{>a842sg`H`aBTZ8efON7^Qq*o5%9T#>kc_8hGd__yz3m5jO^;n462$czm>7F_V~x#&*a@nM zg|`Bc3wv(oY;rVt$*bCLwPVXKug)yJZK2fHx^0K?dq)%tm4r-*a7?m>*>GkMGekXS?qb1qW0P+~9IWN1y*qUTax6`bUnOWjXizv;3)+t{wVkMgSn>yuLbA_i!k zIvjEWh zWVh|5WvbC9ymrzz$9HA-zjQ00E$h24t;X&Td-u8fzT_@1HlNtHLd8yfPuUqiW5 ziz2Hwzoc%sv^aUNHItXkOm9~bo5fJp=O|@RrX8JP_++2o?S1#FoGYhz4)A#!aQ98d zR#V%5E@{fu%ZF(_@2&Cu)x}JT26f7jok5a?8M;C9Er%QBY5H7c&YQE4RuIoWWN0A@ zn)=%Q*`KPrkIk!5KCzVZg+A!v>(JAJ+4-NT{eN5w9X>y2dB1M46LwCHUf?{-#`TT9 z<>0fPb4v=i`95(r>gh+a+VxTbOylyVoLl*SsfK>-yE4T$Qp;;p&C>5|$nssyeSE0* z>N;`R*^h(HY^CgEbUe?PzWw^@60#p{8agtdCSf z+xux;*GrMdOJ=NFWncI9)y7i}0{&y=JyA!_Pk5X*c&DXaduJuH_+9h+yquH%#^YFj zY0O>qo1Cs)R=W*TFO5BEmOD*b+HaN3y{G+mbRT~$Ip%%ukhO+-{ZB|HPR=-MvpRt? zbnV;6nlGQ94?eZT%iOH&kIb`6ULJjT=(DOuQJ2+!VX96tF;Oxen)z`_bzMYOcz&o( z&&!k_#UTb~KB*_h(H)J0e6@J(r&mutsQl_QV(cUhYnw|DllRd(*u;`=d+ z!sk~PZn=Ig=t`Exu<$VL0_DT*l$cwamYt_Xot$~ydr+R&YnMr5Q@Ul^j#SCxnGY=f za;L3^($3NR;*d=`JGZI@%}U9Cx%;R_s(VXPW?S|}W7CN{%HtA3D%U$ROm}r1HYs*C z=dtnz>!S=`fr_Tz5b=)_C%f;@uy>yxo4D-Jea(`SXZ&3{ExPPr0rfd>y(W5)5lMDSMBN^dtltLV^?=6Wm}f5%w(U;>ocQ(dBUZC@3m1YO!~BY z6?k|;fNfmwLH)bMo~CROt(-5q%e?%?eYe)Mg&H$^3@mWFQl2GVH7-_D7%SwsXH|u* zPtRVXW-ygEWy`68Y;W#x*Q&mq`cpKty2T!^D|~BLGSTTgb0b})=p z`{LSGsP_P<_Z0RDc|6j8Dtp8Ib33B$3dDO0T;}aD8c~w+x%ZKR`O4|aP6l1=LzXSv zUokH!U3b>QD^+lBv?U@LhP(7>z%#sSG~#>c&uMr!zx!1NMs-EbB9WV%AWvdC~exiz_lN zT-hG_R66W+PST1E7nBEn8y{^deP&myEs5x$trD4`QQgC36@6#z(%Ax)^5m@cwuWKt zpSXMc#l}6|$2*BKZiRi&t=>z`Y8>ud-dZ{|_0feipIGM~k0uSs-bWd&`o3?N$N8Pk zwlmKz?K9mXd64CQiXrw6qBfGKl)#6N)%Oia&Mwb17iMepnr5}7c+0t!(XQz(0d;{R z(mRb#fA0To@qoMYUWk=zE3AB+5;sI|_BocDZF{dx+$42hy#R)A1G8A=FP$~+NnF!a zON<4b@AdwpNV(43ddt%_rG+7S0ae|M0$sxy`lABJojjW`r2JLsnwY?7 zp3+p+67E8O+jVMfx$QC+#roNYoE_TZWJjeZ8&18y)`!+ByFz(?;m5$zG2`?oWe0bt zb_;pmB_O-wrOjTSYj?T#h-H7B)^@D*nK7%)i+yHUt*BADcrN(tAdk6bJ+AnyKczj9 z_eecXm`w>v&zt&})Udr@Yi@M^*yk_nhfK?LIGaL$y}vX+nEiA>wzmgmSn!zeKKDYN zuOIq&e@6GjZ63nu7GNcvI$-}fZkdH9KahQIbg$Lt3_U#QYP2a@%4wrfq&eu#f%gkml*Lxgld5D4?rd85 z_}MYmq4%ynH`yTz)77Vb_nH19D8W81T78_+fUAYW91r~&wDr-{#C;LP%a~i&Ypk1p zvTEL`Q!0WRE^7zQ+m>=pZIS0(|Kl0o=(FO!U3=!d!oz6H?QJUriQhUDTvoNY=;0G} z-Dh(EGt-x;?5nqDb>{)5y{O9%_*9l{-K#rS^ToY=N8Yh3-=(RX*>H<;H0_BxlQU!1 z=9m}TJ|3I*W$>4tv4*q9w2P72=$?6hW0a|^JUqB#FXg(=J|{Y)znrU-B5)Zxt}k3% zkM6tOD=zv_yR&S|fdz@*JD*#ZZ}k0((Z#BG^@7#0m-jwoso&no+9Mh|R5vr@qTR9s z%jhPt_bZo_^l{(1Va>99lB~V_Vw9e#}7Yj7btLlp(KwD zf73T+`MSJ>O;XnCn`hN-AET7Eu|28$VP1t-j!6dX6vN_3Rp!wpyg#B#vm)+gJq+3` z@XsGaTmJNG-NMJmt50_Iwkw#G&IclizhzYg@72J{Lz3W~or3@LdbCh)@t6lrdCiEu4iv+O`YqH&KKue#R=Kb_Hkv|O4}=dj_( z>kiNQIUbus(Y9L{^Ss9G^cLNtdK80&dkTTN`V6Nu`;cZq?q7>7BOWMc8itQLJW$D= zLu2_qRI1SIp`p+1)~TcA@SaDrHc4w$UPvBH{I+{@-TP--Ha(nB);-qn>jW>f>^!=>$Fg^P^kH z!#-E_EZt{*c*Qzjc;{=V*lEuFFC8n7*IZlLZj!V>Ie2$Y*y}reM7f1+Mi~1T)3&?$ zJ}}Q<LeoI_t z?A$k(GT1!k`{)?*e7yZ2ESiZDiqee4v#%h_wxIJqI|Wk!DcGd zegmW*&grFnedRy)aeUB&6dTR`6OxzJuvd2K+lRV1>1I#Po>A{?=X~+m`+e}Y{ohST z1RvXSQ)8hU<=VR`TONGA6h7xm*@O3|q|uRqLpBQmW4Cr-n2IUBPMv> zi23?%spHt_4}aY7-?Y#JRI^rjy?gApiVrVuIk%yE8q6C$tGM!rtCEx2jc~APh5J0d z@!$2GJ?3)4?l;>UL(ZrKM?d)XCOmRxUERyHLy{Y(ul-@3?auh?W|xP9Ozjv{!-c6G z(sucOf9NSPUmh8hRpGFCum6Bgb2|2O6 z+l5N8o7tD$p9k*F z#P=F6c0bu$_5I6RvF_a?ys7uY!ZO8UfITNS>%rScI*I*+Hzy_>yL|S+lc%evl$Tas zOfvlv%i)@Rx*JUQ&ab)d{G%}ZuV42(UatSFqnA?YiVyiwsj35xjphTBQ|YL%${nhI zOU&%Au9yIR@v^AY(tvzPP{E#s}XFFxe+zuy#lut45 z`WZ%KzfX8|=R&Ca+(AFAU!{lT+%R8%;{I;M*f~A>RU2#H^>Q!RuwYimezi|AcZ~B4 zgVXx$P_9)EUiQe*`2k_;`=RvI_2QWH3Q-!gP1=Hp$*JRg=23n>|FVZQ!pMZj;V{iu zEHe`}!^miai5ZUte*TTUD*=RZ`{H8_S)*))N=k`u_8CRCNLeFF3u6pM%wTMPk(v^z zXd{V2yXr}W&`KpG6_G3nZInW#64L*Eh8TME@82@-|9sx7eBb?+d(ZuybM86kov;K9RKb9A3#d50|@?;wFii` zIY;x`TYZp!pZ?PWj@<@Do_h*^ZTId>=}lY>qO~csL3@D)-p1Mj!#cl^;z0-7VEXc- z_ic~q-d)7{Q1&Nw-v1%QQcL$pOAF+?_dFY7%>p=O3Yit> z$hH&Bn4wFCDA|Vu-07NSu_4njK zqalbuz~ceQF|Z8A4MBA@5zr6^o`@w9@kAm!A)$VR>?WZJl}7nPBt$}>|3oAjkA>lg z8J>h3ibpKK85WDgLwGEKh#C@)L=*-hV2~(0k$?jGDu%=(7Els@A`)?6qT}$$A@TSp zfrx|%5C)0FqdOZd z7DEI=6s+z3p;8nLp@?`O9RXWK4JjJYXdD5sQvh5f&~PB+Fj5rE3mmuwNd#Dj*rn(o zb?8r%;Gr8Kp=Busg(Sig7)!u`Nem5%N)#SP0D3bT2?Q#EFeEDfB$FJX8-JnTNCeaX zTUr2Ui4f3?hJqsop50go0;7Y+K!cQ8_B<1RhoeAmdROA|A*I zJPQ90#UEgx1QY=*{O}M^L4Sr6hj_V5B2@H+zXA3Ee{uoeJX zJK$${@Wdej0~Yy!ErM1+KLjhZAq5tgjRXuHwwfaWdWO;;|D@uGhkq?lI1C;SG-4w9 zJ7gATDvl5l5C;Pso7J`&V-*s3{^K(N&kNI77)gthP?-3@>&u$2>s#DZ1L zFseNo_%4VbKLG)(1%?uIbow8K7y*k10s?#^(C`1Dc4`c4et!hyLu2vq0u36{VFZ4F zqyrh7fH`?+4hj1QVc!xkSfY^VAu0F=)q5iN0qg|>Bn0r4{X<0@252fk#|H~+pc)c} zTwN1jj|yO>C^R0ZAcP@B904rJfCmcjU0|;qB;xv;w>ZF7338aAK#zdd4SGlThpw_= zvlU=^Kq~~)9DjP14PgMl0Lw*!);P?Niw2;919b+Fk_bd2c1S}0DGFkgR0awXp|5cv zzjw{xVgK#O5O!hRJBo0r^@Vg+WfoS>#hGx@Mb=+|tiSxh-Tj(d&>7Ue=B(C`I&C8P)~>2g3B!+(q@hbFQyD z#aox=;!a`Aqte)ONy2Z12eglH16?JYMz{g)sJ|r~q>cxMM__UoOuxQs!_X0>j_tWk zoroElib!>+`(WJwsrU2*7(RX)Ns+_Rl=$~VWw&2&98m|@h<+_mIf( zbbzDd=YKL-q&gPp=cw;vut;^ljDI?V#r!aXW!LXGj&STc^smJtlCu~r^gp1H&7;Y2 zL>=HY{<&zJiS2E!jva=G5UVBX=h5uPVaWDm&J&4)LYD_oY)}Wkk6gWj(bsBl3i59< zOL$Q-j4f{%AOA3hreW;7!+4;F@ox@eA{jo?Z-3~R++o&z!>sRzaWxGQ$@Fd9K!bn+ z{WwtAy2EimVK){0+64iUgQOMl$A#5^`Oj$-WjDkAWYrObm#E{=->W);Fa61SBkqTB z$j)^*&Z5G;+v0n1$m-~SheGJT&>DO3_wxuy_;K8^i6zeCr0k(hKUqmcsiO(si8@IA zXMZo&us;ksHa&#%tYHIu`ahRskUH?4f43F$XVVP*<1}MaOgWHdY>xaPXr_*Y-Qd8~ z(L+3S0v>|}6S9Y?fh9V;!w&-!6TXT9^8gEB@WYT5-F-VAua3n3_iKP3r{{1AC>CPr zOc+*YK?sgIyhnmKfWRoUlFA@=q!8?`V1HF%qj+-&l?N*^y*1{XzJ3ge;RBb0qx*mT zzJ;vH`~%J!tOE5rsO0YANKO<=?`cyyOk11o`ulL(zT$SEl$;}F5rgFNwQXNProq=( zBL@bg+vC$+Wlaf#BErlMMP%{L-a>(l0joe1Yb1KB^Ep9y3WRaQ;2a4A5(SH)5PzLP zm;f1s-(a1QSi;xpe5l}lP9;5(582h2=1gbpA$F#EgJJP?glXDago(lWRiEw)PZwz5 z&G0lEOu+7r>e9#{qG0gFzG1SVyZZtPf~aC|Fk1$<>_&z$t+!|(7_{E$0lb5Lltngu zt}quGD9_KTt;GOEbwDL5Zy!)_i+?rly{%Yvc1ScIBjdiVP(d$x-(W={c`&|SqQGRM zII%o$EK0#Wr}T12Q!0(>;p@@;E-T=v>@`j5`uflp*_e_745-~D4JqABn*CBY27yXL zWGjmZ*V*y_U+vBK#$bR?@0}>!rw5%KaIP2q{qZ{}RG$O!JHTT0FTw9{wSPoL@H>KE zC6$4Z%bX~G2e=gfCHQp$(aP|qQb!1lg7@3t9MO&t8HXk#Nm#rSL>R&E2!54R1_Wes zBz^}3==@voJEA|2;P-p*%f45V1M$lq|M72G|HAwV4~#o_G6o5-jG;KfiV~d11?@}* z+agEsJAz*&l>r-$ICA~VZhu++x9HDEvJ(>L1fd}c@cct044LGJLOBtLcrw|UOmG^} zpGWWu@H=R$I|t&I-I@1qv0shgcLcxRg#M@4+woCOD47 zFI!mRzeWCz;CBSS--Tbc%!!<6zhd8KID+31{Epza55MgB!#Pp@vVVsokKlI%za#jC z@rwrd9aJiy8^4BBcOQ;MFZ_{gjeuXJcj7|>4$6SvSrZ@ry8Z((C={TnZXnp#Q@?OVu;*{&ynz@H*!1fLf<1LF=Rt$r!~6@O!ItQZ0|#P2sLU@Mi2fNB zIgwei`7D1SG}vndaDSfoWM91gLT1UH9h&oI1iKgP7Y@XLOl%xC5bW-RUkHr>`T99- zAlT#jf60sho3@lx1}88Tg7y2~oHF^p+Jn}&I{tR%I|3_}9O!M|_qunl?chWlV7$8j zbnO0<^mXeP{E0LMs72VDlx!i1*1h!YS*Cc>eR`Hb=-woL^naC93Rp_@HR@fCgJ1eS z0s2}tkO5YZtQFo&_`A9v;>+-%dwlZ}NQ6hBx%dEr_ny(Ck{I2)gt0v*SWC#>)0p0~ z6q2`vJBil4l0)_T*1^;<@Fw|#9E$Yb#TcNn_n>CI3x7}#vAa90?{;?wwC}b7fD?aP ze3A9pK8!(zf`8scq$hX^n@NH-hOo(l{f)Zb6cQM0_Pu|s&(ov(f^};j0sB+kn&ROJ z>M;2D!u`Tw@vMg}Vo(NO?N7snBqypbgZ&(f{C!JQBogf699$Pm$dpc_>rq@uepI?I zhj0`2%4Xk=a%5Q!2IZ=>B70Lk*?Ba078bB3WH0M+#(%@LdYq9Mj1%CgND|mU?F_$= z0ujle-W$1>^`L|M<$dh;PJMS0gJJAMq4|J%o3IstMI&mrMWz27_*x^tf*L?^XIN0Y z7tyFb-DA*af-xk)b}n#|{gytAv1SmVXGbs&j9hO=DHNJ1Y$xjf&h8VoR3{%-b8l9q zg5E0@u79f-RPaXDH=uj=o4UI@lE^Dr4>Pa;rW+!T1vNlFUw0oXPj9LZh3x{%Xg9D+ zY_n`|H7<^vWo-4-{_hy`49w!F;pZET``Zix;O}QGa0pGfjpM_&R-(JIi{?x&Z&mDlpq8 zQ&@e2=gF$MG#4t3qQ1yj9cIPwNE>;RJYA{e83W%m*AF~gZNOb>J@4t^*;*hU4E&>} z1n!2Jq85$f>*GywS5va^b#$ka7f@DN(^pbxS~T!<)`X|id<;lFB-R(RerKCE$RAQs z`hOnQBVbX4{^xvFx%ZQ-dK89_A=Rhv1h92>-`0166u>$Gm|xE+U3aPr&4WT?-PU(X zZ?yy1F*`4m+*BT5aFgSnf3Y_QG%9BLy zC-Yd3oeDC{c?bzjWdr@-nhl%?diJUtBY&VD0sRQ*zYp{fz&sn^2UYjuNGZ==pK%26 zBY+IL_=IJ5&3wx(7#7;eZwgBK<+mY0m5(yK()$>K+CK zyy{4BD*=ZB@q--MJ$9?Yf7LxAKz|(p0}{;>yE9mDWcLQB+27YaG~7Kj9Mgpd3k*1# z9Yp)}bdOWI)qh#|o&I_BAfGad_0`}N-lV~w*zL>ijZW@!_H{eVECA>3{wAn@SUjU! zhJGE6-k9y4P-9l(zSG}&P)}1mU-C+d5A0@V^`TE#0U_k%L}it8h26sxZ+}>P(|`*P zrUYm+d>Axa!daI70eb1_PFlq_3xHNRxGJ>JLh!L1mN0mbd>OJ72(o`h_2BO*1p-nB zqltf0xdl=OU&w09`WGvm`uFEkA6!l2zs?NY(=P&B;S$!q0-)#gekZm!4>j9$QoxYc zDNgLeG85I?s;3z|F%2m0K7SlU^Xf$S%K;^h0jG~>T>@`2^=Po8`rS``IHdx0PtJPN~! zlaF1;9Jb`g|I6h2z9MqJck=yY&c{9k9Lo9nDO*3g-po&`wl!E&8Gl^03Wo$%aX8=` zhX;HdHMjwm2p#}_V3+~O4Q{|LrUpG>5Gx7(!CC>;Er|7WG{d?sqa;_#}=NKOU-V{G7#b4c%=7RXaV;KCS zKtKYxDSq%Qw{_xnnju78d3@&}clzEXagD@*P;w}Soz zS6O=oNxQ})e@dW5&6+nHahG?tzlMYo_iV|QhZhZ)0_EFu((T{XE{)O-k2I7GJ`s{% zYgskw#u$f0eLJ^|$F7R=ckSg$FbzjY)b3%{H%;L-$kFCaHu88kepP1Dah{jeX^%su zCZXCo8Gq}h7pug$+;R(PDRJkDOBLOc_tr#u(wIZ~ZJ(SX9L_yZGUi?pF*j$0_u)69 zyz?Zsn;6kQ%qkQq<_!?Q6N~T@dD|Sf>u}{2-F{$V5~YylJ8f0vI4PmbJJ%<2YwE8m z4>8CgJ=6@^EzltRj3hROYok}=tK|f)^rdF;s(<&etv6REnB~_oKQ3WPaZSxereJc3f*#G4+e~_N%(!w{@jXi;_9SYU&CNM2s9a>)^5Fn^+o6#9tACS} z>fA{+TmtsnWGZEAw>-)H{CH9l^)r5#qREk?_{Y3X%aXG)Q^xQ`x4yGpa!M<=Xn})E zPE<+ufs{LSucm&PH(t}}L7M}2!X0xDyao2^wFT`@;yjqX*Je(;I`5JH^wI04<*e6M zJtJkSBImYqnv%=S&=t#tXLFr3zkgDgml&8Nn7sca<@hb3=p%yjiYl|KCh;h7Pt7@G zUr#Fwqa&s&8O^UhY0Gb}!>2{RxUxx;OVUC~W5xT~_YCq(FHWi*Z=!R(qIQXA=2E2G z#+WHW(_{}6yObme$shucGo22qcEz0Dene=7p;yro2j23AqYcl58%W1IiholNY*U%4 zCA_~%-BZG1>GQZQGG?>6RB(Co?&t-N!ryb3wx)hrW@OUtrk*8m6E}8o)pjBjQw^)vutfaMZ>YMyFE990}iVzUwZT9 zTl{<2wiI zb)>e}d~`{Baer)c>HUT@Ua!&fyH0S=U7HcO(j+W)ija(GaE4<5cYo=nIT0C&c{5%K zTeNCuXoN2{Er}&bB%SR#vPb6@i90EJ!qstHqJ&`UO-$`+v)aYG|NWUKypAZybxI`w z=9k8E%?wWJIjl$gpB%&0oY6r1?*}N$JGA}}4V3>#@HavUi}(%4zg+$gOaJb*IpTjh zhSC4fAT*_y{%~zqcz^u|p+?q!zvY;+MAy^=m~@dsQ>=B3V3&ZB4~UfVh2xyjPU>X3 zhZ+jtQS+JK$)UTOA*nNt)zYC&v z{`(;6z9)gWZGXDA6Zm+xEGa!5!t{%;Gp2P1#DPBb7_z>0*nfcT3$9y%ad2X#4(&@c z!wR~y^riSxdNR#0eBgwx)+Dgu$+O3Z=0s(52k^1J$O49CcVIi~Gf95HSno~ugkz_{ zd2sS?gZz5JrsvZ9dfLrYve)zV@C2`7{r8wEJBK zz?4jN*L8QKIDgT9?>pfb|KIpJR)FH~>f&4vDg!3+?|PppiAn>^-ON>qB>T?snRJduk85!+_zV z=T4!)jJEd_i)e$Ivd&X!6b7UB1%0Pi^(DM!Ihxt$L4Uue_ay~lC)&U~U47%ra^|9X z&suo^U#lzp89f8t6EWQHp;n$0vMMor{~rnqHs=oX|AC(o z{Qr*QJN$o2h#vpnz9s(4^LHHf($HDYLkO2TwtpvjImPn$*_qjYop~s>;P5@Xo`^Z&mO;6BfPVqT*b`+eq0>xnzA?Lg#WHc8+}w-Nv%@38BO(ePO6;kB$aU@M zlWlWWv|pM%hxf=`dPB1j|GH+Lmc8$4I}ocCne`Dl423n%h}$kF)*VevrF(eHd{I-j zbAL_J$-~IW`=w^!?*f;>Gz}VJ>jmEh;^t=6_IBqlX9zx88ZYAi&W0#nOdqH5`D)37 zhZO3ku1zmOo(L7btRAi5Ojz+3LM<_vpcTCK+3ltAhO70?oFHWEd(Q2()4(qWc3$Z9^RgYyWn10% z&i+(A^$Zj$Y%G7)2htKEDJw>34af< z3V~JB@XZGh`W;R$>I(LaimJ*yYQJxF>&utVD~nSy2*Dp(Kk@1mDF%0(Es{1MlGD8> zK6@1=B0i2wY{{I*K4tD9{!^G4~%qM-%;F_+I_ zmuQ|l?HAaXasP0=c$mus=99H+ZGVgCG#NhxA9{PLqj)@2YM5oPU$xce$?MRj0*~Y` z$12twveeqw&~#foNh9rX=8-0{<@vh!l%+!V_S|fAQAzd7zsp?Z$F-RFvHGPLO(SvD zschXzqea%_{3Q`<sp{=~~+(*~XO;_JDjB1K9JZi66&(1@PFW&RcUz4HmQb6UVGvu!Ka)a*b zBy~C2Q@oqJPOmfn5^KFoI1@cST+l1qt%z(IAflPF!5BB*!Y{QV@_+FT2iKyNA5$N7 zG?jh2kKdL$J4NyZl~1Q3v9m?vf}d}(BUc_#+Vtt;klh}L%1H)E+qb0{z5H-vn_Pnk z{^YFY6y-gqs=@>uJ@V7zP9Hg-cRp^y#MqX%Yd_99du8d-uDWXlFCx}vwI1WGE!Chk zI9C-va7(YPKw##k@PE$}a(#A0XIrpd-AUKL{h145*Kuc`wpOes)LkQ@ldBL_^9oL@ zWW@)Lsz_L~VS_X+G386grd{aWcRTgv+lpR3`}ioRxoGdqnG=X#sw=*Dd3d*47C8y7 za67)vjOf1HZh1`Y(*wrq5F~xW*4g^!UH1v>cbmAOL~Pdj1AlJihNiMn>12y`lcmv@ znijt=EA!vhv^MDD%bSy3wyLF`tEt<5wR5&kLuzn9_1(76crM|jBJQAB?Nr9Qbi1R| zo01A{TR%hOt)8gUASW?Ql-*Z+Xu-s7P3UwO{;SfQ&r%|rCn{nJj{M0UsqJ&B^P_5YZ=Kd;uaiEHYk?EJ>1b51y2+(S2w zFL)<*Q*N$mQK!=b@_|JKE27_fCtG!V=GWG%mMT6k9DiF9^-$PRd%ciW`{S4rKKblj zlG3Hich(-PamL(|n)RxB2LHk!VQk&=FKgbNw>4WH`2N+>)_^nK0$hZ5x8fx)-ne%w z*mR1x|AqXBr5VY4))pqLv9{Z@WTr%GBy*bi%$;wAM$a;ktde%jy;Xfmm3L9a*e)yQ zBznPf>wnKV_=b<7a^`W%44C2c;EP=quY(%r1b)sB*~a5#E{C|7n{?OY<+@SUi&2zX z!`0DYqj<(md7K!whAA!TK6T9nu5{ODojwZ^C6&jtrjM>amZR#cxgThx6=xc1stC*AJ>G=O ze%q`dxX!U9(RMAPBek)~U&p%z(Xglf>D|%6{Hex)Z+JB8gy!-VjdCu}-}UU%_15`! z1AoTyy`Aoi7O@nn01H6$zkQ1?;$IkTdZt(@VCJ~j*Bl*@0XuhIAlHAcufJTp#Hk}> zI}blo`rPbygt2b-)pkG5yDORCuuOC7J4Cb6(fETZi5~K;An2g6_$LGsqsD)!t!!0ObY7({ERdYdBpnfG->d0F-!SpP z?xTOV#$%7q&vdVdbCZv#&~HPf+}^%_cDl=sWE;$3Bu(kVQ@*LUcergaU14|5_v7>C zu10%NsNj-6Q`@xk6S=iYS*SAy+P0=;_qdkhPepYyx&LBLTDNH438^U;?boW@-CI)- zOu4*ll~}T@wDH;x`f8+W3)fo)%t(m6G5LRdM8!&_Zue*ASfqnIx(N+idV&z{I<`H;>Eo-UrIoD!rEwt}>gx(YAlO%_4Rys$uucoJE1n_lUyx@1icGO<3eRfP$z9^xq3mcYtbJguhiW7T)5$RnwH8%c#~`2>6^Pl+OW*L){t%>*LL`%O!Ccqu?>@J#-7Sd99z!I zr7{!uO!@7xs47H)%#A7QPErZNVUyPhqFzNavk=DkMK-tNypE5$_m>P+mr5?gn8xhx zdWQcJWSEh5>gZ(GXRq`xR$qUYLe<`wAoN8*y!7OHv(bF=h|Y~N)&c@NM=j@scGey! z$#4EtYtZB)ASZy;SG>m2u-k%5CxZ3SBGw zlehUvvbN}~(`%kQZ;woK@tYI=A)ROIvgcb0WP@eS66zjJWD0s7lN5h6LL_}gS0b{% znC)uOTvO>Rjt)&7rA%jTn7`(DsZy|O?B*K+$=YPkH)EOCA<-=_`Oz~nAep*gwQMV{ z;u)$(Z!K8c_C)dAb7W!DES;LgWe=PuEK6Z*IGdPbbdpc>_A-%Vn_0ySfjvUYXk6M` zFA#Ieg7+B}cvaiK-YkCtJ+rDXz2w%=TK}jKDWfvkaXu0k>Ya})kh!LEDzbq9h3pqO zCr`|moD&)M+TitDN!z%h$oK~z&j|X}aIH4C*T_bDMou{8cK_rR>CGv!NejbuL?<S- zV>at*Dupb0au2hl{b`ALoLhe2ZUo`;=g6}j3PNjIPno*cE|6#o5@u=}ttm>C;q{uN zJA1su^hDbiTu(ncPFwaMujR2zQMkqQq{fC^1K}fiPp&*KpL8g_tm&reBa`_1S!W** z>ae-*&8Aa98sw-?ZnfNF3Z(CHRKgB$; z>&3Q^3*kY_5Uq#LZd#*tL-JUKHC7?$RYpCP@8H6WW4eE*=T9M!=FJfl?es#}}VSx{Fd`>#l@_h67De>2)j9XgC+nA~F zk(&F2yXf9!!#8_JHOQVlGU-l3g!5yS=ys~@eeFFFv@QI;luYv6HlOTh^Er1GOx*E4 zS6C<0Yvq57`%Fq^=FK8xb$vs`;t;fn2tH0sZtQvKXWqJ&j_8Vqov+@RpIBx2J`2%O z<&~f#;_x71*BiN+rq|xcpyRkPyXs<8=Y{wtytq2~ju>fL+A{q8zmnH`hiLDdmh6># zWQ*+S@aWg21u{ibCR`L4{UJnVm#VW#{QBTeqFaBG@})1TB#}0rAtPE_qEZ(x$|sE% zTN08jacRwklX07?)<0)bA3AvyYB#ExIch|HxJ3F`B^*G0TC4)FRXT{^qt9Dtbu3V!K zp51>MqrD!Ju;j#!&Ztd$9?zP)?bC%0L%zB*yzMldGYS<^>tDQ)q5kz@!vVFpo3YN0 zX{GO+!xmP}saCt=<270CRRJNwBjx0aJSn_gX?x0q#8AIA6aC&^dK6k(k)yg_@%cgN zd21d&A&Vt+zHL1^ZtIwv*t7MtM{?$K+g^WkOv!FF3%TTUKy2KL)GhW(Z)GUdR{So) zIh;Z6rQGK7#ZFZHVr#GGjtcR(jSU-brqfLAy@g{XCdA{PhDV-TqC$}h%N-wz+L}}L z;Bx$|Q4ViJ7q1~#H*U2mh7u|u#Ee5(i~h2W5E-8rm*bK>*_hW_*lYjzzwm*Atuuf5 zPaoPAcxLQ$e&|zSVO(J;`F=w}$fvmI87i`rSe2NJxO0z8X3{LqxP1-@^NPIN=5fdJ zhE7S>;}aU!Xc}AmnKrxdb;fVI+$3FQHtu`7IrWU){K%^xk1Flai8r+0vHkdjQ^z*c z+=#@dT*xi*M-bf3&0nqeX5O3~23CKo#W(Gmc)C`uKnX%8S(5ZOpcjYD7s^E4h0-Q{ zcE&E5QgFsd*I)Gj#${aac-N@&%Ow}2MY!C0j4)YoasM+}JAt_wdlA{#Q+}I-+m25V zOWP+f`B6F7hSi%_8l7oHw)qx zbn8qGID7Aue%3?+KjvKWEGwa=BbA{~vw290l&oX+eh>U6D;F-|a|j{4Xf{0}gN=p6Q zSo6?JSFWy^H)G!zM55kR?HfxIHV93{y`ENZK3Z}^7wx*qhb-cER&+V z1-3y}*A^~Z68GYQZvF<**3=qv(UzUf4=<+qd3QZfm*#bNapPxn{TXZAAa-?!sK0x zyB-&qdutE5mALlWCufHi^7h~)0oRO6PjyUo`SGew%!-Q?ktu)36Haqf#fG?%3YLc~ zuMnP(=`t2`wcVh)-I}_~k90d+t(_{`GzPwlD=(W#x-NY&f)Tj2URQsT3*qB+a1j$7SgmqbhHG9C9?>q>3ahDQy`lEJOBcu|W?U3@9& zYI5?cyb*!Q7x=ap9Tlv67pE;6_F#pV+NFnEXH-8e-F<&0E4VdIH!tZ>`ke|NN=fF; z2YM~k2X^swc*@~4iY_8uxavmKe$ocWZ1n{F&-dfEwos#(E5!C4>apze-M@;xt1 z$j&93JU@TAi02o)Y?>bUM17BRn+Tr7qv+r=>68*;{uEgTW07)Bobm@^Lc+KpnXJe& zrmff4=Ui8aeB@#wb*QHF*wdN@ZzX2+RIzu(8`^R&FIcKi8g+==YTT00@qAY6qfr_1 z56_FI<*#!nD^|mHr0svG78$g~bPLynMa$mjSlfRyZ||BtEz$SwsnY3Mj5NoF)d-wT zGgWG=p3Z#f*4lb6>Oo~;!@D%>wD6Y?_fO(ih^{x$KUrvd`{+zJmBpP`r#=a{y}wSp z%*VLztyFuWDw1aV1|glspL43l!y+mWDho3De%y0IV( z;i$fm>sr(&?QId4kA?-!-|K97K$q7&qm(%sdx?}j_V`Uycmx@7ccPe(Q}D^~Bd__H zvIh?ya(aENF38B7S~|UKXV4yHhr$gi*ZF_PjP*otX}=3j}c6_5BxBGhFoAT1R{$sZ!tfuV^{?v72mvpj6-lDtB%&^d6|1wGb_4|Lv z)m3+7mX~jPC}-!&r+j^=LXLH`3 ziCxYuez`Dj+jd`}+piEgO)D^mO5|o=ot4?Bwo`WF<;vKtfqWe}L0Xmi3bKF5>#?WC zj6ygb$;psuxDQ=(Fsw;R-Addi5{a>VJjdZ!eQah!adqK=11QPjnIFhS_ti6LSLUEU z+AWD2x3^@D(e}2#vPxdh%K02>a%ZpTMHyXzodLJCk_<##F4`5wN+H~=H6gs~ zFk$O<=zvSun|+Elc=@w)5+&NIl?}b0>P74;1Q3xE+pByE)wQVt%*3oSL?ye zm`g>k9b4YoII}m^{Jh@53eVjZkT6} zlzq^-xOq>yYUFv_)YgAIwC!y!y1L!e-75~1i7-u}6Uqxj|hvnL)c z^tBqU&(2FOJz=)C_N|x3T9V09W$$L}{pg*qBJYLlS$coc@T%6N!i6)Cp}bAgR%ZMq zdsd^|5vMJGaU8vff(;4nn6j@}{>*8v4}J+{xk{7IHn}}>OWM6Led`^qJponJc2&bm z5^paDd>*$9eJn7(vqAZZSDOs+eQ7-PLUXgRz(P8=xDn-0p7$(6Nw+GDUWVrM4v6eH zq1u{x!|#81p?<4*9yE5_!!vY;g6mwaDSRj5?4-$4V_QyV-Iuhm$_P{`-0#0>25oDh zkS*z9Uf0D~!-(v}wO1rIjQ!kn&wY;=(+`InAouL^&aoi#2Iu3_Rl!xolJKQoHUe&1rUC- zT7;4b^i5~*hmU2@<}GSGUeFR1sqXa5$@qV=pytXA={komzuy1Fyk?JD!-joa*0+z; zDwsUh_YV3GduJIGS9b>BT^0%Mt_d!|Ssa!RcZc9^!QI`0ySrPkK!UpkhXfDKhr2tJ zcG^y5rtOc)w9Wf(XLj!0JLjG~_Po#gc_)t*(PP3)`^_!R~D{Th?`*NX!7y_1I6N&y*@(byD!7Z3(UOzu0^puRFZM@J)}36X)l(Y(_R zj7eS+6TiH4nFtFSP7?#3A3QYlc;pxKqnmZwT~leGnI3^1sm=DAFWJ`4v99ae$XpsU zDAdtRZcmci#4oP_iPPf|?xm2Q*wpnXvd7x8A+HbGnSM{^R>{2a-U+f3){LJ$3-LaG zo^vhR4ClcIVw1!(^7a#$J5%Npu?}%j8J#a9RA1k!PdL&t^bl+}X5UhDyv8Vy4;ar` zIvw9X!UHr3G@SKcimSK5AB>VXK21d!TxsT>Kdq{Q8vK5Ja&s`5QYAKtJr5UAj21oL zSt3KQoy2m++dfWZs>_~bB}7AAv1iJE-BmJ+z83NvlN>_y(B!_mC%|ETOiHnnN5{b( z*VEQtP>DhA+#{SN@sa~vq@`|&d;J*vj(58@g^oph?eZeQ2Yi=_tN6-H2`!su8jLeq zu|$WCr`+%+y_V;uKlilWMW8DcvFGF5Sei$__{ikSr0>YuMnQ4c(E?MTC}X>S*972I z(Yg3a>(we<4$6(GLRilC6_!}p!0eWKk#3e{DC9>q)KdT%c8`P%QuS+__p2mG06x<- zBqe$P072xpEI=RGT?yq)>&`g^$rJ^I9tlMk6WFY2=D0$bzuk@}?`j4`R=MK{sH<)} zyBi}mcV9$NINuXtdd2Pt2B!3Xn{$HT)4$Z=&@X}ij@+mmK!#vxDgw{? zg`+;AZ}lr|K}x#~0fruIm-<~hJWlZIwLvPHy^9pxiQ{O2VN!&h^E#)y3r1gWs}#V; z%4}|zM_^w0d&Krz}e%U~Lnt_7cB{lrPVwg4@*gT`73<< zEIW1d9T@KwIQ~<@i+SW_DDT96KdB@`AaY@Nqs}V(5T&U{g#a zWDAbx1B&pHCkvrNN7lW6r;uJ9%mtRW7yX5I+wI+v@1|J_G1)A2OtwI~m!_}2O2y!p z^dE3m@B`En_avRQh3&!?@s>5xKyvKBFxIyrC?v8$(QamoaD;>(D7v�(cEK1(J6z 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 K6EI-_Rs#UBWxK@y delta 73684 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%0DDvbGex2CKm{QT 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 Date: Fri, 15 Mar 2019 10:47:48 -0700 Subject: [PATCH 48/88] remove white-space --- scripts/system/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 469f30cd23..7fdb863a83 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -257,7 +257,7 @@ notice.isFacingAvatar = false; notificationText = Overlays.addOverlay("text3d", notice); - notifications.push(notificationText); + notifications.push(notificationText); } else { notifications.push(Overlays.addOverlay("image3d", notice)); } From 9182db8bd40b0accc57aa65f963cc7a3efdf981f Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 15 Mar 2019 12:56:43 -0700 Subject: [PATCH 49/88] Case 21025 conditionalizing TabletWebView features --- interface/resources/qml/hifi/Card.qml | 5 ++-- interface/resources/qml/hifi/NameCard.qml | 24 ++++++++++++------- .../qml/hifi/commerce/wallet/Wallet.qml | 7 ++++-- .../qml/hifi/commerce/wallet/WalletHome.qml | 6 ++++- .../qml/hifi/tablet/TabletAddressDialog.qml | 7 ++++-- interface/src/Application.cpp | 3 +++ 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index fc49bcf048..9fb8067371 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -40,6 +40,7 @@ Item { property bool isConcurrency: action === 'concurrency'; property bool isAnnouncement: action === 'announcement'; property bool isStacked: !isConcurrency && drillDownToPlace; + property bool has3DHTML: PlatformInfo.has3DHTML(); property int textPadding: 10; @@ -298,7 +299,7 @@ Item { StateImage { id: actionIcon; - visible: !isAnnouncement; + visible: !isAnnouncement && has3DHTML; imageURL: "../../images/info-icon-2-state.svg"; size: 30; buttonState: messageArea.containsMouse ? 1 : 0; @@ -315,7 +316,7 @@ Item { } MouseArea { id: messageArea; - visible: !isAnnouncement; + visible: !isAnnouncement && has3DHTML; width: parent.width; height: messageHeight; anchors.top: lobby.bottom; diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 646fc881e1..c92afe9e14 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -46,6 +46,8 @@ Item { property string placeName: "" property string profilePicBorderColor: (connectionStatus == "connection" ? hifi.colors.indigoAccent : (connectionStatus == "friend" ? hifi.colors.greenHighlight : "transparent")) property alias avImage: avatarImage + property bool has3DHTML: PlatformInfo.has3DHTML(); + Item { id: avatarImage visible: profileUrl !== "" && userName !== ""; @@ -94,10 +96,12 @@ Item { enabled: (selected && activeTab == "nearbyTab") || isMyCard; hoverEnabled: enabled onClicked: { - userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; - userInfoViewer.visible = true; + if (Phas3DHTML) { + userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; + userInfoViewer.visible = true; + } } - onEntered: infoHoverImage.visible = true; + onEntered: infoHoverImage.visible = has3DHTML; onExited: infoHoverImage.visible = false; } } @@ -352,7 +356,7 @@ Item { } StateImage { id: nameCardConnectionInfoImage - visible: selected && !isMyCard && pal.activeTab == "connectionsTab" + visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && has3DHTML imageURL: "../../images/info-icon-2-state.svg" // PLACEHOLDER!!! size: 32; buttonState: 0; @@ -364,8 +368,10 @@ Item { enabled: selected hoverEnabled: true onClicked: { - userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; - userInfoViewer.visible = true; + if(has3DHTML) { + userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; + userInfoViewer.visible = true; + } } onEntered: { nameCardConnectionInfoImage.buttonState = 1; @@ -376,8 +382,7 @@ Item { } FiraSansRegular { id: nameCardConnectionInfoText - visible: selected && !isMyCard && pal.activeTab == "connectionsTab" - width: parent.width + visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && PlatformInfo.has3DHTML() height: displayNameTextPixelSize size: displayNameTextPixelSize - 4 anchors.left: nameCardConnectionInfoImage.right @@ -391,9 +396,10 @@ Item { id: nameCardRemoveConnectionImage visible: selected && !isMyCard && pal.activeTab == "connectionsTab" text: hifi.glyphs.close - size: 28; + size: 24; x: 120 anchors.verticalCenter: nameCardConnectionInfoImage.verticalCenter + anchors.left: has3DHTML ? nameCardConnectionInfoText.right + 10 : avatarImage.right } MouseArea { anchors.fill:nameCardRemoveConnectionImage diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ea74549084..7c2b86ef99 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -32,6 +32,7 @@ Rectangle { property string initialActiveViewAfterStatus5: "walletInventory"; property bool keyboardRaised: false; property bool isPassword: false; + property bool has3DHTML: PlatformInfo.has3DHTML(); anchors.fill: (typeof parent === undefined) ? undefined : parent; @@ -335,8 +336,10 @@ Rectangle { Connections { onSendSignalToWallet: { if (msg.method === 'transactionHistory_usernameLinkClicked') { - userInfoViewer.url = msg.usernameLink; - userInfoViewer.visible = true; + if (has3DHTML) { + userInfoViewer.url = msg.usernameLink; + userInfoViewer.visible = true; + } } else { sendToScript(msg); } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index eb8aa0f809..06d07a28c9 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -24,6 +24,8 @@ Item { HifiConstants { id: hifi; } id: root; + + property bool has3DHTML: PlatformInfo.has3DHTML(); onVisibleChanged: { if (visible) { @@ -333,7 +335,9 @@ Item { onLinkActivated: { if (link.indexOf("users/") !== -1) { - sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link}); + if (has3DHTML) { + sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link}); + } } else { sendSignalToWallet({method: 'transactionHistory_linkClicked', itemId: model.marketplace_item}); } diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 4edae017d1..1342e55b5d 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -35,6 +35,7 @@ StackView { property int cardWidth: 212; property int cardHeight: 152; property var tablet: null; + property bool has3DHTML: PlatformInfo.has3DHTML(); RootHttpRequest { id: http; } signal sendToScript(var message); @@ -75,8 +76,10 @@ StackView { } function goCard(targetString, standaloneOptimized) { if (0 !== targetString.indexOf('hifi://')) { - var card = tabletWebView.createObject(); - card.url = addressBarDialog.metaverseServerUrl + targetString; + if(has3DHTML) { + var card = tabletWebView.createObject(); + card.url = addressBarDialog.metaverseServerUrl + targetString; + } card.parentStackItem = root; root.push(card); return; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index de4a6bb167..417c44fc1f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3043,6 +3043,9 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/wallet/Wallet.qml" }, QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, QUrl{ "hifi/tablet/TabletAddressDialog.qml" }, + QUrl{ "hifi/Card.qml" }, + QUrl{ "hifi/Pal.qml" }, + QUrl{ "hifi/NameCard.qml" }, }, platformInfoCallback); QmlContextCallback ttsCallback = [](QQmlContext* context) { From e8cac1f5985453a704b6c8dd2a687e880a9907f0 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 15 Mar 2019 14:14:16 -0700 Subject: [PATCH 50/88] fix 2017 Unity versions --- .../Editor/AvatarExporter/AvatarExporter.cs | 11 ++++++----- tools/unity-avatar-exporter/Assets/README.txt | 2 +- .../avatarExporter.unitypackage | Bin 74623 -> 74591 bytes 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index c5bc5eb84e..87f401d478 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -17,7 +17,7 @@ using System.Text.RegularExpressions; class AvatarExporter : MonoBehaviour { // update version number for every PR that changes this file, also set updated version in README file - static readonly string AVATAR_EXPORTER_VERSION = "0.3.5"; + static readonly string AVATAR_EXPORTER_VERSION = "0.3.6"; static readonly float HIPS_GROUND_MIN_Y = 0.01f; static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f; @@ -56,6 +56,8 @@ class AvatarExporter : MonoBehaviour { "2018.1.0f2", "2017.4.18f1", "2017.4.17f1", + "2017.4.16f1", + "2017.4.15f1", }; static readonly Dictionary HUMANOID_TO_HIFI_JOINT_NAME = new Dictionary { @@ -1159,8 +1161,7 @@ class AvatarExporter : MonoBehaviour { static string GetMaterialTexture(Material material, string textureProperty) { // ensure the texture property name exists in this material and return its texture directory path if so - string[] textureNames = material.GetTexturePropertyNames(); - if (Array.IndexOf(textureNames, textureProperty) >= 0) { + if (material.HasProperty(textureProperty)) { Texture texture = material.GetTexture(textureProperty); if (texture) { foreach (var textureDependency in textureDependencies) { @@ -1214,10 +1215,10 @@ class AvatarExporter : MonoBehaviour { } if (unsupportedShaderMaterials.Count > 1) { warnings += "The materials " + unsupportedShaders + " are using an unsupported shader. " + - "Please change them to a Standard shader type.\n\n"; + "We recommend you change them to a Standard shader type.\n\n"; } else if (unsupportedShaderMaterials.Count == 1) { warnings += "The material " + unsupportedShaders + " is using an unsupported shader. " + - "Please change it to a Standard shader type.\n\n"; + "We recommend you change it to a Standard shader type.\n\n"; } } diff --git a/tools/unity-avatar-exporter/Assets/README.txt b/tools/unity-avatar-exporter/Assets/README.txt index 767c093800..5b228ebf75 100644 --- a/tools/unity-avatar-exporter/Assets/README.txt +++ b/tools/unity-avatar-exporter/Assets/README.txt @@ -1,6 +1,6 @@ High Fidelity, Inc. Avatar Exporter -Version 0.3.5 +Version 0.3.6 Note: It is recommended to use Unity versions between 2017.4.17f1 and 2018.2.12f1 for this Avatar Exporter. diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index 3e2d6f2aed318abc6382f1a914ec3fb5e87dee69..05ad49baa6bfa1696cb12f659750246f2a6d2d9e 100644 GIT binary patch literal 74591 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

Ly1hzSwUsq8RmDStkK6pT6F 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

=)-SS*oWeSIvIm~V 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ફV}v>+vQ2ynK6RmQ**XvRgr;$h1}&DMk2nm!lYX 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 B?KS`a From b5a45be7b986995e8be2e5f1fffe071331b3e514 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 15 Mar 2019 01:08:53 +0100 Subject: [PATCH 54/88] avatar doctor documentation urls --- interface/src/avatar/AvatarDoctor.cpp | 65 ++++++++++++++------------- interface/src/avatar/AvatarDoctor.h | 2 + 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index 04a426c3db..43e50ea049 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -55,7 +55,7 @@ static QStringList HAND_MAPPING_SUFFIXES = { "HandThumb1", }; -const QUrl DEFAULT_DOCS_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar"); +const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.highfidelity.com/create/avatars/package-avatar.html"); AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) : _avatarFSTFileUrl(avatarFSTFileUrl) { @@ -85,7 +85,7 @@ void AvatarDoctor::startDiagnosing() { const auto resourceLoaded = [this, resource](bool success) { // MODEL if (!success) { - _errors.push_back({ "Model file cannot be opened.", DEFAULT_DOCS_URL }); + addError("Model file cannot be opened.", "missing-file"); emit complete(getErrors()); return; } @@ -93,45 +93,45 @@ void AvatarDoctor::startDiagnosing() { const auto model = resource.data(); const auto avatarModel = resource.data()->getHFMModel(); if (!avatarModel.originalURL.endsWith(".fbx")) { - _errors.push_back({ "Unsupported avatar model format.", DEFAULT_DOCS_URL }); + addError("Unsupported avatar model format.", "unsupported-format"); emit complete(getErrors()); return; } // RIG if (avatarModel.joints.isEmpty()) { - _errors.push_back({ "Avatar has no rig.", DEFAULT_DOCS_URL }); + addError("Avatar has no rig.", "no-rig"); } else { auto jointNames = avatarModel.getJointNames(); if (avatarModel.joints.length() > NETWORKED_JOINTS_LIMIT) { - _errors.push_back({tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), DEFAULT_DOCS_URL }); + addError(tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), "maximum-bone-limit"); } // Avatar does not have Hips bone mapped if (!jointNames.contains("Hips")) { - _errors.push_back({ "Hips are not mapped.", DEFAULT_DOCS_URL }); + addError("Hips are not mapped.", "hips-not-mapped"); } if (!jointNames.contains("Spine")) { - _errors.push_back({ "Spine is not mapped.", DEFAULT_DOCS_URL }); + addError("Spine is not mapped.", "spine-not-mapped"); } if (!jointNames.contains("Spine1")) { - _errors.push_back({ "Chest (Spine1) is not mapped.", DEFAULT_DOCS_URL }); + addError("Chest (Spine1) is not mapped.", "chest-not-mapped"); } if (!jointNames.contains("Neck")) { - _errors.push_back({ "Neck is not mapped.", DEFAULT_DOCS_URL }); + addError("Neck is not mapped.", "neck-not-mapped"); } if (!jointNames.contains("Head")) { - _errors.push_back({ "Head is not mapped.", DEFAULT_DOCS_URL }); + addError("Head is not mapped.", "head-not-mapped"); } if (!jointNames.contains("LeftEye")) { if (jointNames.contains("RightEye")) { - _errors.push_back({ "LeftEye is not mapped.", DEFAULT_DOCS_URL }); + addError("LeftEye is not mapped.", "eye-not-mapped"); } else { - _errors.push_back({ "Eyes are not mapped.", DEFAULT_DOCS_URL }); + addError("Eyes are not mapped.", "eye-not-mapped"); } } else if (!jointNames.contains("RightEye")) { - _errors.push_back({ "RightEye is not mapped.", DEFAULT_DOCS_URL }); + addError("RightEye is not mapped.", "eye-not-mapped"); } const auto checkJointAsymmetry = [jointNames] (const QStringList& jointMappingSuffixes) { @@ -159,13 +159,13 @@ void AvatarDoctor::startDiagnosing() { }; if (checkJointAsymmetry(ARM_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical arm bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical arm bones.", "asymmetrical-bones"); } if (checkJointAsymmetry(HAND_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical hand bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical hand bones.", "asymmetrical-bones"); } if (checkJointAsymmetry(LEG_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical leg bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical leg bones.", "asymmetrical-bones"); } // Multiple skeleton root joints checkup @@ -177,7 +177,7 @@ void AvatarDoctor::startDiagnosing() { } if (skeletonRootJoints > 1) { - _errors.push_back({ "Multiple top-level joints found.", DEFAULT_DOCS_URL }); + addError("Multiple top-level joints found.", "multiple-top-level-joints"); } Rig rig; @@ -191,9 +191,9 @@ void AvatarDoctor::startDiagnosing() { const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; if (avatarHeight < RECOMMENDED_MIN_HEIGHT) { - _errors.push_back({ "Avatar is possibly too short.", DEFAULT_DOCS_URL }); + addError("Avatar is possibly too short.", "short-avatar"); } else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) { - _errors.push_back({ "Avatar is possibly too tall.", DEFAULT_DOCS_URL }); + addError("Avatar is possibly too tall.", "tall-avatar"); } // HipsNotOnGround @@ -204,7 +204,7 @@ void AvatarDoctor::startDiagnosing() { const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips")); if (hipsPosition.y < HIPS_GROUND_MIN_Y) { - _errors.push_back({ "Hips are on ground.", DEFAULT_DOCS_URL }); + addError("Hips are on ground.", "hips-on-ground"); } } } @@ -223,7 +223,7 @@ void AvatarDoctor::startDiagnosing() { const auto hipsToSpine = glm::length(hipsPosition - spinePosition); const auto spineToChest = glm::length(spinePosition - chestPosition); if (hipsToSpine < HIPS_SPINE_CHEST_MIN_SEPARATION && spineToChest < HIPS_SPINE_CHEST_MIN_SEPARATION) { - _errors.push_back({ "Hips/Spine/Chest overlap.", DEFAULT_DOCS_URL }); + addError("Hips/Spine/Chest overlap.", "overlap-error"); } } } @@ -240,21 +240,21 @@ void AvatarDoctor::startDiagnosing() { const auto& uniqueJointValues = jointValues.toSet(); for (const auto& jointName: uniqueJointValues) { if (jointValues.count(jointName) > 1) { - _errors.push_back({ tr("%1 is mapped multiple times.").arg(jointName), DEFAULT_DOCS_URL }); + addError(tr("%1 is mapped multiple times.").arg(jointName), "mapped-multiple-times"); } } } if (!isDescendantOfJointWhenJointsExist("Spine", "Hips")) { - _errors.push_back({ "Spine is not a child of Hips.", DEFAULT_DOCS_URL }); + addError("Spine is not a child of Hips.", "spine-not-child"); } if (!isDescendantOfJointWhenJointsExist("Spine1", "Spine")) { - _errors.push_back({ "Spine1 is not a child of Spine.", DEFAULT_DOCS_URL }); + addError("Spine1 is not a child of Spine.", "spine1-not-child"); } if (!isDescendantOfJointWhenJointsExist("Head", "Spine1")) { - _errors.push_back({ "Head is not a child of Spine1.", DEFAULT_DOCS_URL }); + addError("Head is not a child of Spine1.", "head-not-child"); } } @@ -300,7 +300,7 @@ void AvatarDoctor::startDiagnosing() { connect(resource.data(), &GeometryResource::finished, this, resourceLoaded); } } else { - _errors.push_back({ "Model file cannot be opened", DEFAULT_DOCS_URL }); + addError("Model file cannot be opened", "missing-file"); emit complete(getErrors()); } } @@ -345,7 +345,7 @@ void AvatarDoctor::diagnoseTextures() { QUrl(avatarModel.originalURL)).resolved(QUrl("textures")); if (texturesFound == 0) { - _errors.push_back({ tr("No textures assigned."), DEFAULT_DOCS_URL }); + addError(tr("No textures assigned."), "no-textures-assigned"); } if (!externalTextures.empty()) { @@ -356,11 +356,10 @@ void AvatarDoctor::diagnoseTextures() { auto checkTextureLoadingComplete = [this]() mutable { if (_checkedTextureCount == _externalTextureCount) { if (_missingTextureCount > 0) { - _errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_DOCS_URL }); + addError(tr("Missing %n texture(s).","", _missingTextureCount), "missing-textures"); } if (_unsupportedTextureCount > 0) { - _errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), - DEFAULT_DOCS_URL }); + addError(tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), "unsupported-textures"); } emit complete(getErrors()); @@ -411,6 +410,12 @@ void AvatarDoctor::diagnoseTextures() { } } +void AvatarDoctor::addError(const QString& errorMessage, const QString& docFragment) { + QUrl documentationURL = PACKAGE_AVATAR_DOCS_BASE_URL; + documentationURL.setFragment(docFragment); + _errors.push_back({ errorMessage, documentationURL }); +} + QVariantList AvatarDoctor::getErrors() const { QVariantList result; for (const auto& error : _errors) { diff --git a/interface/src/avatar/AvatarDoctor.h b/interface/src/avatar/AvatarDoctor.h index 780f600bed..1465a5defc 100644 --- a/interface/src/avatar/AvatarDoctor.h +++ b/interface/src/avatar/AvatarDoctor.h @@ -40,6 +40,8 @@ signals: private: void diagnoseTextures(); + void addError(const QString& errorMessage, const QString& docFragment); + QUrl _avatarFSTFileUrl; QVector _errors; From 714115adef8b485f95872c3a409f0c1e44b8abaf Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 15 Mar 2019 21:36:46 +0100 Subject: [PATCH 55/88] - Video, and new Docs url in QML --- .../hifi/avatarPackager/AvatarPackagerApp.qml | 11 ++++++++-- .../avatarPackager/AvatarPackagerHeader.qml | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml index 8afc60fd90..278ce36362 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml @@ -133,7 +133,7 @@ Item { states: [ State { name: AvatarPackagerState.main - PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; backButtonVisible: false } + PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; videoEnabled: true; backButtonVisible: false } PropertyChanges { target: avatarPackagerMain; visible: true } PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer } }, @@ -229,7 +229,11 @@ Item { } function openDocs() { - Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/create-avatars#how-to-package-your-avatar"); + Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/package-avatar.html"); + } + + function openVideo() { + Qt.openUrlExternally("https://youtu.be/zrkEowu_yps"); } AvatarPackagerHeader { @@ -243,6 +247,9 @@ Item { onDocsButtonClicked: { avatarPackager.openDocs(); } + onVideoButtonClicked: { + avatarPackager.openVideo(); + } } Item { diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml index 25201bf81e..31528a8557 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml @@ -13,6 +13,7 @@ ShadowRectangle { property string title: qsTr("Avatar Packager") property alias docsEnabled: docs.visible + property alias videoEnabled: video.visible property bool backButtonVisible: true // If false, is not visible and does not take up space property bool backButtonEnabled: true // If false, is not visible but does not affect space property bool canRename: false @@ -24,6 +25,7 @@ ShadowRectangle { signal backButtonClicked signal docsButtonClicked + signal videoButtonClicked RalewayButton { id: back @@ -126,6 +128,20 @@ ShadowRectangle { } } + RalewayButton { + id: video + visible: false + size: 28 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: docs.left + anchors.rightMargin: 16 + + text: qsTr("Video") + + onClicked: videoButtonClicked() + } + RalewayButton { id: docs visible: false @@ -137,8 +153,6 @@ ShadowRectangle { text: qsTr("Docs") - onClicked: { - docsButtonClicked(); - } + onClicked: docsButtonClicked() } } From 5f3e31b119bd5ad6feda0fa6590254b1a090def2 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Fri, 15 Mar 2019 18:15:18 -0700 Subject: [PATCH 56/88] add ui to kick api --- .../qml/dialogs/TabletMessageBox.qml | 2 +- .../resources/qml/hifi/tablet/TabletRoot.qml | 1 - libraries/script-engine/CMakeLists.txt | 2 +- .../src/UsersScriptingInterface.cpp | 41 ++++++++++++++++++- .../src/UsersScriptingInterface.h | 4 ++ libraries/ui/src/OffscreenUi.cpp | 6 +++ libraries/ui/src/OffscreenUi.h | 5 ++- .../ui/src/ui/TabletScriptingInterface.cpp | 1 + 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/dialogs/TabletMessageBox.qml b/interface/resources/qml/dialogs/TabletMessageBox.qml index 1e6f0734ad..4411651a0f 100644 --- a/interface/resources/qml/dialogs/TabletMessageBox.qml +++ b/interface/resources/qml/dialogs/TabletMessageBox.qml @@ -28,7 +28,7 @@ TabletModalWindow { id: mouse; anchors.fill: parent } - + function click(button) { clickedButton = button; selected(button); diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 8d237d146a..5559c36fd1 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -117,7 +117,6 @@ Rectangle { if (loader.item.hasOwnProperty("gotoPreviousApp")) { loader.item.gotoPreviousApp = true; } - screenChanged("Web", url) }); } diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 82c408f386..e3eb8684d1 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -17,6 +17,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi) +link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi ui qml) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index fef11c12e9..631f0eb743 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -12,6 +12,8 @@ #include "UsersScriptingInterface.h" #include +#include +#include UsersScriptingInterface::UsersScriptingInterface() { // emit a signal when kick permissions have changed @@ -52,8 +54,43 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { } void UsersScriptingInterface::kick(const QUuid& nodeID) { - // ask the NodeList to kick the user with the given session ID - DependencyManager::get()->kickNodeBySessionID(nodeID); + bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); + if (getCanKick() && !waitingForKickResponse) { + + + auto avatarHashMap = DependencyManager::get(); + auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); + + QString userName; + + if (avatar) { + userName = avatar->getSessionDisplayName(); + } else { + userName = nodeID.toString(); + } + + QString kickMessage = "Do you wish to kick " + userName + " from your domain"; + ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Kick User", kickMessage, + QMessageBox::Yes | QMessageBox::No); + + if (dlg->getDialogItem()) { + + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); + + bool yes = (static_cast(answer.toInt()) == QMessageBox::Yes); + // ask the NodeList to kick the user with the given session ID + + if (yes) { + DependencyManager::get()->kickNodeBySessionID(nodeID); + } + + _kickResponseLock.withWriteLock([&] { _waitingForKickResponse = false; }); + }); + + _kickResponseLock.withWriteLock([&] { _waitingForKickResponse = true; }); + } + } } void UsersScriptingInterface::mute(const QUuid& nodeID) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 57de205066..0e3f9be0e0 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -15,6 +15,7 @@ #define hifi_UsersScriptingInterface_h #include +#include /**jsdoc * @namespace Users @@ -195,6 +196,9 @@ signals: private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); + + ReadWriteLockable _kickResponseLock; + bool _waitingForKickResponse { false }; }; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 137cffde94..2f2d38fe2a 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -240,6 +240,12 @@ class MessageBoxListener : public ModalDialogListener { return static_cast(_result.toInt()); } +protected slots: + virtual void onDestroyed() override { + ModalDialogListener::onDestroyed(); + onSelected(QMessageBox::NoButton); + } + private slots: void onSelected(int button) { _result = button; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 46dbdbdf13..6abbc486d0 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -34,6 +34,9 @@ class ModalDialogListener : public QObject { Q_OBJECT friend class OffscreenUi; +public: + QQuickItem* getDialogItem() { return _dialog; }; + protected: ModalDialogListener(QQuickItem* dialog); virtual ~ModalDialogListener(); @@ -43,7 +46,7 @@ signals: void response(const QVariant& value); protected slots: - void onDestroyed(); + virtual void onDestroyed(); protected: QQuickItem* _dialog; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 7a1c37af33..bddb306dca 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -368,6 +368,7 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { if (toolbarMode) { #if !defined(DISABLE_QML) + closeDialog(); // create new desktop window auto tabletRootWindow = new TabletRootWindow(); tabletRootWindow->initQml(QVariantMap()); From 2ab8eb98e8ee5c945f8a98b03fc7aa17f28dda77 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Sun, 17 Mar 2019 14:00:41 -0700 Subject: [PATCH 57/88] better implementation --- interface/src/Application.cpp | 36 ++++++++++++++++ interface/src/Application.h | 1 + libraries/script-engine/CMakeLists.txt | 2 +- .../src/UsersScriptingInterface.cpp | 42 +++---------------- .../src/UsersScriptingInterface.h | 8 ++++ 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index de4a6bb167..581b260751 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2342,6 +2342,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return viewFrustum.getPosition(); }); + DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); }); + render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; if (htmlContent) { @@ -3287,6 +3289,40 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { #endif } +void Application::userKickConfirmation(const QUuid& nodeID) { + auto avatarHashMap = DependencyManager::get(); + auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); + + QString userName; + + if (avatar) { + userName = avatar->getSessionDisplayName(); + } else { + userName = nodeID.toString(); + } + + QString kickMessage = "Do you wish to kick " + userName + " from your domain"; + ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Kick User", kickMessage, + QMessageBox::Yes | QMessageBox::No); + + if (dlg->getDialogItem()) { + + QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { + QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); + + bool yes = (static_cast(answer.toInt()) == QMessageBox::Yes); + // ask the NodeList to kick the user with the given session ID + + if (yes) { + DependencyManager::get()->kickNodeBySessionID(nodeID); + } + + DependencyManager::get()->setWaitForKickResponse(false); + }); + DependencyManager::get()->setWaitForKickResponse(true); + } +} + void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) { surfaceContext->setContextProperty("Users", DependencyManager::get().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get().data()); diff --git a/interface/src/Application.h b/interface/src/Application.h index a8cc9450c5..762ac9585a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -593,6 +593,7 @@ private: void toggleTabletUI(bool shouldOpen = false) const; static void setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties); + void userKickConfirmation(const QUuid& nodeID); MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index e3eb8684d1..82c408f386 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -17,6 +17,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi ui qml) +link_hifi_libraries(shared networking octree shaders gpu procedural graphics material-networking model-networking ktx recording avatars fbx hfm entities controllers animation audio physics image midi) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 631f0eb743..9beb52f20a 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -12,8 +12,6 @@ #include "UsersScriptingInterface.h" #include -#include -#include UsersScriptingInterface::UsersScriptingInterface() { // emit a signal when kick permissions have changed @@ -54,42 +52,14 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { } void UsersScriptingInterface::kick(const QUuid& nodeID) { - bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); - if (getCanKick() && !waitingForKickResponse) { - - auto avatarHashMap = DependencyManager::get(); - auto avatar = avatarHashMap->getAvatarBySessionID(nodeID); - - QString userName; - - if (avatar) { - userName = avatar->getSessionDisplayName(); - } else { - userName = nodeID.toString(); - } - - QString kickMessage = "Do you wish to kick " + userName + " from your domain"; - ModalDialogListener* dlg = OffscreenUi::asyncQuestion("Kick User", kickMessage, - QMessageBox::Yes | QMessageBox::No); - - if (dlg->getDialogItem()) { - - QObject::connect(dlg, &ModalDialogListener::response, this, [=] (QVariant answer) { - QObject::disconnect(dlg, &ModalDialogListener::response, this, nullptr); - - bool yes = (static_cast(answer.toInt()) == QMessageBox::Yes); - // ask the NodeList to kick the user with the given session ID - - if (yes) { - DependencyManager::get()->kickNodeBySessionID(nodeID); - } - - _kickResponseLock.withWriteLock([&] { _waitingForKickResponse = false; }); - }); - - _kickResponseLock.withWriteLock([&] { _waitingForKickResponse = true; }); + if (_kickConfirmationOperator) { + bool waitingForKickResponse = _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); + if (getCanKick() && !waitingForKickResponse) { + _kickConfirmationOperator(nodeID); } + } else { + DependencyManager::get()->kickNodeBySessionID(nodeID); } } diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index 0e3f9be0e0..f8ca974b8b 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -39,6 +39,12 @@ class UsersScriptingInterface : public QObject, public Dependency { public: UsersScriptingInterface(); + void setKickConfirmationOperator(std::function kickConfirmationOperator) { + _kickConfirmationOperator = kickConfirmationOperator; + } + + bool getWaitForKickResponse() { return _kickResponseLock.resultWithReadLock([&] { return _waitingForKickResponse; }); } + void setWaitForKickResponse(bool waitForKickResponse) { _kickResponseLock.withWriteLock([&] { _waitingForKickResponse = waitForKickResponse; }); } public slots: @@ -197,6 +203,8 @@ private: bool getRequestsDomainListData(); void setRequestsDomainListData(bool requests); + std::function _kickConfirmationOperator; + ReadWriteLockable _kickResponseLock; bool _waitingForKickResponse { false }; }; From db22fa9eec728540f6f6ed32d3b52d1f9fd18bea Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 18 Mar 2019 09:22:06 -0700 Subject: [PATCH 58/88] show up audio screen using Settings > Audio --- .../resources/qml/hifi/dialogs/Audio.qml | 27 ------------------- interface/src/Menu.cpp | 10 ++++--- 2 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 interface/resources/qml/hifi/dialogs/Audio.qml diff --git a/interface/resources/qml/hifi/dialogs/Audio.qml b/interface/resources/qml/hifi/dialogs/Audio.qml deleted file mode 100644 index 4ce9e14c42..0000000000 --- a/interface/resources/qml/hifi/dialogs/Audio.qml +++ /dev/null @@ -1,27 +0,0 @@ -// -// Audio.qml -// -// Created by Zach Pomerantz on 6/12/2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import "../../windows" -import "../audio" - -ScrollingWindow { - id: root; - - resizable: true; - destroyOnHidden: true; - width: 400; - height: 577; - minSize: Qt.vector2d(400, 500); - - Audio { id: audio; width: root.width } - - objectName: "AudioDialog"; - title: audio.title; -} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 810e21daf5..394c07e842 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -270,10 +270,14 @@ Menu::Menu() { // Settings > Audio... action = addActionToQMenuAndActionHash(settingsMenu, "Audio..."); connect(action, &QAction::triggered, [] { - static const QUrl widgetUrl("hifi/dialogs/Audio.qml"); static const QUrl tabletUrl("hifi/audio/Audio.qml"); - static const QString name("AudioDialog"); - qApp->showDialog(widgetUrl, tabletUrl, name); + auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); + tablet->pushOntoStack(tabletUrl); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } }); // Settings > Graphics... From fe28eaca7cca368c481eabb6b5ad8dd5a3af9b60 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 18 Mar 2019 11:23:48 -0700 Subject: [PATCH 59/88] fix typo --- interface/resources/qml/hifi/NameCard.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index c92afe9e14..4e578f8274 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -96,7 +96,7 @@ Item { enabled: (selected && activeTab == "nearbyTab") || isMyCard; hoverEnabled: enabled onClicked: { - if (Phas3DHTML) { + if (has3DHTML) { userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; userInfoViewer.visible = true; } From 7a5bbb8f6f42b59e85f8678e066cca7b3dfcfa4c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 18 Mar 2019 14:43:32 -0700 Subject: [PATCH 60/88] fix laser ghosting --- .../src/RenderablePolyLineEntityItem.cpp | 90 +++++++++++-------- .../src/RenderablePolyLineEntityItem.h | 2 +- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 98f79780be..df52934b87 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -95,19 +95,18 @@ bool PolyLineEntityRenderer::needsRenderUpdate() const { } bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - return ( - entity->pointsChanged() || - entity->widthsChanged() || - entity->normalsChanged() || - entity->texturesChanged() || - entity->colorsChanged() || - _isUVModeStretch != entity->getIsUVModeStretch() || - _glow != entity->getGlow() || - _faceCamera != entity->getFaceCamera() - ); + if (entity->pointsChanged() || entity->widthsChanged() || entity->normalsChanged() || entity->texturesChanged() || entity->colorsChanged()) { + return true; + } + + if (_isUVModeStretch != entity->getIsUVModeStretch() || _glow != entity->getGlow() || _faceCamera != entity->getFaceCamera()) { + return true; + } + + return Parent::needsRenderUpdateFromTypedEntity(entity); } -void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { +void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { auto pointsChanged = entity->pointsChanged(); auto widthsChanged = entity->widthsChanged(); auto normalsChanged = entity->normalsChanged(); @@ -119,10 +118,6 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo entity->resetPolyLineChanged(); - // Transform - updateModelTransformAndBound(); - _renderTransform = getModelTransform(); - // Textures if (entity->texturesChanged()) { entity->resetTexturesChanged(); @@ -131,7 +126,9 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (!textures.isEmpty()) { entityTextures = QUrl(textures); } - _texture = DependencyManager::get()->getTexture(entityTextures); + withWriteLock([&] { + _texture = DependencyManager::get()->getTexture(entityTextures); + }); _textureAspectRatio = 1.0f; _textureLoaded = false; } @@ -145,11 +142,13 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo // Data bool faceCameraChanged = faceCamera != _faceCamera; - if (faceCameraChanged || glow != _glow) { - _faceCamera = faceCamera; - _glow = glow; - updateData(); - } + withWriteLock([&] { + if (faceCameraChanged || glow != _glow) { + _faceCamera = faceCamera; + _glow = glow; + updateData(); + } + }); // Geometry if (pointsChanged) { @@ -165,10 +164,21 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo _colors = entity->getStrokeColors(); _color = toGlm(entity->getColor()); } - if (_isUVModeStretch != isUVModeStretch || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { - _isUVModeStretch = isUVModeStretch; - updateGeometry(); - } + + bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch; + _isUVModeStretch = isUVModeStretch; + + void* key = (void*)this; + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [=]() { + withWriteLock([&] { + updateModelTransformAndBound(); + _renderTransform = getModelTransform(); + + if (uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { + updateGeometry(); + } + }); + }); } void PolyLineEntityRenderer::updateGeometry() { @@ -267,22 +277,32 @@ void PolyLineEntityRenderer::updateData() { } void PolyLineEntityRenderer::doRender(RenderArgs* args) { - if (_numVertices < 2) { - return; - } - PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - if (!_pipeline || !_glowPipeline) { + size_t numVertices; + Transform transform; + gpu::TexturePointer texture; + withReadLock([&] { + numVertices = _numVertices; + transform = _renderTransform; + texture = _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture(); + + batch.setResourceBuffer(0, _polylineGeometryBuffer); + batch.setUniformBuffer(0, _polylineDataBuffer); + }); + + if (numVertices < 2) { + return; + } + + if (!_pipeline) { buildPipeline(); } batch.setPipeline(_glow ? _glowPipeline : _pipeline); - batch.setModelTransform(_renderTransform); - batch.setResourceTexture(0, _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture()); - batch.setResourceBuffer(0, _polylineGeometryBuffer); - batch.setUniformBuffer(0, _polylineDataBuffer); - batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * _numVertices), 0); + batch.setModelTransform(transform); + batch.setResourceTexture(0, texture); + batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index b8a3ad3b3e..3815b57671 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -31,7 +31,7 @@ public: protected: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; - virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; From 718eed8d5b8494046b970ded5f5fb36a18be64f2 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 19 Mar 2019 09:58:44 -0700 Subject: [PATCH 61/88] Corrected typo. --- cmake/macros/TargetPython.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/TargetPython.cmake b/cmake/macros/TargetPython.cmake index cd0ea0f34c..2c055cf8bc 100644 --- a/cmake/macros/TargetPython.cmake +++ b/cmake/macros/TargetPython.cmake @@ -1,7 +1,7 @@ macro(TARGET_PYTHON) if (NOT HIFI_PYTHON_EXEC) # Find the python interpreter - if (CAME_VERSION VERSION_LESS 3.12) + if (CMAKE_VERSION VERSION_LESS 3.12) # this logic is deprecated in CMake after 3.12 # FIXME eventually we should make 3.12 the min cmake verion and just use the Python3 find_package path set(Python_ADDITIONAL_VERSIONS 3) From 51ab8880e93eeaa927d0267417e731615a47c139 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Tue, 19 Mar 2019 13:25:40 -0700 Subject: [PATCH 62/88] Corrected labels. --- tools/nitpick/ui/Nitpick.ui | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/nitpick/ui/Nitpick.ui b/tools/nitpick/ui/Nitpick.ui index a0f368863d..e1d7699f22 100644 --- a/tools/nitpick/ui/Nitpick.ui +++ b/tools/nitpick/ui/Nitpick.ui @@ -46,7 +46,7 @@ - 5 + 0 @@ -620,7 +620,7 @@ <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> - usePreviousInstallation + Use Previous Installation false @@ -895,7 +895,7 @@ <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> - usePreviousInstallation + Use Previous Installation false From 775eddc2657df459e76b365cade537fc2252ee0f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 19 Mar 2019 16:16:53 -0700 Subject: [PATCH 63/88] Agent requires the ModelCache singleton for zone entities w/ meshes --- assignment-client/src/Agent.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5c644cb132..3937d5f799 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,6 +52,8 @@ #include #include // TODO: consider moving to scriptengine.h +#include + #include "entities/AssignmentParentFinder.h" #include "AssignmentDynamicFactory.h" #include "RecordingScriptingInterface.h" @@ -99,6 +101,9 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + // Needed to ensure the creation of the DebugDraw instance on the main thread DebugDraw::getInstance(); @@ -819,6 +824,9 @@ void Agent::aboutToFinish() { DependencyManager::get()->cleanup(); + DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); // cleanup the AudioInjectorManager (and any still running injectors) From 19c51b25d1d28878fbe692322774e62905d2fea9 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 19 Mar 2019 22:38:16 +0100 Subject: [PATCH 64/88] don't ignore updates that originate from entityPropertiesTool itself --- scripts/system/edit.js | 7 ++++--- scripts/system/html/js/entityProperties.js | 2 +- scripts/system/libraries/entitySelectionTool.js | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 2c3785217c..ca2918a108 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2286,14 +2286,15 @@ var PropertiesTool = function (opts) { }) }; - function updateSelections(selectionUpdated) { + function updateSelections(selectionUpdated, caller) { if (blockPropertyUpdates) { return; } var data = { type: 'update', - spaceMode: selectionDisplay.getSpaceMode() + spaceMode: selectionDisplay.getSpaceMode(), + isPropertiesToolUpdate: caller === this, }; if (selectionUpdated) { @@ -2339,7 +2340,7 @@ var PropertiesTool = function (opts) { emitScriptEvent(data); } - selectionManager.addEventListener(updateSelections); + selectionManager.addEventListener(updateSelections, this); var onWebEventReceived = function(data) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f501df7933..f259b0a017 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3326,7 +3326,7 @@ function loaded() { let hasSelectedEntityChanged = lastEntityID !== '"' + selectedEntityProperties.id + '"'; - if (!hasSelectedEntityChanged && document.hasFocus()) { + if (!data.isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { // in case the selection has not changed and we still have focus on the properties page, // we will ignore the event. return; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 269283ea6d..064dafec06 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -128,8 +128,11 @@ SelectionManager = (function() { } }; - that.addEventListener = function(func) { - listeners.push(func); + that.addEventListener = function(func, thisContext) { + listeners.push({ + callback: func, + thisContext: thisContext + }); }; that.hasSelection = function() { @@ -572,7 +575,7 @@ SelectionManager = (function() { for (var j = 0; j < listeners.length; j++) { try { - listeners[j](selectionUpdated === true, caller); + listeners[j].callback.call(listeners[j].thisContext, selectionUpdated === true, caller); } catch (e) { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } From 640b05304a8a72abe2591e157da6470edf9f7931 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 19 Mar 2019 16:24:07 -0700 Subject: [PATCH 65/88] Case 21392 - Excise unneeded code from marketplacesInject.js --- scripts/system/html/js/marketplacesInject.js | 365 ------------------- 1 file changed, 365 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 8d408169ba..56075a514e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -28,10 +28,6 @@ var xmlHttpRequest = null; var isPreparing = false; // Explicitly track download request status. - var limitedCommerce = false; - var commerceMode = false; - var userIsLoggedIn = false; - var walletNeedsSetup = false; var marketplaceBaseURL = "https://highfidelity.com"; var messagesWaiting = false; @@ -109,356 +105,6 @@ }); } - emitWalletSetupEvent = function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "WALLET_SETUP" - })); - }; - - function maybeAddSetupWalletButton() { - if (!$('body').hasClass("walletsetup-injected") && userIsLoggedIn && walletNeedsSetup) { - $('body').addClass("walletsetup-injected"); - - var resultsElement = document.getElementById('results'); - var setupWalletElement = document.createElement('div'); - setupWalletElement.classList.add("row"); - setupWalletElement.id = "setupWalletDiv"; - setupWalletElement.style = "height:60px;margin:20px 10px 10px 10px;padding:12px 5px;" + - "background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;"; - - var span = document.createElement('span'); - span.style = "margin:10px 5px;color:#1b6420;font-size:15px;"; - span.innerHTML = "Activate your Wallet to get money and shop in Marketplace."; - - var xButton = document.createElement('a'); - xButton.id = "xButton"; - xButton.setAttribute('href', "#"); - xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;"; - xButton.innerHTML = "X"; - xButton.onclick = function () { - setupWalletElement.remove(); - dummyRow.remove(); - }; - - setupWalletElement.appendChild(span); - setupWalletElement.appendChild(xButton); - - resultsElement.insertBefore(setupWalletElement, resultsElement.firstChild); - - // Dummy row for padding - var dummyRow = document.createElement('div'); - dummyRow.classList.add("row"); - dummyRow.style = "height:15px;"; - resultsElement.insertBefore(dummyRow, resultsElement.firstChild); - } - } - - function maybeAddLogInButton() { - if (!$('body').hasClass("login-injected") && !userIsLoggedIn) { - $('body').addClass("login-injected"); - var resultsElement = document.getElementById('results'); - if (!resultsElement) { // If we're on the main page, this will evaluate to `true` - resultsElement = document.getElementById('item-show'); - resultsElement.style = 'margin-top:0;'; - } - var logInElement = document.createElement('div'); - logInElement.classList.add("row"); - logInElement.id = "logInDiv"; - logInElement.style = "height:60px;margin:20px 10px 10px 10px;padding:5px;" + - "background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;"; - - var button = document.createElement('a'); - button.classList.add("btn"); - button.classList.add("btn-default"); - button.id = "logInButton"; - button.setAttribute('href', "#"); - button.innerHTML = "LOG IN"; - button.style = "width:80px;height:100%;margin-top:0;margin-left:10px;padding:13px;font-weight:bold;background:linear-gradient(white, #ccc);"; - button.onclick = function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "LOGIN" - })); - }; - - var span = document.createElement('span'); - span.style = "margin:10px;color:#1b6420;font-size:15px;"; - span.innerHTML = "to get items from the Marketplace."; - - var xButton = document.createElement('a'); - xButton.id = "xButton"; - xButton.setAttribute('href', "#"); - xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;"; - xButton.innerHTML = "X"; - xButton.onclick = function () { - logInElement.remove(); - dummyRow.remove(); - }; - - logInElement.appendChild(button); - logInElement.appendChild(span); - logInElement.appendChild(xButton); - - resultsElement.insertBefore(logInElement, resultsElement.firstChild); - - // Dummy row for padding - var dummyRow = document.createElement('div'); - dummyRow.classList.add("row"); - dummyRow.style = "height:15px;"; - resultsElement.insertBefore(dummyRow, resultsElement.firstChild); - } - } - - function changeDropdownMenu() { - var logInOrOutButton = document.createElement('a'); - logInOrOutButton.id = "logInOrOutButton"; - logInOrOutButton.setAttribute('href', "#"); - logInOrOutButton.innerHTML = userIsLoggedIn ? "Log Out" : "Log In"; - logInOrOutButton.onclick = function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "LOGIN" - })); - }; - - $($('.dropdown-menu').find('li')[0]).append(logInOrOutButton); - - $('a[href="/marketplace?view=mine"]').each(function () { - $(this).attr('href', '#'); - $(this).on('click', function () { - EventBridge.emitWebEvent(JSON.stringify({ - type: "MY_ITEMS" - })); - }); - }); - } - - function buyButtonClicked(id, referrer, edition) { - EventBridge.emitWebEvent(JSON.stringify({ - type: "CHECKOUT", - itemId: id, - referrer: referrer, - itemEdition: edition - })); - } - - function injectBuyButtonOnMainPage() { - var cost; - - // Unbind original mouseenter and mouseleave behavior - $('body').off('mouseenter', '#price-or-edit .price'); - $('body').off('mouseleave', '#price-or-edit .price'); - - $('.grid-item').find('#price-or-edit').each(function () { - $(this).css({ "margin-top": "0" }); - }); - - $('.grid-item').find('#price-or-edit').find('a').each(function() { - if ($(this).attr('href') !== '#') { // Guard necessary because of the AJAX nature of Marketplace site - $(this).attr('data-href', $(this).attr('href')); - $(this).attr('href', '#'); - } - cost = $(this).closest('.col-xs-3').find('.item-cost').text(); - var costInt = parseInt(cost, 10); - - $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); - $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); - - var priceElement = $(this).find('.price'); - var available = true; - - if (priceElement.text() === 'invalidated' || - priceElement.text() === 'sold out' || - priceElement.text() === 'not for sale') { - available = false; - priceElement.css({ - "padding": "3px 5px 10px 5px", - "height": "40px", - "background": "linear-gradient(#a2a2a2, #fefefe)", - "color": "#000", - "font-weight": "600", - "line-height": "34px" - }); - } else { - priceElement.css({ - "padding": "3px 5px", - "height": "40px", - "background": "linear-gradient(#00b4ef, #0093C5)", - "color": "#FFF", - "font-weight": "600", - "line-height": "34px" - }); - } - - if (parseInt(cost) > 0) { - priceElement.css({ "width": "auto" }); - - if (available) { - priceElement.html(' ' + cost); - } - - priceElement.css({ "min-width": priceElement.width() + 30 }); - } - }); - - // change pricing to GET/BUY on button hover - $('body').on('mouseenter', '#price-or-edit .price', function () { - var $this = $(this); - var buyString = "BUY"; - var getString = "GET"; - // Protection against the button getting stuck in the "BUY"/"GET" state. - // That happens when the browser gets two MOUSEENTER events before getting a - // MOUSELEAVE event. Also, if not available for sale, just return. - if ($this.text() === buyString || - $this.text() === getString || - $this.text() === 'invalidated' || - $this.text() === 'sold out' || - $this.text() === 'not for sale' ) { - return; - } - $this.data('initialHtml', $this.html()); - - var cost = $(this).parent().siblings().text(); - if (parseInt(cost) > 0) { - $this.text(buyString); - } - if (parseInt(cost) == 0) { - $this.text(getString); - } - }); - - $('body').on('mouseleave', '#price-or-edit .price', function () { - var $this = $(this); - $this.html($this.data('initialHtml')); - }); - - - $('.grid-item').find('#price-or-edit').find('a').on('click', function () { - var price = $(this).closest('.grid-item').find('.price').text(); - if (price === 'invalidated' || - price === 'sold out' || - price === 'not for sale') { - return false; - } - buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), - "mainPage", - -1); - }); - } - - function injectUnfocusOnSearch() { - // unfocus input field on search, thus hiding virtual keyboard - $('#search-box').on('submit', function () { - if (document.activeElement) { - document.activeElement.blur(); - } - }); - } - - // fix for 10108 - marketplace category cannot scroll - function injectAddScrollbarToCategories() { - $('#categories-dropdown').on('show.bs.dropdown', function () { - $('body > div.container').css('display', 'none') - $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': 'auto', 'height': 'calc(100vh - 110px)' }); - }); - - $('#categories-dropdown').on('hide.bs.dropdown', function () { - $('body > div.container').css('display', ''); - $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': '', 'height': '' }); - }); - } - - function injectHiFiCode() { - if (commerceMode) { - maybeAddLogInButton(); - maybeAddSetupWalletButton(); - - if (!$('body').hasClass("code-injected")) { - - $('body').addClass("code-injected"); - changeDropdownMenu(); - - var target = document.getElementById('templated-items'); - // MutationObserver is necessary because the DOM is populated after the page is loaded. - // We're searching for changes to the element whose ID is '#templated-items' - this is - // the element that gets filled in by the AJAX. - var observer = new MutationObserver(function (mutations) { - mutations.forEach(function (mutation) { - injectBuyButtonOnMainPage(); - }); - }); - var config = { attributes: true, childList: true, characterData: true }; - observer.observe(target, config); - - // Try this here in case it works (it will if the user just pressed the "back" button, - // since that doesn't trigger another AJAX request. - injectBuyButtonOnMainPage(); - } - } - - injectUnfocusOnSearch(); - injectAddScrollbarToCategories(); - } - - function injectHiFiItemPageCode() { - if (commerceMode) { - maybeAddLogInButton(); - - if (!$('body').hasClass("code-injected")) { - - $('body').addClass("code-injected"); - changeDropdownMenu(); - - var purchaseButton = $('#side-info').find('.btn').first(); - - var href = purchaseButton.attr('href'); - purchaseButton.attr('href', '#'); - var cost = $('.item-cost').text(); - var costInt = parseInt(cost, 10); - var availability = $.trim($('.item-availability').text()); - if (limitedCommerce && (costInt > 0)) { - availability = ''; - } - if (availability === 'available') { - purchaseButton.css({ - "background": "linear-gradient(#00b4ef, #0093C5)", - "color": "#FFF", - "font-weight": "600", - "padding-bottom": "10px" - }); - } else { - purchaseButton.css({ - "background": "linear-gradient(#a2a2a2, #fefefe)", - "color": "#000", - "font-weight": "600", - "padding-bottom": "10px" - }); - } - - var type = $('.item-type').text(); - var isUpdating = window.location.href.indexOf('edition=') > -1; - var urlParams = new URLSearchParams(window.location.search); - if (isUpdating) { - purchaseButton.html('UPDATE FOR FREE'); - } else if (availability !== 'available') { - purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : '')); - } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { - purchaseButton.html('PURCHASE ' + cost); - } - - purchaseButton.on('click', function () { - if ('available' === availability || isUpdating) { - buyButtonClicked(window.location.pathname.split("/")[3], - "itemPage", - urlParams.get('edition')); - } - }); - } - } - - injectUnfocusOnSearch(); - } - function updateClaraCode() { // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. @@ -695,16 +341,9 @@ case DIRECTORY: injectDirectoryCode(); break; - case HIFI: - injectHiFiCode(); - break; case CLARA: injectClaraCode(); break; - case HIFI_ITEM_PAGE: - injectHiFiItemPageCode(); - break; - } } @@ -717,10 +356,6 @@ cancelClaraDownload(); } else if (message.type === "marketplaces") { if (message.action === "commerceSetting") { - limitedCommerce = !!message.data.limitedCommerce; - commerceMode = !!message.data.commerceMode; - userIsLoggedIn = !!message.data.userIsLoggedIn; - walletNeedsSetup = !!message.data.walletNeedsSetup; marketplaceBaseURL = message.data.metaverseServerURL; if (marketplaceBaseURL.indexOf('metaverse.') !== -1) { marketplaceBaseURL = marketplaceBaseURL.replace('metaverse.', ''); From ab61f65ea2897bec5ed2973c6a5825e73603fbbc Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 19 Mar 2019 17:08:04 -0700 Subject: [PATCH 66/88] CR fix --- interface/resources/qml/hifi/NameCard.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 4e578f8274..141ddf0077 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -368,7 +368,7 @@ Item { enabled: selected hoverEnabled: true onClicked: { - if(has3DHTML) { + if (has3DHTML) { userInfoViewer.url = Account.metaverseServerURL + "/users/" + userName; userInfoViewer.visible = true; } From 27fa0dc4c683d699233085a0d67d62a0729e83bc Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 19 Mar 2019 17:24:19 -0700 Subject: [PATCH 67/88] CR fixes --- .../resources/qml/hifi/commerce/marketplace/Marketplace.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index a9f058fce1..3402f919fb 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -359,7 +359,7 @@ Rectangle { } onAccepted: { - if(root.searchString !== searchField.text) { + if (root.searchString !== searchField.text) { root.searchString = searchField.text; getMarketplaceItems(); searchField.forceActiveFocus(); From 9d11e44b4bed1ed0ea32b2c62f28f452719336da Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 20 Mar 2019 11:37:16 -0700 Subject: [PATCH 68/88] update AvatarEntity trait when parentID changes --- interface/src/avatar/MyAvatar.cpp | 23 +++++++++++-------- interface/src/avatar/MyAvatar.h | 2 +- .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/avatars-renderer/Avatar.h | 2 +- .../entities/src/EntityEditPacketSender.cpp | 11 ++------- .../entities/src/EntityEditPacketSender.h | 4 ++-- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02ef91cdba..ddedc270f8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1570,7 +1570,7 @@ void MyAvatar::handleChangedAvatarEntityData() { entityTree->withWriteLock([&] { EntityItemPointer entity = entityTree->addEntity(id, properties); if (entity) { - packetSender->queueEditEntityMessage(PacketType::EntityAdd, entityTree, id, properties); + packetSender->queueEditAvatarEntityMessage(entityTree, id); } }); } @@ -3451,10 +3451,10 @@ float MyAvatar::getGravity() { } void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { - QUuid oldID = getSessionUUID(); + QUuid oldSessionID = getSessionUUID(); Avatar::setSessionUUID(sessionUUID); - QUuid id = getSessionUUID(); - if (id != oldID) { + QUuid newSessionID = getSessionUUID(); + if (newSessionID != oldSessionID) { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { @@ -3462,15 +3462,20 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); + EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { auto entity = entityTree->findEntityByID(entityID); if (!entity) { continue; } - entity->setOwningAvatarID(id); - if (entity->getParentID() == oldID) { - entity->setParentID(id); + entity->setOwningAvatarID(newSessionID); + // NOTE: each attached AvatarEntity should already have the correct updated parentID + // via magic in SpatiallyNestable, but when an AvatarEntity IS parented to MyAvatar + // we need to update the "packedAvatarEntityData" we send to the avatar-mixer + // so that others will get the updated state. + if (entity->getParentID() == newSessionID) { + packetSender->queueEditAvatarEntityMessage(entityTree, entityID); } } }); @@ -5523,14 +5528,14 @@ void MyAvatar::initFlowFromFST() { } } -void MyAvatar::sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { +void MyAvatar::sendPacket(const QUuid& entityID) const { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (entityTree) { entityTree->withWriteLock([&] { // force an update packet EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entityID, properties); + packetSender->queueEditAvatarEntityMessage(entityTree, entityID); }); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index aadc8ee268..905216cfba 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1918,7 +1918,7 @@ private: bool didTeleport(); bool getIsAway() const { return _isAway; } void setAway(bool value); - void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const override; + void sendPacket(const QUuid& entityID) const override; std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 38108416ee..992ee5db96 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -376,7 +376,7 @@ bool Avatar::applyGrabChanges() { const EntityItemPointer& entity = std::dynamic_pointer_cast(target); if (entity && entity->getEntityHostType() == entity::HostType::AVATAR && entity->getSimulationOwner().getID() == getID()) { EntityItemProperties properties = entity->getProperties(); - sendPacket(entity->getID(), properties); + sendPacket(entity->getID()); } } } else { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index d81b04d4b2..6026367440 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -605,7 +605,7 @@ protected: // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; - virtual void sendPacket(const QUuid& entityID, const EntityItemProperties& properties) const { } + virtual void sendPacket(const QUuid& entityID) const { } bool applyGrabChanges(); void relayJointDataToChildren(); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index af0e34303b..0491bdedae 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -39,9 +39,7 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByte } } -void EntityEditPacketSender::queueEditAvatarEntityMessage(EntityTreePointer entityTree, - EntityItemID entityItemID, - const EntityItemProperties& properties) { +void EntityEditPacketSender::queueEditAvatarEntityMessage(EntityTreePointer entityTree, EntityItemID entityItemID) { assert(_myAvatar); if (!entityTree) { qCDebug(entities) << "EntityEditPacketSender::queueEditAvatarEntityMessage null entityTree."; @@ -54,11 +52,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(EntityTreePointer enti } entity->setLastBroadcast(usecTimestampNow()); - // serialize ALL properties in an "AvatarEntity" packet - // rather than just the ones being edited. - EntityItemProperties entityProperties = entity->getProperties(); - entityProperties.merge(properties); - OctreePacketData packetData(false, AvatarTraits::MAXIMUM_TRAIT_SIZE); EncodeBitstreamParams params; EntityTreeElementExtraEncodeDataPointer extra { nullptr }; @@ -82,7 +75,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar"; } else if (properties.getOwningAvatarID() == _myAvatar->getID()) { // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server - queueEditAvatarEntityMessage(entityTree, entityItemID, properties); + queueEditAvatarEntityMessage(entityTree, entityItemID); } else { qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar"; } diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 99a5202986..3cc2f016f0 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -50,8 +50,8 @@ public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); private: - void queueEditAvatarEntityMessage(EntityTreePointer entityTree, - EntityItemID entityItemID, const EntityItemProperties& properties); + friend class MyAvatar; + void queueEditAvatarEntityMessage(EntityTreePointer entityTree, EntityItemID entityItemID); private: std::mutex _mutex; From 3775633cc3b61f8391d7dc7088265fd3118fd8a1 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 20 Mar 2019 15:29:13 -0700 Subject: [PATCH 69/88] Case 20407 - Pay-In API doesn't display error when no username specified --- .../resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 626ac4da65..e159344d5c 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -2248,6 +2248,7 @@ Item { if (sendAssetStep.selectedRecipientUserName === "") { console.log("SendAsset: Script didn't specify a recipient username!"); sendAssetHome.visible = false; + root.nextActiveView = 'paymentFailure'; return; } From b12a2684649c7ab2382dbb958452869f3e03e1f6 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Wed, 20 Mar 2019 16:25:59 -0700 Subject: [PATCH 70/88] Fixed copy-paste-drink too much error --- tools/nitpick/src/AWSInterface.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/nitpick/src/AWSInterface.cpp b/tools/nitpick/src/AWSInterface.cpp index 16c0a220d8..19697d51dc 100644 --- a/tools/nitpick/src/AWSInterface.cpp +++ b/tools/nitpick/src/AWSInterface.cpp @@ -53,10 +53,6 @@ void AWSInterface::createWebPageFromResults(const QString& testResults, _testResults = testResults; - _urlLineEdit = urlLineEdit; - _urlLineEdit->setEnabled(false); - - _urlLineEdit = urlLineEdit; _urlLineEdit->setEnabled(false); From 5888adfd9035090cfc84737d6d5917f371b87d08 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 20 Mar 2019 17:08:42 -0700 Subject: [PATCH 71/88] update terms of service links --- interface/resources/qml/LoginDialog/CompleteProfileBody.qml | 4 ++-- interface/resources/qml/LoginDialog/SignUpBody.qml | 2 +- interface/resources/qml/LoginDialog/UsernameCollisionBody.qml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index 65f8a8c1dc..17d6a7d3b3 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -379,9 +379,9 @@ Item { Component.onCompleted: { // with the link. if (completeProfileBody.withOculus) { - termsText.text = qsTr("By signing up, you agree to High Fidelity's Terms of Service") + termsText.text = qsTr("By signing up, you agree to High Fidelity's Terms of Service") } else { - termsText.text = qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") + termsText.text = qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") } } } diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index 64df9089a1..69ac2f5a6c 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -395,7 +395,7 @@ Item { text: signUpBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By signing up, you agree to High Fidelity's Terms of Service") + termsText.text = qsTr("By signing up, you agree to High Fidelity's Terms of Service") } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 2c8e61a29a..d450b1e7bc 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -218,7 +218,7 @@ Item { text: usernameCollisionBody.termsContainerText Component.onCompleted: { // with the link. - termsText.text = qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") + termsText.text = qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") } } From 61b7b8b66963a5f1fbae027e81fcc0705d074e34 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 21 Mar 2019 08:36:32 -0700 Subject: [PATCH 72/88] reduce lambda copies --- .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index df52934b87..2430643ce2 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -167,14 +167,16 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch; _isUVModeStretch = isUVModeStretch; + + bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged; void* key = (void*)this; - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [=]() { + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () { withWriteLock([&] { updateModelTransformAndBound(); _renderTransform = getModelTransform(); - if (uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { + if (geometryChanged) { updateGeometry(); } }); From c777f94231045cfc35de064fa8d79f6d47f9f1ee Mon Sep 17 00:00:00 2001 From: Robin Wilson Date: Tue, 19 Mar 2019 16:14:55 -0700 Subject: [PATCH 73/88] remove AvatarBookmarks.deleteBookmark function --- interface/src/AvatarBookmarks.cpp | 3 +++ interface/src/AvatarBookmarks.h | 3 +++ interface/src/Bookmarks.h | 5 +---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 5fe35bd23f..54c67daab8 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -149,6 +149,9 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { emit bookmarkDeleted(bookmarkName); } +void AvatarBookmarks::deleteBookmark() { +} + void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto currentAvatarEntities = myAvatar->getAvatarEntityData(); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index 4623e7d929..df75fec865 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -76,6 +76,9 @@ protected: void readFromFile() override; QVariantMap getAvatarDataToBookmark(); +protected slots: + void deleteBookmark() override; + private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; const QString ENTRY_AVATAR_URL = "avatarUrl"; diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index 88510e4eda..56d26b55c6 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -51,13 +51,10 @@ protected: bool _isMenuSorted; protected slots: - /**jsdoc - * @function AvatarBookmarks.deleteBookmark - */ /**jsdoc * @function LocationBookmarks.deleteBookmark */ - void deleteBookmark(); + virtual void deleteBookmark(); private: static bool sortOrder(QAction* a, QAction* b); From 87d75ec75ceee653cfb974ec49d92547c99409ca Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Thu, 21 Mar 2019 12:08:42 -0700 Subject: [PATCH 74/88] Case 20617 - People app filter bar breaks when deleting connections --- interface/resources/qml/hifi/Pal.qml | 8 ++++++++ scripts/system/pal.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 1c190a2b79..55f2bb80b1 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1261,6 +1261,14 @@ Rectangle { case 'refreshConnections': refreshConnections(); break; + case 'connectionRemoved': + for (var i=0; i Date: Thu, 21 Mar 2019 15:00:11 -0700 Subject: [PATCH 75/88] don't queue AvatarEntity messages when not in domain --- interface/src/avatar/MyAvatar.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ddedc270f8..298e661f24 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3454,6 +3454,11 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { QUuid oldSessionID = getSessionUUID(); Avatar::setSessionUUID(sessionUUID); QUuid newSessionID = getSessionUUID(); + if (DependencyManager::get()->getSessionUUID().isNull()) { + // we don't actually have a connection to a domain right now + // so there is no need to queue AvatarEntity messages --> bail early + return; + } if (newSessionID != oldSessionID) { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; From 9a14cfc7dfbeb63a56796bd55dad82579a87b613 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 21 Mar 2019 20:50:59 +0100 Subject: [PATCH 76/88] make sure that onWebEventReceived has the correct context in VR --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index ca2918a108..894ea2b696 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2524,7 +2524,7 @@ var PropertiesTool = function (opts) { createToolsWindow.webEventReceived.addListener(this, onWebEventReceived); - webView.webEventReceived.connect(onWebEventReceived); + webView.webEventReceived.connect(this, onWebEventReceived); return that; }; From 4c7d5c7da7cd876933be0fbe45a247021245f7ba Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Mar 2019 12:19:20 +1300 Subject: [PATCH 77/88] Detect signal functions based on their return type --- tools/jsdoc/plugins/hifi.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index b4350ddbdb..bd77204347 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -121,6 +121,11 @@ exports.handlers = { e.doclet.description = (e.doclet.description ? e.doclet.description : "") + availableIn; } } + + if (e.doclet.kind === "function" && e.doclet.returns && e.doclet.returns[0].type + && e.doclet.returns[0].type.names[0] === "Signal") { + e.doclet.kind = "signal"; + } } }; @@ -178,4 +183,4 @@ exports.defineTags = function (dictionary) { } }); -}; \ No newline at end of file +}; From 025326b85f2a85924386033acd486f15a2a2eb61 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Mar 2019 12:20:10 +1300 Subject: [PATCH 78/88] Remove custom @signal tag --- tools/jsdoc/plugins/hifi.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index bd77204347..b2b91de1c8 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -129,20 +129,6 @@ exports.handlers = { } }; -// Functions for adding @signal custom tag -/** @private */ -function setDocletKindToTitle(doclet, tag) { - doclet.addTag( 'kind', tag.title ); -} - -function setDocletNameToValue(doclet, tag) { - if (tag.value && tag.value.description) { // as in a long tag - doclet.addTag('name', tag.value.description); - } else if (tag.text) { // or a short tag - doclet.addTag('name', tag.text); - } -} - // Define custom hifi tags here exports.defineTags = function (dictionary) { @@ -173,14 +159,5 @@ exports.defineTags = function (dictionary) { doclet.hifiServerEntity = true; } }); - - // @signal - dictionary.defineTag("signal", { - mustHaveValue: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - } - }); }; From ba0923a3ad7a44550257b5196e3f793a1a8898b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Mar 2019 12:20:22 +1300 Subject: [PATCH 79/88] Fix signal summary text --- tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl b/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl index b9a0e0ca86..c5fdefc7d8 100644 --- a/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl +++ b/tools/jsdoc/hifi-jsdoc-template/tmpl/signalList.tmpl @@ -13,8 +13,8 @@ var self = this;

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 B?KS`a literal 74623 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 vsaK=d2 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 From 0d9403051574d6bffd3ab4343ec08d17d78dd5da Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 15 Mar 2019 15:32:31 -0700 Subject: [PATCH 51/88] use web surface alpha --- libraries/render-utils/src/simple_opaque_web_browser.slf | 2 +- libraries/render-utils/src/simple_transparent_web_browser.slf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/simple_opaque_web_browser.slf b/libraries/render-utils/src/simple_opaque_web_browser.slf index 36b0c825ad..df789ee22b 100644 --- a/libraries/render-utils/src/simple_opaque_web_browser.slf +++ b/libraries/render-utils/src/simple_opaque_web_browser.slf @@ -28,7 +28,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord1 _texCoord01.zw void main(void) { - vec4 texel = texture(originalTexture, _texCoord0.st); + vec4 texel = texture(originalTexture, _texCoord0); texel = color_sRGBAToLinear(texel); packDeferredFragmentUnlit(normalize(_normalWS), 1.0, _color.rgb * texel.rgb); } diff --git a/libraries/render-utils/src/simple_transparent_web_browser.slf b/libraries/render-utils/src/simple_transparent_web_browser.slf index 1d5aad0914..599fd3d87f 100644 --- a/libraries/render-utils/src/simple_transparent_web_browser.slf +++ b/libraries/render-utils/src/simple_transparent_web_browser.slf @@ -28,11 +28,11 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord1 _texCoord01.zw void main(void) { - vec4 texel = texture(originalTexture, _texCoord0.st); + vec4 texel = texture(originalTexture, _texCoord0); texel = color_sRGBAToLinear(texel); packDeferredFragmentTranslucent( normalize(_normalWS), - _color.a, + _color.a * texel.a, _color.rgb * texel.rgb, DEFAULT_ROUGHNESS); } From d4e1ec97418614d9a88393352c41ce71d8aa5eb2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 15 Mar 2019 15:50:48 -0700 Subject: [PATCH 52/88] fix emitScriptEvent --- interface/src/Application.cpp | 7 +++++++ libraries/entities-renderer/src/RenderableEntityItem.h | 1 + .../entities-renderer/src/RenderableWebEntityItem.h | 2 +- libraries/entities/src/EntityItem.h | 2 -- libraries/entities/src/EntityScriptingInterface.cpp | 9 +-------- libraries/entities/src/EntityScriptingInterface.h | 1 - libraries/entities/src/EntityTree.cpp | 7 +++++++ libraries/entities/src/EntityTree.h | 4 ++++ 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index de4a6bb167..21ef706dfc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1985,6 +1985,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return nullptr; }); + EntityTree::setEmitScriptEventOperator([this](const QUuid& id, const QVariant& message) { + auto entities = getEntities(); + if (auto entity = entities->renderableForEntityId(id)) { + entity->emitScriptEvent(message); + } + }); + EntityTree::setTextSizeOperator([this](const QUuid& id, const QString& text) { auto entities = getEntities(); if (auto entity = entities->renderableForEntityId(id)) { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index e9a6035e3d..39f9ad091e 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -40,6 +40,7 @@ public: virtual bool wantsKeyboardFocus() const { return false; } virtual void setProxyWindow(QWindow* proxyWindow) {} virtual QObject* getEventHandler() { return nullptr; } + virtual void emitScriptEvent(const QVariant& message) {} const EntityItemPointer& getEntity() const { return _entity; } const ItemID& getRenderItemID() const { return _renderItemID; } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0345898b62..7118774d30 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -106,7 +106,7 @@ private: static std::function&, bool&, std::vector&)> _releaseWebSurfaceOperator; public slots: - void emitScriptEvent(const QVariant& scriptMessage); + void emitScriptEvent(const QVariant& scriptMessage) override; signals: void scriptEventReceived(const QVariant& message); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a8baa413..fae871a124 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -511,8 +511,6 @@ public: virtual void setProxyWindow(QWindow* proxyWindow) {} virtual QObject* getEventHandler() { return nullptr; } - virtual void emitScriptEvent(const QVariant& message) {} - QUuid getLastEditedBy() const { return _lastEditedBy; } void setLastEditedBy(QUuid value) { _lastEditedBy = value; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 22cd26eac6..55a36202a8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -2202,14 +2202,7 @@ bool EntityScriptingInterface::wantsHandControllerPointerEvents(const QUuid& id) } void EntityScriptingInterface::emitScriptEvent(const EntityItemID& entityID, const QVariant& message) { - if (_entityTree) { - _entityTree->withReadLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); - if (entity) { - entity->emitScriptEvent(message); - } - }); - } + EntityTree::emitScriptEvent(entityID, message); } // TODO move this someplace that makes more sense... diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 0cf9070b08..f6aedac3fc 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -1529,7 +1529,6 @@ public slots: * @function Entities.emitScriptEvent * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity. * @param {string} message - The message to send. - * @todo This function is currently not implemented. */ Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d64c8870eb..8bf7c92b1f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2978,6 +2978,7 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { std::function EntityTree::_getEntityObjectOperator = nullptr; std::function EntityTree::_textSizeOperator = nullptr; std::function EntityTree::_areEntityClicksCapturedOperator = nullptr; +std::function EntityTree::_emitScriptEventOperator = nullptr; QObject* EntityTree::getEntityObject(const QUuid& id) { if (_getEntityObjectOperator) { @@ -3000,6 +3001,12 @@ bool EntityTree::areEntityClicksCaptured() { return false; } +void EntityTree::emitScriptEvent(const QUuid& id, const QVariant& message) { + if (_emitScriptEventOperator) { + _emitScriptEventOperator(id, message); + } +} + void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 39b3dc57c7..e627a07d13 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -272,6 +272,9 @@ public: static void setEntityClicksCapturedOperator(std::function areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; } static bool areEntityClicksCaptured(); + static void setEmitScriptEventOperator(std::function emitScriptEventOperator) { _emitScriptEventOperator = emitScriptEventOperator; } + static void emitScriptEvent(const QUuid& id, const QVariant& message); + std::map getNamedPaths() const { return _namedPaths; } void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, @@ -383,6 +386,7 @@ private: static std::function _getEntityObjectOperator; static std::function _textSizeOperator; static std::function _areEntityClicksCapturedOperator; + static std::function _emitScriptEventOperator; std::vector _staleProxies; From e1358fd5f5059d44074aeb3e54836ec06f6d6e17 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 15 Mar 2019 16:28:46 -0700 Subject: [PATCH 53/88] prevent export from humanoid config, cleaner scene switch --- .../Editor/AvatarExporter/AvatarExporter.cs | 28 ++++++++++-------- .../AvatarExporter/HeightReference.prefab | 10 +++---- tools/unity-avatar-exporter/Assets/README.txt | 4 +-- .../avatarExporter.unitypackage | Bin 74591 -> 74615 bytes 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index 87f401d478..11d83a52e8 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -17,7 +17,7 @@ using System.Text.RegularExpressions; class AvatarExporter : MonoBehaviour { // update version number for every PR that changes this file, also set updated version in README file - static readonly string AVATAR_EXPORTER_VERSION = "0.3.6"; + static readonly string AVATAR_EXPORTER_VERSION = "0.3.7"; static readonly float HIPS_GROUND_MIN_Y = 0.01f; static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f; @@ -332,8 +332,7 @@ class AvatarExporter : MonoBehaviour { static List alternateStandardShaderMaterials = new List(); static List unsupportedShaderMaterials = new List(); - static Scene previewScene; - static string previousScene = ""; + static SceneSetup[] previousSceneSetup; static Vector3 previousScenePivot = Vector3.zero; static Quaternion previousSceneRotation = Quaternion.identity; static float previousSceneSize = 0.0f; @@ -1223,14 +1222,22 @@ class AvatarExporter : MonoBehaviour { } static bool OpenPreviewScene() { + // store the current scene setup to restore when closing the preview scene + previousSceneSetup = EditorSceneManager.GetSceneManagerSetup(); + + // if the user is currently in the Humanoid Avatar Configuration then inform them to close it first + if (EditorSceneManager.GetActiveScene().name == "Avatar Configuration" && previousSceneSetup.Length == 0) { + EditorUtility.DisplayDialog("Error", "Please exit the Avatar Configuration before exporting.", "Ok"); + return false; + } + // see if the user wants to save their current scene before opening preview avatar scene in place of user's scene if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { return false; } - // store the user's current scene to re-open when done and open a new default scene in place of the user's scene - previousScene = EditorSceneManager.GetActiveScene().path; - previewScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene); + // open a new empty scene in place of the user's scene + EditorSceneManager.NewScene(NewSceneSetup.EmptyScene); // instantiate a game object to preview the avatar and a game object for the height reference prefab at 0, 0, 0 UnityEngine.Object heightReferenceResource = AssetDatabase.LoadAssetAtPath(HEIGHT_REFERENCE_PREFAB, typeof(UnityEngine.Object)); @@ -1259,13 +1266,8 @@ class AvatarExporter : MonoBehaviour { DestroyImmediate(avatarPreviewObject); DestroyImmediate(heightReferenceObject); - // re-open the scene the user had open before switching to the preview scene - if (!string.IsNullOrEmpty(previousScene)) { - EditorSceneManager.OpenScene(previousScene); - } - - // close the preview scene and flag it to be removed - EditorSceneManager.CloseScene(previewScene, true); + // restore to the previous scene setup that the user had open before exporting + EditorSceneManager.RestoreSceneManagerSetup(previousSceneSetup); // restore the camera pivot and rotation to the user's previous scene settings var sceneView = SceneView.lastActiveSceneView; diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab index 3a6b6b21fa..4464617387 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/HeightReference.prefab @@ -599,7 +599,7 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 4294967295 m_Materials: - - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + - {fileID: 2100000, guid: 83f430ba33ffd544bab7a13796246d30, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -774,7 +774,7 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 4294967295 m_Materials: - - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + - {fileID: 2100000, guid: 83f430ba33ffd544bab7a13796246d30, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -809,7 +809,7 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 4294967295 m_Materials: - - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + - {fileID: 2100000, guid: 83f430ba33ffd544bab7a13796246d30, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -844,7 +844,7 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 4294967295 m_Materials: - - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + - {fileID: 2100000, guid: 83f430ba33ffd544bab7a13796246d30, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -879,7 +879,7 @@ MeshRenderer: m_ReflectionProbeUsage: 1 m_RenderingLayerMask: 4294967295 m_Materials: - - {fileID: 2100000, guid: d1133891b03286946b3b0c63c1a57d08, type: 2} + - {fileID: 2100000, guid: 83f430ba33ffd544bab7a13796246d30, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 diff --git a/tools/unity-avatar-exporter/Assets/README.txt b/tools/unity-avatar-exporter/Assets/README.txt index 5b228ebf75..0b5cb49117 100644 --- a/tools/unity-avatar-exporter/Assets/README.txt +++ b/tools/unity-avatar-exporter/Assets/README.txt @@ -1,8 +1,8 @@ High Fidelity, Inc. Avatar Exporter -Version 0.3.6 +Version 0.3.7 -Note: It is recommended to use Unity versions between 2017.4.17f1 and 2018.2.12f1 for this Avatar Exporter. +Note: It is recommended to use Unity versions between 2017.4.15f1 and 2018.2.12f1 for this Avatar Exporter. To create a new avatar project: 1. Import your .fbx avatar model into your Unity project's Assets by either dragging and dropping the file into the Assets window or by using Assets menu > Import New Assets. diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index 05ad49baa6bfa1696cb12f659750246f2a6d2d9e..d906cfc0a45055b79a0ed8667441799fea682af3 100644 GIT binary patch literal 74615 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

* (function () { * Agent.setIsAvatar(true); @@ -76,9 +76,14 @@ public slots: void setIsAvatar(bool isAvatar) const { _agent->setIsAvatar(isAvatar); } /**jsdoc - * Checks whether or not the script is emulating an avatar. + * Checks whether the script is emulating an avatar. * @function Agent.isAvatar - * @returns {boolean} true if the script is acting as if an avatar, otherwise false. + * @returns {boolean} true if the script is emulating an avatar, otherwise false. + * @example + * (function () { + * print("Agent is avatar: " + Agent.isAvatar()); + * print("Agent is avatar: " + Agent.isAvatar); // Same result. + * }()); */ bool isAvatar() const { return _agent->isAvatar(); } @@ -86,7 +91,15 @@ public slots: * Plays a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless * isAvatar == true. * @function Agent.playAvatarSound - * @param {SoundObject} avatarSound - The sound to play. + * @param {SoundObject} avatarSound - The sound played. + * @example + * (function () { + * Agent.isAvatar = true; + * var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav"); + * Script.setTimeout(function () { // Give the sound time to load. + * Agent.playAvatarSound(sound); + * }, 1000); + * }()); */ void playAvatarSound(SharedSoundPointer avatarSound) const { _agent->playAvatarSound(avatarSound); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 37e82947ae..fbe5675bd8 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -20,7 +20,7 @@ /**jsdoc * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the - * {@link MyAvatar} API. To enable this API, set {@link Agent|Agent.isAvatatr} to true. + * {@link MyAvatar} API. To enable this API, set {@link Agent|Agent.isAvatar} to true. * *

For Interface, client entity, and avatar scripts, see {@link MyAvatar}.

* @@ -30,13 +30,13 @@ * * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. * @property {Vec3} position - The position of the avatar. - * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 - * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. + * @property {number} scale=1.0 - The scale of the avatar. The value can be set to anything between 0.005 and + * 1000.0. When the scale value is fetched, it may temporarily be further limited by the domain's settings. * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in * the application of physics. Read-only. * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar * but is otherwise not used or changed by Interface. - * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * @property {number} bodyYaw - The left or right rotation about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". @@ -57,13 +57,12 @@ * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting * into the domain. * @property {string} displayName - The avatar's display name. - * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the - * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the - * time. - * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's - * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. - * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. - * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * @property {string} sessionDisplayName - displayName's sanitized and default version defined by the avatar mixer + * rather than Interface clients. The result is unique among all avatars present in the domain at the time. + * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's + * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The avatar's FST file. + * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
* Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. @@ -75,6 +74,12 @@ * avatar. Read-only. * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. + * + * @example
+ * (function () { + * Agent.setIsAvatar(true); + * print("Position: " + JSON.stringify(Avatar.position)); // 0, 0, 0 + * }()); */ class ScriptableAvatar : public AvatarData, public Dependency { @@ -90,14 +95,14 @@ public: /**jsdoc * Starts playing an animation on the avatar. * @function Avatar.startAnimation - * @param {string} url - The URL to the animation file. Animation files need to be .FBX format but only need to contain + * @param {string} url - The animation file's URL. Animation files need to be in the FBX format but only need to contain * the avatar skeleton and animation data. * @param {number} [fps=30] - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. * @param {number} [priority=1] - Not used. * @param {boolean} [loop=false] - true if the animation should loop, false if it shouldn't. * @param {boolean} [hold=false] - Not used. - * @param {number} [firstFrame=0] - The frame the animation should start at. - * @param {number} [lastFrame=3.403e+38] - The frame the animation should stop at. + * @param {number} [firstFrame=0] - The frame at which the animation starts. + * @param {number} [lastFrame=3.403e+38] - The frame at which the animation stops. * @param {string[]} [maskedJoints=[]] - The names of joints that should not be animated. */ /// Allows scripts to run animations. @@ -115,6 +120,9 @@ public: * Gets the details of the current avatar animation that is being or was recently played. * @function Avatar.getAnimationDetails * @returns {Avatar.AnimationDetails} The current or recent avatar animation. + * @example + * var animationDetails = Avatar.getAnimationDetails(); + * print("Animation details: " + JSON.stringify(animationDetails)); */ Q_INVOKABLE AnimationDetails getAnimationDetails(); @@ -146,18 +154,21 @@ public: bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } /**jsdoc - * Gets the avatar entities as binary data. - *

Warning: Potentially a very expensive call. Do not use if possible.

+ * Gets details of all avatar entities. + *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.getAvatarEntityData - * @returns {AvatarEntityMap} The avatar entities as binary data. + * @returns {AvatarEntityMap} Details of the avatar entities. + * @example
+ * var avatarEntityData = Avatar.getAvatarEntityData(); + * print("Avatar entities: " + JSON.stringify(avatarEntityData)); */ Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override; /**jsdoc - * Sets the avatar entities from binary data. + * Sets all avatar entities from an object. *

Warning: Potentially an expensive call. Do not use if possible.

* @function Avatar.setAvatarEntityData - * @param {AvatarEntityMap} avatarEntityData - The avatar entities as binary data. + * @param {AvatarEntityMap} avatarEntityData - Details of the avatar entities. */ Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e0e9b5b648..12c260ccde 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3542,8 +3542,8 @@ void MyAvatar::clearScaleRestriction() { /**jsdoc * A teleport target. * @typedef {object} MyAvatar.GoToProperties - * @property {Vec3} position - The new position for the avatar, in world coordinates. - * @property {Quat} [orientation] - The new orientation for the avatar. + * @property {Vec3} position - The avatar's new position. + * @property {Quat} [orientation] - The avatar's new orientation. */ void MyAvatar::goToLocation(const QVariant& propertiesVar) { qCDebug(interfaceapp, "MyAvatar QML goToLocation"); @@ -3902,8 +3902,7 @@ void MyAvatar::setCollisionWithOtherAvatarsFlags() { } /**jsdoc - * A collision capsule is a cylinder with hemispherical ends. It is used, in particular, to approximate the extents of an - * avatar. + * A collision capsule is a cylinder with hemispherical ends. It is often used to approximate the extents of an avatar. * @typedef {object} MyAvatar.CollisionCapsule * @property {Vec3} start - The bottom end of the cylinder, excluding the bottom hemisphere. * @property {Vec3} end - The top end of the cylinder, excluding the top hemisphere. @@ -5361,12 +5360,12 @@ void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr& otherAvatar) /**jsdoc * Physics options to use in the flow simulation of a joint. * @typedef {object} MyAvatar.FlowPhysicsOptions - * @property {boolean} [active=true] - true to enable flow on the joint, false if it isn't., - * @property {number} [radius=0.01] - The thickness of segments and knots. (Needed for collisions.) + * @property {boolean} [active=true] - true to enable flow on the joint, otherwise false. + * @property {number} [radius=0.01] - The thickness of segments and knots (needed for collisions). * @property {number} [gravity=-0.0096] - Y-value of the gravity vector. * @property {number} [inertia=0.8] - Rotational inertia multiplier. * @property {number} [damping=0.85] - The amount of damping on joint oscillation. - * @property {number} [stiffness=0.0] - How stiff each thread is. + * @property {number} [stiffness=0.0] - The stiffness of each thread. * @property {number} [delta=0.55] - Delta time for every integration step. */ /**jsdoc @@ -5454,18 +5453,18 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys * that has been configured. * @property {Object} collisions - The collisions configuration for each joint that * has collisions configured. - * @property {Object} threads - The threads hat have been configured, with the name of the first joint as - * the ThreadName and an array of the indexes of all the joints in the thread as the value. + * @property {Object} threads - The threads that have been configured, with the first joint's name as the + * ThreadName and value as an array of the indexes of all the joints in the thread. */ /**jsdoc * A set of physics options currently used in flow simulation. * @typedef {object} MyAvatar.FlowPhysicsData - * @property {boolean} active - true to enable flow on the joint, false if it isn't., + * @property {boolean} active - true to enable flow on the joint, otherwise false. * @property {number} radius - The thickness of segments and knots. (Needed for collisions.) * @property {number} gravity - Y-value of the gravity vector. * @property {number} inertia - Rotational inertia multiplier. * @property {number} damping - The amount of damping on joint oscillation. - * @property {number} stiffness - How stiff each thread is. + * @property {number} stiffness - The stiffness of each thread. * @property {number} delta - Delta time for every integration step. * @property {number[]} jointIndices - The indexes of the joints the options are applied to. */ diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8951bc7fed..3159348871 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -72,13 +72,13 @@ class MyAvatar : public Avatar { * * @comment IMPORTANT: This group of properties is copied from AvatarData.h; they should NOT be edited here. * @property {Vec3} position - The position of the avatar. - * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 - * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. + * @property {number} scale=1.0 - The scale of the avatar. The value can be set to anything between 0.005 and + * 1000.0. When the scale value is fetched, it may temporarily be further limited by the domain's settings. * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in * the application of physics. Read-only. * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar * but is otherwise not used or changed by Interface. - * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * @property {number} bodyYaw - The left or right rotation about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". @@ -99,13 +99,12 @@ class MyAvatar : public Avatar { * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting * into the domain. * @property {string} displayName - The avatar's display name. - * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the - * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the - * time. - * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's - * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. - * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. - * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * @property {string} sessionDisplayName - displayName's sanitized and default version defined by the avatar + * mixer rather than Interface clients. The result is unique among all avatars present in the domain at the time. + * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's + * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The avatar's FST file. + * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
* Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. @@ -149,12 +148,12 @@ class MyAvatar : public Avatar { * property value is audioListenerModeCustom. * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the * audioListenerMode property value is audioListenerModeCustom. - * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true.
+ * @property {boolean} hasScriptedBlendshapes=false - true to transmit blendshapes over the network.
* Note: Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead. - * @property {boolean} hasProceduralBlinkFaceMovement=true - If true then procedural blinking is turned on. - * @property {boolean} hasProceduralEyeFaceMovement=true - If true then procedural eye movement is turned on. - * @property {boolean} hasAudioEnabledFaceMovement=true - If true then voice audio will move the mouth - * blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. + * @property {boolean} hasProceduralBlinkFaceMovement=true - true if procedural blinking is turned on. + * @property {boolean} hasProceduralEyeFaceMovement=true - true if procedural eye movement is turned on. + * @property {boolean} hasAudioEnabledFaceMovement=true - true to move the mouth blendshapes with voice audio + * when MyAvatar.hasScriptedBlendshapes is enabled. * @property {number} rotationRecenterFilterLength - Configures how quickly the avatar root rotates to recenter its facing * direction to match that of the user's torso based on head and hands orientation. A smaller value makes the * recentering happen more quickly. The minimum value is 0.01. @@ -178,23 +177,23 @@ class MyAvatar : public Avatar { * the palm, in avatar coordinates. If the hand isn't being positioned by a controller, the value is * {@link Vec3(0)|Vec3.ZERO}. Read-only. * - * @property {Pose} leftHandPose - The pose of the left hand as determined by the hand controllers, relative to the avatar. + * @property {Pose} leftHandPose - The left hand's pose as determined by the hand controllers, relative to the avatar. * Read-only. - * @property {Pose} rightHandPose - The pose right hand position as determined by the hand controllers, relative to the - * avatar. Read-only. - * @property {Pose} leftHandTipPose - The pose of the left hand as determined by the hand controllers, relative to the - * avatar, with the position adjusted to be 0.3m along the direction of the palm. Read-only. - * @property {Pose} rightHandTipPose - The pose of the right hand as determined by the hand controllers, relative to the - * avatar, with the position adjusted by 0.3m along the direction of the palm. Read-only. + * @property {Pose} rightHandPose - The right hand's pose as determined by the hand controllers, relative to the avatar. + * Read-only. + * @property {Pose} leftHandTipPose - The left hand's pose as determined by the hand controllers, relative to the avatar, + * with the position adjusted by 0.3m along the direction of the palm. Read-only. + * @property {Pose} rightHandTipPose - The right hand's pose as determined by the hand controllers, relative to the avatar, + * with the position adjusted by 0.3m along the direction of the palm. Read-only. * * @property {number} energy - Deprecated: This property will be removed from the API. * @property {boolean} isAway - true if your avatar is away (i.e., inactive), false if it is * active. * - * @property {boolean} centerOfGravityModelEnabled=true - If true then the avatar hips are placed according to - * the center of gravity model that balances the center of gravity over the base of support of the feet. Setting the - * value false results in the default behavior where the hips are positioned under the head. - * @property {boolean} hmdLeanRecenterEnabled=true - If true then the avatar is re-centered to be under the + * @property {boolean} centerOfGravityModelEnabled=true - true if the avatar hips are placed according to + * the center of gravity model that balances the center of gravity over the base of support of the feet. Set the + * value to false for default behavior where the hips are positioned under the head. + * @property {boolean} hmdLeanRecenterEnabled=true - true IF the avatar is re-centered to be under the * head's position. In room-scale VR, this behavior is what causes your avatar to follow your HMD as you walk around * the room. Setting the value false is useful if you want to pin the avatar to a fixed position. * @property {boolean} collisionsEnabled - Set to true to enable the avatar to collide with the environment, @@ -227,9 +226,9 @@ class MyAvatar : public Avatar { * where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated. * Read-only. * - * @property {number} walkSpeed - Adjusts the walk speed of your avatar. - * @property {number} walkBackwardSpeed - Adjusts the walk backward speed of your avatar. - * @property {number} sprintSpeed - Adjusts the sprint speed of your avatar. + * @property {number} walkSpeed - The walk speed of your avatar. + * @property {number} walkBackwardSpeed - The walk backward speed of your avatar. + * @property {number} sprintSpeed - The sprint speed of your avatar. * @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior. * @property {number} isInSittingState - true if your avatar is sitting (avatar leaning is disabled, * recenntering is enabled), false if it is standing (avatar leaning is enabled, and avatar recenters if it @@ -237,8 +236,8 @@ class MyAvatar : public Avatar { * user sits or stands, unless isSitStandStateLocked == true. Setting the property value overrides the * current siting / standing state, which is updated when the user next sits or stands unless * isSitStandStateLocked == true. - * @property {boolean} isSitStandStateLocked - true locks the avatar sitting / standing state, i.e., disables - * automatically changing it based on the user sitting or standing. + * @property {boolean} isSitStandStateLocked - true to lock the avatar sitting/standing state, i.e., use this + * to disable automatically changing state. * @property {boolean} allowTeleporting - true if teleporting is enabled in the Interface settings, * false if it isn't. Read-only. * @@ -396,7 +395,7 @@ public: *
- * + * * * * function animStateHandler(dictionary) { * print("Anim state dictionary: " + JSON.stringify(dictionary)); @@ -715,7 +715,7 @@ public: /**jsdoc - * Gets whether or not you do snap turns in HMD mode. + * Gets whether you do snap turns in HMD mode. * @function MyAvatar.getSnapTurn * @returns {boolean} true if you do snap turns in HMD mode; false if you do smooth turns in HMD * mode. @@ -761,7 +761,7 @@ public: Q_INVOKABLE QString getHmdAvatarAlignmentType() const; /**jsdoc - * Sets whether the avatar hips are balanced over the feet or positioned under the head. + * Sets whether the avatar's hips are balanced over the feet or positioned under the head. * @function MyAvatar.setCenterOfGravityModelEnabled * @param {boolean} enabled - true to balance the hips over the feet, false to position the hips * under the head. @@ -777,7 +777,7 @@ public: Q_INVOKABLE bool getCenterOfGravityModelEnabled() const { return _centerOfGravityModelEnabled; } /**jsdoc - * Sets whether or not the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering + * Sets whether the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering * causes your avatar to follow your HMD as you walk around the room. Disabling recentering is useful if you want to pin * the avatar to a fixed position. * @function MyAvatar.setHMDLeanRecenterEnabled @@ -787,7 +787,7 @@ public: Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } /**jsdoc - * Gets whether or not the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering + * Gets whether the avatar's position updates to recenter the avatar under the head. In room-scale VR, recentering * causes your avatar to follow your HMD as you walk around the room. * @function MyAvatar.getHMDLeanRecenterEnabled * @returns {boolean} true if recentering is enabled, false if not. @@ -864,7 +864,7 @@ public: float getDriveKey(DriveKeys key) const; /**jsdoc - * Gets the value of a drive key, regardless of whether or not it is disabled. + * Gets the value of a drive key, regardless of whether it is disabled. * @function MyAvatar.getRawDriveKey * @param {MyAvatar.DriveKeys} key - The drive key. * @returns {number} The value of the drive key. @@ -889,14 +889,15 @@ public: Q_INVOKABLE void disableDriveKey(DriveKeys key); /**jsdoc - * Enables the acction associated with a drive key. + * Enables the action associated with a drive key. The action may have been disabled with + * {@link MyAvatar.disableDriveKey|disableDriveKey}. * @function MyAvatar.enableDriveKey * @param {MyAvatar.DriveKeys} key - The drive key to enable. */ Q_INVOKABLE void enableDriveKey(DriveKeys key); /**jsdoc - * Checks whether or not a drive key is disabled. + * Checks whether a drive key is disabled. * @function MyAvatar.isDriveKeyDisabled * @param {DriveKeys} key - The drive key to check. * @returns {boolean} true if the drive key is disabled, false if it isn't. @@ -925,7 +926,7 @@ public: Q_INVOKABLE void triggerRotationRecenter(); /**jsdoc - * Gets whether or not the avatar is configured to keep its center of gravity under its head. + * Gets whether the avatar is configured to keep its center of gravity under its head. * @function MyAvatar.isRecenteringHorizontally * @returns {boolean} true if the avatar is keeping its center of gravity under its head position, * false if not. @@ -967,8 +968,8 @@ public: Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); } /**jsdoc - * If a face tracker is connected and being used, gets the estimated pitch of the user's head scaled such that the avatar - * looks at the edge of the view frustum when the user looks at the edge of their screen. + * If a face tracker is connected and being used, gets the estimated pitch of the user's head scaled. This is scale such + * that the avatar looks at the edge of the view frustum when the user looks at the edge of their screen. * @function MyAvatar.getHeadDeltaPitch * @returns {number} The pitch that the avatar's head should be if a face tracker is connected and being used, otherwise * 0, in degrees. @@ -1080,8 +1081,8 @@ public: /**jsdoc * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's left hand, relative to the avatar, as * positioned by a hand controller (e.g., Oculus Touch or Vive), and translated 0.3m along the palm. - *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints - * for hand animation.) If you are using the Leap Motion, the return value's valid property will be + *

Note: Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints + * for hand animation.) If you are using Leap Motion, the return value's valid property will be * false and any pose values returned will not be meaningful.

* @function MyAvatar.getLeftHandTipPose * @returns {Pose} The pose of the avatar's left hand, relative to the avatar, as positioned by a hand controller, and @@ -1092,8 +1093,8 @@ public: /**jsdoc * Gets the pose (position, rotation, velocity, and angular velocity) of the avatar's right hand, relative to the avatar, as * positioned by a hand controller (e.g., Oculus Touch or Vive), and translated 0.3m along the palm. - *

Note: The Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints - * for hand animation.) If you are using the Leap Motion, the return value's valid property will be + *

Note: Leap Motion isn't part of the hand controller input system. (Instead, it manipulates the avatar's joints + * for hand animation.) If you are using Leap Motion, the return value's valid property will be * false and any pose values returned will not be meaningful.

* @function MyAvatar.getRightHandTipPose * @returns {Pose} The pose of the avatar's right hand, relative to the avatar, as positioned by a hand controller, and @@ -1247,14 +1248,14 @@ public: void clearWornAvatarEntities(); /**jsdoc - * Checks whether your avatar is flying or not. + * Checks whether your avatar is flying. * @function MyAvatar.isFlying * @returns {boolean} true if your avatar is flying and not taking off or falling, false if not. */ Q_INVOKABLE bool isFlying(); /**jsdoc - * Checks whether your avatar is in the air or not. + * Checks whether your avatar is in the air. * @function MyAvatar.isInAir * @returns {boolean} true if your avatar is taking off, flying, or falling, otherwise false * because your avatar is on the ground. @@ -1332,9 +1333,9 @@ public: Q_INVOKABLE void setAvatarScale(float scale); /**jsdoc - * Sets whether or not the avatar should collide with entities. + * Sets whether the avatar should collide with entities. *

Note: A false value won't disable collisions if the avatar is in a zone that disallows - * collisionless avatars, however the false value will be set so that collisions are disabled as soon as the + * collisionless avatars. However, the false value will be set so that collisions are disabled as soon as the * avatar moves to a position where collisionless avatars are allowed. * @function MyAvatar.setCollisionsEnabled * @param {boolean} enabled - true to enable the avatar to collide with entities, false to @@ -1343,7 +1344,7 @@ public: Q_INVOKABLE void setCollisionsEnabled(bool enabled); /**jsdoc - * Gets whether or not the avatar will currently collide with entities. + * Gets whether the avatar will currently collide with entities. *

Note: The avatar will always collide with entities if in a zone that disallows collisionless avatars. * @function MyAvatar.getCollisionsEnabled * @returns {boolean} true if the avatar will currently collide with entities, false if it won't. @@ -1351,7 +1352,7 @@ public: Q_INVOKABLE bool getCollisionsEnabled(); /**jsdoc - * Sets whether or not the avatar should collide with other avatars. + * Sets whether the avatar should collide with other avatars. * @function MyAvatar.setOtherAvatarsCollisionsEnabled * @param {boolean} enabled - true to enable the avatar to collide with other avatars, false * to disable. @@ -1359,7 +1360,7 @@ public: Q_INVOKABLE void setOtherAvatarsCollisionsEnabled(bool enabled); /**jsdoc - * Gets whether or not the avatar will collide with other avatars. + * Gets whether the avatar will collide with other avatars. * @function MyAvatar.getOtherAvatarsCollisionsEnabled * @returns {boolean} true if the avatar will collide with other avatars, false if it won't. */ @@ -1395,6 +1396,10 @@ public: * @function MyAvatar.getAbsoluteJointRotationInObjectFrame * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to the avatar. + * @example

+ * var headIndex = MyAvatar.getJointIndex("Head"); + * var headRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(headIndex); + * print("Head rotation: " + JSON.stringify(Quat.safeEulerAngles(headRotation))); // Degrees */ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; @@ -1404,6 +1409,10 @@ public: * @function MyAvatar.getAbsoluteJointTranslationInObjectFrame * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to the avatar. + * @example + * var headIndex = MyAvatar.getJointIndex("Head"); + * var headTranslation = MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex); + * print("Head translation: " + JSON.stringify(headTranslation)); */ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; @@ -1500,36 +1509,56 @@ public: void prepareAvatarEntityDataForReload(); /**jsdoc - * Creates a new grab, that grabs an entity. + * Creates a new grab that grabs an entity. * @function MyAvatar.grab * @param {Uuid} targetID - The ID of the entity to grab. * @param {number} parentJointIndex - The avatar joint to use to grab the entity. - * @param {Vec3} offset - The target's local positional relative to the joint. + * @param {Vec3} offset - The target's local position relative to the joint. * @param {Quat} rotationalOffset - The target's local rotation relative to the joint. * @returns {Uuid} The ID of the new grab. + * @example + * var entityPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })); + * var entityRotation = MyAvatar.orientation; + * var entityID = Entities.addEntity({ + * type: "Box", + * position: entityPosition, + * rotation: entityRotation, + * dimensions: { x: 0.5, y: 0.5, z: 0.5 } + * }); + * var rightHandJoint = MyAvatar.getJointIndex("RightHand"); + * var relativePosition = Entities.worldToLocalPosition(entityPosition, MyAvatar.SELF_ID, rightHandJoint); + * var relativeRotation = Entities.worldToLocalRotation(entityRotation, MyAvatar.SELF_ID, rightHandJoint); + * var grabID = MyAvatar.grab(entityID, rightHandJoint, relativePosition, relativeRotation); + * + * Script.setTimeout(function () { + * MyAvatar.releaseGrab(grabID); + * Entities.deleteEntity(entityID); + * }, 10000); */ Q_INVOKABLE const QUuid grab(const QUuid& targetID, int parentJointIndex, glm::vec3 positionalOffset, glm::quat rotationalOffset); /**jsdoc - * Releases (deletes) a grab, to stop grabbing an entity. + * Releases (deletes) a grab to stop grabbing an entity. * @function MyAvatar.releaseGrab * @param {Uuid} grabID - The ID of the grab to release. */ Q_INVOKABLE void releaseGrab(const QUuid& grabID); /**jsdoc - * Gets the avatar entities as binary data. + * Gets details of all avatar entities. * @function MyAvatar.getAvatarEntityData - * @override - * @returns {AvatarEntityMap} The avatar entities as binary data. + * @returns {AvatarEntityMap} Details of the avatar entities. + * @example + * var avatarEntityData = MyAvatar.getAvatarEntityData(); + * print("Avatar entities: " + JSON.stringify(avatarEntityData)); */ AvatarEntityMap getAvatarEntityData() const override; /**jsdoc - * Sets the avatar entities from binary data. + * Sets all avatar entities from an object. * @function MyAvatar.setAvatarEntityData - * @param {AvatarEntityMap} avatarEntityData - The avatar entities as binary data. + * @param {AvatarEntityMap} avatarEntityData - Details of the avatar entities. */ void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override; @@ -1549,7 +1578,7 @@ public: /**jsdoc * Enables and disables flow simulation of physics on the avatar's hair, clothes, and body parts. See - * {@link https://docs.highfidelity.com/create/avatars/create-avatars/add-flow.html|Add Flow to Your Avatar} for more + * {@link https://docs.highfidelity.com/create/avatars/add-flow.html|Add Flow to Your Avatar} for more * information. * @function MyAvatar.useFlow * @param {boolean} isActive - true if flow simulation is enabled on the joint, false if it isn't. @@ -1585,7 +1614,7 @@ public slots: * MyAvatar.resetSize(); * * for (var i = 0; i < 5; i++){ - * print ("Growing by 5 percent"); + * print("Growing by 5 percent"); * MyAvatar.increaseSize(); * } */ @@ -1598,7 +1627,7 @@ public slots: * MyAvatar.resetSize(); * * for (var i = 0; i < 5; i++){ - * print ("Shrinking by 5 percent"); + * print("Shrinking by 5 percent"); * MyAvatar.decreaseSize(); * } */ @@ -1695,7 +1724,7 @@ public slots: /**jsdoc - * Adds a thrust to your avatar's current thrust, to be applied for a short while. + * Adds a thrust to your avatar's current thrust to be applied for a short while. * @function MyAvatar.addThrust * @param {Vec3} thrust - The thrust direction and magnitude. * @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead. @@ -1735,7 +1764,9 @@ public slots: void setToggleHips(bool followHead); /**jsdoc - * Displays base of support of feet debug graphics. + * Displays the base of support area debug graphics if in HMD mode. If your head goes outside this area your avatar's hips + * are moved to counterbalance your avatar, and if your head moves too far then your avatar's position is moved (i.e., a + * step happens). * @function MyAvatar.setEnableDebugDrawBaseOfSupport * @param {boolean} enabled - true to show the debug graphics, false to hide. */ @@ -1805,7 +1836,7 @@ public slots: void setEnableDebugDrawDetailedCollision(bool isEnabled); /**jsdoc - * Gets whether or not your avatar mesh is visible. + * Gets whether your avatar mesh is visible. * @function MyAvatar.getEnableMeshVisible * @returns {boolean} true if your avatar's mesh is visible, otherwise false. */ @@ -1830,7 +1861,7 @@ public slots: void sanitizeAvatarEntityProperties(EntityItemProperties& properties) const; /**jsdoc - * Sets whether or not your avatar mesh is visible to you. + * Sets whether your avatar mesh is visible to you. * @function MyAvatar.setEnableMeshVisible * @param {boolean} enabled - true to show your avatar mesh, false to hide. * @example @@ -1842,7 +1873,7 @@ public slots: virtual void setEnableMeshVisible(bool isEnabled) override; /**jsdoc - * Sets whether or not inverse kinematics (IK) for your avatar. + * Sets whether inverse kinematics (IK) is enabled for your avatar. * @function MyAvatar.setEnableInverseKinematics * @param {boolean} enabled - true to enable IK, false to disable. */ @@ -1854,7 +1885,8 @@ public slots: *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphOverrideUrl - * @returns {string} The URL of the override animation graph. "" if there is no override animation graph. + * @returns {string} The URL of the override animation graph JSON file. "" if there is no override animation + * graph. */ QUrl getAnimGraphOverrideUrl() const; // thread-safe @@ -1863,25 +1895,27 @@ public slots: *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphOverrideUrl - * @param {string} url - The URL of the animation graph to use. Set to "" to clear an override. + * @param {string} url - The URL of the animation graph JSON file to use. Set to "" to clear an override. */ void setAnimGraphOverrideUrl(QUrl value); // thread-safe /**jsdoc - * Gets the URL of animation graph that's currently being used for avatar animations. + * Gets the URL of animation graph (i.e., the avatar animation JSON) that's currently being used for avatar animations. *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.getAnimGraphUrl - * @returns {string} The URL of the current animation graph. + * @returns {string} The URL of the current animation graph JSON file. + * @Example
+ * print(MyAvatar.getAnimGraphUrl()); */ QUrl getAnimGraphUrl() const; // thread-safe /**jsdoc - * Sets the current animation graph to use for avatar animations and makes it the default. + * Sets the current animation graph (i.e., the avatar animation JSON) to use for avatar animations and makes it the default. *

See {@link https://docs.highfidelity.com/create/avatars/custom-animations.html|Custom Avatar Animations} for * information on animation graphs.

* @function MyAvatar.setAnimGraphUrl - * @param {string} url - The URL of the animation graph to use. + * @param {string} url - The URL of the animation graph JSON file to use. */ void setAnimGraphUrl(const QUrl& url); // thread-safe @@ -1963,11 +1997,14 @@ signals: void otherAvatarsCollisionsEnabledChanged(bool enabled); /**jsdoc - * Triggered when the avatar's animation graph URL changes. + * Triggered when the avatar's animation graph being used changes. * @function MyAvatar.animGraphUrlChanged - * @param {string} url - The URL of the new animation graph. + * @param {string} url - The URL of the new animation graph JSON file. * @returns {Signal} - */ + * @example
+ * MyAvatar.animGraphUrlChanged.connect(function (url) { + * print("Avatar animation JSON changed to: " + url); + * }); */ void animGraphUrlChanged(const QUrl& url); /**jsdoc diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index d5a110ea76..6d58ecedec 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -66,24 +66,23 @@ public: * * * - * - * + * + * * - * * diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 623672143e..1ad4e100db 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -34,17 +34,21 @@ public: * * * - * - * - * - * + * + * * * * - * - * + * + * * * * diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index cec440eb1a..87e6af5bb3 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -203,7 +203,7 @@ public: /**jsdoc * Gets the default rotation of a joint (in the current avatar) relative to its parent. *

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointRotation * @param {number} index - The joint index. * @returns {Quat} The default rotation of the joint if the joint index is valid, otherwise {@link Quat(0)|Quat.IDENTITY}. @@ -214,7 +214,7 @@ public: * Gets the default translation of a joint (in the current avatar) relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.getDefaultJointTranslation * @param {number} index - The joint index. * @returns {Vec3} The default translation of the joint (in model coordinates) if the joint index is valid, otherwise @@ -223,22 +223,30 @@ public: Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const; /**jsdoc - * Provides read-only access to the default joint rotations in avatar coordinates. + * Gets the default joint rotations in avatar coordinates. * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame * @param index {number} - The joint index. - * @returns {Quat} The rotation of this joint in avatar coordinates. + * @returns {Quat} The default rotation of the joint in avatar coordinates. + * @example
+ * var headIndex = MyAvatar.getJointIndex("Head"); + * var defaultHeadRotation = MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(headIndex); + * print("Default head rotation: " + JSON.stringify(Quat.safeEulerAngles(defaultHeadRotation))); // Degrees */ Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const; /**jsdoc - * Provides read-only access to the default joint translations in avatar coordinates. + * Gets the default joint translations in avatar coordinates. * The default pose of the avatar is defined by the position and orientation of all bones * in the avatar's model file. Typically this is a T-pose. * @function MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame * @param index {number} - The joint index. - * @returns {Vec3} The position of this joint in avatar coordinates. + * @returns {Vec3} The default position of the joint in avatar coordinates. + * @example + * var headIndex = MyAvatar.getJointIndex("Head"); + * var defaultHeadTranslation = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(headIndex); + * print("Default head translation: " + JSON.stringify(defaultHeadTranslation)); */ Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const; @@ -459,7 +467,7 @@ public: Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); } /**jsdoc - * sets the joint of the entity or avatar that the avatar is parented to. + * Sets the joint of the entity or avatar that the avatar is parented to. * @function MyAvatar.setParentJointIndex * @param {number} parentJointIndex - he joint of the entity or avatar that the avatar should be parented to. Use * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 317d9ec903..39dfaa8a1a 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2802,7 +2802,7 @@ glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const /**jsdoc * Information on an attachment worn by the avatar. * @typedef {object} AttachmentData - * @property {string} modelUrl - The URL of the model file. Models can be .FBX or .OBJ format. + * @property {string} modelUrl - The URL of the model file. Models can be FBX or OBJ format. * @property {string} jointName - The offset to apply to the model relative to the joint position. * @property {Vec3} translation - The offset from the joint that the attachment is positioned at. * @property {Vec3} rotation - The rotation applied to the model relative to the joint orientation. @@ -3015,6 +3015,10 @@ float AvatarData::_avatarSortCoefficientSize { 8.0f }; float AvatarData::_avatarSortCoefficientCenter { 0.25f }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; +/**jsdoc + * An object with the UUIDs of avatar entities as keys and avatar entity properties objects as values. + * @typedef {Object.} AvatarEntityMap + */ QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue obj = engine->newObject(); for (auto entityID : value.keys()) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e2d24465cb..00e7e67923 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -61,10 +61,6 @@ using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; -/**jsdoc - * An object with the UUIDs of avatar entities as keys and binary blobs, being the entity properties, as values. - * @typedef {Object.>} AvatarEntityMap - */ using AvatarEntityMap = QMap; using PackedAvatarEntityMap = QMap; // similar to AvatarEntityMap, but different internal format using AvatarEntityIDs = QSet; @@ -439,13 +435,13 @@ class AvatarData : public QObject, public SpatiallyNestable { // IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h and ScriptableAvatar.h. /* * @property {Vec3} position - The position of the avatar. - * @property {number} scale=1.0 - The scale of the avatar. When setting, the value is limited to between 0.005 - * and 1000.0. When getting, the value may temporarily be further limited by the domain's settings. - * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in + * @property {number} scale=1.0 - The scale of the avatar. The value can be set to anything between 0.005 and + * 1000.0. When the scale value is fetched, it may temporarily be further limited by the domain's settings. + * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in * the application of physics. Read-only. - * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar * but is otherwise not used or changed by Interface. - * @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the avatar. + * @property {number} bodyYaw - The left or right rotation about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". @@ -461,28 +457,27 @@ class AvatarData : public QObject, public SpatiallyNestable { * sometimes called "bank". * @property {Vec3} velocity - The current velocity of the avatar. * @property {Vec3} angularVelocity - The current angular velocity of the avatar. - * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the * domain. - * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting * into the domain. * @property {string} displayName - The avatar's display name. - * @property {string} sessionDisplayName - Sanitized, defaulted version of displayName that is defined by the - * avatar mixer rather than by Interface clients. The result is unique among all avatars present in the domain at the - * time. - * @property {boolean} lookAtSnappingEnabled=true - If true, the avatar's eyes snap to look at another avatar's - * eyes if generally in the line of sight and the other avatar also has lookAtSnappingEnabled == true. - * @property {string} skeletonModelURL - The URL of the avatar model's .fst file. - * @property {AttachmentData[]} attachmentData - Information on the attachments worn by the avatar.
+ * @property {string} sessionDisplayName - displayName's sanitized and default version defined by the avatar + * mixer rather than Interface clients. The result is unique among all avatars present in the domain at the time. + * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's + * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. + * @property {string} skeletonModelURL - The avatar's FST file. + * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
* Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. - * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the * avatar's size, orientation, and position in the virtual world. Read-only. - * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the * avatar. Read-only. - * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the * avatar. Read-only. - * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's + * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) @@ -785,7 +780,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -796,7 +791,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -897,7 +892,7 @@ public: Q_INVOKABLE virtual void clearJointData(const QString& name); /**jsdoc - * Checks that the data for a joint are valid. + * Checks if the data for a joint are valid. * @function Avatar.isJointDataValid * @param {string} name - The name of the joint. * @returns {boolean} true if the joint data are valid, false if not. @@ -906,7 +901,7 @@ public: /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see - * Avatar Standards. + * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. @@ -921,7 +916,7 @@ public: * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see - * Avatar Standards.

+ * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. @@ -1062,8 +1057,13 @@ public: * your avatar's face, use {@link Avatar.setForceFaceTrackerConnected} or {@link MyAvatar.setForceFaceTrackerConnected}. * @function Avatar.setBlendshape * @param {string} name - The name of the blendshape, per the - * {@link https://docs.highfidelity.com/create/avatars/create-avatars/avatar-standards.html#blendshapes Avatar Standards}. + * {@link https://docs.highfidelity.com/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. * @param {number} value - A value between 0.0 and 1.0. + * @example
+ * MyAvatar.setForceFaceTrackerConnected(true); + * MyAvatar.setBlendshape("JawOpen", 1.0); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } @@ -1170,7 +1170,7 @@ public: * @example * var attachments = MyAvatar.getaAttachmentData(); * for (var i = 0; i < attachments.length; i++) { - * print (attachments[i].modelURL); + * print(attachments[i].modelURL); * } * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". @@ -1318,6 +1318,14 @@ public: * @function Avatar.getSensorToWorldMatrix * @returns {Mat4} The scale, rotation, and translation transform from the user's real world to the avatar's size, * orientation, and position in the virtual world. + * @example + * var sensorToWorldMatrix = MyAvatar.getSensorToWorldMatrix(); + * print("Sensor to woprld matrix: " + JSON.stringify(sensorToWorldMatrix)); + * print("Rotation: " + JSON.stringify(Mat4.extractRotation(sensorToWorldMatrix))); + * print("Translation: " + JSON.stringify(Mat4.extractTranslation(sensorToWorldMatrix))); + * print("Scale: " + JSON.stringify(Mat4.extractScale(sensorToWorldMatrix))); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; @@ -1335,6 +1343,14 @@ public: * Gets the rotation and translation of the left hand controller relative to the avatar. * @function Avatar.getControllerLeftHandMatrix * @returns {Mat4} The rotation and translation of the left hand controller relative to the avatar. + * @example + * var leftHandMatrix = MyAvatar.getControllerLeftHandMatrix(); + * print("Controller left hand matrix: " + JSON.stringify(leftHandMatrix)); + * print("Rotation: " + JSON.stringify(Mat4.extractRotation(leftHandMatrix))); + * print("Translation: " + JSON.stringify(Mat4.extractTranslation(leftHandMatrix))); + * print("Scale: " + JSON.stringify(Mat4.extractScale(leftHandMatrix))); // Always 1,1,1. + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; @@ -1409,6 +1425,12 @@ signals: * Triggered when the avatar's displayName property value changes. * @function Avatar.displayNameChanged * @returns {Signal} + * @example + * MyAvatar.displayNameChanged.connect(function () { + * print("Avatar display name changed to: " + MyAvatar.displayName); + * }); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void displayNameChanged(); @@ -1416,6 +1438,12 @@ signals: * Triggered when the avatar's sessionDisplayName property value changes. * @function Avatar.sessionDisplayNameChanged * @returns {Signal} + * @example + * MyAvatar.sessionDisplayNameChanged.connect(function () { + * print("Avatar session display name changed to: " + MyAvatar.sessionDisplayName); + * }); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void sessionDisplayNameChanged(); @@ -1423,6 +1451,12 @@ signals: * Triggered when the avatar's model (i.e., skeletonModelURL property value) is changed. * @function Avatar.skeletonModelURLChanged * @returns {Signal} + * @example + * MyAvatar.skeletonModelURLChanged.connect(function () { + * print("Skeleton model changed to: " + MyAvatar.skeletonModelURL); + * }); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void skeletonModelURLChanged(); @@ -1431,6 +1465,12 @@ signals: * @function Avatar.lookAtSnappingChanged * @param {boolean} enabled - true if look-at snapping is enabled, false if not. * @returns {Signal} + * @example + * MyAvatar.lookAtSnappingChanged.connect(function () { + * print("Avatar look-at snapping changed to: " + MyAvatar.lookAtSnappingEnabled); + * }); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void lookAtSnappingChanged(bool enabled); @@ -1438,6 +1478,12 @@ signals: * Triggered when the avatar's sessionUUID property value changes. * @function Avatar.sessionUUIDChanged * @returns {Signal} + * @example + * MyAvatar.sessionUUIDChanged.connect(function () { + * print("Avatar session UUID changed to: " + MyAvatar.sessionUUID); + * }); + * + * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void sessionUUIDChanged(); From 839a03ebe6daa68be3ca5c34155fc3e8109a57d0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 26 Mar 2019 09:41:07 +1300 Subject: [PATCH 86/88] Miscellaneous JSDoc fixes noticed in passing --- interface/src/ui/overlays/Overlays.cpp | 22 +++++++++++----------- libraries/shared/src/shared/Camera.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index eec6eddf44..2ade5edda5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -1876,7 +1876,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as position. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of objects in the world, but behind the HUD. * @property {boolean} drawHUDLayer=false - If true, the overlay is rendered in front of everything, including the HUD. @@ -1916,7 +1916,7 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as position. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a - * parentID set, otherwise the same value as rotation. Synonym: localOrientation. + * parentID set, otherwise the same value as rotation. Synonym: localOrientation. * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. @@ -1927,11 +1927,11 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * parentID is an avatar skeleton. A value of 65535 means "no joint". * - * @property {number} startAt = 0 - The counter - clockwise angle from the overlay's x-axis that drawing starts at, in degrees. - * @property {number} endAt = 360 - The counter - clockwise angle from the overlay's x-axis that drawing ends at, in degrees. - * @property {number} outerRadius = 1 - The outer radius of the overlay, in meters.Synonym: radius. - * @property {number} innerRadius = 0 - The inner radius of the overlay, in meters. - * @property {Color} color = 255, 255, 255 - The color of the overlay.Setting this value also sets the values of + * @property {number} startAt = 0 - The counter - clockwise angle from the overlay's x-axis that drawing starts at in degrees. + * @property {number} endAt = 360 - The counter - clockwise angle from the overlay's x-axis that drawing ends at in degrees. + * @property {number} outerRadius = 1 - The outer radius of the overlay in meters. Synonym: radius. + * @property {number} innerRadius = 0 - The inner radius of the overlay in meters. + * @property {Color} color = 255, 255, 255 - The color of the overlay. Setting this value also sets the values of * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. * Write - only. @@ -1945,9 +1945,9 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {Color} innerEndColor - The color at the inner end point of the overlay. * @property {Color} outerStartColor - The color at the outer start point of the overlay. * @property {Color} outerEndColor - The color at the outer end point of the overlay. - * @property {number} alpha = 0.5 - The opacity of the overlay, 0.0 -1.0.Setting this value also sets + * @property {number} alpha = 0.5 - The opacity of the overlay, 0.0 -1.0. Setting this value also sets * the values of innerStartAlpha, innerEndAlpha, outerStartAlpha, and - * outerEndAlpha.Synonym: Alpha; write - only. + * outerEndAlpha. Synonym: Alpha; write - only. * @property {number} startAlpha - Sets the values of innerStartAlpha and outerStartAlpha. * Write - only. * @property {number} endAlpha - Sets the values of innerEndAlpha and outerEndAlpha. @@ -1964,9 +1964,9 @@ QVector Overlays::findOverlays(const glm::vec3& center, float radius) { * @property {boolean} hasTickMarks = false - If true, tick marks are drawn. * @property {number} majorTickMarksAngle = 0 - The angle between major tick marks, in degrees. * @property {number} minorTickMarksAngle = 0 - The angle between minor tick marks, in degrees. - * @property {number} majorTickMarksLength = 0 - The length of the major tick marks, in meters.A positive value draws tick marks + * @property {number} majorTickMarksLength = 0 - The length of the major tick marks, in meters. A positive value draws tick marks * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. - * @property {number} minorTickMarksLength = 0 - The length of the minor tick marks, in meters.A positive value draws tick marks + * @property {number} minorTickMarksLength = 0 - The length of the minor tick marks, in meters. A positive value draws tick marks * outwards from the inner radius; a negative value draws tick marks inwards from the outer radius. * @property {Color} majorTickMarksColor = 0, 0, 0 - The color of the major tick marks. * @property {Color} minorTickMarksColor = 0, 0, 0 - The color of the minor tick marks. diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 31e6228bb9..0132e58d18 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -138,7 +138,7 @@ public slots: * var pickRay = Camera.computePickRay(event.x, event.y); * var intersection = Entities.findRayIntersection(pickRay); * if (intersection.intersects) { - * print ("You clicked on entity " + intersection.entityID); + * print("You clicked on entity " + intersection.entityID); * } * } * From 4a832be8c65efb9bc5504a802702427bed58c34b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 26 Mar 2019 10:03:30 +1300 Subject: [PATCH 87/88] Fix JSDoc post-merge --- interface/src/avatar/MyAvatar.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f95ea898ba..edb686a6a6 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1609,8 +1609,7 @@ public: public slots: /**jsdoc - * @function MyAvatar.setSessionUUID - * @param {Uuid} sessionUUID + * @comment Uses the base class's JSDoc. */ virtual void setSessionUUID(const QUuid& sessionUUID) override; From 0173c87695f60b5430d205e6b703348f6d356091 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 22 Mar 2019 02:43:06 +0100 Subject: [PATCH 88/88] Fix clashing hyperlinks in AvatarPackager project page --- interface/resources/qml/hifi/avatarPackager/AvatarProject.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml index bf8c06d1b3..e5bffa7829 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml @@ -339,8 +339,8 @@ Item { visible: AvatarPackagerCore.currentAvatarProject && AvatarPackagerCore.currentAvatarProject.hasErrors anchors { - top: notForSaleMessage.bottom - topMargin: 16 + top: notForSaleMessage.visible ? notForSaleMessage.bottom : infoMessage .bottom + bottom: showFilesText.top horizontalCenter: parent.horizontalCenter }
NameType

Description
leftHandPoleVector{@link Vec3}The direction the elbow should point in rig * coordinates.
leftHandIKEnabledbooleantrue if IK is enabled for the left - * hand.
rightHandIKEnabledbooleantrue if IK is enabled for the right - * hand.
rightHandPosition{@link Vec3}The desired position of the RightHand * joint in rig coordinates.
rightHandRotation{@link Quat}The desired orientation of the @@ -214,8 +210,6 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig *
rightFootPoleVector{@link Vec3}The direction the knee should face in rig * coordinates.
splineIKEnabledbooleantrue if IK is enabled for the spline.
isTalkingbooleantrue if the avatar is talking.
notIsTalkingbooleantrue if the avatar is not talking.
- - + + From 77ea47a9dbbb49c626e3ccae79fbcd34645fffd1 Mon Sep 17 00:00:00 2001 From: David Back Date: Fri, 22 Mar 2019 11:21:39 -0700 Subject: [PATCH 80/88] fix no name material override, hide auto scale text after sliding --- .../Editor/AvatarExporter/AvatarExporter.cs | 21 +++++++++++------- tools/unity-avatar-exporter/Assets/README.txt | 2 +- .../avatarExporter.unitypackage | Bin 74615 -> 74729 bytes 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs index 11d83a52e8..4e06772f4b 100644 --- a/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs +++ b/tools/unity-avatar-exporter/Assets/Editor/AvatarExporter/AvatarExporter.cs @@ -17,7 +17,7 @@ using System.Text.RegularExpressions; class AvatarExporter : MonoBehaviour { // update version number for every PR that changes this file, also set updated version in README file - static readonly string AVATAR_EXPORTER_VERSION = "0.3.7"; + static readonly string AVATAR_EXPORTER_VERSION = "0.4.0"; static readonly float HIPS_GROUND_MIN_Y = 0.01f; static readonly float HIPS_SPINE_CHEST_MIN_SEPARATION = 0.001f; @@ -697,11 +697,10 @@ class AvatarExporter : MonoBehaviour { if (materialDatas.Count > 0) { string materialJson = "{ "; foreach (var materialData in materialDatas) { - // if this is the only material in the mapping and it is the default name No Name mapped to No Name, + // if this is the only material in the mapping and it is mapped to default material name No Name, // then the avatar has no embedded materials and this material should be applied to all meshes string materialName = materialData.Key; - if (materialMappings.Count == 1 && materialName == DEFAULT_MATERIAL_NAME && - materialMappings[materialName] == DEFAULT_MATERIAL_NAME) { + if (materialMappings.Count == 1 && materialName == DEFAULT_MATERIAL_NAME) { materialJson += "\"all\": "; } else { materialJson += "\"mat::" + materialName + "\": "; @@ -1300,6 +1299,7 @@ class ExportProjectWindow : EditorWindow { const float DEFAULT_AVATAR_HEIGHT = 1.755f; const float MAXIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; const float MINIMUM_RECOMMENDED_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f; + const float SLIDER_DIFFERENCE_REMOVE_TEXT = 0.01f; readonly Color COLOR_YELLOW = Color.yellow; //new Color(0.9176f, 0.8274f, 0.0f); readonly Color COLOR_BACKGROUND = new Color(0.5f, 0.5f, 0.5f); @@ -1314,6 +1314,7 @@ class ExportProjectWindow : EditorWindow { Vector2 warningScrollPosition = new Vector2(0, 0); string scaleWarningText = ""; float sliderScale = 0.30103f; + float originalSliderScale; public delegate void OnExportDelegate(string projectDirectory, string projectName, float scale); OnExportDelegate onExportCallback; @@ -1344,6 +1345,8 @@ class ExportProjectWindow : EditorWindow { SetAvatarScale(newScale); scaleWarningText = "Avatar's scale automatically adjusted to be within the recommended range."; } + + originalSliderScale = sliderScale; } void OnGUI() { @@ -1460,10 +1463,9 @@ class ExportProjectWindow : EditorWindow { Close(); } - // when any value changes check for any errors and update scale warning if we are not exporting + // When a text field changes check for any errors if we didn't just check errors from clicking Export above if (GUI.changed && !export) { CheckForErrors(false); - UpdateScaleWarning(); } } @@ -1521,13 +1523,14 @@ class ExportProjectWindow : EditorWindow { } void UpdateScaleWarning() { - // called on any input changes + // called on any scale changes float height = GetAvatarHeight(); if (height < MINIMUM_RECOMMENDED_HEIGHT) { scaleWarningText = "The height of the avatar is below the recommended minimum."; } else if (height > MAXIMUM_RECOMMENDED_HEIGHT) { scaleWarningText = "The height of the avatar is above the recommended maximum."; - } else { + } else if (Mathf.Abs(originalSliderScale - sliderScale) > SLIDER_DIFFERENCE_REMOVE_TEXT) { + // once moving slider beyond a small threshold, remove the automatically scaled text scaleWarningText = ""; } } @@ -1555,6 +1558,8 @@ class ExportProjectWindow : EditorWindow { // adjust slider scale value to match the new actual scale value sliderScale = GetSliderScaleFromActualScale(actualScale); + + UpdateScaleWarning(); } float GetSliderScaleFromActualScale(float actualScale) { diff --git a/tools/unity-avatar-exporter/Assets/README.txt b/tools/unity-avatar-exporter/Assets/README.txt index 0b5cb49117..410314d8b4 100644 --- a/tools/unity-avatar-exporter/Assets/README.txt +++ b/tools/unity-avatar-exporter/Assets/README.txt @@ -1,6 +1,6 @@ High Fidelity, Inc. Avatar Exporter -Version 0.3.7 +Version 0.4.0 Note: It is recommended to use Unity versions between 2017.4.15f1 and 2018.2.12f1 for this Avatar Exporter. diff --git a/tools/unity-avatar-exporter/avatarExporter.unitypackage b/tools/unity-avatar-exporter/avatarExporter.unitypackage index d906cfc0a45055b79a0ed8667441799fea682af3..328972736bea1c62716589eb2c0cad5171001f2d 100644 GIT binary patch delta 74262 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%0DP)&)c5gO^Ga*>!vx;Z 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

    jV;9GJz8I}aM(Zh=9i<)~Fn3Taj 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ફ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 From 55b3b5034a0dad81c70dc499033ac478fc3d6a4a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 22 Mar 2019 15:47:13 -0700 Subject: [PATCH 81/88] always update OwingAvatarID of AvatarEntities --- interface/src/avatar/MyAvatar.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 298e661f24..4ea0d37710 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3454,11 +3454,6 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { QUuid oldSessionID = getSessionUUID(); Avatar::setSessionUUID(sessionUUID); QUuid newSessionID = getSessionUUID(); - if (DependencyManager::get()->getSessionUUID().isNull()) { - // we don't actually have a connection to a domain right now - // so there is no need to queue AvatarEntity messages --> bail early - return; - } if (newSessionID != oldSessionID) { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; @@ -3467,6 +3462,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); + bool sendPackets = DependencyManager::get()->getSessionUUID().isNull(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { @@ -3474,12 +3470,14 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { if (!entity) { continue; } + // update OwningAvatarID so entity can be identified as "ours" later entity->setOwningAvatarID(newSessionID); - // NOTE: each attached AvatarEntity should already have the correct updated parentID - // via magic in SpatiallyNestable, but when an AvatarEntity IS parented to MyAvatar - // we need to update the "packedAvatarEntityData" we send to the avatar-mixer - // so that others will get the updated state. - if (entity->getParentID() == newSessionID) { + // NOTE: each attached AvatarEntity already have the correct updated parentID + // via magic in SpatiallyNestable, hence we check agains newSessionID + if (sendPackets && entity->getParentID() == newSessionID) { + // but when we have a real session and the AvatarEntity is parented to MyAvatar + // we need to update the "packedAvatarEntityData" sent to the avatar-mixer + // because it contains a stale parentID somewhere deep inside packetSender->queueEditAvatarEntityMessage(entityTree, entityID); } } From 7db498a1308d9147613ae8b1be79845d0647cb67 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 22 Mar 2019 16:20:54 -0700 Subject: [PATCH 82/88] fix shadows when tablet is open and misc threading issues --- .../src/RenderableMaterialEntityItem.cpp | 6 +++++- .../src/RenderableShapeEntityItem.cpp | 18 ++++++++++++++---- .../src/RenderableTextEntityItem.cpp | 8 +++----- libraries/render-utils/src/GeometryCache.h | 11 ++++++----- .../render-utils/src/RenderCommonTask.cpp | 6 +++++- .../render-utils/src/RenderDeferredTask.cpp | 8 ++++---- .../render-utils/src/RenderDeferredTask.h | 10 +++++----- .../render-utils/src/RenderForwardTask.cpp | 10 +++++++--- libraries/render-utils/src/RenderForwardTask.h | 3 ++- .../src/simple_transparent_textured.slf | 4 ++-- 10 files changed, 53 insertions(+), 31 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 2eb877b0e1..15842336f5 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -186,7 +186,11 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (_networkMaterial->isLoaded()) { onMaterialRequestFinished(!_networkMaterial->isFailed()); } else { - connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished); + connect(_networkMaterial.data(), &Resource::finished, this, [&](bool success) { + withWriteLock([&] { + onMaterialRequestFinished(success); + }); + }); } } } else if (materialDataChanged && usingMaterialData) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b33eb619c8..28b0bb8f23 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -249,10 +249,14 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { graphics::MultiMaterial materials; auto geometryCache = DependencyManager::get(); GeometryCache::Shape geometryShape; + PrimitiveMode primitiveMode; + RenderLayer renderLayer; bool proceduralRender = false; glm::vec4 outColor; withReadLock([&] { geometryShape = geometryCache->getShapeForEntityShape(_shape); + primitiveMode = _primitiveMode; + renderLayer = _renderLayer; batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation materials = _materials["0"]; auto& schema = materials.getSchemaBuffer().get(); @@ -267,7 +271,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { }); if (proceduralRender) { - if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { + if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { geometryCache->renderWireShape(batch, geometryShape, outColor); } else { geometryCache->renderShape(batch, geometryShape, outColor); @@ -275,10 +279,16 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else if (!useMaterialPipeline(materials)) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) { - geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, args->_shapePipeline); + render::ShapePipelinePointer pipeline; + if (renderLayer == RenderLayer::WORLD) { + pipeline = GeometryCache::getShapePipeline(false, outColor.a < 1.0f, true, false); } else { - geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, args->_shapePipeline); + pipeline = GeometryCache::getShapePipeline(false, outColor.a < 1.0f, true, false, false, true); + } + if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { + geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline); + } else { + geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a3e1a2f56d..81f367a956 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -162,10 +162,12 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::vec4 backgroundColor; Transform modelTransform; glm::vec3 dimensions; + BillboardMode billboardMode; bool layered; withReadLock([&] { modelTransform = _renderTransform; dimensions = _dimensions; + billboardMode = _billboardMode; float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; textColor = glm::vec4(_textColor, fadeRatio * _textAlpha); @@ -190,7 +192,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { } auto transformToTopLeft = modelTransform; - transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode, args->getViewFrustum().getPosition())); + transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), billboardMode, args->getViewFrustum().getPosition())); transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed @@ -210,10 +212,6 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin)); _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, layered); } - - if (layered) { - DependencyManager::get()->unsetKeyLightBatch(batch); - } } QSizeF TextEntityRenderer::textSize(const QString& text) const { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index cd3454bf38..e84f2e25a4 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -368,6 +368,12 @@ public: const ShapeData * getShapeData(Shape shape) const; graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); + + static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, + bool unlit = false, bool depthBias = false, bool forward = false); + static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, + bool unlit = false, bool depthBias = false); + private: GeometryCache(); @@ -471,11 +477,6 @@ private: gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline; gpu::ShaderPointer _simpleTransparentWebBrowserShader; gpu::PipelinePointer _simpleTransparentWebBrowserPipeline; - - static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool forward = false); - static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false); }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 385e384efe..b1a62625b2 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -95,7 +95,11 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& // Setup lighting model for all items; batch.setUniformBuffer(ru::Buffer::LightModel, lightingModel->getParametersBuffer()); - renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + if (_opaquePass) { + renderStateSortShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + } else { + renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + } args->_batch = nullptr; }); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 089d267711..ea2b05a6fa 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -216,8 +216,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawHazeDeferred", drawHazeInputs); // Render transparent objects forward in LightingBuffer - const auto transparentsInputs = DrawDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); - task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); + const auto transparentsInputs = RenderTransparentDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); + task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); const auto outlineRangeTimer = task.addJob("BeginHighlightRangeTimer", "Highlight"); @@ -436,7 +436,7 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input } -void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { +void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -453,7 +453,7 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& RenderArgs* args = renderContext->args; - gpu::doInBatch("DrawDeferred::run", args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("RenderTransparentDeferred::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; // Setup camera, projection and viewport for all items diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 0a188ec3a6..3eb1153928 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -19,7 +19,7 @@ #include "LightClusters.h" #include "RenderShadowTask.h" -class DrawDeferredConfig : public render::Job::Config { +class RenderTransparentDeferredConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY newStats) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) @@ -41,13 +41,13 @@ protected: int _numDrawn{ 0 }; }; -class DrawDeferred { +class RenderTransparentDeferred { public: using Inputs = render::VaryingSet7; - using Config = DrawDeferredConfig; - using JobModel = render::Job::ModelI; + using Config = RenderTransparentDeferredConfig; + using JobModel = render::Job::ModelI; - DrawDeferred(render::ShapePlumberPointer shapePlumber) + RenderTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void configure(const Config& config) { _maxDrawn = config.maxDrawn; } diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 73692b41c2..0bc117bdb9 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -98,7 +98,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Draw opaques forward const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); - task.addJob("DrawOpaques", opaqueInputs, shapePlumber); + task.addJob("DrawOpaques", opaqueInputs, shapePlumber, true); // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame).asVarying(); @@ -106,7 +106,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Draw transparent objects forward const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying(); - task.addJob("DrawTransparents", transparentInputs, shapePlumber); + task.addJob("DrawTransparents", transparentInputs, shapePlumber, false); // Layered const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); @@ -261,7 +261,11 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i args->_globalShapeKey = globalKey._flags.to_ulong(); // Render items - renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + if (_opaquePass) { + renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + } else { + renderShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + } args->_batch = nullptr; args->_globalShapeKey = 0; diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 85b51ad5fa..40d004ddb2 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -76,12 +76,13 @@ public: using Inputs = render::VaryingSet2; using JobModel = render::Job::ModelI; - DrawForward(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} + DrawForward(const render::ShapePlumberPointer& shapePlumber, bool opaquePass) : _shapePlumber(shapePlumber), _opaquePass(opaquePass) {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: render::ShapePlumberPointer _shapePlumber; + bool _opaquePass; }; #endif // hifi_RenderForwardTask_h diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index f1bb2b1ea2..9f8a88c7c2 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -16,8 +16,8 @@ <@include gpu/Color.slh@> <@include render-utils/ShaderConstants.h@> -<@include ForwardGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<@include DeferredGlobalLight.slh@> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> From 57a02bc1d57584de4de914ffc8c960bbe653658a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 25 Mar 2019 11:29:25 -0700 Subject: [PATCH 83/88] capture lambda by value --- .../entities-renderer/src/RenderableMaterialEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 15842336f5..b474fb2266 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -169,7 +169,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (urlChanged && !usingMaterialData) { _networkMaterial = MaterialCache::instance().getMaterial(_materialURL); - auto onMaterialRequestFinished = [&, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { + auto onMaterialRequestFinished = [this, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { if (success) { deleteMaterial(oldParentID, oldParentMaterialName); _texturesLoaded = false; @@ -186,7 +186,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (_networkMaterial->isLoaded()) { onMaterialRequestFinished(!_networkMaterial->isFailed()); } else { - connect(_networkMaterial.data(), &Resource::finished, this, [&](bool success) { + connect(_networkMaterial.data(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) { withWriteLock([&] { onMaterialRequestFinished(success); }); From cd2dbbb955012431b92886756ca11d0d6d6e91d4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 25 Mar 2019 11:52:52 -0700 Subject: [PATCH 84/88] fix logic typo --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4ea0d37710..b204588774 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3462,7 +3462,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { _avatarEntitiesLock.withReadLock([&] { avatarEntityIDs = _packedAvatarEntityData.keys(); }); - bool sendPackets = DependencyManager::get()->getSessionUUID().isNull(); + bool sendPackets = !DependencyManager::get()->getSessionUUID().isNull(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); entityTree->withWriteLock([&] { for (const auto& entityID : avatarEntityIDs) { @@ -3473,7 +3473,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) { // update OwningAvatarID so entity can be identified as "ours" later entity->setOwningAvatarID(newSessionID); // NOTE: each attached AvatarEntity already have the correct updated parentID - // via magic in SpatiallyNestable, hence we check agains newSessionID + // via magic in SpatiallyNestable, hence we check against newSessionID if (sendPackets && entity->getParentID() == newSessionID) { // but when we have a real session and the AvatarEntity is parented to MyAvatar // we need to update the "packedAvatarEntityData" sent to the avatar-mixer From b78ae80ae621fc657f6882faa8ff8e84e6f282a9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 26 Mar 2019 09:40:47 +1300 Subject: [PATCH 85/88] Address review comments and add further examples --- .../src/AgentScriptingInterface.h | 33 ++- .../src/avatars/ScriptableAvatar.h | 49 ++-- interface/src/avatar/MyAvatar.cpp | 21 +- interface/src/avatar/MyAvatar.h | 225 ++++++++++-------- .../animation/src/AnimInverseKinematics.h | 23 +- libraries/animation/src/AnimOverlay.h | 16 +- .../src/avatars-renderer/Avatar.h | 22 +- libraries/avatars/src/AvatarData.cpp | 6 +- libraries/avatars/src/AvatarData.h | 104 +++++--- 9 files changed, 310 insertions(+), 189 deletions(-) diff --git a/assignment-client/src/AgentScriptingInterface.h b/assignment-client/src/AgentScriptingInterface.h index 1242634dd5..b1a8aaff96 100644 --- a/assignment-client/src/AgentScriptingInterface.h +++ b/assignment-client/src/AgentScriptingInterface.h @@ -18,8 +18,8 @@ #include "Agent.h" /**jsdoc - * The Agent API enables an assignment client to emulate an avatar. In particular, setting isAvatar = - * true connects the assignment client to the avatar and audio mixers and enables the {@link Avatar} API to be used. + * The Agent API enables an assignment client to emulate an avatar. Setting isAvatar = true connects + * the assignment client to the avatar and audio mixers, and enables the {@link Avatar} API to be used. * * @namespace Agent * @@ -29,13 +29,13 @@ * false. * @property {boolean} isPlayingAvatarSound - true if the script has a sound to play, otherwise false. * Sounds are played when isAvatar is true, from the position and with the orientation of the - * scripted avatar's head.Read-only. + * scripted avatar's head. Read-only. * @property {boolean} isListeningToAudioStream - true if the agent is "listening" to the audio stream from the * domain, otherwise false. * @property {boolean} isNoiseGateEnabled - true if the noise gate is enabled, otherwise false. When * enabled, the input audio stream is blocked (fully attenuated) if it falls below an adaptive threshold. - * @property {number} lastReceivedAudioLoudness - The current loudness of the audio input, nominal range 0.0 (no - * sound) – 1.0 (the onset of clipping). Read-only. + * @property {number} lastReceivedAudioLoudness - The current loudness of the audio input. Nominal range [0.0 (no + * sound) – 1.0 (the onset of clipping)]. Read-only. * @property {Uuid} sessionUUID - The unique ID associated with the agent's current session in the domain. Read-only. */ class AgentScriptingInterface : public QObject { @@ -63,9 +63,9 @@ public: public slots: /**jsdoc - * Sets whether or not the script should emulate an avatar. + * Sets whether the script should emulate an avatar. * @function Agent.setIsAvatar - * @param {boolean} isAvatar - true if the script should act as if an avatar, otherwise false. + * @param {boolean} isAvatar - true if the script emulates an avatar, otherwise false. * @example
Make an assignment client script emulate an avatar.Check whether the agent is emulating an avatar.Play a sound from an emulated avatar.Create a scriptable avatar.Report the current animation details.Report the current avatar entities.
8PITCHRotate the user's avatar head and attached camera about its negative * x-axis (i.e., positive values pitch down) at a rate proportional to the control value, if the camera isn't in HMD, * independent, or mirror modes.
9ZOOMZooms the camera in or out.
9ZOOMZoom the camera in or out.
10DELTA_YAWRotate the user's avatar about its y-axis by an amount proportional * to the control value, if the camera isn't in independent or mirror modes.
11DELTA_PITCHRotate the user's avatar head and attached camera about its @@ -471,13 +470,14 @@ public: void setCollisionWithOtherAvatarsFlags() override; /**jsdoc - * Resets the sensor positioning of your HMD (if used) and recenters your avatar body and head. + * Resets the sensor positioning of your HMD (if in use) and recenters your avatar body and head. * @function MyAvatar.resetSensorsAndBody */ Q_INVOKABLE void resetSensorsAndBody(); /**jsdoc - * Moves and orients the avatar, such that it is directly underneath the HMD, with toes pointed forward. + * Moves and orients the avatar, such that it is directly underneath the HMD, with toes pointed forward in the direction of + * the HMD. * @function MyAvatar.centerBody */ Q_INVOKABLE void centerBody(); // thread-safe @@ -486,7 +486,7 @@ public: /**jsdoc * Clears inverse kinematics joint limit history. *

The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to - * forget this history, to prevent contorted joints.

+ * forget this history to prevent contorted joints, e.g., after finishing with an override animation.

* @function MyAvatar.clearIKJointLimitHistory */ Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe @@ -502,14 +502,14 @@ public: /**jsdoc * Gets the avatar orientation. Suitable for use in QML. * @function MyAvatar.setOrientationVar - * @param {object} newOrientationVar - The avatar orientation. + * @param {object} newOrientationVar - The avatar's orientation. */ Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); /**jsdoc * Gets the avatar orientation. Suitable for use in QML. * @function MyAvatar.getOrientationVar - * @returns {object} The avatar orientation. + * @returns {object} The avatar's orientation. */ Q_INVOKABLE QVariant getOrientationVar() const; @@ -558,9 +558,9 @@ public: *

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards.

+ * Avatar Standards.

* @function MyAvatar.overrideAnimation - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the + * @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the * avatar skeleton and animation data. * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. * @param loop {boolean} Set to true if the animation should loop. @@ -572,6 +572,7 @@ public: * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); * Script.setTimeout(function () { * MyAvatar.restoreAnimation(); + * MyAvatar.clearIKJointLimitHistory(); * }, 3000); */ Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); @@ -617,18 +618,18 @@ public: *

Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily * understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or * "walkFwd". To get the full list of roles, use {@ link MyAvatar.getAnimationRoles}. - * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how + * For each role, the avatar-animation.json defines when the animation is used, the animation clip (FBX) used, and how * animations are blended together with procedural data (such as look at vectors, hand sensors etc.). - * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation + * overrideRoleAnimation() is used to change the animation clip (FBX) associated with a specified animation * role. To end the role animation and restore the default, use {@link MyAvatar.restoreRoleAnimation}.

*

Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body.

*

Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see - * Avatar Standards. + * Avatar Standards. * @function MyAvatar.overrideRoleAnimation * @param role {string} The animation role to override - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. + * @param url {string} The URL to the animation file. Animation files need to be in FBX format, but only need to contain the avatar skeleton and animation data. * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. * @param loop {boolean} Set to true if the animation should loop * @param firstFrame {number} The frame the animation should start at @@ -653,9 +654,9 @@ public: *

Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily * understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or * "walkFwd". To get the full list of roles, use {@link MyAvatar.getAnimationRoles}. For each role, - * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are + * the avatar-animation.json defines when the animation is used, the animation clip (FBX) used, and how animations are * blended together with procedural data (such as look-at vectors, hand sensors etc.). You can change the animation clip - * (.FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}. + * (FBX) associated with a specified animation role using {@link MyAvatar.overrideRoleAnimation}. * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have * not specified an override animation for the specified role, this function has no effect. * @function MyAvatar.restoreRoleAnimation @@ -688,10 +689,9 @@ public: * @function MyAvatar.addAnimationStateHandler * @param {function} handler - The animation state handler function to add. * @param {Array|null} propertiesList - The list of {@link MyAvatar.AnimStateDictionary|AnimStateDictionary} - * properties that should be included the in parameter that the handler function is called with. If null + * properties that should be included in the parameter that the handler function is called with. If null * then all properties are included in the call parameter. - * @returns {number} The ID of the animation state handler function if successfully added, undefined if not - * successfully added. + * @returns {number} The ID of the animation state handler function if successfully added, undefined if not. * @example

Log all the animation state dictionary parameters for a short while.Report the rotation of your avatar's head joint relative to your avatar.Report the translation of your avatar's head joint relative to your avatar.Create and grab an entity for a short while.Report the current avatar entities.Make your avatar invisible for 10s.Report the current avatar animation JSON being used.Report when the current avatar animation JSON being used changes.
ValueName

Description
0RelaxToUnderPosesThis is a blend between PreviousSolution and - * UnderPoses: it is 15/16 PreviousSolution and 1/16 UnderPoses. This - * provides some of the benefits of using UnderPoses so that the underlying animation is still visible, - * while at the same time converging faster then using the UnderPoses only initial solution.
1RelaxToLimitCenterPosesThis is a blend between - * Previous Solution and LimitCenterPoses: it is 15/16 PreviousSolution and - * 1/16 LimitCenterPoses. This should converge quickly because it is close to the previous solution, but - * still provides the benefits of avoiding limb locking.
0RelaxToUnderPosesThis is a blend: it is 15/16 PreviousSolution + * and 1/16 UnderPoses. This provides some of the benefits of using UnderPoses so that the + * underlying animation is still visible, while at the same time converging faster then using the + * UnderPoses as the only initial solution.
1RelaxToLimitCenterPosesThis is a blend: it is 15/16 + * PreviousSolution and 1/16 LimitCenterPoses. This should converge quickly because it is + * close to the previous solution, but still provides the benefits of avoiding limb locking.
2PreviousSolutionThe IK system will begin to solve from the same position and * orientations for each joint that was the result from the previous frame.
- * Pros: because the end effectors typically do not move much from frame to frame, this is likely to converge quickly + * Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly * to a valid solution.
* Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be * awkward and uncomfortable. It can also result in locked elbows and knees.
3UnderPosesThe IK occurs at one of the top-most layers, it has access to the + *
3UnderPosesThe IK occurs at one of the top-most layers. It has access to the * full posture that was computed via canned animations and blends. We call this animated set of poses the "under * pose". The under poses are what would be visible if IK was completely disabled. Using the under poses as the - * initial conditions of the CCD solve will cause some of the animated motion to be blended in to the result of the + * initial conditions of the CCD solve will cause some of the animated motion to be blended into the result of the * IK. This can result in very natural results, especially if there are only a few IK targets enabled. On the other * hand, because the under poses might be quite far from the desired end effector, it can converge slowly in some * cases, causing it to never reach the IK target in the allotted number of iterations. Also, in situations where all @@ -93,7 +92,7 @@ public: * constraints. This can prevent the IK solution from getting locked or stuck at a particular constraint. For * example, if the arm is pointing straight outward from the body, as the end effector moves towards the body, at * some point the elbow should bend to accommodate. However, because the CCD solver is stuck at a local maximum, it - * will not rotate the elbow, unless the initial conditions already has the elbow bent, which is the case for + * will not rotate the elbow, unless the initial conditions already have the elbow bent, which is the case for * LimitCenterPoses. When all the IK targets are enabled, this result will provide a consistent starting * point for each IK solve, hopefully resulting in a consistent, natural result.
0FullBodyBoneSetAll joints.
1UpperBodyBoneSetOnly the "Spine" joint and its children.
2LowerBodyBoneSetOnly the leg joints and their children.
3LeftArmBoneSetJoints that are children of the "LeftShoulder" joint.
4RightArmBoneSetJoints that are children of the "RightShoulder" + *
3LeftArmBoneSetJoints that are the children of the "LeftShoulder" * joint.
5AboveTheHeadBoneSetJoints that are children of the "Head" joint.
6BelowTheHeadBoneSetJoints that are NOT children of the "head" + *
4RightArmBoneSetJoints that are the children of the "RightShoulder" + * joint.
5AboveTheHeadBoneSetJoints that are the children of the "Head" + * joint.
6BelowTheHeadBoneSetJoints that are NOT the children of the "head" * joint.
7HeadOnlyBoneSetThe "Head" joint.
8SpineOnlyBoneSetThe "Spine" joint.
9EmptyBoneSetNo joints.
10LeftHandBoneSetjoints that are children of the "LeftHand" joint.
11RightHandBoneSetJoints that are children of the "RightHand" joint.
10LeftHandBoneSetjoints that are the children of the "LeftHand" + * joint.
11RightHandBoneSetJoints that are the children of the "RightHand" + * joint.
12HipsOnlyBoneSetThe "Hips" joint.
13BothFeetBoneSetThe "LeftFoot" and "RightFoot" joints.
Report the default rotation of your avatar's head joint relative to your avatar.Report the default translation of your avatar's head joint relative to your avatar.Open your avatar's mouth wide.Report the URLs of all current attachments.Report the sensor to world matrix.Report the left hand controller matrix.Report when your avatar display name changes.Report when your avatar's session display name changes.Report when your avatar's skeleton model changes.Report when your look-at snapping setting changes.Report when your avatar's session UUID changes.