mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 18:44:00 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into tablet-ui-edit-js
This commit is contained in:
commit
767d6e43fb
20 changed files with 499 additions and 142 deletions
|
@ -43,7 +43,6 @@
|
|||
#include <WebSocketServerClass.h>
|
||||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
#include "entities/AssignmentParentFinder.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "AbstractAudioInterface.h"
|
||||
|
@ -88,9 +87,9 @@ void Agent::playAvatarSound(SharedSoundPointer sound) {
|
|||
QMetaObject::invokeMethod(this, "playAvatarSound", Q_ARG(SharedSoundPointer, sound));
|
||||
return;
|
||||
} else {
|
||||
// TODO: seems to add occasional artifact in tests. I believe it is
|
||||
// TODO: seems to add occasional artifact in tests. I believe it is
|
||||
// correct to do this, but need to figure out for sure, so commenting this
|
||||
// out until I verify.
|
||||
// out until I verify.
|
||||
// _numAvatarSoundSentBytes = 0;
|
||||
setAvatarSound(sound);
|
||||
}
|
||||
|
@ -105,7 +104,7 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
|
|||
if (message->getSize() > statsMessageLength) {
|
||||
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
||||
int piggyBackedSizeWithHeader = message->getSize() - statsMessageLength;
|
||||
|
||||
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
||||
memcpy(buffer.get(), message->getRawMessage() + statsMessageLength, piggyBackedSizeWithHeader);
|
||||
|
||||
|
@ -284,7 +283,7 @@ void Agent::selectAudioFormat(const QString& selectedCodecName) {
|
|||
for (auto& plugin : codecPlugins) {
|
||||
if (_selectedCodecName == plugin->getName()) {
|
||||
_codec = plugin;
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
|
||||
qDebug() << "Selected Codec Plugin:" << _codec.get();
|
||||
break;
|
||||
|
@ -380,6 +379,8 @@ void Agent::executeScript() {
|
|||
audioTransform.setTranslation(scriptedAvatar->getPosition());
|
||||
audioTransform.setRotation(headOrientation);
|
||||
|
||||
computeLoudness(&audio, scriptedAvatar);
|
||||
|
||||
QByteArray encodedBuffer;
|
||||
if (_encoder) {
|
||||
_encoder->encode(audio, encodedBuffer);
|
||||
|
@ -424,16 +425,16 @@ void Agent::executeScript() {
|
|||
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
|
||||
|
||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||
|
||||
|
||||
// 100Hz timer for audio
|
||||
AvatarAudioTimer* audioTimerWorker = new AvatarAudioTimer();
|
||||
audioTimerWorker->moveToThread(&_avatarAudioTimerThread);
|
||||
connect(audioTimerWorker, &AvatarAudioTimer::avatarTick, this, &Agent::processAgentAvatarAudio);
|
||||
connect(this, &Agent::startAvatarAudioTimer, audioTimerWorker, &AvatarAudioTimer::start);
|
||||
connect(this, &Agent::stopAvatarAudioTimer, audioTimerWorker, &AvatarAudioTimer::stop);
|
||||
connect(&_avatarAudioTimerThread, &QThread::finished, audioTimerWorker, &QObject::deleteLater);
|
||||
connect(&_avatarAudioTimerThread, &QThread::finished, audioTimerWorker, &QObject::deleteLater);
|
||||
_avatarAudioTimerThread.start();
|
||||
|
||||
|
||||
// Agents should run at 45hz
|
||||
static const int AVATAR_DATA_HZ = 45;
|
||||
static const int AVATAR_DATA_IN_MSECS = MSECS_PER_SECOND / AVATAR_DATA_HZ;
|
||||
|
@ -456,14 +457,14 @@ QUuid Agent::getSessionUUID() const {
|
|||
return DependencyManager::get<NodeList>()->getSessionUUID();
|
||||
}
|
||||
|
||||
void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
||||
void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
||||
// this must happen on Agent's main thread
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setIsListeningToAudioStream", Q_ARG(bool, isListeningToAudioStream));
|
||||
return;
|
||||
}
|
||||
if (_isListeningToAudioStream) {
|
||||
// have to tell just the audio mixer to KillAvatar.
|
||||
// have to tell just the audio mixer to KillAvatar.
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode(
|
||||
|
@ -479,7 +480,7 @@ void Agent::setIsListeningToAudioStream(bool isListeningToAudioStream) {
|
|||
});
|
||||
|
||||
}
|
||||
_isListeningToAudioStream = isListeningToAudioStream;
|
||||
_isListeningToAudioStream = isListeningToAudioStream;
|
||||
}
|
||||
|
||||
void Agent::setIsAvatar(bool isAvatar) {
|
||||
|
@ -560,6 +561,7 @@ void Agent::processAgentAvatar() {
|
|||
nodeList->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) {
|
||||
_flushEncoder = false;
|
||||
static const QByteArray zeros(AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, 0);
|
||||
|
@ -570,6 +572,22 @@ void Agent::encodeFrameOfZeros(QByteArray& encodedZeros) {
|
|||
}
|
||||
}
|
||||
|
||||
void Agent::computeLoudness(const QByteArray* decodedBuffer, QSharedPointer<ScriptableAvatar> scriptableAvatar) {
|
||||
float loudness = 0.0f;
|
||||
if (decodedBuffer) {
|
||||
auto soundData = reinterpret_cast<const int16_t*>(decodedBuffer->constData());
|
||||
int numFrames = decodedBuffer->size() / sizeof(int16_t);
|
||||
// now iterate and come up with average
|
||||
if (numFrames > 0) {
|
||||
for(int i = 0; i < numFrames; i++) {
|
||||
loudness += (float) std::abs(soundData[i]);
|
||||
}
|
||||
loudness /= numFrames;
|
||||
}
|
||||
}
|
||||
scriptableAvatar->setAudioLoudness(loudness);
|
||||
}
|
||||
|
||||
void Agent::processAgentAvatarAudio() {
|
||||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
bool isPlayingRecording = recordingInterface->isPlaying();
|
||||
|
@ -619,6 +637,7 @@ void Agent::processAgentAvatarAudio() {
|
|||
audioPacket->seek(sizeof(quint16));
|
||||
|
||||
if (silentFrame) {
|
||||
|
||||
if (!_isListeningToAudioStream) {
|
||||
// if we have a silent frame and we're not listening then just send nothing and break out of here
|
||||
return;
|
||||
|
@ -626,7 +645,7 @@ void Agent::processAgentAvatarAudio() {
|
|||
|
||||
// write the codec
|
||||
audioPacket->writeString(_selectedCodecName);
|
||||
|
||||
|
||||
// write the number of silent samples so the audio-mixer can uphold timing
|
||||
audioPacket->writePrimitive(numAvailableSamples);
|
||||
|
||||
|
@ -636,8 +655,11 @@ void Agent::processAgentAvatarAudio() {
|
|||
audioPacket->writePrimitive(headOrientation);
|
||||
audioPacket->writePrimitive(scriptedAvatar->getPosition());
|
||||
audioPacket->writePrimitive(glm::vec3(0));
|
||||
|
||||
// no matter what, the loudness should be set to 0
|
||||
computeLoudness(nullptr, scriptedAvatar);
|
||||
} else if (nextSoundOutput) {
|
||||
|
||||
|
||||
// write the codec
|
||||
audioPacket->writeString(_selectedCodecName);
|
||||
|
||||
|
@ -654,6 +676,8 @@ void Agent::processAgentAvatarAudio() {
|
|||
QByteArray encodedBuffer;
|
||||
if (_flushEncoder) {
|
||||
encodeFrameOfZeros(encodedBuffer);
|
||||
// loudness is 0
|
||||
computeLoudness(nullptr, scriptedAvatar);
|
||||
} else {
|
||||
QByteArray decodedBuffer(reinterpret_cast<const char*>(nextSoundOutput), numAvailableSamples*sizeof(int16_t));
|
||||
if (_encoder) {
|
||||
|
@ -662,10 +686,15 @@ void Agent::processAgentAvatarAudio() {
|
|||
} else {
|
||||
encodedBuffer = decodedBuffer;
|
||||
}
|
||||
computeLoudness(&decodedBuffer, scriptedAvatar);
|
||||
}
|
||||
audioPacket->write(encodedBuffer.constData(), encodedBuffer.size());
|
||||
}
|
||||
|
||||
// we should never have both nextSoundOutput being null and silentFrame being false, but lets
|
||||
// assert on it in case things above change in a bad way
|
||||
assert(nextSoundOutput || silentFrame);
|
||||
|
||||
// write audio packet to AudioMixer nodes
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([this, &nodeList, &audioPacket](const SharedNodePointer& node) {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
#include "MixedAudioStream.h"
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
||||
class Agent : public ThreadedAssignment {
|
||||
Q_OBJECT
|
||||
|
@ -68,10 +69,10 @@ private slots:
|
|||
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void nodeActivated(SharedNodePointer activatedNode);
|
||||
|
||||
|
||||
void processAgentAvatar();
|
||||
void processAgentAvatarAudio();
|
||||
|
||||
|
@ -82,6 +83,7 @@ private:
|
|||
void negotiateAudioFormat();
|
||||
void selectAudioFormat(const QString& selectedCodecName);
|
||||
void encodeFrameOfZeros(QByteArray& encodedZeros);
|
||||
void computeLoudness(const QByteArray* decodedBuffer, QSharedPointer<ScriptableAvatar>);
|
||||
|
||||
std::unique_ptr<ScriptEngine> _scriptEngine;
|
||||
EntityEditPacketSender _entityEditSender;
|
||||
|
@ -103,10 +105,10 @@ private:
|
|||
bool _isAvatar = false;
|
||||
QTimer* _avatarIdentityTimer = nullptr;
|
||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||
|
||||
|
||||
CodecPluginPointer _codec;
|
||||
QString _selectedCodecName;
|
||||
Encoder* _encoder { nullptr };
|
||||
Encoder* _encoder { nullptr };
|
||||
QThread _avatarAudioTimerThread;
|
||||
bool _flushEncoder { false };
|
||||
};
|
||||
|
|
|
@ -549,6 +549,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
|||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false;
|
||||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
|
||||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
|
||||
QApplication(argc, argv),
|
||||
|
@ -572,6 +573,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR),
|
||||
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
|
||||
_tabletVisibleToOthersSetting("tabletVisibleToOthers", DEFAULT_TABLET_VISIBLE_TO_OTHERS),
|
||||
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
|
||||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
|
@ -2362,6 +2364,10 @@ void Application::setTabletVisibleToOthersSetting(bool value) {
|
|||
updateSystemTabletMode();
|
||||
}
|
||||
|
||||
void Application::setPreferAvatarFingerOverStylus(bool value) {
|
||||
_preferAvatarFingerOverStylusSetting.set(value);
|
||||
}
|
||||
|
||||
void Application::setSettingConstrainToolbarPosition(bool setting) {
|
||||
_constrainToolbarPosition.set(setting);
|
||||
DependencyManager::get<OffscreenUi>()->setConstrainToolbarToCenterX(setting);
|
||||
|
|
|
@ -220,6 +220,8 @@ public:
|
|||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
bool getTabletVisibleToOthersSetting() { return _tabletVisibleToOthersSetting.get(); }
|
||||
void setTabletVisibleToOthersSetting(bool value);
|
||||
bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
|
||||
void setPreferAvatarFingerOverStylus(bool value);
|
||||
|
||||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
void setSettingConstrainToolbarPosition(bool setting);
|
||||
|
@ -565,6 +567,7 @@ private:
|
|||
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _tabletVisibleToOthersSetting;
|
||||
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
|
||||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
|
||||
float _scaleMirror;
|
||||
|
|
|
@ -110,13 +110,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MAC)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
#else
|
||||
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||
#endif
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
}
|
||||
|
||||
// Once computed the cluster matrices, update the buffer(s)
|
||||
|
@ -149,13 +143,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
|
||||
jointMatrix = cauterizeMatrix;
|
||||
}
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MAC)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
#else
|
||||
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||
#endif
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
}
|
||||
|
||||
if (!_cauterizeBoneSet.empty() && (state.clusterMatrices.size() > 1)) {
|
||||
|
|
|
@ -60,13 +60,7 @@ void SoftAttachmentModel::updateClusterMatrices() {
|
|||
} else {
|
||||
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
}
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MAC)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
#else
|
||||
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||
#endif
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
}
|
||||
|
||||
// Once computed the cluster matrices, update the buffer(s)
|
||||
|
|
|
@ -107,6 +107,12 @@ void setupPreferences() {
|
|||
auto setter = [](bool value) { qApp->setTabletVisibleToOthersSetting(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Tablet Is Visible To Others", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
|
||||
}
|
||||
|
||||
// Snapshots
|
||||
static const QString SNAPSHOTS { "Snapshots" };
|
||||
{
|
||||
|
|
|
@ -198,18 +198,27 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
_webSurface->getRootItem()->setProperty("scriptURL", _scriptURL);
|
||||
currentContext->makeCurrent(currentSurface);
|
||||
|
||||
auto selfOverlayID = getOverlayID();
|
||||
std::weak_ptr<Web3DOverlay> weakSelf = std::dynamic_pointer_cast<Web3DOverlay>(qApp->getOverlays().getOverlay(selfOverlayID));
|
||||
auto forwardPointerEvent = [=](OverlayID overlayID, const PointerEvent& event) {
|
||||
if (overlayID == getOverlayID()) {
|
||||
handlePointerEvent(event);
|
||||
auto self = weakSelf.lock();
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
if (overlayID == selfOverlayID) {
|
||||
self->handlePointerEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
_mousePressConnection = connect(&(qApp->getOverlays()), &Overlays::mousePressOnOverlay, forwardPointerEvent);
|
||||
_mouseReleaseConnection = connect(&(qApp->getOverlays()), &Overlays::mouseReleaseOnOverlay, forwardPointerEvent);
|
||||
_mouseMoveConnection = connect(&(qApp->getOverlays()), &Overlays::mouseMoveOnOverlay, forwardPointerEvent);
|
||||
_hoverLeaveConnection = connect(&(qApp->getOverlays()), &Overlays::hoverLeaveOverlay,
|
||||
[=](OverlayID overlayID, const PointerEvent& event) {
|
||||
if (this->_pressed && this->getOverlayID() == overlayID) {
|
||||
_mousePressConnection = connect(&(qApp->getOverlays()), &Overlays::mousePressOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
|
||||
_mouseReleaseConnection = connect(&(qApp->getOverlays()), &Overlays::mouseReleaseOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
|
||||
_mouseMoveConnection = connect(&(qApp->getOverlays()), &Overlays::mouseMoveOnOverlay, this, forwardPointerEvent, Qt::DirectConnection);
|
||||
_hoverLeaveConnection = connect(&(qApp->getOverlays()), &Overlays::hoverLeaveOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) {
|
||||
auto self = weakSelf.lock();
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
if (self->_pressed && overlayID == selfOverlayID) {
|
||||
// If the user mouses off the overlay while the button is down, simulate a touch end.
|
||||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
|
@ -222,12 +231,12 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
touchPoints.push_back(point);
|
||||
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased,
|
||||
touchPoints);
|
||||
touchEvent->setWindow(_webSurface->getWindow());
|
||||
touchEvent->setWindow(self->_webSurface->getWindow());
|
||||
touchEvent->setDevice(&_touchDevice);
|
||||
touchEvent->setTarget(_webSurface->getRootItem());
|
||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||
touchEvent->setTarget(self->_webSurface->getRootItem());
|
||||
QCoreApplication::postEvent(self->_webSurface->getWindow(), touchEvent);
|
||||
}
|
||||
});
|
||||
}, Qt::DirectConnection);
|
||||
|
||||
_emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
|
||||
_webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
|
||||
|
|
|
@ -50,15 +50,9 @@ glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
|
|||
}
|
||||
|
||||
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MAC)
|
||||
glm::mat4 result;
|
||||
glm::mat4 lhsMat = *this;
|
||||
glm::mat4 rhsMat = rhs;
|
||||
glm_mat4_mul((glm_vec4*)&lhsMat, (glm_vec4*)&rhsMat, (glm_vec4*)&result);
|
||||
glm_mat4u_mul(*this, rhs, result);
|
||||
return AnimPose(result);
|
||||
#else
|
||||
return AnimPose(static_cast<glm::mat4>(*this) * static_cast<glm::mat4>(rhs));
|
||||
#endif
|
||||
}
|
||||
|
||||
AnimPose AnimPose::inverse() const {
|
||||
|
|
|
@ -1178,13 +1178,7 @@ void Model::updateClusterMatrices() {
|
|||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
#if (GLM_ARCH & GLM_ARCH_SSE2) && !(defined Q_OS_MAC)
|
||||
glm::mat4 out, inverseBindMatrix = cluster.inverseBindMatrix;
|
||||
glm_mat4_mul((glm_vec4*)&jointMatrix, (glm_vec4*)&inverseBindMatrix, (glm_vec4*)&out);
|
||||
state.clusterMatrices[j] = out;
|
||||
#else
|
||||
state.clusterMatrices[j] = jointMatrix * cluster.inverseBindMatrix;
|
||||
#endif
|
||||
glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]);
|
||||
}
|
||||
|
||||
// Once computed the cluster matrices, update the buffer(s)
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
/**jsdoc
|
||||
* A Quaternion
|
||||
*
|
||||
* @typedef Quat
|
||||
* @property {float} x imaginary component i.
|
||||
* @property {float} y imaginary component j.
|
||||
* @property {float} z imaginary component k.
|
||||
* @property {float} w real component.
|
||||
*/
|
||||
|
||||
/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API
|
||||
class Quat : public QObject {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <AudioConstants.h>
|
||||
#include <AudioEffectOptions.h>
|
||||
#include <AvatarData.h>
|
||||
#include <DebugDraw.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
|
@ -630,6 +631,8 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
registerGlobalObject("Assets", &_assetScriptingInterface);
|
||||
registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
|
||||
|
||||
registerGlobalObject("DebugDraw", &DebugDraw::getInstance());
|
||||
}
|
||||
|
||||
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
||||
|
|
|
@ -37,6 +37,15 @@
|
|||
* @property {float} z Z-coordinate of the vector.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* A 4-dimensional vector.
|
||||
*
|
||||
* @typedef Vec4
|
||||
* @property {float} x X-coordinate of the vector.
|
||||
* @property {float} y Y-coordinate of the vector.
|
||||
* @property {float} z Z-coordinate of the vector.
|
||||
* @property {float} w W-coordinate of the vector.
|
||||
*/
|
||||
|
||||
/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
|
||||
class Vec3 : public QObject {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "DebugDraw.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
using Lock = std::unique_lock<std::mutex>;
|
||||
|
||||
DebugDraw& DebugDraw::getInstance() {
|
||||
static DebugDraw* instance = globalInstance<DebugDraw>("com.highfidelity.DebugDraw");
|
||||
return *instance;
|
||||
|
@ -25,22 +27,50 @@ DebugDraw::~DebugDraw() {
|
|||
|
||||
// world space line, drawn only once
|
||||
void DebugDraw::drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color) {
|
||||
Lock lock(_mapMutex);
|
||||
_rays.push_back(Ray(start, end, color));
|
||||
}
|
||||
|
||||
void DebugDraw::addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
|
||||
void DebugDraw::addMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
|
||||
Lock lock(_mapMutex);
|
||||
_markers[key] = MarkerInfo(rotation, position, color);
|
||||
}
|
||||
|
||||
void DebugDraw::removeMarker(const std::string& key) {
|
||||
void DebugDraw::removeMarker(const QString& key) {
|
||||
Lock lock(_mapMutex);
|
||||
_markers.erase(key);
|
||||
}
|
||||
|
||||
void DebugDraw::addMyAvatarMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
|
||||
void DebugDraw::addMyAvatarMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color) {
|
||||
Lock lock(_mapMutex);
|
||||
_myAvatarMarkers[key] = MarkerInfo(rotation, position, color);
|
||||
}
|
||||
|
||||
void DebugDraw::removeMyAvatarMarker(const std::string& key) {
|
||||
void DebugDraw::removeMyAvatarMarker(const QString& key) {
|
||||
Lock lock(_mapMutex);
|
||||
_myAvatarMarkers.erase(key);
|
||||
}
|
||||
|
||||
//
|
||||
// accessors used by renderer
|
||||
//
|
||||
|
||||
DebugDraw::MarkerMap DebugDraw::getMarkerMap() const {
|
||||
Lock lock(_mapMutex);
|
||||
return _markers;
|
||||
}
|
||||
|
||||
DebugDraw::MarkerMap DebugDraw::getMyAvatarMarkerMap() const {
|
||||
Lock lock(_mapMutex);
|
||||
return _myAvatarMarkers;
|
||||
}
|
||||
|
||||
DebugDraw::Rays DebugDraw::getRays() const {
|
||||
Lock lock(_mapMutex);
|
||||
return _rays;
|
||||
}
|
||||
|
||||
void DebugDraw::clearRays() {
|
||||
Lock lock(_mapMutex);
|
||||
_rays.clear();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef hifi_DebugDraw_h
|
||||
#define hifi_DebugDraw_h
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
|
@ -17,26 +18,69 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
class DebugDraw {
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
/**jsdoc
|
||||
* Helper functions to render ephemeral debug markers and lines.
|
||||
* DebugDraw markers and lines are only visible locally, they are not visible by other users.
|
||||
* @namespace DebugDraw
|
||||
*/
|
||||
class DebugDraw : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static DebugDraw& getInstance();
|
||||
|
||||
DebugDraw();
|
||||
~DebugDraw();
|
||||
|
||||
// world space line, drawn only once
|
||||
void drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color);
|
||||
/**jsdoc
|
||||
* Draws a line in world space, but it will only be visible for a single frame.
|
||||
* @function DebugDraw.drawRay
|
||||
* @param {Vec3} start - start position of line in world space.
|
||||
* @param {Vec3} end - end position of line in world space.
|
||||
* @param {Vec4} color - color of line, each component should be in the zero to one range. x = red, y = blue, z = green, w = alpha.
|
||||
*/
|
||||
Q_INVOKABLE void drawRay(const glm::vec3& start, const glm::vec3& end, const glm::vec4& color);
|
||||
|
||||
// world space maker, marker drawn every frame until it is removed.
|
||||
void addMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
|
||||
void removeMarker(const std::string& key);
|
||||
/**jsdoc
|
||||
* Adds a debug marker to the world. This marker will be drawn every frame until it is removed with DebugDraw.removeMarker.
|
||||
* This can be called repeatedly to change the position of the marker.
|
||||
* @function DebugDraw.addMarker
|
||||
* @param {string} key - name to uniquely identify this marker, later used for DebugDraw.removeMarker.
|
||||
* @param {Quat} rotation - start position of line in world space.
|
||||
* @param {Vec3} position - position of the marker in world space.
|
||||
* @param {Vec4} color - color of the marker.
|
||||
*/
|
||||
Q_INVOKABLE void addMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
|
||||
|
||||
// myAvatar relative marker, maker is drawn every frame until it is removed.
|
||||
void addMyAvatarMarker(const std::string& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
|
||||
void removeMyAvatarMarker(const std::string& key);
|
||||
/**jsdoc
|
||||
* Removes debug marker from the world. Once a marker is removed, it will no longer be visible.
|
||||
* @function DebugDraw.removeMarker
|
||||
* @param {string} key - name of marker to remove.
|
||||
*/
|
||||
Q_INVOKABLE void removeMarker(const QString& key);
|
||||
|
||||
/**jsdoc
|
||||
* Adds a debug marker to the world, this marker will be drawn every frame until it is removed with DebugDraw.removeMyAvatarMarker.
|
||||
* This can be called repeatedly to change the position of the marker.
|
||||
* @function DebugDraw.addMyAvatarMarker
|
||||
* @param {string} key - name to uniquely identify this marker, later used for DebugDraw.removeMyAvatarMarker.
|
||||
* @param {Quat} rotation - start position of line in avatar space.
|
||||
* @param {Vec3} position - position of the marker in avatar space.
|
||||
* @param {Vec4} color - color of the marker.
|
||||
*/
|
||||
Q_INVOKABLE void addMyAvatarMarker(const QString& key, const glm::quat& rotation, const glm::vec3& position, const glm::vec4& color);
|
||||
|
||||
/**jsdoc
|
||||
* Removes debug marker from the world. Once a marker is removed, it will no longer be visible
|
||||
* @function DebugDraw.removeMyAvatarMarker
|
||||
* @param {string} key - name of marker to remove.
|
||||
*/
|
||||
Q_INVOKABLE void removeMyAvatarMarker(const QString& key);
|
||||
|
||||
using MarkerInfo = std::tuple<glm::quat, glm::vec3, glm::vec4>;
|
||||
using MarkerMap = std::unordered_map<std::string, MarkerInfo>;
|
||||
using MarkerMap = std::map<QString, MarkerInfo>;
|
||||
using Ray = std::tuple<glm::vec3, glm::vec3, glm::vec4>;
|
||||
using Rays = std::vector<Ray>;
|
||||
|
||||
|
@ -44,16 +88,17 @@ public:
|
|||
// accessors used by renderer
|
||||
//
|
||||
|
||||
const MarkerMap& getMarkerMap() const { return _markers; }
|
||||
const MarkerMap& getMyAvatarMarkerMap() const { return _myAvatarMarkers; }
|
||||
MarkerMap getMarkerMap() const;
|
||||
MarkerMap getMyAvatarMarkerMap() const;
|
||||
void updateMyAvatarPos(const glm::vec3& pos) { _myAvatarPos = pos; }
|
||||
const glm::vec3& getMyAvatarPos() const { return _myAvatarPos; }
|
||||
void updateMyAvatarRot(const glm::quat& rot) { _myAvatarRot = rot; }
|
||||
const glm::quat& getMyAvatarRot() const { return _myAvatarRot; }
|
||||
const Rays getRays() const { return _rays; }
|
||||
void clearRays() { _rays.clear(); }
|
||||
Rays getRays() const;
|
||||
void clearRays();
|
||||
|
||||
protected:
|
||||
mutable std::mutex _mapMutex;
|
||||
MarkerMap _markers;
|
||||
MarkerMap _myAvatarMarkers;
|
||||
glm::quat _myAvatarRot;
|
||||
|
|
|
@ -245,4 +245,53 @@ inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value
|
|||
|
||||
glm::mat4 orthoInverse(const glm::mat4& m);
|
||||
|
||||
//
|
||||
// Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128
|
||||
//
|
||||
inline void glm_mat4u_mul(const glm::mat4& m1, const glm::mat4& m2, glm::mat4& r) {
|
||||
|
||||
#if GLM_ARCH & GLM_ARCH_SSE2_BIT
|
||||
__m128 u0 = _mm_loadu_ps((float*)&m1[0][0]);
|
||||
__m128 u1 = _mm_loadu_ps((float*)&m1[1][0]);
|
||||
__m128 u2 = _mm_loadu_ps((float*)&m1[2][0]);
|
||||
__m128 u3 = _mm_loadu_ps((float*)&m1[3][0]);
|
||||
|
||||
__m128 v0 = _mm_loadu_ps((float*)&m2[0][0]);
|
||||
__m128 v1 = _mm_loadu_ps((float*)&m2[1][0]);
|
||||
__m128 v2 = _mm_loadu_ps((float*)&m2[2][0]);
|
||||
__m128 v3 = _mm_loadu_ps((float*)&m2[3][0]);
|
||||
|
||||
__m128 t0 = _mm_mul_ps(_mm_shuffle_ps(v0, v0, _MM_SHUFFLE(0,0,0,0)), u0);
|
||||
__m128 t1 = _mm_mul_ps(_mm_shuffle_ps(v0, v0, _MM_SHUFFLE(1,1,1,1)), u1);
|
||||
__m128 t2 = _mm_mul_ps(_mm_shuffle_ps(v0, v0, _MM_SHUFFLE(2,2,2,2)), u2);
|
||||
__m128 t3 = _mm_mul_ps(_mm_shuffle_ps(v0, v0, _MM_SHUFFLE(3,3,3,3)), u3);
|
||||
v0 = _mm_add_ps(_mm_add_ps(t0, t1), _mm_add_ps(t2, t3));
|
||||
|
||||
t0 = _mm_mul_ps(_mm_shuffle_ps(v1, v1, _MM_SHUFFLE(0,0,0,0)), u0);
|
||||
t1 = _mm_mul_ps(_mm_shuffle_ps(v1, v1, _MM_SHUFFLE(1,1,1,1)), u1);
|
||||
t2 = _mm_mul_ps(_mm_shuffle_ps(v1, v1, _MM_SHUFFLE(2,2,2,2)), u2);
|
||||
t3 = _mm_mul_ps(_mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3,3,3,3)), u3);
|
||||
v1 = _mm_add_ps(_mm_add_ps(t0, t1), _mm_add_ps(t2, t3));
|
||||
|
||||
t0 = _mm_mul_ps(_mm_shuffle_ps(v2, v2, _MM_SHUFFLE(0,0,0,0)), u0);
|
||||
t1 = _mm_mul_ps(_mm_shuffle_ps(v2, v2, _MM_SHUFFLE(1,1,1,1)), u1);
|
||||
t2 = _mm_mul_ps(_mm_shuffle_ps(v2, v2, _MM_SHUFFLE(2,2,2,2)), u2);
|
||||
t3 = _mm_mul_ps(_mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3,3,3,3)), u3);
|
||||
v2 = _mm_add_ps(_mm_add_ps(t0, t1), _mm_add_ps(t2, t3));
|
||||
|
||||
t0 = _mm_mul_ps(_mm_shuffle_ps(v3, v3, _MM_SHUFFLE(0,0,0,0)), u0);
|
||||
t1 = _mm_mul_ps(_mm_shuffle_ps(v3, v3, _MM_SHUFFLE(1,1,1,1)), u1);
|
||||
t2 = _mm_mul_ps(_mm_shuffle_ps(v3, v3, _MM_SHUFFLE(2,2,2,2)), u2);
|
||||
t3 = _mm_mul_ps(_mm_shuffle_ps(v3, v3, _MM_SHUFFLE(3,3,3,3)), u3);
|
||||
v3 = _mm_add_ps(_mm_add_ps(t0, t1), _mm_add_ps(t2, t3));
|
||||
|
||||
_mm_storeu_ps((float*)&r[0][0], v0);
|
||||
_mm_storeu_ps((float*)&r[1][0], v1);
|
||||
_mm_storeu_ps((float*)&r[2][0], v2);
|
||||
_mm_storeu_ps((float*)&r[3][0], v3);
|
||||
#else
|
||||
r = m1 * m2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // hifi_GLMHelpers_h
|
||||
|
|
|
@ -74,6 +74,10 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe
|
|||
var WEB_TOUCH_TOO_CLOSE = 0.03; // if the stylus is pushed far though the web surface, don't consider it touching
|
||||
var WEB_TOUCH_Y_TOUCH_DEADZONE_SIZE = 0.01;
|
||||
|
||||
var FINGER_TOUCH_Y_OFFSET = -0.02;
|
||||
var FINGER_TOUCH_MIN = -0.01 - FINGER_TOUCH_Y_OFFSET;
|
||||
var FINGER_TOUCH_MAX = 0.01 - FINGER_TOUCH_Y_OFFSET;
|
||||
|
||||
//
|
||||
// distant manipulation
|
||||
//
|
||||
|
@ -258,19 +262,51 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = {
|
|||
updateMethod: "farTrigger"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_ENTITY_STYLUS_TOUCHING] = {
|
||||
name: "entityTouching",
|
||||
name: "entityStylusTouching",
|
||||
enterMethod: "entityTouchingEnter",
|
||||
exitMethod: "entityTouchingExit",
|
||||
updateMethod: "entityTouching"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_ENTITY_LASER_TOUCHING] = {
|
||||
name: "entityLaserTouching",
|
||||
enterMethod: "entityTouchingEnter",
|
||||
exitMethod: "entityTouchingExit",
|
||||
updateMethod: "entityTouching"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_ENTITY_LASER_TOUCHING] = CONTROLLER_STATE_MACHINE[STATE_ENTITY_STYLUS_TOUCHING];
|
||||
CONTROLLER_STATE_MACHINE[STATE_OVERLAY_STYLUS_TOUCHING] = {
|
||||
name: "overlayTouching",
|
||||
name: "overlayStylusTouching",
|
||||
enterMethod: "overlayTouchingEnter",
|
||||
exitMethod: "overlayTouchingExit",
|
||||
updateMethod: "overlayTouching"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_OVERLAY_LASER_TOUCHING] = CONTROLLER_STATE_MACHINE[STATE_OVERLAY_STYLUS_TOUCHING];
|
||||
CONTROLLER_STATE_MACHINE[STATE_OVERLAY_LASER_TOUCHING] = {
|
||||
name: "overlayLaserTouching",
|
||||
enterMethod: "overlayTouchingEnter",
|
||||
exitMethod: "overlayTouchingExit",
|
||||
updateMethod: "overlayTouching"
|
||||
};
|
||||
|
||||
function getFingerWorldLocation(hand) {
|
||||
var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4";
|
||||
|
||||
var fingerJointIndex = MyAvatar.getJointIndex(fingerJointName);
|
||||
var fingerPosition = MyAvatar.getAbsoluteJointTranslationInObjectFrame(fingerJointIndex);
|
||||
var fingerRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(fingerJointIndex);
|
||||
var worldFingerRotation = Quat.multiply(MyAvatar.orientation, fingerRotation);
|
||||
var worldFingerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, fingerPosition));
|
||||
|
||||
// local y offset.
|
||||
var localYOffset = Vec3.multiplyQbyV(worldFingerRotation, {x: 0, y: FINGER_TOUCH_Y_OFFSET, z: 0});
|
||||
|
||||
var offsetWorldFingerPosition = Vec3.sum(worldFingerPosition, localYOffset);
|
||||
|
||||
return {
|
||||
position: offsetWorldFingerPosition,
|
||||
orientation: worldFingerRotation,
|
||||
rotation: worldFingerRotation,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
// Object assign polyfill
|
||||
if (typeof Object.assign != 'function') {
|
||||
|
@ -374,6 +410,7 @@ function handLaserIntersectItem(position, rotation, start) {
|
|||
direction: rayDirection,
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
return intersectionInfo;
|
||||
} else {
|
||||
// entity has been destroyed? or is no longer in cache
|
||||
|
@ -440,16 +477,18 @@ function entityIsGrabbedByOther(entityID) {
|
|||
var actionID = actionIDs[actionIndex];
|
||||
var actionArguments = Entities.getActionArguments(entityID, actionID);
|
||||
var tag = actionArguments.tag;
|
||||
if (tag == getTag()) {
|
||||
if (tag === getTag()) {
|
||||
// we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
|
||||
continue;
|
||||
}
|
||||
if (tag.slice(0, 5) == "grab-") {
|
||||
var GRAB_PREFIX_LENGTH = 5;
|
||||
var UUID_LENGTH = 38;
|
||||
if (tag && tag.slice(0, GRAB_PREFIX_LENGTH) == "grab-") {
|
||||
// we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
|
||||
return true;
|
||||
return tag.slice(GRAB_PREFIX_LENGTH, GRAB_PREFIX_LENGTH + UUID_LENGTH - 1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
function propsArePhysical(props) {
|
||||
|
@ -824,6 +863,9 @@ function MyController(hand) {
|
|||
// for visualizations
|
||||
this.overlayLine = null;
|
||||
this.searchSphere = null;
|
||||
this.otherGrabbingLine = null;
|
||||
|
||||
this.otherGrabbingUUID = null;
|
||||
|
||||
this.waitForTriggerRelease = false;
|
||||
|
||||
|
@ -845,6 +887,8 @@ function MyController(hand) {
|
|||
this.tabletStabbedPos2D = null;
|
||||
this.tabletStabbedPos3D = null;
|
||||
|
||||
this.useFingerInsteadOfStylus = false;
|
||||
|
||||
var _this = this;
|
||||
|
||||
var suppressedIn2D = [STATE_OFF, STATE_SEARCHING];
|
||||
|
@ -858,10 +902,22 @@ function MyController(hand) {
|
|||
this.updateSmoothedTrigger();
|
||||
this.maybeScaleMyAvatar();
|
||||
|
||||
var DEFAULT_USE_FINGER_AS_STYLUS = false;
|
||||
var USE_FINGER_AS_STYLUS = Settings.getValue("preferAvatarFingerOverStylus");
|
||||
if (USE_FINGER_AS_STYLUS === "") {
|
||||
USE_FINGER_AS_STYLUS = DEFAULT_USE_FINGER_AS_STYLUS;
|
||||
}
|
||||
if (USE_FINGER_AS_STYLUS && MyAvatar.getJointIndex("LeftHandIndex4") !== -1) {
|
||||
this.useFingerInsteadOfStylus = true;
|
||||
} else {
|
||||
this.useFingerInsteadOfStylus = false;
|
||||
}
|
||||
|
||||
if (this.ignoreInput()) {
|
||||
|
||||
// Most hand input is disabled, because we are interacting with the 2d hud.
|
||||
// However, we still should check for collisions of the stylus with the web overlay.
|
||||
|
||||
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
this.processStylus(controllerLocation.position);
|
||||
|
||||
|
@ -1095,6 +1151,29 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) {
|
||||
if (this.otherGrabbingLine === null) {
|
||||
var lineProperties = {
|
||||
lineWidth: 5,
|
||||
start: avatarPosition,
|
||||
end: entityPosition,
|
||||
color: color,
|
||||
glow: 1.0,
|
||||
ignoreRayIntersection: true,
|
||||
drawInFront: true,
|
||||
visible: true,
|
||||
alpha: 1
|
||||
};
|
||||
this.otherGrabbingLine = Overlays.addOverlay("line3d", lineProperties);
|
||||
} else {
|
||||
Overlays.editOverlay(this.otherGrabbingLine, {
|
||||
start: avatarPosition,
|
||||
end: entityPosition,
|
||||
color: color
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.evalLightWorldTransform = function(modelPos, modelRot) {
|
||||
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
|
@ -1138,14 +1217,20 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
this.turnOffVisualizations = function() {
|
||||
this.otherGrabbingLineOff = function() {
|
||||
if (this.otherGrabbingLine !== null) {
|
||||
Overlays.deleteOverlay(this.otherGrabbingLine);
|
||||
}
|
||||
this.otherGrabbingLine = null;
|
||||
};
|
||||
|
||||
this.turnOffVisualizations = function() {
|
||||
this.overlayLineOff();
|
||||
this.grabPointSphereOff();
|
||||
this.lineOff();
|
||||
this.searchSphereOff();
|
||||
this.otherGrabbingLineOff();
|
||||
restore2DMode();
|
||||
|
||||
};
|
||||
|
||||
this.triggerPress = function(value) {
|
||||
|
@ -1208,30 +1293,54 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.processStylus = function(worldHandPosition) {
|
||||
// see if the hand is near a tablet or web-entity
|
||||
var candidateEntities = Entities.findEntities(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE);
|
||||
entityPropertiesCache.addEntities(candidateEntities);
|
||||
var nearWeb = false;
|
||||
for (var i = 0; i < candidateEntities.length; i++) {
|
||||
var props = entityPropertiesCache.getProps(candidateEntities[i]);
|
||||
if (props && (props.type == "Web" || this.isTablet(candidateEntities[i]))) {
|
||||
nearWeb = true;
|
||||
break;
|
||||
|
||||
var performRayTest = false;
|
||||
if (this.useFingerInsteadOfStylus) {
|
||||
this.hideStylus();
|
||||
performRayTest = true;
|
||||
} else {
|
||||
var i;
|
||||
|
||||
// see if the hand is near a tablet or web-entity
|
||||
var candidateEntities = Entities.findEntities(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE);
|
||||
entityPropertiesCache.addEntities(candidateEntities);
|
||||
for (i = 0; i < candidateEntities.length; i++) {
|
||||
var props = entityPropertiesCache.getProps(candidateEntities[i]);
|
||||
if (props && (props.type == "Web" || this.isTablet(candidateEntities[i]))) {
|
||||
performRayTest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!performRayTest) {
|
||||
var candidateOverlays = Overlays.findOverlays(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE);
|
||||
for (i = 0; i < candidateOverlays.length; i++) {
|
||||
if (this.isTablet(candidateOverlays[i])) {
|
||||
performRayTest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (performRayTest) {
|
||||
this.showStylus();
|
||||
} else {
|
||||
this.hideStylus();
|
||||
}
|
||||
}
|
||||
|
||||
var candidateOverlays = Overlays.findOverlays(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE);
|
||||
for (var j = 0; j < candidateOverlays.length; j++) {
|
||||
if (this.isTablet(candidateOverlays[j])) {
|
||||
nearWeb = true;
|
||||
if (performRayTest) {
|
||||
var rayPickInfo = this.calcRayPickInfo(this.hand, this.useFingerInsteadOfStylus);
|
||||
var max, min;
|
||||
if (this.useFingerInsteadOfStylus) {
|
||||
max = FINGER_TOUCH_MAX;
|
||||
min = FINGER_TOUCH_MIN;
|
||||
} else {
|
||||
max = WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET;
|
||||
min = WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_TOO_CLOSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearWeb) {
|
||||
this.showStylus();
|
||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||
if (rayPickInfo.distance < WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET &&
|
||||
rayPickInfo.distance > WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_TOO_CLOSE) {
|
||||
if (rayPickInfo.distance < max && rayPickInfo.distance > min) {
|
||||
this.handleStylusOnHomeButton(rayPickInfo);
|
||||
if (this.handleStylusOnWebEntity(rayPickInfo)) {
|
||||
return;
|
||||
|
@ -1240,10 +1349,8 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
this.homeButtonTouched = false;
|
||||
}
|
||||
} else {
|
||||
this.hideStylus();
|
||||
this.homeButtonTouched = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1362,10 +1469,17 @@ function MyController(hand) {
|
|||
|
||||
// Performs ray pick test from the hand controller into the world
|
||||
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
|
||||
// @param {bool} if true use the world position/orientation of the index finger to cast the ray from.
|
||||
// @returns {object} returns object with two keys entityID and distance
|
||||
//
|
||||
this.calcRayPickInfo = function(hand) {
|
||||
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
this.calcRayPickInfo = function(hand, useFingerInsteadOfController) {
|
||||
|
||||
var controllerLocation;
|
||||
if (useFingerInsteadOfController) {
|
||||
controllerLocation = getFingerWorldLocation(hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var worldHandPosition = controllerLocation.position;
|
||||
var worldHandRotation = controllerLocation.orientation;
|
||||
|
||||
|
@ -1572,7 +1686,8 @@ function MyController(hand) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (entityIsGrabbedByOther(entityID)) {
|
||||
this.otherGrabbingUUID = entityIsGrabbedByOther(entityID);
|
||||
if (this.otherGrabbingUUID !== null) {
|
||||
// don't distance grab something that is already grabbed.
|
||||
if (debug) {
|
||||
print("distance grab is skipping '" + props.name + "': already grabbed by another.");
|
||||
|
@ -1789,6 +1904,7 @@ function MyController(hand) {
|
|||
} else {
|
||||
// potentialFarTriggerEntity = entity;
|
||||
}
|
||||
this.otherGrabbingLineOff();
|
||||
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
||||
this.grabbedThingID = entity;
|
||||
|
@ -1803,7 +1919,25 @@ function MyController(hand) {
|
|||
} else {
|
||||
// potentialFarGrabEntity = entity;
|
||||
}
|
||||
this.otherGrabbingLineOff();
|
||||
} else if (this.otherGrabbingUUID !== null) {
|
||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
||||
var avatar = AvatarList.getAvatar(this.otherGrabbingUUID);
|
||||
var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar.
|
||||
var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR));
|
||||
var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid.
|
||||
Vec3.multiplyQbyV(rayPickInfo.properties.rotation ,
|
||||
Vec3.multiplyVbyV(rayPickInfo.properties.dimensions,
|
||||
Vec3.subtract(DEFAULT_REGISTRATION_POINT, rayPickInfo.properties.registrationPoint))));
|
||||
this.otherGrabbingLineOn(startPosition, finishPisition, COLORS_GRAB_DISTANCE_HOLD);
|
||||
} else {
|
||||
this.otherGrabbingLineOff();
|
||||
}
|
||||
} else {
|
||||
this.otherGrabbingLineOff();
|
||||
}
|
||||
} else {
|
||||
this.otherGrabbingLineOff();
|
||||
}
|
||||
|
||||
this.updateEquipHaptics(potentialEquipHotspot, handPosition);
|
||||
|
@ -2449,6 +2583,7 @@ function MyController(hand) {
|
|||
this.lineOff();
|
||||
this.overlayLineOff();
|
||||
this.searchSphereOff();
|
||||
this.otherGrabbingLineOff();
|
||||
|
||||
this.dropGestureReset();
|
||||
this.clearEquipHaptics();
|
||||
|
@ -2981,8 +3116,13 @@ function MyController(hand) {
|
|||
|
||||
this.entityTouchingEnter = function() {
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_ENTITY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
|
@ -3018,8 +3158,13 @@ function MyController(hand) {
|
|||
|
||||
this.entityTouchingExit = function() {
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_ENTITY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
var pointerEvent;
|
||||
if (this.deadspotExpired) {
|
||||
|
@ -3059,12 +3204,24 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_ENTITY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
|
||||
var max;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_ENTITY_STYLUS_TOUCHING) {
|
||||
max = FINGER_TOUCH_MAX;
|
||||
} else {
|
||||
max = WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET;
|
||||
}
|
||||
|
||||
if (this.state == STATE_ENTITY_STYLUS_TOUCHING &&
|
||||
intersectInfo.distance > WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET) {
|
||||
intersectInfo.distance > max) {
|
||||
this.setState(STATE_OFF, "pulled away from web entity");
|
||||
return;
|
||||
}
|
||||
|
@ -3107,8 +3264,13 @@ function MyController(hand) {
|
|||
|
||||
this.overlayTouchingEnter = function () {
|
||||
// Test for intersection between controller laser and Web overlay plane.
|
||||
var intersectInfo =
|
||||
handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_OVERLAY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
|
@ -3143,8 +3305,13 @@ function MyController(hand) {
|
|||
|
||||
this.overlayTouchingExit = function () {
|
||||
// Test for intersection between controller laser and Web overlay plane.
|
||||
var intersectInfo =
|
||||
handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_OVERLAY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
var pointerEvent;
|
||||
|
||||
|
@ -3201,12 +3368,25 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
// Test for intersection between controller laser and Web overlay plane.
|
||||
var intersectInfo =
|
||||
handLaserIntersectOverlay(this.grabbedOverlay, getControllerWorldLocation(this.handToController(), true));
|
||||
var controllerLocation;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_OVERLAY_STYLUS_TOUCHING) {
|
||||
controllerLocation = getFingerWorldLocation(this.hand);
|
||||
} else {
|
||||
controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
}
|
||||
var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation);
|
||||
if (intersectInfo) {
|
||||
|
||||
if (this.state == STATE_OVERLAY_STYLUS_TOUCHING &&
|
||||
intersectInfo.distance > WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET + WEB_TOUCH_Y_TOUCH_DEADZONE_SIZE) {
|
||||
var max, min;
|
||||
if (this.useFingerInsteadOfStylus && this.state === STATE_OVERLAY_STYLUS_TOUCHING) {
|
||||
max = FINGER_TOUCH_MAX;
|
||||
min = FINGER_TOUCH_MIN;
|
||||
} else {
|
||||
max = WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET + WEB_TOUCH_Y_TOUCH_DEADZONE_SIZE;
|
||||
min = WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_TOO_CLOSE;
|
||||
}
|
||||
|
||||
if (this.state == STATE_OVERLAY_STYLUS_TOUCHING && intersectInfo.distance > max) {
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "pulled away from overlay");
|
||||
return;
|
||||
|
@ -3217,7 +3397,7 @@ function MyController(hand) {
|
|||
|
||||
if (this.state == STATE_OVERLAY_STYLUS_TOUCHING &&
|
||||
!this.tabletStabbed &&
|
||||
intersectInfo.distance < WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_TOO_CLOSE) {
|
||||
intersectInfo.distance < min) {
|
||||
// they've stabbed the tablet, don't send events until they pull back
|
||||
this.tabletStabbed = true;
|
||||
this.tabletStabbedPos2D = pos2D;
|
||||
|
|
|
@ -248,12 +248,16 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
}
|
||||
break;
|
||||
case 'refresh':
|
||||
data = {};
|
||||
ExtendedOverlay.some(function (overlay) { // capture the audio data
|
||||
data[overlay.key] = overlay;
|
||||
});
|
||||
removeOverlays();
|
||||
// If filter is specified from .qml instead of through settings, update the settings.
|
||||
if (message.params.filter !== undefined) {
|
||||
Settings.setValue('pal/filtered', !!message.params.filter);
|
||||
}
|
||||
populateUserList(message.params.selected);
|
||||
populateUserList(message.params.selected, data);
|
||||
UserActivityLogger.palAction("refresh", "");
|
||||
break;
|
||||
case 'displayNameUpdate':
|
||||
|
@ -285,7 +289,7 @@ function addAvatarNode(id) {
|
|||
}
|
||||
// Each open/refresh will capture a stable set of avatarsOfInterest, within the specified filter.
|
||||
var avatarsOfInterest = {};
|
||||
function populateUserList(selectData) {
|
||||
function populateUserList(selectData, oldAudioData) {
|
||||
var filter = Settings.getValue('pal/filtered') && {distance: Settings.getValue('pal/nearDistance')};
|
||||
var data = [], avatars = AvatarList.getAvatarIdentifiers();
|
||||
avatarsOfInterest = {};
|
||||
|
@ -317,12 +321,13 @@ function populateUserList(selectData) {
|
|||
if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) {
|
||||
return;
|
||||
}
|
||||
var oldAudio = oldAudioData && oldAudioData[id];
|
||||
var avatarPalDatum = {
|
||||
displayName: name,
|
||||
userName: '',
|
||||
sessionId: id || '',
|
||||
audioLevel: 0.0,
|
||||
avgAudioLevel: 0.0,
|
||||
audioLevel: (oldAudio && oldAudio.audioLevel) || 0.0,
|
||||
avgAudioLevel: (oldAudio && oldAudio.avgAudioLevel) || 0.0,
|
||||
admin: false,
|
||||
personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null
|
||||
ignore: !!id && Users.getIgnoreStatus(id) // ditto
|
||||
|
|
|
@ -115,8 +115,8 @@ void GLMHelpersTests::testSimd() {
|
|||
|
||||
a1 = a * b;
|
||||
b1 = b * a;
|
||||
glm_mat4_mul((glm_vec4*)&a, (glm_vec4*)&b, (glm_vec4*)&a2);
|
||||
glm_mat4_mul((glm_vec4*)&b, (glm_vec4*)&a, (glm_vec4*)&b2);
|
||||
glm_mat4u_mul(a, b, a2);
|
||||
glm_mat4u_mul(b, a, b2);
|
||||
|
||||
|
||||
{
|
||||
|
@ -133,8 +133,8 @@ void GLMHelpersTests::testSimd() {
|
|||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
for (size_t i = 0; i < LOOPS; ++i) {
|
||||
glm_mat4_mul((glm_vec4*)&a, (glm_vec4*)&b, (glm_vec4*)&a2);
|
||||
glm_mat4_mul((glm_vec4*)&b, (glm_vec4*)&a, (glm_vec4*)&b2);
|
||||
glm_mat4u_mul(a, b, a2);
|
||||
glm_mat4u_mul(b, a, b2);
|
||||
}
|
||||
qDebug() << "SIMD " << timer.elapsed();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ exports.handlers = {
|
|||
'../../libraries/networking/src',
|
||||
'../../libraries/animation/src',
|
||||
'../../libraries/entities/src',
|
||||
'../../libraries/shared/src'
|
||||
];
|
||||
var exts = ['.h', '.cpp'];
|
||||
|
||||
|
|
Loading…
Reference in a new issue