3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-12 08:18:26 +02:00

Merge branch 'master' of github.com:highfidelity/hifi into 21660-updateNitpickForQuest

This commit is contained in:
NissimHadar 2019-03-28 12:46:36 -07:00
commit 705e6432a7
74 changed files with 2978 additions and 1133 deletions
assignment-client/src
interface
libraries
scripts/system
commerce
controllers
marketplaces
tools

View file

@ -18,16 +18,25 @@
#include "Agent.h"
/**jsdoc
* The <code>Agent</code> API enables an assignment client to emulate an avatar. Setting <code>isAvatar = true</code> 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 <em>Read-only.</em>
* @property {boolean} isListeningToAudioStream
* @property {boolean} isNoiseGateEnabled
* @property {number} lastReceivedAudioLoudness <em>Read-only.</em>
* @property {Uuid} sessionUUID <em>Read-only.</em>
* @property {boolean} isAvatar - <code>true</code> if the assignment client script is emulating an avatar, otherwise
* <code>false</code>.
* @property {boolean} isPlayingAvatarSound - <code>true</code> if the script has a sound to play, otherwise <code>false</code>.
* Sounds are played when <code>isAvatar</code> is <code>true</code>, from the position and with the orientation of the
* scripted avatar's head. <em>Read-only.</em>
* @property {boolean} isListeningToAudioStream - <code>true</code> if the agent is "listening" to the audio stream from the
* domain, otherwise <code>false</code>.
* @property {boolean} isNoiseGateEnabled - <code>true</code> if the noise gate is enabled, otherwise <code>false</code>. 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 [<code>0.0</code> (no
* sound) &ndash; <code>1.0</code> (the onset of clipping)]. <em>Read-only.</em>
* @property {Uuid} sessionUUID - The unique ID associated with the agent's current session in the domain. <em>Read-only.</em>
*/
class AgentScriptingInterface : public QObject {
Q_OBJECT
@ -54,20 +63,43 @@ public:
public slots:
/**jsdoc
* Sets whether the script should emulate an avatar.
* @function Agent.setIsAvatar
* @param {boolean} isAvatar
* @param {boolean} isAvatar - <code>true</code> if the script emulates an avatar, otherwise <code>false</code>.
* @example <caption>Make an assignment client script emulate an avatar.</caption>
* (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
* Checks whether the script is emulating an avatar.
* @function Agent.isAvatar
* @returns {boolean}
* @returns {boolean} <code>true</code> if the script is emulating an avatar, otherwise <code>false</code>.
* @example <caption>Check whether the agent is emulating an avatar.</caption>
* (function () {
* print("Agent is avatar: " + Agent.isAvatar());
* print("Agent is avatar: " + Agent.isAvatar); // Same result.
* }());
*/
bool isAvatar() const { return _agent->isAvatar(); }
/**jsdoc
* Plays a sound from the position and with the orientation of the emulated avatar's head. No sound is played unless
* <code>isAvatar == true</code>.
* @function Agent.playAvatarSound
* @param {object} avatarSound
* @param {SoundObject} avatarSound - The sound played.
* @example <caption>Play a sound from an emulated avatar.</caption>
* (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); }

View file

@ -97,6 +97,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
PacketType::RadiusIgnoreRequest,
PacketType::RequestsDomainListData,
PacketType::PerAvatarGainSet,
PacketType::InjectorGainSet,
PacketType::AudioSoloRequest },
this, "queueAudioPacket");

View file

@ -92,6 +92,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) {
case PacketType::PerAvatarGainSet:
parsePerAvatarGainSet(*packet, node);
break;
case PacketType::InjectorGainSet:
parseInjectorGainSet(*packet, node);
break;
case PacketType::NodeIgnoreRequest:
parseNodeIgnoreRequest(packet, node);
break;
@ -197,14 +200,25 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const
if (avatarUUID.isNull()) {
// set the MASTER avatar gain
setMasterAvatarGain(gain);
qCDebug(audio) << "Setting MASTER avatar gain for " << uuid << " to " << gain;
qCDebug(audio) << "Setting MASTER avatar gain for" << uuid << "to" << gain;
} else {
// set the per-source avatar gain
setGainForAvatar(avatarUUID, gain);
qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to " << gain;
qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to" << gain;
}
}
void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node) {
QUuid uuid = node->getUUID();
uint8_t packedGain;
message.readPrimitive(&packedGain);
float gain = unpackFloatGainFromByte(packedGain);
setMasterInjectorGain(gain);
qCDebug(audio) << "Setting MASTER injector gain for" << uuid << "to" << gain;
}
void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) {
auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){
return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull();

View file

@ -63,6 +63,7 @@ public:
void negotiateAudioFormat(ReceivedMessage& message, const SharedNodePointer& node);
void parseRequestsDomainListData(ReceivedMessage& message);
void parsePerAvatarGainSet(ReceivedMessage& message, const SharedNodePointer& node);
void parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node);
void parseNodeIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseRadiusIgnoreRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
void parseSoloRequest(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& node);
@ -84,6 +85,8 @@ public:
float getMasterAvatarGain() const { return _masterAvatarGain; }
void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; }
float getMasterInjectorGain() const { return _masterInjectorGain; }
void setMasterInjectorGain(float gain) { _masterInjectorGain = gain; }
AudioLimiter audioLimiter;
@ -189,6 +192,7 @@ private:
int _frameToSendStats { 0 };
float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars
float _masterInjectorGain { 1.0f }; // per-listener mixing gain, applied only to injectors
CodecPluginPointer _codec;
QString _selectedCodecName;

View file

@ -50,7 +50,7 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData&
// mix helpers
inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd);
inline float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream,
inline float computeGain(float masterAvatarGain, float masterInjectorGain, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho);
inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition);
@ -338,8 +338,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
}
if (!isThrottling) {
updateHRTFParameters(stream, *listenerAudioStream,
listenerData->getMasterAvatarGain());
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
listenerData->getMasterInjectorGain());
}
return false;
});
@ -363,8 +363,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
}
if (!isThrottling) {
updateHRTFParameters(stream, *listenerAudioStream,
listenerData->getMasterAvatarGain());
updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
listenerData->getMasterInjectorGain());
}
return false;
});
@ -381,13 +381,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
stream.approximateVolume = approximateVolume(stream, listenerAudioStream);
} else {
if (shouldBeSkipped(stream, *listener, *listenerAudioStream, *listenerData)) {
addStream(stream, *listenerAudioStream, 0.0f, isSoloing);
addStream(stream, *listenerAudioStream, 0.0f, 0.0f, isSoloing);
streams.skipped.push_back(move(stream));
++stats.activeToSkipped;
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
isSoloing);
if (shouldBeInactive(stream)) {
@ -423,7 +423,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
return true;
}
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(),
addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(),
isSoloing);
if (shouldBeInactive(stream)) {
@ -491,7 +491,9 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain, bool isSoloing) {
float masterAvatarGain,
float masterInjectorGain,
bool isSoloing) {
++stats.totalMixes;
auto streamToAdd = mixableStream.positionalStream;
@ -504,9 +506,10 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
float distance = glm::max(glm::length(relativePosition), EPSILON);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
float gain = masterListenerGain;
float gain = masterAvatarGain;
if (!isSoloing) {
gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition,
distance, isEcho);
}
const int HRTF_DATASET_INDEX = 1;
@ -585,8 +588,9 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre
}
void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain) {
AvatarAudioStream& listeningNodeStream,
float masterAvatarGain,
float masterInjectorGain) {
auto streamToAdd = mixableStream.positionalStream;
// check if this is a server echo of a source back to itself
@ -595,7 +599,8 @@ void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream&
glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition();
float distance = glm::max(glm::length(relativePosition), EPSILON);
float gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho);
float gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition,
distance, isEcho);
float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition);
mixableStream.hrtf->setParameterHistory(azimuth, distance, gain);
@ -720,6 +725,7 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
// injector: apply attenuation
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
// injector: skip master gain
}
// avatar: skip attenuation - it is too costly to approximate
@ -729,16 +735,23 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi
float distance = glm::length(relativePosition);
return gain / distance;
// avatar: skip master gain - it is constant for all streams
// avatar: skip master gain
}
float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho) {
float computeGain(float masterAvatarGain,
float masterInjectorGain,
const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition,
float distance,
bool isEcho) {
float gain = 1.0f;
// injector: apply attenuation
if (streamToAdd.getType() == PositionalAudioStream::Injector) {
gain *= reinterpret_cast<const InjectedAudioStream*>(&streamToAdd)->getAttenuationRatio();
// apply master gain
gain *= masterInjectorGain;
// avatar: apply fixed off-axis attenuation to make them quieter as they turn away
} else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) {
@ -754,8 +767,8 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
gain *= offAxisCoefficient;
// apply master gain, only to avatars
gain *= masterListenerGain;
// apply master gain
gain *= masterAvatarGain;
}
auto& audioZones = AudioMixer::getAudioZones();
@ -797,8 +810,9 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
return gain;
}
float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition) {
float computeAzimuth(const AvatarAudioStream& listeningNodeStream,
const PositionalAudioStream& streamToAdd,
const glm::vec3& relativePosition) {
glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation());
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;

View file

@ -57,10 +57,13 @@ private:
bool prepareMix(const SharedNodePointer& listener);
void addStream(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain, bool isSoloing);
float masterAvatarGain,
float masterInjectorGain,
bool isSoloing);
void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream,
AvatarAudioStream& listeningNodeStream,
float masterListenerGain);
float masterAvatarGain,
float masterInjectorGain);
void resetHRTFState(AudioMixerClientData::MixableStream& mixableStream);
void addStreams(Node& listener, AudioMixerClientData& listenerData);

View file

@ -20,25 +20,29 @@
/**jsdoc
* The <code>Avatar</code> 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.isAvatar} to <code>true</code>.
*
* <p>For Interface, client entity, and avatar scripts, see {@link MyAvatar}.</p>
*
* <p><strong>Note:</strong> In the examples, use "<code>Avatar</code>" instead of "<code>MyAvatar</code>".</p>
*
* @namespace Avatar
*
* @hifi-assignment-client
*
* @property {Vec3} position
* @property {number} scale
* @property {number} density <em>Read-only.</em>
* @property {Vec3} handPosition
* @property {number} bodyYaw - The rotation left or right about an axis running from the head to the feet of the 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. The value can be set to anything between <code>0.005</code> and
* <code>1000.0</code>. 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/m<sup>3</sup>. The density is used to work out its mass in
* the application of physics. <em>Read-only.</em>
* @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 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".
* @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,79 +50,36 @@
* 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 - <code>displayName's</code> 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 - <code>true</code> 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 <code>lookAtSnappingEnabled == true</code>.
* @property {string} skeletonModelURL - The avatar's FST file.
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
* <strong>Deprecated:</strong> Use avatar entities instead.
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
* @property {Uuid} sessionUUID <em>Read-only.</em>
* @property {Mat4} sensorToWorldMatrix <em>Read-only.</em>
* @property {Mat4} controllerLeftHandMatrix <em>Read-only.</em>
* @property {Mat4} controllerRightHandMatrix <em>Read-only.</em>
* @property {number} sensorToWorldScale <em>Read-only.</em>
* @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. <em>Read-only.</em>
* @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. <em>Read-only.</em>
* @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the
* avatar. <em>Read-only.</em>
* @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the
* avatar. <em>Read-only.</em>
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. <em>Read-only.</em>
*
* @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
* @example <caption>Create a scriptable avatar.</caption>
* (function () {
* Agent.setIsAvatar(true);
* print("Position: " + JSON.stringify(Avatar.position)); // 0, 0, 0
* }());
*/
class ScriptableAvatar : public AvatarData, public Dependency {
@ -132,15 +93,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 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] - <em>Not used.</em>
* @param {boolean} [loop=false] - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
* @param {boolean} [hold=false] - <em>Not used.</em>
* @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.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
@ -148,39 +111,37 @@ 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.
* @example <caption>Report the current animation details.</caption>
* var animationDetails = Avatar.getAnimationDetails();
* print("Animation details: " + JSON.stringify(animationDetails));
*/
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 <caption>Report the names of all the joints in your current avatar.</caption>
* print(JSON.stringify(MyAvatar.getJointNames()));
*/
* @comment Uses the base class's JSDoc.
*/
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 <caption>Report the index of your avatar's left arm joint.</caption>
* print(JSON.stringify(MyAvatar.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;
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;
@ -192,32 +153,42 @@ public:
void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement);
bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); }
/**jsdoc
* Potentially Very Expensive. Do not use.
/**jsdoc
* Gets details of all avatar entities.
* <p><strong>Warning:</strong> Potentially an expensive call. Do not use if possible.</p>
* @function Avatar.getAvatarEntityData
* @returns {object}
* @returns {AvatarEntityMap} Details of the avatar entities.
* @example <caption>Report the current avatar entities.</caption>
* var avatarEntityData = Avatar.getAvatarEntityData();
* print("Avatar entities: " + JSON.stringify(avatarEntityData));
*/
Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const override;
/**jsdoc
* @function MyAvatar.setAvatarEntityData
* @param {object} avatarEntityData
*/
* Sets all avatar entities from an object.
* <p><strong>Warning:</strong> Potentially an expensive call. Do not use if possible.</p>
* @function Avatar.setAvatarEntityData
* @param {AvatarEntityMap} avatarEntityData - Details of the avatar entities.
*/
Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData) override;
/**jsdoc
* @function MyAvatar.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 - Delta time.
* @deprecated This function is deprecated and will be removed.
*/
void update(float deltatime);
/**jsdoc
* @function MyAvatar.setJointMappingsFromNetworkReply
*/
* @function Avatar.setJointMappingsFromNetworkReply
* @deprecated This function is deprecated and will be removed.
*/
void setJointMappingsFromNetworkReply();
private:

View file

