diff --git a/examples/headMove.js b/examples/headMove.js index 4d432c28b3..894575ead1 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -43,7 +43,7 @@ var noFly = true; var fixedWalkVelocity = true; //var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 }; -var roomLimits = { xMin: 193.0, xMax: 206.5, zMin: 251.4, zMax: 269.5 }; +var roomLimits = { xMin: 100.0, xMax: 206.5, zMin: 251.4, zMax: 269.5 }; function isInRoom(position) { var BUFFER = 2.0; diff --git a/examples/radio.js b/examples/radio.js index 6ffc7deb84..83e81e7e02 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -9,18 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var position = { x:1, y: 1, z: 10 }; -var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); -var scale = 1.0; var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2Finished.fbx"; var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw"; var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 0.7; -audioOptions.position = position; -audioOptions.orientation = Quat.multiply(AudioRotationOffset, rotation); +audioOptions.volume = 0.5; audioOptions.loop = true; audioOptions.isStereo = true; var injector = null; @@ -34,15 +29,22 @@ function update() { if (entity === null) { if (sound.downloaded) { print("Sound file downloaded"); + var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, + { x: 0, y: 0.3, z: -1 })); + var rotation = Quat.multiply(MyAvatar.orientation, + Quat.fromPitchYawRollDegrees(0, -90, 0)); entity = Entities.addEntity({ - type: "Model", - position: position, - rotation: rotation, - radius: scale / 2.0, - modelURL: modelURL + type: "Model", + position: position, + rotation: rotation, + dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + modelURL: modelURL }); properties = Entities.getEntityProperties(entity); + audioOptions.position = position; + audioOptions.orientation = rotation; injector = Audio.playSound(sound, audioOptions); } } else { @@ -61,6 +63,7 @@ function update() { Script.update.disconnect(update); Script.scriptEnding.connect(scriptEnding); scriptEnding(); + Script.stop(); } } } diff --git a/examples/xbox.js b/examples/xbox.js new file mode 100644 index 0000000000..603e0dbf56 --- /dev/null +++ b/examples/xbox.js @@ -0,0 +1,19 @@ +// +// xbox.js +// examples +// +// Created by Stephen Birarda on September 23, 2014 +// +// Copyright 2014 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 +// + +gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); + +function reportAxisValue(axis, newValue, oldValue) { + print("The value for axis " + axis + " has changed to " + newValue + ". It was " + oldValue); +} + +gamepad.axisValueChanged.connect(reportAxisValue); \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 5653286104..a2a832afe0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp") +set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) @@ -120,6 +120,10 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE}) # include the library directories (ignoring warnings) + if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS) + set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR}) + endif () + include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) # perform the system include hack for OS X to ignore warnings @@ -129,6 +133,10 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) endforeach() endif () + if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES) + set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY}) + endif () + target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES}) endif () diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f2ae7a2d8b..60073f8d2b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,6 +82,7 @@ #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" +#include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -1822,6 +1823,8 @@ void Application::init() { _entities.init(); _entities.setViewFrustum(getViewFrustum()); + _entityCollisionSystem.init(&_entityEditSender, _entities.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + _entityClipboardRenderer.init(); _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); @@ -1887,13 +1890,12 @@ void Application::shrinkMirrorView() { } } -const float HEAD_SPHERE_RADIUS = 0.07f; +const float HEAD_SPHERE_RADIUS = 0.1f; bool Application::isLookingAtMyAvatar(Avatar* avatar) { - glm::vec3 theirLookat = avatar->getHead()->getLookAtPosition(); - glm::vec3 myHeadPosition = _myAvatar->getHead()->getPosition(); - - if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) { + glm::vec3 theirLookAt = avatar->getHead()->getLookAtPosition(); + glm::vec3 myEyePosition = _myAvatar->getHead()->getEyePosition(); + if (pointInSphere(theirLookAt, myEyePosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) { return true; } return false; @@ -1999,21 +2001,23 @@ void Application::updateMyAvatarLookAtPosition() { lookAtSpot = _myCamera.getPosition(); } else { - if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) { + AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef(); + if (lookingAt && _myAvatar != lookingAt.data()) { + isLookingAtSomeone = true; // If I am looking at someone else, look directly at one of their eyes if (tracker) { // If tracker active, look at the eye for the side my gaze is biased toward if (tracker->getEstimatedEyeYaw() > _myAvatar->getHead()->getFinalYaw()) { // Look at their right eye - lookAtSpot = static_cast(_myAvatar->getLookAtTargetAvatar())->getHead()->getRightEyePosition(); + lookAtSpot = static_cast(lookingAt.data())->getHead()->getRightEyePosition(); } else { // Look at their left eye - lookAtSpot = static_cast(_myAvatar->getLookAtTargetAvatar())->getHead()->getLeftEyePosition(); + lookAtSpot = static_cast(lookingAt.data())->getHead()->getLeftEyePosition(); } } else { // Need to add randomly looking back and forth between left and right eye for case with no tracker - lookAtSpot = static_cast(_myAvatar->getLookAtTargetAvatar())->getHead()->getEyePosition(); + lookAtSpot = static_cast(lookingAt.data())->getHead()->getEyePosition(); } } else { // I am not looking at anyone else, so just look forward @@ -2160,7 +2164,7 @@ void Application::update(float deltaTime) { updateFaceshift(); updateVisage(); _sixenseManager.update(deltaTime); - _joystickManager.update(); + JoystickScriptingInterface::getInstance().update(); _prioVR.update(deltaTime); } @@ -2189,6 +2193,10 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("entities"); _entities.update(); // update the models... + { + PerformanceTimer perfTimer("collisions"); + _entityCollisionSystem.update(); // collide the entities... + } } { @@ -3770,6 +3778,14 @@ void Application::saveScripts() { _settings->endArray(); } +QScriptValue joystickToScriptValue(QScriptEngine *engine, Joystick* const &in) { + return engine->newQObject(in); +} + +void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) { + out = qobject_cast(object.toQObject()); +} + ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow) { QUrl scriptUrl(scriptFilename); @@ -3852,6 +3868,9 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AvatarManager", &_avatarManager); + + scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); + qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); diff --git a/interface/src/Application.h b/interface/src/Application.h index d17bacd413..f8710bae7b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -59,7 +60,6 @@ #include "avatar/MyAvatar.h" #include "devices/Faceplus.h" #include "devices/Faceshift.h" -#include "devices/JoystickManager.h" #include "devices/PrioVR.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" @@ -221,7 +221,6 @@ public: FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } PrioVR* getPrioVR() { return &_prioVR; } - JoystickManager* getJoystickManager() { return &_joystickManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } QSystemTrayIcon* getTrayIcon() { return _trayIcon; } @@ -300,6 +299,8 @@ public: ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; } void setCursorVisible(bool visible); + + bool isLookingAtMyAvatar(Avatar* avatar); signals: @@ -414,7 +415,6 @@ private: void updateCursor(float deltaTime); Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); - bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest); @@ -482,6 +482,7 @@ private: ParticleCollisionSystem _particleCollisionSystem; EntityTreeRenderer _entities; + EntityCollisionSystem _entityCollisionSystem; EntityTreeRenderer _entityClipboardRenderer; EntityTree _entityClipboard; @@ -511,7 +512,6 @@ private: SixenseManager _sixenseManager; PrioVR _prioVR; - JoystickManager _joystickManager; Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index e9cc6f9271..365064e979 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -630,7 +630,7 @@ void Audio::handleAudioInput() { measuredDcOffset += networkAudioSamples[i]; networkAudioSamples[i] -= (int16_t) _dcOffset; thisSample = fabsf(networkAudioSamples[i]); - if (thisSample >= (32767.0f * CLIPPING_THRESHOLD)) { + if (thisSample >= ((float)MAX_16_BIT_AUDIO_SAMPLE * CLIPPING_THRESHOLD)) { _timeSinceLastClip = 0.0f; } loudness += thisSample; @@ -1375,32 +1375,16 @@ int Audio::addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_ return 0; } - // Constant multiplier to map sample value to vertical size of scope - float multiplier = (float)MULTIPLIER_SCOPE_HEIGHT / logf(2.0f); - - // Used to scale each sample. (logf(sample) + fadeOffset) is same as logf(sample * fade). - float fadeOffset = logf(fade); - // Temporary variable receives sample value float sample; - // Temporary variable receives mapping of sample value - int16_t value; - QMutexLocker lock(&_guard); // Short int pointer to mapped samples in byte array int16_t* destination = (int16_t*) byteArray->data(); for (int i = 0; i < sourceSamplesPerChannel; i++) { sample = (float)source[i * sourceNumberOfChannels + sourceChannel]; - if (sample > 1) { - value = (int16_t)(multiplier * (logf(sample) + fadeOffset)); - } else if (sample < -1) { - value = (int16_t)(-multiplier * (logf(-sample) + fadeOffset)); - } else { - value = 0; - } - destination[frameOffset] = value; + destination[frameOffset] = sample / (float) MAX_16_BIT_AUDIO_SAMPLE * (float)SCOPE_HEIGHT / 2.0f; frameOffset = (frameOffset == _samplesPerScope - 1) ? 0 : frameOffset + 1; } return frameOffset; diff --git a/interface/src/Audio.h b/interface/src/Audio.h index f100c04684..e94e5ab16c 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -47,6 +47,8 @@ static const int NUM_AUDIO_CHANNELS = 2; +static const int MAX_16_BIT_AUDIO_SAMPLE = 32767; + class QAudioInput; class QAudioOutput; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f781ee8a9f..a4ef9cc022 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -158,7 +158,8 @@ Menu::Menu() : addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::SHIFT | Qt::Key_R, + appInstance, SLOT(reloadAllScripts())); addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, appInstance, SLOT(toggleRunningScriptsWidget())); diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index d51973f88f..70f59f0661 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -64,7 +64,7 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta glm::translate(state.getDefaultTranslationInConstrainedFrame()) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)); glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f)); - glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() + + glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getCorrectedLookAtPosition() + _owningHead->getSaccade() - model->getTranslation(), 1.0f)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 93119e6f6f..9c41cf8084 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -34,6 +34,7 @@ Head::Head(Avatar* owningAvatar) : _lastLoudness(0.0f), _longTermAverageLoudness(-1.0f), _audioAttack(0.0f), + _audioJawOpen(0.0f), _angularVelocity(0,0,0), _renderLookatVectors(false), _saccade(0.0f, 0.0f, 0.0f), @@ -47,6 +48,7 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanSideways(0.f), _deltaLeanForward(0.f), _isCameraMoving(false), + _isLookingAtMe(false), _faceModel(this) { @@ -156,11 +158,21 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } // use data to update fake Faceshift blendshape coefficients - const float JAW_OPEN_SCALE = 10.f; + const float JAW_OPEN_SCALE = 0.015f; + const float JAW_OPEN_RATE = 0.9f; + const float JAW_CLOSE_RATE = 0.90f; + float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE; + if (audioDelta > _audioJawOpen) { + _audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE; + } else { + _audioJawOpen *= JAW_CLOSE_RATE; + } + _audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f); + Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, _browAudioLift, - glm::clamp(log(_averageLoudness) / JAW_OPEN_SCALE, 0.0f, 1.0f), + _audioJawOpen, _blendshapeCoefficients); } @@ -199,7 +211,7 @@ void Head::render(float alpha, Model::RenderMode mode) { } void Head::renderPostLighting() { - renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition); + renderLookatVectors(_leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); } void Head::setScale (float scale) { @@ -217,6 +229,19 @@ glm::quat Head::getFinalOrientationInLocalFrame() const { return glm::quat(glm::radians(glm::vec3(getFinalPitch(), getFinalYaw(), getFinalRoll() ))); } +glm::vec3 Head::getCorrectedLookAtPosition() { + if (_isLookingAtMe) { + return _correctedLookAtPosition; + } else { + return getLookAtPosition(); + } +} + +void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { + _isLookingAtMe = true; + _correctedLookAtPosition = correctedLookAtPosition; +} + glm::quat Head::getCameraOrientation () const { if (OculusManager::isConnected()) { return getOrientation(); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 58feeff420..efcee9ed8d 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -63,6 +63,11 @@ public: const glm::vec3& getAngularVelocity() const { return _angularVelocity; } void setAngularVelocity(glm::vec3 angularVelocity) { _angularVelocity = angularVelocity; } + void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); + glm::vec3 getCorrectedLookAtPosition(); + void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } + bool getIsLookingAtMe() { return _isLookingAtMe; } + float getScale() const { return _scale; } glm::vec3 getPosition() const { return _position; } const glm::vec3& getEyePosition() const { return _eyePosition; } @@ -125,6 +130,7 @@ private: float _lastLoudness; float _longTermAverageLoudness; float _audioAttack; + float _audioJawOpen; glm::vec3 _angularVelocity; bool _renderLookatVectors; glm::vec3 _saccade; @@ -143,8 +149,11 @@ private: float _deltaLeanForward; bool _isCameraMoving; + bool _isLookingAtMe; FaceModel _faceModel; + glm::vec3 _correctedLookAtPosition; + // private methods void renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f1c63d44e7..51f582c4f8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -951,20 +951,37 @@ void MyAvatar::updateLookAtTargetAvatar() { // _lookAtTargetAvatar.clear(); _targetAvatarPosition = glm::vec3(0.0f); - const float MIN_LOOKAT_ANGLE = PI / 4.0f; // Smallest angle between face and person where we will look at someone - float smallestAngleTo = MIN_LOOKAT_ANGLE; + + glm::quat faceRotation = Application::getInstance()->getViewFrustum()->getOrientation(); + FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); + if (tracker) { + // If faceshift or other face tracker in use, add on the actual angle of the head + faceRotation *= tracker->getHeadRotation(); + } + glm::vec3 lookForward = faceRotation * IDENTITY_FRONT; + glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.f; + + int howManyLookingAtMe = 0; foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { Avatar* avatar = static_cast(avatarPointer.data()); avatar->setIsLookAtTarget(false); - if (!avatar->isMyAvatar()) { - glm::vec3 DEFAULT_GAZE_IN_HEAD_FRAME = glm::vec3(0.0f, 0.0f, -1.0f); - float angleTo = glm::angle(getHead()->getFinalOrientationInWorldFrame() * DEFAULT_GAZE_IN_HEAD_FRAME, - glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition())); + if (!avatar->isMyAvatar() && avatar->isInitialized()) { + float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); if (angleTo < smallestAngleTo) { _lookAtTargetAvatar = avatarPointer; _targetAvatarPosition = avatarPointer->getPosition(); smallestAngleTo = angleTo; } + // Check if this avatar is looking at me, and fix their gaze on my camera if so + if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { + howManyLookingAtMe++; + // Have that avatar look directly at my camera + // Philip TODO: correct to look at left/right eye + avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition()); + } else { + avatar->getHead()->clearCorrectedLookAtPosition(); + } } } if (_lookAtTargetAvatar) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b5eb7ee1ff..e38f3f4b2b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -119,7 +119,7 @@ public: Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); } Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } - AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } + QWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp new file mode 100644 index 0000000000..d220a827f1 --- /dev/null +++ b/interface/src/devices/Joystick.cpp @@ -0,0 +1,60 @@ +// +// Joystick.cpp +// interface/src/devices +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#include + +#include + +#include "Joystick.h" + +#ifdef HAVE_SDL + +Joystick::Joystick(const QString& name, SDL_Joystick* sdlJoystick) : + _name(name), + _axes(QVector(SDL_JoystickNumAxes(sdlJoystick))), + _buttons(QVector(SDL_JoystickNumButtons(sdlJoystick))), + _sdlJoystick(sdlJoystick) +{ + +} + +#endif + +Joystick::~Joystick() { +#ifdef HAVE_SDL + SDL_JoystickClose(_sdlJoystick); +#endif +} + +void Joystick::update() { +#ifdef HAVE_SDL + // update our current values, emit a signal when there is a change + for (int j = 0; j < getNumAxes(); j++) { + float value = glm::round(SDL_JoystickGetAxis(_sdlJoystick, j) + 0.5f) / std::numeric_limits::max(); + const float DEAD_ZONE = 0.1f; + float cleanValue = glm::abs(value) < DEAD_ZONE ? 0.0f : value; + + if (_axes[j] != cleanValue) { + float oldValue = _axes[j]; + _axes[j] = cleanValue; + emit axisValueChanged(j, cleanValue, oldValue); + } + } + for (int j = 0; j < getNumButtons(); j++) { + bool newValue = SDL_JoystickGetButton(_sdlJoystick, j); + if (_buttons[j] != newValue) { + bool oldValue = _buttons[j]; + _buttons[j] = newValue; + emit buttonStateChanged(j, newValue, oldValue); + } + } +#endif +} \ No newline at end of file diff --git a/interface/src/devices/Joystick.h b/interface/src/devices/Joystick.h new file mode 100644 index 0000000000..228e993204 --- /dev/null +++ b/interface/src/devices/Joystick.h @@ -0,0 +1,61 @@ +// +// Joystick.h +// interface/src/devices +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#ifndef hifi_Joystick_h +#define hifi_Joystick_h + +#include +#include + +#ifdef HAVE_SDL +#include +#undef main +#endif + +class Joystick : public QObject { + Q_OBJECT + + Q_PROPERTY(QString name READ getName) + + Q_PROPERTY(int numAxes READ getNumAxes) + Q_PROPERTY(int numButtons READ getNumButtons) +public: + Joystick(); + ~Joystick(); + +#ifdef HAVE_SDL + Joystick(const QString& name, SDL_Joystick* sdlJoystick); +#endif + + void update(); + + const QString& getName() const { return _name; } + + const QVector& getAxes() const { return _axes; } + const QVector& getButtons() const { return _buttons; } + + int getNumAxes() const { return _axes.size(); } + int getNumButtons() const { return _buttons.size(); } + +signals: + void axisValueChanged(int axis, float newValue, float oldValue); + void buttonStateChanged(int button, float newValue, float oldValue); +private: + QString _name; + QVector _axes; + QVector _buttons; + +#ifdef HAVE_SDL + SDL_Joystick* _sdlJoystick; +#endif +}; + +#endif // hifi_JoystickTracker_h \ No newline at end of file diff --git a/interface/src/devices/JoystickManager.cpp b/interface/src/devices/JoystickManager.cpp deleted file mode 100644 index 8169c6d06e..0000000000 --- a/interface/src/devices/JoystickManager.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// JoystickManager.cpp -// interface/src/devices -// -// Created by Andrzej Kapolka on 5/15/14. -// Copyright 2014 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 -// - -#include - -#include -#include - -#include - -#include "JoystickManager.h" - -using namespace std; - -JoystickManager::JoystickManager() { -#ifdef HAVE_SDL - SDL_Init(SDL_INIT_JOYSTICK); - int joystickCount = SDL_NumJoysticks(); - for (int i = 0; i < joystickCount; i++) { - SDL_Joystick* joystick = SDL_JoystickOpen(i); - if (joystick) { - JoystickState state = { SDL_JoystickName(i), QVector(SDL_JoystickNumAxes(joystick)), - QVector(SDL_JoystickNumButtons(joystick)) }; - _joystickStates.append(state); - _joysticks.append(joystick); - } - } -#endif -} - -JoystickManager::~JoystickManager() { -#ifdef HAVE_SDL - foreach (SDL_Joystick* joystick, _joysticks) { - SDL_JoystickClose(joystick); - } - SDL_Quit(); -#endif -} - -void JoystickManager::update() { -#ifdef HAVE_SDL - PerformanceTimer perfTimer("joystick"); - SDL_JoystickUpdate(); - - for (int i = 0; i < _joystickStates.size(); i++) { - SDL_Joystick* joystick = _joysticks.at(i); - JoystickState& state = _joystickStates[i]; - for (int j = 0; j < state.axes.size(); j++) { - float value = glm::round(SDL_JoystickGetAxis(joystick, j) + 0.5f) / numeric_limits::max(); - const float DEAD_ZONE = 0.1f; - state.axes[j] = glm::abs(value) < DEAD_ZONE ? 0.0f : value; - } - for (int j = 0; j < state.buttons.size(); j++) { - state.buttons[j] = SDL_JoystickGetButton(joystick, j); - } - } -#endif -} diff --git a/interface/src/devices/JoystickManager.h b/interface/src/devices/JoystickManager.h deleted file mode 100644 index 53a255e129..0000000000 --- a/interface/src/devices/JoystickManager.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// JoystickManager.h -// interface/src/devices -// -// Created by Andrzej Kapolka on 5/15/14. -// Copyright 2014 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 -// - -#ifndef hifi_JoystickManager_h -#define hifi_JoystickManager_h - -#include -#include - -#ifdef HAVE_SDL -#include -#undef main -#endif - -class JoystickState; - -/// Handles joystick input through SDL. -class JoystickManager : public QObject { - Q_OBJECT - -public: - - JoystickManager(); - virtual ~JoystickManager(); - - const QVector& getJoystickStates() const { return _joystickStates; } - - void update(); - -private: - QVector _joystickStates; - -#ifdef HAVE_SDL - QVector _joysticks; -#endif -}; - -class JoystickState { -public: - QString name; - QVector axes; - QVector buttons; -}; - -#endif // hifi_JoystickManager_h diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index fad80fda6f..e810f9e370 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -17,6 +17,7 @@ #include "Application.h" #include "PrioVR.h" +#include "scripting/JoystickScriptingInterface.h" #include "ui/TextRenderer.h" #ifdef HAVE_PRIOVR @@ -61,18 +62,22 @@ static void setPalm(float deltaTime, int index) { palm->setActive(true); // Read controller buttons and joystick into the hand - if (!Application::getInstance()->getJoystickManager()->getJoystickStates().isEmpty()) { - const JoystickState& state = Application::getInstance()->getJoystickManager()->getJoystickStates().at(0); - if (state.axes.size() >= 4 && state.buttons.size() >= 4) { + const QString PRIO_JOYSTICK_NAME = "PrioVR"; + Joystick* prioJoystick = JoystickScriptingInterface::getInstance().joystickWithName(PRIO_JOYSTICK_NAME); + if (prioJoystick) { + const QVector axes = prioJoystick->getAxes(); + const QVector buttons = prioJoystick->getButtons(); + + if (axes.size() >= 4 && buttons.size() >= 4) { if (index == LEFT_HAND_INDEX) { - palm->setControllerButtons(state.buttons.at(1) ? BUTTON_FWD : 0); - palm->setTrigger(state.buttons.at(0) ? 1.0f : 0.0f); - palm->setJoystick(state.axes.at(0), -state.axes.at(1)); + palm->setControllerButtons(buttons[1] ? BUTTON_FWD : 0); + palm->setTrigger(buttons[0] ? 1.0f : 0.0f); + palm->setJoystick(axes[0], -axes[1]); } else { - palm->setControllerButtons(state.buttons.at(3) ? BUTTON_FWD : 0); - palm->setTrigger(state.buttons.at(2) ? 1.0f : 0.0f); - palm->setJoystick(state.axes.at(2), -state.axes.at(3)); + palm->setControllerButtons(buttons[3] ? BUTTON_FWD : 0); + palm->setTrigger(buttons[2] ? 1.0f : 0.0f); + palm->setJoystick(axes[2], -axes[3]); } } } diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 095277d960..e744d74da3 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -296,7 +296,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) ModelEntityItem* modelEntity = static_cast(entityItem); qDebug() << " url:" << modelEntity->getModelURL(); } - qDebug() << " entityBox:" << entityBox; + qDebug() << " entityBox:" << entityItem->getAABox(); qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters"; qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters"; qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance); diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp new file mode 100644 index 0000000000..1e35c11f61 --- /dev/null +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -0,0 +1,84 @@ +// +// JoystickScriptingInterface.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/15/14. +// Copyright 2014 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 +// + +#include + +#ifdef HAVE_SDL +#include +#undef main +#endif + +#include + +#include "JoystickScriptingInterface.h" + +JoystickScriptingInterface& JoystickScriptingInterface::getInstance() { + static JoystickScriptingInterface sharedInstance; + return sharedInstance; +} + +JoystickScriptingInterface::JoystickScriptingInterface() : + _openJoysticks(), + _availableDeviceNames() +{ +#ifdef HAVE_SDL + SDL_Init(SDL_INIT_JOYSTICK); + + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + _availableDeviceNames << SDL_JoystickName(i); + } +#endif +} + +JoystickScriptingInterface::~JoystickScriptingInterface() { + qDeleteAll(_openJoysticks); + +#ifdef HAVE_SDL + SDL_Quit(); +#endif +} + +void JoystickScriptingInterface::update() { +#ifdef HAVE_SDL + PerformanceTimer perfTimer("JoystickScriptingInterface::update"); + SDL_JoystickUpdate(); + + foreach(Joystick* joystick, _openJoysticks) { + joystick->update(); + } + +#endif +} + +Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { + Joystick* matchingJoystick = _openJoysticks.value(name); +#ifdef HAVE_SDL + if (!matchingJoystick) { + // we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + if (SDL_JoystickName(i) == name) { + matchingJoystick = _openJoysticks.insert(name, new Joystick(name, SDL_JoystickOpen(i))).value(); + break; + } + } + + qDebug() << "No matching joystick found with name" << name << "- returning NULL pointer."; + } +#endif + + return matchingJoystick; +} + + diff --git a/interface/src/scripting/JoystickScriptingInterface.h b/interface/src/scripting/JoystickScriptingInterface.h new file mode 100644 index 0000000000..98e38f5698 --- /dev/null +++ b/interface/src/scripting/JoystickScriptingInterface.h @@ -0,0 +1,43 @@ +// +// JoystickScriptingInterface.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/15/14. +// Copyright 2014 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 +// + +#ifndef hifi_JoystickScriptingInterface_h +#define hifi_JoystickScriptingInterface_h + +#include +#include + +#include "devices/Joystick.h" + +/// Handles joystick input through SDL. +class JoystickScriptingInterface : public QObject { + Q_OBJECT + + Q_PROPERTY(QStringList availableJoystickNames READ getAvailableJoystickNames) +public: + static JoystickScriptingInterface& getInstance(); + + const QStringList& getAvailableJoystickNames() const { return _availableDeviceNames; } + + void update(); + +public slots: + Joystick* joystickWithName(const QString& name); + +private: + JoystickScriptingInterface(); + ~JoystickScriptingInterface(); + + QMap _openJoysticks; + QStringList _availableDeviceNames; +}; + +#endif // hifi_JoystickScriptingInterface_h diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 6efa2f8fe6..baf3995d0e 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -136,6 +136,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { ui->noRunningScriptsLabel->setVisible(list.isEmpty()); + ui->runningScriptsList->setVisible(!list.isEmpty()); ui->reloadAllButton->setVisible(!list.isEmpty()); ui->stopAllButton->setVisible(!list.isEmpty()); diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index ddc6ad6c27..a55e949d7a 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -6,8 +6,8 @@ 0 0 - 324 - 643 + 319 + 481 @@ -29,57 +29,12 @@ 20 - - - - - 6 - - - 0 - - - 6 - - - - - color: #0e7077; -font-size: 20px; - - - Running Scripts - - - 0 - - - -1 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + 0 - 1 + 0 @@ -157,7 +112,7 @@ font: bold 16px; 0 - 8 + 4 @@ -218,6 +173,12 @@ font: bold 16px; + + + 0 + 0 + + 0 @@ -236,6 +197,12 @@ font: bold 16px; + + + 0 + 0 + + Helvetica,Arial,sans-serif @@ -252,10 +219,13 @@ font: bold 16px; 0 - Qt::ScrollBarAsNeeded + Qt::ScrollBarAlwaysOn - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents true @@ -268,7 +238,7 @@ font: bold 16px; 0 0 - 284 + 264 16 @@ -278,6 +248,9 @@ font: bold 16px; 0 + + Qt::LeftToRight + font-size: 14px; @@ -303,14 +276,20 @@ font: bold 16px; + + + 0 + 0 + + - font: 14px; + font: 14px; color: #5f5f5f; - There are no scripts currently running. + There are no scripts running. - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -325,15 +304,9 @@ font: bold 16px; 0 - 2 + 0 - - - 0 - 300 - - 0 @@ -352,6 +325,12 @@ font: bold 16px; + + + 0 + 0 + + 0 @@ -432,6 +411,12 @@ font: bold 16px; + + + 0 + 0 + + Qt::ScrollBarAlwaysOn diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 202121bad3..f996fc2bad 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,11 +21,6 @@ AvatarHashMap::AvatarHashMap() : connect(NodeList::getInstance(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } -void AvatarHashMap::insert(const QUuid& sessionUUID, AvatarSharedPointer avatar) { - _avatarHash.insert(sessionUUID, avatar); - avatar->setSessionUUID(sessionUUID); -} - AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) { qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarHashMap."; return _avatarHash.erase(iterator); @@ -95,9 +90,11 @@ AvatarSharedPointer AvatarHashMap::matchingOrNewAvatar(const QUuid& sessionUUID, matchingAvatar = newSharedAvatar(); qDebug() << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap."; - _avatarHash.insert(sessionUUID, matchingAvatar); + matchingAvatar->setSessionUUID(sessionUUID); matchingAvatar->setOwningAvatarMixer(mixerWeakPointer); + + _avatarHash.insert(sessionUUID, matchingAvatar); } return matchingAvatar; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index fe9ab3d634..d52c656bc1 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -30,8 +30,6 @@ public: const AvatarHash& getAvatarHash() { return _avatarHash; } int size() const { return _avatarHash.size(); } - - virtual void insert(const QUuid& sessionUUID, AvatarSharedPointer avatar); public slots: void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index fb3ee2b5e7..b34391d580 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -32,7 +32,7 @@ static const int MAGIC_NUMBER_SIZE = 8; static const char MAGIC_NUMBER[MAGIC_NUMBER_SIZE] = {17, 72, 70, 82, 13, 10, 26, 10}; // Version (Major, Minor) -static const QPair VERSION(0, 1); +static const QPair VERSION(0, 2); int SCALE_RADIX = 10; int BLENDSHAPE_RADIX = 15; @@ -118,12 +118,6 @@ bool readQuat(QDataStream& stream, glm::quat& value) { return true; } -void writeFloat(QDataStream& stream, float value, int radix) { - unsigned char buffer[256]; - int writtenToBuffer = packFloatScalarToSignedTwoByteFixed(buffer, value, radix); - stream.writeRawData(reinterpret_cast(buffer), writtenToBuffer); -} - bool readFloat(QDataStream& stream, float& value, int radix) { int floatByteSize = 2; // 1 floats * 2 bytes int16_t buffer[256]; @@ -185,7 +179,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { // Orientation writeQuat(fileStream, context.orientation); // Scale - writeFloat(fileStream, context.scale, SCALE_RADIX); + fileStream << context.scale; // Head model fileStream << context.headModel; // Skeleton model @@ -204,7 +198,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { // Orientation writeQuat(fileStream, data.rotation); // Scale - writeFloat(fileStream, data.scale, SCALE_RADIX); + fileStream << data.scale; } // RECORDING @@ -231,7 +225,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { for (quint32 j = 0; j < numBlendshapes; ++j) { if (i == 0 || frame._blendshapeCoefficients[j] != previousFrame._blendshapeCoefficients[j]) { - writeFloat(stream, frame.getBlendshapeCoefficients()[j], BLENDSHAPE_RADIX); + stream << frame.getBlendshapeCoefficients()[j]; mask.setBit(maskIndex); } ++maskIndex; @@ -277,7 +271,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._scale != previousFrame._scale) { - writeFloat(stream, frame._scale, SCALE_RADIX); + stream << frame._scale; mask.setBit(maskIndex); } maskIndex++; @@ -297,7 +291,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._leanSideways != previousFrame._leanSideways) { - writeFloat(stream, frame._leanSideways, LEAN_RADIX); + stream << frame._leanSideways; mask.setBit(maskIndex); } maskIndex++; @@ -307,7 +301,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._leanForward != previousFrame._leanForward) { - writeFloat(stream, frame._leanForward, LEAN_RADIX); + stream << frame._leanForward; mask.setBit(maskIndex); } maskIndex++; @@ -438,7 +432,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString QPair version; fileStream >> version; // File format version - if (version != VERSION) { + if (version != VERSION && version != QPair(0,1)) { qDebug() << "ERROR: This file format version is not supported."; return recording; } @@ -484,10 +478,10 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } // Scale - if (!readFloat(fileStream, context.scale, SCALE_RADIX)) { - qDebug() << "Couldn't read file correctly. (Invalid float)"; - recording.clear(); - return recording; + if (version == QPair(0,1)) { + readFloat(fileStream, context.scale, SCALE_RADIX); + } else { + fileStream >> context.scale; } // Head model fileStream >> context.headModel; @@ -519,9 +513,10 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } // Scale - if (!readFloat(fileStream, data.scale, SCALE_RADIX)) { - qDebug() << "Couldn't read attachment correctly. (Invalid float)"; - continue; + if (version == QPair(0,1)) { + readFloat(fileStream, data.scale, SCALE_RADIX); + } else { + fileStream >> data.scale; } context.attachments << data; } @@ -548,8 +543,12 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } frame._blendshapeCoefficients.resize(numBlendshapes); for (quint32 j = 0; j < numBlendshapes; ++j) { - if (!mask[maskIndex++] || !readFloat(stream, frame._blendshapeCoefficients[j], BLENDSHAPE_RADIX)) { + if (!mask[maskIndex++]) { frame._blendshapeCoefficients[j] = previousFrame._blendshapeCoefficients[j]; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._blendshapeCoefficients[j], BLENDSHAPE_RADIX); + } else { + stream >> frame._blendshapeCoefficients[j]; } } // Joint Rotations @@ -571,20 +570,32 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString frame._rotation = previousFrame._rotation; } - if (!mask[maskIndex++] || !readFloat(stream, frame._scale, SCALE_RADIX)) { + if (!mask[maskIndex++]) { frame._scale = previousFrame._scale; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._scale, SCALE_RADIX); + } else { + stream >> frame._scale; } if (!mask[maskIndex++] || !readQuat(stream, frame._headRotation)) { frame._headRotation = previousFrame._headRotation; } - if (!mask[maskIndex++] || !readFloat(stream, frame._leanSideways, LEAN_RADIX)) { + if (!mask[maskIndex++]) { frame._leanSideways = previousFrame._leanSideways; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._leanSideways, LEAN_RADIX); + } else { + stream >> frame._leanSideways; } - if (!mask[maskIndex++] || !readFloat(stream, frame._leanForward, LEAN_RADIX)) { + if (!mask[maskIndex++]) { frame._leanForward = previousFrame._leanForward; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._leanForward, LEAN_RADIX); + } else { + stream >> frame._leanForward; } if (!mask[maskIndex++] || !readVec3(stream, frame._lookAtPosition)) { diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp new file mode 100644 index 0000000000..c9040c7a6e --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -0,0 +1,259 @@ +// +// EntityCollisionSystem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-2014 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 +// + +#include +#include +#include +#include +#include +#include + +#include "EntityItem.h" +#include "EntityCollisionSystem.h" +#include "EntityEditPacketSender.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +const int MAX_COLLISIONS_PER_Entity = 16; + +EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) { + init(packetSender, Entities, voxels, audio, avatars); +} + +void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { + _packetSender = packetSender; + _entities = Entities; + _voxels = voxels; + _audio = audio; + _avatars = avatars; +} + +EntityCollisionSystem::~EntityCollisionSystem() { +} + +void EntityCollisionSystem::update() { + // update all Entities + if (_entities->tryLockForRead()) { + QList& movingEntities = _entities->getMovingEntities(); + foreach (EntityItem* entity, movingEntities) { + checkEntity(entity); + } + _entities->unlock(); + } +} + + +void EntityCollisionSystem::checkEntity(EntityItem* entity) { + updateCollisionWithVoxels(entity); + updateCollisionWithEntities(entity); + updateCollisionWithAvatars(entity); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithVoxel(EntityItem* entity, + VoxelDetail* voxelDetails, const CollisionInfo& collision) { + EntityItemID entityItemID = entity->getEntityItemID(); + emit EntityCollisionWithVoxel(entityItemID, *voxelDetails, collision); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA, + EntityItem* entityB, const CollisionInfo& collision) { + + EntityItemID idA = entityA->getEntityItemID(); + EntityItemID idB = entityB->getEntityItemID(); + emit EntityCollisionWithEntity(idA, idB, collision); +} + +void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.4f; + const float DAMPING = 0.05f; + CollisionInfo collisionInfo; + collisionInfo._damping = DAMPING; + collisionInfo._elasticity = ELASTICITY; + VoxelDetail* voxelDetails = NULL; + if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) { + + // findSpherePenetration() only computes the penetration but we also want some other collision info + // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding + // the results to systems outside of this octree reference frame. + collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration)); + // let the global script run their collision scripts for Entities if they have them + emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo); + + // we must scale back down to the octree reference frame before updating the Entity properties + collisionInfo._penetration /= (float)(TREE_SCALE); + collisionInfo._contactPoint /= (float)(TREE_SCALE); + + applyHardCollision(entity, collisionInfo); + delete voxelDetails; // cleanup returned details + } +} + +void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { + glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); + float radius = entityA->getRadius() * (float)(TREE_SCALE); + glm::vec3 penetration; + EntityItem* entityB; + if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { + // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. + glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); + + // Even if the Entities overlap... when the Entities are already moving appart + // we don't want to count this as a collision. + glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); + + bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; + bool doCollisions = movingTowardEachOther; // don't do collisions if the entities are moving away from each other + + if (doCollisions) { + quint64 now = usecTimestampNow(); + + CollisionInfo collision; + collision._penetration = penetration; + // for now the contactPoint is the average between the the two paricle centers + collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); + emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); + + glm::vec3 axis = glm::normalize(penetration); + glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; + + float massA = entityA->getMass(); + float massB = entityB->getMass(); + float totalMass = massA + massB; + + // handle Entity A + glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); + glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits; + + EntityItemProperties propertiesA = entityA->getProperties(); + EntityItemID idA(entityA->getID()); + propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); + propertiesA.setPosition(newPositionA * (float)TREE_SCALE); + propertiesA.setLastEdited(now); + + _entities->updateEntity(idA, propertiesA); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); + + glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass); + glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; + + EntityItemProperties propertiesB = entityB->getProperties(); + + EntityItemID idB(entityB->getID()); + propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); + propertiesB.setPosition(newPositionB * (float)TREE_SCALE); + propertiesB.setLastEdited(now); + + _entities->updateEntity(idB, propertiesB); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); + + // TODO: Do we need this? + //_packetSender->releaseQueuedMessages(); + } + } +} + +void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { + + // Entities that are in hand, don't collide with avatars + if (!_avatars) { + return; + } + + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.9f; + const float DAMPING = 0.1f; + glm::vec3 penetration; + + _collisions.clear(); + foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { + AvatarData* avatar = avatarPointer.data(); + + float totalRadius = avatar->getBoundingRadius() + radius; + glm::vec3 relativePosition = center - avatar->getPosition(); + if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) { + continue; + } + + if (avatar->findSphereCollisions(center, radius, _collisions)) { + int numCollisions = _collisions.size(); + for (int i = 0; i < numCollisions; ++i) { + CollisionInfo* collision = _collisions.getCollision(i); + collision->_damping = DAMPING; + collision->_elasticity = ELASTICITY; + + collision->_addedVelocity /= (float)(TREE_SCALE); + glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity(); + + if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { + // only collide when Entity and collision point are moving toward each other + // (doing this prevents some "collision snagging" when Entity penetrates the object) + collision->_penetration /= (float)(TREE_SCALE); + applyHardCollision(entity, *collision); + } + } + } + } +} + +void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo) { + // HALTING_* params are determined using expected acceleration of gravity over some timescale. + // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal. + const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second + const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE); + + // + // Update the entity in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) + // if elasticity = 1.0, collision is 100% elastic. + // + glm::vec3 position = entity->getPosition(); + glm::vec3 velocity = entity->getVelocity(); + + const float EPSILON = 0.0f; + glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; + float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); + if (velocityDotPenetration < EPSILON) { + // entity is moving into collision surface + // + // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing + // (other's mass could be stored in the Collision Info). The smaller mass should surrender more + // position offset and should slave more to the other's velocity in the static-friction case. + position -= collisionInfo._penetration; + + if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) { + // static friction kicks in and entities moves with colliding object + velocity = collisionInfo._addedVelocity; + } else { + glm::vec3 direction = glm::normalize(collisionInfo._penetration); + velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection + velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction + } + } + + EntityItemProperties properties = entity->getProperties(); + EntityItemID entityItemID(entity->getID()); + + properties.setPosition(position * (float)TREE_SCALE); + properties.setVelocity(velocity * (float)TREE_SCALE); + properties.setLastEdited(usecTimestampNow()); + + _entities->updateEntity(entityItemID, properties); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); +} diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h new file mode 100644 index 0000000000..b2cf7a9df4 --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -0,0 +1,75 @@ +// +// EntityCollisionSystem.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-2014 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 +// + +#ifndef hifi_EntityCollisionSystem_h +#define hifi_EntityCollisionSystem_h + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "EntityItem.h" + +class AbstractAudioInterface; +class AvatarData; +class EntityEditPacketSender; +class EntityTree; +class VoxelTree; + +class EntityCollisionSystem : public QObject { +Q_OBJECT +public: + EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL, + VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, + AvatarHashMap* avatars = NULL); + + void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels, + AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL); + + ~EntityCollisionSystem(); + + void update(); + + void checkEntity(EntityItem* Entity); + void updateCollisionWithVoxels(EntityItem* Entity); + void updateCollisionWithEntities(EntityItem* Entity); + void updateCollisionWithAvatars(EntityItem* Entity); + void queueEntityPropertiesUpdate(EntityItem* Entity); + void updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency); + +signals: + void EntityCollisionWithVoxel(const EntityItemID& entityItemID, const VoxelDetail& voxel, const CollisionInfo& penetration); + void EntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& penetration); + +private: + void applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo); + + static bool updateOperation(OctreeElement* element, void* extraData); + void emitGlobalEntityCollisionWithVoxel(EntityItem* Entity, VoxelDetail* voxelDetails, const CollisionInfo& penetration); + void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration); + + EntityEditPacketSender* _packetSender; + EntityTree* _entities; + VoxelTree* _voxels; + AbstractAudioInterface* _audio; + AvatarHashMap* _avatars; + CollisionList _collisions; +}; + +#endif // hifi_EntityCollisionSystem_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1406239f23..cfed16c443 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -535,6 +535,10 @@ bool EntityItem::isRestingOnSurface() const { void EntityItem::update(const quint64& updateTime) { bool wantDebug = false; + + if (_lastUpdated == 0) { + _lastUpdated = updateTime; + } float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND); @@ -578,7 +582,7 @@ void EntityItem::update(const quint64& updateTime) { _lastUpdated = updateTime; if (wantDebug) { - qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; + qDebug() << " ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; } if (hasAngularVelocity()) { @@ -614,7 +618,7 @@ void EntityItem::update(const quint64& updateTime) { glm::vec3 newPosition = position + (velocity * timeElapsed); if (wantDebug) { - qDebug() << "EntityItem::update()...."; + qDebug() << " EntityItem::update()...."; qDebug() << " timeElapsed:" << timeElapsed; qDebug() << " old AACube:" << getMaximumAACube(); qDebug() << " old position:" << position; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a41d4523f9..bbfba30e9c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -20,6 +20,7 @@ #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include +#include #include "EntityItemID.h" #include "EntityItemProperties.h" @@ -227,6 +228,7 @@ public: // TODO: We need to get rid of these users of getRadius()... float getRadius() const; + void applyHardCollision(const CollisionInfo& collisionInfo); protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f2f2483bb4..7cc5ebb545 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() : _id(UNKNOWN_ENTITY_ID), _idSet(false), - _lastEdited(0), + _lastEdited(0), // ???? _created(UNKNOWN_CREATED_TIME), _type(EntityTypes::Unknown), diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a52a75e071..f4f9343f76 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -90,6 +90,8 @@ public: // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } + float getEditedAgo() const /// Elapsed seconds since this entity was last edited + { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } EntityPropertyFlags getChangedProperties() const; /// used by EntityScriptingInterface to return EntityItemProperties for unknown models @@ -121,14 +123,14 @@ public: float getMass() const { return _mass; } void setMass(float value) { _mass = value; _massChanged = true; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second const glm::vec3& getVelocity() const { return _velocity; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared const glm::vec3& getGravity() const { return _gravity; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; } float getDamping() const { return _damping; } @@ -219,9 +221,10 @@ public: bool getVisible() const { return _visible; } void setVisible(bool value) { _visible = value; _visibleChanged = true; } -private: void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } +private: + QUuid _id; bool _idSet; quint64 _lastEdited; @@ -285,4 +288,16 @@ Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); + +inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { + debug << "EntityItemProperties[" << "\n" + << " position:" << properties.getPosition() << "in meters" << "\n" + << " velocity:" << properties.getVelocity() << "in meters" << "\n" + << " last edited:" << properties.getLastEdited() << "\n" + << " edited ago:" << properties.getEditedAgo() << "\n" + << "]"; + + return debug; +} + #endif // hifi_EntityItemProperties_h diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d97a87faca..634a04a688 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -139,6 +139,8 @@ public: void trackDeletedEntity(const EntityItemID& entityID); + QList& getMovingEntities() { return _movingEntities; } + private: void updateChangingEntities(quint64 now, QSet& entitiesToDelete); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 1a1412d305..df2e40d96f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -732,9 +732,6 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (!box.expandedContains(args->center, args->radius)) { return false; } - if (!element->isLeaf()) { - return true; // recurse on children - } if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { @@ -744,6 +741,9 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { args->found = true; } } + if (!element->isLeaf()) { + return true; // recurse on children + } return false; } diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index e3b568365b..c7a94985cf 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -930,6 +930,9 @@ void Particle::applyHardCollision(const CollisionInfo& collisionInfo) { } void Particle::update(const quint64& now) { + if (_lastUpdated == 0) { + _lastUpdated = now; + } float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND); _lastUpdated = now; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cfea0d6b86..545e4709f9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -72,11 +72,11 @@ void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) { out = qobject_cast(object.toQObject()); } -QScriptValue injectorToScriptValueInputController(QScriptEngine *engine, AbstractInputController* const &in) { +QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { return engine->newQObject(in); } -void injectorFromScriptValueInputController(const QScriptValue &object, AbstractInputController* &out) { +void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { out = qobject_cast(object.toQObject()); } @@ -277,7 +277,7 @@ void ScriptEngine::init() { globalObject().setProperty("LocalVoxels", localVoxelsValue); qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); - qScriptRegisterMetaType( this, injectorToScriptValueInputController, injectorFromScriptValueInputController); + qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h index 50a60828e7..b94601392b 100644 --- a/libraries/voxels/src/VoxelDetail.h +++ b/libraries/voxels/src/VoxelDetail.h @@ -51,4 +51,19 @@ Q_DECLARE_METATYPE(RayToVoxelIntersectionResult) QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToVoxelIntersectionResult& results); void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, RayToVoxelIntersectionResult& results); + +inline QDebug operator<<(QDebug debug, const VoxelDetail& details) { + const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe + + debug << "VoxelDetail[ (" + << details.x * (float)TREE_SCALE << "," << details.y * (float)TREE_SCALE << "," << details.z * (float)TREE_SCALE + << " ) to (" + << details.x + details.s * (float)TREE_SCALE << "," << details.y + details.s * (float)TREE_SCALE + << "," << details.z + details.s * (float)TREE_SCALE << ") size: (" + << details.s * (float)TREE_SCALE << "," << details.s * (float)TREE_SCALE << "," << details.s * (float)TREE_SCALE << ")" + << " in meters]"; + + return debug; +} + #endif // hifi_VoxelDetail_h diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 45277d6165..5143c04918 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project(Script Network) include_glm() # link in the shared libraries -link_hifi_libraries(animation fbx entities networking octree shared) +link_hifi_libraries(shared octree voxels fbx metavoxels networking particles entities avatars audio animation script-engine) link_shared_dependencies()