diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index 2198ed378a..d491434b5f 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -9,8 +9,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://static.oculus.com/sdk-downloads/ovr_sdk_win_0.5.0.1.zip - URL_MD5 d3fc4c02db9be5ff08af4ef4c97b32f9 + URL http://static.oculus.com/sdk-downloads/0.6.0.0/1431634088/ovr_sdk_win_0.6.0.0.zip + URL_MD5 a3dfdab037a854fdcf7e6033fa8d7028 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/externals/boostconfig/CMakeLists.txt b/cmake/externals/boostconfig/CMakeLists.txt index 8785e0d7c7..8494bb0114 100644 --- a/cmake/externals/boostconfig/CMakeLists.txt +++ b/cmake/externals/boostconfig/CMakeLists.txt @@ -3,13 +3,13 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) include(ExternalProject) ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://github.com/boostorg/config/archive/boost-1.58.0.zip - URL_MD5 42fa673bae2b7645a22736445e80eb8d - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 + ${EXTERNAL_NAME} + URL https://github.com/boostorg/config/archive/boost-1.58.0.zip + URL_MD5 42fa673bae2b7645a22736445e80eb8d + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) diff --git a/cmake/externals/oglplus/CMakeLists.txt b/cmake/externals/oglplus/CMakeLists.txt index 1413edce34..23ba0d515e 100644 --- a/cmake/externals/oglplus/CMakeLists.txt +++ b/cmake/externals/oglplus/CMakeLists.txt @@ -3,13 +3,13 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) include(ExternalProject) ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://softlayer-dal.dl.sourceforge.net/project/oglplus/oglplus-0.61.x/oglplus-0.61.0.zip - URL_MD5 bb55038c36c660d2b6c7be380414fa60 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 + ${EXTERNAL_NAME} + GIT_REPOSITORY https://github.com/jherico/oglplus.git + GIT_TAG 470d8e56fd6bf3913ceec03d82f42d3bafab2cbe + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 ) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) diff --git a/cmake/macros/SetupHifiOpenGL.cmake b/cmake/macros/SetupHifiOpenGL.cmake new file mode 100644 index 0000000000..c62e43c27e --- /dev/null +++ b/cmake/macros/SetupHifiOpenGL.cmake @@ -0,0 +1,43 @@ + + +macro(SETUP_HIFI_OPENGL) + + if (APPLE) + + # link in required OS X frameworks and include the right GL headers + find_library(OpenGL OpenGL) + target_link_libraries(${TARGET_NAME} ${OpenGL}) + + elseif (WIN32) + + add_dependency_external_projects(glew) + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} opengl32.lib) + + if (USE_NSIGHT) + # try to find the Nsight package and add it to the build if we find it + find_package(NSIGHT) + if (NSIGHT_FOUND) + include_directories(${NSIGHT_INCLUDE_DIRS}) + add_definitions(-DNSIGHT_FOUND) + target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}") + endif() + endif() + + elseif(ANDROID) + + target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL") + + else() + + find_package(OpenGL REQUIRED) + if (${OPENGL_INCLUDE_DIR}) + include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") + endif() + target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") + target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) + + endif() + +endmacro() diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index b6462b7fc3..a3a155871f 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -11,7 +11,6 @@ Script.load("progress.js"); Script.load("edit.js"); Script.load("selectAudioDevice.js"); -Script.load("controllers/hydra/hydraMove.js"); Script.load("inspect.js"); Script.load("lobby.js"); Script.load("notifications.js"); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index f029088b1a..27a2929d1c 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -354,7 +354,8 @@ var elZoneAtmosphereScatteringWavelengthsZ = document.getElementById("property-zone-atmosphere-scattering-wavelengths-z"); var elZoneAtmosphereHasStars = document.getElementById("property-zone-atmosphere-has-stars"); - var elPolyVoxSelections = document.querySelectorAll(".poly-vox-section"); + var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); + allSections.push(elPolyVoxSections); var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); @@ -602,6 +603,10 @@ elParticleLocalGravity.value = properties.localGravity.toFixed(2); elParticleRadius.value = properties.particleRadius.toFixed(3); } else if (properties.type == "PolyVox") { + for (var i = 0; i < elPolyVoxSections.length; i++) { + elPolyVoxSections[i].style.display = 'block'; + } + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); @@ -1014,9 +1019,9 @@
Voxel Volume Size
-
X
-
Y
-
Z
+
X
+
Y
+
Z
Surface Extractor
diff --git a/interface/resources/images/address-bar.svg b/interface/resources/images/address-bar.svg index 0a472cbe4e..dd907aa6c9 100644 --- a/interface/resources/images/address-bar.svg +++ b/interface/resources/images/address-bar.svg @@ -15,7 +15,7 @@ viewBox="0 0 1440 200" id="svg4136" inkscape:version="0.91 r13725" - sodipodi:docname="address-bar.svg"> + sodipodi:docname="address-bar.002.svg"> @@ -39,14 +39,14 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="1840" + inkscape:window-width="1835" inkscape:window-height="1057" id="namedview4140" showgrid="false" - inkscape:zoom="0.8671875" - inkscape:cx="707.02439" + inkscape:zoom="0.61319416" + inkscape:cx="132.58366" inkscape:cy="52.468468" - inkscape:window-x="72" + inkscape:window-x="77" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4136" /> @@ -59,6 +59,15 @@ y="30" rx="16.025024" ry="17.019567" /> + + diff --git a/interface/resources/images/darkgreyarrow.png b/interface/resources/images/darkgreyarrow.png new file mode 100644 index 0000000000..4c9a8a2bbf Binary files /dev/null and b/interface/resources/images/darkgreyarrow.png differ diff --git a/interface/resources/images/darkgreyarrow.svg b/interface/resources/images/darkgreyarrow.svg new file mode 100644 index 0000000000..2d049d90d7 --- /dev/null +++ b/interface/resources/images/darkgreyarrow.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/interface/resources/images/left-arrow.svg b/interface/resources/images/left-arrow.svg new file mode 100644 index 0000000000..cede9fa959 --- /dev/null +++ b/interface/resources/images/left-arrow.svg @@ -0,0 +1,50 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/lightgreyarrow.png b/interface/resources/images/lightgreyarrow.png new file mode 100644 index 0000000000..1e221f3c73 Binary files /dev/null and b/interface/resources/images/lightgreyarrow.png differ diff --git a/interface/resources/images/lightgreyarrow.svg b/interface/resources/images/lightgreyarrow.svg new file mode 100644 index 0000000000..fbe697c3e9 --- /dev/null +++ b/interface/resources/images/lightgreyarrow.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/interface/resources/images/sepline.png b/interface/resources/images/sepline.png new file mode 100644 index 0000000000..959fcee72f Binary files /dev/null and b/interface/resources/images/sepline.png differ diff --git a/interface/resources/images/sepline.svg b/interface/resources/images/sepline.svg new file mode 100644 index 0000000000..32afaf7148 --- /dev/null +++ b/interface/resources/images/sepline.svg @@ -0,0 +1,3 @@ + + +2015-06-12 18:23ZCanvas 1 Navi Bar diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 3377b20d87..70cb22dc4d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -45,19 +45,64 @@ DialogContainer { property int inputAreaHeight: 56.0 * root.scale // Height of the background's input area property int inputAreaStep: (height - inputAreaHeight) / 2 + Image { + id: backArrow + + source: "../images/left-arrow.svg" + scale: 0.9 + + anchors { + fill: parent + leftMargin: parent.height + hifi.layout.spacing + 6 + rightMargin: parent.height + hifi.layout.spacing * 60 + topMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing + bottomMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + addressBarDialog.loadBack() + } + } + } + + Image { + id: forwardArrow + + source: "../images/darkgreyarrow.svg" + + anchors { + fill: parent + leftMargin: parent.height + hifi.layout.spacing * 9 + rightMargin: parent.height + hifi.layout.spacing * 53 + topMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing + bottomMargin: parent.inputAreaStep + parent.inputAreaStep + hifi.layout.spacing + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + addressBarDialog.loadForward() + } + } + } + TextInput { id: addressLine anchors { fill: parent - leftMargin: parent.height + hifi.layout.spacing * 2 + leftMargin: parent.height + parent.height + hifi.layout.spacing * 5 rightMargin: hifi.layout.spacing * 2 topMargin: parent.inputAreaStep + hifi.layout.spacing bottomMargin: parent.inputAreaStep + hifi.layout.spacing } - font.pixelSize: hifi.fonts.pixelSize * root.scale + font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 helperText: "Go to: place, @user, /path, network address" @@ -66,7 +111,7 @@ DialogContainer { addressBarDialog.loadAddress(addressLine.text) } } - + MouseArea { // Drag the icon width: parent.height @@ -82,6 +127,7 @@ DialogContainer { } } + /* MouseArea { // Drag the input rectangle width: parent.width - parent.height @@ -95,7 +141,7 @@ DialogContainer { maximumX: root.parent ? root.maximumX : 0 maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 } - } + }*/ } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 66bc9b5c25..ad64f74056 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -115,6 +115,7 @@ #include "devices/Faceshift.h" #include "devices/Leapmotion.h" #include "devices/RealSense.h" +#include "devices/SDL2Manager.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -129,7 +130,6 @@ #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" -#include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -196,7 +196,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; Setting::Handle maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); - #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -889,6 +888,9 @@ void Application::paintGL() { } glEnable(GL_LINE_SMOOTH); + + Menu::getInstance()->setIsOptionChecked("First Person", _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); + Application::getInstance()->cameraMenuChanged(); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { // Always use the default eye position, not the actual head eye position. @@ -907,9 +909,8 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - static const float THIRD_PERSON_CAMERA_DISTANCE = 1.5f; _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * THIRD_PERSON_CAMERA_DISTANCE * _myAvatar->getScale()); + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); if (OculusManager::isConnected()) { _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); } else { @@ -1492,6 +1493,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::focusOutEvent(QFocusEvent* event) { _keyboardMouseDevice.focusOutEvent(event); + SixenseManager::getInstance().focusOutEvent(); + SDL2Manager::getInstance()->focusOutEvent(); // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { @@ -1927,11 +1930,15 @@ void Application::setEnableVRMode(bool enableVRMode) { // attempt to reconnect the Oculus manager - it's possible this was a workaround // for the sixense crash OculusManager::disconnect(); - OculusManager::connect(); + OculusManager::connect(_glWidget->context()->contextHandle()); + _glWidget->setFocus(); + _glWidget->makeCurrent(); + glClear(GL_COLOR_BUFFER_BIT); } OculusManager::recalibrate(); } else { OculusManager::abandonCalibration(); + OculusManager::disconnect(); _mirrorCamera.setHmdPosition(glm::vec3()); _mirrorCamera.setHmdRotation(glm::quat()); @@ -2167,13 +2174,6 @@ void Application::init() { _mirrorCamera.setMode(CAMERA_MODE_MIRROR); - OculusManager::connect(); - if (OculusManager::isConnected()) { - QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), - "trigger", - Qt::QueuedConnection); - } - TV3DManager::connect(); if (TV3DManager::isConnected()) { QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), @@ -2406,10 +2406,14 @@ void Application::cameraMenuChanged() { } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + _myAvatar->setBoomLength(MyAvatar::ZOOM_MIN); } } else { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); + if (_myAvatar->getBoomLength() == MyAvatar::ZOOM_MIN) { + _myAvatar->setBoomLength(MyAvatar::ZOOM_DEFAULT); + } } } } @@ -2487,7 +2491,7 @@ void Application::update(float deltaTime) { } SixenseManager::getInstance().update(deltaTime); - JoystickScriptingInterface::getInstance().update(); + SDL2Manager::getInstance()->update(); } _userInputMapper.update(deltaTime); @@ -2508,7 +2512,8 @@ void Application::update(float deltaTime) { _myAvatar->setDriveKeys(ROT_DOWN, _userInputMapper.getActionState(UserInputMapper::PITCH_DOWN)); _myAvatar->setDriveKeys(ROT_LEFT, _userInputMapper.getActionState(UserInputMapper::YAW_LEFT)); _myAvatar->setDriveKeys(ROT_RIGHT, _userInputMapper.getActionState(UserInputMapper::YAW_RIGHT)); - + _myAvatar->setDriveKeys(BOOM_IN, _userInputMapper.getActionState(UserInputMapper::BOOM_IN)); + _myAvatar->setDriveKeys(BOOM_OUT, _userInputMapper.getActionState(UserInputMapper::BOOM_OUT)); updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... @@ -3260,6 +3265,9 @@ namespace render { template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) { + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); auto skybox = model::SkyboxPointer(); @@ -3327,7 +3335,8 @@ namespace render { PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); - background->_environment->renderAtmospheres(*(args->_viewFrustum)); + + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); } } @@ -3336,13 +3345,13 @@ namespace render { skybox = skyStage->getSkybox(); if (skybox) { - gpu::Batch batch; model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox); - - gpu::GLBackend::renderBatch(batch, true); - glUseProgram(0); } } + // FIX ME - If I don't call this renderBatch() here, then the atmosphere and skybox don't render, but it + // seems like these payloadRender() methods shouldn't be doing this. We need to investigate why the engine + // isn't rendering our batch + gpu::GLBackend::renderBatch(batch, true); } } @@ -4078,7 +4087,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b198d12a6e..2590808347 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -52,6 +52,8 @@ enum DriveKeys { ROT_RIGHT, ROT_UP, ROT_DOWN, + BOOM_IN, + BOOM_OUT, MAX_DRIVE_KEYS }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6816e1fa39..9e998e7aeb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -70,6 +70,10 @@ const int SCRIPTED_MOTOR_CAMERA_FRAME = 0; const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; +const float MyAvatar::ZOOM_MIN = 0.5f; +const float MyAvatar::ZOOM_MAX = 10.0f; +const float MyAvatar::ZOOM_DEFAULT = 1.5f; + MyAvatar::MyAvatar() : Avatar(), _turningKeyPressTime(0.0f), @@ -77,6 +81,7 @@ MyAvatar::MyAvatar() : _wasPushing(false), _isPushing(false), _isBraking(false), + _boomLength(ZOOM_DEFAULT), _trapDuration(0.0f), _thrust(0.0f), _keyboardMotorVelocity(0.0f), @@ -1347,6 +1352,9 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe } } } + + _boomLength += _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN]; + _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); return newLocalVelocity; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a3dc34e6e0..7adaf908f4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -162,6 +162,13 @@ public: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } + float getBoomLength() const { return _boomLength; } + void setBoomLength(float boomLength) { _boomLength = boomLength; } + + static const float ZOOM_MIN; + static const float ZOOM_MAX; + static const float ZOOM_DEFAULT; + public slots: void increaseSize(); void decreaseSize(); @@ -210,6 +217,8 @@ private: bool _wasPushing; bool _isPushing; bool _isBraking; + + float _boomLength; float _trapDuration; // seconds that avatar has been trapped by collisions glm::vec3 _thrust; // impulse accumulator for outside sources diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp index 8b225437c2..07f3c4873c 100644 --- a/interface/src/devices/Joystick.cpp +++ b/interface/src/devices/Joystick.cpp @@ -13,8 +13,11 @@ #include +#include "Application.h" + #include "Joystick.h" +const float CONTROLLER_THRESHOLD = 0.25f; #ifdef HAVE_SDL2 const float MAX_AXIS = 32768.0f; @@ -22,10 +25,7 @@ const float MAX_AXIS = 32768.0f; Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : _sdlGameController(sdlGameController), _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), - _instanceId(instanceId), - _name(name), - _axes(QVector(SDL_JoystickNumAxes(_sdlJoystick))), - _buttons(QVector(SDL_JoystickNumButtons(_sdlJoystick))) + _instanceId(instanceId) { } @@ -42,24 +42,204 @@ void Joystick::closeJoystick() { #endif } +void Joystick::update() { + for (auto axisState : _axisStateMap) { + if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { + _axisStateMap[axisState.first] = 0.0f; + } + } +} + +void Joystick::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; #ifdef HAVE_SDL2 void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { - if (_axes.size() <= event.axis) { - _axes.resize(event.axis + 1); + SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; + + switch (axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; + _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; + break; + case SDL_CONTROLLER_AXIS_LEFTY: + _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; + _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; + break; + case SDL_CONTROLLER_AXIS_RIGHTX: + _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; + _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; + break; + case SDL_CONTROLLER_AXIS_RIGHTY: + _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; + _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; + break; + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS; + break; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS; + break; + default: + break; } - - float oldValue = _axes[event.axis]; - float newValue = event.value / MAX_AXIS; - _axes[event.axis] = newValue; - - emit axisValueChanged(event.axis, newValue, oldValue); } void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { - bool oldValue = _buttons[event.button]; + auto input = makeInput((SDL_GameControllerButton) event.button); bool newValue = event.state == SDL_PRESSED; - _buttons[event.button] = newValue; - emit buttonStateChanged(event.button, newValue, oldValue); + if (newValue) { + _buttonPressedMap.insert(input.getChannel()); + } else { + _buttonPressedMap.erase(input.getChannel()); + } } #endif + + +void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name)); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; +#ifdef HAVE_SDL2 + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); + +#endif + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + mapper.registerDevice(_deviceID, proxy); +} + +void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { +#ifdef HAVE_SDL2 + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float DPAD_MOVE_SPEED = 0.5f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BOOM_SPEED = 0.1f; + + // Y axes are flipped (up is negative) + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), JOYSTICK_YAW_SPEED); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); + + + // Hold front right shoulder button for precision controls + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_A), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_B), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); +#endif +} + +float Joystick::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } else { + return 0.0f; + } + } + return 0.0f; +} + +float Joystick::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } else { + return 0.0f; + } +} + +#ifdef HAVE_SDL2 +UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} +#endif + +UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + diff --git a/interface/src/devices/Joystick.h b/interface/src/devices/Joystick.h index eeeeb03759..c546a82aaa 100644 --- a/interface/src/devices/Joystick.h +++ b/interface/src/devices/Joystick.h @@ -20,6 +20,8 @@ #undef main #endif +#include "ui/UserInputMapper.h" + class Joystick : public QObject { Q_OBJECT @@ -29,12 +31,40 @@ class Joystick : public QObject { Q_PROPERTY(int instanceId READ getInstanceId) #endif - Q_PROPERTY(int numAxes READ getNumAxes) - Q_PROPERTY(int numButtons READ getNumButtons) public: + enum JoystickAxisChannel { + LEFT_AXIS_X_POS = 0, + LEFT_AXIS_X_NEG, + LEFT_AXIS_Y_POS, + LEFT_AXIS_Y_NEG, + RIGHT_AXIS_X_POS, + RIGHT_AXIS_X_NEG, + RIGHT_AXIS_Y_POS, + RIGHT_AXIS_Y_NEG, + RIGHT_SHOULDER, + LEFT_SHOULDER, + }; + Joystick(); ~Joystick(); + typedef std::unordered_set ButtonPressedMap; + typedef std::map AxisStateMap; + + float getButton(int channel) const; + float getAxis(int channel) const; + +#ifdef HAVE_SDL2 + UserInputMapper::Input makeInput(SDL_GameControllerButton button); +#endif + UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis); + + void registerToUserInputMapper(UserInputMapper& mapper); + void assignDefaultInputMapping(UserInputMapper& mapper); + + void update(); + void focusOutEvent(); + #ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); #endif @@ -51,15 +81,8 @@ public: int getInstanceId() const { return _instanceId; } #endif - const QVector& getAxes() const { return _axes; } - const QVector& getButtons() const { return _buttons; } + int getDeviceID() { return _deviceID; } - 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: #ifdef HAVE_SDL2 SDL_GameController* _sdlGameController; @@ -68,8 +91,12 @@ private: #endif QString _name; - QVector _axes; - QVector _buttons; + +protected: + int _deviceID = 0; + + ButtonPressedMap _buttonPressedMap; + AxisStateMap _axisStateMap; }; -#endif // hifi_JoystickTracker_h +#endif // hifi_Joystick_h diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index 8a336064e5..fcb60cca26 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -160,8 +160,8 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { _deviceID = mapper.getFreeDeviceID(); auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard")); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { @@ -170,7 +170,33 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } + for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + } availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); + + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); + return availableInputs; }; proxy->resetDeviceBindings = [this, &mapper] () -> bool { @@ -189,7 +215,7 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { const float MOUSE_PITCH_SPEED = 0.25f; const float TOUCH_YAW_SPEED = 0.5f; const float TOUCH_PITCH_SPEED = 0.25f; - //const float BUTTON_BOOM_SPEED = 0.1f; + const float BUTTON_BOOM_SPEED = 0.1f; // AWSD keys mapping mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED); @@ -199,8 +225,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED); - // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); - // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); @@ -216,8 +242,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); - // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); - // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); @@ -246,8 +272,8 @@ void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); // Wheel move - //mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); - //mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index a166866dd0..196f74025c 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -34,7 +37,6 @@ #include "InterfaceLogging.h" #include "Application.h" -#include template void for_each_eye(Function function) { @@ -53,27 +55,133 @@ void for_each_eye(const ovrHmd & hmd, Function function) { } } -#ifdef OVR_CLIENT_DISTORTION -ProgramObject OculusManager::_program; -int OculusManager::_textureLocation; -int OculusManager::_eyeToSourceUVScaleLocation; -int OculusManager::_eyeToSourceUVOffsetLocation; -int OculusManager::_eyeRotationStartLocation; -int OculusManager::_eyeRotationEndLocation; -int OculusManager::_positionAttributeLocation; -int OculusManager::_colorAttributeLocation; -int OculusManager::_texCoord0AttributeLocation; -int OculusManager::_texCoord1AttributeLocation; -int OculusManager::_texCoord2AttributeLocation; -ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2]; -GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 }; -GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 }; -GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 }; -ovrFrameTiming OculusManager::_hmdFrameTiming; -bool OculusManager::_programInitialized = false; -#endif + +#ifdef Q_OS_WIN + +// A base class for FBO wrappers that need to use the Oculus C +// API to manage textures via ovrHmd_CreateSwapTextureSetGL, +// ovrHmd_CreateMirrorTextureGL, etc +template +struct RiftFramebufferWrapper : public FramebufferWrapper { + ovrHmd hmd; + RiftFramebufferWrapper(const ovrHmd & hmd) : hmd(hmd) { + color = 0; + depth = 0; + }; + + void Resize(const uvec2 & size) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + this->size = size; + initColor(); + initDone(); + } + +protected: + virtual void initDepth() override final { + } +}; + +// A wrapper for constructing and using a swap texture set, +// where each frame you draw to a texture via the FBO, +// then submit it and increment to the next texture. +// The Oculus SDK manages the creation and destruction of +// the textures +struct SwapFramebufferWrapper : public RiftFramebufferWrapper { + SwapFramebufferWrapper(const ovrHmd & hmd) + : RiftFramebufferWrapper(hmd) { + } + + ~SwapFramebufferWrapper() { + if (color) { + ovrHmd_DestroySwapTextureSet(hmd, color); + color = nullptr; + } + } + + void Increment() { + ++color->CurrentIndex; + color->CurrentIndex %= color->TextureCount; + } + +protected: + virtual void initColor() override { + if (color) { + ovrHmd_DestroySwapTextureSet(hmd, color); + color = nullptr; + } + + ovrResult result = ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color); + Q_ASSERT(OVR_SUCCESS(result)); + + for (int i = 0; i < color->TextureCount; ++i) { + ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i]; + glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + glBindTexture(GL_TEXTURE_2D, 0); + } + + virtual void initDone() override { + } + + virtual void onBind(oglplus::Framebuffer::Target target) override { + ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]); + glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0); + } + + virtual void onUnbind(oglplus::Framebuffer::Target target) override { + glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + } +}; + + +// We use a FBO to wrap the mirror texture because it makes it easier to +// render to the screen via glBlitFramebuffer +struct MirrorFramebufferWrapper : public RiftFramebufferWrapper { + MirrorFramebufferWrapper(const ovrHmd & hmd) + : RiftFramebufferWrapper(hmd) { + } + + virtual ~MirrorFramebufferWrapper() { + if (color) { + ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + color = nullptr; + } + } + +private: + void initColor() override { + if (color) { + ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + color = nullptr; + } + ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color); + Q_ASSERT(OVR_SUCCESS(result)); + } + + void initDone() override { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo)); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color->OGL.TexId, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } +}; + +SwapFramebufferWrapper* OculusManager::_swapFbo{ nullptr }; +MirrorFramebufferWrapper* OculusManager::_mirrorFbo{ nullptr }; +ovrLayerEyeFov OculusManager::_sceneLayer; + +#else ovrTexture OculusManager::_eyeTextures[ovrEye_Count]; +GlWindow* OculusManager::_outputWindow{ nullptr }; + +#endif + bool OculusManager::_isConnected = false; ovrHmd OculusManager::_ovrHmd; ovrFovPort OculusManager::_eyeFov[ovrEye_Count]; @@ -104,146 +212,178 @@ bool OculusManager::_eyePerFrameMode = false; ovrEyeType OculusManager::_lastEyeRendered = ovrEye_Count; ovrSizei OculusManager::_recommendedTexSize = { 0, 0 }; float OculusManager::_offscreenRenderScale = 1.0; - - -void OculusManager::initSdk() { - ovr_Initialize(); - _ovrHmd = ovrHmd_Create(0); - if (!_ovrHmd) { - _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2); - } -} - -void OculusManager::shutdownSdk() { - if (_ovrHmd) { - ovrHmd_Destroy(_ovrHmd); - _ovrHmd = nullptr; - ovr_Shutdown(); - } -} +ovrRecti OculusManager::_eyeViewports[ovrEye_Count]; void OculusManager::init() { -#ifdef OVR_DIRECT_MODE - initSdk(); -#endif } void OculusManager::deinit() { -#ifdef OVR_DIRECT_MODE - shutdownSdk(); -#endif } -void OculusManager::connect() { -#ifndef OVR_DIRECT_MODE - initSdk(); -#endif - _calibrationState = UNCALIBRATED; +void OculusManager::connect(QOpenGLContext* shareContext) { qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING; - if (_ovrHmd) { - if (!_isConnected) { - UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); - } - _isConnected = true; - for_each_eye([&](ovrEyeType eye) { - _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; - }); + ovrInitParams initParams; memset(&initParams, 0, sizeof(initParams)); - ovrGLConfig cfg; - memset(&cfg, 0, sizeof(cfg)); - cfg.OGL.Header.API = ovrRenderAPI_OpenGL; - cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution; - cfg.OGL.Header.Multisample = 1; - - int distortionCaps = 0 - | ovrDistortionCap_Vignette - | ovrDistortionCap_Overdrive - | ovrDistortionCap_TimeWarp; - - int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config, - distortionCaps, _eyeFov, _eyeRenderDesc); - assert(configResult); - - - _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); - _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; - for_each_eye([&](ovrEyeType eye) { - //Get texture size - _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL; - _eyeTextures[eye].Header.TextureSize = _renderTargetSize; - _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 }; - }); - _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w; - - ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); - - ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | - ovrTrackingCap_MagYawCorrection, - ovrTrackingCap_Orientation); - - if (!_camera) { - _camera = new Camera; - configureCamera(*_camera); // no need to use screen dimensions; they're ignored - } -#ifdef OVR_CLIENT_DISTORTION - if (!_programInitialized) { - // Shader program - _programInitialized = true; - _program.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/oculus.vert"); - _program.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/oculus.frag"); - _program.link(); - - // Uniforms - _textureLocation = _program.uniformLocation("texture"); - _eyeToSourceUVScaleLocation = _program.uniformLocation("EyeToSourceUVScale"); - _eyeToSourceUVOffsetLocation = _program.uniformLocation("EyeToSourceUVOffset"); - _eyeRotationStartLocation = _program.uniformLocation("EyeRotationStart"); - _eyeRotationEndLocation = _program.uniformLocation("EyeRotationEnd"); - - // Attributes - _positionAttributeLocation = _program.attributeLocation("position"); - _colorAttributeLocation = _program.attributeLocation("color"); - _texCoord0AttributeLocation = _program.attributeLocation("texCoord0"); - _texCoord1AttributeLocation = _program.attributeLocation("texCoord1"); - _texCoord2AttributeLocation = _program.attributeLocation("texCoord2"); - } - - //Generate the distortion VBOs - generateDistortionMesh(); +#ifdef DEBUG + initParams.Flags |= ovrInit_Debug; #endif - } else { + + ovr_Initialize(&initParams); + +#ifdef Q_OS_WIN + + ovrResult res = ovrHmd_Create(0, &_ovrHmd); +#ifdef DEBUG + if (!OVR_SUCCESS(res)) { + res = ovrHmd_CreateDebug(ovrHmd_DK2, &_ovrHmd); + Q_ASSERT(OVR_SUCCESS(res)); + } +#endif + +#else + + _ovrHmd = ovrHmd_Create(0); +#ifdef DEBUG + if (!_ovrHmd) { + _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2); + } +#endif + +#endif + + if (!_ovrHmd) { _isConnected = false; // we're definitely not in "VR mode" so tell the menu that Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); + ovr_Shutdown(); + return; + } + + _calibrationState = UNCALIBRATED; + if (!_isConnected) { + UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); + } + _isConnected = true; + + for_each_eye([&](ovrEyeType eye) { + _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye]; + }); + + _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f); + _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; + +#ifdef Q_OS_WIN + + _mirrorFbo = new MirrorFramebufferWrapper(_ovrHmd); + _swapFbo = new SwapFramebufferWrapper(_ovrHmd); + _swapFbo->Init(toGlm(_renderTargetSize)); + _sceneLayer.ColorTexture[0] = _swapFbo->color; + _sceneLayer.ColorTexture[1] = nullptr; + _sceneLayer.Viewport[0].Pos = { 0, 0 }; + _sceneLayer.Viewport[0].Size = _recommendedTexSize; + _sceneLayer.Viewport[1].Pos = { _recommendedTexSize.w, 0 }; + _sceneLayer.Viewport[1].Size = _recommendedTexSize; + _sceneLayer.Header.Type = ovrLayerType_EyeFov; + _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; + for_each_eye([&](ovrEyeType eye) { + _eyeViewports[eye] = _sceneLayer.Viewport[eye]; + _sceneLayer.Fov[eye] = _eyeFov[eye]; + }); + + + +#else + _outputWindow = new GlWindow(shareContext); + _outputWindow->show(); +// _outputWindow->setFlags(Qt::FramelessWindowHint ); +// _outputWindow->resize(_ovrHmd->Resolution.w, _ovrHmd->Resolution.h); +// _outputWindow->setPosition(_ovrHmd->WindowsPos.x, _ovrHmd->WindowsPos.y); + ivec2 desiredPosition = toGlm(_ovrHmd->WindowsPos); + foreach(QScreen* screen, qGuiApp->screens()) { + ivec2 screenPosition = toGlm(screen->geometry().topLeft()); + if (screenPosition == desiredPosition) { + _outputWindow->setScreen(screen); + break; + } + } + _outputWindow->showFullScreen(); + _outputWindow->makeCurrent(); + + ovrGLConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.OGL.Header.API = ovrRenderAPI_OpenGL; + cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution; + cfg.OGL.Header.Multisample = 0; + + int distortionCaps = 0 + | ovrDistortionCap_Vignette + | ovrDistortionCap_Overdrive + | ovrDistortionCap_TimeWarp; + + int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config, + distortionCaps, _eyeFov, _eyeRenderDesc); + assert(configResult); + _outputWindow->doneCurrent(); + + for_each_eye([&](ovrEyeType eye) { + //Get texture size + _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL; + _eyeTextures[eye].Header.TextureSize = _renderTargetSize; + _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 }; + _eyeTextures[eye].Header.RenderViewport.Size = _renderTargetSize; + _eyeTextures[eye].Header.RenderViewport.Size.w /= 2; + }); + _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w; + for_each_eye([&](ovrEyeType eye) { + _eyeViewports[eye] = _eyeTextures[eye].Header.RenderViewport; + }); +#endif + + ovrHmd_SetEnabledCaps(_ovrHmd, + ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction); + + ovrHmd_ConfigureTracking(_ovrHmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, + ovrTrackingCap_Orientation); + + if (!_camera) { + _camera = new Camera; + configureCamera(*_camera); // no need to use screen dimensions; they're ignored } } //Disconnects and deallocates the OR void OculusManager::disconnect() { if (_isConnected) { + +#ifdef Q_OS_WIN + if (_swapFbo) { + delete _swapFbo; + _swapFbo = nullptr; + } + + if (_mirrorFbo) { + delete _mirrorFbo; + _mirrorFbo = nullptr; + } +#else + _outputWindow->showNormal(); + _outputWindow->deleteLater(); + _outputWindow = nullptr; +#endif + + if (_ovrHmd) { + ovrHmd_Destroy(_ovrHmd); + _ovrHmd = nullptr; + } + ovr_Shutdown(); + _isConnected = false; // Prepare to potentially have to dismiss the HSW again // if the user re-enables VR _hswDismissed = false; -#ifndef OVR_DIRECT_MODE - shutdownSdk(); -#endif - -#ifdef OVR_CLIENT_DISTORTION - //Free the distortion mesh data - for (int i = 0; i < ovrEye_Count; i++) { - if (_vertices[i] != 0) { - glDeleteBuffers(1, &(_vertices[i])); - _vertices[i] = 0; - } - if (_indices[i] != 0) { - glDeleteBuffers(1, &(_indices[i])); - _indices[i] = 0; - } - } -#endif } } @@ -346,7 +486,6 @@ void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { break; default: break; - } } @@ -363,64 +502,8 @@ void OculusManager::abandonCalibration() { } } -#ifdef OVR_CLIENT_DISTORTION -void OculusManager::generateDistortionMesh() { - - //Check if we already have the distortion mesh - if (_vertices[0] != 0) { - printf("WARNING: Tried to generate Oculus distortion mesh twice without freeing the VBOs."); - return; - } - - for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) { - // Allocate and generate distortion mesh vertices - ovrDistortionMesh meshData; - ovrHmd_CreateDistortionMesh(_ovrHmd, _eyeRenderDesc[eyeNum].Eye, _eyeRenderDesc[eyeNum].Fov, _ovrHmd->DistortionCaps, &meshData); - - // Parse the vertex data and create a render ready vertex buffer - DistortionVertex* pVBVerts = new DistortionVertex[meshData.VertexCount]; - _meshSize[eyeNum] = meshData.IndexCount; - - // Convert the oculus vertex data to the DistortionVertex format. - DistortionVertex* v = pVBVerts; - ovrDistortionVertex* ov = meshData.pVertexData; - for (unsigned int vertNum = 0; vertNum < meshData.VertexCount; vertNum++) { - v->pos.x = ov->ScreenPosNDC.x; - v->pos.y = ov->ScreenPosNDC.y; - v->texR.x = ov->TanEyeAnglesR.x; - v->texR.y = ov->TanEyeAnglesR.y; - v->texG.x = ov->TanEyeAnglesG.x; - v->texG.y = ov->TanEyeAnglesG.y; - v->texB.x = ov->TanEyeAnglesB.x; - v->texB.y = ov->TanEyeAnglesB.y; - v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f); - v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f); - v++; - ov++; - } - - //vertices - glGenBuffers(1, &(_vertices[eyeNum])); - glBindBuffer(GL_ARRAY_BUFFER, _vertices[eyeNum]); - glBufferData(GL_ARRAY_BUFFER, sizeof(DistortionVertex) * meshData.VertexCount, pVBVerts, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - //indices - glGenBuffers(1, &(_indices[eyeNum])); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices[eyeNum]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * meshData.IndexCount, meshData.pIndexData, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - //Now that we have the VBOs we can get rid of the mesh data - delete [] pVBVerts; - ovrHmd_DestroyDistortionMesh(&meshData); - } - -} -#endif - bool OculusManager::isConnected() { - return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode); + return _isConnected; } //Begins the frame timing for oculus prediction purposes @@ -428,10 +511,6 @@ void OculusManager::beginFrameTiming() { if (_frameTimingActive) { printf("WARNING: Called OculusManager::beginFrameTiming() twice in a row, need to call OculusManager::endFrameTiming()."); } - -#ifdef OVR_CLIENT_DISTORTION - _hmdFrameTiming = ovrHmd_BeginFrameTiming(_ovrHmd, _frameIndex); -#endif _frameTimingActive = true; } @@ -441,9 +520,6 @@ bool OculusManager::allowSwap() { //Ends frame timing void OculusManager::endFrameTiming() { -#ifdef OVR_CLIENT_DISTORTION - ovrHmd_EndFrameTiming(_ovrHmd); -#endif _frameIndex++; _frameTimingActive = false; } @@ -474,39 +550,8 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const oldFrameIndex = _frameIndex; #endif - // Every so often do some additional timing calculations and debug output - bool debugFrame = 0 == _frameIndex % 400; +#ifndef Q_OS_WIN -#if 0 - // Try to measure the amount of time taken to do the distortion - // (does not seem to work on OSX with SDK based distortion) - // FIXME can't use a static object here, because it will cause a crash when the - // query attempts deconstruct after the GL context is gone. - static bool timerActive = false; - static QOpenGLTimerQuery timerQuery; - if (!timerQuery.isCreated()) { - timerQuery.create(); - } - - if (timerActive && timerQuery.isResultAvailable()) { - auto result = timerQuery.waitForResult(); - if (result) { qCDebug(interfaceapp) << "Distortion took " << result << "ns"; }; - timerActive = false; - } -#endif - -#ifdef OVR_DIRECT_MODE - static bool attached = false; - if (!attached) { - attached = true; - void * nativeWindowHandle = (void*)(size_t)glCanvas->effectiveWinId(); - if (nullptr != nativeWindowHandle) { - ovrHmd_AttachToWindow(_ovrHmd, nativeWindowHandle, nullptr, nullptr); - } - } -#endif - -#ifndef OVR_CLIENT_DISTORTION // FIXME: we need a better way of responding to the HSW. In particular // we need to ensure that it's only displayed once per session, rather than // every time the user toggles VR mode, and we need to hook it up to actual @@ -523,7 +568,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const } #endif - //beginFrameTiming must be called before display if (!_frameTimingActive) { printf("WARNING: Called OculusManager::display() without calling OculusManager::beginFrameTiming() first."); @@ -565,7 +609,9 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const static ovrVector3f eyeOffsets[2] = { { 0, 0, 0 }, { 0, 0, 0 } }; ovrPosef eyePoses[ovrEye_Count]; ovrHmd_GetEyePoses(_ovrHmd, _frameIndex, eyeOffsets, eyePoses, nullptr); +#ifndef Q_OS_WIN ovrHmd_BeginFrame(_ovrHmd, _frameIndex); +#endif static ovrPosef eyeRenderPose[ovrEye_Count]; //Render each eye into an fbo for_each_eye(_ovrHmd, [&](ovrEyeType eye){ @@ -607,10 +653,9 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; + ovrRecti & vp = _eyeViewports[eye]; vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; - glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); renderArgs->_renderSide = RenderArgs::MONO; @@ -638,52 +683,53 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const // restore our normal viewport glViewport(0, 0, deviceSize.width(), deviceSize.height()); -#if 0 - if (debugFrame && !timerActive) { - timerQuery.begin(); - } -#endif +#ifdef Q_OS_WIN + auto srcFboSize = finalFbo->getSize(); -#ifdef OVR_CLIENT_DISTORTION - - //Wait till time-warp to reduce latency - ovr_WaitTillTime(_hmdFrameTiming.TimewarpPointSeconds); - -#ifdef DEBUG_RENDER_WITHOUT_DISTORTION - auto fboSize = finalFbo->getSize(); + // Blit to the oculus provided texture glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); + _swapFbo->Bound(oglplus::Framebuffer::Target::Draw, [&] { + glBlitFramebuffer( + 0, 0, srcFboSize.x, srcFboSize.y, + 0, 0, _swapFbo->size.x, _swapFbo->size.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + }); + + // Blit to the onscreen window + auto destWindowSize = qApp->getDeviceSize(); glBlitFramebuffer( - 0, 0, fboSize.x, fboSize.y, - 0, 0, deviceSize.width(), deviceSize.height(), + 0, 0, srcFboSize.x, srcFboSize.y, + 0, 0, destWindowSize.width(), destWindowSize.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); -#else - //Clear the color buffer to ensure that there isnt any residual color - //Left over from when OR was not connected. - glClear(GL_COLOR_BUFFER_BIT); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0))); - //Renders the distorted mesh onto the screen - renderDistortionMesh(eyeRenderPose); - glBindTexture(GL_TEXTURE_2D, 0); -#endif - glCanvas->swapBuffers(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); -#else + // Submit the frame to the Oculus SDK for timewarp and distortion + for_each_eye([&](ovrEyeType eye) { + _sceneLayer.RenderPose[eye] = eyeRenderPose[eye]; + }); + auto header = &_sceneLayer.Header; + ovrResult res = ovrHmd_SubmitFrame(_ovrHmd, _frameIndex, nullptr, &header, 1); + Q_ASSERT(OVR_SUCCESS(res)); + _swapFbo->Increment(); +#else + GLsync syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + _outputWindow->makeCurrent(); + // force the compositing context to wait for the texture + // rendering to complete before it starts the distortion rendering, + // but without triggering a CPU/GPU synchronization + glWaitSync(syncObject, 0, GL_TIMEOUT_IGNORED); + + GLuint textureId = gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)); for_each_eye([&](ovrEyeType eye) { ovrGLTexture & glEyeTexture = reinterpret_cast(_eyeTextures[eye]); - glEyeTexture.OGL.TexId = finalFbo->texture(); - + glEyeTexture.OGL.TexId = textureId; }); + // restore our normal viewport ovrHmd_EndFrame(_ovrHmd, eyeRenderPose, _eyeTextures); - -#endif - -#if 0 - if (debugFrame && !timerActive) { - timerQuery.end(); - timerActive = true; - } + glCanvas->makeCurrent(); #endif // No DK2, no message. @@ -773,8 +819,11 @@ void OculusManager::renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]) { glEnable(GL_DEPTH_TEST); _program.release(); glBindBuffer(GL_ARRAY_BUFFER, 0); +======= + + +>>>>>>> db56e15410520adab59d0e54348d9b32d17d808a } -#endif //Tries to reconnect to the sensors void OculusManager::reset() { @@ -783,21 +832,6 @@ void OculusManager::reset() { } } -//Gets the current predicted angles from the oculus sensors -void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { - ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); - if (ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) { - glm::vec3 euler = glm::eulerAngles(toGlm(ts.HeadPose.ThePose.Orientation)); - yaw = euler.y; - pitch = euler.x; - roll = euler.z; - } else { - yaw = 0.0f; - pitch = 0.0f; - roll = 0.0f; - } -} - glm::vec3 OculusManager::getRelativePosition() { ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); return toGlm(trackingState.HeadPose.ThePose.Position); @@ -828,6 +862,9 @@ void OculusManager::overrideOffAxisFrustum(float& left, float& right, float& bot } int OculusManager::getHMDScreen() { +#ifdef Q_OS_WIN + return -1; +#else int hmdScreenIndex = -1; // unknown // TODO: it might be smarter to handle multiple HMDs connected in this case. but for now, // we will simply assume the initialization code that set up _ovrHmd picked the best hmd @@ -878,5 +915,6 @@ int OculusManager::getHMDScreen() { } } return hmdScreenIndex; +#endif } diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 799f07ea35..b82379a7b3 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -22,30 +22,16 @@ #include "RenderArgs.h" +class QOpenGLContext; + class Camera; +class GlWindow; class PalmData; class Text3DOverlay; -// Uncomment this to enable client side distortion. NOT recommended since -// the Oculus SDK will ideally provide the best practices for distortion in -// in terms of performance and quality, and by using it we will get updated -// best practices for free with new runtime releases. -#define OVR_CLIENT_DISTORTION 1 - - -// Direct HMD mode is currently only supported on windows and some linux systems will -// misbehave if we try to enable the Oculus SDK at all, so isolate support for Direct -// mode only to windows for now #ifdef Q_OS_WIN -// On Win32 platforms, enabling Direct HMD requires that the SDK be -// initialized before the GL context is set up, but this breaks v-sync -// for any application that has a Direct mode enable Rift connected -// but is not rendering to it. For the time being I'm setting this as -// a macro enabled mechanism which changes where the SDK is initialized. -// To enable Direct HMD mode, you can un-comment this, but with the -// caveat that it will break v-sync in NON-VR mode if you have an Oculus -// Rift connect and in Direct mode -#define OVR_DIRECT_MODE 1 +struct SwapFramebufferWrapper; +struct MirrorFramebufferWrapper; #endif @@ -54,7 +40,7 @@ class OculusManager { public: static void init(); static void deinit(); - static void connect(); + static void connect(QOpenGLContext* shareContext); static void disconnect(); static bool isConnected(); static void recalibrate(); @@ -66,10 +52,6 @@ public: static void display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera); static void reset(); - /// param \yaw[out] yaw in radians - /// param \pitch[out] pitch in radians - /// param \roll[out] roll in radians - static void getEulerAngles(float& yaw, float& pitch, float& roll); static glm::vec3 getRelativePosition(); static glm::quat getOrientation(); static QSize getRenderTargetSize(); @@ -85,44 +67,7 @@ public: private: static void initSdk(); static void shutdownSdk(); -#ifdef OVR_CLIENT_DISTORTION - static void generateDistortionMesh(); - static void renderDistortionMesh(ovrPosef eyeRenderPose[ovrEye_Count]); - struct DistortionVertex { - glm::vec2 pos; - glm::vec2 texR; - glm::vec2 texG; - glm::vec2 texB; - struct { - GLubyte r; - GLubyte g; - GLubyte b; - GLubyte a; - } color; - }; - static ProgramObject _program; - //Uniforms - static int _textureLocation; - static int _eyeToSourceUVScaleLocation; - static int _eyeToSourceUVOffsetLocation; - static int _eyeRotationStartLocation; - static int _eyeRotationEndLocation; - //Attributes - static int _positionAttributeLocation; - static int _colorAttributeLocation; - static int _texCoord0AttributeLocation; - static int _texCoord1AttributeLocation; - static int _texCoord2AttributeLocation; - static ovrVector2f _UVScaleOffset[ovrEye_Count][2]; - static GLuint _vertices[ovrEye_Count]; - static GLuint _indices[ovrEye_Count]; - static GLsizei _meshSize[ovrEye_Count]; - static ovrFrameTiming _hmdFrameTiming; - static bool _programInitialized; -#endif - - static ovrTexture _eyeTextures[ovrEye_Count]; static bool _isConnected; static glm::vec3 _eyePositions[ovrEye_Count]; static ovrHmd _ovrHmd; @@ -130,6 +75,7 @@ private: static ovrVector3f _eyeOffset[ovrEye_Count]; static glm::mat4 _eyeProjection[ovrEye_Count]; static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count]; + static ovrRecti _eyeViewports[ovrEye_Count]; static ovrSizei _renderTargetSize; static unsigned int _frameIndex; static bool _frameTimingActive; @@ -162,6 +108,14 @@ private: static float _offscreenRenderScale; static bool _eyePerFrameMode; static ovrEyeType _lastEyeRendered; +#ifdef Q_OS_WIN + static SwapFramebufferWrapper* _swapFbo; + static MirrorFramebufferWrapper* _mirrorFbo; + static ovrLayerEyeFov _sceneLayer; +#else + static ovrTexture _eyeTextures[ovrEye_Count]; + static GlWindow* _outputWindow; +#endif }; @@ -181,6 +135,10 @@ inline glm::vec2 toGlm(const ovrVector2f & ov) { return glm::make_vec2(&ov.x); } +inline glm::ivec2 toGlm(const ovrVector2i & ov) { + return glm::ivec2(ov.x, ov.y); +} + inline glm::uvec2 toGlm(const ovrSizei & ov) { return glm::uvec2(ov.w, ov.h); } diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/devices/SDL2Manager.cpp similarity index 62% rename from interface/src/scripting/JoystickScriptingInterface.cpp rename to interface/src/devices/SDL2Manager.cpp index 0490b1f704..e039a80b0d 100644 --- a/interface/src/scripting/JoystickScriptingInterface.cpp +++ b/interface/src/devices/SDL2Manager.cpp @@ -1,73 +1,66 @@ // -// JoystickScriptingInterface.cpp +// SDL2Manager.cpp // interface/src/devices // -// Created by Andrzej Kapolka on 5/15/14. -// Copyright 2014 High Fidelity, Inc. +// Created by Sam Gondelman on 6/5/15. +// Copyright 2015 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 - -#ifdef HAVE_SDL2 -#include -#undef main -#endif #include #include #include -#include "Application.h" +#include "Application.h" -#include "JoystickScriptingInterface.h" +#include "SDL2Manager.h" #ifdef HAVE_SDL2 -SDL_JoystickID getInstanceId(SDL_GameController* controller) { +SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); return SDL_JoystickInstanceID(joystick); } #endif -JoystickScriptingInterface& JoystickScriptingInterface::getInstance() { - static JoystickScriptingInterface sharedInstance; - return sharedInstance; -} - -JoystickScriptingInterface::JoystickScriptingInterface() : +SDL2Manager::SDL2Manager() : #ifdef HAVE_SDL2 - _openJoysticks(), +_openJoysticks(), #endif - _isInitialized(false) +_isInitialized(false) { #ifdef HAVE_SDL2 bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0); if (initSuccess) { int joystickCount = SDL_NumJoysticks(); - + for (int i = 0; i < joystickCount; i++) { SDL_GameController* controller = SDL_GameControllerOpen(i); if (controller) { SDL_JoystickID id = getInstanceId(controller); - Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - _openJoysticks[id] = joystick; + if (!_openJoysticks.contains(id)) { + Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + _openJoysticks[id] = joystick; + joystick->registerToUserInputMapper(*Application::getUserInputMapper()); + joystick->assignDefaultInputMapping(*Application::getUserInputMapper()); + emit joystickAdded(joystick); + } } } - + _isInitialized = true; } else { - qDebug() << "Error initializing SDL"; + qDebug() << "Error initializing SDL2 Manager"; } #endif } -JoystickScriptingInterface::~JoystickScriptingInterface() { +SDL2Manager::~SDL2Manager() { #ifdef HAVE_SDL2 qDeleteAll(_openJoysticks); @@ -75,34 +68,27 @@ JoystickScriptingInterface::~JoystickScriptingInterface() { #endif } -const QObjectList JoystickScriptingInterface::getAllJoysticks() const { - QObjectList objectList; -#ifdef HAVE_SDL2 - const QList joystickList = _openJoysticks.values(); - for (int i = 0; i < joystickList.length(); i++) { - objectList << joystickList[i]; - } -#endif - return objectList; +SDL2Manager* SDL2Manager::getInstance() { + static SDL2Manager sharedInstance; + return &sharedInstance; } -Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { +void SDL2Manager::focusOutEvent() { #ifdef HAVE_SDL2 - QMap::iterator iter = _openJoysticks.begin(); - while (iter != _openJoysticks.end()) { - if (iter.value()->getName() == name) { - return iter.value(); - } - iter++; + for (auto joystick : _openJoysticks) { + joystick->focusOutEvent(); } #endif - return NULL; } -void JoystickScriptingInterface::update() { +void SDL2Manager::update() { #ifdef HAVE_SDL2 if (_isInitialized) { - PerformanceTimer perfTimer("JoystickScriptingInterface::update"); + for (auto joystick : _openJoysticks) { + joystick->update(); + } + + PerformanceTimer perfTimer("SDL2Manager::update"); SDL_GameControllerUpdate(); SDL_Event event; while (SDL_PollEvent(&event)) { @@ -120,16 +106,16 @@ void JoystickScriptingInterface::update() { if (event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) { // this will either start or stop a global back event QEvent::Type backType = (event.type == SDL_CONTROLLERBUTTONDOWN) - ? HFBackEvent::startType() - : HFBackEvent::endType(); + ? HFBackEvent::startType() + : HFBackEvent::endType(); HFBackEvent backEvent(backType); qApp->sendEvent(qApp, &backEvent); } else if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A) { // this will either start or stop a global action event QEvent::Type actionType = (event.type == SDL_CONTROLLERBUTTONDOWN) - ? HFActionEvent::startType() - : HFActionEvent::endType(); + ? HFActionEvent::startType() + : HFActionEvent::endType(); // global action events fire in the center of the screen Application* app = Application::getInstance(); @@ -141,14 +127,19 @@ void JoystickScriptingInterface::update() { } else if (event.type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); - + SDL_JoystickID id = getInstanceId(controller); - Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - _openJoysticks[id] = joystick; - emit joystickAdded(joystick); + if (!_openJoysticks.contains(id)) { + Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + _openJoysticks[id] = joystick; + joystick->registerToUserInputMapper(*Application::getUserInputMapper()); + joystick->assignDefaultInputMapping(*Application::getUserInputMapper()); + emit joystickAdded(joystick); + } } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { Joystick* joystick = _openJoysticks[event.cdevice.which]; _openJoysticks.remove(event.cdevice.which); + Application::getUserInputMapper()->removeDevice(joystick->getDeviceID()); emit joystickRemoved(joystick); } } diff --git a/interface/src/scripting/JoystickScriptingInterface.h b/interface/src/devices/SDL2Manager.h similarity index 53% rename from interface/src/scripting/JoystickScriptingInterface.h rename to interface/src/devices/SDL2Manager.h index c9a68d24b1..0a32570ee2 100644 --- a/interface/src/scripting/JoystickScriptingInterface.h +++ b/interface/src/devices/SDL2Manager.h @@ -1,77 +1,46 @@ // -// JoystickScriptingInterface.h +// SDL2Manager.h // interface/src/devices // -// Created by Andrzej Kapolka on 5/15/14. -// Copyright 2014 High Fidelity, Inc. +// Created by Sam Gondelman on 6/5/15. +// Copyright 2015 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 +#ifndef hifi__SDL2Manager_h +#define hifi__SDL2Manager_h #ifdef HAVE_SDL2 #include #endif +#include "ui/UserInputMapper.h" + #include "devices/Joystick.h" -/// Handles joystick input through SDL. -class JoystickScriptingInterface : public QObject { +class SDL2Manager : public QObject { Q_OBJECT - -#ifdef HAVE_SDL2 - Q_PROPERTY(int AXIS_INVALID READ axisInvalid) - Q_PROPERTY(int AXIS_LEFT_X READ axisLeftX) - Q_PROPERTY(int AXIS_LEFT_Y READ axisLeftY) - Q_PROPERTY(int AXIS_RIGHT_X READ axisRightX) - Q_PROPERTY(int AXIS_RIGHT_Y READ axisRightY) - Q_PROPERTY(int AXIS_TRIGGER_LEFT READ axisTriggerLeft) - Q_PROPERTY(int AXIS_TRIGGER_RIGHT READ axisTriggerRight) - Q_PROPERTY(int AXIS_MAX READ axisMax) - - Q_PROPERTY(int BUTTON_INVALID READ buttonInvalid) - Q_PROPERTY(int BUTTON_FACE_BOTTOM READ buttonFaceBottom) - Q_PROPERTY(int BUTTON_FACE_RIGHT READ buttonFaceRight) - Q_PROPERTY(int BUTTON_FACE_LEFT READ buttonFaceLeft) - Q_PROPERTY(int BUTTON_FACE_TOP READ buttonFaceTop) - Q_PROPERTY(int BUTTON_BACK READ buttonBack) - Q_PROPERTY(int BUTTON_GUIDE READ buttonGuide) - Q_PROPERTY(int BUTTON_START READ buttonStart) - Q_PROPERTY(int BUTTON_LEFT_STICK READ buttonLeftStick) - Q_PROPERTY(int BUTTON_RIGHT_STICK READ buttonRightStick) - Q_PROPERTY(int BUTTON_LEFT_SHOULDER READ buttonLeftShoulder) - Q_PROPERTY(int BUTTON_RIGHT_SHOULDER READ buttonRightShoulder) - Q_PROPERTY(int BUTTON_DPAD_UP READ buttonDpadUp) - Q_PROPERTY(int BUTTON_DPAD_DOWN READ buttonDpadDown) - Q_PROPERTY(int BUTTON_DPAD_LEFT READ buttonDpadLeft) - Q_PROPERTY(int BUTTON_DPAD_RIGHT READ buttonDpadRight) - Q_PROPERTY(int BUTTON_MAX READ buttonMax) - - Q_PROPERTY(int BUTTON_PRESSED READ buttonPressed) - Q_PROPERTY(int BUTTON_RELEASED READ buttonRelease) -#endif - + public: - static JoystickScriptingInterface& getInstance(); - + SDL2Manager(); + ~SDL2Manager(); + + void focusOutEvent(); + void update(); - -public slots: - Joystick* joystickWithName(const QString& name); - const QObjectList getAllJoysticks() const; - + + static SDL2Manager* getInstance(); + signals: void joystickAdded(Joystick* joystick); void joystickRemoved(Joystick* joystick); - + private: #ifdef HAVE_SDL2 + SDL_JoystickID getInstanceId(SDL_GameController* controller); + int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; } int axisLeftX() const { return SDL_CONTROLLER_AXIS_LEFTX; } int axisLeftY() const { return SDL_CONTROLLER_AXIS_LEFTY; } @@ -80,7 +49,7 @@ private: int axisTriggerLeft() const { return SDL_CONTROLLER_AXIS_TRIGGERLEFT; } int axisTriggerRight() const { return SDL_CONTROLLER_AXIS_TRIGGERRIGHT; } int axisMax() const { return SDL_CONTROLLER_AXIS_MAX; } - + int buttonInvalid() const { return SDL_CONTROLLER_BUTTON_INVALID; } int buttonFaceBottom() const { return SDL_CONTROLLER_BUTTON_A; } int buttonFaceRight() const { return SDL_CONTROLLER_BUTTON_B; } @@ -98,18 +67,15 @@ private: int buttonDpadLeft() const { return SDL_CONTROLLER_BUTTON_DPAD_LEFT; } int buttonDpadRight() const { return SDL_CONTROLLER_BUTTON_DPAD_RIGHT; } int buttonMax() const { return SDL_CONTROLLER_BUTTON_MAX; } - + int buttonPressed() const { return SDL_PRESSED; } int buttonRelease() const { return SDL_RELEASED; } #endif - - JoystickScriptingInterface(); - ~JoystickScriptingInterface(); - + #ifdef HAVE_SDL2 QMap _openJoysticks; #endif bool _isInitialized; }; -#endif // hifi_JoystickScriptingInterface_h +#endif // hifi__SDL2Manager_h diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 07b554ca4c..32c5a7c157 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -19,6 +19,10 @@ #include "UserActivityLogger.h" #include "InterfaceLogging.h" +// These bits aren't used for buttons, so they can be used as masks: +const unsigned int LEFT_MASK = 0; +const unsigned int RIGHT_MASK = 1U << 1; + #ifdef HAVE_SIXENSE const int CALIBRATION_STATE_IDLE = 0; @@ -61,6 +65,8 @@ SixenseManager::SixenseManager() : _bumperPressed[1] = false; _oldX[1] = -1; _oldY[1] = -1; + _prevPalms[0] = nullptr; + _prevPalms[1] = nullptr; } SixenseManager::~SixenseManager() { @@ -147,6 +153,7 @@ void SixenseManager::update(float deltaTime) { #ifdef HAVE_SIXENSE Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); if (_isInitialized && _isEnabled) { + _buttonPressedMap.clear(); #ifdef __APPLE__ SixenseBaseFunction sixenseGetNumActiveControllers = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); @@ -154,12 +161,24 @@ void SixenseManager::update(float deltaTime) { if (sixenseGetNumActiveControllers() == 0) { _hydrasConnected = false; + if (_deviceID != 0) { + Application::getUserInputMapper()->removeDevice(_deviceID); + _deviceID = 0; + if (_prevPalms[0]) { + _prevPalms[0]->setActive(false); + } + if (_prevPalms[1]) { + _prevPalms[1]->setActive(false); + } + } return; } PerformanceTimer perfTimer("sixense"); if (!_hydrasConnected) { _hydrasConnected = true; + registerToUserInputMapper(*Application::getUserInputMapper()); + getInstance().assignDefaultInputMapping(*Application::getUserInputMapper()); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } @@ -198,6 +217,7 @@ void SixenseManager::update(float deltaTime) { for (size_t j = 0; j < hand->getNumPalms(); j++) { if (hand->getPalms()[j].getSixenseID() == data->controller_index) { palm = &(hand->getPalms()[j]); + _prevPalms[numActiveControllers - 1] = palm; foundHand = true; } } @@ -206,6 +226,7 @@ void SixenseManager::update(float deltaTime) { hand->getPalms().push_back(newPalm); palm = &(hand->getPalms()[hand->getNumPalms() - 1]); palm->setSixenseID(data->controller_index); + _prevPalms[numActiveControllers - 1] = palm; qCDebug(interfaceapp, "Found new Sixense controller, ID %i", data->controller_index); } @@ -216,12 +237,14 @@ void SixenseManager::update(float deltaTime) { palm->setActive(false); // if this isn't a Sixsense ID palm, always make it inactive } - // Read controller buttons and joystick into the hand palm->setControllerButtons(data->buttons); palm->setTrigger(data->trigger); palm->setJoystick(data->joystick_x, data->joystick_y); + handleButtonEvent(data->buttons, numActiveControllers - 1); + handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); + // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) { emulateMouse(palm, numActiveControllers - 1); @@ -590,3 +613,143 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { #endif // HAVE_SIXENSE +void SixenseManager::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { + _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f; + _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f; + _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f; + _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f; + _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger; +} + +void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { + if (buttons & BUTTON_0) { + _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); + } + if (buttons & BUTTON_1) { + _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); + } + if (buttons & BUTTON_2) { + _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); + } + if (buttons & BUTTON_3) { + _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); + } + if (buttons & BUTTON_4) { + _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); + } + if (buttons & BUTTON_FWD) { + _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); + } + if (buttons & BUTTON_TRIGGER) { + _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel()); + } +} + +void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Hydra")); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2")); + + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right")); + availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press")); + + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + mapper.registerDevice(_deviceID, proxy); +} + +void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BUTTON_MOVE_SPEED = 1.0f; + const float BOOM_SPEED = 0.1f; + + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED); + + // Buttons + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); + + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); +} + +float SixenseManager::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } else { + return 0.0f; + } + } + return 0.0f; +} + +float SixenseManager::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } else { + return 0.0f; + } +} + +UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) { + return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) { + return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); +} diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 6f47f2fac9..c27b3ca0e2 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -13,6 +13,7 @@ #define hifi_SixenseManager_h #include +#include #ifdef HAVE_SIXENSE #include @@ -25,6 +26,8 @@ #endif +#include "ui/UserInputMapper.h" + class PalmData; const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 @@ -33,6 +36,7 @@ const unsigned int BUTTON_2 = 1U << 6; const unsigned int BUTTON_3 = 1U << 3; const unsigned int BUTTON_4 = 1U << 4; const unsigned int BUTTON_FWD = 1U << 7; +const unsigned int BUTTON_TRIGGER = 1U << 8; // Event type that represents using the controller const unsigned int CONTROLLER_0_EVENT = 1500U; @@ -45,6 +49,14 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public QObject { Q_OBJECT public: + enum JoystickAxisChannel { + AXIS_Y_POS = 1U << 0, + AXIS_Y_NEG = 1U << 3, + AXIS_X_POS = 1U << 4, + AXIS_X_NEG = 1U << 5, + BACK_TRIGGER = 1U << 6, + }; + static SixenseManager& getInstance(); void initialize(); @@ -60,6 +72,21 @@ public: bool getInvertButtons() const { return _invertButtons; } void setInvertButtons(bool invertSixenseButtons) { _invertButtons = invertSixenseButtons; } + typedef std::unordered_set ButtonPressedMap; + typedef std::map AxisStateMap; + + float getButton(int channel) const; + float getAxis(int channel) const; + + UserInputMapper::Input makeInput(unsigned int button, int index); + UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); + + void registerToUserInputMapper(UserInputMapper& mapper); + void assignDefaultInputMapping(UserInputMapper& mapper); + + void update(); + void focusOutEvent(); + public slots: void toggleSixense(bool shouldEnable); void setFilter(bool filter); @@ -69,6 +96,8 @@ private: SixenseManager(); ~SixenseManager(); + void handleButtonEvent(unsigned int buttons, int index); + void handleAxisEvent(float x, float y, float trigger, int index); #ifdef HAVE_SIXENSE void updateCalibration(const sixenseControllerData* controllers); void emulateMouse(PalmData* palm, int index); @@ -104,12 +133,19 @@ private: bool _bumperPressed[2]; int _oldX[2]; int _oldY[2]; + PalmData* _prevPalms[2]; bool _lowVelocityFilter; bool _controllersAtBase; float _reticleMoveSpeed = DEFAULT_SIXENSE_RETICLE_MOVE_SPEED; bool _invertButtons = DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS; + +protected: + int _deviceID = 0; + + ButtonPressedMap _buttonPressedMap; + AxisStateMap _axisStateMap; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 3c3a843586..80f51ff906 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -35,6 +35,16 @@ void AddressBarDialog::loadAddress(const QString& address) { } } +void AddressBarDialog::loadBack() { + qDebug() << "Called LoadBack"; + DependencyManager::get()->goBack(); +} + +void AddressBarDialog::loadForward() { + qDebug() << "Called LoadForward"; + DependencyManager::get()->goForward(); +} + void AddressBarDialog::displayAddressOfflineMessage() { OffscreenUi::error("That user or place is currently offline"); } diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 395cf3c725..da54e29929 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -28,6 +28,8 @@ protected: void hide(); Q_INVOKABLE void loadAddress(const QString& address); + Q_INVOKABLE void loadBack(); + Q_INVOKABLE void loadForward(); }; #endif diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 4b79892d0d..2b58ad93fc 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -376,7 +376,7 @@ void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { textureAspectRatio = _textureAspectRatio; _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); - } + } with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { _overlays.render(); @@ -605,7 +605,6 @@ void ApplicationOverlay::renderPointers() { _lastMouseMove = usecTimestampNow(); } else if (usecTimestampNow() - _lastMouseMove > MAX_IDLE_TIME * USECS_PER_SECOND) { //float pitch = 0.0f, yaw = 0.0f, roll = 0.0f; // radians - //OculusManager::getEulerAngles(yaw, pitch, roll); glm::quat orientation = qApp->getHeadOrientation(); // (glm::vec3(pitch, yaw, roll)); glm::vec3 result; diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index 0dfbb202da..9afd0f51c7 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -56,8 +56,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : this->QDialog::setLayout(form); - _wasMoved = false; - _previousRect = Application::getInstance()->getWindow()->rect(); Application::getInstance()->getWindow()->activateWindow(); // watch for our application window moving screens. If it does we want to update our screen details @@ -136,24 +134,6 @@ void HMDToolsDialog::enterHDMMode() { if (!_inHDMMode) { _switchModeButton->setText("Leave HMD Mode"); _debugDetails->setText(getDebugDetails()); - - _hmdScreenNumber = OculusManager::getHMDScreen(); - - if (_hmdScreenNumber >= 0) { - QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); - _hmdScreen = QGuiApplication::screens()[_hmdScreenNumber]; - - _previousRect = Application::getInstance()->getWindow()->rect(); - _previousRect = QRect(mainWindow->mapToGlobal(_previousRect.topLeft()), - mainWindow->mapToGlobal(_previousRect.bottomRight())); - _previousScreen = mainWindow->screen(); - QRect rect = QApplication::desktop()->screenGeometry(_hmdScreenNumber); - mainWindow->setScreen(_hmdScreen); - mainWindow->setGeometry(rect); - - _wasMoved = true; - } - // if we're on a single screen setup, then hide our tools window when entering HMD mode if (QApplication::desktop()->screenCount() == 1) { @@ -161,58 +141,21 @@ void HMDToolsDialog::enterHDMMode() { } Application::getInstance()->setEnableVRMode(true); - - const int SLIGHT_DELAY = 500; - // If we go to fullscreen immediately, it ends up on the primary monitor, - // even though we've already moved the window. By adding this delay, the - // fullscreen target screen ends up correct. - QTimer::singleShot(SLIGHT_DELAY, this, [&]{ - Application::getInstance()->setFullscreen(true); - activateWindowAfterEnterMode(); - }); - + _inHDMMode = true; } } -void HMDToolsDialog::activateWindowAfterEnterMode() { - Application::getInstance()->getWindow()->activateWindow(); - - // center the cursor on the main application window - centerCursorOnWidget(Application::getInstance()->getWindow()); -} - void HMDToolsDialog::leaveHDMMode() { if (_inHDMMode) { _switchModeButton->setText("Enter HMD Mode"); _debugDetails->setText(getDebugDetails()); - Application::getInstance()->setEnableVRMode(false); - Application::getInstance()->setFullscreen(false); Application::getInstance()->getWindow()->activateWindow(); - - if (_wasMoved) { - QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); - mainWindow->setScreen(_previousScreen); - mainWindow->setGeometry(_previousRect); - - const int SLIGHT_DELAY = 1500; - QTimer::singleShot(SLIGHT_DELAY, this, SLOT(moveWindowAfterLeaveMode())); - } - _wasMoved = false; _inHDMMode = false; } } -void HMDToolsDialog::moveWindowAfterLeaveMode() { - QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); - mainWindow->setScreen(_previousScreen); - mainWindow->setGeometry(_previousRect); - Application::getInstance()->getWindow()->activateWindow(); - Application::getInstance()->resetSensors(); -} - - void HMDToolsDialog::reject() { // Just regularly close upon ESC close(); @@ -244,13 +187,15 @@ void HMDToolsDialog::hideEvent(QHideEvent* event) { void HMDToolsDialog::aboutToQuit() { if (_inHDMMode) { + // FIXME this is ineffective because it doesn't trigger the menu to + // save the fact that VR Mode is not checked. leaveHDMMode(); } } void HMDToolsDialog::screenCountChanged(int newCount) { if (!OculusManager::isConnected()) { - OculusManager::connect(); + //OculusManager::connect(); } int hmdScreenNumber = OculusManager::getHMDScreen(); diff --git a/interface/src/ui/HMDToolsDialog.h b/interface/src/ui/HMDToolsDialog.h index 10dc1c5c80..c16957f2d7 100644 --- a/interface/src/ui/HMDToolsDialog.h +++ b/interface/src/ui/HMDToolsDialog.h @@ -35,8 +35,6 @@ signals: public slots: void reject(); void switchModeClicked(bool checked); - void activateWindowAfterEnterMode(); - void moveWindowAfterLeaveMode(); void applicationWindowScreenChanged(QScreen* screen); void aboutToQuit(); void screenCountChanged(int newCount); @@ -51,8 +49,6 @@ private: void enterHDMMode(); void leaveHDMMode(); - bool _wasMoved; - QRect _previousRect; QScreen* _previousScreen; QScreen* _hmdScreen; int _hmdScreenNumber; diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index e994b3cf30..2c28b79c75 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -106,6 +106,11 @@ void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { } } +void UserInputMapper::removeDevice(int device) { + removeAllInputChannelsForDevice((uint16) device); + _registeredDevices.erase(device); +} + int UserInputMapper::getInputChannels(InputChannels& channels) const { for (auto& channel : _actionToInputsMap) { channels.push_back(channel.second); diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index 0a08e277db..84ca192e3e 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -189,6 +189,7 @@ public: bool removeInputChannel(InputChannel channel); void removeAllInputChannels(); void removeAllInputChannelsForDevice(uint16 device); + void removeDevice(int device); //Grab all the input channels currently in use, return the number int getInputChannels(InputChannels& channels) const; QVector getAllInputsForDevice(uint16 device); diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3e3e823737..e7b043f44f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -43,72 +43,90 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); + glm::quat rotation; + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + rotation *= getRotation(); + } else { + rotation = getRotation(); + } - glEnable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); - glBindTexture(GL_TEXTURE_2D, _texture->getID()); + QRect fromImage; + if (_fromImage.isNull()) { + fromImage.setX(0); + fromImage.setY(0); + fromImage.setWidth(imageWidth); + fromImage.setHeight(imageHeight); + } else { + float scaleX = imageWidth / _texture->getOriginalWidth(); + float scaleY = imageHeight / _texture->getOriginalHeight(); - glPushMatrix(); { - glTranslatef(_position.x, _position.y, _position.z); - glm::quat rotation; - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - rotation *= getRotation(); - } else { - rotation = getRotation(); - } - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(_scale, _scale, _scale); + fromImage.setX(scaleX * _fromImage.x()); + fromImage.setY(scaleY * _fromImage.y()); + fromImage.setWidth(scaleX * _fromImage.width()); + fromImage.setHeight(scaleY * _fromImage.height()); + } - const float MAX_COLOR = 255.0f; - xColor color = getColor(); - float alpha = getAlpha(); + float maxSize = glm::max(fromImage.width(), fromImage.height()); + float x = fromImage.width() / (2.0f * maxSize); + float y = -fromImage.height() / (2.0f * maxSize); - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); + glm::vec2 topLeft(-x, -y); + glm::vec2 bottomRight(x, y); + glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); + glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, + (fromImage.y() + fromImage.height()) / imageHeight); - QRect fromImage; - if (_fromImage.isNull()) { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); - float scaleY = imageHeight / _texture->getOriginalHeight(); + const float MAX_COLOR = 255.0f; + xColor color = getColor(); + float alpha = getAlpha(); - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setY(scaleY * _fromImage.y()); - fromImage.setWidth(scaleX * _fromImage.width()); - fromImage.setHeight(scaleY * _fromImage.height()); - } + auto batch = args->_batch; - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = fromImage.width() / (2.0f * maxSize); - float y = -fromImage.height() / (2.0f * maxSize); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(rotation); + transform.setScale(_scale); - glm::vec2 topLeft(-x, -y); - glm::vec2 bottomRight(x, y); - glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); - glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, - (fromImage.y() + fromImage.height()) / imageHeight); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + batch->setModelTransform(transform); + batch->setUniformTexture(0, _texture->getGPUTexture()); + + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me + } else { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); - } glPopMatrix(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE_2D); - glEnable(GL_LIGHTING); - glDisable(GL_ALPHA_TEST); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); - glBindTexture(GL_TEXTURE_2D, 0); + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glScalef(_scale, _scale, _scale); + + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + } glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void BillboardOverlay::setProperties(const QScriptValue &properties) { diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 6fc9fe6e27..37b30a5bcb 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -35,13 +35,6 @@ void Cube3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; @@ -49,91 +42,161 @@ void Cube3DOverlay::render(RenderArgs* args) { //glDisable(GL_LIGHTING); - // TODO: handle registration point?? + // TODO: handle registration point?? glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); - - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - if (_isSolid) { - if (_borderSize > 0) { - // Draw a cube at a larger size behind the main cube, creating - // a border effect. - // Disable writing to the depth mask so that the "border" cube will not - // occlude the main cube. This means the border could be covered by - // overlays that are further back and drawn later, but this is good - // enough for the use-case. - glDepthMask(GL_FALSE); - glPushMatrix(); - glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } else { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } + auto batch = args->_batch; - glPopMatrix(); - glDepthMask(GL_TRUE); - } + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); + if (_isSolid) { + // if (_borderSize > 0) { + // // Draw a cube at a larger size behind the main cube, creating + // // a border effect. + // // Disable writing to the depth mask so that the "border" cube will not + // // occlude the main cube. This means the border could be covered by + // // overlays that are further back and drawn later, but this is good + // // enough for the use-case. + // transform.setScale(dimensions * _borderSize); + // batch->setModelTransform(transform); + // DependencyManager::get()->renderSolidCube(*batch, 1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + // } + + transform.setScale(dimensions); + batch->setModelTransform(transform); + DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); + } else { + + if (getIsDashedLine()) { + transform.setScale(1.0f); + batch->setModelTransform(transform); + + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor); - glPushMatrix(); - glScalef(dimensions.x, dimensions.y, dimensions.z); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } else { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } - glPopMatrix(); } else { - glLineWidth(_lineWidth); - - if (getIsDashedLine()) { - glm::vec3 halfDimensions = dimensions / 2.0f; - glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); - glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); - - glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); - glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); - - auto geometryCache = DependencyManager::get(); - - geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); - - geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); - geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); - - geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); - - } else { - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderWireCube(1.0f, cubeColor); - } + transform.setScale(dimensions); + batch->setModelTransform(transform); + DependencyManager::get()->renderWireCube(*batch, 1.0f, cubeColor); } - glPopMatrix(); - glPopMatrix(); + } + } else { + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - if (glower) { - delete glower; + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + if (_isSolid) { + if (_borderSize > 0) { + // Draw a cube at a larger size behind the main cube, creating + // a border effect. + // Disable writing to the depth mask so that the "border" cube will not + // occlude the main cube. This means the border could be covered by + // overlays that are further back and drawn later, but this is good + // enough for the use-case. + glDepthMask(GL_FALSE); + glPushMatrix(); + glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); + + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } else { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } + + glPopMatrix(); + glDepthMask(GL_TRUE); + } + + glPushMatrix(); + glScalef(dimensions.x, dimensions.y, dimensions.z); + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } else { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } + glPopMatrix(); + } else { + glLineWidth(_lineWidth); + + if (getIsDashedLine()) { + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); + + } else { + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderWireCube(1.0f, cubeColor); + } + } + glPopMatrix(); + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index b39b2c3274..e68e5b47f2 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -36,87 +36,128 @@ void Grid3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - if (!_gridProgram.isLinked()) { - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.link()) { - qDebug() << "Failed to link: " + _gridProgram.log(); - return; - } - } - - // Render code largely taken from MetavoxelEditor::render() - glDisable(GL_LIGHTING); - - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = getRotation(); - - glm::vec3 axis = glm::axis(rotation); - - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glLineWidth(1.5f); - - // center the grid around the camera position on the plane - glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition(); - float spacing = _minorGridWidth; - - float alpha = getAlpha(); - xColor color = getColor(); - glm::vec3 position = getPosition(); - const int MINOR_GRID_DIVISIONS = 200; const int MAJOR_GRID_DIVISIONS = 100; const float MAX_COLOR = 255.0f; + // center the grid around the camera position on the plane + glm::vec3 rotated = glm::inverse(_rotation) * Application::getInstance()->getCamera()->getPosition(); + float spacing = _minorGridWidth; + + float alpha = getAlpha(); + xColor color = getColor(); glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - _gridProgram.bind(); + auto batch = args->_batch; - // Minor grid - glPushMatrix(); - { - glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + if (batch) { + Transform transform; + transform.setRotation(_rotation); - float scale = MINOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + // Minor grid + { + batch->_glLineWidth(1.0f); + auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MINOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + + // Major grid + { + batch->_glLineWidth(4.0f); + spacing *= _majorGridEvery; + auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MAJOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + } else { + if (!_gridProgram.isLinked()) { + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.link()) { + qDebug() << "Failed to link: " + _gridProgram.log(); + return; + } + } + + // Render code largely taken from MetavoxelEditor::render() + glDisable(GL_LIGHTING); + + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getRotation(); + + glm::vec3 axis = glm::axis(rotation); + + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + glLineWidth(1.5f); + + glm::vec3 position = getPosition(); + + _gridProgram.bind(); + + // Minor grid + glPushMatrix(); + { + glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + + float scale = MINOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + // Major grid + glPushMatrix(); + { + glLineWidth(4.0f); + spacing *= _majorGridEvery; + glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); + + float scale = MAJOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); } - glPopMatrix(); - - // Major grid - glPushMatrix(); - { - glLineWidth(4.0f); - spacing *= _majorGridEvery; - glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); - - float scale = MAJOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); - } - glPopMatrix(); - - _gridProgram.release(); - - glPopMatrix(); - - glEnable(GL_LIGHTING); - glDepthMask(GL_TRUE); } void Grid3DOverlay::setProperties(const QScriptValue& properties) { diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 6672a88e45..34c983d7b2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -37,41 +37,57 @@ void Line3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - - glPushMatrix(); - - glDisable(GL_LIGHTING); - glLineWidth(_lineWidth); - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glm::vec3 position = getPosition(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + batch->setModelTransform(transform); - if (getIsDashedLine()) { - // TODO: add support for color to renderDashedLine() - DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(*batch, _position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); + } } else { - DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); - } - glEnable(GL_LIGHTING); + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPopMatrix(); + glPushMatrix(); - if (glower) { - delete glower; + glDisable(GL_LIGHTING); + glLineWidth(_lineWidth); + + glm::vec3 position = getPosition(); + glm::quat rotation = getRotation(); + + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); + } + glEnable(GL_LIGHTING); + + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index d5e4b34f6b..bcfba67313 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -66,8 +66,8 @@ namespace render { } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { - glPushMatrix(); if (overlay->getAnchor() == Overlay::MY_AVATAR) { + glPushMatrix(); MyAvatar* avatar = DependencyManager::get()->getMyAvatar(); glm::quat myAvatarRotation = avatar->getOrientation(); glm::vec3 myAvatarPosition = avatar->getPosition(); @@ -78,9 +78,11 @@ namespace render { glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z); glRotatef(angle, axis.x, axis.y, axis.z); glScalef(myAvatarScale, myAvatarScale, myAvatarScale); + overlay->render(args); + glPopMatrix(); + } else { + overlay->render(args); } - overlay->render(args); - glPopMatrix(); } } } diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 8662030e23..dc5fdeabb2 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -41,74 +41,116 @@ void Rectangle3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; glm::quat rotation = getRotation(); - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + auto batch = args->_batch; - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - //glScalef(dimensions.x, dimensions.y, 1.0f); + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); - glLineWidth(_lineWidth); + batch->setModelTransform(transform); + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, rectangleColor); + } else { auto geometryCache = DependencyManager::get(); + if (getIsDashedLine()) { + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... - if (getIsSolid()) { - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); - DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor); + geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor); + geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor); + geometryCache->renderDashedLine(*batch, point4, point1, rectangleColor); } else { - if (getIsDashedLine()) { + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); - glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - - geometryCache->renderDashedLine(point1, point2, rectangleColor); - geometryCache->renderDashedLine(point2, point3, rectangleColor); - geometryCache->renderDashedLine(point3, point4, rectangleColor); - geometryCache->renderDashedLine(point4, point1, rectangleColor); - - } else { - - if (halfDimensions != _previousHalfDimensions) { - QVector border; - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - - _previousHalfDimensions = halfDimensions; - - } - geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + _previousHalfDimensions = halfDimensions; } + geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID); } - + } + } else { + glDisable(GL_LIGHTING); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + //glScalef(dimensions.x, dimensions.y, 1.0f); + + glLineWidth(_lineWidth); + + auto geometryCache = DependencyManager::get(); + + // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + } else { + if (getIsDashedLine()) { + + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); + + geometryCache->renderDashedLine(point1, point2, rectangleColor); + geometryCache->renderDashedLine(point2, point3, rectangleColor); + geometryCache->renderDashedLine(point3, point4, rectangleColor); + geometryCache->renderDashedLine(point4, point1, rectangleColor); + + } else { + + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); + + _previousHalfDimensions = halfDimensions; + + } + geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + } + } + + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index a0e8d06b41..f5fba0ed05 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,33 +39,45 @@ void Sphere3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - - glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + transform.setScale(_dimensions); + + batch->setModelTransform(transform); + DependencyManager::get()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid); + } else { + glDisable(GL_LIGHTING); + + glm::vec3 position = getPosition(); + glm::vec3 center = getCenter(); + glm::vec3 dimensions = getDimensions(); + glm::quat rotation = getRotation(); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5504d0593f..5a39239948 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1020,7 +1020,10 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { localOutput->moveToThread(injector->getLocalBuffer()->thread()); // have it be stopped when that local buffer is about to close - connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop); + connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged); + connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop); + connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop); + connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); qCDebug(audioclient) << "Starting QAudioOutput for local injector" << localOutput; @@ -1338,3 +1341,9 @@ void AudioClient::saveSettings() { windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction()); repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade()); } + +void AudioClient::audioStateChanged(QAudio::State state) { + if (state == QAudio::IdleState) { + emit audioFinished(); + } +} diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index d291334cce..aeea7c07c1 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -188,6 +188,8 @@ signals: void receivedFirstPacket(); void disconnected(); + void audioFinished(); + protected: AudioClient(); ~AudioClient(); @@ -196,6 +198,9 @@ protected: deleteLater(); } +private slots: + void audioStateChanged(QAudio::State state); + private: void outputFormatChanged(); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 912351e21a..675a3b8b28 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -60,7 +60,6 @@ void AudioInjector::setIsFinished(bool isFinished) { if (_shouldDeleteAfterFinish) { // we've been asked to delete after finishing, trigger a queued deleteLater here - qCDebug(audio) << "AudioInjector triggering delete from setIsFinished"; QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); } } @@ -122,9 +121,6 @@ void AudioInjector::injectLocally() { success = _localAudioInterface->outputLocalInjector(_options.stereo, this); - // if we're not looping and the buffer tells us it is empty then emit finished - connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop); - if (!success) { qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; } diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp index f40e613437..27e93a678e 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.cpp +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -69,10 +69,7 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { _currentOffset += bytesRead; } - if (!_shouldLoop && bytesRead == bytesToEnd) { - // we hit the end of the buffer, emit a signal - emit bufferEmpty(); - } else if (_shouldLoop && _currentOffset == _rawAudioArray.size()) { + if (_shouldLoop && _currentOffset == _rawAudioArray.size()) { _currentOffset = 0; } diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h index 9753cbbd83..32a1221b78 100644 --- a/libraries/audio/src/AudioInjectorLocalBuffer.h +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -32,9 +32,6 @@ public: void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; } void setVolume(float volume) { _volume = glm::clamp(volume, 0.0f, 1.0f); } -signals: - void bufferEmpty(); - private: qint64 recursiveReadFromFront(char* data, qint64 maxSize); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3e83c6f559..e1bd01547e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -48,6 +48,9 @@ #include "RenderablePolyVoxEntityItem.h" #include "EntitiesRendererLogging.h" +#include "DependencyManager.h" +#include "AddressManager.h" + EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : OctreeRenderer(), @@ -911,15 +914,13 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI bool precisionPicking = false; // for mouse moves we do not do precision picking RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { + //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); - // load the entity script if needed... QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mouseMoveEvent").isValid()) { entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs); } - - //qCDebug(entitiesrenderer) << "mouseMoveEvent over entity:" << rayPickResult.entityID; emit mouseMoveOnEntity(rayPickResult, event, deviceID); if (entityScript.property("mouseMoveOnEntity").isValid()) { entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index f82078428d..9b22f7e1e9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -129,7 +129,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData->setBorderValue(255); #ifdef WANT_DEBUG - qDebug() << " new size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); + qDebug() << " new voxel-space size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); #endif // I'm not sure this is needed... the docs say that each element is initialized with its default @@ -228,12 +228,15 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { return _volData->getVoxelAt(x, y, z); } -void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { +void RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { + // set a voxel without recompressing the voxel data assert(_volData); if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { return; } + updateOnCount(x, y, z, toValue); + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { @@ -242,6 +245,14 @@ void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) } +void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + if (_locked) { + return; + } + setVoxelInternal(x, y, z, toValue); + compressVolumeData(); +} + void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { // keep _onCount up to date if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { @@ -263,11 +274,15 @@ void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV } void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { + if (_locked) { + return; + } + for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -275,11 +290,19 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { - updateOnCount(position.x, position.y, position.z, toValue); - setVoxel(position.x, position.y, position.z, toValue); + if (_locked) { + return; + } + + // same as setVoxel but takes a vector rather than 3 floats. + setVoxel((int)position.x, (int)position.y, (int)position.z, toValue); } void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { + if (_locked) { + return; + } + // This three-level for loop iterates over every voxel in the volume for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { @@ -291,7 +314,7 @@ void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi // If the current voxel is less than 'radius' units from the center then we make it solid. if (fDistToCenter <= radius) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -388,10 +411,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { getModel(); } - Transform transform; - transform.setTranslation(getPosition() - getRegistrationPoint() * getDimensions()); - transform.setRotation(getRotation()); - transform.setScale(getDimensions() / _voxelVolumeSize); + Transform transform(voxelToWorldMatrix()); auto mesh = _modelGeometry.getMesh(); Q_ASSERT(args->_batch); @@ -522,6 +542,11 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { QByteArray newVoxelData; QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + + #ifdef WANT_DEBUG + qDebug() << "compressing voxel data of size:" << voxelXSize << voxelYSize << voxelZSize; + #endif + writer << voxelXSize << voxelYSize << voxelZSize; QByteArray compressedData = qCompress(uncompressedData, 9); @@ -581,7 +606,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { for (int x = 0; x < voxelXSize; x++) { int uncompressedIndex = (z * voxelYSize * voxelXSize) + (y * voxelZSize) + x; updateOnCount(x, y, z, uncompressedData[uncompressedIndex]); - setVoxel(x, y, z, uncompressedData[uncompressedIndex]); + setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); } } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 77aeb24f2a..814f3deb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -73,11 +73,12 @@ public: virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue); SIMPLE_RENDERABLE(); - + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. + void setVoxelInternal(int x, int y, int z, uint8_t toValue); void compressVolumeData(); void decompressVolumeData(); diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp index 051accc732..bb96b9698d 100644 --- a/libraries/entities/src/DeleteEntityOperator.cpp +++ b/libraries/entities/src/DeleteEntityOperator.cpp @@ -95,6 +95,7 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) { EntityItemPointer theEntity = details.entity; bool entityDeleted = entityTreeElement->removeEntityItem(theEntity); // remove it from the element assert(entityDeleted); + (void)entityDeleted; // quite warning _tree->setContainingElement(details.entity->getEntityItemID(), NULL); // update or id to element lookup _foundCount++; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 056cae5296..da5f96f503 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -67,12 +67,12 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _simulatorIDChangedTime(0), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _name(ENTITY_ITEM_DEFAULT_NAME), + _href(""), + _description(""), _dirtyFlags(0), _element(nullptr), _physicsInfo(nullptr), - _simulated(false), - _href(""), - _description("") + _simulated(false) { quint64 now = usecTimestampNow(); _lastSimulated = now; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 95ab7d1035..84fbf11311 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -23,7 +23,7 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32); const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f; -const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); // XXX +const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData()); const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = PolyVoxEntityItem::SURFACE_MARCHING_CUBES; @@ -31,6 +31,25 @@ EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const return EntityItemPointer(new PolyVoxEntityItem(entityID, properties)); } + + + +QByteArray PolyVoxEntityItem::makeEmptyVoxelData(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) { + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + QByteArray newVoxelData; + QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + writer << voxelXSize << voxelYSize << voxelZSize; + + QByteArray compressedData = qCompress(uncompressedData, 9); + writer << compressedData; + + return newVoxelData; +} + + + PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID), _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index bf8214675b..e5d511c087 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -12,14 +12,14 @@ #ifndef hifi_PolyVoxEntityItem_h #define hifi_PolyVoxEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class PolyVoxEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - + ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity @@ -29,22 +29,22 @@ class PolyVoxEntityItem : public EntityItem { // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - + // never have a ray intersection pick a PolyVoxEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { return false; } virtual void debugDump() const; @@ -58,12 +58,12 @@ class PolyVoxEntityItem : public EntityItem { enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, - SURFACE_EDGED_CUBIC + SURFACE_EDGED_CUBIC }; virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { - setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); + setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } @@ -82,10 +82,11 @@ class PolyVoxEntityItem : public EntityItem { virtual void setAll(uint8_t toValue) {} virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue) {} - + virtual uint8_t getVoxel(int x, int y, int z) { return 0; } virtual void setVoxel(int x, int y, int z, uint8_t toValue) {} + static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16); protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 405ea9459d..bbd2121c54 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -150,6 +150,8 @@ public: void _glUseProgram(GLuint program); void _glUniform1f(GLint location, GLfloat v0); void _glUniform2f(GLint location, GLfloat v0, GLfloat v1); + void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); @@ -210,6 +212,8 @@ public: COMMAND_glUseProgram, COMMAND_glUniform1f, COMMAND_glUniform2f, + COMMAND_glUniform3f, + COMMAND_glUniform3fv, COMMAND_glUniform4fv, COMMAND_glUniformMatrix4fv, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index da6979fb27..20a6b60901 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -61,6 +61,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUseProgram), (&::gpu::GLBackend::do_glUniform1f), (&::gpu::GLBackend::do_glUniform2f), + (&::gpu::GLBackend::do_glUniform3f), + (&::gpu::GLBackend::do_glUniform3fv), (&::gpu::GLBackend::do_glUniform4fv), (&::gpu::GLBackend::do_glUniformMatrix4fv), @@ -462,6 +464,7 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) { DO_IT_NOW(_glUniform2f, 1); } + void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that @@ -475,6 +478,56 @@ void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + ADD_COMMAND_GL(glUniform3f); + + _params.push_back(v2); + _params.push_back(v1); + _params.push_back(v0); + _params.push_back(location); + + DO_IT_NOW(_glUniform3f, 1); +} + +void GLBackend::do_glUniform3f(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3f( + batch._params[paramOffset + 3]._int, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void) CHECK_GL_ERROR(); +} + +void Batch::_glUniform3fv(GLint location, GLsizei count, const GLfloat* value) { + ADD_COMMAND_GL(glUniform3fv); + + const int VEC3_SIZE = 3 * sizeof(float); + _params.push_back(cacheData(count * VEC3_SIZE, value)); + _params.push_back(count); + _params.push_back(location); + + DO_IT_NOW(_glUniform3fv, 3); +} +void GLBackend::do_glUniform3fv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3fv( + batch._params[paramOffset + 2]._int, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + + (void) CHECK_GL_ERROR(); +} + + void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) { ADD_COMMAND_GL(glUniform4fv); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index e7a5e62df8..c87394869c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -379,6 +379,8 @@ protected: void do_glUseProgram(Batch& batch, uint32 paramOffset); void do_glUniform1f(Batch& batch, uint32 paramOffset); void do_glUniform2f(Batch& batch, uint32 paramOffset); + void do_glUniform3f(Batch& batch, uint32 paramOffset); + void do_glUniform3fv(Batch& batch, uint32 paramOffset); void do_glUniform4fv(Batch& batch, uint32 paramOffset); void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset); diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index e0ea2f2d98..45adbcdb3c 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -61,7 +61,11 @@ void makeBindings(GLBackend::GLShader* shader) { if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "texcoord"); } - + loc = glGetAttribLocation(glprogram, "attribTexcoord"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "attribTexcoord"); + } + loc = glGetAttribLocation(glprogram, "tangent"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "tangent"); diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 3f760e4cc8..2e3c2dca70 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -75,6 +75,8 @@ void GLBackend::syncTransformStateCache() { } void GLBackend::updateTransform() { + GLint originalMatrixMode; + glGetIntegerv(GL_MATRIX_MODE, &originalMatrixMode); // Check all the dirty flags and update the state accordingly if (_transform._invalidProj) { _transform._transformCamera._projection = _transform._projection; @@ -132,12 +134,13 @@ void GLBackend::updateTransform() { (void) CHECK_GL_ERROR(); } + if (_transform._invalidModel || _transform._invalidView) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } if (!_transform._model.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } Transform::Mat4 modelView; if (!_transform._view.isIdentity()) { Transform mvx; @@ -147,19 +150,12 @@ void GLBackend::updateTransform() { _transform._model.getMatrix(modelView); } glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else if (!_transform._view.isIdentity()) { + Transform::Mat4 modelView; + _transform._view.getInverseMatrix(modelView); + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { - if (!_transform._view.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } - Transform::Mat4 modelView; - _transform._view.getInverseMatrix(modelView); - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else { - // TODO: eventually do something about the matrix when neither view nor model is specified? - // glLoadIdentity(); - } + glLoadIdentity(); } (void) CHECK_GL_ERROR(); } @@ -167,6 +163,7 @@ void GLBackend::updateTransform() { // Flags are clean _transform._invalidView = _transform._invalidProj = _transform._invalidModel = false; + glMatrixMode(originalMatrixMode); } diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index f328cb9884..ea1be77552 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -1,325 +1,305 @@ -// -// Stage.cpp -// libraries/model/src/model -// -// Created by Sam Gateau on 2/24/2015. -// 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 "Stage.h" - -#include -#include -#include - -#include "SkyFromAtmosphere_vert.h" -#include "SkyFromAtmosphere_frag.h" - -using namespace model; - - -void EarthSunModel::updateAll() const { - updateWorldToSurface(); - updateSurfaceToEye(); - updateSun(); -} - -Mat4d EarthSunModel::evalWorldToGeoLocationMat(double longitude, double latitude, double absAltitude, double scale) { - // Longitude is along Z axis but - from east to west - Mat4d rotLon = glm::rotate(glm::radians(longitude), Vec3d(0.0, 0.0, 1.0)); - - // latitude is along X axis + from south to north - Mat4d rotLat = glm::rotate(-glm::radians(latitude), Vec3d(1.0, 0.0, 0.0)); - - // translation is movin to the earth surface + altiture at the radius along Y axis - Mat4d surfaceT = glm::translate(Vec3d(0.0, -absAltitude, 0.0)); - - // Mat4d worldScale = glm::scale(Vec3d(scale)); - - Mat4d worldToGeoLocMat = surfaceT * rotLat * rotLon; - - return worldToGeoLocMat; -} - -void EarthSunModel::updateWorldToSurface() const { - // Check if the final position is too close to the earth center ? - float absAltitude = _earthRadius + _altitude; - if (absAltitude < 0.01f) { - absAltitude = 0.01f; - } - - // Final world to local Frame - _worldToSurfaceMat = evalWorldToGeoLocationMat(_longitude, _latitude, absAltitude, _scale); - // and the inverse - _surfaceToWorldMat = glm::inverse(_worldToSurfaceMat); - - _surfacePos = Vec3d(_surfaceToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0)); -} - -void EarthSunModel::updateSurfaceToEye() const { - _surfaceToEyeMat = glm::inverse(_eyeToSurfaceMat); - _worldToEyeMat = _surfaceToEyeMat * _worldToSurfaceMat; - _eyeToWorldMat = _surfaceToWorldMat * _eyeToSurfaceMat; - _eyePos = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0) ); - _eyeDir = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, -1.0, 0.0) ); -} - -void EarthSunModel::updateSun() const { - // Longitude is along Y axis but - from east to west - Mat4d rotSunLon; - - Mat4d rotSun = evalWorldToGeoLocationMat(_sunLongitude, _sunLatitude, _earthRadius, _scale); - rotSun = glm::inverse(rotSun); - - _sunDir = Vec3d(rotSun * Vec4d(0.0, 1.0, 0.0, 0.0)); - - // sun direction is looking up toward Y axis at the specified sun lat, long - Vec3d lssd = Vec3d(_worldToSurfaceMat * Vec4d(_sunDir.x, _sunDir.y, _sunDir.z, 0.0)); - - // apply surface rotation offset - glm::dquat dSurfOrient(_surfaceOrientation); - lssd = glm::rotate(dSurfOrient, lssd); - - _surfaceSunDir = glm::normalize(Vec3(lssd.x, lssd.y, lssd.z)); -} - -void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { - _surfaceOrientation = orientation; - invalidate(); -} - -double moduloRange(double val, double minVal, double maxVal) { - double range = maxVal - minVal; - double rval = (val - minVal) / range; - rval = rval - floor(rval); - return rval * range + minVal; -} - -const float MAX_LONGITUDE = 180.0f; -const float MAX_LATITUDE = 90.0f; - -float validateLongitude(float lon) { - return moduloRange(lon, -MAX_LONGITUDE, MAX_LONGITUDE); -} - -float validateLatitude(float lat) { - return moduloRange(lat, -MAX_LATITUDE, MAX_LATITUDE); -} - -float validateAltitude(float altitude) { - const float MIN_ALTITUDE = -1000.0f; - const float MAX_ALTITUDE = 100000.0f; - return std::min(std::max(altitude, MIN_ALTITUDE), MAX_ALTITUDE); -} - -void EarthSunModel::setLatitude(float lat) { - _latitude = validateLatitude(lat); - invalidate(); -} -void EarthSunModel::setLongitude(float lon) { - _longitude = validateLongitude(lon); - invalidate(); -} -void EarthSunModel::setAltitude(float altitude) { - _altitude = validateAltitude(altitude); - invalidate(); -} - -void EarthSunModel::setSunLatitude(float lat) { - _sunLatitude = validateLatitude(lat); - invalidate(); -} -void EarthSunModel::setSunLongitude(float lon) { - _sunLongitude = validateLongitude(lon); - invalidate(); -} - -Atmosphere::Atmosphere() { - // only if created from nothing shall we create the Buffer to store the properties - Data data; - _dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Byte*) &data)); - - setScatteringWavelength(_scatteringWavelength); - setRayleighScattering(_rayleighScattering); - setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); -} - -void Atmosphere::setScatteringWavelength(Vec3 wavelength) { - _scatteringWavelength = wavelength; - Data& data = editData(); - data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); -} - -void Atmosphere::setRayleighScattering(float scattering) { - _rayleighScattering = scattering; - updateScattering(); -} - -void Atmosphere::setMieScattering(float scattering) { - _mieScattering = scattering; - updateScattering(); -} - -void Atmosphere::setSunBrightness(float brightness) { - _sunBrightness = brightness; - updateScattering(); -} - -void Atmosphere::updateScattering() { - Data& data = editData(); - - data._scatterings.x = getRayleighScattering() * getSunBrightness(); - data._scatterings.y = getMieScattering() * getSunBrightness(); - - data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); - data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); -} - -void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { - Data& data = editData(); - data._radiuses.x = inner; - data._radiuses.y = outer; - data._scales.x = 1.0f / (outer - inner); - data._scales.z = data._scales.x / data._scales.y; -} - - -const int NUM_DAYS_PER_YEAR = 365; -const float NUM_HOURS_PER_DAY = 24.0f; -const float NUM_HOURS_PER_HALF_DAY = NUM_HOURS_PER_DAY * 0.5f; - -SunSkyStage::SunSkyStage() : - _sunLight(new Light()), - _skybox(new Skybox()) -{ - _sunLight->setType(Light::SUN); - - setSunIntensity(1.0f); - setSunAmbientIntensity(0.5f); - setSunColor(Vec3(1.0f, 1.0f, 1.0f)); - - // Default origin location is a special place in the world... - setOriginLocation(122.407f, 37.777f, 0.03f); - // Noun - setDayTime(12.0f); - // Begining of march - setYearTime(60.0f); - - auto skyFromAtmosphereVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))); - auto skyFromAtmosphereFragment = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))); - auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyFromAtmosphereVertex, skyFromAtmosphereFragment)); - - auto skyState = gpu::StatePointer(new gpu::State()); - // skyState->setStencilEnable(false); - // skyState->setBlendEnable(false); - - _skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - - - _skybox.reset(new Skybox()); - _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); - -} - -SunSkyStage::~SunSkyStage() { -} - -void SunSkyStage::setDayTime(float hour) { - _dayTime = moduloRange(hour, 0.f, NUM_HOURS_PER_DAY); - invalidate(); -} - -void SunSkyStage::setYearTime(unsigned int day) { - _yearTime = day % NUM_DAYS_PER_YEAR; - invalidate(); -} - -void SunSkyStage::setOriginOrientation(const Quat& orientation) { - _earthSunModel.setSurfaceOrientation(orientation); - invalidate(); -} - -void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { - _earthSunModel.setLongitude(longitude); - _earthSunModel.setLatitude(latitude); - _earthSunModel.setAltitude(altitude); - invalidate(); -} - -void SunSkyStage::setSunModelEnable(bool isEnabled) { - _sunModelEnable = isEnabled; - invalidate(); -} - -void SunSkyStage::setSunColor(const Vec3& color) { - _sunLight->setColor(color); -} -void SunSkyStage::setSunIntensity(float intensity) { - _sunLight->setIntensity(intensity); -} -void SunSkyStage::setSunAmbientIntensity(float intensity) { - _sunLight->setAmbientIntensity(intensity); -} - -void SunSkyStage::setSunDirection(const Vec3& direction) { - if (!isSunModelEnabled()) { - _sunLight->setDirection(direction); - } -} - -// THe sun declinaison calculus is taken from https://en.wikipedia.org/wiki/Position_of_the_Sun -double evalSunDeclinaison(double dayNumber) { - return -(23.0 + 44.0/60.0)*cos(glm::radians((360.0/365.0)*(dayNumber + 10.0))); -} - -void SunSkyStage::updateGraphicsObject() const { - // Always update the sunLongitude based on the current dayTime and the current origin - // The day time is supposed to be local at the origin - float signedNormalizedDayTime = (_dayTime - NUM_HOURS_PER_HALF_DAY) / NUM_HOURS_PER_HALF_DAY; - float sunLongitude = _earthSunModel.getLongitude() + (MAX_LONGITUDE * signedNormalizedDayTime); - _earthSunModel.setSunLongitude(sunLongitude); - - // And update the sunLAtitude as the declinaison depending of the time of the year - _earthSunModel.setSunLatitude(evalSunDeclinaison(_yearTime)); - - if (isSunModelEnabled()) { - Vec3d sunLightDir = -_earthSunModel.getSurfaceSunDir(); - _sunLight->setDirection(Vec3(sunLightDir.x, sunLightDir.y, sunLightDir.z)); - - double originAlt = _earthSunModel.getAltitude(); - _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); - } - - // Background - switch (getBackgroundMode()) { - case NO_BACKGROUND: { - break; - } - case SKY_DOME: { - break; - } - case SKY_BOX: { - break; - } - case NUM_BACKGROUND_MODES: - Q_UNREACHABLE(); - }; - - static int firstTime = 0; - if (firstTime == 0) { - firstTime++; - gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); - } -} - -void SunSkyStage::setBackgroundMode(BackgroundMode mode) { - _backgroundMode = mode; - invalidate(); -} - -void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { - _skybox = skybox; - invalidate(); +// +// Stage.cpp +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/2015. +// 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 "Stage.h" + +#include +#include +#include + +using namespace model; + + +void EarthSunModel::updateAll() const { + updateWorldToSurface(); + updateSurfaceToEye(); + updateSun(); +} + +Mat4d EarthSunModel::evalWorldToGeoLocationMat(double longitude, double latitude, double absAltitude, double scale) { + // Longitude is along Z axis but - from east to west + Mat4d rotLon = glm::rotate(glm::radians(longitude), Vec3d(0.0, 0.0, 1.0)); + + // latitude is along X axis + from south to north + Mat4d rotLat = glm::rotate(-glm::radians(latitude), Vec3d(1.0, 0.0, 0.0)); + + // translation is movin to the earth surface + altiture at the radius along Y axis + Mat4d surfaceT = glm::translate(Vec3d(0.0, -absAltitude, 0.0)); + + // Mat4d worldScale = glm::scale(Vec3d(scale)); + + Mat4d worldToGeoLocMat = surfaceT * rotLat * rotLon; + + return worldToGeoLocMat; +} + +void EarthSunModel::updateWorldToSurface() const { + // Check if the final position is too close to the earth center ? + double absAltitude = _earthRadius + _altitude; + if ( absAltitude < 0.01) { + absAltitude = 0.01; + } + + // Final world to local Frame + _worldToSurfaceMat = evalWorldToGeoLocationMat(_longitude, _latitude, absAltitude, _scale); + // and the inverse + _surfaceToWorldMat = glm::inverse(_worldToSurfaceMat); + + _surfacePos = Vec3d(_surfaceToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0)); +} + +void EarthSunModel::updateSurfaceToEye() const { + _surfaceToEyeMat = glm::inverse(_eyeToSurfaceMat); + _worldToEyeMat = _surfaceToEyeMat * _worldToSurfaceMat; + _eyeToWorldMat = _surfaceToWorldMat * _eyeToSurfaceMat; + _eyePos = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0) ); + _eyeDir = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, -1.0, 0.0) ); +} + +void EarthSunModel::updateSun() const { + // Longitude is along Y axis but - from east to west + Mat4d rotSunLon; + + Mat4d rotSun = evalWorldToGeoLocationMat(_sunLongitude, _sunLatitude, _earthRadius, _scale); + rotSun = glm::inverse(rotSun); + + _sunDir = Vec3d(rotSun * Vec4d(0.0, 1.0, 0.0, 0.0)); + + // sun direction is looking up toward Y axis at the specified sun lat, long + Vec3d lssd = Vec3d(_worldToSurfaceMat * Vec4d(_sunDir.x, _sunDir.y, _sunDir.z, 0.0)); + + // apply surface rotation offset + glm::dquat dSurfOrient(_surfaceOrientation); + lssd = glm::rotate(dSurfOrient, lssd); + + _surfaceSunDir = glm::normalize(Vec3(lssd.x, lssd.y, lssd.z)); +} + +void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { + _surfaceOrientation = orientation; + invalidate(); +} + +double moduloRange(double val, double minVal, double maxVal) { + double range = maxVal - minVal; + double rval = (val - minVal) / range; + rval = rval - floor(rval); + return rval * range + minVal; +} + +const float MAX_LONGITUDE = 180.0f; +const float MAX_LATITUDE = 90.0f; + +float validateLongitude(float lon) { + return moduloRange(lon, -MAX_LONGITUDE, MAX_LONGITUDE); +} + +float validateLatitude(float lat) { + return moduloRange(lat, -MAX_LATITUDE, MAX_LATITUDE); +} + +float validateAltitude(float altitude) { + const float MIN_ALTITUDE = -1000.0f; + const float MAX_ALTITUDE = 100000.0f; + return std::min(std::max(altitude, MIN_ALTITUDE), MAX_ALTITUDE); +} + +void EarthSunModel::setLatitude(float lat) { + _latitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setLongitude(float lon) { + _longitude = validateLongitude(lon); + invalidate(); +} +void EarthSunModel::setAltitude(float altitude) { + _altitude = validateAltitude(altitude); + invalidate(); +} + +void EarthSunModel::setSunLatitude(float lat) { + _sunLatitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setSunLongitude(float lon) { + _sunLongitude = validateLongitude(lon); + invalidate(); +} + +Atmosphere::Atmosphere() { + // only if created from nothing shall we create the Buffer to store the properties + Data data; + _dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Byte*) &data)); + + setScatteringWavelength(_scatteringWavelength); + setRayleighScattering(_rayleighScattering); + setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); +} + +void Atmosphere::setScatteringWavelength(Vec3 wavelength) { + _scatteringWavelength = wavelength; + Data& data = editData(); + data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); +} + +void Atmosphere::setRayleighScattering(float scattering) { + _rayleighScattering = scattering; + updateScattering(); +} + +void Atmosphere::setMieScattering(float scattering) { + _mieScattering = scattering; + updateScattering(); +} + +void Atmosphere::setSunBrightness(float brightness) { + _sunBrightness = brightness; + updateScattering(); +} + +void Atmosphere::updateScattering() { + Data& data = editData(); + + data._scatterings.x = getRayleighScattering() * getSunBrightness(); + data._scatterings.y = getMieScattering() * getSunBrightness(); + + data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); + data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); +} + +void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { + Data& data = editData(); + data._radiuses.x = inner; + data._radiuses.y = outer; + data._scales.x = 1.0f / (outer - inner); + data._scales.z = data._scales.x / data._scales.y; +} + + +const int NUM_DAYS_PER_YEAR = 365; +const float NUM_HOURS_PER_DAY = 24.0f; +const float NUM_HOURS_PER_HALF_DAY = NUM_HOURS_PER_DAY * 0.5f; + +SunSkyStage::SunSkyStage() : + _sunLight(new Light()), + _skybox(new Skybox()) +{ + _sunLight->setType(Light::SUN); + + setSunIntensity(1.0f); + setSunAmbientIntensity(0.5f); + setSunColor(Vec3(1.0f, 1.0f, 1.0f)); + + // Default origin location is a special place in the world... + setOriginLocation(122.407f, 37.777f, 0.03f); + // Noun + setDayTime(12.0f); + // Begining of march + setYearTime(60.0f); + + _skybox.reset(new Skybox()); + _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); + +} + +SunSkyStage::~SunSkyStage() { +} + +void SunSkyStage::setDayTime(float hour) { + _dayTime = moduloRange(hour, 0.f, NUM_HOURS_PER_DAY); + invalidate(); +} + +void SunSkyStage::setYearTime(unsigned int day) { + _yearTime = day % NUM_DAYS_PER_YEAR; + invalidate(); +} + +void SunSkyStage::setOriginOrientation(const Quat& orientation) { + _earthSunModel.setSurfaceOrientation(orientation); + invalidate(); +} + +void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { + _earthSunModel.setLongitude(longitude); + _earthSunModel.setLatitude(latitude); + _earthSunModel.setAltitude(altitude); + invalidate(); +} + +void SunSkyStage::setSunModelEnable(bool isEnabled) { + _sunModelEnable = isEnabled; + invalidate(); +} + +void SunSkyStage::setSunColor(const Vec3& color) { + _sunLight->setColor(color); +} +void SunSkyStage::setSunIntensity(float intensity) { + _sunLight->setIntensity(intensity); +} +void SunSkyStage::setSunAmbientIntensity(float intensity) { + _sunLight->setAmbientIntensity(intensity); +} + +void SunSkyStage::setSunDirection(const Vec3& direction) { + if (!isSunModelEnabled()) { + _sunLight->setDirection(direction); + } +} + +// THe sun declinaison calculus is taken from https://en.wikipedia.org/wiki/Position_of_the_Sun +double evalSunDeclinaison(double dayNumber) { + return -(23.0 + 44.0/60.0)*cos(glm::radians((360.0/365.0)*(dayNumber + 10.0))); +} + +void SunSkyStage::updateGraphicsObject() const { + // Always update the sunLongitude based on the current dayTime and the current origin + // The day time is supposed to be local at the origin + double signedNormalizedDayTime = (_dayTime - NUM_HOURS_PER_HALF_DAY) / NUM_HOURS_PER_HALF_DAY; + double sunLongitude = _earthSunModel.getLongitude() + (MAX_LONGITUDE * signedNormalizedDayTime); + _earthSunModel.setSunLongitude(sunLongitude); + + // And update the sunLAtitude as the declinaison depending of the time of the year + _earthSunModel.setSunLatitude(evalSunDeclinaison(_yearTime)); + + if (isSunModelEnabled()) { + Vec3d sunLightDir = -_earthSunModel.getSurfaceSunDir(); + _sunLight->setDirection(Vec3(sunLightDir.x, sunLightDir.y, sunLightDir.z)); + + double originAlt = _earthSunModel.getAltitude(); + _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); + } + + // Background + switch (getBackgroundMode()) { + case NO_BACKGROUND: { + break; + } + case SKY_DOME: { + break; + } + case SKY_BOX: { + break; + } + case NUM_BACKGROUND_MODES: + Q_UNREACHABLE(); + }; +} + +void SunSkyStage::setBackgroundMode(BackgroundMode mode) { + _backgroundMode = mode; + invalidate(); +} + +void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { + _skybox = skybox; + invalidate(); } diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 8575626821..b4df45e024 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -1,249 +1,247 @@ -// -// Stage.h -// libraries/model/src/model -// -// Created by Sam Gateau on 2/24/2015. -// 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_model_Stage_h -#define hifi_model_Stage_h - -#include "gpu/Pipeline.h" - -#include "Light.h" -#include "Skybox.h" - -namespace model { - -typedef glm::dvec3 Vec3d; -typedef glm::dvec4 Vec4d; -typedef glm::dmat4 Mat4d; -typedef glm::mat4 Mat4; - -class EarthSunModel { -public: - - void setScale(float scale); - float getScale() const { return _scale; } - - void setLatitude(float lat); - float getLatitude() const { return _latitude; } - void setLongitude(float lon); - float getLongitude() const { return _longitude; } - void setAltitude(float altitude); - float getAltitude() const { return _altitude; } - - - void setSurfaceOrientation(const Quat& orientation); - const Quat& getSurfaceOrientation() const { valid(); return _surfaceOrientation; } - - const Vec3d& getSurfacePos() const { valid(); return _surfacePos; } - - const Mat4d& getSurfaceToWorldMat() const { valid(); return _surfaceToWorldMat; } - const Mat4d& getWoldToSurfaceMat() const { valid(); return _worldToSurfaceMat; } - - const Mat4d& getEyeToSurfaceMat() const { valid(); return _eyeToSurfaceMat; } - const Mat4d& getSurfaceToEyeMat() const { valid(); return _surfaceToEyeMat; } - - const Mat4d& getEyeToWorldMat() const { valid(); return _eyeToWorldMat; } - const Mat4d& getWorldToEyeMat() const { valid(); return _worldToEyeMat; } - - - //or set the surfaceToEye mat directly - void setEyeToSurfaceMat( const Mat4d& e2s); - - const Vec3d& getEyePos() const { valid(); return _eyePos; } - const Vec3d& getEyeDir() const { valid(); return _eyeDir; } - - void setSunLongitude(float lon); - float getSunLongitude() const { return _sunLongitude; } - - void setSunLatitude(float lat); - float getSunLatitude() const { return _sunLatitude; } - - const Vec3d& getWorldSunDir() const { valid(); return _sunDir; } - const Vec3d& getSurfaceSunDir() const { valid(); return _surfaceSunDir; } - - - EarthSunModel() { valid(); } - -protected: - float _scale = 1000.0f; //Km - float _earthRadius = 6360.0; - - Quat _surfaceOrientation; - - float _longitude = 0.0f; - float _latitude = 0.0f; - float _altitude = 0.01f; - mutable Vec3d _surfacePos; - mutable Mat4d _worldToSurfaceMat; - mutable Mat4d _surfaceToWorldMat; - void updateWorldToSurface() const; - - mutable Mat4d _surfaceToEyeMat; - mutable Mat4d _eyeToSurfaceMat; - mutable Vec3d _eyeDir; - mutable Vec3d _eyePos; - void updateSurfaceToEye() const; - - mutable Mat4d _worldToEyeMat; - mutable Mat4d _eyeToWorldMat; - - float _sunLongitude = 0.0f; - float _sunLatitude = 0.0f; - mutable Vec3d _sunDir; - mutable Vec3d _surfaceSunDir; - void updateSun() const; - - mutable bool _invalid = true; - void invalidate() const { _invalid = true; } - void valid() const { if (_invalid) { updateAll(); _invalid = false; } } - void updateAll() const; - - static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); -}; - - -class Atmosphere { -public: - - Atmosphere(); - Atmosphere(const Atmosphere& atmosphere); - Atmosphere& operator= (const Atmosphere& atmosphere); - virtual ~Atmosphere() {}; - - - void setScatteringWavelength(Vec3 wavelength); - const Vec3& getScatteringWavelength() const { return _scatteringWavelength; } - - void setRayleighScattering(float scattering); - float getRayleighScattering() const { return _rayleighScattering; } - - void setMieScattering(float scattering); - float getMieScattering() const { return _mieScattering; } - - void setSunBrightness(float brightness); - float getSunBrightness() const { return _sunBrightness; } - - void setInnerOuterRadiuses(float inner, float outer); - float getInnerRadius() const { return getData()._radiuses.x; } - float getOuterRadius() const { return getData()._radiuses.y; } - - // Data to access the attribute values of the atmosphere - class Data { - public: - Vec4 _invWaveLength = Vec4(0.0f); - Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f); - Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f); - Vec4 _scatterings = Vec4(0.0f); - Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f); - - Data() {} - }; - - const UniformBufferView& getDataBuffer() const { return _dataBuffer; } - -protected: - UniformBufferView _dataBuffer; - Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f); - float _rayleighScattering = 0.0025f; - float _mieScattering = 0.0010f; - float _sunBrightness = 20.0f; - - const Data& getData() const { return _dataBuffer.get(); } - Data& editData() { return _dataBuffer.edit(); } - - void updateScattering(); -}; -typedef std::shared_ptr< Atmosphere > AtmospherePointer; - -// Sun sky stage generates the rendering primitives to display a scene realistically -// at the specified location and time around earth -class SunSkyStage { -public: - - SunSkyStage(); - ~SunSkyStage(); - - // time of the day (local to the position) expressed in decimal hour in the range [0.0, 24.0] - void setDayTime(float hour); - float getDayTime() const { return _dayTime; } - - // time of the year expressed in day in the range [0, 365] - void setYearTime(unsigned int day); - unsigned int getYearTime() const { return _yearTime; } - - // Origin orientation used to modify the cardinal axis alignement used. - // THe default is north along +Z axis and west along +X axis. this orientation gets added - // to the transform stack producing the sun light direction. - void setOriginOrientation(const Quat& orientation); - const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } - - // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] - void setOriginLocation(float longitude, float latitude, float surfaceAltitude); - float getOriginLatitude() const { return _earthSunModel.getLatitude(); } - float getOriginLongitude() const { return _earthSunModel.getLongitude(); } - float getOriginSurfaceAltitude() const { return _earthSunModel.getAltitude(); } - - // Enable / disable the effect of the time and location on the sun direction and color - void setSunModelEnable(bool isEnabled); - bool isSunModelEnabled() const { return _sunModelEnable; } - - // Sun properties - void setSunColor(const Vec3& color); - const Vec3& getSunColor() const { return getSunLight()->getColor(); } - void setSunIntensity(float intensity); - float getSunIntensity() const { return getSunLight()->getIntensity(); } - void setSunAmbientIntensity(float intensity); - float getSunAmbientIntensity() const { return getSunLight()->getAmbientIntensity(); } - - // The sun direction is expressed in the world space - void setSunDirection(const Vec3& direction); - const Vec3& getSunDirection() const { return getSunLight()->getDirection(); } - - LightPointer getSunLight() const { valid(); return _sunLight; } - AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; } - - enum BackgroundMode { - NO_BACKGROUND = 0, - SKY_DOME, - SKY_BOX, - - NUM_BACKGROUND_MODES, - }; - void setBackgroundMode(BackgroundMode mode); - BackgroundMode getBackgroundMode() const { return _backgroundMode; } - - // Skybox - void setSkybox(const SkyboxPointer& skybox); - const SkyboxPointer& getSkybox() const { valid(); return _skybox; } - -protected: - BackgroundMode _backgroundMode = SKY_BOX; - - LightPointer _sunLight; - AtmospherePointer _atmosphere; - mutable SkyboxPointer _skybox; - - gpu::PipelinePointer _skyPipeline; - - float _dayTime = 12.0f; - int _yearTime = 0; - mutable EarthSunModel _earthSunModel; - bool _sunModelEnable = true; - - mutable bool _invalid = true; - void invalidate() const { _invalid = true; } - void valid() const { if (_invalid) { updateGraphicsObject(); _invalid = false; } } - void updateGraphicsObject() const; -}; - -typedef std::shared_ptr< SunSkyStage > SunSkyStagePointer; - -}; - -#endif +// +// Stage.h +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/2015. +// 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_model_Stage_h +#define hifi_model_Stage_h + +#include "gpu/Pipeline.h" + +#include "Light.h" +#include "Skybox.h" + +namespace model { + +typedef glm::dvec3 Vec3d; +typedef glm::dvec4 Vec4d; +typedef glm::dmat4 Mat4d; +typedef glm::mat4 Mat4; + +class EarthSunModel { +public: + + void setScale(float scale); + float getScale() const { return _scale; } + + void setLatitude(float lat); + float getLatitude() const { return _latitude; } + void setLongitude(float lon); + float getLongitude() const { return _longitude; } + void setAltitude(float altitude); + float getAltitude() const { return _altitude; } + + + void setSurfaceOrientation(const Quat& orientation); + const Quat& getSurfaceOrientation() const { valid(); return _surfaceOrientation; } + + const Vec3d& getSurfacePos() const { valid(); return _surfacePos; } + + const Mat4d& getSurfaceToWorldMat() const { valid(); return _surfaceToWorldMat; } + const Mat4d& getWoldToSurfaceMat() const { valid(); return _worldToSurfaceMat; } + + const Mat4d& getEyeToSurfaceMat() const { valid(); return _eyeToSurfaceMat; } + const Mat4d& getSurfaceToEyeMat() const { valid(); return _surfaceToEyeMat; } + + const Mat4d& getEyeToWorldMat() const { valid(); return _eyeToWorldMat; } + const Mat4d& getWorldToEyeMat() const { valid(); return _worldToEyeMat; } + + + //or set the surfaceToEye mat directly + void setEyeToSurfaceMat( const Mat4d& e2s); + + const Vec3d& getEyePos() const { valid(); return _eyePos; } + const Vec3d& getEyeDir() const { valid(); return _eyeDir; } + + void setSunLongitude(float lon); + float getSunLongitude() const { return _sunLongitude; } + + void setSunLatitude(float lat); + float getSunLatitude() const { return _sunLatitude; } + + const Vec3d& getWorldSunDir() const { valid(); return _sunDir; } + const Vec3d& getSurfaceSunDir() const { valid(); return _surfaceSunDir; } + + + EarthSunModel() { valid(); } + +protected: + float _scale = 1000.0f; //Km + double _earthRadius = 6360.0; + + Quat _surfaceOrientation; + + float _longitude = 0.0f; + float _latitude = 0.0f; + float _altitude = 0.01f; + mutable Vec3d _surfacePos; + mutable Mat4d _worldToSurfaceMat; + mutable Mat4d _surfaceToWorldMat; + void updateWorldToSurface() const; + + mutable Mat4d _surfaceToEyeMat; + mutable Mat4d _eyeToSurfaceMat; + mutable Vec3d _eyeDir; + mutable Vec3d _eyePos; + void updateSurfaceToEye() const; + + mutable Mat4d _worldToEyeMat; + mutable Mat4d _eyeToWorldMat; + + float _sunLongitude = 0.0f; + float _sunLatitude = 0.0f; + mutable Vec3d _sunDir; + mutable Vec3d _surfaceSunDir; + void updateSun() const; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateAll(); _invalid = false; } } + void updateAll() const; + + static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); +}; + + +class Atmosphere { +public: + + Atmosphere(); + Atmosphere(const Atmosphere& atmosphere); + Atmosphere& operator= (const Atmosphere& atmosphere); + virtual ~Atmosphere() {}; + + + void setScatteringWavelength(Vec3 wavelength); + const Vec3& getScatteringWavelength() const { return _scatteringWavelength; } + + void setRayleighScattering(float scattering); + float getRayleighScattering() const { return _rayleighScattering; } + + void setMieScattering(float scattering); + float getMieScattering() const { return _mieScattering; } + + void setSunBrightness(float brightness); + float getSunBrightness() const { return _sunBrightness; } + + void setInnerOuterRadiuses(float inner, float outer); + float getInnerRadius() const { return getData()._radiuses.x; } + float getOuterRadius() const { return getData()._radiuses.y; } + + // Data to access the attribute values of the atmosphere + class Data { + public: + Vec4 _invWaveLength = Vec4(0.0f); + Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f); + Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f); + Vec4 _scatterings = Vec4(0.0f); + Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f); + + Data() {} + }; + + const UniformBufferView& getDataBuffer() const { return _dataBuffer; } + +protected: + UniformBufferView _dataBuffer; + Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f); + float _rayleighScattering = 0.0025f; + float _mieScattering = 0.0010f; + float _sunBrightness = 20.0f; + + const Data& getData() const { return _dataBuffer.get(); } + Data& editData() { return _dataBuffer.edit(); } + + void updateScattering(); +}; +typedef std::shared_ptr< Atmosphere > AtmospherePointer; + +// Sun sky stage generates the rendering primitives to display a scene realistically +// at the specified location and time around earth +class SunSkyStage { +public: + + SunSkyStage(); + ~SunSkyStage(); + + // time of the day (local to the position) expressed in decimal hour in the range [0.0, 24.0] + void setDayTime(float hour); + float getDayTime() const { return _dayTime; } + + // time of the year expressed in day in the range [0, 365] + void setYearTime(unsigned int day); + unsigned int getYearTime() const { return _yearTime; } + + // Origin orientation used to modify the cardinal axis alignement used. + // THe default is north along +Z axis and west along +X axis. this orientation gets added + // to the transform stack producing the sun light direction. + void setOriginOrientation(const Quat& orientation); + const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } + + // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] + void setOriginLocation(float longitude, float latitude, float surfaceAltitude); + float getOriginLatitude() const { return _earthSunModel.getLatitude(); } + float getOriginLongitude() const { return _earthSunModel.getLongitude(); } + float getOriginSurfaceAltitude() const { return _earthSunModel.getAltitude(); } + + // Enable / disable the effect of the time and location on the sun direction and color + void setSunModelEnable(bool isEnabled); + bool isSunModelEnabled() const { return _sunModelEnable; } + + // Sun properties + void setSunColor(const Vec3& color); + const Vec3& getSunColor() const { return getSunLight()->getColor(); } + void setSunIntensity(float intensity); + float getSunIntensity() const { return getSunLight()->getIntensity(); } + void setSunAmbientIntensity(float intensity); + float getSunAmbientIntensity() const { return getSunLight()->getAmbientIntensity(); } + + // The sun direction is expressed in the world space + void setSunDirection(const Vec3& direction); + const Vec3& getSunDirection() const { return getSunLight()->getDirection(); } + + LightPointer getSunLight() const { valid(); return _sunLight; } + AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; } + + enum BackgroundMode { + NO_BACKGROUND = 0, + SKY_DOME, + SKY_BOX, + + NUM_BACKGROUND_MODES, + }; + void setBackgroundMode(BackgroundMode mode); + BackgroundMode getBackgroundMode() const { return _backgroundMode; } + + // Skybox + void setSkybox(const SkyboxPointer& skybox); + const SkyboxPointer& getSkybox() const { valid(); return _skybox; } + +protected: + BackgroundMode _backgroundMode = SKY_BOX; + + LightPointer _sunLight; + AtmospherePointer _atmosphere; + mutable SkyboxPointer _skybox; + + float _dayTime = 12.0f; + int _yearTime = 0; + mutable EarthSunModel _earthSunModel; + bool _sunModelEnable = true; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateGraphicsObject(); _invalid = false; } } + void updateGraphicsObject() const; +}; + +typedef std::shared_ptr< SunSkyStage > SunSkyStagePointer; + +}; + +#endif diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 6941d7335d..a8ed54fdd1 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -305,7 +305,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const << returnedPath; } } else { - handlePath(overridePath, trigger); + handlePath(returnedPath, trigger); } } else { // we didn't override the path or get one back - ask the DS for the viewpoint of its index path diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index a0fc7eb0be..b4c34176a4 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -72,6 +72,8 @@ public slots: void goBack(); void goForward(); + void goToUser(const QString& username); + void storeCurrentAddress(); void copyAddress(); @@ -100,7 +102,6 @@ private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); - void goToUser(const QString& username); void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); private: void setHost(const QString& host, LookupTrigger trigger); diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 7c977210fc..db2e2953c0 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -384,10 +384,12 @@ bool OctreePacketData::appendValue(const glm::vec3& value) { bool OctreePacketData::appendValue(const QVector& value) { uint16_t qVecSize = value.size(); bool success = appendValue(qVecSize); - success = append((const unsigned char*)value.constData(), qVecSize * sizeof(glm::vec3)); if (success) { - _bytesOfValues += qVecSize * sizeof(glm::vec3); - _totalBytesOfValues += qVecSize * sizeof(glm::vec3); + success = append((const unsigned char*)value.constData(), qVecSize * sizeof(glm::vec3)); + if (success) { + _bytesOfValues += qVecSize * sizeof(glm::vec3); + _totalBytesOfValues += qVecSize * sizeof(glm::vec3); + } } return success; } diff --git a/libraries/physics/src/MeshMassProperties.cpp b/libraries/physics/src/MeshMassProperties.cpp index ee2945422d..d18c068d26 100644 --- a/libraries/physics/src/MeshMassProperties.cpp +++ b/libraries/physics/src/MeshMassProperties.cpp @@ -270,7 +270,9 @@ void MeshMassProperties::computeMassProperties(const VectorOfPoints& points, con } // create some variables to hold temporary results + #ifndef NDEBUG uint32_t numPoints = points.size(); + #endif const btVector3 p0(0.0f, 0.0f, 0.0f); btMatrix3x3 tetraInertia; btMatrix3x3 doubleDebugInertia; @@ -281,9 +283,11 @@ void MeshMassProperties::computeMassProperties(const VectorOfPoints& points, con uint32_t numTriangles = triangleIndices.size() / 3; for (uint32_t i = 0; i < numTriangles; ++i) { uint32_t t = 3 * i; + #ifndef NDEBUG assert(triangleIndices[t] < numPoints); assert(triangleIndices[t + 1] < numPoints); assert(triangleIndices[t + 2] < numPoints); + #endif // extract raw vertices tetraPoints[0] = p0; diff --git a/interface/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp similarity index 57% rename from interface/src/Environment.cpp rename to libraries/render-utils/src/Environment.cpp index 9f197920d9..411beca0ae 100644 --- a/interface/src/Environment.cpp +++ b/libraries/render-utils/src/Environment.cpp @@ -9,25 +9,25 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "InterfaceConfig.h" - #include #include #include +#include "GeometryCache.h" #include +#include +#include #include #include -#include #include -#include "Application.h" -#include "Camera.h" -#include "world.h" -#include "InterfaceLogging.h" - #include "Environment.h" +#include "SkyFromSpace_vert.h" +#include "SkyFromSpace_frag.h" +#include "SkyFromAtmosphere_vert.h" +#include "SkyFromAtmosphere_frag.h" + uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash @@ -42,20 +42,15 @@ Environment::Environment() } Environment::~Environment() { - if (_initialized) { - delete _skyFromAtmosphereProgram; - delete _skyFromSpaceProgram; - } } void Environment::init() { if (_initialized) { - qCDebug(interfaceapp, "[ERROR] Environment is already initialized."); return; } - _skyFromAtmosphereProgram = createSkyProgram("Atmosphere", _skyFromAtmosphereUniformLocations); - _skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations); + setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _skyFromSpaceProgram, _skyFromSpaceUniformLocations); + setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _skyFromAtmosphereProgram, _skyFromAtmosphereUniformLocations); // start off with a default-constructed environment data _data[HifiSockAddr()][0]; @@ -63,22 +58,60 @@ void Environment::init() { _initialized = true; } +void Environment::setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipeline, int* locations) { + + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(false); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + locations[CAMERA_POS_LOCATION] = program->getUniforms().findLocation("v3CameraPos"); + locations[LIGHT_POS_LOCATION] = program->getUniforms().findLocation("v3LightPos"); + locations[INV_WAVELENGTH_LOCATION] = program->getUniforms().findLocation("v3InvWavelength"); + locations[CAMERA_HEIGHT2_LOCATION] = program->getUniforms().findLocation("fCameraHeight2"); + locations[OUTER_RADIUS_LOCATION] = program->getUniforms().findLocation("fOuterRadius"); + locations[OUTER_RADIUS2_LOCATION] = program->getUniforms().findLocation("fOuterRadius2"); + locations[INNER_RADIUS_LOCATION] = program->getUniforms().findLocation("fInnerRadius"); + locations[KR_ESUN_LOCATION] = program->getUniforms().findLocation("fKrESun"); + locations[KM_ESUN_LOCATION] = program->getUniforms().findLocation("fKmESun"); + locations[KR_4PI_LOCATION] = program->getUniforms().findLocation("fKr4PI"); + locations[KM_4PI_LOCATION] = program->getUniforms().findLocation("fKm4PI"); + locations[SCALE_LOCATION] = program->getUniforms().findLocation("fScale"); + locations[SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleDepth"); + locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleOverScaleDepth"); + locations[G_LOCATION] = program->getUniforms().findLocation("g"); + locations[G2_LOCATION] = program->getUniforms().findLocation("g2"); +} + void Environment::resetToDefault() { _data.clear(); _data[HifiSockAddr()][0]; } -void Environment::renderAtmospheres(ViewFrustum& camera) { +void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) { // get the lock for the duration of the call QMutexLocker locker(&_mutex); if (_environmentIsOverridden) { - renderAtmosphere(camera, _overrideData); + renderAtmosphere(batch, camera, _overrideData); } else { foreach (const ServerData& serverData, _data) { // TODO: do something about EnvironmentData foreach (const EnvironmentData& environmentData, serverData) { - renderAtmosphere(camera, environmentData); + renderAtmosphere(batch, camera, environmentData); } } } @@ -201,87 +234,51 @@ int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& return bytesRead; } -ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { - ProgramObject* program = new ProgramObject(); - QByteArray prefix = QString(PathUtils::resourcesPath() + "/shaders/SkyFrom" + from).toUtf8(); - program->addShaderFromSourceFile(QGLShader::Vertex, prefix + ".vert"); - program->addShaderFromSourceFile(QGLShader::Fragment, prefix + ".frag"); - program->link(); - - locations[CAMERA_POS_LOCATION] = program->uniformLocation("v3CameraPos"); - locations[LIGHT_POS_LOCATION] = program->uniformLocation("v3LightPos"); - locations[INV_WAVELENGTH_LOCATION] = program->uniformLocation("v3InvWavelength"); - locations[CAMERA_HEIGHT2_LOCATION] = program->uniformLocation("fCameraHeight2"); - locations[OUTER_RADIUS_LOCATION] = program->uniformLocation("fOuterRadius"); - locations[OUTER_RADIUS2_LOCATION] = program->uniformLocation("fOuterRadius2"); - locations[INNER_RADIUS_LOCATION] = program->uniformLocation("fInnerRadius"); - locations[KR_ESUN_LOCATION] = program->uniformLocation("fKrESun"); - locations[KM_ESUN_LOCATION] = program->uniformLocation("fKmESun"); - locations[KR_4PI_LOCATION] = program->uniformLocation("fKr4PI"); - locations[KM_4PI_LOCATION] = program->uniformLocation("fKm4PI"); - locations[SCALE_LOCATION] = program->uniformLocation("fScale"); - locations[SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleDepth"); - locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleOverScaleDepth"); - locations[G_LOCATION] = program->uniformLocation("g"); - locations[G2_LOCATION] = program->uniformLocation("g2"); - - return program; -} +void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { -void Environment::renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data) { glm::vec3 center = data.getAtmosphereCenter(); - glPushMatrix(); - glTranslatef(center.x, center.y, center.z); + Transform transform; + transform.setTranslation(center); + batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside - ProgramObject* program; int* locations; if (height < data.getAtmosphereOuterRadius()) { - program = _skyFromAtmosphereProgram; + batch.setPipeline(_skyFromAtmosphereProgram); locations = _skyFromAtmosphereUniformLocations; } else { - program = _skyFromSpaceProgram; + batch.setPipeline(_skyFromSpaceProgram); locations = _skyFromSpaceUniformLocations; } // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - program->bind(); - program->setUniform(locations[CAMERA_POS_LOCATION], relativeCameraPos); + batch._glUniform3f(locations[CAMERA_POS_LOCATION], relativeCameraPos.x, relativeCameraPos.y, relativeCameraPos.z); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); - program->setUniform(locations[LIGHT_POS_LOCATION], lightDirection); - program->setUniformValue(locations[INV_WAVELENGTH_LOCATION], - 1 / powf(data.getScatteringWavelengths().r, 4.0f), - 1 / powf(data.getScatteringWavelengths().g, 4.0f), - 1 / powf(data.getScatteringWavelengths().b, 4.0f)); - program->setUniformValue(locations[CAMERA_HEIGHT2_LOCATION], height * height); - program->setUniformValue(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); - program->setUniformValue(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); - program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); - program->setUniformValue(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); - program->setUniformValue(locations[SCALE_DEPTH_LOCATION], 0.25f); - program->setUniformValue(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], + batch._glUniform3f(locations[LIGHT_POS_LOCATION], lightDirection.x, lightDirection.y, lightDirection.z); + batch._glUniform3f(locations[INV_WAVELENGTH_LOCATION], + 1 / powf(data.getScatteringWavelengths().r, 4.0f), + 1 / powf(data.getScatteringWavelengths().g, 4.0f), + 1 / powf(data.getScatteringWavelengths().b, 4.0f)); + batch._glUniform1f(locations[CAMERA_HEIGHT2_LOCATION], height * height); + batch._glUniform1f(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); + batch._glUniform1f(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); + batch._glUniform1f(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); + batch._glUniform1f(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); + batch._glUniform1f(locations[SCALE_DEPTH_LOCATION], 0.25f); + batch._glUniform1f(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], (1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())) / 0.25f); - program->setUniformValue(locations[G_LOCATION], -0.990f); - program->setUniformValue(locations[G2_LOCATION], -0.990f * -0.990f); + batch._glUniform1f(locations[G_LOCATION], -0.990f); + batch._glUniform1f(locations[G2_LOCATION], -0.990f * -0.990f); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - DependencyManager::get()->renderSphere(1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere - glDepthMask(GL_TRUE); - - program->release(); - - glPopMatrix(); + DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 0.0f, 0.0f, 0.5f)); //Draw a unit sphere } diff --git a/interface/src/Environment.h b/libraries/render-utils/src/Environment.h similarity index 81% rename from interface/src/Environment.h rename to libraries/render-utils/src/Environment.h index c1b4171947..65e0df4b36 100644 --- a/interface/src/Environment.h +++ b/libraries/render-utils/src/Environment.h @@ -16,8 +16,9 @@ #include #include +#include -#include "EnvironmentData.h" +#include class ViewFrustum; class ProgramObject; @@ -29,7 +30,7 @@ public: void init(); void resetToDefault(); - void renderAtmospheres(ViewFrustum& camera); + void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera); void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; } void endOverride() { _environmentIsOverridden = false; } @@ -44,14 +45,10 @@ private: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated - ProgramObject* createSkyProgram(const char* from, int* locations); - - void renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data); + void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); bool _initialized; - ProgramObject* _skyFromAtmosphereProgram; - ProgramObject* _skyFromSpaceProgram; - + enum { CAMERA_POS_LOCATION, LIGHT_POS_LOCATION, @@ -72,6 +69,11 @@ private: LOCATION_COUNT }; + void setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations); + + + gpu::PipelinePointer _skyFromAtmosphereProgram; + gpu::PipelinePointer _skyFromSpaceProgram; int _skyFromAtmosphereUniformLocations[LOCATION_COUNT]; int _skyFromSpaceUniformLocations[LOCATION_COUNT]; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d9dda279e0..ce3c6769ca 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -15,9 +15,12 @@ #include "DeferredLightingEffect.h" #include "ViewFrustum.h" #include "RenderArgs.h" +#include "TextureCache.h" #include +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" using namespace render; @@ -50,7 +53,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); - _jobs.push_back(Job(DrawPostLayered())); + _jobs.push_back(Job(DrawOverlay3D())); _jobs.push_back(Job(ResetGLState())); } @@ -225,10 +228,76 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); args->_context->render((*args->_batch)); args->_batch = nullptr; // reset blend function to standard... - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); } } + +const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() const { + if (!_opaquePipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(overlay3D_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(overlay3D_frag))); + + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + auto state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + + _opaquePipeline.reset(gpu::Pipeline::create(program, state)); + } + return _opaquePipeline; +} + +template <> void render::jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOverlay3D"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() == 1)) { + inItems.emplace_back(id); + } + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(job.getOpaquePipeline()); + batch.setUniformTexture(0, args->_whiteTexture); + + if (!inItems.empty()) { + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + renderItems(sceneContext, renderContext, inItems); + } + + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + args->_whiteTexture.reset(); +} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e2cac53c0d..3b0ffdfc9b 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -14,6 +14,8 @@ #include "render/DrawTask.h" +#include "gpu/Pipeline.h" + class PrepareDeferred { public: }; @@ -50,6 +52,15 @@ namespace render { template <> void jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); } +class DrawOverlay3D { + mutable gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable +public: + const gpu::PipelinePointer& getOpaquePipeline() const; +}; +namespace render { +template <> void jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + class RenderDeferredTask : public render::Task { public: diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf similarity index 96% rename from libraries/model/src/model/SkyFromAtmosphere.slf rename to libraries/render-utils/src/SkyFromAtmosphere.slf index 02036d0d7c..1e1718677c 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -53,12 +53,14 @@ uniform float g2; varying vec3 position; + float scale(float fCos) { float x = 1.0 - fCos; return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); } + void main (void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) @@ -102,7 +104,8 @@ void main (void) float fCos = dot(v3LightPos, v3Direction) / length(v3Direction); float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); - gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/model/src/model/SkyFromAtmosphere.slv b/libraries/render-utils/src/SkyFromAtmosphere.slv similarity index 90% rename from libraries/model/src/model/SkyFromAtmosphere.slv rename to libraries/render-utils/src/SkyFromAtmosphere.slv index 785f89bb48..29c907b94c 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slv +++ b/libraries/render-utils/src/SkyFromAtmosphere.slv @@ -33,6 +33,9 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform vec3 v3CameraPos; // The camera's current position uniform vec3 v3LightPos; // The direction vector to the light source uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels @@ -52,17 +55,14 @@ const float fSamples = 2.0; varying vec3 position; - -float scale(float fCos) -{ - float x = 1.0 - fCos; - return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); -} - void main(void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) position = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(position, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/model/src/model/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf similarity index 96% rename from libraries/model/src/model/SkyFromSpace.slf rename to libraries/render-utils/src/SkyFromSpace.slf index 5f6ce80efa..2373511932 100755 --- a/libraries/model/src/model/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -108,7 +108,8 @@ void main (void) float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); vec3 color = v3FrontColor * (v3InvWavelength * fKrESun); vec3 secondaryColor = v3FrontColor * fKmESun; - gl_FragColor.rgb = color + fMiePhase * secondaryColor; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = color + fMiePhase * secondaryColor; + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/model/src/model/SkyFromSpace.slv b/libraries/render-utils/src/SkyFromSpace.slv similarity index 80% rename from libraries/model/src/model/SkyFromSpace.slv rename to libraries/render-utils/src/SkyFromSpace.slv index 6740d1909e..6427af6715 100755 --- a/libraries/model/src/model/SkyFromSpace.slv +++ b/libraries/render-utils/src/SkyFromSpace.slv @@ -1,5 +1,6 @@ -#version 120 - +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> // // For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: // @@ -32,12 +33,20 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform float fOuterRadius; // The outer (atmosphere) radius varying vec3 position; -void main(void) -{ + +void main(void) { position = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(position, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 0eb560bf72..2f898542b9 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -451,7 +451,6 @@ TextRenderer3D::TextRenderer3D(const char* family, float pointSize, int weight, EffectType effect, int effectThickness, const QColor& color) : _effectType(effect), _effectThickness(effectThickness), - _pointSize(pointSize), _color(toGlm(color)), _font(loadFont3D(family)) { if (!_font) { diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index e61203b06f..9f6a16d743 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -65,8 +65,6 @@ private: // the thickness of the effect const int _effectThickness; - const float _pointSize; - // text color const glm::vec4 _color; diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf new file mode 100644 index 0000000000..69ea8c0e76 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slf @@ -0,0 +1,29 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model.frag +// fragment shader +// +// Created by Sam Gateau on 6/16/15. +// Copyright 2015 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 +// + +uniform sampler2D diffuseMap; + +varying vec2 varTexcoord; + +varying vec3 varEyeNormal; + +varying vec4 varColor; + + +void main(void) { + vec4 diffuse = texture2D(diffuseMap, varTexcoord.st); + if (diffuse.a < 0.5) { + discard; + } + gl_FragColor = vec4(varColor * diffuse); +} diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv new file mode 100644 index 0000000000..cdb11c1d08 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slv @@ -0,0 +1,40 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// overlay3D.slv +// +// Created by Sam Gateau on 6/16/15. +// Copyright 2015 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +//attribute vec2 texcoord; + +varying vec2 varTexcoord; + +// interpolated eye position +varying vec4 varEyePosition; + +// the interpolated normal +varying vec3 varEyeNormal; + +varying vec4 varColor; + +void main(void) { + varTexcoord = gl_MultiTexCoord0.xy; + + // pass along the color + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, gl_Vertex, varEyePosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varEyeNormal.xyz)$> +} diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 9785994a29..c4159b1190 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -2,8 +2,20 @@ set(TARGET_NAME shared) # use setup_hifi_library macro to setup our project and link appropriate Qt modules # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network OpenGL Script Widgets) + +setup_hifi_opengl() add_dependency_external_projects(glm) find_package(GLM REQUIRED) -target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) \ No newline at end of file +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + +if (WIN32) + add_dependency_external_projects(boostconfig) + find_package(BoostConfig REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${BOOSTCONFIG_INCLUDE_DIRS}) + + add_dependency_external_projects(oglplus) + find_package(OGLPLUS REQUIRED) + target_include_directories(${TARGET_NAME} PUBLIC ${OGLPLUS_INCLUDE_DIRS}) +endif() \ No newline at end of file diff --git a/libraries/shared/src/MatrixStack.h b/libraries/shared/src/MatrixStack.h index a02e284725..fdfea2b3cb 100644 --- a/libraries/shared/src/MatrixStack.h +++ b/libraries/shared/src/MatrixStack.h @@ -118,11 +118,15 @@ public: template void withPush(Function f) { + #ifndef NDEBUG size_t startingDepth = size(); + #endif push(); f(); pop(); - assert(startingDepth = size()); + #ifndef NDEBUG + assert(startingDepth == size()); + #endif } template diff --git a/libraries/shared/src/OglplusHelpers.cpp b/libraries/shared/src/OglplusHelpers.cpp new file mode 100644 index 0000000000..47f9778cac --- /dev/null +++ b/libraries/shared/src/OglplusHelpers.cpp @@ -0,0 +1,325 @@ +#ifdef Q_OS_WIN + +// +// Created by Bradley Austin Davis on 2015/05/29 +// Copyright 2015 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 "OglplusHelpers.h" + +using namespace oglplus; +using namespace oglplus::shapes; + +static const char * SIMPLE_TEXTURED_VS = R"VS(#version 410 core +#pragma line __LINE__ + +uniform mat4 Projection = mat4(1); +uniform mat4 ModelView = mat4(1); + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec2 TexCoord; + +out vec2 vTexCoord; + +void main() { + gl_Position = Projection * ModelView * vec4(Position, 1); + vTexCoord = TexCoord; +} + +)VS"; + +static const char * SIMPLE_TEXTURED_FS = R"FS(#version 410 core +#pragma line __LINE__ + +uniform sampler2D sampler; +uniform float Alpha = 1.0; + +in vec2 vTexCoord; +out vec4 vFragColor; + +void main() { + vec4 c = texture(sampler, vTexCoord); + c.a = min(Alpha, c.a); + vFragColor = c; +} + +)FS"; + + +ProgramPtr loadDefaultShader() { + ProgramPtr result; + compileProgram(result, SIMPLE_TEXTURED_VS, SIMPLE_TEXTURED_FS); + return result; +} + +void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs) { + using namespace oglplus; + try { + result = ProgramPtr(new Program()); + // attach the shaders to the program + result->AttachShader( + VertexShader() + .Source(GLSLSource(vs)) + .Compile() + ); + result->AttachShader( + FragmentShader() + .Source(GLSLSource(fs)) + .Compile() + ); + result->Link(); + } catch (ProgramBuildError & err) { + Q_UNUSED(err); + Q_ASSERT_X(false, "compileProgram", "Failed to build shader program"); + qFatal((const char*)err.Message); + result.reset(); + } +} + + +ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect) { + using namespace oglplus; + Vec3f a(1, 0, 0); + Vec3f b(0, 1, 0); + if (aspect > 1) { + b[1] /= aspect; + } else { + a[0] *= aspect; + } + return ShapeWrapperPtr( + new shapes::ShapeWrapper({ "Position", "TexCoord" }, shapes::Plane(a, b), *program) + ); +} + +// Return a point's cartesian coordinates on a sphere from pitch and yaw +static glm::vec3 getPoint(float yaw, float pitch) { + return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), + glm::sin(-pitch), + glm::cos(-pitch) * (-glm::cos(yaw))); +} + + +class SphereSection : public DrawingInstructionWriter, public DrawMode { +public: + using IndexArray = std::vector; + using PosArray = std::vector; + using TexArray = std::vector; + /// The type of the index container returned by Indices() + // vertex positions + PosArray _pos_data; + // vertex tex coords + TexArray _tex_data; + IndexArray _idx_data; + unsigned int _prim_count{ 0 }; + +public: + SphereSection( + const float fov, + const float aspectRatio, + const int slices_, + const int stacks_) { + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm + if (fov >= PI) { + qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; + } + + int gridSize = std::max(slices_, stacks_); + int gridSizeLog2 = 1; + while (1 << gridSizeLog2 < gridSize) { + ++gridSizeLog2; + } + gridSize = (1 << gridSizeLog2) + 1; + // Compute number of vertices needed + int vertices = gridSize * gridSize; + _pos_data.resize(vertices * 3); + _tex_data.resize(vertices * 2); + + // Compute vertices positions and texture UV coordinate + for (int y = 0; y <= gridSize; ++y) { + for (int x = 0; x <= gridSize; ++x) { + + } + } + for (int i = 0; i < gridSize; i++) { + float stacksRatio = (float)i / (float)(gridSize - 1); // First stack is 0.0f, last stack is 1.0f + // abs(theta) <= fov / 2.0f + float pitch = -fov * (stacksRatio - 0.5f); + for (int j = 0; j < gridSize; j++) { + float slicesRatio = (float)j / (float)(gridSize - 1); // First slice is 0.0f, last slice is 1.0f + // abs(phi) <= fov * aspectRatio / 2.0f + float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); + int vertex = i * gridSize + j; + int posOffset = vertex * 3; + int texOffset = vertex * 2; + vec3 pos = getPoint(yaw, pitch); + _pos_data[posOffset] = pos.x; + _pos_data[posOffset + 1] = pos.y; + _pos_data[posOffset + 2] = pos.z; + _tex_data[texOffset] = slicesRatio; + _tex_data[texOffset + 1] = stacksRatio; + } + } // done with vertices + + int rowLen = gridSize; + + // gridsize now refers to the triangles, not the vertices, so reduce by one + // or die by fencepost error http://en.wikipedia.org/wiki/Off-by-one_error + --gridSize; + int quads = gridSize * gridSize; + for (int t = 0; t < quads; ++t) { + int x = + ((t & 0x0001) >> 0) | + ((t & 0x0004) >> 1) | + ((t & 0x0010) >> 2) | + ((t & 0x0040) >> 3) | + ((t & 0x0100) >> 4) | + ((t & 0x0400) >> 5) | + ((t & 0x1000) >> 6) | + ((t & 0x4000) >> 7); + int y = + ((t & 0x0002) >> 1) | + ((t & 0x0008) >> 2) | + ((t & 0x0020) >> 3) | + ((t & 0x0080) >> 4) | + ((t & 0x0200) >> 5) | + ((t & 0x0800) >> 6) | + ((t & 0x2000) >> 7) | + ((t & 0x8000) >> 8); + int i = x * (rowLen) + y; + + _idx_data.push_back(i); + _idx_data.push_back(i + 1); + _idx_data.push_back(i + rowLen + 1); + + _idx_data.push_back(i + rowLen + 1); + _idx_data.push_back(i + rowLen); + _idx_data.push_back(i); + } + _prim_count = quads * 2; + } + + /// Returns the winding direction of faces + FaceOrientation FaceWinding(void) const { + return FaceOrientation::CCW; + } + + typedef GLuint(SphereSection::*VertexAttribFunc)(std::vector&) const; + + /// Makes the vertex positions and returns the number of values per vertex + template + GLuint Positions(std::vector& dest) const { + dest.clear(); + dest.insert(dest.begin(), _pos_data.begin(), _pos_data.end()); + return 3; + } + + /// Makes the vertex normals and returns the number of values per vertex + template + GLuint Normals(std::vector& dest) const { + dest.clear(); + return 3; + } + + /// Makes the vertex tangents and returns the number of values per vertex + template + GLuint Tangents(std::vector& dest) const { + dest.clear(); + return 3; + } + + /// Makes the vertex bi-tangents and returns the number of values per vertex + template + GLuint Bitangents(std::vector& dest) const { + dest.clear(); + return 3; + } + + /// Makes the texture coordinates returns the number of values per vertex + template + GLuint TexCoordinates(std::vector& dest) const { + dest.clear(); + dest.insert(dest.begin(), _tex_data.begin(), _tex_data.end()); + return 2; + } + + typedef VertexAttribsInfo< + SphereSection, + std::tuple< + VertexPositionsTag, + VertexNormalsTag, + VertexTangentsTag, + VertexBitangentsTag, + VertexTexCoordinatesTag + > + > VertexAttribs; + + Spheref MakeBoundingSphere(void) const { + GLfloat min_x = _pos_data[3], max_x = _pos_data[3]; + GLfloat min_y = _pos_data[4], max_y = _pos_data[4]; + GLfloat min_z = _pos_data[5], max_z = _pos_data[5]; + for (std::size_t v = 0, vn = _pos_data.size() / 3; v != vn; ++v) { + GLfloat x = _pos_data[v * 3 + 0]; + GLfloat y = _pos_data[v * 3 + 1]; + GLfloat z = _pos_data[v * 3 + 2]; + + if (min_x > x) min_x = x; + if (min_y > y) min_y = y; + if (min_z > z) min_z = z; + if (max_x < x) max_x = x; + if (max_y < y) max_y = y; + if (max_z < z) max_z = z; + } + + Vec3f c( + (min_x + max_x) * 0.5f, + (min_y + max_y) * 0.5f, + (min_z + max_z) * 0.5f + ); + + return Spheref( + c.x(), c.y(), c.z(), + Distance(c, Vec3f(min_x, min_y, min_z)) + ); + } + + /// Queries the bounding sphere coordinates and dimensions + template + void BoundingSphere(oglplus::Sphere& bounding_sphere) const { + bounding_sphere = oglplus::Sphere(MakeBoundingSphere()); + } + + + /// Returns element indices that are used with the drawing instructions + const IndexArray & Indices(Default = Default()) const { + return _idx_data; + } + + /// Returns the instructions for rendering of faces + DrawingInstructions Instructions(PrimitiveType primitive) const { + DrawingInstructions instr = this->MakeInstructions(); + DrawOperation operation; + operation.method = DrawOperation::Method::DrawElements; + operation.mode = primitive; + operation.first = 0; + operation.count = _prim_count * 3; + operation.restart_index = DrawOperation::NoRestartIndex(); + operation.phase = 0; + this->AddInstruction(instr, operation); + return std::move(instr); + } + + /// Returns the instructions for rendering of faces + DrawingInstructions Instructions(Default = Default()) const { + return Instructions(PrimitiveType::Triangles); + } +}; + +ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov, float aspect, int slices, int stacks) { + using namespace oglplus; + return ShapeWrapperPtr( + new shapes::ShapeWrapper({ "Position", "TexCoord" }, SphereSection(fov, aspect, slices, stacks), *program) + ); +} +#endif \ No newline at end of file diff --git a/libraries/shared/src/OglplusHelpers.h b/libraries/shared/src/OglplusHelpers.h new file mode 100644 index 0000000000..47e7331ce0 --- /dev/null +++ b/libraries/shared/src/OglplusHelpers.h @@ -0,0 +1,173 @@ +// +// Created by Bradley Austin Davis on 2015/05/26 +// Copyright 2015 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 +// +#pragma once + +// FIXME support oglplus on all platforms +// For now it's a convenient helper for Windows + +#include + +#ifdef Q_OS_WIN +#include "GLMHelpers.h" + +#define OGLPLUS_USE_GLCOREARB_H 0 + +#if defined(__APPLE__) + +#define OGLPLUS_USE_GL3_H 1 + +#elif defined(WIN32) + +#define OGLPLUS_USE_GLEW 1 +#pragma warning(disable : 4068) + +#elif defined(ANDROID) + +#else + +#define OGLPLUS_USE_GLCOREARB_H 1 + +#endif + + + +#define OGLPLUS_USE_BOOST_CONFIG 1 +#define OGLPLUS_NO_SITE_CONFIG 1 +#define OGLPLUS_LOW_PROFILE 1 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "NumericalConstants.h" + +using FramebufferPtr = std::shared_ptr; +using ShapeWrapperPtr = std::shared_ptr; +using BufferPtr = std::shared_ptr; +using VertexArrayPtr = std::shared_ptr; +using ProgramPtr = std::shared_ptr; +using Mat4Uniform = oglplus::Uniform; + +ProgramPtr loadDefaultShader(); +void compileProgram(ProgramPtr & result, const std::string& vs, const std::string& fs); +ShapeWrapperPtr loadPlane(ProgramPtr program, float aspect = 1.0f); +ShapeWrapperPtr loadSphereSection(ProgramPtr program, float fov = PI / 3.0f * 2.0f, float aspect = 16.0f / 9.0f, int slices = 32, int stacks = 32); + + +// A basic wrapper for constructing a framebuffer with a renderbuffer +// for the depth attachment and an undefined type for the color attachement +// This allows us to reuse the basic framebuffer code for both the Mirror +// FBO as well as the Oculus swap textures we will use to render the scene +// Though we don't really need depth at all for the mirror FBO, or even an +// FBO, but using one means I can just use a glBlitFramebuffer to get it onto +// the screen. +template < + typename C, + typename D +> +struct FramebufferWrapper { + uvec2 size; + oglplus::Framebuffer fbo; + C color; + D depth; + + FramebufferWrapper() {} + + virtual ~FramebufferWrapper() { + } + + virtual void Init(const uvec2 & size) { + this->size = size; + initColor(); + initDepth(); + initDone(); + } + + template + void Bound(F f) { + Bound(oglplus::Framebuffer::Target::Draw, f); + } + + template + void Bound(oglplus::Framebuffer::Target target , F f) { + fbo.Bind(target); + onBind(target); + f(); + onUnbind(target); + oglplus::DefaultFramebuffer().Bind(target); + } + + void Viewport() { + oglplus::Context::Viewport(size.x, size.y); + } + +protected: + virtual void onBind(oglplus::Framebuffer::Target target) {} + virtual void onUnbind(oglplus::Framebuffer::Target target) {} + + static GLenum toEnum(oglplus::Framebuffer::Target target) { + switch (target) { + case oglplus::Framebuffer::Target::Draw: + return GL_DRAW_FRAMEBUFFER; + case oglplus::Framebuffer::Target::Read: + return GL_READ_FRAMEBUFFER; + default: + Q_ASSERT(false); + return GL_FRAMEBUFFER; + } + } + + virtual void initDepth() {} + + virtual void initColor() {} + + virtual void initDone() = 0; +}; + +struct BasicFramebufferWrapper : public FramebufferWrapper { +protected: + virtual void initDepth() override { + using namespace oglplus; + Context::Bound(Renderbuffer::Target::Renderbuffer, depth) + .Storage( + PixelDataInternalFormat::DepthComponent, + size.x, size.y); + } + + virtual void initColor() override { + using namespace oglplus; + Context::Bound(oglplus::Texture::Target::_2D, color) + .MinFilter(TextureMinFilter::Linear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Image2D( + 0, PixelDataInternalFormat::RGBA8, + size.x, size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr + ); + } + + virtual void initDone() override { + using namespace oglplus; + static const Framebuffer::Target target = Framebuffer::Target::Draw; + Bound(target, [&] { + fbo.AttachTexture(target, FramebufferAttachment::Color, color, 0); + fbo.AttachRenderbuffer(target, FramebufferAttachment::Depth, depth); + fbo.Complete(target); + }); + } +}; + +using BasicFramebufferWrapperPtr = std::shared_ptr; +#endif diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index cce9637cd4..08d83a29ca 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -55,7 +55,7 @@ const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 15; const int16_t COLLISION_MASK_DEFAULT = ~ COLLISION_GROUP_COLLISIONLESS; // STATIC also doesn't collide with other STATIC -const int16_t COLLISION_MASK_STATIC = ~ (COLLISION_GROUP_COLLISIONLESS | COLLISION_MASK_STATIC); +const int16_t COLLISION_MASK_STATIC = ~ (COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_STATIC); const int16_t COLLISION_MASK_KINEMATIC = COLLISION_MASK_DEFAULT; diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 84b3a202b4..9673623b13 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -13,6 +13,8 @@ #define hifi_RenderArgs_h #include +#include + class AABox; class OctreeRenderer; @@ -20,6 +22,7 @@ class ViewFrustum; namespace gpu { class Batch; class Context; +class Texture; } class RenderDetails { @@ -109,6 +112,8 @@ public: gpu::Batch* _batch = nullptr; ShoudRenderFunctor _shouldRender; + std::shared_ptr _whiteTexture; + RenderDetails _details; float _alphaThreshold = 0.5f; diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index 6c21557325..c0d21e9995 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -13,35 +13,61 @@ #include "SharedLogging.h" #include "VariantMapToScriptValue.h" + +QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) { + switch(qValue.type()) { + case QVariant::Bool: + return qValue.toBool(); + break; + case QVariant::Int: + return qValue.toInt(); + break; + case QVariant::Double: + return qValue.toDouble(); + break; + case QVariant::String: { + return scriptEngine.newVariant(qValue); + break; + } + case QVariant::Map: { + QVariantMap childMap = qValue.toMap(); + return variantMapToScriptValue(childMap, scriptEngine); + break; + } + case QVariant::List: { + QVariantList childList = qValue.toList(); + return variantListToScriptValue(childList, scriptEngine); + break; + } + default: + qCDebug(shared) << "unhandled QScript type" << qValue.type(); + break; + } + + return QScriptValue(); +} + + QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) { QScriptValue scriptValue = scriptEngine.newObject(); for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) { QString key = iter.key(); QVariant qValue = iter.value(); - - switch(qValue.type()) { - case QVariant::Bool: - scriptValue.setProperty(key, qValue.toBool()); - break; - case QVariant::Int: - scriptValue.setProperty(key, qValue.toInt()); - break; - case QVariant::Double: - scriptValue.setProperty(key, qValue.toDouble()); - break; - case QVariant::String: { - scriptValue.setProperty(key, scriptEngine.newVariant(qValue)); - break; - } - case QVariant::Map: { - QVariantMap childMap = qValue.toMap(); - scriptValue.setProperty(key, variantMapToScriptValue(childMap, scriptEngine)); - break; - } - default: - qCDebug(shared) << "unhandled QScript type" << qValue.type(); - } + scriptValue.setProperty(key, variantToScriptValue(qValue, scriptEngine)); + } + + return scriptValue; +} + + +QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine) { + QScriptValue scriptValue = scriptEngine.newObject(); + + scriptValue.setProperty("length", variantList.size()); + int i = 0; + foreach (QVariant v, variantList) { + scriptValue.setProperty(i++, variantToScriptValue(v, scriptEngine)); } return scriptValue; diff --git a/libraries/shared/src/VariantMapToScriptValue.h b/libraries/shared/src/VariantMapToScriptValue.h index 503f8c6490..ea65cccb3d 100644 --- a/libraries/shared/src/VariantMapToScriptValue.h +++ b/libraries/shared/src/VariantMapToScriptValue.h @@ -13,4 +13,6 @@ #include #include +QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine); QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine); +QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine); diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index b6cb0b136b..1b402ff2a3 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -135,8 +135,8 @@ void VrMenu::setRootMenu(QObject* rootMenu) { void VrMenu::addMenu(QMenu* menu) { Q_ASSERT(!MenuUserData::forObject(menu)); - QObject * parent = menu->parent(); - QObject * qmlParent; + QObject* parent = menu->parent(); + QObject* qmlParent = nullptr; if (dynamic_cast(parent)) { MenuUserData* userData = MenuUserData::forObject(parent); qmlParent = findMenuObject(userData->uuid.toString()); diff --git a/tools/vhacd-util/src/VHACDUtilApp.cpp b/tools/vhacd-util/src/VHACDUtilApp.cpp index ed4dab020f..2af6bca337 100644 --- a/tools/vhacd-util/src/VHACDUtilApp.cpp +++ b/tools/vhacd-util/src/VHACDUtilApp.cpp @@ -322,6 +322,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; writeOBJ(outputFileName, fbx, outputCentimeters, count); count++; + (void)meshPart; // quiet warning } } }