@ -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
@ -85,32 +87,92 @@ Rectangle {
}
function updateMyAvatarGainFromQML(sliderValue, isReleased) {
if (Users.getAvatarGain(myAvatarUuid) != sliderValue) {
Users.setAvatarGain(myAvatarUuid, sliderValue);
if (AudioScriptingInterface.getAvatarGain() != sliderValue) {
AudioScriptingInterface.setAvatarGain(sliderValue);
}
}
function updateInjectorGainFromQML(sliderValue, isReleased) {
if (AudioScriptingInterface.getInjectorGain() != sliderValue) {
AudioScriptingInterface.setInjectorGain(sliderValue); // server side
AudioScriptingInterface.setLocalInjectorGain(sliderValue); // client side
}
}
function updateSystemInjectorGainFromQML(sliderValue, isReleased) {
if (AudioScriptingInterface.getSystemInjectorGain() != sliderValue) {
AudioScriptingInterface.setSystemInjectorGain(sliderValue);
}
}
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 +191,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 +210,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 +233,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 +259,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 +275,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 +291,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 +312,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,12 +348,13 @@ 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);
height: contentHeight;
spacing: 4;
snapMode: ListView.SnapToItem;
clip: true;
model: AudioScriptingInterface.devices.input;
delegate: Item {
@ -301,17 +393,28 @@ 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
@ -341,9 +444,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 {
@ -370,20 +474,22 @@ Rectangle {
}
Item {
id: gainContainer
id: avatarGainContainer
x: margins.paddings;
anchors.top: outputView.bottom;
anchors.topMargin: 10;
width: parent.width - margins.paddings*2
height: gainSliderTextMetrics.height
height: avatarGainSliderTextMetrics.height
HifiControlsUit.Slider {
id: gainSlider
id: avatarGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: Users.getAvatarGain(myAvatarUuid)
value: AudioScriptingInterface.getAvatarGain()
onValueChanged: {
updateMyAvatarGainFromQML(value, false);
}
@ -399,7 +505,7 @@ Rectangle {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
avatarGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
@ -413,13 +519,13 @@ Rectangle {
}
}
TextMetrics {
id: gainSliderTextMetrics
text: gainSliderText.text
font: gainSliderText.font
id: avatarGainSliderTextMetrics
text: avatarGainSliderText.text
font: avatarGainSliderText.font
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: gainSliderText;
id: avatarGainSliderText;
text: "Avatar volume";
size: 16;
anchors.left: parent.left;
@ -429,12 +535,133 @@ Rectangle {
}
}
AudioControls.PlaySampleSound {
x: margins.paddings
Item {
id: injectorGainContainer
x: margins.paddings;
width: parent.width - margins.paddings*2
height: injectorGainSliderTextMetrics.height
anchors.top: avatarGainContainer.bottom;
anchors.topMargin: 10;
visible: (bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR);
anchors { left: parent.left; leftMargin: margins.paddings }
HifiControlsUit.Slider {
id: injectorGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: AudioScriptingInterface.getInjectorGain()
onValueChanged: {
updateInjectorGainFromQML(value, false);
}
onPressedChanged: {
if (!pressed) {
updateInjectorGainFromQML(value, false);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
injectorGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
}
TextMetrics {
id: injectorGainSliderTextMetrics
text: injectorGainSliderText.text
font: injectorGainSliderText.font
}
RalewayRegular {
id: injectorGainSliderText;
text: "Environment volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
Item {
id: systemInjectorGainContainer
x: margins.paddings;
width: parent.width - margins.paddings*2
height: systemInjectorGainSliderTextMetrics.height
anchors.top: injectorGainContainer.bottom;
anchors.topMargin: 10;
HifiControlsUit.Slider {
id: systemInjectorGainSlider
anchors.right: parent.right
height: parent.height
width: 200
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
value: AudioScriptingInterface.getSystemInjectorGain()
onValueChanged: {
updateSystemInjectorGainFromQML(value, false);
}
onPressedChanged: {
if (!pressed) {
updateSystemInjectorGainFromQML(value, false);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
systemInjectorGainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
}
TextMetrics {
id: systemInjectorGainSliderTextMetrics
text: systemInjectorGainSliderText.text
font: systemInjectorGainSliderText.font
}
RalewayRegular {
id: systemInjectorGainSliderText;
text: "System Sound volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
}
AudioControls.PlaySampleSound {
id: playSampleSound
x: margins.paddings
anchors.top: systemInjectorGainContainer.bottom;
anchors.topMargin: 10;
}
}
}

View file

@ -16,7 +16,7 @@ import stylesUit 1.0
TabButton {
id: control
font.pixelSize: height / 2
font.pixelSize: 14
HifiConstants { id: hifi; }

View file

@ -17,17 +17,17 @@ import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit
RowLayout {
property bool audioLoopedBack: AudioScriptingInterface.getServerEcho();
property bool audioLoopedBack: AudioScriptingInterface.getLocalEcho();
function startAudioLoopback() {
if (!audioLoopedBack) {
audioLoopedBack = true;
AudioScriptingInterface.setServerEcho(true);
AudioScriptingInterface.setLocalEcho(true);
}
}
function stopAudioLoopback() {
if (audioLoopedBack) {
audioLoopedBack = false;
AudioScriptingInterface.setServerEcho(false);
AudioScriptingInterface.setLocalEcho(false);
}
}
@ -44,8 +44,11 @@ RowLayout {
}
HifiControlsUit.Button {
text: audioLoopedBack ? qsTr("STOP TESTING YOUR VOICE") : qsTr("TEST YOUR VOICE");
text: audioLoopedBack ? qsTr("STOP TESTING VOICE") : qsTr("TEST YOUR VOICE");
color: audioLoopedBack ? hifi.buttons.red : hifi.buttons.blue;
fontSize: 15;
width: 200;
height: 32;
onClicked: {
if (audioLoopedBack) {
loopbackTimer.stop();
@ -57,11 +60,11 @@ RowLayout {
}
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
font.italic: true
text: audioLoopedBack ? qsTr("Speak in your input") : "";
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: audioLoopedBack ? qsTr("Speak in your input") : "";
// }
}

View file

@ -56,16 +56,19 @@ RowLayout {
HifiConstants { id: hifi; }
HifiControlsUit.Button {
text: isPlaying ? qsTr("STOP TESTING YOUR SOUND") : qsTr("TEST YOUR SOUND");
text: isPlaying ? qsTr("STOP TESTING") : qsTr("TEST YOUR SOUND");
color: isPlaying ? hifi.buttons.red : hifi.buttons.blue;
onClicked: isPlaying ? stopSound() : playSound();
fontSize: 15;
width: 200;
height: 32;
}
RalewayRegular {
Layout.leftMargin: 2;
size: 14;
color: "white";
font.italic: true
text: isPlaying ? qsTr("Listen to your output") : "";
}
// RalewayRegular {
// Layout.leftMargin: 2;
// size: 14;
// color: "white";
// font.italic: true
// text: isPlaying ? qsTr("Listen to your output") : "";
// }
}

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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
}

View file

@ -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;
}

View file

@ -664,7 +664,7 @@ Rectangle {
text: "LOG IN"
onClicked: {
sendToScript({method: 'needsLogIn_loginClicked'});
sendToScript({method: 'marketplace_loginClicked'});
}
}

View file

@ -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;
}

View file

@ -1206,10 +1206,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
if (tabletScriptingInterface) {
tabletScriptingInterface->setQmlTabletRoot(SYSTEM_TABLET, nullptr);
}
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
entityScriptingInterface->deleteEntity(getTabletScreenID());
entityScriptingInterface->deleteEntity(getTabletHomeButtonID());

View file

@ -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<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
tablet->pushOntoStack(tabletUrl);
if (!hmd->getShouldShowTablet()) {
hmd->toggleShouldShowTablet();
}
});
// Settings > Graphics...

View file

@ -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) {

View file

@ -40,6 +40,8 @@ signals:
private:
void diagnoseTextures();
void addError(const QString& errorMessage, const QString& docFragment);
QUrl _avatarFSTFileUrl;
QVector<AvatarDiagnosticResult> _errors;

View file

@ -324,8 +324,11 @@ QString MyAvatar::getDominantHand() const {
void MyAvatar::setDominantHand(const QString& hand) {
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
_dominantHand.set(hand);
emit dominantHandChanged(hand);
bool changed = (hand != _dominantHand.get());
if (changed) {
_dominantHand.set(hand);
emit dominantHandChanged(hand);
}
}
}
@ -2385,7 +2388,19 @@ void MyAvatar::clearWornAvatarEntities() {
}
}
/**jsdoc
* Information about an avatar entity.
* <table>
* <thead>
* <tr><th>Property</th><th>Type</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>id</code></td><td>Uuid</td><td>Entity ID.</td></tr>
* <tr><td><code>properties</code></td><td>{@link Entities.EntityProperties}</td><td>Entity properties.</td></tr>
* </tbody>
* </table>
* @typedef {object} MyAvatar.AvatarEntityData
*/
QVariantList MyAvatar::getAvatarEntitiesVariant() {
// NOTE: this method is NOT efficient
QVariantList avatarEntitiesData;
@ -3454,11 +3469,6 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
QUuid oldSessionID = getSessionUUID();
Avatar::setSessionUUID(sessionUUID);
QUuid newSessionID = getSessionUUID();
if (DependencyManager::get<NodeList>()->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<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
@ -3467,6 +3477,7 @@ void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
_avatarEntitiesLock.withReadLock([&] {
avatarEntityIDs = _packedAvatarEntityData.keys();
});
bool sendPackets = !DependencyManager::get<NodeList>()->getSessionUUID().isNull();
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
entityTree->withWriteLock([&] {
for (const auto& entityID : avatarEntityIDs) {
@ -3474,12 +3485,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 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
// because it contains a stale parentID somewhere deep inside
packetSender->queueEditAvatarEntityMessage(entityTree, entityID);
}
}
@ -3565,6 +3578,12 @@ void MyAvatar::clearScaleRestriction() {
_haveReceivedHeightLimitsFromDomain = false;
}
/**jsdoc
* A teleport target.
* @typedef {object} MyAvatar.GoToProperties
* @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");
auto properties = propertiesVar.toMap();
@ -3921,6 +3940,13 @@ void MyAvatar::setCollisionWithOtherAvatarsFlags() {
_characterController.setPendingFlagsUpdateCollisionMask();
}
/**jsdoc
* 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.
* @property {number} radius - The radius of the cylinder and the hemispheres.
*/
void MyAvatar::updateCollisionCapsuleCache() {
glm::vec3 start, end;
float radius;
@ -5370,6 +5396,24 @@ void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr<Avatar>& otherAvatar)
}
}
/**jsdoc
* Physics options to use in the flow simulation of a joint.
* @typedef {object} MyAvatar.FlowPhysicsOptions
* @property {boolean} [active=true] - <code>true</code> to enable flow on the joint, otherwise <code>false</code>.
* @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] - The stiffness of each thread.
* @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 <code>"sphere"</code> 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 (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFlow",
@ -5419,7 +5463,7 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys
}
auto collisionJoints = collisionsConfig.keys();
if (collisionJoints.size() > 0) {
collisionSystem.resetCollisions();
collisionSystem.clearSelfCollisions();
for (auto &jointName : collisionJoints) {
int jointIndex = getJointIndex(jointName);
FlowCollisionSettings collisionsSettings;
@ -5434,9 +5478,43 @@ void MyAvatar::useFlow(bool isActive, bool isCollidable, const QVariantMap& phys
collisionSystem.addCollisionSphere(jointIndex, collisionsSettings);
}
}
flow.updateScale();
}
}
/**jsdoc
* Flow options currently used in flow simulation.
* @typedef {object} MyAvatar.FlowData
* @property {boolean} initialized - <code>true</code> if flow has been initialized for the current avatar, <code>false</code>
* if it hasn't.
* @property {boolean} active - <code>true</code> if flow is enabled, <code>false</code> if it isn't.
* @property {boolean} colliding - <code>true</code> if collisions are enabled, <code>false</code> if they aren't.
* @property {Object<GroupName, MyAvatar.FlowPhysicsData>} physicsData - The physics configuration for each group of joints
* that has been configured.
* @property {Object<JointName, MyAvatar.FlowCollisionsData>} collisions - The collisions configuration for each joint that
* has collisions configured.
* @property {Object<ThreadName, number[]>} threads - The threads that have been configured, with the first joint's name as the
* <code>ThreadName</code> 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 - <code>true</code> to enable flow on the joint, otherwise <code>false</code>.
* @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 - 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.
*/
/**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()) {

File diff suppressed because it is too large Load diff

View file

@ -228,6 +228,9 @@ void Audio::loadData() {
_hmdMuted = _hmdMutedSetting.get();
_pttDesktop = _pttDesktopSetting.get();
_pttHMD = _pttHMDSetting.get();
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false));
}
bool Audio::getPTTHMD() const {
@ -374,6 +377,18 @@ void Audio::handlePushedToTalk(bool enabled) {
}
}
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseInputDevice(device, isHMD);
});
}
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseOutputDevice(device, isHMD);
});
}
void Audio::setReverb(bool enable) {
withWriteLock([&] {
DependencyManager::get<AudioClient>()->setReverb(enable);
@ -386,14 +401,66 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
});
}
void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
void Audio::setAvatarGain(float gain) {
withWriteLock([&] {
_devices.chooseInputDevice(device, isHMD);
// ask the NodeList to set the master avatar gain
DependencyManager::get<NodeList>()->setAvatarGain(QUuid(), gain);
});
}
void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
withWriteLock([&] {
_devices.chooseOutputDevice(device, isHMD);
float Audio::getAvatarGain() {
return resultWithReadLock<float>([&] {
return DependencyManager::get<NodeList>()->getAvatarGain(QUuid());
});
}
void Audio::setInjectorGain(float gain) {
withWriteLock([&] {
// ask the NodeList to set the audio injector gain
DependencyManager::get<NodeList>()->setInjectorGain(gain);
});
}
float Audio::getInjectorGain() {
return resultWithReadLock<float>([&] {
return DependencyManager::get<NodeList>()->getInjectorGain();
});
}
void Audio::setLocalInjectorGain(float gain) {
withWriteLock([&] {
if (_localInjectorGain != gain) {
_localInjectorGain = gain;
// convert dB to amplitude
gain = fastExp2f(gain / 6.02059991f);
// quantize and limit to match NodeList::setInjectorGain()
gain = unpackFloatGainFromByte(packFloatGainToByte(gain));
DependencyManager::get<AudioClient>()->setLocalInjectorGain(gain);
}
});
}
float Audio::getLocalInjectorGain() {
return resultWithReadLock<float>([&] {
return _localInjectorGain;
});
}
void Audio::setSystemInjectorGain(float gain) {
withWriteLock([&] {
if (_systemInjectorGain != gain) {
_systemInjectorGain = gain;
// convert dB to amplitude
gain = fastExp2f(gain / 6.02059991f);
// quantize and limit to match NodeList::setInjectorGain()
gain = unpackFloatGainFromByte(packFloatGainToByte(gain));
DependencyManager::get<AudioClient>()->setSystemInjectorGain(gain);
}
});
}
float Audio::getSystemInjectorGain() {
return resultWithReadLock<float>([&] {
return _systemInjectorGain;
});
}

View file

@ -170,6 +170,66 @@ public:
*/
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
/**jsdoc
* Sets the avatar gain at the server.
* Units are Decibels (dB)
* @function Audio.setAvatarGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setAvatarGain(float gain);
/**jsdoc
* Gets the avatar gain at the server.
* @function Audio.getAvatarGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getAvatarGain();
/**jsdoc
* Sets the injector gain at the server.
* Units are Decibels (dB)
* @function Audio.setInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setInjectorGain(float gain);
/**jsdoc
* Gets the injector gain at the server.
* @function Audio.getInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getInjectorGain();
/**jsdoc
* Sets the local injector gain in the client.
* Units are Decibels (dB)
* @function Audio.setLocalInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setLocalInjectorGain(float gain);
/**jsdoc
* Gets the local injector gain in the client.
* @function Audio.getLocalInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getLocalInjectorGain();
/**jsdoc
* Sets the injector gain for system sounds.
* Units are Decibels (dB)
* @function Audio.setSystemInjectorGain
* @param {number} gain (in dB)
*/
Q_INVOKABLE void setSystemInjectorGain(float gain);
/**jsdoc
* Gets the injector gain for system sounds.
* @function Audio.getSystemInjectorGain
* @returns {number} gain (in dB)
*/
Q_INVOKABLE float getSystemInjectorGain();
/**jsdoc
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
* @function Audio.startRecording
@ -350,6 +410,8 @@ private:
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
float _localInjectorGain { 0.0f }; // in dB
float _systemInjectorGain { 0.0f }; // in dB
bool _isClipping { false };
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
bool _enableWarnWhenMuted { true };

View file

@ -1711,9 +1711,9 @@ QVector<QUuid> 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
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
*
* @property {boolean} isFacingAvatar - If <code>true< / code>, the overlay is rotated to face the user's camera about an axis
* @property {boolean} isFacingAvatar - If <code>true</code>, 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 <code>\n< / code> for a line break.
* @property {string} text="" - The text to display.Text does not automatically wrap; use <code>\n</code> 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.
@ -1876,7 +1876,7 @@ QVector<QUuid> 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
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
* @property {boolean} drawInFront=false - If <code>true</code>, the overlay is rendered in front of objects in the world, but behind the HUD.
* @property {boolean} drawHUDLayer=false - If <code>true</code>, the overlay is rendered in front of everything, including the HUD.
@ -1916,7 +1916,7 @@ QVector<QUuid> 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
* <code>parentID</code> set, otherwise the same value as <code>position</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} ignorePickIntersection=false - If <code>true</code>, picks ignore the overlay. <code>ignoreRayIntersection</code> is a synonym.
@ -1927,46 +1927,46 @@ QVector<QUuid> 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
* <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> 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: <code>radius< / code>.
* @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
* <code>innerStartColor< / code>, <code>innerEndColor< / code>, <code>outerStartColor< / code>, and <code>outerEndColor< / code>.
* @property {Color} startColor - Sets the values of <code>innerStartColor< / code> and <code>outerStartColor< / code>.
* <em>Write - only.< / em>
* @property {Color} endColor - Sets the values of <code>innerEndColor< / code> and <code>outerEndColor< / code>.
* <em>Write - only.< / em>
* @property {Color} innerColor - Sets the values of <code>innerStartColor< / code> and <code>innerEndColor< / code>.
* <em>Write - only.< / em>
* @property {Color} outerColor - Sets the values of <code>outerStartColor< / code> and <code>outerEndColor< / code>.
* <em>Write - only.< / em>
* @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: <code>radius</code>.
* @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
* <code>innerStartColor</code>, <code>innerEndColor</code>, <code>outerStartColor</code>, and <code>outerEndColor</code>.
* @property {Color} startColor - Sets the values of <code>innerStartColor</code> and <code>outerStartColor</code>.
* <em>Write - only.</em>
* @property {Color} endColor - Sets the values of <code>innerEndColor</code> and <code>outerEndColor</code>.
* <em>Write - only.</em>
* @property {Color} innerColor - Sets the values of <code>innerStartColor</code> and <code>innerEndColor</code>.
* <em>Write - only.</em>
* @property {Color} outerColor - Sets the values of <code>outerStartColor</code> and <code>outerEndColor</code>.
* <em>Write - only.</em>
* @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, <code>0.0< / code> -<code>1.0< / code>.Setting this value also sets
* the values of <code>innerStartAlpha< / code>, <code>innerEndAlpha< / code>, <code>outerStartAlpha< / code>, and
* <code>outerEndAlpha< / code>.Synonym: <code>Alpha< / code>; <em>write - only< / em>.
* @property {number} startAlpha - Sets the values of <code>innerStartAlpha< / code> and <code>outerStartAlpha< / code>.
* <em>Write - only.< / em>
* @property {number} endAlpha - Sets the values of <code>innerEndAlpha< / code> and <code>outerEndAlpha< / code>.
* <em>Write - only.< / em>
* @property {number} innerAlpha - Sets the values of <code>innerStartAlpha< / code> and <code>innerEndAlpha< / code>.
* <em>Write - only.< / em>
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha< / code> and <code>outerEndAlpha< / code>.
* <em>Write - only.< / em>
* @property {number} alpha = 0.5 - The opacity of the overlay, <code>0.0</code> -<code>1.0</code>. Setting this value also sets
* the values of <code>innerStartAlpha</code>, <code>innerEndAlpha</code>, <code>outerStartAlpha</code>, and
* <code>outerEndAlpha</code>. Synonym: <code>Alpha</code>; <em>write - only</em>.
* @property {number} startAlpha - Sets the values of <code>innerStartAlpha</code> and <code>outerStartAlpha</code>.
* <em>Write - only.</em>
* @property {number} endAlpha - Sets the values of <code>innerEndAlpha</code> and <code>outerEndAlpha</code>.
* <em>Write - only.</em>
* @property {number} innerAlpha - Sets the values of <code>innerStartAlpha</code> and <code>innerEndAlpha</code>.
* <em>Write - only.</em>
* @property {number} outerAlpha - Sets the values of <code>outerStartAlpha</code> and <code>outerEndAlpha</code>.
* <em>Write - only.</em>
* @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 <code>true< / code>, tick marks are drawn.
* @property {boolean} hasTickMarks = false - If <code>true</code>, 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.

View file

@ -59,6 +59,46 @@ public:
float getMaxErrorOnLastSolve() { return _maxErrorOnLastSolve; }
/**jsdoc
* <p>Specifies the initial conditions of the IK solver.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Name</p><th>Description</th>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>RelaxToUnderPoses</td><td>This is a blend: it is 15/16 <code>PreviousSolution</code>
* and 1/16 <code>UnderPoses</code>. This provides some of the benefits of using <code>UnderPoses</code> so that the
* underlying animation is still visible, while at the same time converging faster then using the
* <code>UnderPoses</code> as the only initial solution.</td></tr>
* <tr><td><code>1</code></td><td>RelaxToLimitCenterPoses</td><td>This is a blend: it is 15/16
* <code>PreviousSolution</code> and 1/16 <code>LimitCenterPoses</code>. This should converge quickly because it is
* close to the previous solution, but still provides the benefits of avoiding limb locking.</td></tr>
* <tr><td><code>2</code></td><td>PreviousSolution</td><td>The IK system will begin to solve from the same position and
* orientations for each joint that was the result from the previous frame.<br />
* Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly
* to a valid solution.<br />
* 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.</td></tr>
* <tr><td><code>3</code></td><td>UnderPoses</td><td>The 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 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
* 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.</td></tr>
* <tr><td><code>4</code></td><td>LimitCenterPoses</td><td>This 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 have the elbow bent, which is the case for
* <code>LimitCenterPoses</code>. 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.</td></tr>
* </tbody>
* </table>
* @typedef {number} MyAvatar.AnimIKSolutionSource
*/
enum class SolutionSource {
RelaxToUnderPoses = 0,
RelaxToLimitCenterPoses,

View file

@ -24,6 +24,37 @@ class AnimOverlay : public AnimNode {
public:
friend class AnimTests;
/**jsdoc
* <p>Specifies sets of joints.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Name</p><th>Description</th>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>FullBodyBoneSet</td><td>All joints.</td></tr>
* <tr><td><code>1</code></td><td>UpperBodyBoneSet</td><td>Only the "Spine" joint and its children.</td></tr>
* <tr><td><code>2</code></td><td>LowerBodyBoneSet</td><td>Only the leg joints and their children.</td></tr>
* <tr><td><code>3</code></td><td>LeftArmBoneSet</td><td>Joints that are the children of the "LeftShoulder"
* joint.</td></tr>
* <tr><td><code>4</code></td><td>RightArmBoneSet</td><td>Joints that are the children of the "RightShoulder"
* joint.</td></tr>
* <tr><td><code>5</code></td><td>AboveTheHeadBoneSet</td><td>Joints that are the children of the "Head"
* joint.</td></tr>
* <tr><td><code>6</code></td><td>BelowTheHeadBoneSet</td><td>Joints that are NOT the children of the "head"
* joint.</td></tr>
* <tr><td><code>7</code></td><td>HeadOnlyBoneSet</td><td>The "Head" joint.</td></tr>
* <tr><td><code>8</code></td><td>SpineOnlyBoneSet</td><td>The "Spine" joint.</td></tr>
* <tr><td><code>9</code></td><td>EmptyBoneSet</td><td>No joints.</td></tr>
* <tr><td><code>10</code></td><td>LeftHandBoneSet</td><td>joints that are the children of the "LeftHand"
* joint.</td></tr>
* <tr><td><code>11</code></td><td>RightHandBoneSet</td><td>Joints that are the children of the "RightHand"
* joint.</td></tr>
* <tr><td><code>12</code></td><td>HipsOnlyBoneSet</td><td>The "Hips" joint.</td></tr>
* <tr><td><code>13</code></td><td>BothFeetBoneSet</td><td>The "LeftFoot" and "RightFoot" joints.</td></tr>
* </tbody>
* </table>
* @typedef {number} MyAvatar.AnimOverlayBoneSet
*/
enum BoneSet {
FullBodyBoneSet = 0,
UpperBodyBoneSet,

View file

@ -67,17 +67,23 @@ void FlowCollisionSystem::addCollisionSphere(int jointIndex, const FlowCollision
auto collision = FlowCollisionSphere(jointIndex, settings, isTouch);
collision.setPosition(position);
if (isSelfCollision) {
_selfCollisions.push_back(collision);
if (!isTouch) {
_selfCollisions.push_back(collision);
} else {
_selfTouchCollisions.push_back(collision);
}
} else {
_othersCollisions.push_back(collision);
}
};
void FlowCollisionSystem::resetCollisions() {
_allCollisions.clear();
_othersCollisions.clear();
_selfTouchCollisions.clear();
_selfCollisions.clear();
}
FlowCollisionResult FlowCollisionSystem::computeCollision(const std::vector<FlowCollisionResult> collisions) {
FlowCollisionResult result;
if (collisions.size() > 1) {
@ -106,6 +112,10 @@ void FlowCollisionSystem::setScale(float scale) {
_selfCollisions[j]._radius = _selfCollisions[j]._initialRadius * scale;
_selfCollisions[j]._offset = _selfCollisions[j]._initialOffset * scale;
}
for (size_t j = 0; j < _selfTouchCollisions.size(); j++) {
_selfTouchCollisions[j]._radius = _selfTouchCollisions[j]._initialRadius * scale;
_selfTouchCollisions[j]._offset = _selfTouchCollisions[j]._initialOffset * scale;
}
};
std::vector<FlowCollisionResult> FlowCollisionSystem::checkFlowThreadCollisions(FlowThread* flowThread) {
@ -178,9 +188,9 @@ void FlowCollisionSystem::setCollisionSettingsByJoint(int jointIndex, const Flow
}
void FlowCollisionSystem::prepareCollisions() {
_allCollisions.clear();
_allCollisions.resize(_selfCollisions.size() + _othersCollisions.size());
std::copy(_selfCollisions.begin(), _selfCollisions.begin() + _selfCollisions.size(), _allCollisions.begin());
std::copy(_othersCollisions.begin(), _othersCollisions.begin() + _othersCollisions.size(), _allCollisions.begin() + _selfCollisions.size());
_allCollisions.insert(_allCollisions.end(), _selfCollisions.begin(), _selfCollisions.end());
_allCollisions.insert(_allCollisions.end(), _othersCollisions.begin(), _othersCollisions.end());
_allCollisions.insert(_allCollisions.end(), _selfTouchCollisions.begin(), _selfTouchCollisions.end());
_othersCollisions.clear();
}
@ -273,18 +283,20 @@ void FlowJoint::setRecoveryPosition(const glm::vec3& recoveryPosition) {
}
void FlowJoint::update(float deltaTime) {
glm::vec3 accelerationOffset = glm::vec3(0.0f);
if (_settings._stiffness > 0.0f) {
glm::vec3 recoveryVector = _recoveryPosition - _currentPosition;
float recoveryFactor = powf(_settings._stiffness, 3.0f);
accelerationOffset = recoveryVector * recoveryFactor;
}
FlowNode::update(deltaTime, accelerationOffset);
if (_anchored) {
if (!_isHelper) {
_currentPosition = _updatedPosition;
} else {
_currentPosition = _parentPosition;
if (_settings._active) {
glm::vec3 accelerationOffset = glm::vec3(0.0f);
if (_settings._stiffness > 0.0f) {
glm::vec3 recoveryVector = _recoveryPosition - _currentPosition;
float recoveryFactor = powf(_settings._stiffness, 3.0f);
accelerationOffset = recoveryVector * recoveryFactor;
}
FlowNode::update(deltaTime, accelerationOffset);
if (_anchored) {
if (!_isHelper) {
_currentPosition = _updatedPosition;
} else {
_currentPosition = _parentPosition;
}
}
}
};
@ -674,6 +686,14 @@ bool Flow::updateRootFramePositions(const AnimPoseVec& absolutePoses, size_t thr
return true;
}
void Flow::updateCollisionJoint(FlowCollisionSphere& collision, AnimPoseVec& absolutePoses) {
glm::quat jointRotation;
getJointPositionInWorldFrame(absolutePoses, collision._jointIndex, collision._position, _entityPosition, _entityRotation);
getJointRotationInWorldFrame(absolutePoses, collision._jointIndex, jointRotation, _entityRotation);
glm::vec3 worldOffset = jointRotation * collision._offset;
collision._position = collision._position + worldOffset;
}
void Flow::updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses) {
updateAbsolutePoses(relativePoses, absolutePoses);
for (auto &jointData : _flowJointData) {
@ -695,11 +715,11 @@ void Flow::updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses)
}
auto &selfCollisions = _collisionSystem.getSelfCollisions();
for (auto &collision : selfCollisions) {
glm::quat jointRotation;
getJointPositionInWorldFrame(absolutePoses, collision._jointIndex, collision._position, _entityPosition, _entityRotation);
getJointRotationInWorldFrame(absolutePoses, collision._jointIndex, jointRotation, _entityRotation);
glm::vec3 worldOffset = jointRotation * collision._offset;
collision._position = collision._position + worldOffset;
updateCollisionJoint(collision, absolutePoses);
}
auto &selfTouchCollisions = _collisionSystem.getSelfTouchCollisions();
for (auto &collision : selfTouchCollisions) {
updateCollisionJoint(collision, absolutePoses);
}
_collisionSystem.prepareCollisions();
}
@ -710,7 +730,7 @@ void Flow::setJoints(AnimPoseVec& relativePoses, const std::vector<bool>& overri
for (int jointIndex : joints) {
auto &joint = _flowJointData[jointIndex];
if (jointIndex >= 0 && jointIndex < (int)relativePoses.size() && !overrideFlags[jointIndex]) {
relativePoses[jointIndex].rot() = joint.getCurrentRotation();
relativePoses[jointIndex].rot() = joint.getSettings()._active ? joint.getCurrentRotation() : joint.getInitialRotation();
}
}
}

View file

@ -140,6 +140,7 @@ public:
std::vector<FlowCollisionResult> checkFlowThreadCollisions(FlowThread* flowThread);
std::vector<FlowCollisionSphere>& getSelfCollisions() { return _selfCollisions; };
std::vector<FlowCollisionSphere>& getSelfTouchCollisions() { return _selfTouchCollisions; };
void setOthersCollisions(const std::vector<FlowCollisionSphere>& othersCollisions) { _othersCollisions = othersCollisions; }
void prepareCollisions();
void resetCollisions();
@ -150,9 +151,11 @@ public:
void setActive(bool active) { _active = active; }
bool getActive() const { return _active; }
const std::vector<FlowCollisionSphere>& getCollisions() const { return _selfCollisions; }
void clearSelfCollisions() { _selfCollisions.clear(); }
protected:
std::vector<FlowCollisionSphere> _selfCollisions;
std::vector<FlowCollisionSphere> _othersCollisions;
std::vector<FlowCollisionSphere> _selfTouchCollisions;
std::vector<FlowCollisionSphere> _allCollisions;
float _scale { 1.0f };
bool _active { false };
@ -210,7 +213,7 @@ public:
bool isHelper() const { return _isHelper; }
const FlowPhysicsSettings& getSettings() { return _settings; }
void setSettings(const FlowPhysicsSettings& settings) { _settings = settings; }
void setSettings(const FlowPhysicsSettings& settings) { _settings = settings; _initialRadius = _settings._radius; }
const glm::vec3& getCurrentPosition() const { return _currentPosition; }
int getIndex() const { return _index; }
@ -222,6 +225,7 @@ public:
const glm::quat& getCurrentRotation() const { return _currentRotation; }
const glm::vec3& getCurrentTranslation() const { return _initialTranslation; }
const glm::vec3& getInitialPosition() const { return _initialPosition; }
const glm::quat& getInitialRotation() const { return _initialRotation; }
bool isColliding() const { return _colliding; }
protected:
@ -297,6 +301,7 @@ public:
void setPhysicsSettingsForGroup(const QString& group, const FlowPhysicsSettings& settings);
const std::map<QString, FlowPhysicsSettings>& getGroupSettings() const { return _groupSettings; }
void cleanUp();
void updateScale() { setScale(_scale); }
signals:
void onCleanup();
@ -311,6 +316,7 @@ private:
void setJoints(AnimPoseVec& relativePoses, const std::vector<bool>& overrideFlags);
void updateJoints(AnimPoseVec& relativePoses, AnimPoseVec& absolutePoses);
void updateCollisionJoint(FlowCollisionSphere& collision, AnimPoseVec& absolutePoses);
bool updateRootFramePositions(const AnimPoseVec& absolutePoses, size_t threadIndex);
void updateGroupSettings(const QString& group, const FlowPhysicsSettings& settings);
void setScale(float scale);

View file

@ -16,6 +16,27 @@ const float HACK_HMD_TARGET_WEIGHT = 8.0f;
class IKTarget {
public:
/**jsdoc
* <p>An IK target type.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Name</p><th>Description</th>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>RotationAndPosition</td><td>Attempt to reach the rotation and position end
* effector.</td></tr>
* <tr><td><code>1</code></td><td>RotationOnly</td><td>Attempt to reach the end effector rotation only.</td></tr>
* <tr><td><code>2</code></td><td>HmdHead</td><td><strong>Deprecated:</strong> A special mode of IK that would attempt
* to prevent unnecessary bending of the spine.</td></tr>
* <tr><td><code>3</code></td><td>HipsRelativeRotationAndPosition</td><td>Attempt to reach a rotation and position end
* effector that is not in absolute rig coordinates but is offset by the avatar hips translation.</td></tr>
* <tr><td><code>4</code></td><td>Spline</td><td>Use 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.</td></tr>
* <tr><td><code>5</code></td><td>Unknown</td><td>IK is disabled.</td></tr>
* </tbody>
* </table>
* @typedef {number} MyAvatar.IKTargetType
*/
enum class Type {
RotationAndPosition,
RotationOnly,

View file

@ -88,6 +88,218 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_ROTATION("mainStateMachineRig
static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRightHandPosition");
/**jsdoc
* <p>An <code>AnimStateDictionary</code> object may have the following properties. It may also have other properties, set by
* scripts.</p>
* <p><strong>Warning:</strong> These properties are subject to change.
* <table>
* <thead>
* <tr><th>Name</th><th>Type</p><th>Description</th>
* </thead>
* <tbody>
* <tr><td><code>userAnimNone</code></td><td>boolean</td><td><code>true</code> when no user overrideAnimation is
* playing.</td></tr>
* <tr><td><code>userAnimA</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is
* playing.</td></tr>
* <tr><td><code>userAnimB</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is
* playing.</td></tr>
*
* <tr><td><code>sine</code></td><td>number</td><td>Oscillating sine wave.</td></tr>
* <tr><td><code>moveForwardSpeed</code></td><td>number</td><td>Controls the blend between the various forward walking
* &amp; running animations.</td></tr>
* <tr><td><code>moveBackwardSpeed</code></td><td>number</td><td>Controls the blend between the various backward walking
* &amp; running animations.</td></tr>
* <tr><td><code>moveLateralSpeed</code></td><td>number</td><td>Controls the blend between the various sidestep walking
* &amp; running animations.</td></tr>
*
* <tr><td><code>isMovingForward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving
* forward.</td></tr>
* <tr><td><code>isMovingBackward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving
* backward.</td></tr>
* <tr><td><code>isMovingRight</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the
* right.</td></tr>
* <tr><td><code>isMovingLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the
* left.</td></tr>
* <tr><td><code>isMovingRightHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the right
* while the user is in HMD mode.</td></tr>
* <tr><td><code>isMovingLeftHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the left while
* the user is in HMD mode.</td></tr>
* <tr><td><code>isNotMoving</code></td><td>boolean</td><td><code>true</code> if the avatar is stationary.</td></tr>
*
* <tr><td><code>isTurningRight</code></td><td>boolean</td><td><code>true</code> if the avatar is turning
* clockwise.</td></tr>
* <tr><td><code>isTurningLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is turning
* counter-clockwise.</td></tr>
* <tr><td><code>isNotTurning</code></td><td>boolean</td><td><code>true</code> if the avatar is not turning.</td></tr>
* <tr><td><code>isFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is flying.</td></tr>
* <tr><td><code>isNotFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is not flying.</td></tr>
* <tr><td><code>isTakeoffStand</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a
* standing jump.</td></tr>
* <tr><td><code>isTakeoffRun</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a running
* jump.</td></tr>
* <tr><td><code>isNotTakeoff</code></td><td>boolean</td><td><code>true</code> if the avatar is not jumping.</td></tr>
* <tr><td><code>isInAirStand</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a standing
* jump.</td></tr>
* <tr><td><code>isInAirRun</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a running
* jump.</td></tr>
* <tr><td><code>isNotInAir</code></td><td>boolean</td><td><code>true</code> if the avatar on the ground.</td></tr>
*
* <tr><td><code>inAirAlpha</code></td><td>number</td><td>Used to interpolate between the up, apex, and down in-air
* animations.</td></tr>
* <tr><td><code>ikOverlayAlpha</code></td><td>number</td><td>The blend between upper body and spline IK versus the
* underlying animation</td></tr>
*
* <tr><td><code>headPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Head</code> joint in
* rig coordinates.</td></tr>
* <tr><td><code>headRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Head</code> joint in
* rig coordinates.</td></tr>
* <tr><td><code>headType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the
* head.</td></tr>
* <tr><td><code>headWeight</code></td><td>number</td><td>How strongly the head chain blends with the other IK
* chains.</td></tr>
*
* <tr><td><code>leftHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>LeftHand</code>
* joint in rig coordinates.</td></tr>
* <tr><td><code>leftHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>LeftHand</code>
* joint in rig coordinates.</td></tr>
* <tr><td><code>leftHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the
* left arm.</td></tr>
* <tr><td><code>leftHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is
* controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the
* underlying animation.</td></tr>
* <tr><td><code>leftHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local
* coordinate system of the elbow.</td></tr>
* <tr><td><code>leftHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig
* coordinates.</td></tr>
*
* <tr><td><code>rightHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>RightHand</code>
* joint in rig coordinates.</td></tr>
* <tr><td><code>rightHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the
* <code>RightHand</code> joint in rig coordinates.</td></tr>
* <tr><td><code>rightHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for
* the right arm.</td></tr>
* <tr><td><code>rightHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is
* controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the
* underlying animation.</td></tr>
* <tr><td><code>rightHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local
* coordinate system of the elbow.</td></tr>
* <tr><td><code>rightHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig
* coordinates.</td></tr>
*
* <tr><td><code>leftFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the left
* foot.</td></tr>
* <tr><td><code>rightFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the right
* foot.</td></tr>
*
* <tr><td><code>leftFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position
* of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr>
* <tr><td><code>leftFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation
* of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr>
* <tr><td><code>leftFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is
* controlled by the <code>leftFootPoleVector</code> property value. Otherwise the knee direction comes from the
* underlying animation.</td></tr>
* <tr><td><code>leftFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig
* coordinates.</td></tr>
* <tr><td><code>rightFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position
* of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr>
* <tr><td><code>rightFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation
* of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr>
* <tr><td><code>rightFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is
* controlled by the <code>rightFootPoleVector</code> property value. Otherwise the knee direction comes from the
* underlying animation.</td></tr>
* <tr><td><code>rightFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig
* coordinates.</td></tr>
*
* <tr><td><code>isTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is talking.</td></tr>
* <tr><td><code>notIsTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is not talking.</td></tr>
*
* <tr><td><code>solutionSource</code></td><td>{@link MyAvatar.AnimIKSolutionSource|AnimIKSolutionSource}</td>
* <td>Determines the initial conditions of the IK solver.</td></tr>
* <tr><td><code>defaultPoseOverlayAlpha</code></td><td>number</td><td>Controls the blend between the main animation state
* machine and the default pose. Mostly used during full body tracking so that walking &amp; jumping animations do not
* affect the IK of the figure.</td></tr>
* <tr><td><code>defaultPoseOverlayBoneSet</code></td><td>{@link MyAvatar.AnimOverlayBoneSet|AnimOverlayBoneSet}</td>
* <td>Specifies which bones will be replace by the source overlay.</td></tr>
* <tr><td><code>hipsType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the
* hips.</td></tr>
* <tr><td><code>hipsPosition</code></td><td>{@link Vec3}</td><td>The desired position of <code>Hips</code> joint in rig
* coordinates.</td></tr>
* <tr><td><code>hipsRotation</code></td><td>{@link Quat}</td><td>the desired orientation of the <code>Hips</code> joint in
* rig coordinates.</td></tr>
* <tr><td><code>spine2Type</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the
* <code>Spine2</code> joint.</td></tr>
* <tr><td><code>spine2Position</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Spine2</code> joint
* in rig coordinates.</td></tr>
* <tr><td><code>spine2Rotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Spine2</code>
* joint in rig coordinates.</td></tr>
*
* <tr><td><code>leftFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying
* animation.</td></tr>
* <tr><td><code>rightFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying
* animation.</td></tr>
* <tr><td><code>hipsWeight</code></td><td>number</td><td>How strongly the hips target blends with the IK solution for
* other IK chains.</td></tr>
* <tr><td><code>leftHandWeight</code></td><td>number</td><td>How strongly the left hand blends with IK solution of other
* IK chains.</td></tr>
* <tr><td><code>rightHandWeight</code></td><td>number</td><td>How strongly the right hand blends with IK solution of other
* IK chains.</td></tr>
* <tr><td><code>spine2Weight</code></td><td>number</td><td>How strongly the spine2 chain blends with the rest of the IK
* solution.</td></tr>
*
* <tr><td><code>leftHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses, such
* as point and thumbs up.</td></tr>
* <tr><td><code>leftHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand.
* Usually changed as you squeeze the trigger of the hand controller.</td></tr>
* <tr><td><code>rightHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses,
* such as point and thumbs up.</td></tr>
* <tr><td><code>rightHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand.
* Usually changed as you squeeze the trigger of the hand controller.</td></tr>
* <tr><td><code>isLeftIndexPoint</code></td><td>boolean</td><td><code>true</code> if the left hand should be
* pointing.</td></tr>
* <tr><td><code>isLeftThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be
* thumbs-up.</td></tr>
* <tr><td><code>isLeftIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be
* pointing and thumbs-up.</td></tr>
* <tr><td><code>isLeftHandGrasp</code></td><td>boolean</td><td><code>true</code> if the left hand should be at rest,
* grasping the controller.</td></tr>
* <tr><td><code>isRightIndexPoint</code></td><td>boolean</td><td><code>true</code> if the right hand should be
* pointing.</td></tr>
* <tr><td><code>isRightThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should be
* thumbs-up.</td></tr>
* <tr><td><code>isRightIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should
* be pointing and thumbs-up.</td></tr>
* <tr><td><code>isRightHandGrasp</code></td><td>boolean</td><td><code>true</code> if the right hand should be at rest,
* grasping the controller.</td></tr>
*
* </tbody>
* </table>
* <p>Note: Rig coordinates are <code>+z</code> forward and <code>+y</code> up.</p>
* @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<std::mutex> guard(rigRegistryMutex);
@ -1210,7 +1422,8 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
_networkAnimState.blendTime += deltaTime;
alpha = _computeNetworkAnimation ? (_networkAnimState.blendTime / TOTAL_BLEND_TIME) : (1.0f - (_networkAnimState.blendTime / TOTAL_BLEND_TIME));
alpha = glm::clamp(alpha, 0.0f, 1.0f);
for (size_t i = 0; i < _networkPoseSet._relativePoses.size(); i++) {
size_t numJoints = std::min(_networkPoseSet._relativePoses.size(), _internalPoseSet._relativePoses.size());
for (size_t i = 0; i < numJoints; i++) {
_networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], alpha);
}
}

View file

@ -1052,7 +1052,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
return;
}
@ -1368,7 +1368,9 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
memset(_localScratchBuffer, 0, bytesToRead);
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
float gain = options.volume;
bool isSystemSound = !options.positionSet && !options.ambisonic;
float gain = options.volume * (isSystemSound ? _systemInjectorGain : _localInjectorGain);
if (options.ambisonic) {

View file

@ -241,6 +241,8 @@ public slots:
void setInputVolume(float volume, bool emitSignal = true);
void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options);
void setLocalInjectorGain(float gain) { _localInjectorGain = gain; };
void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; };
void outputNotify();
@ -395,6 +397,8 @@ private:
int16_t* _outputScratchBuffer { NULL };
// for local audio (used by audio injectors thread)
std::atomic<float> _localInjectorGain { 1.0f };
std::atomic<float> _systemInjectorGain { 1.0f };
float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
float* _localOutputMixBuffer { NULL };

View file

@ -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:
@ -196,36 +201,52 @@ public:
virtual QStringList getJointNames() const override;
/**jsdoc
* Gets the default rotation of a joint (in the current avatar) relative to its parent.
* <p>For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
* @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.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
* @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;
/**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} index number
* @returns {Quat} The rotation of this joint in avatar coordinates.
* @param index {number} - The joint index.
* @returns {Quat} The default rotation of the joint in avatar coordinates.
* @example <caption>Report the default rotation of your avatar's head joint relative to your avatar.</caption>
* 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} index number
* @returns {Vec3} The position of this joint in avatar coordinates.
* @param index {number} - The joint index.
* @returns {Vec3} The default position of the joint in avatar coordinates.
* @example <caption>Report the default translation of your avatar's head joint relative to your avatar.</caption>
* 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;
@ -233,59 +254,88 @@ 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.
* <p><strong>Warning:</strong> Not able to be used in the <code>MyAvatar</code> API.</p>
* @function MyAvatar.setAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @param {Quat} rotation - The rotation of the joint relative to the avatar. <em>Not used.</em>
* @returns {boolean} <code>false</code>.
*/
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
/**jsdoc
* Sets the translation of a joint relative to the avatar.
* <p><strong>Warning:</strong> Not able to be used in the <code>MyAvatar</code> API.</p>
* @function MyAvatar.setAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @param {Vec3} translation - The translation of the joint relative to the avatar. <em>Not used.</em>
* @returns {boolean} <code>false</code>.
*/
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
virtual glm::vec3 getSpine2SplineOffset() const { return _spine2SplineOffset; }
virtual float getSpine2SplineRatio() const { return _spine2SplineRatio; }
// world-space to avatar-space rigconversion functions
/**jsdoc
* @function MyAvatar.worldToJointPoint
* @param {Vec3} position
* @param {number} [jointIndex=-1]
* @returns {Vec3}
*/
* 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 - 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
* @function MyAvatar.worldToJointDirection
* @param {Vec3} direction
* @param {number} [jointIndex=-1]
* @returns {Vec3}
*/
* 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 - 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
* @function MyAvatar.worldToJointRotation
* @param {Quat} rotation
* @param {number} [jointIndex=-1]
* @returns {Quat}
* 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 - 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
* @function MyAvatar.jointToWorldPoint
* @param {vec3} position
* @param {number} [jointIndex=-1]
* @returns {Vec3}
*/
* 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 - 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
* @function MyAvatar.jointToWorldDirection
* @param {Vec3} direction
* @param {number} [jointIndex=-1]
* @returns {Vec3}
*/
* 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 - 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
* @function MyAvatar.jointToWorldRotation
* @param {Quat} rotation
* @param {number} [jointIndex=-1]
* @returns {Quat}
*/
* 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 - 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;
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
@ -297,7 +347,7 @@ public:
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
/**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 <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
* @function MyAvatar.setSkeletonOffset
* @param {Vec3} offset - The skeleton offset to set.
@ -313,7 +363,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 <code>{ x: 0, y: 0.1, z: 0 }</code>, your avatar will appear to be raised off the ground slightly.
* @function MyAvatar.getSkeletonOffset
* @returns {Vec3} The current skeleton offset.
@ -325,7 +375,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.
@ -333,7 +383,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.
@ -343,7 +393,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 <caption>Report the position of your avatar's neck.</caption>
@ -352,8 +402,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; }
@ -377,47 +428,55 @@ 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;
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. <code>65535</code> or
* <code>-1</code> 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
* <code>65535</code> or <code>-1</code> 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;
/**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.
@ -443,8 +502,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;
@ -500,6 +560,13 @@ public:
uint32_t appendSubMetaItems(render::ItemIDs& subItems);
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 - The avatar's target scale.
* @returns Signal
*/
void targetScaleChanged(float targetScale);
public slots:
@ -508,7 +575,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 <caption>Report the position of your avatar's left palm.</caption>
@ -517,15 +584,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 <caption>Report the rotation of your avatar's left palm.</caption>
* 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 <caption>Report the position of your avatar's right palm.</caption>
@ -542,21 +610,26 @@ public slots:
*/
glm::quat getRightPalmRotation() const;
/**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);
/**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:

View file

@ -1434,6 +1434,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.
*
* <table>
* <thead>
* <tr><th>Rate Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"globalPosition"</code></td><td>Incoming global position.</td></tr>
* <tr><td><code>"localPosition"</code></td><td>Incoming local position.</td></tr>
* <tr><td><code>"avatarBoundingBox"</code></td><td>Incoming avatar bounding box.</td></tr>
* <tr><td><code>"avatarOrientation"</code></td><td>Incoming avatar orientation.</td></tr>
* <tr><td><code>"avatarScale"</code></td><td>Incoming avatar scale.</td></tr>
* <tr><td><code>"lookAtPosition"</code></td><td>Incoming look-at position.</td></tr>
* <tr><td><code>"audioLoudness"</code></td><td>Incoming audio loudness.</td></tr>
* <tr><td><code>"sensorToWorkMatrix"</code></td><td>Incoming sensor-to-world matrix.</td></tr>
* <tr><td><code>"additionalFlags"</code></td><td>Incoming additional avatar flags.</td></tr>
* <tr><td><code>"parentInfo"</code></td><td>Incoming parent information.</td></tr>
* <tr><td><code>"faceTracker"</code></td><td>Incoming face tracker data.</td></tr>
* <tr><td><code>"jointData"</code></td><td>Incoming joint data.</td></tr>
* <tr><td><code>"jointDefaultPoseFlagsRate"</code></td><td>Incoming joint default pose flags.</td></tr>
* <tr><td><code>"farGrabJointRate"</code></td><td>Incoming far grab joint.</td></tr>
* <tr><td><code>"globalPositionOutbound"</code></td><td>Outgoing global position.</td></tr>
* <tr><td><code>"localPositionOutbound"</code></td><td>Outgoing local position.</td></tr>
* <tr><td><code>"avatarBoundingBoxOutbound"</code></td><td>Outgoing avatar bounding box.</td></tr>
* <tr><td><code>"avatarOrientationOutbound"</code></td><td>Outgoing avatar orientation.</td></tr>
* <tr><td><code>"avatarScaleOutbound"</code></td><td>Outgoing avatar scale.</td></tr>
* <tr><td><code>"lookAtPositionOutbound"</code></td><td>Outgoing look-at position.</td></tr>
* <tr><td><code>"audioLoudnessOutbound"</code></td><td>Outgoing audio loudness.</td></tr>
* <tr><td><code>"sensorToWorkMatrixOutbound"</code></td><td>Outgoing sensor-to-world matrix.</td></tr>
* <tr><td><code>"additionalFlagsOutbound"</code></td><td>Outgoing additional avatar flags.</td></tr>
* <tr><td><code>"parentInfoOutbound"</code></td><td>Outgoing parent information.</td></tr>
* <tr><td><code>"faceTrackerOutbound"</code></td><td>Outgoing face tracker data.</td></tr>
* <tr><td><code>"jointDataOutbound"</code></td><td>Outgoing joint data.</td></tr>
* <tr><td><code>"jointDefaultPoseFlagsOutbound"</code></td><td>Outgoing joint default pose flags.</td></tr>
* <tr><td><code>""</code></td><td>When no rate name is specified, the total incoming data rate is provided.</td></tr>
* </tbody>
* </table>
*
* @typedef {string} AvatarDataRate
*/
float AvatarData::getDataRate(const QString& rateName) const {
if (rateName == "") {
return _parseBufferRate.rate() / BYTES_PER_KILOBIT;
@ -1495,6 +1536,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.
*
* <table>
* <thead>
* <tr><th>Rate Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>"globalPosition"</code></td><td>Global position.</td></tr>
* <tr><td><code>"localPosition"</code></td><td>Local position.</td></tr>
* <tr><td><code>"avatarBoundingBox"</code></td><td>Avatar bounding box.</td></tr>
* <tr><td><code>"avatarOrientation"</code></td><td>Avatar orientation.</td></tr>
* <tr><td><code>"avatarScale"</code></td><td>Avatar scale.</td></tr>
* <tr><td><code>"lookAtPosition"</code></td><td>Look-at position.</td></tr>
* <tr><td><code>"audioLoudness"</code></td><td>Audio loudness.</td></tr>
* <tr><td><code>"sensorToWorkMatrix"</code></td><td>Sensor-to-world matrix.</td></tr>
* <tr><td><code>"additionalFlags"</code></td><td>Additional avatar flags.</td></tr>
* <tr><td><code>"parentInfo"</code></td><td>Parent information.</td></tr>
* <tr><td><code>"faceTracker"</code></td><td>Face tracker data.</td></tr>
* <tr><td><code>"jointData"</code></td><td>Joint data.</td></tr>
* <tr><td><code>"farGrabJointData"</code></td><td>Far grab joint data.</td></tr>
* <tr><td><code>""</code></td><td>When no rate name is specified, the overall update rate is provided.</td></tr>
* </tbody>
* </table>
*
* @typedef {string} AvatarUpdateRate
*/
float AvatarData::getUpdateRate(const QString& rateName) const {
if (rateName == "") {
return _parseBufferUpdateRate.rate();
@ -2730,13 +2800,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 <code>true</code> and the model has a skeleton, the bones of the attached model's skeleton are
* rotated to fit the avatar's current pose. If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and
* <code>scale</code> parameters are ignored.
*/
QVariant AttachmentData::toVariant() const {
QVariantMap result;
@ -2942,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.<Uuid, Entities.EntityProperties>} AvatarEntityMap
*/
QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) {
QScriptValue obj = engine->newObject();
for (auto entityID : value.keys()) {

View file

@ -116,6 +116,24 @@ const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit
const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit
const int HAS_HERO_PRIORITY = 12; // 13th bit (be scared)
/**jsdoc
* <p>The pointing state of the hands is specified by the following values:
</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>No hand is pointing.</td></tr>
* <tr><td><code>1</code></td><td>The left hand is pointing.</td></tr>
* <tr><td><code>2</code></td><td>The right hand is pointing.</td></tr>
* <tr><td><code>4</code></td><td>It is the index finger that is pointing.</td></tr>
* </tbody>
* </table>
* <p>The values for the hand states are added together to give the <code>HandState</code> value. For example, if the left
* hand's finger is pointing, the value is <code>1 + 4 == 5</code>.
* @typedef {number} HandState
*/
const char HAND_STATE_NULL = 0;
const char LEFT_HAND_POINTING_FLAG = 1;
const char RIGHT_HAND_POINTING_FLAG = 2;
@ -414,7 +432,54 @@ 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 - The position of the avatar.
* @property {number} scale=1.0 - The scale of the avatar. The value can be set to anything between <code>0.005</code> and
* <code>1000.0</code>. 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/m<sup>3</sup>. The density is used to work out its mass in
* the application of physics. <em>Read-only.</em>
* @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 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".
* @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 - 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".
* @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 - 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 - <code>displayName's</code> 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 - <code>true</code> 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 <code>lookAtSnappingEnabled == true</code>.
* @property {string} skeletonModelURL - The avatar's FST file.
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
* <strong>Deprecated:</strong> Use avatar entities instead.
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
* @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. <em>Read-only.</em>
* @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. <em>Read-only.</em>
* @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the
* avatar. <em>Read-only.</em>
* @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the
* avatar. <em>Read-only.</em>
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. <em>Read-only.</em>
*/
Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript)
Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale)
Q_PROPERTY(float density READ getDensity)
@ -568,18 +633,18 @@ public:
virtual bool getHasAudioEnabledFaceMovement() const { return false; }
/**jsdoc
* Returns 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 MyAvatar.getDomainMinScale
* @returns {number} minimum scale allowed for this avatar in the current domain.
* @function Avatar.getDomainMinScale
* @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.
* 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 MyAvatar.getDomainMaxScale
* @returns {number} maximum scale allowed for this avatar in the current domain.
* @function Avatar.getDomainMaxScale
* @returns {number} The maximum scale allowed for this avatar in the current domain.
*/
Q_INVOKABLE float getDomainMaxScale() const;
@ -592,18 +657,18 @@ public:
virtual bool canMeasureEyeHeight() const { return false; }
/**jsdoc
* Provides read only access to 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 MyAvatar.getEyeHeight
* @returns {number} Eye height of avatar in meters.
* @function Avatar.getEyeHeight
* @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.
* 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 MyAvatar.getHeight
* @returns {number} Height of avatar in meters.
* @function Avatar.getHeight
* @returns {number} The height of the avatar.
*/
Q_INVOKABLE virtual float getHeight() const;
@ -613,36 +678,43 @@ public:
void setDomainMaximumHeight(float domainMaximumHeight);
/**jsdoc
* @function MyAvatar.setHandState
* @param {string} state
* 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 {HandState} state - The pointing state of the hand.
*/
Q_INVOKABLE void setHandState(char s) { _handState = s; }
/**jsdoc
* @function MyAvatar.getHandState
* @returns {string}
* 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 {HandState} The pointing state of the hand.
*/
Q_INVOKABLE char getHandState() const { return _handState; }
const QVector<JointData>& getRawJointData() const { return _jointData; }
/**jsdoc
* @function MyAvatar.setRawJointData
* @param {JointData[]} data
* Sets joint translations and rotations from raw joint data.
* @function Avatar.setRawJointData
* @param {JointData[]} data - The raw joint data.
* @deprecated This function is deprecated and will be removed.
*/
Q_INVOKABLE void setRawJointData(QVector<JointData> data);
/**jsdoc
* Set a specific joint's rotation and position relative to its parent.
* <p>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.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>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.</p>
* @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.
* @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates.
* @example <caption>Set your avatar to it's default T-pose for a while.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/t-pose.png" /></caption>
* // Set all joint translations and rotations to defaults.
@ -657,91 +729,98 @@ 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);
/**jsdoc
* Set a specific joint's rotation relative to its parent.
* Sets a specific joint's rotation relative to its parent.
* <p>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.</p>
* @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.
*/
Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation);
/**jsdoc
* Set a specific joint's translation relative to its parent.
* <p>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.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>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.</p>
* @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.
* @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);
/**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.
* <p>Note: This is slightly faster than the function variation that specifies the joint name.</p>
* @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
* @param {number} index
* @returns {boolean}
* Checks that the data for a joint are valid.
* @function Avatar.isJointDataValid
* @param {number} index - The index of the joint.
* @returns {boolean} <code>true</code> if the joint data are valid, <code>false</code> 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
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointRotation
* Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.
* @function Avatar.getJointRotation
* @param {number} index - The index of the joint.
* @returns {Quat} The rotation of the joint relative to its parent.
*/
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
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointTranslation
* Gets the translation of a joint relative to its parent, in model coordinates.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
* @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
* Set a specific joint's rotation and position relative to its parent.
* <p>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.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>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.</p>
* @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.
* @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);
/**jsdoc
* Set a specific joint's rotation relative to its parent.
* Sets a specific joint's rotation relative to its parent.
* <p>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.</p>
* @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 <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
@ -762,104 +841,125 @@ 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);
/**jsdoc
* Set a specific joint's translation relative to its parent.
* <p>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.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>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.</p>
* @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.
* @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates.
* @example <caption>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.<br />
* <img alt="Avatar with neck stretched" src="https://docs.highfidelity.com/images/stretched-neck.png" /></caption>
* // 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 () {
* 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);
/**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.
* <p>Note: This is slightly slower than the function variation that specifies the joint index.</p>
* @function MyAvatar.clearJointData
* @function Avatar.clearJointData
* @param {string} name - The name of the joint.
* @example <caption>Offset and restore the position of your avatar's head.</caption>
* // 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);
*
* // 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
* @param {string} name
* @returns {boolean}
* Checks if the data for a joint are valid.
* @function Avatar.isJointDataValid
* @param {string} name - The name of the joint.
* @returns {boolean} <code>true</code> if the joint data are valid, <code>false</code> 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
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointRotation
* Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.
* @function Avatar.getJointRotation
* @param {string} name - The name of the joint.
* @returns {Quat} The rotation of the joint relative to its parent.
* @example <caption>Report the rotation of your avatar's hips joint.</caption>
* 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
* <a href="https://docs.highfidelity.com/create-and-explore/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.getJointTranslation
* Gets the translation of a joint relative to its parent, in model coordinates.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>For information on the joint hierarchy used, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
* @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 <caption>Report the translation of your avatar's hips joint.</caption>
* 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
* 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}.
* returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the <code>Avatar</code> API.
* @example <caption>Report the rotations of all your avatar's joints.</caption>
* print(JSON.stringify(MyAvatar.getJointRotations()));
*
* // Note: If using from the Avatar API, replace all "MyAvatar" with "Avatar".
*/
Q_INVOKABLE virtual QVector<glm::quat> getJointRotations() const;
/**jsdoc
* @function MyAvatar.getJointTranslations
* @returns {Vec3[]}
* Gets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in
* model coordinates.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* @function Avatar.getJointTranslations
* @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
* <code>Avatar</code> API.
*/
Q_INVOKABLE virtual QVector<glm::vec3> 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.
* <p>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.</p>
* @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}.
* array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the <code>Avatar</code> API.
* @example <caption>Set your avatar to its default T-pose then rotate its right arm.<br />
* <img alt="Avatar in T-pose" src="https://docs.highfidelity.com/images/armpose.png" /></caption>
* // Set all joint translations and rotations to defaults.
@ -883,19 +983,31 @@ 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<glm::quat>& jointRotations);
/**jsdoc
* @function MyAvatar.setJointTranslations
* @param {Vec3[]} translations
* Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in
* model coordinates.
* <p><strong>Warning:</strong> These coordinates are not necessarily in meters.</p>
* <p>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.</p>
* @function Avatar.setJointTranslations
* @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
* <code>Avatar</code> API.
*/
Q_INVOKABLE virtual void setJointTranslations(const QVector<glm::vec3>& 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 MyAvatar.clearJointsData
* @function Avatar.clearJointsData
* @example <caption>Set your avatar to it's default T-pose for a while.</caption>
* // Set all joint translations and rotations to defaults.
* var i, length, rotation, translation;
@ -909,49 +1021,69 @@ 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
* 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 <code>Avatar</code> 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 <code>-1</code>.
* @example <caption>Report the index of your avatar's left arm joint.</caption>
* print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"));
* 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
* Gets the names of all the joints in the current avatar.
* @function Avatar.getJointNames
* @returns {string[]} The joint names.
* @example <caption>Report the names of all the joints in your current avatar.</caption>
* 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
* @param {string} name
* @param {number} value
* 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 - The name of the blendshape, per the
* {@link https://docs.highfidelity.com/create/avatars/avatar-standards.html#blendshapes Avatar Standards}.
* @param {number} value - A value between <code>0.0</code> and <code>1.0</code>.
* @example <caption>Open your avatar's mouth wide.</caption>
* 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); }
/**jsdoc
* @function MyAvatar.getAttachmentsVariant
* @returns {object}
* Gets information about the models currently attached to your avatar.
* @function Avatar.getAttachmentsVariant
* @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
* @function MyAvatar.setAttachmentsVariant
* @param {object} variant
* 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 {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);
@ -959,22 +1091,28 @@ public:
virtual void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload);
/**jsdoc
* @function MyAvatar.updateAvatarEntity
* @param {Uuid} entityID
* @param {string} entityData
* @function Avatar.updateAvatarEntity
* @param {Uuid} entityID - The entity ID.
* @param {Array.<byte>} 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 MyAvatar.clearAvatarEntity
* @param {Uuid} entityID
* @function Avatar.clearAvatarEntity
* @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
* @function MyAvatar.setForceFaceTrackerConnected
* @param {boolean} connected
* 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 - <code>true</code> to enable blendshape changes to be transmitted to other users,
* <code>false</code> to disable.
*/
Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; }
@ -1025,24 +1163,28 @@ public:
}
/**jsdoc
* Get information about all models currently attached to your avatar.
* @function MyAvatar.getAttachmentData
* 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 <caption>Report the URLs of all current attachments.</caption>
* 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".
*/
Q_INVOKABLE virtual QVector<AttachmentData> 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 <code>attachmentData</code> to <code>null</code>.
* @function MyAvatar.setAttachmentData
* @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use
* @function Avatar.setAttachmentData
* @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use
* <code>null</code> to remove all attachments.
* @deprecated Use avatar entities instead.
* @example <caption>Remove a hat attachment if your avatar is wearing it.</caption>
* var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx";
* var attachments = MyAvatar.getAttachmentData();
@ -1054,15 +1196,17 @@ public:
* break;
* }
* }
*
* // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar".
*/
Q_INVOKABLE virtual void setAttachmentData(const QVector<AttachmentData>& 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.
* <p>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.</p>
* @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.
@ -1070,12 +1214,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 <code>true</code> so that the bones of the
* attached model's skeleton are be rotated to fit the avatar's current pose. <code>isSoft</code> is used, for example,
* attached model's skeleton are rotated to fit the avatar's current pose. <code>isSoft</code> is used, for example,
* to have clothing that moves with the avatar.<br />
* If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and <code>scale</code> parameters are
* ignored.
* @param {boolean} [allowDuplicates=false]
* @param {boolean} [useSaved=true]
* @param {boolean} [allowDuplicates=false] - If <code>true</code> then more than one copy of any particular model may be
* attached to the same joint; if <code>false</code> then the same model cannot be attached to the same joint.
* @param {boolean} [useSaved=true] - <em>Not used.</em>
* @deprecated Use avatar entities instead.
* @example <caption>Attach a cowboy hat to your avatar's head.</caption>
* var attachment = {
* modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
@ -1092,6 +1238,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(),
@ -1099,20 +1247,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.
* @function MyAvatar.detachOne
* 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 <code>""</code>, 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.
* @function MyAvatar.detachAll
* 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 <code>""</code>, 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());
@ -1151,14 +1301,12 @@ public:
AABox getDefaultBubbleBox() const;
/**jsdoc
* @function MyAvatar.getAvatarEntityData
* @returns {object}
* @comment Documented in derived classes' JSDoc because implementations are different.
*/
Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const;
/**jsdoc
* @function MyAvatar.setAvatarEntityData
* @param {object} avatarEntityData
* @comment Documented in derived classes' JSDoc because implementations are different.
*/
Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData);
@ -1166,45 +1314,69 @@ public:
AvatarEntityIDs getAndClearRecentlyRemovedIDs();
/**jsdoc
* @function MyAvatar.getSensorToWorldMatrix
* @returns {Mat4}
* 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} 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 <caption>Report the sensor to world matrix.</caption>
* 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;
/**jsdoc
* @function MyAvatar.getSensorToWorldScale
* @returns {number}
* 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} 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
* @function MyAvatar.getControllerLeftHandMatrix
* @returns {Mat4}
* 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 <caption>Report the left hand controller matrix.</caption>
* 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;
/**jsdoc
* @function MyAvatar.getControllerRightHandMatrix
* @returns {Mat4}
* Gets the rotation and translation of the right hand controller relative to the avatar.
* @function Avatar.getControllerRightHandMatrix
* @returns {Mat4} The rotation and translation of the right hand controller relative to the avatar.
*/
// thread safe
Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const;
/**jsdoc
* @function MyAvatar.getDataRate
* @param {string} [rateName=""]
* @returns {number}
* Gets the amount of avatar mixer data being generated by the avatar.
* @function Avatar.getDataRate
* @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
* @function MyAvatar.getUpdateRate
* @param {string} [rateName=""]
* @returns {number}
* Gets the update rate of avatar mixer data being generated by the avatar.
* @function Avatar.getUpdateRate
* @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;
@ -1250,52 +1422,92 @@ public:
signals:
/**jsdoc
* @function MyAvatar.displayNameChanged
* Triggered when the avatar's <code>displayName</code> property value changes.
* @function Avatar.displayNameChanged
* @returns {Signal}
* @example <caption>Report when your avatar display name changes.</caption>
* 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();
/**jsdoc
* @function MyAvatar.sessionDisplayNameChanged
* Triggered when the avatar's <code>sessionDisplayName</code> property value changes.
* @function Avatar.sessionDisplayNameChanged
* @returns {Signal}
* @example <caption>Report when your avatar's session display name changes.</caption>
* 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();
/**jsdoc
* @function MyAvatar.skeletonModelURLChanged
* Triggered when the avatar's model (i.e., <code>skeletonModelURL</code> property value) is changed.
* @function Avatar.skeletonModelURLChanged
* @returns {Signal}
* @example <caption>Report when your avatar's skeleton model changes.</caption>
* MyAvatar.skeletonModelURLChanged.connect(function () {
* print("Skeleton model changed to: " + MyAvatar.skeletonModelURL);
* });
*
* // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar".
*/
void skeletonModelURLChanged();
/**jsdoc
* @function MyAvatar.lookAtSnappingChanged
* @param {boolean} enabled
* Triggered when the avatar's <code>lookAtSnappingEnabled</code> property value changes.
* @function Avatar.lookAtSnappingChanged
* @param {boolean} enabled - <code>true</code> if look-at snapping is enabled, <code>false</code> if not.
* @returns {Signal}
* @example <caption>Report when your look-at snapping setting changes.</caption>
* 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);
/**jsdoc
* @function MyAvatar.sessionUUIDChanged
* Triggered when the avatar's <code>sessionUUID</code> property value changes.
* @function Avatar.sessionUUIDChanged
* @returns {Signal}
* @example <caption>Report when your avatar's session UUID changes.</caption>
* 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();
public slots:
/**jsdoc
* @function MyAvatar.sendAvatarDataPacket
* @param {boolean} [sendAll=false]
* @function Avatar.sendAvatarDataPacket
* @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 MyAvatar.sendIdentityPacket
* @function Avatar.sendIdentityPacket
* @returns {number}
* @deprecated This function is deprecated and will be removed.
*/
int sendIdentityPacket();
/**jsdoc
* @function MyAvatar.setSessionUUID
* @param {Uuid} sessionUUID
* @function Avatar.setSessionUUID
* @param {Uuid} sessionUUID - Session UUID.
* @deprecated This function is deprecated and will be removed.
*/
virtual void setSessionUUID(const QUuid& sessionUUID) {
if (sessionUUID != getID()) {
@ -1308,44 +1520,61 @@ public slots:
}
}
/**jsdoc
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index
* @returns {Quat}
* Gets the rotation of a joint relative to the avatar.
* <p><strong>Warning:</strong> Not able to be used in the <code>Avatar</code> API.</p>
* @function Avatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @returns {Quat} <code>Quat.IDENTITY</code>.
*/
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
/**jsdoc
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index
* @returns {Vec3}
* Gets the translation of a joint relative to the avatar.
* <p><strong>Warning:</strong> Not able to be used in the <code>Avatar</code> API.</p>
* @function Avatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @returns {Vec3} <code>Vec3.ZERO</code>.
*/
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
/**jsdoc
* @function MyAvatar.setAbsoluteJointRotationInObjectFrame
* @param {number} index
* @param {Quat} rotation
* @returns {boolean}
* Sets the rotation of a joint relative to the avatar.
* <p><strong>Warning:</strong> Not able to be used in the <code>Avatar</code> API.</p>
* @function Avatar.setAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @param {Quat} rotation - The rotation of the joint relative to the avatar. <em>Not used.</em>
* @returns {boolean} <code>false</code>.
*/
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
/**jsdoc
* @function MyAvatar.setAbsoluteJointTranslationInObjectFrame
* @param {number} index
* @param {Vec3} translation
* @returns {boolean}
* Sets the translation of a joint relative to the avatar.
* <p><strong>Warning:</strong> Not able to be used in the <code>Avatar</code> API.</p>
* @function Avatar.setAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint. <em>Not used.</em>
* @param {Vec3} translation - The translation of the joint relative to the avatar. <em>Not used.</em>
* @returns {boolean} <code>false</code>.
*/
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
/**jsdoc
* @function MyAvatar.getTargetScale
* @returns {number}
* Gets the target scale of the avatar without any restrictions on permissible values imposed by the domain. In contrast, the
* <code>scale</code> property's value may be limited by the domain's settings.
* @function Avatar.getTargetScale
* @returns {number} The target scale of the avatar.
* @example <caption>Compare the target and current avatar scales.</caption>
* 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 MyAvatar.resetLastSent
* @function Avatar.resetLastSent
* @deprecated This function is deprecated and will be removed.
*/
void resetLastSent() { _lastToByteArray = 0; }

View file

@ -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,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, [this, onMaterialRequestFinished](bool success) {
withWriteLock([&] {
onMaterialRequestFinished(success);
});
});
}
}
} else if (materialDataChanged && usingMaterialData) {

View file

@ -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<TextureCache>()->getTexture(entityTextures);
withWriteLock([&] {
_texture = DependencyManager::get<TextureCache>()->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,23 @@ 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;
bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged;
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () {
withWriteLock([&] {
updateModelTransformAndBound();
_renderTransform = getModelTransform();
if (geometryChanged) {
updateGeometry();
}
});
});
}
void PolyLineEntityRenderer::updateGeometry() {
@ -267,22 +279,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<TextureCache>()->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<TextureCache>()->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);
}

View file

@ -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;

View file

@ -249,10 +249,14 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
graphics::MultiMaterial materials;
auto geometryCache = DependencyManager::get<GeometryCache>();
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<graphics::MultiMaterial::Schema>();
@ -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 = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
} else {
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, args->_shapePipeline);
pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
}
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) {

View file

@ -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<DeferredLightingEffect>()->unsetKeyLightBatch(batch);
}
}
QSizeF TextEntityRenderer::textSize(const QString& text) const {

View file

@ -443,6 +443,7 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
QString hifiGlobalNodeID;
unsigned int meshIndex = 0;
haveReportedUnhandledRotationOrder = false;
int fbxVersionNumber = -1;
foreach (const FBXNode& child, node.children) {
if (child.name == "FBXHeaderExtension") {
@ -465,6 +466,8 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
}
}
}
} else if (object.name == "FBXVersion") {
fbxVersionNumber = object.properties.at(0).toInt();
}
}
} else if (child.name == "GlobalSettings") {
@ -1311,8 +1314,6 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
joint.bindTransformFoundInCluster = false;
hfmModel.joints.append(joint);
QString rotationID = localRotations.value(modelID);
AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID));
AnimationCurve yRotCurve = animationCurves.value(yComponents.value(rotationID));
@ -1335,7 +1336,13 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const
xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()),
yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()),
zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size()));
if ((fbxVersionNumber < 7500) && (i == 0)) {
joint.translation = hfmModel.animationFrames[i].translations[jointIndex];
joint.rotation = hfmModel.animationFrames[i].rotations[jointIndex];
}
}
hfmModel.joints.append(joint);
}
// NOTE: shapeVertices are in joint-frame

View file

@ -40,7 +40,7 @@ public:
auto lastSlash = filename.rfind('/');
result = filename.substr(0, lastSlash + 1);
} else {
std::string result = QFileInfo(filename.c_str()).absoluteDir().canonicalPath().toStdString();
result = QFileInfo(filename.c_str()).absoluteDir().canonicalPath().toStdString();
if (*result.rbegin() != '/') {
result += '/';
}

View file

@ -1016,6 +1016,14 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
// also send them the current ignore radius state.
sendIgnoreRadiusStateToNode(newNode);
// also send the current avatar and injector gains
if (_avatarGain != 0.0f) {
setAvatarGain(QUuid(), _avatarGain);
}
if (_injectorGain != 0.0f) {
setInjectorGain(_injectorGain);
}
}
if (newNode->getType() == NodeType::AvatarMixer) {
// this is a mixer that we just added - it's unlikely it knows who we were previously ignoring in this session,
@ -1062,13 +1070,17 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
if (nodeID.isNull()) {
qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain;
} else {
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
}
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
QWriteLocker lock{ &_avatarGainMapLock };
_avatarGainMap[nodeID] = gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
_avatarGain = gain;
} else {
qCDebug(networking) << "Sending Set Avatar Gain packet with UUID:" << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
QWriteLocker lock{ &_avatarGainMapLock };
_avatarGainMap[nodeID] = gain;
}
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";
@ -1079,14 +1091,41 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
}
float NodeList::getAvatarGain(const QUuid& nodeID) {
QReadLocker lock{ &_avatarGainMapLock };
auto it = _avatarGainMap.find(nodeID);
if (it != _avatarGainMap.cend()) {
return it->second;
if (nodeID.isNull()) {
return _avatarGain;
} else {
QReadLocker lock{ &_avatarGainMapLock };
auto it = _avatarGainMap.find(nodeID);
if (it != _avatarGainMap.cend()) {
return it->second;
}
}
return 0.0f;
}
void NodeList::setInjectorGain(float gain) {
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// setup the packet
auto setInjectorGainPacket = NLPacket::create(PacketType::InjectorGainSet, sizeof(float), true);
// We need to convert the gain in dB (from the script) to an amplitude before packing it.
setInjectorGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.02059991f)));
qCDebug(networking) << "Sending Set Injector Gain packet with Gain:" << gain;
sendPacket(std::move(setInjectorGainPacket), *audioMixer);
_injectorGain = gain;
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";
}
}
float NodeList::getInjectorGain() {
return _injectorGain;
}
void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
// send a request to domain-server to kick the node with the given session ID
// the domain-server will handle the persistence of the kick (via username or IP)

View file

@ -83,6 +83,8 @@ public:
bool isPersonalMutingNode(const QUuid& nodeID) const;
void setAvatarGain(const QUuid& nodeID, float gain);
float getAvatarGain(const QUuid& nodeID);
void setInjectorGain(float gain);
float getInjectorGain();
void kickNodeBySessionID(const QUuid& nodeID);
void muteNodeBySessionID(const QUuid& nodeID);
@ -181,6 +183,9 @@ private:
mutable QReadWriteLock _avatarGainMapLock;
tbb::concurrent_unordered_map<QUuid, float, UUIDHasher> _avatarGainMap;
std::atomic<float> _avatarGain { 0.0f }; // in dB
std::atomic<float> _injectorGain { 0.0f }; // in dB
void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode);
#if defined(Q_OS_ANDROID)
Setting::Handle<bool> _ignoreRadiusEnabled { "IgnoreRadiusEnabled", false };

View file

@ -57,7 +57,7 @@ public:
ICEServerQuery,
OctreeStats,
SetAvatarTraits,
UNUSED_PACKET_TYPE,
InjectorGainSet,
AssignmentClientStatus,
NoisyMute,
AvatarIdentity,

View file

@ -178,7 +178,6 @@ void CauterizedModel::updateClusterMatrices() {
}
}
}
computeMeshPartLocalBounds();
// post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>();

View file

@ -722,6 +722,8 @@ gpu::ShaderPointer GeometryCache::_unlitFadeShader;
render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline;
render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline;
render::ShapePipelinePointer GeometryCache::_forwardSimpleOpaquePipeline;
render::ShapePipelinePointer GeometryCache::_forwardSimpleTransparentPipeline;
render::ShapePipelinePointer GeometryCache::_simpleOpaqueFadePipeline;
render::ShapePipelinePointer GeometryCache::_simpleTransparentFadePipeline;
render::ShapePipelinePointer GeometryCache::_simpleWirePipeline;
@ -801,6 +803,8 @@ void GeometryCache::initializeShapePipelines() {
if (!_simpleOpaquePipeline) {
_simpleOpaquePipeline = getShapePipeline(false, false, true, false);
_simpleTransparentPipeline = getShapePipeline(false, true, true, false);
_forwardSimpleOpaquePipeline = getShapePipeline(false, false, true, false, false, true);
_forwardSimpleTransparentPipeline = getShapePipeline(false, true, true, false, false, true);
_simpleOpaqueFadePipeline = getFadingShapePipeline(false, false, false, false, false);
_simpleTransparentFadePipeline = getFadingShapePipeline(false, true, false, false, false);
_simpleWirePipeline = getShapePipeline(false, false, true, true);

View file

@ -181,6 +181,11 @@ public:
static void initializeShapePipelines();
render::ShapePipelinePointer getOpaqueShapePipeline() { assert(_simpleOpaquePipeline != nullptr); return _simpleOpaquePipeline; }
render::ShapePipelinePointer getTransparentShapePipeline() { assert(_simpleTransparentPipeline != nullptr); return _simpleTransparentPipeline; }
render::ShapePipelinePointer getForwardOpaqueShapePipeline() { assert(_forwardSimpleOpaquePipeline != nullptr); return _forwardSimpleOpaquePipeline; }
render::ShapePipelinePointer getForwardTransparentShapePipeline() { assert(_forwardSimpleTransparentPipeline != nullptr); return _forwardSimpleTransparentPipeline; }
// Static (instanced) geometry
void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
@ -368,6 +373,7 @@ public:
const ShapeData * getShapeData(Shape shape) const;
graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color);
private:
GeometryCache();
@ -461,6 +467,8 @@ private:
static gpu::ShaderPointer _unlitFadeShader;
static render::ShapePipelinePointer _simpleOpaquePipeline;
static render::ShapePipelinePointer _simpleTransparentPipeline;
static render::ShapePipelinePointer _forwardSimpleOpaquePipeline;
static render::ShapePipelinePointer _forwardSimpleTransparentPipeline;
static render::ShapePipelinePointer _simpleOpaqueFadePipeline;
static render::ShapePipelinePointer _simpleTransparentFadePipeline;
static render::ShapePipelinePointer _simpleWirePipeline;

View file

@ -1346,19 +1346,14 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
}
void Model::computeMeshPartLocalBounds() {
render::Transaction transaction;
auto meshStates = _meshStates;
for (auto renderItem : _modelMeshRenderItemIDs) {
transaction.updateItem<ModelMeshPartPayload>(renderItem, [this, meshStates](ModelMeshPartPayload& data) {
const Model::MeshState& state = meshStates.at(data._meshIndex);
if (_useDualQuaternionSkinning) {
data.computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
data.computeAdjustedLocalBound(state.clusterMatrices);
}
});
for (auto& part : _modelMeshRenderItems) {
const Model::MeshState& state = _meshStates.at(part->_meshIndex);
if (_useDualQuaternionSkinning) {
part->computeAdjustedLocalBound(state.clusterDualQuaternions);
} else {
part->computeAdjustedLocalBound(state.clusterMatrices);
}
}
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
}
// virtual
@ -1391,7 +1386,6 @@ void Model::updateClusterMatrices() {
}
}
}
computeMeshPartLocalBounds();
// post the blender if we're not currently waiting for one to finish
auto modelBlender = DependencyManager::get<ModelBlender>();
@ -1648,7 +1642,7 @@ using packBlendshapeOffsetTo = void(glm::uvec4& packed, const BlendshapeOffsetUn
void packBlendshapeOffsetTo_Pos_F32_3xSN10_Nor_3xSN10_Tan_3xSN10(glm::uvec4& packed, const BlendshapeOffsetUnpacked& unpacked) {
float len = glm::compMax(glm::abs(unpacked.positionOffset));
glm::vec3 normalizedPos(unpacked.positionOffset);
if (len > 1.0f) {
if (len > 0.0f) {
normalizedPos /= len;
} else {
len = 1.0f;

View file

@ -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;
});
}

View file

@ -216,8 +216,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<DrawHaze>("DrawHazeDeferred", drawHazeInputs);
// Render transparent objects forward in LightingBuffer
const auto transparentsInputs = DrawDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying();
task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
const auto transparentsInputs = RenderTransparentDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying();
task.addJob<RenderTransparentDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
const auto outlineRangeTimer = task.addJob<BeginGPURangeTimer>("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

View file

@ -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<render::ItemBounds, HazeStage::FramePointer, LightStage::FramePointer, LightingModelPointer, LightClustersPointer, LightStage::ShadowFramePointer, glm::vec2>;
using Config = DrawDeferredConfig;
using JobModel = render::Job::ModelI<DrawDeferred, Inputs, Config>;
using Config = RenderTransparentDeferredConfig;
using JobModel = render::Job::ModelI<RenderTransparentDeferred, Inputs, Config>;
DrawDeferred(render::ShapePlumberPointer shapePlumber)
RenderTransparentDeferred(render::ShapePlumberPointer shapePlumber)
: _shapePlumber{ shapePlumber } {}
void configure(const Config& config) { _maxDrawn = config.maxDrawn; }

View file

@ -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<DrawForward>("DrawOpaques", opaqueInputs, shapePlumber);
task.addJob<DrawForward>("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<DrawForward>("DrawTransparents", transparentInputs, shapePlumber);
task.addJob<DrawForward>("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;

View file

@ -76,12 +76,13 @@ public:
using Inputs = render::VaryingSet2<render::ItemBounds, LightingModelPointer>;
using JobModel = render::Job::ModelI<DrawForward, Inputs>;
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

View file

@ -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()$>

View file

@ -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]

View file

@ -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);

View file

@ -18,9 +18,11 @@
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>none</code></td><td>The entity will not be billboarded.</td></tr>
* <tr><td><code>yaw</code></td><td>The entity will yaw, but not pitch, to face the camera. Its actual rotation will be ignored.</td></tr>
* <tr><td><code>full</code></td><td>The entity will be billboarded to face the camera. Its actual rotation will be ignored.</td></tr>
* <tr><td><code>"none"</code></td><td>The entity will not be billboarded.</td></tr>
* <tr><td><code>"yaw"</code></td><td>The entity will yaw, but not pitch, to face the camera. Its actual rotation will be
* ignored.</td></tr>
* <tr><td><code>"full"</code></td><td>The entity will be billboarded to face the camera. Its actual rotation will be
* ignored.</td></tr>
* </tbody>
* </table>
* @typedef {string} BillboardMode

View file

@ -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();
@ -1147,19 +1148,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 - <em>Not used.</em>
* @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 - <em>Not used.</em>
* @property {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
* @property {boolean} hold - <em>Not used.</em>
* @property {number} firstFrame - The frame the animation should start at.
* @property {number} lastFrame - The frame the animation should stop at.
* @property {boolean} running - <em>Not used.</em>
* @property {number} currentFrame - The current frame being played.
* @property {boolean} startAutomatically - <em>Not used.</em>
* @property {boolean} allowTranslation - <em>Not used.</em>
*/
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) {
QScriptValue obj = engine->newObject();

View file

@ -43,6 +43,27 @@ Q_DECLARE_METATYPE(std::function<QVariant()>);
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);

View file

@ -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);
* }
* }
*

View file

@ -377,6 +377,9 @@ function deleteSendMoneyParticleEffect() {
}
function onUsernameChanged() {
if (ui.checkIsOpen()) {
ui.open(WALLET_QML_SOURCE);
}
}
var MARKETPLACE_QML_PATH = "hifi/commerce/marketplace/Marketplace.qml";

View file

@ -12,7 +12,7 @@
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES,
getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers,
PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers,
PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE, Keyboard
PointerManager, print, Keyboard
*/
controllerDispatcherPlugins = {};
@ -53,6 +53,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
this.blacklist = [];
this.pointerManager = new PointerManager();
this.grabSphereOverlays = [null, null];
this.targetIDs = {};
// a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are
// not set to false (not in use), a module cannot start. When a module is using a slot, that module's name
@ -225,8 +226,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
if (tabletIndex !== -1 && closebyOverlays.indexOf(HMD.tabletID) === -1) {
nearbyOverlays.splice(tabletIndex, 1);
}
if (miniTabletIndex !== -1
&& ((closebyOverlays.indexOf(HMD.miniTabletID) === -1) || h !== HMD.miniTabletHand)) {
if (miniTabletIndex !== -1 &&
((closebyOverlays.indexOf(HMD.miniTabletID) === -1) || h !== HMD.miniTabletHand)) {
nearbyOverlays.splice(miniTabletIndex, 1);
}
}
@ -336,6 +337,23 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
});
}
// also make sure we have the properties from the current module's target
for (var tIDRunningPluginName in _this.runningPluginNames) {
if (_this.runningPluginNames.hasOwnProperty(tIDRunningPluginName)) {
var targetIDs = _this.targetIDs[tIDRunningPluginName];
if (targetIDs) {
for (var k = 0; k < targetIDs.length; k++) {
var targetID = targetIDs[k];
if (!nearbyEntityPropertiesByID[targetID]) {
var targetProps = Entities.getEntityProperties(targetID, DISPATCHER_PROPERTIES);
targetProps.id = targetID;
nearbyEntityPropertiesByID[targetID] = targetProps;
}
}
}
}
}
// bundle up all the data about the current situation
var controllerData = {
triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue],
@ -402,10 +420,23 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Script.beginProfileRange("dispatch.run." + runningPluginName);
}
var runningness = plugin.run(controllerData, deltaTime);
if (DEBUG) {
if (JSON.stringify(_this.targetIDs[runningPluginName]) != JSON.stringify(runningness.targets)) {
print("controllerDispatcher targetIDs[" + runningPluginName + "] = " +
JSON.stringify(runningness.targets));
}
}
_this.targetIDs[runningPluginName] = runningness.targets;
if (!runningness.active) {
// plugin is finished running, for now. remove it from the list
// of running plugins and mark its activity-slots as "not in use"
delete _this.runningPluginNames[runningPluginName];
delete _this.targetIDs[runningPluginName];
if (DEBUG) {
print("controllerDispatcher deleted targetIDs[" + runningPluginName + "]");
}
_this.markSlots(plugin, false);
_this.pointerManager.makePointerInvisible(plugin.parameters.handLaser);
if (DEBUG) {
@ -527,8 +558,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
}
if (action === "tablet") {
var tabletIDs = message.blacklist
? [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] : [];
var tabletIDs = message.blacklist ?
[HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] :
[];
if (message.hand === LEFT_HAND) {
_this.leftBlacklistTabletIDs = tabletIDs;
_this.setLeftBlacklist();

View file

@ -7,10 +7,10 @@
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, print, getControllerJointIndex,
enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther, Messages, makeDispatcherModuleParameters,
enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters,
makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic,
entityIsCloneable, cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode, getGrabbableData,
entityIsEquippable
entityIsCloneable, cloneEntity, DISPATCHER_PROPERTIES, Uuid, isInEditMode, getGrabbableData,
entityIsEquippable, HMD
*/
Script.include("/~/system/libraries/Xform.js");
@ -183,8 +183,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var TRIGGER_OFF_VALUE = 0.1;
var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab
var BUMPER_ON_VALUE = 0.5;
var ATTACHPOINT_MAX_DISTANCE = 3.0;
var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}";
// var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}";
var UNEQUIP_KEY = "u";
@ -200,7 +201,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
indicatorOffset: props.grab.equippableIndicatorOffset
};
} else {
return null
return null;
}
}
@ -231,6 +232,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand";
var joints = avatarSettingsData[hotspot.key];
if (joints) {
// make sure they are reasonable
if (joints[jointName] && joints[jointName][0] &&
Vec3.length(joints[jointName][0]) > ATTACHPOINT_MAX_DISTANCE) {
print("equipEntity -- Warning: rejecting settings attachPoint " + Vec3.length(joints[jointName][0]));
return undefined;
}
return joints[jointName];
}
}
@ -456,6 +463,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
};
this.startEquipEntity = function (controllerData) {
var _this = this;
this.dropGestureReset();
this.clearEquipHaptics();
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
@ -498,17 +507,23 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
if (entityIsCloneable(grabbedProperties)) {
var cloneID = this.cloneHotspot(grabbedProperties, controllerData);
this.targetEntityID = cloneID;
Entities.editEntity(this.targetEntityID, reparentProps);
controllerData.nearbyEntityPropertiesByID[this.targetEntityID] = grabbedProperties;
isClone = true;
} else if (!grabbedProperties.locked) {
Entities.editEntity(this.targetEntityID, reparentProps);
} else {
} else if (grabbedProperties.locked) {
this.grabbedHotspot = null;
this.targetEntityID = null;
return;
}
// HACK -- when
// https://highfidelity.fogbugz.com/f/cases/21767/entity-edits-shortly-after-an-add-often-fail
// is resolved, this can just be an editEntity rather than a setTimeout.
this.editDelayTimeout = Script.setTimeout(function () {
_this.editDelayTimeout = null;
Entities.editEntity(_this.targetEntityID, reparentProps);
}, 100);
// we don't want to send startEquip message until the trigger is released. otherwise,
// guns etc will fire right as they are equipped.
this.shouldSendStart = true;
@ -519,7 +534,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
var _this = this;
var grabEquipCheck = function() {
var args = [_this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(_this.targetEntityID, "startEquip", args);
@ -532,6 +546,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
};
this.endEquipEntity = function () {
if (this.editDelayTimeout) {
Script.clearTimeout(this.editDelayTimeout);
this.editDelayTimeout = null;
}
this.storeAttachPointInSettings();
Entities.editEntity(this.targetEntityID, {
parentID: Uuid.NULL,
@ -595,7 +615,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
equipHotspotBuddy.update(deltaTime, timestamp, controllerData);
// if the potentialHotspot is cloneable, clone it and return it
// if the potentialHotspot os not cloneable and locked return null
// if the potentialHotspot is not cloneable and locked return null
if (potentialEquipHotspot &&
(((this.triggerSmoothedSqueezed() || this.secondarySmoothedSqueezed()) && !this.waitForTriggerRelease) ||
this.messageGrabEntity)) {
@ -603,7 +623,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.targetEntityID = this.grabbedHotspot.entityID;
this.startEquipEntity(controllerData);
this.equipedWithSecondary = this.secondarySmoothedSqueezed();
return makeRunningValues(true, [potentialEquipHotspot.entityID], []);
return makeRunningValues(true, [this.targetEntityID], []);
} else {
return makeRunningValues(false, [], []);
}

View file

@ -615,11 +615,10 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) {
openMarketplace(message.itemId, message.itemEdition);
break;
case 'passphrasePopup_cancelClicked':
case 'needsLogIn_cancelClicked':
// Should/must NOT check for wallet setup.
openMarketplace();
break;
case 'needsLogIn_loginClicked':
case 'marketplace_loginClicked':
openLoginWindow();
break;
case 'disableHmdPreview':

View file

@ -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

View file

@ -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");
}
@ -146,6 +149,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) {

View file

@ -1,62 +0,0 @@
import os
import json
import shutil
import sys
def scriptRelative(*paths):
scriptdir = os.path.dirname(os.path.realpath(sys.argv[0]))
result = os.path.join(scriptdir, *paths)
result = os.path.realpath(result)
result = os.path.normcase(result)
return result
class FrameProcessor:
def __init__(self, filename):
self.filename = filename
dir, name = os.path.split(self.filename)
self.dir = dir
self.ktxDir = os.path.join(self.dir, 'ktx')
os.makedirs(self.ktxDir, exist_ok=True)
self.resDir = scriptRelative("../interface/resources")
if (name.endswith(".json")):
self.name = name[0:-5]
else:
self.name = name
self.filename = self.filename + '.json'
with open(self.filename, 'r') as f:
self.json = json.load(f)
def processKtx(self, texture):
if texture is None: return
if not 'ktxFile' in texture: return
sourceKtx = texture['ktxFile']
if sourceKtx.startswith(':'):
sourceKtx = sourceKtx[1:]
while sourceKtx.startswith('/'):
sourceKtx = sourceKtx[1:]
sourceKtx = os.path.join(self.resDir, sourceKtx)
sourceKtxDir, sourceKtxName = os.path.split(sourceKtx)
destKtx = os.path.join(self.ktxDir, sourceKtxName)
if not os.path.isfile(destKtx):
shutil.copy(sourceKtx, destKtx)
newValue = 'ktx/' + sourceKtxName
texture['ktxFile'] = newValue
def process(self):
for texture in self.json['textures']:
self.processKtx(texture)
with open(self.filename, 'w') as f:
json.dump(self.json, f, indent=2)
fp = FrameProcessor("D:/Frames/20190114_1629.json")
fp.process()
#C:\Users\bdavi\git\hifi\interface\resources\meshes

View file

@ -36,7 +36,10 @@ class FrameProcessor:
if not 'ktxFile' in texture: return
sourceKtx = texture['ktxFile']
if sourceKtx.startswith(':'):
sourceKtx = os.path.join(self.resDir, sourceKtx[3:])
sourceKtx = sourceKtx[1:]
while sourceKtx.startswith('/'):
sourceKtx = sourceKtx[1:]
sourceKtx = os.path.join(self.resDir, sourceKtx)
sourceKtxDir, sourceKtxName = os.path.split(sourceKtx)
destKtx = os.path.join(self.ktxDir, sourceKtxName)
if not os.path.isfile(destKtx):
@ -52,7 +55,7 @@ class FrameProcessor:
with open(self.filename, 'w') as f:
json.dump(self.json, f, indent=2)
fp = FrameProcessor("D:/Frames/20190110_1635.json")
fp = FrameProcessor("D:/Frames/20190112_1647.json")
fp.process()

View file

@ -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) {

View file

@ -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.